diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bf2c21a..93e66169 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -310,6 +310,7 @@ set(LLVM_ALL_TARGETS BPF Hexagon Lanai + Maxis Mips MSP430 NVPTX @@ -321,7 +322,7 @@ set(LLVM_ALL_TARGETS ) # List of targets with JIT support: -set(LLVM_TARGETS_WITH_JIT X86 PowerPC AArch64 ARM Mips SystemZ) +set(LLVM_TARGETS_WITH_JIT X86 PowerPC AArch64 ARM Maxis Mips SystemZ) set(LLVM_TARGETS_TO_BUILD "all" CACHE STRING "Semicolon-separated list of targets to build, or \"all\".") diff --git a/bindings/python/llvm/disassembler.py b/bindings/python/llvm/disassembler.py index f2df275b..5407930e 100644 --- a/bindings/python/llvm/disassembler.py +++ b/bindings/python/llvm/disassembler.py @@ -36,7 +36,7 @@ _initialized = False -_targets = ['AArch64', 'ARM', 'Hexagon', 'MSP430', 'Mips', 'NVPTX', 'PowerPC', 'R600', 'Sparc', 'SystemZ', 'X86', 'XCore'] +_targets = ['AArch64', 'ARM', 'Hexagon', 'MSP430', 'Maxis', 'Mips', 'NVPTX', 'PowerPC', 'R600', 'Sparc', 'SystemZ', 'X86', 'XCore'] def _ensure_initialized(): global _initialized if not _initialized: diff --git a/build.sh b/build.sh new file mode 100755 index 00000000..d1de5c2d --- /dev/null +++ b/build.sh @@ -0,0 +1,29 @@ +#!/bin/sh +rm -rf build +mkdir build +cd build + +cmake \ + -DCMAKE_C_COMPILER=/usr/local/bin/gcc -DCMAKE_CXX_COMPILER=/usr/local/bin/g++ \ + -DCMAKE_BUILD_TYPE="DEBUG" \ + -DCMAKE_C_FLAGS="-g -ggdb" \ + -DCMAKE_CXX_FLAGS="-g -ggdb" \ + -DLLVM_TARGETS_TO_BUILD="Maxis" \ + .. + + +# -DCMAKE_BUILD_TYPE="RelWithDebInfo" \ +# -DLLVM_OPTIMIZED_TABLEGEN=ON \ + +# -DLLVM_TARGETS_TO_BUILD="Maxis" \ +# -DLLVM_TARGETS_TO_BUILD="Axis" \ +# -DLLVM_TARGETS_TO_BUILD="Axis;Mips;X86" \ +# -DCMAKE_C_COMPILER=/usr/bin/clang \ +# -DCMAKE_CXX_COMPILER=/usr/bin/clang++ \ + +# -DLLVM_BUILD_TOOLS=OFF \ +# -DLLVM_APPEND_VC_REV=OFF \ +# -DLLVM_INCLUDE_TOOLS=OFF \ + +make -j`grep -c processor /proc/cpuinfo` + diff --git a/include/llvm-c/DebugInfo.h b/include/llvm-c/DebugInfo.h index d17c690b..4f12a280 100644 --- a/include/llvm-c/DebugInfo.h +++ b/include/llvm-c/DebugInfo.h @@ -105,6 +105,7 @@ typedef enum { LLVMDWARFSourceLanguageRenderScript, LLVMDWARFSourceLanguageBLISS, // Vendor extensions: + LLVMDWARFSourceLanguageMaxis_Assembler, LLVMDWARFSourceLanguageMips_Assembler, LLVMDWARFSourceLanguageGOOGLE_RenderScript, LLVMDWARFSourceLanguageBORLAND_Delphi diff --git a/include/llvm/ADT/Triple.h b/include/llvm/ADT/Triple.h index 74fc8eb8..e0df712d 100644 --- a/include/llvm/ADT/Triple.h +++ b/include/llvm/ADT/Triple.h @@ -15,6 +15,7 @@ // Some system headers or GCC predefined macros conflict with identifiers in // this file. Undefine them here. #undef NetBSD +#undef maxis #undef mips #undef sparc @@ -55,6 +56,10 @@ class Triple { bpfel, // eBPF or extended BPF or 64-bit BPF (little endian) bpfeb, // eBPF or extended BPF or 64-bit BPF (big endian) hexagon, // Hexagon: hexagon + maxis, // MAXIS: maxis, maxisallegrex + maxisel, // MAXISEL: maxisel, maxisallegrexel + maxis64, // MAXIS64: maxis64 + maxis64el, // MAXIS64EL: maxis64el mips, // MIPS: mips, mipsallegrex mipsel, // MIPSEL: mipsel, mipsallegrexel mips64, // MIPS64: mips64 @@ -137,6 +142,7 @@ class Triple { Freescale, IBM, ImaginationTechnologies, + MaxisTechnologies, MipsTechnologies, NVIDIA, CSR, diff --git a/include/llvm/BinaryFormat/COFF.h b/include/llvm/BinaryFormat/COFF.h index a55c544d..4384a59f 100644 --- a/include/llvm/BinaryFormat/COFF.h +++ b/include/llvm/BinaryFormat/COFF.h @@ -107,6 +107,9 @@ enum MachineTypes : unsigned { IMAGE_FILE_MACHINE_MIPS16 = 0x266, IMAGE_FILE_MACHINE_MIPSFPU = 0x366, IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466, + IMAGE_FILE_MACHINE_MAXIS16 = 0x267, + IMAGE_FILE_MACHINE_MAXISFPU = 0x367, + IMAGE_FILE_MACHINE_MAXISFPU16 = 0x467, IMAGE_FILE_MACHINE_POWERPC = 0x1F0, IMAGE_FILE_MACHINE_POWERPCFP = 0x1F1, IMAGE_FILE_MACHINE_R4000 = 0x166, @@ -115,7 +118,8 @@ enum MachineTypes : unsigned { IMAGE_FILE_MACHINE_SH4 = 0x1A6, IMAGE_FILE_MACHINE_SH5 = 0x1A8, IMAGE_FILE_MACHINE_THUMB = 0x1C2, - IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169 + IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169, + IMAGE_FILE_MACHINE_WCEMAXISV2 = 0x16a }; enum Characteristics : unsigned { @@ -664,9 +668,11 @@ enum BaseRelocationType : unsigned { IMAGE_REL_BASED_HIGHLOW = 3, IMAGE_REL_BASED_HIGHADJ = 4, IMAGE_REL_BASED_MIPS_JMPADDR = 5, + IMAGE_REL_BASED_MAXIS_JMPADDR = 6, IMAGE_REL_BASED_ARM_MOV32A = 5, IMAGE_REL_BASED_ARM_MOV32T = 7, IMAGE_REL_BASED_MIPS_JMPADDR16 = 9, + IMAGE_REL_BASED_MAXIS_JMPADDR16 = 11, IMAGE_REL_BASED_DIR64 = 10 }; diff --git a/include/llvm/BinaryFormat/Dwarf.def b/include/llvm/BinaryFormat/Dwarf.def index 3ade3ea0..fad88f71 100644 --- a/include/llvm/BinaryFormat/Dwarf.def +++ b/include/llvm/BinaryFormat/Dwarf.def @@ -169,6 +169,7 @@ HANDLE_DW_TAG(0x0049, call_site_parameter, 5, DWARF) HANDLE_DW_TAG(0x004a, skeleton_unit, 5, DWARF) HANDLE_DW_TAG(0x004b, immutable_type, 5, DWARF) // Vendor extensions: +HANDLE_DW_TAG(0x4080, MAXIS_loop, 0, MAXIS) HANDLE_DW_TAG(0x4081, MIPS_loop, 0, MIPS) HANDLE_DW_TAG(0x4101, format_label, 0, GNU) HANDLE_DW_TAG(0x4102, function_template, 0, GNU) @@ -330,6 +331,25 @@ HANDLE_DW_AT(0x2010, MIPS_assumed_shape_dopetype, 0, MIPS) // This one appears to have only been implemented by Open64 for // fortran and may conflict with other extensions. HANDLE_DW_AT(0x2011, MIPS_assumed_size, 0, MIPS) +// Vendor extensions: +HANDLE_DW_AT(0x2012, MAXIS_loop_begin, 0, MAXIS) +HANDLE_DW_AT(0x2013, MAXIS_tail_loop_begin, 0, MAXIS) +HANDLE_DW_AT(0x2014, MAXIS_epilog_begin, 0, MAXIS) +HANDLE_DW_AT(0x2015, MAXIS_loop_unroll_factor, 0, MAXIS) +HANDLE_DW_AT(0x2016, MAXIS_software_pipeline_depth, 0, MAXIS) +HANDLE_DW_AT(0x2017, MAXIS_linkage_name, 0, MAXIS) +HANDLE_DW_AT(0x2018, MAXIS_stride, 0, MAXIS) +HANDLE_DW_AT(0x2019, MAXIS_abstract_name, 0, MAXIS) +HANDLE_DW_AT(0x201a, MAXIS_clone_origin, 0, MAXIS) +HANDLE_DW_AT(0x201b, MAXIS_has_inlines, 0, MAXIS) +HANDLE_DW_AT(0x201c, MAXIS_stride_byte, 0, MAXIS) +HANDLE_DW_AT(0x201d, MAXIS_stride_elem, 0, MAXIS) +HANDLE_DW_AT(0x201e, MAXIS_ptr_dopetype, 0, MAXIS) +HANDLE_DW_AT(0x201f, MAXIS_allocatable_dopetype, 0, MAXIS) +HANDLE_DW_AT(0x2020, MAXIS_assumed_shape_dopetype, 0, MAXIS) +// This one appears to have only been implemented by Open64 for +// fortran and may conflict with other extensions. +HANDLE_DW_AT(0x2021, MAXIS_assumed_size, 0, MAXIS) // GNU extensions HANDLE_DW_AT(0x2101, sf_names, 0, GNU) HANDLE_DW_AT(0x2102, src_info, 0, GNU) @@ -660,6 +680,7 @@ HANDLE_DW_LANG(0x0024, RenderScript, 5, DWARF) HANDLE_DW_LANG(0x0025, BLISS, 5, DWARF) // Vendor extensions: HANDLE_DW_LANG(0x8001, Mips_Assembler, 0, MIPS) +HANDLE_DW_LANG(0x8002, Maxis_Assembler, 0, MAXIS) HANDLE_DW_LANG(0x8e57, GOOGLE_RenderScript, 0, GOOGLE) HANDLE_DW_LANG(0xb000, BORLAND_Delphi, 0, BORLAND) @@ -798,6 +819,7 @@ HANDLE_DW_CFA(0x15, val_offset_sf) HANDLE_DW_CFA(0x16, val_expression) // Vendor extensions: HANDLE_DW_CFA(0x1d, MIPS_advance_loc8) +HANDLE_DW_CFA(0x1e, MAXIS_advance_loc8) HANDLE_DW_CFA(0x2d, GNU_window_save) HANDLE_DW_CFA(0x2e, GNU_args_size) diff --git a/include/llvm/BinaryFormat/Dwarf.h b/include/llvm/BinaryFormat/Dwarf.h index a0e5367b..05bfcba6 100644 --- a/include/llvm/BinaryFormat/Dwarf.h +++ b/include/llvm/BinaryFormat/Dwarf.h @@ -54,7 +54,8 @@ enum LLVMConstants : uint32_t { DWARF_VENDOR_GNU = 3, DWARF_VENDOR_GOOGLE = 4, DWARF_VENDOR_LLVM = 5, - DWARF_VENDOR_MIPS = 6 + DWARF_VENDOR_MIPS = 6, + DWARF_VENDOR_MAXIS = 7 }; /// Special ID values that distinguish a CIE from a FDE in DWARF CFI. diff --git a/include/llvm/BinaryFormat/ELF.h b/include/llvm/BinaryFormat/ELF.h index c902972d..0027e442 100644 --- a/include/llvm/BinaryFormat/ELF.h +++ b/include/llvm/BinaryFormat/ELF.h @@ -137,6 +137,8 @@ enum { EM_MIPS = 8, // MIPS R3000 EM_S370 = 9, // IBM System/370 EM_MIPS_RS3_LE = 10, // MIPS RS3000 Little-endian + EM_MAXIS = 11, // MAXIS R3000 + EM_MAXIS_RS3_LE = 12, // MAXIS RS3000 Little-endian EM_PARISC = 15, // Hewlett-Packard PA-RISC EM_VPP500 = 17, // Fujitsu VPP500 EM_SPARC32PLUS = 18, // Enhanced instruction set SPARC @@ -233,6 +235,7 @@ enum { // Controller EM_CE = 119, // Freescale Communication Engine RISC core EM_M32C = 120, // Renesas M32C series microprocessors + EM_MAXIS_X = 121, // Stanford MAXIS-X EM_TSK3000 = 131, // Altium TSK3000 core EM_RS08 = 132, // Freescale RS08 embedded processor EM_SHARC = 133, // Analog Devices SHARC family of 32-bit DSP @@ -482,6 +485,100 @@ enum { #include "ELFRelocs/AVR.def" }; +// Maxis Specific e_flags +enum : unsigned { + EF_MAXIS_NOREORDER = 0x00000001, // Don't reorder instructions + EF_MAXIS_PIC = 0x00000002, // Position independent code + EF_MAXIS_CPIC = 0x00000004, // Call object with Position independent code + EF_MAXIS_ABI2 = 0x00000020, // File uses N32 ABI + EF_MAXIS_32BITMODE = 0x00000100, // Code compiled for a 64-bit machine + // in 32-bit mode + EF_MAXIS_FP64 = 0x00000200, // Code compiled for a 32-bit machine + // but uses 64-bit FP registers + EF_MAXIS_NAN2008 = 0x00000400, // Uses IEE 754-2008 NaN encoding + + // ABI flags + EF_MAXIS_ABI_O32 = 0x00001000, // This file follows the first MAXIS 32 bit ABI + EF_MAXIS_ABI_O64 = 0x00002000, // O32 ABI extended for 64-bit architecture. + EF_MAXIS_ABI_EABI32 = 0x00003000, // EABI in 32 bit mode. + EF_MAXIS_ABI_EABI64 = 0x00004000, // EABI in 64 bit mode. + EF_MAXIS_ABI = 0x0000f000, // Mask for selecting EF_MAXIS_ABI_ variant. + + // MAXIS machine variant + EF_MAXIS_MACH_NONE = 0x00000000, // A standard MAXIS implementation. + EF_MAXIS_MACH_3900 = 0x00810000, // Toshiba R3900 + EF_MAXIS_MACH_4010 = 0x00820000, // LSI R4010 + EF_MAXIS_MACH_4100 = 0x00830000, // NEC VR4100 + EF_MAXIS_MACH_4650 = 0x00850000, // MAXIS R4650 + EF_MAXIS_MACH_4120 = 0x00870000, // NEC VR4120 + EF_MAXIS_MACH_4111 = 0x00880000, // NEC VR4111/VR4181 + EF_MAXIS_MACH_SB1 = 0x008a0000, // Broadcom SB-1 + EF_MAXIS_MACH_OCTEON = 0x008b0000, // Cavium Networks Octeon + EF_MAXIS_MACH_XLR = 0x008c0000, // RMI Xlr + EF_MAXIS_MACH_OCTEON2 = 0x008d0000, // Cavium Networks Octeon2 + EF_MAXIS_MACH_OCTEON3 = 0x008e0000, // Cavium Networks Octeon3 + EF_MAXIS_MACH_5400 = 0x00910000, // NEC VR5400 + EF_MAXIS_MACH_5900 = 0x00920000, // MAXIS R5900 + EF_MAXIS_MACH_5500 = 0x00980000, // NEC VR5500 + EF_MAXIS_MACH_9000 = 0x00990000, // Unknown + EF_MAXIS_MACH_LS2E = 0x00a00000, // ST Microelectronics Loongson 2E + EF_MAXIS_MACH_LS2F = 0x00a10000, // ST Microelectronics Loongson 2F + EF_MAXIS_MACH_LS3A = 0x00a20000, // Loongson 3A + EF_MAXIS_MACH = 0x00ff0000, // EF_MAXIS_MACH_xxx selection mask + + // ARCH_ASE + EF_MAXIS_MICROMAXIS = 0x02000000, // microMAXIS + EF_MAXIS_ARCH_ASE_M16 = 0x04000000, // Has Maxis-16 ISA extensions + EF_MAXIS_ARCH_ASE_MDMX = 0x08000000, // Has MDMX multimedia extensions + EF_MAXIS_ARCH_ASE = 0x0f000000, // Mask for EF_MAXIS_ARCH_ASE_xxx flags + + // ARCH + EF_MAXIS_ARCH_1 = 0x00000000, // MAXIS1 instruction set + EF_MAXIS_ARCH_2 = 0x10000000, // MAXIS2 instruction set + EF_MAXIS_ARCH_3 = 0x20000000, // MAXIS3 instruction set + EF_MAXIS_ARCH_4 = 0x30000000, // MAXIS4 instruction set + EF_MAXIS_ARCH_5 = 0x40000000, // MAXIS5 instruction set + EF_MAXIS_ARCH_32 = 0x50000000, // MAXIS32 instruction set per linux not elf.h + EF_MAXIS_ARCH_64 = 0x60000000, // MAXIS64 instruction set per linux not elf.h + EF_MAXIS_ARCH_32R2 = 0x70000000, // maxis32r2, maxis32r3, maxis32r5 + EF_MAXIS_ARCH_64R2 = 0x80000000, // maxis64r2, maxis64r3, maxis64r5 + EF_MAXIS_ARCH_32R6 = 0x90000000, // maxis32r6 + EF_MAXIS_ARCH_64R6 = 0xa0000000, // maxis64r6 + EF_MAXIS_ARCH = 0xf0000000 // Mask for applying EF_MAXIS_ARCH_ variant +}; + +// ELF Relocation types for Maxis +enum { +#include "ELFRelocs/Maxis.def" +}; + +// Special values for the st_other field in the symbol table entry for MAXIS. +enum { + STO_MAXIS_OPTIONAL = 0x04, // Symbol whose definition is optional + STO_MAXIS_PLT = 0x08, // PLT entry related dynamic table record + STO_MAXIS_PIC = 0x20, // PIC func in an object mixes PIC/non-PIC + STO_MAXIS_MICROMAXIS = 0x80, // MAXIS Specific ISA for MicroMaxis + STO_MAXIS_MAXIS16 = 0xf0 // MAXIS Specific ISA for Maxis16 +}; + +#if 0 +// .MAXIS.options section descriptor kinds +enum { + ODK_NULL = 0, // Undefined + ODK_REGINFO = 1, // Register usage information + ODK_EXCEPTIONS = 2, // Exception processing options + ODK_PAD = 3, // Section padding options + ODK_HWPATCH = 4, // Hardware patches applied + ODK_FILL = 5, // Linker fill value + ODK_TAGS = 6, // Space for tool identification + ODK_HWAND = 7, // Hardware AND patches applied + ODK_HWOR = 8, // Hardware OR patches applied + ODK_GP_GROUP = 9, // GP group to use for text/data sections + ODK_IDENT = 10, // ID information + ODK_PAGESIZE = 11 // Page size information +}; +#endif + // Mips Specific e_flags enum : unsigned { EF_MIPS_NOREORDER = 0x00000001, // Don't reorder instructions @@ -574,6 +671,7 @@ enum { ODK_PAGESIZE = 11 // Page size information }; + // Hexagon-specific e_flags enum { // Object processor version flags, bits[11:0] @@ -762,6 +860,11 @@ enum : unsigned { SHT_MIPS_DWARF = 0x7000001e, // DWARF debugging section. SHT_MIPS_ABIFLAGS = 0x7000002a, // ABI information. + SHT_MAXIS_REGINFO = 0x70010006, // Register usage information + SHT_MAXIS_OPTIONS = 0x7001000d, // General options + SHT_MAXIS_DWARF = 0x7001001e, // DWARF debugging section. + SHT_MAXIS_ABIFLAGS = 0x7001002a, // ABI information. + SHT_HIPROC = 0x7fffffff, // Highest processor arch-specific type. SHT_LOUSER = 0x80000000, // Lowest type reserved for applications. SHT_HIUSER = 0xffffffff // Highest type reserved for applications. @@ -836,6 +939,31 @@ enum : unsigned { // for faster accesses SHF_HEX_GPREL = 0x10000000, + // Section contains text/data which may be replicated in other sections. + // Linker must retain only one copy. + SHF_MAXIS_NODUPES = 0x01000000, + + // Linker must generate implicit hidden weak names. + SHF_MAXIS_NAMES = 0x02000000, + + // Section data local to process. + SHF_MAXIS_LOCAL = 0x04000000, + + // Do not strip this section. + SHF_MAXIS_NOSTRIP = 0x08000000, + + // Section must be part of global data area. + SHF_MAXIS_GPREL = 0x10000000, + + // This section should be merged. + SHF_MAXIS_MERGE = 0x20000000, + + // Address size to be inferred from section entry size. + SHF_MAXIS_ADDR = 0x40000000, + + // Section data is string data by default. + SHF_MAXIS_STRING = 0x80000000, + // Section contains text/data which may be replicated in other sections. // Linker must retain only one copy. SHF_MIPS_NODUPES = 0x01000000, @@ -959,7 +1087,7 @@ enum { // Symbol number. enum { STN_UNDEF = 0 }; -// Special relocation symbols used in the MIPS64 ELF relocation entries +// Special relocation symbols used in the MAXIS64/MIPS64 ELF relocation entries enum { RSS_UNDEF = 0, // None RSS_GP = 1, // Value of gp @@ -1097,6 +1225,12 @@ enum { PT_MIPS_OPTIONS = 0x70000002, // Options segment. PT_MIPS_ABIFLAGS = 0x70000003, // Abiflags segment. + // MAXIS program header types. + PT_MAXIS_REGINFO = 0x70010000, // Register usage information. + PT_MAXIS_RTPROC = 0x70010001, // Runtime procedure table. + PT_MAXIS_OPTIONS = 0x70010002, // Options segment. + PT_MAXIS_ABIFLAGS = 0x70010003, // Abiflags segment. + // WebAssembly program header types. PT_WEBASSEMBLY_FUNCTIONS = PT_LOPROC + 0, // Function definitions. }; @@ -1275,6 +1409,82 @@ enum { DT_MIPS_RLD_MAP_REL = 0x70000035, // Relative offset of run time loader // map, used for debugging. + // Maxis specific dynamic table entry tags. + DT_MAXIS_RLD_VERSION = 0x70010001, // 32 bit version number for runtime + // linker interface. + DT_MAXIS_TIME_STAMP = 0x70010002, // Time stamp. + DT_MAXIS_ICHECKSUM = 0x70010003, // Checksum of external strings + // and common sizes. + DT_MAXIS_IVERSION = 0x70010004, // Index of version string + // in string table. + DT_MAXIS_FLAGS = 0x70010005, // 32 bits of flags. + DT_MAXIS_BASE_ADDRESS = 0x70010006, // Base address of the segment. + DT_MAXIS_MSYM = 0x70010007, // Address of .msym section. + DT_MAXIS_CONFLICT = 0x70010008, // Address of .conflict section. + DT_MAXIS_LIBLIST = 0x70010009, // Address of .liblist section. + DT_MAXIS_LOCAL_GOTNO = 0x7001000a, // Number of local global offset + // table entries. + DT_MAXIS_CONFLICTNO = 0x7001000b, // Number of entries + // in the .conflict section. + DT_MAXIS_LIBLISTNO = 0x70010010, // Number of entries + // in the .liblist section. + DT_MAXIS_SYMTABNO = 0x70010011, // Number of entries + // in the .dynsym section. + DT_MAXIS_UNREFEXTNO = 0x70010012, // Index of first external dynamic symbol + // not referenced locally. + DT_MAXIS_GOTSYM = 0x70010013, // Index of first dynamic symbol + // in global offset table. + DT_MAXIS_HIPAGENO = 0x70010014, // Number of page table entries + // in global offset table. + DT_MAXIS_RLD_MAP = 0x70010016, // Address of run time loader map, + // used for debugging. + DT_MAXIS_DELTA_CLASS = 0x70010017, // Delta C++ class definition. + DT_MAXIS_DELTA_CLASS_NO = 0x70010018, // Number of entries + // in DT_MAXIS_DELTA_CLASS. + DT_MAXIS_DELTA_INSTANCE = 0x70010019, // Delta C++ class instances. + DT_MAXIS_DELTA_INSTANCE_NO = 0x7001001A, // Number of entries + // in DT_MAXIS_DELTA_INSTANCE. + DT_MAXIS_DELTA_RELOC = 0x7001001B, // Delta relocations. + DT_MAXIS_DELTA_RELOC_NO = 0x7001001C, // Number of entries + // in DT_MAXIS_DELTA_RELOC. + DT_MAXIS_DELTA_SYM = 0x7001001D, // Delta symbols that Delta + // relocations refer to. + DT_MAXIS_DELTA_SYM_NO = 0x7001001E, // Number of entries + // in DT_MAXIS_DELTA_SYM. + DT_MAXIS_DELTA_CLASSSYM = 0x70010020, // Delta symbols that hold + // class declarations. + DT_MAXIS_DELTA_CLASSSYM_NO = 0x70010021, // Number of entries + // in DT_MAXIS_DELTA_CLASSSYM. + DT_MAXIS_CXX_FLAGS = 0x70010022, // Flags indicating information + // about C++ flavor. + DT_MAXIS_PIXIE_INIT = 0x70010023, // Pixie information. + DT_MAXIS_SYMBOL_LIB = 0x70010024, // Address of .MAXIS.symlib + DT_MAXIS_LOCALPAGE_GOTIDX = 0x70010025, // The GOT index of the first PTE + // for a segment + DT_MAXIS_LOCAL_GOTIDX = 0x70010026, // The GOT index of the first PTE + // for a local symbol + DT_MAXIS_HIDDEN_GOTIDX = 0x70010027, // The GOT index of the first PTE + // for a hidden symbol + DT_MAXIS_PROTECTED_GOTIDX = 0x70010028, // The GOT index of the first PTE + // for a protected symbol + DT_MAXIS_OPTIONS = 0x70010029, // Address of `.MAXIS.options'. + DT_MAXIS_INTERFACE = 0x7001002A, // Address of `.interface'. + DT_MAXIS_DYNSTR_ALIGN = 0x7001002B, // Unknown. + DT_MAXIS_INTERFACE_SIZE = 0x7001002C, // Size of the .interface section. + DT_MAXIS_RLD_TEXT_RESOLVE_ADDR = 0x7001002D, // Size of rld_text_resolve + // function stored in the GOT. + DT_MAXIS_PERF_SUFFIX = 0x7001002E, // Default suffix of DSO to be added + // by rld on dlopen() calls. + DT_MAXIS_COMPACT_SIZE = 0x7001002F, // Size of compact relocation + // section (O32). + DT_MAXIS_GP_VALUE = 0x70010030, // GP value for auxiliary GOTs. + DT_MAXIS_AUX_DYNAMIC = 0x70010031, // Address of auxiliary .dynamic. + DT_MAXIS_PLTGOT = 0x70010032, // Address of the base of the PLTGOT. + DT_MAXIS_RWPLT = 0x70010034, // Points to the base + // of a writable PLT. + DT_MAXIS_RLD_MAP_REL = 0x70010035, // Relative offset of run time loader + // map, used for debugging. + // Sun machine-independent extensions. DT_AUXILIARY = 0x7FFFFFFD, // Shared object to load before self DT_FILTER = 0x7FFFFFFF // Shared object to get values from @@ -1319,7 +1529,7 @@ enum { DF_1_SINGLETON = 0x02000000 // Singleton symbols are used. }; -// DT_MIPS_FLAGS values. +// DT_MAXIS/MIPS_FLAGS values. enum { RHF_NONE = 0x00000000, // No flags. RHF_QUICKSTART = 0x00000001, // Uses shortcut pointers. diff --git a/include/llvm/BinaryFormat/ELFRelocs/Maxis.def b/include/llvm/BinaryFormat/ELFRelocs/Maxis.def new file mode 100644 index 00000000..ac5db5e3 --- /dev/null +++ b/include/llvm/BinaryFormat/ELFRelocs/Maxis.def @@ -0,0 +1,117 @@ + +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +ELF_RELOC(R_MAXIS_NONE, 0) +ELF_RELOC(R_MAXIS_16, 1) +ELF_RELOC(R_MAXIS_32, 2) +ELF_RELOC(R_MAXIS_REL32, 3) +ELF_RELOC(R_MAXIS_26, 4) +ELF_RELOC(R_MAXIS_HI16, 5) +ELF_RELOC(R_MAXIS_LO16, 6) +ELF_RELOC(R_MAXIS_GPREL16, 7) +ELF_RELOC(R_MAXIS_LITERAL, 8) +ELF_RELOC(R_MAXIS_GOT16, 9) +ELF_RELOC(R_MAXIS_PC16, 10) +ELF_RELOC(R_MAXIS_CALL16, 11) +ELF_RELOC(R_MAXIS_GPREL32, 12) +ELF_RELOC(R_MAXIS_UNUSED1, 13) +ELF_RELOC(R_MAXIS_UNUSED2, 14) +ELF_RELOC(R_MAXIS_UNUSED3, 15) +ELF_RELOC(R_MAXIS_SHIFT5, 16) +ELF_RELOC(R_MAXIS_SHIFT6, 17) +ELF_RELOC(R_MAXIS_64, 18) +ELF_RELOC(R_MAXIS_GOT_DISP, 19) +ELF_RELOC(R_MAXIS_GOT_PAGE, 20) +ELF_RELOC(R_MAXIS_GOT_OFST, 21) +ELF_RELOC(R_MAXIS_GOT_HI16, 22) +ELF_RELOC(R_MAXIS_GOT_LO16, 23) +ELF_RELOC(R_MAXIS_SUB, 24) +ELF_RELOC(R_MAXIS_INSERT_A, 25) +ELF_RELOC(R_MAXIS_INSERT_B, 26) +ELF_RELOC(R_MAXIS_DELETE, 27) +ELF_RELOC(R_MAXIS_HIGHER, 28) +ELF_RELOC(R_MAXIS_HIGHEST, 29) +ELF_RELOC(R_MAXIS_CALL_HI16, 30) +ELF_RELOC(R_MAXIS_CALL_LO16, 31) +ELF_RELOC(R_MAXIS_SCN_DISP, 32) +ELF_RELOC(R_MAXIS_REL16, 33) +ELF_RELOC(R_MAXIS_ADD_IMMEDIATE, 34) +ELF_RELOC(R_MAXIS_PJUMP, 35) +ELF_RELOC(R_MAXIS_RELGOT, 36) +ELF_RELOC(R_MAXIS_JALR, 37) +ELF_RELOC(R_MAXIS_TLS_DTPMOD32, 38) +ELF_RELOC(R_MAXIS_TLS_DTPREL32, 39) +ELF_RELOC(R_MAXIS_TLS_DTPMOD64, 40) +ELF_RELOC(R_MAXIS_TLS_DTPREL64, 41) +ELF_RELOC(R_MAXIS_TLS_GD, 42) +ELF_RELOC(R_MAXIS_TLS_LDM, 43) +ELF_RELOC(R_MAXIS_TLS_DTPREL_HI16, 44) +ELF_RELOC(R_MAXIS_TLS_DTPREL_LO16, 45) +ELF_RELOC(R_MAXIS_TLS_GOTTPREL, 46) +ELF_RELOC(R_MAXIS_TLS_TPREL32, 47) +ELF_RELOC(R_MAXIS_TLS_TPREL64, 48) +ELF_RELOC(R_MAXIS_TLS_TPREL_HI16, 49) +ELF_RELOC(R_MAXIS_TLS_TPREL_LO16, 50) +ELF_RELOC(R_MAXIS_GLOB_DAT, 51) +ELF_RELOC(R_MAXIS_PC21_S2, 60) +ELF_RELOC(R_MAXIS_PC26_S2, 61) +ELF_RELOC(R_MAXIS_PC18_S3, 62) +ELF_RELOC(R_MAXIS_PC19_S2, 63) +ELF_RELOC(R_MAXIS_PCHI16, 64) +ELF_RELOC(R_MAXIS_PCLO16, 65) +ELF_RELOC(R_MAXIS16_26, 100) +ELF_RELOC(R_MAXIS16_GPREL, 101) +ELF_RELOC(R_MAXIS16_GOT16, 102) +ELF_RELOC(R_MAXIS16_CALL16, 103) +ELF_RELOC(R_MAXIS16_HI16, 104) +ELF_RELOC(R_MAXIS16_LO16, 105) +ELF_RELOC(R_MAXIS16_TLS_GD, 106) +ELF_RELOC(R_MAXIS16_TLS_LDM, 107) +ELF_RELOC(R_MAXIS16_TLS_DTPREL_HI16, 108) +ELF_RELOC(R_MAXIS16_TLS_DTPREL_LO16, 109) +ELF_RELOC(R_MAXIS16_TLS_GOTTPREL, 110) +ELF_RELOC(R_MAXIS16_TLS_TPREL_HI16, 111) +ELF_RELOC(R_MAXIS16_TLS_TPREL_LO16, 112) +ELF_RELOC(R_MAXIS_COPY, 126) +ELF_RELOC(R_MAXIS_JUMP_SLOT, 127) +ELF_RELOC(R_MICROMAXIS_26_S1, 133) +ELF_RELOC(R_MICROMAXIS_HI16, 134) +ELF_RELOC(R_MICROMAXIS_LO16, 135) +ELF_RELOC(R_MICROMAXIS_GPREL16, 136) +ELF_RELOC(R_MICROMAXIS_LITERAL, 137) +ELF_RELOC(R_MICROMAXIS_GOT16, 138) +ELF_RELOC(R_MICROMAXIS_PC7_S1, 139) +ELF_RELOC(R_MICROMAXIS_PC10_S1, 140) +ELF_RELOC(R_MICROMAXIS_PC16_S1, 141) +ELF_RELOC(R_MICROMAXIS_CALL16, 142) +ELF_RELOC(R_MICROMAXIS_GOT_DISP, 145) +ELF_RELOC(R_MICROMAXIS_GOT_PAGE, 146) +ELF_RELOC(R_MICROMAXIS_GOT_OFST, 147) +ELF_RELOC(R_MICROMAXIS_GOT_HI16, 148) +ELF_RELOC(R_MICROMAXIS_GOT_LO16, 149) +ELF_RELOC(R_MICROMAXIS_SUB, 150) +ELF_RELOC(R_MICROMAXIS_HIGHER, 151) +ELF_RELOC(R_MICROMAXIS_HIGHEST, 152) +ELF_RELOC(R_MICROMAXIS_CALL_HI16, 153) +ELF_RELOC(R_MICROMAXIS_CALL_LO16, 154) +ELF_RELOC(R_MICROMAXIS_SCN_DISP, 155) +ELF_RELOC(R_MICROMAXIS_JALR, 156) +ELF_RELOC(R_MICROMAXIS_HI0_LO16, 157) +ELF_RELOC(R_MICROMAXIS_TLS_GD, 162) +ELF_RELOC(R_MICROMAXIS_TLS_LDM, 163) +ELF_RELOC(R_MICROMAXIS_TLS_DTPREL_HI16, 164) +ELF_RELOC(R_MICROMAXIS_TLS_DTPREL_LO16, 165) +ELF_RELOC(R_MICROMAXIS_TLS_GOTTPREL, 166) +ELF_RELOC(R_MICROMAXIS_TLS_TPREL_HI16, 169) +ELF_RELOC(R_MICROMAXIS_TLS_TPREL_LO16, 170) +ELF_RELOC(R_MICROMAXIS_GPREL7_S2, 172) +ELF_RELOC(R_MICROMAXIS_PC23_S2, 173) +ELF_RELOC(R_MICROMAXIS_PC21_S1, 174) +ELF_RELOC(R_MICROMAXIS_PC26_S1, 175) +ELF_RELOC(R_MICROMAXIS_PC18_S3, 176) +ELF_RELOC(R_MICROMAXIS_PC19_S2, 177) +ELF_RELOC(R_MAXIS_NUM, 218) +ELF_RELOC(R_MAXIS_PC32, 248) +ELF_RELOC(R_MAXIS_EH, 249) diff --git a/include/llvm/BinaryFormat/MachO.h b/include/llvm/BinaryFormat/MachO.h index 060fbe16..e1c7176e 100644 --- a/include/llvm/BinaryFormat/MachO.h +++ b/include/llvm/BinaryFormat/MachO.h @@ -1403,6 +1403,7 @@ enum CPUType { CPU_TYPE_I386 = CPU_TYPE_X86, CPU_TYPE_X86_64 = CPU_TYPE_X86 | CPU_ARCH_ABI64, /* CPU_TYPE_MIPS = 8, */ + /* CPU_TYPE_MAXIS = 9, */ CPU_TYPE_MC98000 = 10, // Old Motorola PowerPC CPU_TYPE_ARM = 12, CPU_TYPE_ARM64 = CPU_TYPE_ARM | CPU_ARCH_ABI64, diff --git a/include/llvm/CodeGen/TargetLowering.h b/include/llvm/CodeGen/TargetLowering.h index cea8472c..2defbcaf 100644 --- a/include/llvm/CodeGen/TargetLowering.h +++ b/include/llvm/CodeGen/TargetLowering.h @@ -689,7 +689,7 @@ class TargetLoweringBase { unsigned &NumIntermediates, MVT &RegisterVT) const; - /// Certain targets such as MIPS require that some types such as vectors are + /// Certain targets such as MAXIS/MIPS require that some types such as vectors are /// always broken down into scalars in some contexts. This occurs even if the /// vector type is legal. virtual unsigned getVectorTypeBreakdownForCallingConv( @@ -1109,7 +1109,7 @@ class TargetLoweringBase { /// Certain combinations of ABIs, Targets and features require that types /// are legal for some operations and not for other operations. - /// For MIPS all vector types must be passed through the integer register set. + /// For MAXIS/MIPS all vector types must be passed through the integer register set. virtual MVT getRegisterTypeForCallingConv(MVT VT) const { return getRegisterType(VT); } @@ -1119,7 +1119,7 @@ class TargetLoweringBase { return getRegisterType(Context, VT); } - /// Certain targets require unusual breakdowns of certain types. For MIPS, + /// Certain targets require unusual breakdowns of certain types. For MAXIS/MIPS, /// this occurs when a vector type is used, as vector are passed through the /// integer register set. virtual unsigned getNumRegistersForCallingConv(LLVMContext &Context, diff --git a/include/llvm/DebugInfo/CodeView/CodeView.h b/include/llvm/DebugInfo/CodeView/CodeView.h index 1a4f510c..7904dd12 100644 --- a/include/llvm/DebugInfo/CodeView/CodeView.h +++ b/include/llvm/DebugInfo/CodeView/CodeView.h @@ -99,6 +99,15 @@ enum class CPUType : uint16_t { M68020 = 0x22, M68030 = 0x23, M68040 = 0x24, + MAXIS = 0x25, + MAXIS16 = 0x26, + MAXIS32 = 0x27, + MAXIS64 = 0x28, + MAXISI = 0x29, + MAXISII = 0x2a, + MAXISIII = 0x2b, + MAXISIV = 0x2c, + MAXISV = 0x2d, Alpha = 0x30, Alpha21164 = 0x31, Alpha21164A = 0x32, @@ -195,7 +204,8 @@ enum class CallingConvention : uint8_t { ClrCall = 0x16, // clr call Inline = 0x17, // Marker for routines always inlined and thus lacking a convention - NearVector = 0x18 // near left to right push with regs, callee pops stack + NearVector = 0x18, // near left to right push with regs, callee pops stack + MaxisCall = 0x19, // Maxis call }; enum class ClassOptions : uint16_t { diff --git a/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def b/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def index 41c53807..af94553c 100644 --- a/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def +++ b/include/llvm/DebugInfo/CodeView/CodeViewSymbols.def @@ -69,6 +69,8 @@ CV_SYMBOL(S_GTHREAD32_16t , 0x020e) CV_SYMBOL(S_SLINK32 , 0x020f) CV_SYMBOL(S_LPROCMIPS_16t , 0x0300) CV_SYMBOL(S_GPROCMIPS_16t , 0x0301) +CV_SYMBOL(S_LPROCMAXIS_16t, 0x0302) +CV_SYMBOL(S_GPROCMAXIS_16t, 0x0303) CV_SYMBOL(S_PROCREF_ST , 0x0400) CV_SYMBOL(S_DATAREF_ST , 0x0401) CV_SYMBOL(S_ALIGN , 0x0402) @@ -121,6 +123,9 @@ CV_SYMBOL(S_MANMANYREG2_ST, 0x1027) CV_SYMBOL(S_MANTYPREF , 0x1028) CV_SYMBOL(S_UNAMESPACE_ST , 0x1029) +CV_SYMBOL(S_LPROCMAXIS_ST , 0x102a) +CV_SYMBOL(S_GPROCMAXIS_ST , 0x102b) + // End of S_*_ST symbols, which do not appear to be generated by modern // compilers. CV_SYMBOL(S_ST_MAX , 0x1100) @@ -136,6 +141,9 @@ CV_SYMBOL(S_GPROCIA64 , 0x1119) CV_SYMBOL(S_LOCALSLOT , 0x111a) CV_SYMBOL(S_PARAMSLOT , 0x111b) +CV_SYMBOL(S_LPROCMAXIS , 0x111c) +CV_SYMBOL(S_GPROCMAXIS , 0x111d) + // Managed code symbols. CV_SYMBOL(S_MANFRAMEREL , 0x111e) CV_SYMBOL(S_MANREGISTER , 0x111f) @@ -166,6 +174,8 @@ CV_SYMBOL(S_LPROCMIPS_ID , 0x1148) CV_SYMBOL(S_GPROCMIPS_ID , 0x1149) CV_SYMBOL(S_LPROCIA64_ID , 0x114a) CV_SYMBOL(S_GPROCIA64_ID , 0x114b) +CV_SYMBOL(S_LPROCMAXIS_ID , 0x114c) +CV_SYMBOL(S_GPROCMAXIS_ID , 0x114d) CV_SYMBOL(S_DEFRANGE_HLSL , 0x1150) CV_SYMBOL(S_GDATA_HLSL , 0x1151) diff --git a/include/llvm/DebugInfo/PDB/PDBTypes.h b/include/llvm/DebugInfo/PDB/PDBTypes.h index a6c6da37..452b622a 100644 --- a/include/llvm/DebugInfo/PDB/PDBTypes.h +++ b/include/llvm/DebugInfo/PDB/PDBTypes.h @@ -122,6 +122,9 @@ enum class PDB_Machine { Mips16 = 0x266, MipsFpu = 0x366, MipsFpu16 = 0x466, + Maxis16 = 0x267, + MaxisFpu = 0x367, + MaxisFpu16 = 0x467, PowerPC = 0x1F0, PowerPCFP = 0x1F1, R4000 = 0x166, @@ -130,7 +133,8 @@ enum class PDB_Machine { SH4 = 0x1A6, SH5 = 0x1A8, Thumb = 0x1C2, - WceMipsV2 = 0x169 + WceMipsV2 = 0x169, + WceMaxisV2 = 0x16a }; /// These values correspond to the CV_call_e enumeration, and are documented diff --git a/include/llvm/IR/DataLayout.h b/include/llvm/IR/DataLayout.h index a6c71a5a..8f3e89ae 100644 --- a/include/llvm/IR/DataLayout.h +++ b/include/llvm/IR/DataLayout.h @@ -120,6 +120,7 @@ class DataLayout { MM_MachO, MM_WinCOFF, MM_WinCOFFX86, + MM_Maxis, MM_Mips }; ManglingModeT ManglingMode; @@ -268,6 +269,7 @@ class DataLayout { switch (ManglingMode) { case MM_None: case MM_ELF: + case MM_Maxis: case MM_Mips: case MM_WinCOFF: return '\0'; @@ -285,6 +287,8 @@ class DataLayout { case MM_ELF: case MM_WinCOFF: return ".L"; + case MM_Maxis: + return "$"; case MM_Mips: return "$"; case MM_MachO: diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index a2a1f262..c6f26c49 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -974,6 +974,7 @@ include "llvm/IR/IntrinsicsAArch64.td" include "llvm/IR/IntrinsicsXCore.td" include "llvm/IR/IntrinsicsHexagon.td" include "llvm/IR/IntrinsicsNVVM.td" +include "llvm/IR/IntrinsicsMaxis.td" include "llvm/IR/IntrinsicsMips.td" include "llvm/IR/IntrinsicsAMDGPU.td" include "llvm/IR/IntrinsicsBPF.td" diff --git a/include/llvm/IR/IntrinsicsMaxis.td b/include/llvm/IR/IntrinsicsMaxis.td new file mode 100644 index 00000000..b922b95a --- /dev/null +++ b/include/llvm/IR/IntrinsicsMaxis.td @@ -0,0 +1,1771 @@ +//===- IntrinsicsMaxis.td - Defines Maxis intrinsics ---------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines all of the MAXIS-specific intrinsics. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// MAXIS DSP data types +def maxis_v2q15_ty: LLVMType; +def maxis_v4q7_ty: LLVMType; +def maxis_q31_ty: LLVMType; + +let TargetPrefix = "maxis" in { // All intrinsics start with "llvm.maxis.". + +//===----------------------------------------------------------------------===// +// MAXIS DSP Rev 1 + +//===----------------------------------------------------------------------===// +// Addition/subtraction + +def int_maxis_addu_qb : GCCBuiltin<"__builtin_maxis_addu_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_addu_s_qb : GCCBuiltin<"__builtin_maxis_addu_s_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_subu_qb : GCCBuiltin<"__builtin_maxis_subu_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_subu_s_qb : GCCBuiltin<"__builtin_maxis_subu_s_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], [IntrNoMem]>; + +def int_maxis_addq_ph : GCCBuiltin<"__builtin_maxis_addq_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], + [Commutative, IntrNoMem]>; +def int_maxis_addq_s_ph : GCCBuiltin<"__builtin_maxis_addq_s_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], + [Commutative, IntrNoMem]>; +def int_maxis_subq_ph : GCCBuiltin<"__builtin_maxis_subq_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [IntrNoMem]>; +def int_maxis_subq_s_ph : GCCBuiltin<"__builtin_maxis_subq_s_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [IntrNoMem]>; + +def int_maxis_madd: GCCBuiltin<"__builtin_maxis_madd">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem, Commutative]>; +def int_maxis_maddu: GCCBuiltin<"__builtin_maxis_maddu">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem, Commutative]>; + +def int_maxis_msub: GCCBuiltin<"__builtin_maxis_msub">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_msubu: GCCBuiltin<"__builtin_maxis_msubu">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_addq_s_w: GCCBuiltin<"__builtin_maxis_addq_s_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, maxis_q31_ty], [Commutative]>; +def int_maxis_subq_s_w: GCCBuiltin<"__builtin_maxis_subq_s_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, maxis_q31_ty], []>; + +def int_maxis_addsc: GCCBuiltin<"__builtin_maxis_addsc">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [Commutative]>; +def int_maxis_addwc: GCCBuiltin<"__builtin_maxis_addwc">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [Commutative]>; + +def int_maxis_modsub: GCCBuiltin<"__builtin_maxis_modsub">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_raddu_w_qb: GCCBuiltin<"__builtin_maxis_raddu_w_qb">, + Intrinsic<[llvm_i32_ty], [llvm_v4i8_ty], [IntrNoMem]>; + +//===----------------------------------------------------------------------===// +// Absolute value + +def int_maxis_absq_s_ph: GCCBuiltin<"__builtin_maxis_absq_s_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty], []>; +def int_maxis_absq_s_w: GCCBuiltin<"__builtin_maxis_absq_s_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty], []>; + +//===----------------------------------------------------------------------===// +// Precision reduce/expand + +def int_maxis_precrq_qb_ph: GCCBuiltin<"__builtin_maxis_precrq_qb_ph">, + Intrinsic<[llvm_v4i8_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [IntrNoMem]>; +def int_maxis_precrqu_s_qb_ph: GCCBuiltin<"__builtin_maxis_precrqu_s_qb_ph">, + Intrinsic<[llvm_v4i8_ty], [maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_precrq_ph_w: GCCBuiltin<"__builtin_maxis_precrq_ph_w">, + Intrinsic<[maxis_v2q15_ty], [maxis_q31_ty, maxis_q31_ty], [IntrNoMem]>; +def int_maxis_precrq_rs_ph_w: GCCBuiltin<"__builtin_maxis_precrq_rs_ph_w">, + Intrinsic<[maxis_v2q15_ty], [maxis_q31_ty, maxis_q31_ty], []>; +def int_maxis_preceq_w_phl: GCCBuiltin<"__builtin_maxis_preceq_w_phl">, + Intrinsic<[maxis_q31_ty], [maxis_v2q15_ty], [IntrNoMem]>; +def int_maxis_preceq_w_phr: GCCBuiltin<"__builtin_maxis_preceq_w_phr">, + Intrinsic<[maxis_q31_ty], [maxis_v2q15_ty], [IntrNoMem]>; +def int_maxis_precequ_ph_qbl: GCCBuiltin<"__builtin_maxis_precequ_ph_qbl">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_precequ_ph_qbr: GCCBuiltin<"__builtin_maxis_precequ_ph_qbr">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_precequ_ph_qbla: GCCBuiltin<"__builtin_maxis_precequ_ph_qbla">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_precequ_ph_qbra: GCCBuiltin<"__builtin_maxis_precequ_ph_qbra">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_preceu_ph_qbl: GCCBuiltin<"__builtin_maxis_preceu_ph_qbl">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_preceu_ph_qbr: GCCBuiltin<"__builtin_maxis_preceu_ph_qbr">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_preceu_ph_qbla: GCCBuiltin<"__builtin_maxis_preceu_ph_qbla">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_preceu_ph_qbra: GCCBuiltin<"__builtin_maxis_preceu_ph_qbra">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty], [IntrNoMem]>; + +//===----------------------------------------------------------------------===// +// Shift + +def int_maxis_shll_qb: GCCBuiltin<"__builtin_maxis_shll_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_i32_ty], []>; +def int_maxis_shrl_qb: GCCBuiltin<"__builtin_maxis_shrl_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_shll_ph: GCCBuiltin<"__builtin_maxis_shll_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, llvm_i32_ty], []>; +def int_maxis_shll_s_ph: GCCBuiltin<"__builtin_maxis_shll_s_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, llvm_i32_ty], []>; +def int_maxis_shra_ph: GCCBuiltin<"__builtin_maxis_shra_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_shra_r_ph: GCCBuiltin<"__builtin_maxis_shra_r_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_shll_s_w: GCCBuiltin<"__builtin_maxis_shll_s_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, llvm_i32_ty], []>; +def int_maxis_shra_r_w: GCCBuiltin<"__builtin_maxis_shra_r_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_shilo: GCCBuiltin<"__builtin_maxis_shilo">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i32_ty], [IntrNoMem]>; + +//===----------------------------------------------------------------------===// +// Multiplication + +def int_maxis_muleu_s_ph_qbl: GCCBuiltin<"__builtin_maxis_muleu_s_ph_qbl">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty, maxis_v2q15_ty], []>; +def int_maxis_muleu_s_ph_qbr: GCCBuiltin<"__builtin_maxis_muleu_s_ph_qbr">, + Intrinsic<[maxis_v2q15_ty], [llvm_v4i8_ty, maxis_v2q15_ty], []>; +def int_maxis_mulq_rs_ph: GCCBuiltin<"__builtin_maxis_mulq_rs_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [Commutative]>; +def int_maxis_muleq_s_w_phl: GCCBuiltin<"__builtin_maxis_muleq_s_w_phl">, + Intrinsic<[maxis_q31_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [Commutative]>; +def int_maxis_muleq_s_w_phr: GCCBuiltin<"__builtin_maxis_muleq_s_w_phr">, + Intrinsic<[maxis_q31_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [Commutative]>; +def int_maxis_mulsaq_s_w_ph: GCCBuiltin<"__builtin_maxis_mulsaq_s_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_maq_s_w_phl: GCCBuiltin<"__builtin_maxis_maq_s_w_phl">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_maq_s_w_phr: GCCBuiltin<"__builtin_maxis_maq_s_w_phr">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_maq_sa_w_phl: GCCBuiltin<"__builtin_maxis_maq_sa_w_phl">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_maq_sa_w_phr: GCCBuiltin<"__builtin_maxis_maq_sa_w_phr">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_mult: GCCBuiltin<"__builtin_maxis_mult">, + Intrinsic<[llvm_i64_ty], [llvm_i32_ty, llvm_i32_ty], + [IntrNoMem, Commutative]>; +def int_maxis_multu: GCCBuiltin<"__builtin_maxis_multu">, + Intrinsic<[llvm_i64_ty], [llvm_i32_ty, llvm_i32_ty], + [IntrNoMem, Commutative]>; + +//===----------------------------------------------------------------------===// +// Dot product with accumulate/subtract + +def int_maxis_dpau_h_qbl: GCCBuiltin<"__builtin_maxis_dpau_h_qbl">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v4i8_ty, llvm_v4i8_ty], + [IntrNoMem]>; +def int_maxis_dpau_h_qbr: GCCBuiltin<"__builtin_maxis_dpau_h_qbr">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v4i8_ty, llvm_v4i8_ty], + [IntrNoMem]>; +def int_maxis_dpsu_h_qbl: GCCBuiltin<"__builtin_maxis_dpsu_h_qbl">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v4i8_ty, llvm_v4i8_ty], + [IntrNoMem]>; +def int_maxis_dpsu_h_qbr: GCCBuiltin<"__builtin_maxis_dpsu_h_qbr">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v4i8_ty, llvm_v4i8_ty], + [IntrNoMem]>; +def int_maxis_dpaq_s_w_ph: GCCBuiltin<"__builtin_maxis_dpaq_s_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_dpsq_s_w_ph: GCCBuiltin<"__builtin_maxis_dpsq_s_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_dpaq_sa_l_w: GCCBuiltin<"__builtin_maxis_dpaq_sa_l_w">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_q31_ty, maxis_q31_ty], []>; +def int_maxis_dpsq_sa_l_w: GCCBuiltin<"__builtin_maxis_dpsq_sa_l_w">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_q31_ty, maxis_q31_ty], []>; + +//===----------------------------------------------------------------------===// +// Comparison + +def int_maxis_cmpu_eq_qb: GCCBuiltin<"__builtin_maxis_cmpu_eq_qb">, + Intrinsic<[], [llvm_v4i8_ty, llvm_v4i8_ty], [Commutative]>; +def int_maxis_cmpu_lt_qb: GCCBuiltin<"__builtin_maxis_cmpu_lt_qb">, + Intrinsic<[], [llvm_v4i8_ty, llvm_v4i8_ty], []>; +def int_maxis_cmpu_le_qb: GCCBuiltin<"__builtin_maxis_cmpu_le_qb">, + Intrinsic<[], [llvm_v4i8_ty, llvm_v4i8_ty], []>; +def int_maxis_cmpgu_eq_qb: GCCBuiltin<"__builtin_maxis_cmpgu_eq_qb">, + Intrinsic<[llvm_i32_ty], [llvm_v4i8_ty, llvm_v4i8_ty], [Commutative]>; +def int_maxis_cmpgu_lt_qb: GCCBuiltin<"__builtin_maxis_cmpgu_lt_qb">, + Intrinsic<[llvm_i32_ty], [llvm_v4i8_ty, llvm_v4i8_ty], []>; +def int_maxis_cmpgu_le_qb: GCCBuiltin<"__builtin_maxis_cmpgu_le_qb">, + Intrinsic<[llvm_i32_ty], [llvm_v4i8_ty, llvm_v4i8_ty], []>; +def int_maxis_cmp_eq_ph: GCCBuiltin<"__builtin_maxis_cmp_eq_ph">, + Intrinsic<[], [maxis_v2q15_ty, maxis_v2q15_ty], [Commutative]>; +def int_maxis_cmp_lt_ph: GCCBuiltin<"__builtin_maxis_cmp_lt_ph">, + Intrinsic<[], [maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_cmp_le_ph: GCCBuiltin<"__builtin_maxis_cmp_le_ph">, + Intrinsic<[], [maxis_v2q15_ty, maxis_v2q15_ty], []>; + +//===----------------------------------------------------------------------===// +// Extracting + +def int_maxis_extr_s_h: GCCBuiltin<"__builtin_maxis_extr_s_h">, + Intrinsic<[llvm_i32_ty], [llvm_i64_ty, llvm_i32_ty], []>; +def int_maxis_extr_w: GCCBuiltin<"__builtin_maxis_extr_w">, + Intrinsic<[llvm_i32_ty], [llvm_i64_ty, llvm_i32_ty], []>; +def int_maxis_extr_rs_w: GCCBuiltin<"__builtin_maxis_extr_rs_w">, + Intrinsic<[llvm_i32_ty], [llvm_i64_ty, llvm_i32_ty], []>; +def int_maxis_extr_r_w: GCCBuiltin<"__builtin_maxis_extr_r_w">, + Intrinsic<[llvm_i32_ty], [llvm_i64_ty, llvm_i32_ty], []>; +def int_maxis_extp: GCCBuiltin<"__builtin_maxis_extp">, + Intrinsic<[llvm_i32_ty], [llvm_i64_ty, llvm_i32_ty], []>; +def int_maxis_extpdp: GCCBuiltin<"__builtin_maxis_extpdp">, + Intrinsic<[llvm_i32_ty], [llvm_i64_ty, llvm_i32_ty], []>; + +//===----------------------------------------------------------------------===// +// Misc + +def int_maxis_wrdsp: GCCBuiltin<"__builtin_maxis_wrdsp">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; +def int_maxis_rddsp: GCCBuiltin<"__builtin_maxis_rddsp">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty], [IntrReadMem]>; + +def int_maxis_insv: GCCBuiltin<"__builtin_maxis_insv">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty], [IntrReadMem]>; +def int_maxis_bitrev: GCCBuiltin<"__builtin_maxis_bitrev">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_packrl_ph: GCCBuiltin<"__builtin_maxis_packrl_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [IntrNoMem]>; + +def int_maxis_repl_qb: GCCBuiltin<"__builtin_maxis_repl_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_i32_ty], [IntrNoMem]>; +def int_maxis_repl_ph: GCCBuiltin<"__builtin_maxis_repl_ph">, + Intrinsic<[maxis_v2q15_ty], [llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_pick_qb: GCCBuiltin<"__builtin_maxis_pick_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], [IntrReadMem]>; +def int_maxis_pick_ph: GCCBuiltin<"__builtin_maxis_pick_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [IntrReadMem]>; + +def int_maxis_mthlip: GCCBuiltin<"__builtin_maxis_mthlip">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i32_ty], []>; + +def int_maxis_bposge32: GCCBuiltin<"__builtin_maxis_bposge32">, + Intrinsic<[llvm_i32_ty], [], [IntrReadMem]>; + +def int_maxis_lbux: GCCBuiltin<"__builtin_maxis_lbux">, + Intrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_i32_ty], [IntrReadMem, IntrArgMemOnly]>; +def int_maxis_lhx: GCCBuiltin<"__builtin_maxis_lhx">, + Intrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_i32_ty], [IntrReadMem, IntrArgMemOnly]>; +def int_maxis_lwx: GCCBuiltin<"__builtin_maxis_lwx">, + Intrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_i32_ty], [IntrReadMem, IntrArgMemOnly]>; + +//===----------------------------------------------------------------------===// +// MAXIS DSP Rev 2 + +def int_maxis_absq_s_qb: GCCBuiltin<"__builtin_maxis_absq_s_qb">, + Intrinsic<[maxis_v4q7_ty], [maxis_v4q7_ty], []>; + +def int_maxis_addqh_ph: GCCBuiltin<"__builtin_maxis_addqh_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], + [IntrNoMem, Commutative]>; +def int_maxis_addqh_r_ph: GCCBuiltin<"__builtin_maxis_addqh_r_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], + [IntrNoMem, Commutative]>; +def int_maxis_addqh_w: GCCBuiltin<"__builtin_maxis_addqh_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, maxis_q31_ty], + [IntrNoMem, Commutative]>; +def int_maxis_addqh_r_w: GCCBuiltin<"__builtin_maxis_addqh_r_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, maxis_q31_ty], + [IntrNoMem, Commutative]>; + +def int_maxis_addu_ph: GCCBuiltin<"__builtin_maxis_addu_ph">, + Intrinsic<[llvm_v2i16_ty], [llvm_v2i16_ty, llvm_v2i16_ty], [Commutative]>; +def int_maxis_addu_s_ph: GCCBuiltin<"__builtin_maxis_addu_s_ph">, + Intrinsic<[llvm_v2i16_ty], [llvm_v2i16_ty, llvm_v2i16_ty], [Commutative]>; + +def int_maxis_adduh_qb: GCCBuiltin<"__builtin_maxis_adduh_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], + [IntrNoMem, Commutative]>; +def int_maxis_adduh_r_qb: GCCBuiltin<"__builtin_maxis_adduh_r_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], + [IntrNoMem, Commutative]>; + +def int_maxis_append: GCCBuiltin<"__builtin_maxis_append">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_balign: GCCBuiltin<"__builtin_maxis_balign">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_cmpgdu_eq_qb: GCCBuiltin<"__builtin_maxis_cmpgdu_eq_qb">, + Intrinsic<[llvm_i32_ty], [llvm_v4i8_ty, llvm_v4i8_ty], [Commutative]>; +def int_maxis_cmpgdu_lt_qb: GCCBuiltin<"__builtin_maxis_cmpgdu_lt_qb">, + Intrinsic<[llvm_i32_ty], [llvm_v4i8_ty, llvm_v4i8_ty], []>; +def int_maxis_cmpgdu_le_qb: GCCBuiltin<"__builtin_maxis_cmpgdu_le_qb">, + Intrinsic<[llvm_i32_ty], [llvm_v4i8_ty, llvm_v4i8_ty], []>; + +def int_maxis_dpa_w_ph: GCCBuiltin<"__builtin_maxis_dpa_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v2i16_ty, llvm_v2i16_ty], + [IntrNoMem]>; +def int_maxis_dps_w_ph: GCCBuiltin<"__builtin_maxis_dps_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v2i16_ty, llvm_v2i16_ty], + [IntrNoMem]>; + +def int_maxis_dpaqx_s_w_ph: GCCBuiltin<"__builtin_maxis_dpaqx_s_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_dpaqx_sa_w_ph: GCCBuiltin<"__builtin_maxis_dpaqx_sa_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_dpax_w_ph: GCCBuiltin<"__builtin_maxis_dpax_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v2i16_ty, llvm_v2i16_ty], + [IntrNoMem]>; +def int_maxis_dpsx_w_ph: GCCBuiltin<"__builtin_maxis_dpsx_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v2i16_ty, llvm_v2i16_ty], + [IntrNoMem]>; +def int_maxis_dpsqx_s_w_ph: GCCBuiltin<"__builtin_maxis_dpsqx_s_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; +def int_maxis_dpsqx_sa_w_ph: GCCBuiltin<"__builtin_maxis_dpsqx_sa_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, maxis_v2q15_ty, maxis_v2q15_ty], []>; + +def int_maxis_mul_ph: GCCBuiltin<"__builtin_maxis_mul_ph">, + Intrinsic<[llvm_v2i16_ty], [llvm_v2i16_ty, llvm_v2i16_ty], [Commutative]>; +def int_maxis_mul_s_ph: GCCBuiltin<"__builtin_maxis_mul_s_ph">, + Intrinsic<[llvm_v2i16_ty], [llvm_v2i16_ty, llvm_v2i16_ty], [Commutative]>; + +def int_maxis_mulq_rs_w: GCCBuiltin<"__builtin_maxis_mulq_rs_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, maxis_q31_ty], [Commutative]>; +def int_maxis_mulq_s_ph: GCCBuiltin<"__builtin_maxis_mulq_s_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [Commutative]>; +def int_maxis_mulq_s_w: GCCBuiltin<"__builtin_maxis_mulq_s_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, maxis_q31_ty], [Commutative]>; +def int_maxis_mulsa_w_ph: GCCBuiltin<"__builtin_maxis_mulsa_w_ph">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_v2i16_ty, llvm_v2i16_ty], + [IntrNoMem]>; + +def int_maxis_precr_qb_ph: GCCBuiltin<"__builtin_maxis_precr_qb_ph">, + Intrinsic<[llvm_v4i8_ty], [llvm_v2i16_ty, llvm_v2i16_ty], []>; +def int_maxis_precr_sra_ph_w: GCCBuiltin<"__builtin_maxis_precr_sra_ph_w">, + Intrinsic<[llvm_v2i16_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_precr_sra_r_ph_w: GCCBuiltin<"__builtin_maxis_precr_sra_r_ph_w">, + Intrinsic<[llvm_v2i16_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_prepend: GCCBuiltin<"__builtin_maxis_prepend">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_shra_qb: GCCBuiltin<"__builtin_maxis_shra_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_shra_r_qb: GCCBuiltin<"__builtin_maxis_shra_r_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_shrl_ph: GCCBuiltin<"__builtin_maxis_shrl_ph">, + Intrinsic<[llvm_v2i16_ty], [llvm_v2i16_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_subqh_ph: GCCBuiltin<"__builtin_maxis_subqh_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [IntrNoMem]>; +def int_maxis_subqh_r_ph: GCCBuiltin<"__builtin_maxis_subqh_r_ph">, + Intrinsic<[maxis_v2q15_ty], [maxis_v2q15_ty, maxis_v2q15_ty], [IntrNoMem]>; +def int_maxis_subqh_w: GCCBuiltin<"__builtin_maxis_subqh_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, maxis_q31_ty], [IntrNoMem]>; +def int_maxis_subqh_r_w: GCCBuiltin<"__builtin_maxis_subqh_r_w">, + Intrinsic<[maxis_q31_ty], [maxis_q31_ty, maxis_q31_ty], [IntrNoMem]>; + +def int_maxis_subu_ph: GCCBuiltin<"__builtin_maxis_subu_ph">, + Intrinsic<[llvm_v2i16_ty], [llvm_v2i16_ty, llvm_v2i16_ty], []>; +def int_maxis_subu_s_ph: GCCBuiltin<"__builtin_maxis_subu_s_ph">, + Intrinsic<[llvm_v2i16_ty], [llvm_v2i16_ty, llvm_v2i16_ty], []>; + +def int_maxis_subuh_qb: GCCBuiltin<"__builtin_maxis_subuh_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], [IntrNoMem]>; +def int_maxis_subuh_r_qb: GCCBuiltin<"__builtin_maxis_subuh_r_qb">, + Intrinsic<[llvm_v4i8_ty], [llvm_v4i8_ty, llvm_v4i8_ty], [IntrNoMem]>; + +//===----------------------------------------------------------------------===// +// MAXIS MSA + +//===----------------------------------------------------------------------===// +// Addition/subtraction + +def int_maxis_add_a_b : GCCBuiltin<"__builtin_msa_add_a_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_add_a_h : GCCBuiltin<"__builtin_msa_add_a_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_add_a_w : GCCBuiltin<"__builtin_msa_add_a_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_add_a_d : GCCBuiltin<"__builtin_msa_add_a_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_adds_a_b : GCCBuiltin<"__builtin_msa_adds_a_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_a_h : GCCBuiltin<"__builtin_msa_adds_a_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_a_w : GCCBuiltin<"__builtin_msa_adds_a_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_a_d : GCCBuiltin<"__builtin_msa_adds_a_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_adds_s_b : GCCBuiltin<"__builtin_msa_adds_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_s_h : GCCBuiltin<"__builtin_msa_adds_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_s_w : GCCBuiltin<"__builtin_msa_adds_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_s_d : GCCBuiltin<"__builtin_msa_adds_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_adds_u_b : GCCBuiltin<"__builtin_msa_adds_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_u_h : GCCBuiltin<"__builtin_msa_adds_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_u_w : GCCBuiltin<"__builtin_msa_adds_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_adds_u_d : GCCBuiltin<"__builtin_msa_adds_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_addv_b : GCCBuiltin<"__builtin_msa_addv_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_addv_h : GCCBuiltin<"__builtin_msa_addv_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_addv_w : GCCBuiltin<"__builtin_msa_addv_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_addv_d : GCCBuiltin<"__builtin_msa_addv_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_addvi_b : GCCBuiltin<"__builtin_msa_addvi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_addvi_h : GCCBuiltin<"__builtin_msa_addvi_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_addvi_w : GCCBuiltin<"__builtin_msa_addvi_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_addvi_d : GCCBuiltin<"__builtin_msa_addvi_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_and_v : GCCBuiltin<"__builtin_msa_and_v">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; + +def int_maxis_andi_b : GCCBuiltin<"__builtin_msa_andi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_asub_s_b : GCCBuiltin<"__builtin_msa_asub_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_asub_s_h : GCCBuiltin<"__builtin_msa_asub_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_asub_s_w : GCCBuiltin<"__builtin_msa_asub_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_asub_s_d : GCCBuiltin<"__builtin_msa_asub_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_asub_u_b : GCCBuiltin<"__builtin_msa_asub_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_asub_u_h : GCCBuiltin<"__builtin_msa_asub_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_asub_u_w : GCCBuiltin<"__builtin_msa_asub_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_asub_u_d : GCCBuiltin<"__builtin_msa_asub_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_ave_s_b : GCCBuiltin<"__builtin_msa_ave_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_ave_s_h : GCCBuiltin<"__builtin_msa_ave_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_ave_s_w : GCCBuiltin<"__builtin_msa_ave_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_ave_s_d : GCCBuiltin<"__builtin_msa_ave_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_ave_u_b : GCCBuiltin<"__builtin_msa_ave_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_ave_u_h : GCCBuiltin<"__builtin_msa_ave_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_ave_u_w : GCCBuiltin<"__builtin_msa_ave_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_ave_u_d : GCCBuiltin<"__builtin_msa_ave_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_aver_s_b : GCCBuiltin<"__builtin_msa_aver_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_aver_s_h : GCCBuiltin<"__builtin_msa_aver_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_aver_s_w : GCCBuiltin<"__builtin_msa_aver_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_aver_s_d : GCCBuiltin<"__builtin_msa_aver_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_aver_u_b : GCCBuiltin<"__builtin_msa_aver_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], + [Commutative, IntrNoMem]>; +def int_maxis_aver_u_h : GCCBuiltin<"__builtin_msa_aver_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], + [Commutative, IntrNoMem]>; +def int_maxis_aver_u_w : GCCBuiltin<"__builtin_msa_aver_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], + [Commutative, IntrNoMem]>; +def int_maxis_aver_u_d : GCCBuiltin<"__builtin_msa_aver_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], + [Commutative, IntrNoMem]>; + +def int_maxis_bclr_b : GCCBuiltin<"__builtin_msa_bclr_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_bclr_h : GCCBuiltin<"__builtin_msa_bclr_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_bclr_w : GCCBuiltin<"__builtin_msa_bclr_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_bclr_d : GCCBuiltin<"__builtin_msa_bclr_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_bclri_b : GCCBuiltin<"__builtin_msa_bclri_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bclri_h : GCCBuiltin<"__builtin_msa_bclri_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bclri_w : GCCBuiltin<"__builtin_msa_bclri_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bclri_d : GCCBuiltin<"__builtin_msa_bclri_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_binsl_b : GCCBuiltin<"__builtin_msa_binsl_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_binsl_h : GCCBuiltin<"__builtin_msa_binsl_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_binsl_w : GCCBuiltin<"__builtin_msa_binsl_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; +def int_maxis_binsl_d : GCCBuiltin<"__builtin_msa_binsl_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_v2i64_ty], + [IntrNoMem]>; + +def int_maxis_binsli_b : GCCBuiltin<"__builtin_msa_binsli_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_binsli_h : GCCBuiltin<"__builtin_msa_binsli_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_binsli_w : GCCBuiltin<"__builtin_msa_binsli_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_binsli_d : GCCBuiltin<"__builtin_msa_binsli_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_binsr_b : GCCBuiltin<"__builtin_msa_binsr_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_binsr_h : GCCBuiltin<"__builtin_msa_binsr_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_binsr_w : GCCBuiltin<"__builtin_msa_binsr_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; +def int_maxis_binsr_d : GCCBuiltin<"__builtin_msa_binsr_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_v2i64_ty], + [IntrNoMem]>; + +def int_maxis_binsri_b : GCCBuiltin<"__builtin_msa_binsri_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_binsri_h : GCCBuiltin<"__builtin_msa_binsri_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_binsri_w : GCCBuiltin<"__builtin_msa_binsri_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_binsri_d : GCCBuiltin<"__builtin_msa_binsri_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_bmnz_v : GCCBuiltin<"__builtin_msa_bmnz_v">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; + +def int_maxis_bmnzi_b : GCCBuiltin<"__builtin_msa_bmnzi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_bmz_v : GCCBuiltin<"__builtin_msa_bmz_v">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; + +def int_maxis_bmzi_b : GCCBuiltin<"__builtin_msa_bmzi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_bneg_b : GCCBuiltin<"__builtin_msa_bneg_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_bneg_h : GCCBuiltin<"__builtin_msa_bneg_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_bneg_w : GCCBuiltin<"__builtin_msa_bneg_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_bneg_d : GCCBuiltin<"__builtin_msa_bneg_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_bnegi_b : GCCBuiltin<"__builtin_msa_bnegi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bnegi_h : GCCBuiltin<"__builtin_msa_bnegi_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bnegi_w : GCCBuiltin<"__builtin_msa_bnegi_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bnegi_d : GCCBuiltin<"__builtin_msa_bnegi_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_bnz_b : GCCBuiltin<"__builtin_msa_bnz_b">, + Intrinsic<[llvm_i32_ty], [llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_bnz_h : GCCBuiltin<"__builtin_msa_bnz_h">, + Intrinsic<[llvm_i32_ty], [llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_bnz_w : GCCBuiltin<"__builtin_msa_bnz_w">, + Intrinsic<[llvm_i32_ty], [llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_bnz_d : GCCBuiltin<"__builtin_msa_bnz_d">, + Intrinsic<[llvm_i32_ty], [llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_bnz_v : GCCBuiltin<"__builtin_msa_bnz_v">, + Intrinsic<[llvm_i32_ty], [llvm_v16i8_ty], [IntrNoMem]>; + +def int_maxis_bsel_v : GCCBuiltin<"__builtin_msa_bsel_v">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; + +def int_maxis_bseli_b : GCCBuiltin<"__builtin_msa_bseli_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_bset_b : GCCBuiltin<"__builtin_msa_bset_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_bset_h : GCCBuiltin<"__builtin_msa_bset_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_bset_w : GCCBuiltin<"__builtin_msa_bset_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_bset_d : GCCBuiltin<"__builtin_msa_bset_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_bseti_b : GCCBuiltin<"__builtin_msa_bseti_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bseti_h : GCCBuiltin<"__builtin_msa_bseti_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bseti_w : GCCBuiltin<"__builtin_msa_bseti_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_bseti_d : GCCBuiltin<"__builtin_msa_bseti_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_bz_b : GCCBuiltin<"__builtin_msa_bz_b">, + Intrinsic<[llvm_i32_ty], [llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_bz_h : GCCBuiltin<"__builtin_msa_bz_h">, + Intrinsic<[llvm_i32_ty], [llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_bz_w : GCCBuiltin<"__builtin_msa_bz_w">, + Intrinsic<[llvm_i32_ty], [llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_bz_d : GCCBuiltin<"__builtin_msa_bz_d">, + Intrinsic<[llvm_i32_ty], [llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_bz_v : GCCBuiltin<"__builtin_msa_bz_v">, + Intrinsic<[llvm_i32_ty], [llvm_v16i8_ty], [IntrNoMem]>; + +def int_maxis_ceq_b : GCCBuiltin<"__builtin_msa_ceq_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_ceq_h : GCCBuiltin<"__builtin_msa_ceq_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_ceq_w : GCCBuiltin<"__builtin_msa_ceq_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_ceq_d : GCCBuiltin<"__builtin_msa_ceq_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_ceqi_b : GCCBuiltin<"__builtin_msa_ceqi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_ceqi_h : GCCBuiltin<"__builtin_msa_ceqi_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_ceqi_w : GCCBuiltin<"__builtin_msa_ceqi_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_ceqi_d : GCCBuiltin<"__builtin_msa_ceqi_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_cfcmsa : GCCBuiltin<"__builtin_msa_cfcmsa">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty], []>; + +def int_maxis_cle_s_b : GCCBuiltin<"__builtin_msa_cle_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_cle_s_h : GCCBuiltin<"__builtin_msa_cle_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_cle_s_w : GCCBuiltin<"__builtin_msa_cle_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_cle_s_d : GCCBuiltin<"__builtin_msa_cle_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_cle_u_b : GCCBuiltin<"__builtin_msa_cle_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_cle_u_h : GCCBuiltin<"__builtin_msa_cle_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_cle_u_w : GCCBuiltin<"__builtin_msa_cle_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_cle_u_d : GCCBuiltin<"__builtin_msa_cle_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_clei_s_b : GCCBuiltin<"__builtin_msa_clei_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clei_s_h : GCCBuiltin<"__builtin_msa_clei_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clei_s_w : GCCBuiltin<"__builtin_msa_clei_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clei_s_d : GCCBuiltin<"__builtin_msa_clei_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_clei_u_b : GCCBuiltin<"__builtin_msa_clei_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clei_u_h : GCCBuiltin<"__builtin_msa_clei_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clei_u_w : GCCBuiltin<"__builtin_msa_clei_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clei_u_d : GCCBuiltin<"__builtin_msa_clei_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_clt_s_b : GCCBuiltin<"__builtin_msa_clt_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_clt_s_h : GCCBuiltin<"__builtin_msa_clt_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_clt_s_w : GCCBuiltin<"__builtin_msa_clt_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_clt_s_d : GCCBuiltin<"__builtin_msa_clt_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_clt_u_b : GCCBuiltin<"__builtin_msa_clt_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_clt_u_h : GCCBuiltin<"__builtin_msa_clt_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_clt_u_w : GCCBuiltin<"__builtin_msa_clt_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_clt_u_d : GCCBuiltin<"__builtin_msa_clt_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_clti_s_b : GCCBuiltin<"__builtin_msa_clti_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clti_s_h : GCCBuiltin<"__builtin_msa_clti_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clti_s_w : GCCBuiltin<"__builtin_msa_clti_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clti_s_d : GCCBuiltin<"__builtin_msa_clti_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_clti_u_b : GCCBuiltin<"__builtin_msa_clti_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clti_u_h : GCCBuiltin<"__builtin_msa_clti_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clti_u_w : GCCBuiltin<"__builtin_msa_clti_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_clti_u_d : GCCBuiltin<"__builtin_msa_clti_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_copy_s_b : GCCBuiltin<"__builtin_msa_copy_s_b">, + Intrinsic<[llvm_i32_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_copy_s_h : GCCBuiltin<"__builtin_msa_copy_s_h">, + Intrinsic<[llvm_i32_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_copy_s_w : GCCBuiltin<"__builtin_msa_copy_s_w">, + Intrinsic<[llvm_i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_copy_s_d : GCCBuiltin<"__builtin_msa_copy_s_d">, + Intrinsic<[llvm_i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_copy_u_b : GCCBuiltin<"__builtin_msa_copy_u_b">, + Intrinsic<[llvm_i32_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_copy_u_h : GCCBuiltin<"__builtin_msa_copy_u_h">, + Intrinsic<[llvm_i32_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_copy_u_w : GCCBuiltin<"__builtin_msa_copy_u_w">, + Intrinsic<[llvm_i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_copy_u_d : GCCBuiltin<"__builtin_msa_copy_u_d">, + Intrinsic<[llvm_i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_ctcmsa : GCCBuiltin<"__builtin_msa_ctcmsa">, + Intrinsic<[], [llvm_i32_ty, llvm_i32_ty], []>; + +def int_maxis_div_s_b : GCCBuiltin<"__builtin_msa_div_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_div_s_h : GCCBuiltin<"__builtin_msa_div_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_div_s_w : GCCBuiltin<"__builtin_msa_div_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_div_s_d : GCCBuiltin<"__builtin_msa_div_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_div_u_b : GCCBuiltin<"__builtin_msa_div_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_div_u_h : GCCBuiltin<"__builtin_msa_div_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_div_u_w : GCCBuiltin<"__builtin_msa_div_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_div_u_d : GCCBuiltin<"__builtin_msa_div_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +// This instruction is part of the MSA spec but it does not share the +// __builtin_msa prefix because it operates on GP registers. +def int_maxis_dlsa : GCCBuiltin<"__builtin_maxis_dlsa">, + Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_dotp_s_h : GCCBuiltin<"__builtin_msa_dotp_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_dotp_s_w : GCCBuiltin<"__builtin_msa_dotp_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_dotp_s_d : GCCBuiltin<"__builtin_msa_dotp_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_dotp_u_h : GCCBuiltin<"__builtin_msa_dotp_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_dotp_u_w : GCCBuiltin<"__builtin_msa_dotp_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_dotp_u_d : GCCBuiltin<"__builtin_msa_dotp_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_dpadd_s_h : GCCBuiltin<"__builtin_msa_dpadd_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_dpadd_s_w : GCCBuiltin<"__builtin_msa_dpadd_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_dpadd_s_d : GCCBuiltin<"__builtin_msa_dpadd_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; + +def int_maxis_dpadd_u_h : GCCBuiltin<"__builtin_msa_dpadd_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_dpadd_u_w : GCCBuiltin<"__builtin_msa_dpadd_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_dpadd_u_d : GCCBuiltin<"__builtin_msa_dpadd_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; + +def int_maxis_dpsub_s_h : GCCBuiltin<"__builtin_msa_dpsub_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_dpsub_s_w : GCCBuiltin<"__builtin_msa_dpsub_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_dpsub_s_d : GCCBuiltin<"__builtin_msa_dpsub_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; + +def int_maxis_dpsub_u_h : GCCBuiltin<"__builtin_msa_dpsub_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_dpsub_u_w : GCCBuiltin<"__builtin_msa_dpsub_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_dpsub_u_d : GCCBuiltin<"__builtin_msa_dpsub_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; + +def int_maxis_fadd_w : GCCBuiltin<"__builtin_msa_fadd_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fadd_d : GCCBuiltin<"__builtin_msa_fadd_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcaf_w : GCCBuiltin<"__builtin_msa_fcaf_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcaf_d : GCCBuiltin<"__builtin_msa_fcaf_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fceq_w : GCCBuiltin<"__builtin_msa_fceq_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fceq_d : GCCBuiltin<"__builtin_msa_fceq_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcle_w : GCCBuiltin<"__builtin_msa_fcle_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcle_d : GCCBuiltin<"__builtin_msa_fcle_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fclt_w : GCCBuiltin<"__builtin_msa_fclt_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fclt_d : GCCBuiltin<"__builtin_msa_fclt_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fclass_w : GCCBuiltin<"__builtin_msa_fclass_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fclass_d : GCCBuiltin<"__builtin_msa_fclass_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcne_w : GCCBuiltin<"__builtin_msa_fcne_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcne_d : GCCBuiltin<"__builtin_msa_fcne_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcor_w : GCCBuiltin<"__builtin_msa_fcor_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcor_d : GCCBuiltin<"__builtin_msa_fcor_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcueq_w : GCCBuiltin<"__builtin_msa_fcueq_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcueq_d : GCCBuiltin<"__builtin_msa_fcueq_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcule_w : GCCBuiltin<"__builtin_msa_fcule_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcule_d : GCCBuiltin<"__builtin_msa_fcule_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcult_w : GCCBuiltin<"__builtin_msa_fcult_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcult_d : GCCBuiltin<"__builtin_msa_fcult_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcun_w : GCCBuiltin<"__builtin_msa_fcun_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcun_d : GCCBuiltin<"__builtin_msa_fcun_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fcune_w : GCCBuiltin<"__builtin_msa_fcune_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fcune_d : GCCBuiltin<"__builtin_msa_fcune_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fdiv_w : GCCBuiltin<"__builtin_msa_fdiv_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fdiv_d : GCCBuiltin<"__builtin_msa_fdiv_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fexdo_h : GCCBuiltin<"__builtin_msa_fexdo_h">, + Intrinsic<[llvm_v8f16_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fexdo_w : GCCBuiltin<"__builtin_msa_fexdo_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fexp2_w : GCCBuiltin<"__builtin_msa_fexp2_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_fexp2_d : GCCBuiltin<"__builtin_msa_fexp2_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_fexupl_w : GCCBuiltin<"__builtin_msa_fexupl_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v8f16_ty], [IntrNoMem]>; +def int_maxis_fexupl_d : GCCBuiltin<"__builtin_msa_fexupl_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v4f32_ty], [IntrNoMem]>; + +def int_maxis_fexupr_w : GCCBuiltin<"__builtin_msa_fexupr_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v8f16_ty], [IntrNoMem]>; +def int_maxis_fexupr_d : GCCBuiltin<"__builtin_msa_fexupr_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v4f32_ty], [IntrNoMem]>; + +def int_maxis_ffint_s_w : GCCBuiltin<"__builtin_msa_ffint_s_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_ffint_s_d : GCCBuiltin<"__builtin_msa_ffint_s_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_ffint_u_w : GCCBuiltin<"__builtin_msa_ffint_u_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_ffint_u_d : GCCBuiltin<"__builtin_msa_ffint_u_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_ffql_w : GCCBuiltin<"__builtin_msa_ffql_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_ffql_d : GCCBuiltin<"__builtin_msa_ffql_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_ffqr_w : GCCBuiltin<"__builtin_msa_ffqr_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_ffqr_d : GCCBuiltin<"__builtin_msa_ffqr_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_fill_b : GCCBuiltin<"__builtin_msa_fill_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_i32_ty], [IntrNoMem]>; +def int_maxis_fill_h : GCCBuiltin<"__builtin_msa_fill_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_i32_ty], [IntrNoMem]>; +def int_maxis_fill_w : GCCBuiltin<"__builtin_msa_fill_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_i32_ty], [IntrNoMem]>; +def int_maxis_fill_d : GCCBuiltin<"__builtin_msa_fill_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_i64_ty], [IntrNoMem]>; + +def int_maxis_flog2_w : GCCBuiltin<"__builtin_msa_flog2_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_flog2_d : GCCBuiltin<"__builtin_msa_flog2_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fmadd_w : GCCBuiltin<"__builtin_msa_fmadd_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty, llvm_v4f32_ty], + [IntrNoMem]>; +def int_maxis_fmadd_d : GCCBuiltin<"__builtin_msa_fmadd_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty, llvm_v2f64_ty], + [IntrNoMem]>; + +def int_maxis_fmax_w : GCCBuiltin<"__builtin_msa_fmax_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fmax_d : GCCBuiltin<"__builtin_msa_fmax_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fmax_a_w : GCCBuiltin<"__builtin_msa_fmax_a_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fmax_a_d : GCCBuiltin<"__builtin_msa_fmax_a_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fmin_w : GCCBuiltin<"__builtin_msa_fmin_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fmin_d : GCCBuiltin<"__builtin_msa_fmin_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fmin_a_w : GCCBuiltin<"__builtin_msa_fmin_a_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fmin_a_d : GCCBuiltin<"__builtin_msa_fmin_a_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fmsub_w : GCCBuiltin<"__builtin_msa_fmsub_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty, llvm_v4f32_ty], + [IntrNoMem]>; +def int_maxis_fmsub_d : GCCBuiltin<"__builtin_msa_fmsub_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty, llvm_v2f64_ty], + [IntrNoMem]>; + +def int_maxis_fmul_w : GCCBuiltin<"__builtin_msa_fmul_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fmul_d : GCCBuiltin<"__builtin_msa_fmul_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_frint_w : GCCBuiltin<"__builtin_msa_frint_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_frint_d : GCCBuiltin<"__builtin_msa_frint_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_frcp_w : GCCBuiltin<"__builtin_msa_frcp_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_frcp_d : GCCBuiltin<"__builtin_msa_frcp_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_frsqrt_w : GCCBuiltin<"__builtin_msa_frsqrt_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_frsqrt_d : GCCBuiltin<"__builtin_msa_frsqrt_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsaf_w : GCCBuiltin<"__builtin_msa_fsaf_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsaf_d : GCCBuiltin<"__builtin_msa_fsaf_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fseq_w : GCCBuiltin<"__builtin_msa_fseq_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fseq_d : GCCBuiltin<"__builtin_msa_fseq_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsle_w : GCCBuiltin<"__builtin_msa_fsle_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsle_d : GCCBuiltin<"__builtin_msa_fsle_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fslt_w : GCCBuiltin<"__builtin_msa_fslt_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fslt_d : GCCBuiltin<"__builtin_msa_fslt_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsne_w : GCCBuiltin<"__builtin_msa_fsne_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsne_d : GCCBuiltin<"__builtin_msa_fsne_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsor_w : GCCBuiltin<"__builtin_msa_fsor_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsor_d : GCCBuiltin<"__builtin_msa_fsor_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsqrt_w : GCCBuiltin<"__builtin_msa_fsqrt_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsqrt_d : GCCBuiltin<"__builtin_msa_fsqrt_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsub_w : GCCBuiltin<"__builtin_msa_fsub_w">, + Intrinsic<[llvm_v4f32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsub_d : GCCBuiltin<"__builtin_msa_fsub_d">, + Intrinsic<[llvm_v2f64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsueq_w : GCCBuiltin<"__builtin_msa_fsueq_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsueq_d : GCCBuiltin<"__builtin_msa_fsueq_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsule_w : GCCBuiltin<"__builtin_msa_fsule_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsule_d : GCCBuiltin<"__builtin_msa_fsule_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsult_w : GCCBuiltin<"__builtin_msa_fsult_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsult_d : GCCBuiltin<"__builtin_msa_fsult_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsun_w : GCCBuiltin<"__builtin_msa_fsun_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsun_d : GCCBuiltin<"__builtin_msa_fsun_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_fsune_w : GCCBuiltin<"__builtin_msa_fsune_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_fsune_d : GCCBuiltin<"__builtin_msa_fsune_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_ftint_s_w : GCCBuiltin<"__builtin_msa_ftint_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_ftint_s_d : GCCBuiltin<"__builtin_msa_ftint_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_ftint_u_w : GCCBuiltin<"__builtin_msa_ftint_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_ftint_u_d : GCCBuiltin<"__builtin_msa_ftint_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_ftq_h : GCCBuiltin<"__builtin_msa_ftq_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v4f32_ty, llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_ftq_w : GCCBuiltin<"__builtin_msa_ftq_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v2f64_ty, llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_ftrunc_s_w : GCCBuiltin<"__builtin_msa_ftrunc_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_ftrunc_s_d : GCCBuiltin<"__builtin_msa_ftrunc_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_ftrunc_u_w : GCCBuiltin<"__builtin_msa_ftrunc_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4f32_ty], [IntrNoMem]>; +def int_maxis_ftrunc_u_d : GCCBuiltin<"__builtin_msa_ftrunc_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2f64_ty], [IntrNoMem]>; + +def int_maxis_hadd_s_h : GCCBuiltin<"__builtin_msa_hadd_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_hadd_s_w : GCCBuiltin<"__builtin_msa_hadd_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_hadd_s_d : GCCBuiltin<"__builtin_msa_hadd_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_hadd_u_h : GCCBuiltin<"__builtin_msa_hadd_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_hadd_u_w : GCCBuiltin<"__builtin_msa_hadd_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_hadd_u_d : GCCBuiltin<"__builtin_msa_hadd_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_hsub_s_h : GCCBuiltin<"__builtin_msa_hsub_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_hsub_s_w : GCCBuiltin<"__builtin_msa_hsub_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_hsub_s_d : GCCBuiltin<"__builtin_msa_hsub_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_hsub_u_h : GCCBuiltin<"__builtin_msa_hsub_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_hsub_u_w : GCCBuiltin<"__builtin_msa_hsub_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_hsub_u_d : GCCBuiltin<"__builtin_msa_hsub_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_ilvev_b : GCCBuiltin<"__builtin_msa_ilvev_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_ilvev_h : GCCBuiltin<"__builtin_msa_ilvev_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_ilvev_w : GCCBuiltin<"__builtin_msa_ilvev_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_ilvev_d : GCCBuiltin<"__builtin_msa_ilvev_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_ilvl_b : GCCBuiltin<"__builtin_msa_ilvl_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_ilvl_h : GCCBuiltin<"__builtin_msa_ilvl_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_ilvl_w : GCCBuiltin<"__builtin_msa_ilvl_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_ilvl_d : GCCBuiltin<"__builtin_msa_ilvl_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_ilvod_b : GCCBuiltin<"__builtin_msa_ilvod_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_ilvod_h : GCCBuiltin<"__builtin_msa_ilvod_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_ilvod_w : GCCBuiltin<"__builtin_msa_ilvod_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_ilvod_d : GCCBuiltin<"__builtin_msa_ilvod_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_ilvr_b : GCCBuiltin<"__builtin_msa_ilvr_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_ilvr_h : GCCBuiltin<"__builtin_msa_ilvr_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_ilvr_w : GCCBuiltin<"__builtin_msa_ilvr_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_ilvr_d : GCCBuiltin<"__builtin_msa_ilvr_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_insert_b : GCCBuiltin<"__builtin_msa_insert_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_insert_h : GCCBuiltin<"__builtin_msa_insert_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_insert_w : GCCBuiltin<"__builtin_msa_insert_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_insert_d : GCCBuiltin<"__builtin_msa_insert_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty, llvm_i64_ty], + [IntrNoMem]>; + +def int_maxis_insve_b : GCCBuiltin<"__builtin_msa_insve_b">, + Intrinsic<[llvm_v16i8_ty], + [llvm_v16i8_ty, llvm_i32_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_insve_h : GCCBuiltin<"__builtin_msa_insve_h">, + Intrinsic<[llvm_v8i16_ty], + [llvm_v8i16_ty, llvm_i32_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_insve_w : GCCBuiltin<"__builtin_msa_insve_w">, + Intrinsic<[llvm_v4i32_ty], + [llvm_v4i32_ty, llvm_i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; +def int_maxis_insve_d : GCCBuiltin<"__builtin_msa_insve_d">, + Intrinsic<[llvm_v2i64_ty], + [llvm_v2i64_ty, llvm_i32_ty, llvm_v2i64_ty], + [IntrNoMem]>; + +def int_maxis_ld_b : GCCBuiltin<"__builtin_msa_ld_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_ptr_ty, llvm_i32_ty], + [IntrReadMem, IntrArgMemOnly]>; +def int_maxis_ld_h : GCCBuiltin<"__builtin_msa_ld_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_ptr_ty, llvm_i32_ty], + [IntrReadMem, IntrArgMemOnly]>; +def int_maxis_ld_w : GCCBuiltin<"__builtin_msa_ld_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_ptr_ty, llvm_i32_ty], + [IntrReadMem, IntrArgMemOnly]>; +def int_maxis_ld_d : GCCBuiltin<"__builtin_msa_ld_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_ptr_ty, llvm_i32_ty], + [IntrReadMem, IntrArgMemOnly]>; + +def int_maxis_ldi_b : GCCBuiltin<"__builtin_msa_ldi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_i32_ty], [IntrNoMem]>; +def int_maxis_ldi_h : GCCBuiltin<"__builtin_msa_ldi_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_i32_ty], [IntrNoMem]>; +def int_maxis_ldi_w : GCCBuiltin<"__builtin_msa_ldi_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_i32_ty], [IntrNoMem]>; +def int_maxis_ldi_d : GCCBuiltin<"__builtin_msa_ldi_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_i32_ty], [IntrNoMem]>; + +// This instruction is part of the MSA spec but it does not share the +// __builtin_msa prefix because it operates on the GPR registers. +def int_maxis_lsa : GCCBuiltin<"__builtin_maxis_lsa">, + Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_madd_q_h : GCCBuiltin<"__builtin_msa_madd_q_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_madd_q_w : GCCBuiltin<"__builtin_msa_madd_q_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; + +def int_maxis_maddr_q_h : GCCBuiltin<"__builtin_msa_maddr_q_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_maddr_q_w : GCCBuiltin<"__builtin_msa_maddr_q_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; + +def int_maxis_maddv_b : GCCBuiltin<"__builtin_msa_maddv_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_maddv_h : GCCBuiltin<"__builtin_msa_maddv_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_maddv_w : GCCBuiltin<"__builtin_msa_maddv_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; +def int_maxis_maddv_d : GCCBuiltin<"__builtin_msa_maddv_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_v2i64_ty], + [IntrNoMem]>; + +def int_maxis_max_a_b : GCCBuiltin<"__builtin_msa_max_a_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_max_a_h : GCCBuiltin<"__builtin_msa_max_a_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_max_a_w : GCCBuiltin<"__builtin_msa_max_a_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_max_a_d : GCCBuiltin<"__builtin_msa_max_a_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_max_s_b : GCCBuiltin<"__builtin_msa_max_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_max_s_h : GCCBuiltin<"__builtin_msa_max_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_max_s_w : GCCBuiltin<"__builtin_msa_max_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_max_s_d : GCCBuiltin<"__builtin_msa_max_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_max_u_b : GCCBuiltin<"__builtin_msa_max_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_max_u_h : GCCBuiltin<"__builtin_msa_max_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_max_u_w : GCCBuiltin<"__builtin_msa_max_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_max_u_d : GCCBuiltin<"__builtin_msa_max_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_maxi_s_b : GCCBuiltin<"__builtin_msa_maxi_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_maxi_s_h : GCCBuiltin<"__builtin_msa_maxi_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_maxi_s_w : GCCBuiltin<"__builtin_msa_maxi_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_maxi_s_d : GCCBuiltin<"__builtin_msa_maxi_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_maxi_u_b : GCCBuiltin<"__builtin_msa_maxi_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_maxi_u_h : GCCBuiltin<"__builtin_msa_maxi_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_maxi_u_w : GCCBuiltin<"__builtin_msa_maxi_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_maxi_u_d : GCCBuiltin<"__builtin_msa_maxi_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_min_a_b : GCCBuiltin<"__builtin_msa_min_a_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_min_a_h : GCCBuiltin<"__builtin_msa_min_a_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_min_a_w : GCCBuiltin<"__builtin_msa_min_a_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_min_a_d : GCCBuiltin<"__builtin_msa_min_a_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_min_s_b : GCCBuiltin<"__builtin_msa_min_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_min_s_h : GCCBuiltin<"__builtin_msa_min_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_min_s_w : GCCBuiltin<"__builtin_msa_min_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_min_s_d : GCCBuiltin<"__builtin_msa_min_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_min_u_b : GCCBuiltin<"__builtin_msa_min_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_min_u_h : GCCBuiltin<"__builtin_msa_min_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_min_u_w : GCCBuiltin<"__builtin_msa_min_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_min_u_d : GCCBuiltin<"__builtin_msa_min_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_mini_s_b : GCCBuiltin<"__builtin_msa_mini_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_mini_s_h : GCCBuiltin<"__builtin_msa_mini_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_mini_s_w : GCCBuiltin<"__builtin_msa_mini_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_mini_s_d : GCCBuiltin<"__builtin_msa_mini_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_mini_u_b : GCCBuiltin<"__builtin_msa_mini_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_mini_u_h : GCCBuiltin<"__builtin_msa_mini_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_mini_u_w : GCCBuiltin<"__builtin_msa_mini_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_mini_u_d : GCCBuiltin<"__builtin_msa_mini_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_mod_s_b : GCCBuiltin<"__builtin_msa_mod_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_mod_s_h : GCCBuiltin<"__builtin_msa_mod_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_mod_s_w : GCCBuiltin<"__builtin_msa_mod_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_mod_s_d : GCCBuiltin<"__builtin_msa_mod_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_mod_u_b : GCCBuiltin<"__builtin_msa_mod_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_mod_u_h : GCCBuiltin<"__builtin_msa_mod_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_mod_u_w : GCCBuiltin<"__builtin_msa_mod_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_mod_u_d : GCCBuiltin<"__builtin_msa_mod_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_move_v : GCCBuiltin<"__builtin_msa_move_v">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty], [IntrNoMem]>; + +def int_maxis_msub_q_h : GCCBuiltin<"__builtin_msa_msub_q_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_msub_q_w : GCCBuiltin<"__builtin_msa_msub_q_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; + +def int_maxis_msubr_q_h : GCCBuiltin<"__builtin_msa_msubr_q_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_msubr_q_w : GCCBuiltin<"__builtin_msa_msubr_q_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; + +def int_maxis_msubv_b : GCCBuiltin<"__builtin_msa_msubv_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_msubv_h : GCCBuiltin<"__builtin_msa_msubv_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_msubv_w : GCCBuiltin<"__builtin_msa_msubv_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; +def int_maxis_msubv_d : GCCBuiltin<"__builtin_msa_msubv_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_v2i64_ty], + [IntrNoMem]>; + +def int_maxis_mul_q_h : GCCBuiltin<"__builtin_msa_mul_q_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_mul_q_w : GCCBuiltin<"__builtin_msa_mul_q_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_mulr_q_h : GCCBuiltin<"__builtin_msa_mulr_q_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_mulr_q_w : GCCBuiltin<"__builtin_msa_mulr_q_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; + +def int_maxis_mulv_b : GCCBuiltin<"__builtin_msa_mulv_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_mulv_h : GCCBuiltin<"__builtin_msa_mulv_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_mulv_w : GCCBuiltin<"__builtin_msa_mulv_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_mulv_d : GCCBuiltin<"__builtin_msa_mulv_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_nloc_b : GCCBuiltin<"__builtin_msa_nloc_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_nloc_h : GCCBuiltin<"__builtin_msa_nloc_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_nloc_w : GCCBuiltin<"__builtin_msa_nloc_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_nloc_d : GCCBuiltin<"__builtin_msa_nloc_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_nlzc_b : GCCBuiltin<"__builtin_msa_nlzc_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_nlzc_h : GCCBuiltin<"__builtin_msa_nlzc_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_nlzc_w : GCCBuiltin<"__builtin_msa_nlzc_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_nlzc_d : GCCBuiltin<"__builtin_msa_nlzc_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_nor_v : GCCBuiltin<"__builtin_msa_nor_v">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; + +def int_maxis_nori_b : GCCBuiltin<"__builtin_msa_nori_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_or_v : GCCBuiltin<"__builtin_msa_or_v">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; + +def int_maxis_ori_b : GCCBuiltin<"__builtin_msa_ori_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_pckev_b : GCCBuiltin<"__builtin_msa_pckev_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_pckev_h : GCCBuiltin<"__builtin_msa_pckev_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_pckev_w : GCCBuiltin<"__builtin_msa_pckev_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_pckev_d : GCCBuiltin<"__builtin_msa_pckev_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_pckod_b : GCCBuiltin<"__builtin_msa_pckod_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_pckod_h : GCCBuiltin<"__builtin_msa_pckod_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_pckod_w : GCCBuiltin<"__builtin_msa_pckod_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_pckod_d : GCCBuiltin<"__builtin_msa_pckod_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_pcnt_b : GCCBuiltin<"__builtin_msa_pcnt_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_pcnt_h : GCCBuiltin<"__builtin_msa_pcnt_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_pcnt_w : GCCBuiltin<"__builtin_msa_pcnt_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_pcnt_d : GCCBuiltin<"__builtin_msa_pcnt_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_sat_s_b : GCCBuiltin<"__builtin_msa_sat_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sat_s_h : GCCBuiltin<"__builtin_msa_sat_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sat_s_w : GCCBuiltin<"__builtin_msa_sat_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sat_s_d : GCCBuiltin<"__builtin_msa_sat_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_sat_u_b : GCCBuiltin<"__builtin_msa_sat_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sat_u_h : GCCBuiltin<"__builtin_msa_sat_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sat_u_w : GCCBuiltin<"__builtin_msa_sat_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sat_u_d : GCCBuiltin<"__builtin_msa_sat_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_shf_b : GCCBuiltin<"__builtin_msa_shf_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_shf_h : GCCBuiltin<"__builtin_msa_shf_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_shf_w : GCCBuiltin<"__builtin_msa_shf_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_sld_b : GCCBuiltin<"__builtin_msa_sld_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sld_h : GCCBuiltin<"__builtin_msa_sld_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sld_w : GCCBuiltin<"__builtin_msa_sld_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_sld_d : GCCBuiltin<"__builtin_msa_sld_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_sldi_b : GCCBuiltin<"__builtin_msa_sldi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_sldi_h : GCCBuiltin<"__builtin_msa_sldi_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_sldi_w : GCCBuiltin<"__builtin_msa_sldi_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_i32_ty], + [IntrNoMem]>; +def int_maxis_sldi_d : GCCBuiltin<"__builtin_msa_sldi_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_i32_ty], + [IntrNoMem]>; + +def int_maxis_sll_b : GCCBuiltin<"__builtin_msa_sll_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_sll_h : GCCBuiltin<"__builtin_msa_sll_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_sll_w : GCCBuiltin<"__builtin_msa_sll_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_sll_d : GCCBuiltin<"__builtin_msa_sll_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_slli_b : GCCBuiltin<"__builtin_msa_slli_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_slli_h : GCCBuiltin<"__builtin_msa_slli_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_slli_w : GCCBuiltin<"__builtin_msa_slli_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_slli_d : GCCBuiltin<"__builtin_msa_slli_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_splat_b : GCCBuiltin<"__builtin_msa_splat_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_splat_h : GCCBuiltin<"__builtin_msa_splat_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_splat_w : GCCBuiltin<"__builtin_msa_splat_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_splat_d : GCCBuiltin<"__builtin_msa_splat_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_splati_b : GCCBuiltin<"__builtin_msa_splati_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_splati_h : GCCBuiltin<"__builtin_msa_splati_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_splati_w : GCCBuiltin<"__builtin_msa_splati_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_splati_d : GCCBuiltin<"__builtin_msa_splati_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_sra_b : GCCBuiltin<"__builtin_msa_sra_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_sra_h : GCCBuiltin<"__builtin_msa_sra_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_sra_w : GCCBuiltin<"__builtin_msa_sra_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_sra_d : GCCBuiltin<"__builtin_msa_sra_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_srai_b : GCCBuiltin<"__builtin_msa_srai_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srai_h : GCCBuiltin<"__builtin_msa_srai_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srai_w : GCCBuiltin<"__builtin_msa_srai_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srai_d : GCCBuiltin<"__builtin_msa_srai_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_srar_b : GCCBuiltin<"__builtin_msa_srar_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_srar_h : GCCBuiltin<"__builtin_msa_srar_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_srar_w : GCCBuiltin<"__builtin_msa_srar_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_srar_d : GCCBuiltin<"__builtin_msa_srar_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_srari_b : GCCBuiltin<"__builtin_msa_srari_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srari_h : GCCBuiltin<"__builtin_msa_srari_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srari_w : GCCBuiltin<"__builtin_msa_srari_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srari_d : GCCBuiltin<"__builtin_msa_srari_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_srl_b : GCCBuiltin<"__builtin_msa_srl_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_srl_h : GCCBuiltin<"__builtin_msa_srl_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_srl_w : GCCBuiltin<"__builtin_msa_srl_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_srl_d : GCCBuiltin<"__builtin_msa_srl_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_srli_b : GCCBuiltin<"__builtin_msa_srli_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srli_h : GCCBuiltin<"__builtin_msa_srli_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srli_w : GCCBuiltin<"__builtin_msa_srli_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srli_d : GCCBuiltin<"__builtin_msa_srli_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_srlr_b : GCCBuiltin<"__builtin_msa_srlr_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_srlr_h : GCCBuiltin<"__builtin_msa_srlr_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_srlr_w : GCCBuiltin<"__builtin_msa_srlr_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_srlr_d : GCCBuiltin<"__builtin_msa_srlr_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_srlri_b : GCCBuiltin<"__builtin_msa_srlri_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srlri_h : GCCBuiltin<"__builtin_msa_srlri_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srlri_w : GCCBuiltin<"__builtin_msa_srlri_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_srlri_d : GCCBuiltin<"__builtin_msa_srlri_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_st_b : GCCBuiltin<"__builtin_msa_st_b">, + Intrinsic<[], [llvm_v16i8_ty, llvm_ptr_ty, llvm_i32_ty], + [IntrArgMemOnly]>; +def int_maxis_st_h : GCCBuiltin<"__builtin_msa_st_h">, + Intrinsic<[], [llvm_v8i16_ty, llvm_ptr_ty, llvm_i32_ty], + [IntrArgMemOnly]>; +def int_maxis_st_w : GCCBuiltin<"__builtin_msa_st_w">, + Intrinsic<[], [llvm_v4i32_ty, llvm_ptr_ty, llvm_i32_ty], + [IntrArgMemOnly]>; +def int_maxis_st_d : GCCBuiltin<"__builtin_msa_st_d">, + Intrinsic<[], [llvm_v2i64_ty, llvm_ptr_ty, llvm_i32_ty], + [IntrArgMemOnly]>; + +def int_maxis_subs_s_b : GCCBuiltin<"__builtin_msa_subs_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_subs_s_h : GCCBuiltin<"__builtin_msa_subs_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_subs_s_w : GCCBuiltin<"__builtin_msa_subs_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_subs_s_d : GCCBuiltin<"__builtin_msa_subs_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_subs_u_b : GCCBuiltin<"__builtin_msa_subs_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_subs_u_h : GCCBuiltin<"__builtin_msa_subs_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_subs_u_w : GCCBuiltin<"__builtin_msa_subs_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_subs_u_d : GCCBuiltin<"__builtin_msa_subs_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_subsus_u_b : GCCBuiltin<"__builtin_msa_subsus_u_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_subsus_u_h : GCCBuiltin<"__builtin_msa_subsus_u_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_subsus_u_w : GCCBuiltin<"__builtin_msa_subsus_u_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_subsus_u_d : GCCBuiltin<"__builtin_msa_subsus_u_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_subsuu_s_b : GCCBuiltin<"__builtin_msa_subsuu_s_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_subsuu_s_h : GCCBuiltin<"__builtin_msa_subsuu_s_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_subsuu_s_w : GCCBuiltin<"__builtin_msa_subsuu_s_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_subsuu_s_d : GCCBuiltin<"__builtin_msa_subsuu_s_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_subv_b : GCCBuiltin<"__builtin_msa_subv_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; +def int_maxis_subv_h : GCCBuiltin<"__builtin_msa_subv_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty], [IntrNoMem]>; +def int_maxis_subv_w : GCCBuiltin<"__builtin_msa_subv_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty], [IntrNoMem]>; +def int_maxis_subv_d : GCCBuiltin<"__builtin_msa_subv_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty], [IntrNoMem]>; + +def int_maxis_subvi_b : GCCBuiltin<"__builtin_msa_subvi_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_subvi_h : GCCBuiltin<"__builtin_msa_subvi_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_subvi_w : GCCBuiltin<"__builtin_msa_subvi_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_i32_ty], [IntrNoMem]>; +def int_maxis_subvi_d : GCCBuiltin<"__builtin_msa_subvi_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_i32_ty], [IntrNoMem]>; + +def int_maxis_vshf_b : GCCBuiltin<"__builtin_msa_vshf_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty, llvm_v16i8_ty], + [IntrNoMem]>; +def int_maxis_vshf_h : GCCBuiltin<"__builtin_msa_vshf_h">, + Intrinsic<[llvm_v8i16_ty], [llvm_v8i16_ty, llvm_v8i16_ty, llvm_v8i16_ty], + [IntrNoMem]>; +def int_maxis_vshf_w : GCCBuiltin<"__builtin_msa_vshf_w">, + Intrinsic<[llvm_v4i32_ty], [llvm_v4i32_ty, llvm_v4i32_ty, llvm_v4i32_ty], + [IntrNoMem]>; +def int_maxis_vshf_d : GCCBuiltin<"__builtin_msa_vshf_d">, + Intrinsic<[llvm_v2i64_ty], [llvm_v2i64_ty, llvm_v2i64_ty, llvm_v2i64_ty], + [IntrNoMem]>; + +def int_maxis_xor_v : GCCBuiltin<"__builtin_msa_xor_v">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_v16i8_ty], [IntrNoMem]>; + +def int_maxis_xori_b : GCCBuiltin<"__builtin_msa_xori_b">, + Intrinsic<[llvm_v16i8_ty], [llvm_v16i8_ty, llvm_i32_ty], [IntrNoMem]>; +} diff --git a/include/llvm/MC/MCAsmInfo.h b/include/llvm/MC/MCAsmInfo.h index c538c46f..c71f63d2 100644 --- a/include/llvm/MC/MCAsmInfo.h +++ b/include/llvm/MC/MCAsmInfo.h @@ -40,6 +40,7 @@ enum class EncodingType { CE, /// Windows CE ARM, PowerPC, SH3, SH4 Itanium, /// Windows x64, Windows Itanium (IA-64) X86, /// Windows x86, uses no CFI, just EH tables + MAXIS = Alpha, MIPS = Alpha, }; @@ -184,18 +185,18 @@ class MCAsmInfo { const char *Data64bitsDirective; /// If non-null, a directive that is used to emit a word which should be - /// relocated as a 64-bit GP-relative offset, e.g. .gpdword on Mips. Defaults + /// relocated as a 64-bit GP-relative offset, e.g. .gpdword on Maxis/Mips. Defaults /// to nullptr. const char *GPRel64Directive = nullptr; /// If non-null, a directive that is used to emit a word which should be - /// relocated as a 32-bit GP-relative offset, e.g. .gpword on Mips or .gprel32 + /// relocated as a 32-bit GP-relative offset, e.g. .gpword on Maxis/Mips or .gprel32 /// on Alpha. Defaults to nullptr. const char *GPRel32Directive = nullptr; /// If non-null, directives that are used to emit a word/dword which should /// be relocated as a 32/64-bit DTP/TP-relative offset, e.g. .dtprelword/ - /// .dtpreldword/.tprelword/.tpreldword on Mips. + /// .dtpreldword/.tprelword/.tpreldword on Maxis/Mips. const char *DTPRel32Directive = nullptr; const char *DTPRel64Directive = nullptr; const char *TPRel32Directive = nullptr; @@ -371,6 +372,10 @@ class MCAsmInfo { // X86_64 ELF. bool RelaxELFRelocations = true; + // If true, then the lexer and expression parser will support %neg(), + // %hi(), and similar unary operators. + bool HasMaxisExpressions = false; + // If true, then the lexer and expression parser will support %neg(), // %hi(), and similar unary operators. bool HasMipsExpressions = false; @@ -616,6 +621,7 @@ class MCAsmInfo { bool canRelaxRelocations() const { return RelaxELFRelocations; } void setRelaxELFRelocations(bool V) { RelaxELFRelocations = V; } + bool hasMaxisExpressions() const { return HasMaxisExpressions; } bool hasMipsExpressions() const { return HasMipsExpressions; } }; diff --git a/include/llvm/MC/MCParser/MCAsmLexer.h b/include/llvm/MC/MCParser/MCAsmLexer.h index 7836ece2..825b6a62 100644 --- a/include/llvm/MC/MCParser/MCAsmLexer.h +++ b/include/llvm/MC/MCParser/MCAsmLexer.h @@ -59,7 +59,7 @@ class AsmToken { Less, LessEqual, LessLess, LessGreater, Greater, GreaterEqual, GreaterGreater, At, - // MIPS unary expression operators such as %neg. + // MAXIS/MIPS unary expression operators such as %neg. PercentCall16, PercentCall_Hi, PercentCall_Lo, PercentDtprel_Hi, PercentDtprel_Lo, PercentGot, PercentGot_Disp, PercentGot_Hi, PercentGot_Lo, PercentGot_Ofst, PercentGot_Page, PercentGottprel, PercentGp_Rel, PercentHi, diff --git a/include/llvm/Object/ELF.h b/include/llvm/Object/ELF.h index 45c98233..26763c36 100644 --- a/include/llvm/Object/ELF.h +++ b/include/llvm/Object/ELF.h @@ -114,6 +114,16 @@ class ELFFile { static Expected create(StringRef Object); + bool isMaxisELF64() const { + return getHeader()->e_machine == ELF::EM_MAXIS && + getHeader()->getFileClass() == ELF::ELFCLASS64; + } + + bool isMaxis64EL() const { + return isMaxisELF64() && + getHeader()->getDataEncoding() == ELF::ELFDATA2LSB; + } + bool isMipsELF64() const { return getHeader()->e_machine == ELF::EM_MIPS && getHeader()->getFileClass() == ELF::ELFCLASS64; @@ -298,15 +308,15 @@ StringRef ELFFile::getRelocationTypeName(uint32_t Type) const { template void ELFFile::getRelocationTypeName(uint32_t Type, SmallVectorImpl &Result) const { - if (!isMipsELF64()) { + if (!isMaxisELF64() && !isMipsELF64()) { StringRef Name = getRelocationTypeName(Type); Result.append(Name.begin(), Name.end()); } else { - // The Mips N64 ABI allows up to three operations to be specified per + // The Maxis/Mips N64 ABI allows up to three operations to be specified per // relocation record. Unfortunately there's no easy way to test for the // presence of N64 ELFs as they have no special flag that identifies them - // as being N64. We can safely assume at the moment that all Mips - // ELFCLASS64 ELFs are N64. New Mips64 ABIs should provide enough + // as being N64. We can safely assume at the moment that all Maxis/Mips + // ELFCLASS64 ELFs are N64. New Maxis64/Mips64 ABIs should provide enough // information to disambiguate between old vs new ABIs. uint8_t Type1 = (Type >> 0) & 0xFF; uint8_t Type2 = (Type >> 8) & 0xFF; @@ -330,7 +340,7 @@ template Expected ELFFile::getRelocationSymbol(const Elf_Rel *Rel, const Elf_Shdr *SymTab) const { - uint32_t Index = Rel->getSymbol(isMips64EL()); + uint32_t Index = isMaxisELF64() ? Rel->getSymbol(isMaxis64EL()) : Rel->getSymbol(isMips64EL()); if (Index == 0) return nullptr; return getEntry(SymTab, Index); diff --git a/include/llvm/Object/ELFObjectFile.h b/include/llvm/Object/ELFObjectFile.h index 40503cb6..91fa842b 100644 --- a/include/llvm/Object/ELFObjectFile.h +++ b/include/llvm/Object/ELFObjectFile.h @@ -73,6 +73,8 @@ class ELFObjectFileBase : public ObjectFile { SubtargetFeatures getFeatures() const override; + SubtargetFeatures getMAXISFeatures() const; + SubtargetFeatures getMIPSFeatures() const; SubtargetFeatures getARMFeatures() const; @@ -454,8 +456,10 @@ uint64_t ELFObjectFile::getSymbolValueImpl(DataRefImpl Symb) const { return Ret; const Elf_Ehdr *Header = EF.getHeader(); - // Clear the ARM/Thumb or microMIPS indicator flag. - if ((Header->e_machine == ELF::EM_ARM || Header->e_machine == ELF::EM_MIPS) && + // Clear the ARM/Thumb or microMAXIS/microMIPS indicator flag. + if ((Header->e_machine == ELF::EM_ARM + || Header->e_machine == ELF::EM_MAXIS + || Header->e_machine == ELF::EM_MIPS) && ESym->getType() == ELF::STT_FUNC) Ret &= ~1; @@ -977,6 +981,8 @@ StringRef ELFObjectFile::getFileFormatName() const { return "ELF32-hexagon"; case ELF::EM_LANAI: return "ELF32-lanai"; + case ELF::EM_MAXIS: + return "ELF32-maxis"; case ELF::EM_MIPS: return "ELF32-mips"; case ELF::EM_PPC: @@ -1009,6 +1015,8 @@ StringRef ELFObjectFile::getFileFormatName() const { return "ELF64-s390"; case ELF::EM_SPARCV9: return "ELF64-sparc"; + case ELF::EM_MAXIS: + return "ELF64-maxis"; case ELF::EM_MIPS: return "ELF64-mips"; case ELF::EM_WEBASSEMBLY: @@ -1044,6 +1052,15 @@ template Triple::ArchType ELFObjectFile::getArch() const { return Triple::hexagon; case ELF::EM_LANAI: return Triple::lanai; + case ELF::EM_MAXIS: + switch (EF.getHeader()->e_ident[ELF::EI_CLASS]) { + case ELF::ELFCLASS32: + return IsLittleEndian ? Triple::maxisel : Triple::maxis; + case ELF::ELFCLASS64: + return IsLittleEndian ? Triple::maxis64el : Triple::maxis64; + default: + report_fatal_error("Invalid ELFCLASS!"); + } case ELF::EM_MIPS: switch (EF.getHeader()->e_ident[ELF::EI_CLASS]) { case ELF::ELFCLASS32: diff --git a/include/llvm/Object/ELFTypes.h b/include/llvm/Object/ELFTypes.h index 83b68854..f071988b 100644 --- a/include/llvm/Object/ELFTypes.h +++ b/include/llvm/Object/ELFTypes.h @@ -381,6 +381,7 @@ struct Elf_Dyn_Impl : Elf_Dyn_Base { uintX_t getPtr() const { return d_un.d_ptr; } }; + template struct Elf_Rel_Impl, false> { LLVM_ELF_IMPORT_TYPES(TargetEndianness, false) @@ -590,6 +591,62 @@ struct Elf_Chdr_Impl> { Elf_Xword ch_addralign; }; +// MAXIS .reginfo section +template +struct Elf_Maxis_RegInfo; + +template +struct Elf_Maxis_RegInfo> { + LLVM_ELF_IMPORT_TYPES(TargetEndianness, false) + Elf_Word ri_gprmask; // bit-mask of used general registers + Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers + Elf_Addr ri_gp_value; // gp register value +}; + +template +struct Elf_Maxis_RegInfo> { + LLVM_ELF_IMPORT_TYPES(TargetEndianness, true) + Elf_Word ri_gprmask; // bit-mask of used general registers + Elf_Word ri_pad; // unused padding field + Elf_Word ri_cprmask[4]; // bit-mask of used co-processor registers + Elf_Addr ri_gp_value; // gp register value +}; + +// .MAXIS.options section +template struct Elf_Maxis_Options { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + uint8_t kind; // Determines interpretation of variable part of descriptor + uint8_t size; // Byte size of descriptor, including this header + Elf_Half section; // Section header index of section affected, + // or 0 for global options + Elf_Word info; // Kind-specific information + + Elf_Maxis_RegInfo &getRegInfo() { + assert(kind == ELF::ODK_REGINFO); + return *reinterpret_cast *>( + (uint8_t *)this + sizeof(Elf_Maxis_Options)); + } + const Elf_Maxis_RegInfo &getRegInfo() const { + return const_cast(this)->getRegInfo(); + } +}; + +// .MAXIS.abiflags section content +template struct Elf_Maxis_ABIFlags { + LLVM_ELF_IMPORT_TYPES_ELFT(ELFT) + Elf_Half version; // Version of the structure + uint8_t isa_level; // ISA level: 1-5, 32, and 64 + uint8_t isa_rev; // ISA revision (0 for MAXIS I - MAXIS V) + uint8_t gpr_size; // General purpose registers size + uint8_t cpr1_size; // Co-processor 1 registers size + uint8_t cpr2_size; // Co-processor 2 registers size + uint8_t fp_abi; // Floating-point ABI flag + Elf_Word isa_ext; // Processor-specific extension + Elf_Word ases; // ASEs flags + Elf_Word flags1; // General flags + Elf_Word flags2; // General flags +}; + // MIPS .reginfo section template struct Elf_Mips_RegInfo; diff --git a/include/llvm/Object/RelocVisitor.h b/include/llvm/Object/RelocVisitor.h index 2d0e938f..d73bb284 100644 --- a/include/llvm/Object/RelocVisitor.h +++ b/include/llvm/Object/RelocVisitor.h @@ -68,6 +68,9 @@ class RelocVisitor { case Triple::bpfel: case Triple::bpfeb: return visitBpf(Rel, R, Value); + case Triple::maxis64el: + case Triple::maxis64: + return visitMaxis64(Rel, R, Value); case Triple::mips64el: case Triple::mips64: return visitMips64(Rel, R, Value); @@ -100,6 +103,9 @@ class RelocVisitor { return visitARM(Rel, R, Value); case Triple::lanai: return visitLanai(Rel, R, Value); + case Triple::maxisel: + case Triple::maxis: + return visitMaxis32(Rel, R, Value); case Triple::mipsel: case Triple::mips: return visitMips32(Rel, R, Value); @@ -163,6 +169,19 @@ class RelocVisitor { return 0; } + uint64_t visitMaxis64(uint32_t Rel, RelocationRef R, uint64_t Value) { + switch (Rel) { + case ELF::R_MAXIS_32: + return (Value + getELFAddend(R)) & 0xFFFFFFFF; + case ELF::R_MAXIS_64: + return Value + getELFAddend(R); + case ELF::R_MAXIS_TLS_DTPREL64: + return Value + getELFAddend(R) - 0x8000; + } + HasError = true; + return 0; + } + uint64_t visitMips64(uint32_t Rel, RelocationRef R, uint64_t Value) { switch (Rel) { case ELF::R_MIPS_32: @@ -261,6 +280,16 @@ class RelocVisitor { return 0; } + uint64_t visitMaxis32(uint32_t Rel, RelocationRef R, uint64_t Value) { + // FIXME: Take in account implicit addends to get correct results. + if (Rel == ELF::R_MAXIS_32) + return Value & 0xFFFFFFFF; + if (Rel == ELF::R_MAXIS_TLS_DTPREL32) + return Value & 0xFFFFFFFF; + HasError = true; + return 0; + } + uint64_t visitMips32(uint32_t Rel, RelocationRef R, uint64_t Value) { // FIXME: Take in account implicit addends to get correct results. if (Rel == ELF::R_MIPS_32) diff --git a/include/llvm/ObjectYAML/ELFYAML.h b/include/llvm/ObjectYAML/ELFYAML.h index 7ba83967..1eca4812 100644 --- a/include/llvm/ObjectYAML/ELFYAML.h +++ b/include/llvm/ObjectYAML/ELFYAML.h @@ -55,6 +55,13 @@ LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_STT) LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_STV) LLVM_YAML_STRONG_TYPEDEF(uint8_t, ELF_STO) +LLVM_YAML_STRONG_TYPEDEF(uint8_t, MAXIS_AFL_REG) +LLVM_YAML_STRONG_TYPEDEF(uint8_t, MAXIS_ABI_FP) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, MAXIS_AFL_EXT) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, MAXIS_AFL_ASE) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, MAXIS_AFL_FLAGS1) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, MAXIS_ISA) + LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_AFL_REG) LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_ABI_FP) LLVM_YAML_STRONG_TYPEDEF(uint32_t, MIPS_AFL_EXT) @@ -113,6 +120,7 @@ struct Section { RawContent, Relocation, NoBits, + MaxisABIFlags, MipsABIFlags }; SectionKind Kind; @@ -177,6 +185,27 @@ struct RelocationSection : Section { } }; +// Represents .MAXIS.abiflags section +struct MaxisABIFlags : Section { + llvm::yaml::Hex16 Version; + MAXIS_ISA ISALevel; + llvm::yaml::Hex8 ISARevision; + MAXIS_AFL_REG GPRSize; + MAXIS_AFL_REG CPR1Size; + MAXIS_AFL_REG CPR2Size; + MAXIS_ABI_FP FpABI; + MAXIS_AFL_EXT ISAExtension; + MAXIS_AFL_ASE ASEs; + MAXIS_AFL_FLAGS1 Flags1; + llvm::yaml::Hex32 Flags2; + + MaxisABIFlags() : Section(SectionKind::MaxisABIFlags) {} + + static bool classof(const Section *S) { + return S->Kind == SectionKind::MaxisABIFlags; + } +}; + // Represents .MIPS.abiflags section struct MipsABIFlags : Section { llvm::yaml::Hex16 Version; @@ -300,6 +329,36 @@ struct ScalarEnumerationTraits { static void enumeration(IO &IO, ELFYAML::ELF_RSS &Value); }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, ELFYAML::MAXIS_AFL_REG &Value); +}; + +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, ELFYAML::MAXIS_ABI_FP &Value); +}; + +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, ELFYAML::MAXIS_AFL_EXT &Value); +}; + +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, ELFYAML::MAXIS_ISA &Value); +}; + +template <> +struct ScalarBitSetTraits { + static void bitset(IO &IO, ELFYAML::MAXIS_AFL_ASE &Value); +}; + +template <> +struct ScalarBitSetTraits { + static void bitset(IO &IO, ELFYAML::MAXIS_AFL_FLAGS1 &Value); +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, ELFYAML::MIPS_AFL_REG &Value); diff --git a/include/llvm/Support/MaxisABIFlags.h b/include/llvm/Support/MaxisABIFlags.h new file mode 100644 index 00000000..d6733bbe --- /dev/null +++ b/include/llvm/Support/MaxisABIFlags.h @@ -0,0 +1,102 @@ +//===--- MaxisABIFlags.h - MAXIS ABI flags ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the constants for the ABI flags structure contained +// in the .MAXIS.abiflags section. +// +// https://dmz-portal.maxis.com/wiki/MAXIS_O32_ABI_-_FR0_and_FR1_Interlinking +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_MAXISABIFLAGS_H +#define LLVM_SUPPORT_MAXISABIFLAGS_H + +namespace llvm { +namespace Maxis { + +// Values for the xxx_size bytes of an ABI flags structure. +enum AFL_REG { + AFL_REG_NONE = 0x00, // No registers + AFL_REG_32 = 0x01, // 32-bit registers + AFL_REG_64 = 0x02, // 64-bit registers + AFL_REG_128 = 0x03 // 128-bit registers +}; + +// Masks for the ases word of an ABI flags structure. +enum AFL_ASE { + AFL_ASE_DSP = 0x00000001, // DSP ASE + AFL_ASE_DSPR2 = 0x00000002, // DSP R2 ASE + AFL_ASE_EVA = 0x00000004, // Enhanced VA Scheme + AFL_ASE_MCU = 0x00000008, // MCU (MicroController) ASE + AFL_ASE_MDMX = 0x00000010, // MDMX ASE + AFL_ASE_MAXIS3D = 0x00000020, // MAXIS-3D ASE + AFL_ASE_MT = 0x00000040, // MT ASE + AFL_ASE_SMARTMAXIS = 0x00000080, // SmartMAXIS ASE + AFL_ASE_VIRT = 0x00000100, // VZ ASE + AFL_ASE_MSA = 0x00000200, // MSA ASE + AFL_ASE_MAXIS16 = 0x00000400, // MAXIS16 ASE + AFL_ASE_MICROMAXIS = 0x00000800, // MICROMAXIS ASE + AFL_ASE_XPA = 0x00001000 // XPA ASE +}; + +// Values for the isa_ext word of an ABI flags structure. +enum AFL_EXT { + AFL_EXT_NONE = 0, // None + AFL_EXT_XLR = 1, // RMI Xlr instruction + AFL_EXT_OCTEON2 = 2, // Cavium Networks Octeon2 + AFL_EXT_OCTEONP = 3, // Cavium Networks OcteonP + AFL_EXT_LOONGSON_3A = 4, // Loongson 3A + AFL_EXT_OCTEON = 5, // Cavium Networks Octeon + AFL_EXT_5900 = 6, // MAXIS R5900 instruction + AFL_EXT_4650 = 7, // MAXIS R4650 instruction + AFL_EXT_4010 = 8, // LSI R4010 instruction + AFL_EXT_4100 = 9, // NEC VR4100 instruction + AFL_EXT_3900 = 10, // Toshiba R3900 instruction + AFL_EXT_10000 = 11, // MAXIS R10000 instruction + AFL_EXT_SB1 = 12, // Broadcom SB-1 instruction + AFL_EXT_4111 = 13, // NEC VR4111/VR4181 instruction + AFL_EXT_4120 = 14, // NEC VR4120 instruction + AFL_EXT_5400 = 15, // NEC VR5400 instruction + AFL_EXT_5500 = 16, // NEC VR5500 instruction + AFL_EXT_LOONGSON_2E = 17, // ST Microelectronics Loongson 2E + AFL_EXT_LOONGSON_2F = 18, // ST Microelectronics Loongson 2F + AFL_EXT_OCTEON3 = 19 // Cavium Networks Octeon3 +}; + +// Values for the flags1 word of an ABI flags structure. +enum AFL_FLAGS1 { AFL_FLAGS1_ODDSPREG = 1 }; + +// MAXIS object attribute tags +enum { + Tag_GNU_MAXIS_ABI_FP = 4, // Floating-point ABI used by this object file + Tag_GNU_MAXIS_ABI_MSA = 8, // MSA ABI used by this object file +}; + +// Values for the fp_abi word of an ABI flags structure +// and for the Tag_GNU_MAXIS_ABI_FP attribute tag. +enum Val_GNU_MAXIS_ABI_FP { + Val_GNU_MAXIS_ABI_FP_ANY = 0, // not tagged + Val_GNU_MAXIS_ABI_FP_DOUBLE = 1, // hard float / -mdouble-float + Val_GNU_MAXIS_ABI_FP_SINGLE = 2, // hard float / -msingle-float + Val_GNU_MAXIS_ABI_FP_SOFT = 3, // soft float + Val_GNU_MAXIS_ABI_FP_OLD_64 = 4, // -maxis32r2 -mfp64 + Val_GNU_MAXIS_ABI_FP_XX = 5, // -mfpxx + Val_GNU_MAXIS_ABI_FP_64 = 6, // -maxis32r2 -mfp64 + Val_GNU_MAXIS_ABI_FP_64A = 7 // -maxis32r2 -mfp64 -mno-odd-spreg +}; + +// Values for the Tag_GNU_MAXIS_ABI_MSA attribute tag. +enum Val_GNU_MAXIS_ABI_MSA { + Val_GNU_MAXIS_ABI_MSA_ANY = 0, // not tagged + Val_GNU_MAXIS_ABI_MSA_128 = 1 // 128-bit MSA +}; +} +} + +#endif diff --git a/include/llvm/Target/TargetSchedule.td b/include/llvm/Target/TargetSchedule.td index 7b00c942..3ab1537d 100644 --- a/include/llvm/Target/TargetSchedule.td +++ b/include/llvm/Target/TargetSchedule.td @@ -101,7 +101,7 @@ class SchedMachineModel { // A processor may only implement part of published ISA, due to either new ISA // extensions, (e.g. Pentium 4 doesn't have AVX) or implementation - // (ARM/MIPS/PowerPC/SPARC soft float cores). + // (ARM/MAXIS/MIPS/PowerPC/SPARC soft float cores). // // For a processor which doesn't support some feature(s), the schedule model // can use: diff --git a/include/llvm/module.modulemap b/include/llvm/module.modulemap index d8b07c4f..dae70c49 100644 --- a/include/llvm/module.modulemap +++ b/include/llvm/module.modulemap @@ -53,6 +53,7 @@ module LLVM_BinaryFormat { textual header "BinaryFormat/ELFRelocs/Hexagon.def" textual header "BinaryFormat/ELFRelocs/i386.def" textual header "BinaryFormat/ELFRelocs/Lanai.def" + textual header "BinaryFormat/ELFRelocs/Maxis.def" textual header "BinaryFormat/ELFRelocs/Mips.def" textual header "BinaryFormat/ELFRelocs/PowerPC64.def" textual header "BinaryFormat/ELFRelocs/PowerPC.def" diff --git a/lib/Analysis/TargetLibraryInfo.cpp b/lib/Analysis/TargetLibraryInfo.cpp index d18246ac..5334b4dc 100644 --- a/lib/Analysis/TargetLibraryInfo.cpp +++ b/lib/Analysis/TargetLibraryInfo.cpp @@ -71,9 +71,11 @@ static void initialize(TargetLibraryInfoImpl &TLI, const Triple &T, ShouldExtI32Param = true; ShouldExtI32Return = true; } - // Mips, on the other hand, needs signext on i32 parameters corresponding + // Maxis/Mips, on the other hand, needs signext on i32 parameters corresponding // to both signed and unsigned ints. - if (T.getArch() == Triple::mips || T.getArch() == Triple::mipsel || + if (T.getArch() == Triple::maxis || T.getArch() == Triple::maxisel || + T.getArch() == Triple::maxis64 || T.getArch() == Triple::maxis64el || + T.getArch() == Triple::mips || T.getArch() == Triple::mipsel || T.getArch() == Triple::mips64 || T.getArch() == Triple::mips64el) { ShouldSignExtI32Param = true; } diff --git a/lib/CodeGen/LiveVariables.cpp b/lib/CodeGen/LiveVariables.cpp index 032dd66a..a27707b8 100644 --- a/lib/CodeGen/LiveVariables.cpp +++ b/lib/CodeGen/LiveVariables.cpp @@ -528,7 +528,7 @@ void LiveVariables::runOnInstr(MachineInstr &MI, UseRegs.push_back(MOReg); } else { assert(MO.isDef()); - // FIXME: We should not remove any dead flags. However the MIPS RDDSP + // FIXME: We should not remove any dead flags. However the MAXIS MIPS RDDSP // instruction needs it at the moment: http://llvm.org/PR27116. if (TargetRegisterInfo::isPhysicalRegister(MOReg) && !MRI->isReserved(MOReg)) diff --git a/lib/CodeGen/MachineBasicBlock.cpp b/lib/CodeGen/MachineBasicBlock.cpp index 209abf34..a505adc4 100644 --- a/lib/CodeGen/MachineBasicBlock.cpp +++ b/lib/CodeGen/MachineBasicBlock.cpp @@ -782,7 +782,7 @@ MachineBasicBlock *MachineBasicBlock::SplitCriticalEdge(MachineBasicBlock *Succ, else if (Indexes) Indexes->insertMBBInMaps(NMBB); - // On some targets like Mips, branches may kill virtual registers. Make sure + // On some targets like Maxis/Mips, branches may kill virtual registers. Make sure // that LiveVariables is properly updated after updateTerminator replaces the // terminators. LiveVariables *LV = P.getAnalysisIfAvailable(); diff --git a/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 3ffc6fa9..3b18a9b8 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -1124,7 +1124,7 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, Elt = ConstantInt::get(*getContext(), NewVal); } // In other cases the element type is illegal and needs to be expanded, for - // example v2i64 on MIPS32. In this case, find the nearest legal type, split + // example v2i64 on MAXIS32/MIPS32. In this case, find the nearest legal type, split // the value into n parts and use a vector type with n-times the elements. // Then bitcast to the type requested. // Legalizing constants too early makes the DAGCombiner's job harder so we @@ -1160,7 +1160,7 @@ SDValue SelectionDAG::getConstant(const ConstantInt &Val, const SDLoc &DL, // vector shuffle in this situation). However, we do not need any code to // perform this reversal because getConstant() is producing a vector // splat. - // This situation occurs in MIPS MSA. + // This situation occurs in MAXIS/MIPS MSA. SmallVector Ops; for (unsigned i = 0, e = VT.getVectorNumElements(); i != e; ++i) diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index 68bbd62e..908aae0d 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -8151,7 +8151,7 @@ TargetLowering::LowerCallTo(TargetLowering::CallLoweringInfo &CLI) const { Args[i].Node.getResNo() + Value); ISD::ArgFlagsTy Flags; - // Certain targets (such as MIPS), may have a different ABI alignment + // Certain targets (such as MAXIS/MIPS), may have a different ABI alignment // for a type depending on the context. Give the target a chance to // specify the alignment it wants. unsigned OriginalAlignment = getABIAlignmentForCallingConv(ArgTy, DL); @@ -8633,7 +8633,7 @@ void SelectionDAGISel::LowerArguments(const Function &F) { Type *ArgTy = VT.getTypeForEVT(*DAG.getContext()); ISD::ArgFlagsTy Flags; - // Certain targets (such as MIPS), may have a different ABI alignment + // Certain targets (such as MAXIS/MIPS), may have a different ABI alignment // for a type depending on the context. Give the target a chance to // specify the alignment it wants. unsigned OriginalAlignment = diff --git a/lib/CodeGen/XRayInstrumentation.cpp b/lib/CodeGen/XRayInstrumentation.cpp index 3d83afcf..f9ec295c 100644 --- a/lib/CodeGen/XRayInstrumentation.cpp +++ b/lib/CodeGen/XRayInstrumentation.cpp @@ -194,6 +194,10 @@ bool XRayInstrumentation::runOnMachineFunction(MachineFunction &MF) { case Triple::ArchType::arm: case Triple::ArchType::thumb: case Triple::ArchType::aarch64: + case Triple::ArchType::maxis: + case Triple::ArchType::maxisel: + case Triple::ArchType::maxis64: + case Triple::ArchType::maxis64el: case Triple::ArchType::mips: case Triple::ArchType::mipsel: case Triple::ArchType::mips64: diff --git a/lib/DebugInfo/CodeView/EnumTables.cpp b/lib/DebugInfo/CodeView/EnumTables.cpp index d8301cab..05685201 100644 --- a/lib/DebugInfo/CodeView/EnumTables.cpp +++ b/lib/DebugInfo/CodeView/EnumTables.cpp @@ -132,6 +132,15 @@ static const EnumEntry CPUTypeNames[] = { CV_ENUM_CLASS_ENT(CPUType, Pentium), CV_ENUM_CLASS_ENT(CPUType, PentiumPro), CV_ENUM_CLASS_ENT(CPUType, Pentium3), + CV_ENUM_CLASS_ENT(CPUType, MAXIS), + CV_ENUM_CLASS_ENT(CPUType, MAXIS16), + CV_ENUM_CLASS_ENT(CPUType, MAXIS32), + CV_ENUM_CLASS_ENT(CPUType, MAXIS64), + CV_ENUM_CLASS_ENT(CPUType, MAXISI), + CV_ENUM_CLASS_ENT(CPUType, MAXISII), + CV_ENUM_CLASS_ENT(CPUType, MAXISIII), + CV_ENUM_CLASS_ENT(CPUType, MAXISIV), + CV_ENUM_CLASS_ENT(CPUType, MAXISV), CV_ENUM_CLASS_ENT(CPUType, MIPS), CV_ENUM_CLASS_ENT(CPUType, MIPS16), CV_ENUM_CLASS_ENT(CPUType, MIPS32), diff --git a/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp b/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp index e7998b87..0ab7201f 100644 --- a/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp +++ b/lib/DebugInfo/CodeView/TypeDumpVisitor.cpp @@ -120,6 +120,7 @@ static const EnumEntry CallingConventions[] = { ENUM_ENTRY(CallingConvention, NearSysCall), ENUM_ENTRY(CallingConvention, FarSysCall), ENUM_ENTRY(CallingConvention, ThisCall), + ENUM_ENTRY(CallingConvention, MaxisCall), ENUM_ENTRY(CallingConvention, MipsCall), ENUM_ENTRY(CallingConvention, Generic), ENUM_ENTRY(CallingConvention, AlphaCall), diff --git a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 3312da67..c14b0ce1 100644 --- a/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -364,6 +364,7 @@ static ArrayRef getOperandTypes() { DECLARE_OP1(DW_CFA_advance_loc1, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_advance_loc2, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_advance_loc4, OT_FactoredCodeOffset); + DECLARE_OP1(DW_CFA_MAXIS_advance_loc8, OT_FactoredCodeOffset); DECLARE_OP1(DW_CFA_MIPS_advance_loc8, OT_FactoredCodeOffset); DECLARE_OP2(DW_CFA_def_cfa, OT_Register, OT_Offset); DECLARE_OP2(DW_CFA_def_cfa_sf, OT_Register, OT_SignedFactDataOffset); diff --git a/lib/DebugInfo/PDB/PDBExtras.cpp b/lib/DebugInfo/PDB/PDBExtras.cpp index ee752cda..693185e4 100644 --- a/lib/DebugInfo/PDB/PDBExtras.cpp +++ b/lib/DebugInfo/PDB/PDBExtras.cpp @@ -57,6 +57,7 @@ raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, NearSysCall, "syscall", OS) CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, FarSysCall , "syscall", OS) CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, ThisCall , "thiscall", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, MaxisCall , "maxiscall", OS) CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, MipsCall , "mipscall", OS) CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, Generic , "genericcall", OS) CASE_OUTPUT_ENUM_CLASS_STR(PDB_CallingConv, AlphaCall , "alphacall", OS) @@ -236,6 +237,9 @@ raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, x86, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Ia64, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, M32R, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Maxis16, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, MaxisFpu, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, MaxisFpu16, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Mips16, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, MipsFpu, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, MipsFpu16, OS) @@ -247,6 +251,7 @@ raw_ostream &llvm::pdb::operator<<(raw_ostream &OS, CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, SH4, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, SH5, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, Thumb, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, WceMaxisV2, OS) CASE_OUTPUT_ENUM_CLASS_NAME(PDB_Machine, WceMipsV2, OS) default: OS << "Unknown"; diff --git a/lib/Demangle/ItaniumDemangle.cpp b/lib/Demangle/ItaniumDemangle.cpp index 9c2258f5..7bcee488 100644 --- a/lib/Demangle/ItaniumDemangle.cpp +++ b/lib/Demangle/ItaniumDemangle.cpp @@ -103,10 +103,10 @@ template <> struct float_data { const char *float_data::spec = "%a"; template <> struct float_data { -#if defined(__mips__) && defined(__mips_n64) || defined(__aarch64__) || \ +#if (defined(__maxis__) && defined(__maxis_n64)) || (defined(__mips__) && defined(__mips_n64)) || defined(__aarch64__) || \ defined(__wasm__) static const size_t mangled_size = 32; -#elif defined(__arm__) || defined(__mips__) || defined(__hexagon__) +#elif defined(__arm__) || defined(__maxis__) || defined(__mips__) || defined(__hexagon__) static const size_t mangled_size = 16; #else static const size_t mangled_size = diff --git a/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt b/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt index 3fa7ee6b..66e24619 100644 --- a/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt +++ b/lib/ExecutionEngine/RuntimeDyld/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_library(LLVMRuntimeDyld RuntimeDyldCOFF.cpp RuntimeDyldELF.cpp RuntimeDyldMachO.cpp + Targets/RuntimeDyldELFMaxis.cpp Targets/RuntimeDyldELFMips.cpp DEPENDS diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index c5e4dfa1..eb894b15 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -182,6 +182,7 @@ RuntimeDyldImpl::loadObjectImpl(const object::ObjectFile &Obj) { // Save information about our target Arch = (Triple::ArchType)Obj.getArch(); IsTargetLittleEndian = Obj.isLittleEndian(); + setMaxisABI(Obj); setMipsABI(Obj); // Compute the memory size required to load all sections to be loaded @@ -878,6 +879,48 @@ uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr, // and stubs for branches Thumb - ARM and ARM - Thumb. writeBytesUnaligned(0xe51ff004, Addr, 4); // ldr pc, [pc, #-4] return Addr + 4; + } else if (IsMaxisO32ABI || IsMaxisN32ABI) { + // 0: 3c190000 lui t9,%hi(addr). + // 4: 27390000 addiu t9,t9,%lo(addr). + // 8: 03200008 jr t9. + // c: 00000000 nop. + const unsigned LuiT9Instr = 0x3c190000, AdduiT9Instr = 0x27390000; + const unsigned NopInstr = 0x0; + unsigned JrT9Instr = 0x03200008; + if ((AbiVariant & ELF::EF_MAXIS_ARCH) == ELF::EF_MAXIS_ARCH_32R6 || + (AbiVariant & ELF::EF_MAXIS_ARCH) == ELF::EF_MAXIS_ARCH_64R6) + JrT9Instr = 0x03200009; + + writeBytesUnaligned(LuiT9Instr, Addr, 4); + writeBytesUnaligned(AdduiT9Instr, Addr + 4, 4); + writeBytesUnaligned(JrT9Instr, Addr + 8, 4); + writeBytesUnaligned(NopInstr, Addr + 12, 4); + return Addr; + } else if (IsMaxisN64ABI) { + // 0: 3c190000 lui t9,%highest(addr). + // 4: 67390000 daddiu t9,t9,%higher(addr). + // 8: 0019CC38 dsll t9,t9,16. + // c: 67390000 daddiu t9,t9,%hi(addr). + // 10: 0019CC38 dsll t9,t9,16. + // 14: 67390000 daddiu t9,t9,%lo(addr). + // 18: 03200008 jr t9. + // 1c: 00000000 nop. + const unsigned LuiT9Instr = 0x3c190000, DaddiuT9Instr = 0x67390000, + DsllT9Instr = 0x19CC38; + const unsigned NopInstr = 0x0; + unsigned JrT9Instr = 0x03200008; + if ((AbiVariant & ELF::EF_MAXIS_ARCH) == ELF::EF_MAXIS_ARCH_64R6) + JrT9Instr = 0x03200009; + + writeBytesUnaligned(LuiT9Instr, Addr, 4); + writeBytesUnaligned(DaddiuT9Instr, Addr + 4, 4); + writeBytesUnaligned(DsllT9Instr, Addr + 8, 4); + writeBytesUnaligned(DaddiuT9Instr, Addr + 12, 4); + writeBytesUnaligned(DsllT9Instr, Addr + 16, 4); + writeBytesUnaligned(DaddiuT9Instr, Addr + 20, 4); + writeBytesUnaligned(JrT9Instr, Addr + 24, 4); + writeBytesUnaligned(NopInstr, Addr + 28, 4); + return Addr; } else if (IsMipsO32ABI || IsMipsN32ABI) { // 0: 3c190000 lui t9,%hi(addr). // 4: 27390000 addiu t9,t9,%lo(addr). diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp index c0047d0c..3b9e7c20 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp @@ -13,6 +13,7 @@ #include "RuntimeDyldELF.h" #include "RuntimeDyldCheckerImpl.h" +#include "Targets/RuntimeDyldELFMaxis.h" #include "Targets/RuntimeDyldELFMips.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" @@ -240,6 +241,11 @@ llvm::RuntimeDyldELF::create(Triple::ArchType Arch, switch (Arch) { default: return make_unique(MemMgr, Resolver); + case Triple::maxis: + case Triple::maxisel: + case Triple::maxis64: + case Triple::maxis64el: + return make_unique(MemMgr, Resolver); case Triple::mips: case Triple::mipsel: case Triple::mips64: @@ -518,6 +524,21 @@ void RuntimeDyldELF::resolveARMRelocation(const SectionEntry &Section, } } +void RuntimeDyldELF::setMaxisABI(const ObjectFile &Obj) { + if (Arch == Triple::UnknownArch || + !StringRef(Triple::getArchTypePrefix(Arch)).equals("maxis")) { + IsMaxisO32ABI = false; + IsMaxisN32ABI = false; + IsMaxisN64ABI = false; + return; + } + unsigned AbiVariant; + Obj.getPlatformFlags(AbiVariant); + IsMaxisO32ABI = AbiVariant & ELF::EF_MAXIS_ABI_O32; + IsMaxisN32ABI = AbiVariant & ELF::EF_MAXIS_ABI2; + IsMaxisN64ABI = Obj.getFileFormatName().equals("ELF64-maxis"); +} + void RuntimeDyldELF::setMipsABI(const ObjectFile &Obj) { if (Arch == Triple::UnknownArch || !StringRef(Triple::getArchTypePrefix(Arch)).equals("mips")) { @@ -951,6 +972,29 @@ void RuntimeDyldELF::processSimpleRelocation(unsigned SectionID, uint64_t Offset addRelocationForSection(RE, Value.SectionID); } +uint32_t RuntimeDyldELF::getMaxisMatchingLoRelocation(uint32_t RelType, + bool IsLocal) const { + switch (RelType) { + case ELF::R_MICROMAXIS_GOT16: + if (IsLocal) + return ELF::R_MICROMAXIS_LO16; + break; + case ELF::R_MICROMAXIS_HI16: + return ELF::R_MICROMAXIS_LO16; + case ELF::R_MAXIS_GOT16: + if (IsLocal) + return ELF::R_MAXIS_LO16; + break; + case ELF::R_MAXIS_HI16: + return ELF::R_MAXIS_LO16; + case ELF::R_MAXIS_PCHI16: + return ELF::R_MAXIS_PCLO16; + default: + break; + } + return ELF::R_MAXIS_NONE; +} + uint32_t RuntimeDyldELF::getMatchingLoRelocation(uint32_t RelType, bool IsLocal) const { switch (RelType) { @@ -1231,6 +1275,184 @@ RuntimeDyldELF::processRelocationRef( } processSimpleRelocation(SectionID, Offset, RelType, Value); } + } else if (IsMaxisO32ABI) { + uint8_t *Placeholder = reinterpret_cast( + computePlaceholderAddress(SectionID, Offset)); + uint32_t Opcode = readBytesUnaligned(Placeholder, 4); + if (RelType == ELF::R_MAXIS_26) { + // This is an Maxis branch relocation, need to use a stub function. + DEBUG(dbgs() << "\t\tThis is a Maxis branch relocation."); + SectionEntry &Section = Sections[SectionID]; + + // Extract the addend from the instruction. + // We shift up by two since the Value will be down shifted again + // when applying the relocation. + uint32_t Addend = (Opcode & 0x03ffffff) << 2; + + Value.Addend += Addend; + + // Look up for existing stub. + StubMap::const_iterator i = Stubs.find(Value); + if (i != Stubs.end()) { + RelocationEntry RE(SectionID, Offset, RelType, i->second); + addRelocationForSection(RE, SectionID); + DEBUG(dbgs() << " Stub function found\n"); + } else { + // Create a new stub function. + DEBUG(dbgs() << " Create a new stub function\n"); + Stubs[Value] = Section.getStubOffset(); + + unsigned AbiVariant; + O.getPlatformFlags(AbiVariant); + + uint8_t *StubTargetAddr = createStubFunction( + Section.getAddressWithOffset(Section.getStubOffset()), AbiVariant); + + // Creating Hi and Lo relocations for the filled stub instructions. + RelocationEntry REHi(SectionID, StubTargetAddr - Section.getAddress(), + ELF::R_MAXIS_HI16, Value.Addend); + RelocationEntry RELo(SectionID, + StubTargetAddr - Section.getAddress() + 4, + ELF::R_MAXIS_LO16, Value.Addend); + + if (Value.SymbolName) { + addRelocationForSymbol(REHi, Value.SymbolName); + addRelocationForSymbol(RELo, Value.SymbolName); + } else { + addRelocationForSection(REHi, Value.SectionID); + addRelocationForSection(RELo, Value.SectionID); + } + + RelocationEntry RE(SectionID, Offset, RelType, Section.getStubOffset()); + addRelocationForSection(RE, SectionID); + Section.advanceStubOffset(getMaxStubSize()); + } + } else if (RelType == ELF::R_MAXIS_HI16 || RelType == ELF::R_MAXIS_PCHI16) { + int64_t Addend = (Opcode & 0x0000ffff) << 16; + RelocationEntry RE(SectionID, Offset, RelType, Addend); + PendingRelocs.push_back(std::make_pair(Value, RE)); + } else if (RelType == ELF::R_MAXIS_LO16 || RelType == ELF::R_MAXIS_PCLO16) { + int64_t Addend = Value.Addend + SignExtend32<16>(Opcode & 0x0000ffff); + for (auto I = PendingRelocs.begin(); I != PendingRelocs.end();) { + const RelocationValueRef &MatchingValue = I->first; + RelocationEntry &Reloc = I->second; + if (MatchingValue == Value && + RelType == getMatchingLoRelocation(Reloc.RelType) && + SectionID == Reloc.SectionID) { + Reloc.Addend += Addend; + if (Value.SymbolName) + addRelocationForSymbol(Reloc, Value.SymbolName); + else + addRelocationForSection(Reloc, Value.SectionID); + I = PendingRelocs.erase(I); + } else + ++I; + } + RelocationEntry RE(SectionID, Offset, RelType, Addend); + if (Value.SymbolName) + addRelocationForSymbol(RE, Value.SymbolName); + else + addRelocationForSection(RE, Value.SectionID); + } else { + if (RelType == ELF::R_MAXIS_32) + Value.Addend += Opcode; + else if (RelType == ELF::R_MAXIS_PC16) + Value.Addend += SignExtend32<18>((Opcode & 0x0000ffff) << 2); + else if (RelType == ELF::R_MAXIS_PC19_S2) + Value.Addend += SignExtend32<21>((Opcode & 0x0007ffff) << 2); + else if (RelType == ELF::R_MAXIS_PC21_S2) + Value.Addend += SignExtend32<23>((Opcode & 0x001fffff) << 2); + else if (RelType == ELF::R_MAXIS_PC26_S2) + Value.Addend += SignExtend32<28>((Opcode & 0x03ffffff) << 2); + processSimpleRelocation(SectionID, Offset, RelType, Value); + } + } else if (IsMaxisN32ABI || IsMaxisN64ABI) { + uint32_t r_type = RelType & 0xff; + RelocationEntry RE(SectionID, Offset, RelType, Value.Addend); + if (r_type == ELF::R_MAXIS_CALL16 || r_type == ELF::R_MAXIS_GOT_PAGE + || r_type == ELF::R_MAXIS_GOT_DISP) { + StringMap::iterator i = GOTSymbolOffsets.find(TargetName); + if (i != GOTSymbolOffsets.end()) + RE.SymOffset = i->second; + else { + RE.SymOffset = allocateGOTEntries(1); + GOTSymbolOffsets[TargetName] = RE.SymOffset; + } + if (Value.SymbolName) + addRelocationForSymbol(RE, Value.SymbolName); + else + addRelocationForSection(RE, Value.SectionID); + } else if (RelType == ELF::R_MAXIS_26) { + // This is an Maxis branch relocation, need to use a stub function. + DEBUG(dbgs() << "\t\tThis is a Maxis branch relocation."); + SectionEntry &Section = Sections[SectionID]; + + // Look up for existing stub. + StubMap::const_iterator i = Stubs.find(Value); + if (i != Stubs.end()) { + RelocationEntry RE(SectionID, Offset, RelType, i->second); + addRelocationForSection(RE, SectionID); + DEBUG(dbgs() << " Stub function found\n"); + } else { + // Create a new stub function. + DEBUG(dbgs() << " Create a new stub function\n"); + Stubs[Value] = Section.getStubOffset(); + + unsigned AbiVariant; + O.getPlatformFlags(AbiVariant); + + uint8_t *StubTargetAddr = createStubFunction( + Section.getAddressWithOffset(Section.getStubOffset()), AbiVariant); + + if (IsMaxisN32ABI) { + // Creating Hi and Lo relocations for the filled stub instructions. + RelocationEntry REHi(SectionID, StubTargetAddr - Section.getAddress(), + ELF::R_MAXIS_HI16, Value.Addend); + RelocationEntry RELo(SectionID, + StubTargetAddr - Section.getAddress() + 4, + ELF::R_MAXIS_LO16, Value.Addend); + if (Value.SymbolName) { + addRelocationForSymbol(REHi, Value.SymbolName); + addRelocationForSymbol(RELo, Value.SymbolName); + } else { + addRelocationForSection(REHi, Value.SectionID); + addRelocationForSection(RELo, Value.SectionID); + } + } else { + // Creating Highest, Higher, Hi and Lo relocations for the filled stub + // instructions. + RelocationEntry REHighest(SectionID, + StubTargetAddr - Section.getAddress(), + ELF::R_MAXIS_HIGHEST, Value.Addend); + RelocationEntry REHigher(SectionID, + StubTargetAddr - Section.getAddress() + 4, + ELF::R_MAXIS_HIGHER, Value.Addend); + RelocationEntry REHi(SectionID, + StubTargetAddr - Section.getAddress() + 12, + ELF::R_MAXIS_HI16, Value.Addend); + RelocationEntry RELo(SectionID, + StubTargetAddr - Section.getAddress() + 20, + ELF::R_MAXIS_LO16, Value.Addend); + if (Value.SymbolName) { + addRelocationForSymbol(REHighest, Value.SymbolName); + addRelocationForSymbol(REHigher, Value.SymbolName); + addRelocationForSymbol(REHi, Value.SymbolName); + addRelocationForSymbol(RELo, Value.SymbolName); + } else { + addRelocationForSection(REHighest, Value.SectionID); + addRelocationForSection(REHigher, Value.SectionID); + addRelocationForSection(REHi, Value.SectionID); + addRelocationForSection(RELo, Value.SectionID); + } + } + RelocationEntry RE(SectionID, Offset, RelType, Section.getStubOffset()); + addRelocationForSection(RE, SectionID); + Section.advanceStubOffset(getMaxStubSize()); + } + } else { + processSimpleRelocation(SectionID, Offset, RelType, Value); + } + } else if (IsMipsO32ABI) { uint8_t *Placeholder = reinterpret_cast( computePlaceholderAddress(SectionID, Offset)); @@ -1727,6 +1949,17 @@ size_t RuntimeDyldELF::getGOTEntrySize() { case Triple::thumb: Result = sizeof(uint32_t); break; + case Triple::maxis: + case Triple::maxisel: + case Triple::maxis64: + case Triple::maxis64el: + if (IsMaxisO32ABI || IsMaxisN32ABI) + Result = sizeof(uint32_t); + else if (IsMaxisN64ABI) + Result = sizeof(uint64_t); + else + llvm_unreachable("Maxis ABI not handled"); + break; case Triple::mips: case Triple::mipsel: case Triple::mips64: @@ -1793,6 +2026,9 @@ RelocationEntry RuntimeDyldELF::computeGOTOffsetRE(uint64_t GOTOffset, Error RuntimeDyldELF::finalizeLoad(const ObjectFile &Obj, ObjSectionToIDMap &SectionMap) { + if (IsMaxisO32ABI) + if (!PendingRelocs.empty()) + return make_error("Can't find matching LO16 reloc"); if (IsMipsO32ABI) if (!PendingRelocs.empty()) return make_error("Can't find matching LO16 reloc"); @@ -1815,7 +2051,20 @@ Error RuntimeDyldELF::finalizeLoad(const ObjectFile &Obj, // For now, initialize all GOT entries to zero. We'll fill them in as // needed when GOT-based relocations are applied. memset(Addr, 0, TotalSize); - if (IsMipsN32ABI || IsMipsN64ABI) { + if (IsMaxisN32ABI || IsMaxisN64ABI) { + // To correctly resolve Maxis GOT relocations, we need a mapping from + // object's sections to GOTs. + for (section_iterator SI = Obj.section_begin(), SE = Obj.section_end(); + SI != SE; ++SI) { + if (SI->relocation_begin() != SI->relocation_end()) { + section_iterator RelocatedSection = SI->getRelocatedSection(); + ObjSectionToIDMap::iterator i = SectionMap.find(*RelocatedSection); + assert (i != SectionMap.end()); + SectionToGOTMap[i->second] = GOTSectionID; + } + } + GOTSymbolOffsets.clear(); + } else if (IsMipsN32ABI || IsMipsN64ABI) { // To correctly resolve Mips GOT relocations, we need a mapping from // object's sections to GOTs. for (section_iterator SI = Obj.section_begin(), SE = Obj.section_end(); diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h index f37bd0bb..478db202 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h @@ -66,9 +66,9 @@ class RuntimeDyldELF : public RuntimeDyldImpl { return 20; // movz; movk; movk; movk; br if (Arch == Triple::arm || Arch == Triple::thumb) return 8; // 32-bit instruction and 32-bit address - else if (IsMipsO32ABI || IsMipsN32ABI) + else if (IsMaxisO32ABI || IsMaxisN32ABI || IsMipsO32ABI || IsMipsN32ABI) return 16; - else if (IsMipsN64ABI) + else if (IsMaxisN64ABI || IsMipsN64ABI) return 32; else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le) return 44; @@ -87,6 +87,8 @@ class RuntimeDyldELF : public RuntimeDyldImpl { return 1; } + void setMaxisABI(const ObjectFile &Obj) override; + void setMipsABI(const ObjectFile &Obj) override; Error findPPC64TOCSection(const ELFObjectFileBase &Obj, @@ -126,6 +128,10 @@ class RuntimeDyldELF : public RuntimeDyldImpl { void processSimpleRelocation(unsigned SectionID, uint64_t Offset, unsigned RelType, RelocationValueRef Value); // Return matching *LO16 relocation (Mips specific) + uint32_t getMaxisMatchingLoRelocation(uint32_t RelType, + bool IsLocal = false) const; + + // Return matching *LO16 relocation (Maxis specific) uint32_t getMatchingLoRelocation(uint32_t RelType, bool IsLocal = false) const; @@ -139,15 +145,15 @@ class RuntimeDyldELF : public RuntimeDyldImpl { protected: // A map from section to a GOT section that has entries for section's GOT - // relocations. (Mips64 specific) + // relocations. (Maxis64/Mips64 specific) DenseMap SectionToGOTMap; private: - // A map to avoid duplicate got entries (Mips64 specific) + // A map to avoid duplicate got entries (Maxis64/Mips64 specific) StringMap GOTSymbolOffsets; // *HI16 relocations will be added for resolving when we find matching - // *LO16 part. (Mips specific) + // *LO16 part. (Maxis/Mips specific) SmallVector, 8> PendingRelocs; // When a module is loaded we save the SectionID of the EH frame section diff --git a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h index e046a850..1b1e48ec 100644 --- a/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h +++ b/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h @@ -293,6 +293,9 @@ class RuntimeDyldImpl { Triple::ArchType Arch; bool IsTargetLittleEndian; + bool IsMaxisO32ABI; + bool IsMaxisN32ABI; + bool IsMaxisN64ABI; bool IsMipsO32ABI; bool IsMipsN32ABI; bool IsMipsN64ABI; @@ -356,12 +359,18 @@ class RuntimeDyldImpl { *(Addr + 7) = Value & 0xFF; } + virtual void setMaxisABI(const ObjectFile &Obj) { + IsMaxisO32ABI = false; + IsMaxisN32ABI = false; + IsMaxisN64ABI = false; + } + virtual void setMipsABI(const ObjectFile &Obj) { IsMipsO32ABI = false; IsMipsN32ABI = false; IsMipsN64ABI = false; } - + /// Endian-aware read Read the least significant Size bytes from Src. uint64_t readBytesUnaligned(uint8_t *Src, unsigned Size) const; diff --git a/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldELFMaxis.cpp b/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldELFMaxis.cpp new file mode 100644 index 00000000..6a31467e --- /dev/null +++ b/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldELFMaxis.cpp @@ -0,0 +1,321 @@ +//===-- RuntimeDyldELFMaxis.cpp ---- ELF/Maxis specific code. -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RuntimeDyldELFMaxis.h" +#include "llvm/BinaryFormat/ELF.h" + +#define DEBUG_TYPE "dyld" + +void RuntimeDyldELFMaxis::resolveRelocation(const RelocationEntry &RE, + uint64_t Value) { + const SectionEntry &Section = Sections[RE.SectionID]; + if (IsMaxisO32ABI) + resolveMAXISO32Relocation(Section, RE.Offset, Value, RE.RelType, RE.Addend); + else if (IsMaxisN32ABI) { + resolveMAXISN32Relocation(Section, RE.Offset, Value, RE.RelType, RE.Addend, + RE.SymOffset, RE.SectionID); + } else if (IsMaxisN64ABI) + resolveMAXISN64Relocation(Section, RE.Offset, Value, RE.RelType, RE.Addend, + RE.SymOffset, RE.SectionID); + else + llvm_unreachable("Maxis ABI not handled"); +} + +uint64_t RuntimeDyldELFMaxis::evaluateRelocation(const RelocationEntry &RE, + uint64_t Value, + uint64_t Addend) { + if (IsMaxisN32ABI) { + const SectionEntry &Section = Sections[RE.SectionID]; + Value = evaluateMAXIS64Relocation(Section, RE.Offset, Value, RE.RelType, + Addend, RE.SymOffset, RE.SectionID); + return Value; + } + llvm_unreachable("Not reachable"); +} + +void RuntimeDyldELFMaxis::applyRelocation(const RelocationEntry &RE, + uint64_t Value) { + if (IsMaxisN32ABI) { + const SectionEntry &Section = Sections[RE.SectionID]; + applyMAXISRelocation(Section.getAddressWithOffset(RE.Offset), Value, + RE.RelType); + return; + } + llvm_unreachable("Not reachable"); +} + +int64_t +RuntimeDyldELFMaxis::evaluateMAXIS32Relocation(const SectionEntry &Section, + uint64_t Offset, uint64_t Value, + uint32_t Type) { + + DEBUG(dbgs() << "evaluateMAXIS32Relocation, LocalAddress: 0x" + << format("%llx", Section.getAddressWithOffset(Offset)) + << " FinalAddress: 0x" + << format("%llx", Section.getLoadAddressWithOffset(Offset)) + << " Value: 0x" << format("%llx", Value) << " Type: 0x" + << format("%x", Type) << "\n"); + + switch (Type) { + default: + llvm_unreachable("Unknown relocation type!"); + return Value; + case ELF::R_MAXIS_32: + return Value; + case ELF::R_MAXIS_26: + return Value >> 2; + case ELF::R_MAXIS_HI16: + // Get the higher 16-bits. Also add 1 if bit 15 is 1. + return (Value + 0x8000) >> 16; + case ELF::R_MAXIS_LO16: + return Value; + case ELF::R_MAXIS_PC32: { + uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return Value - FinalAddress; + } + case ELF::R_MAXIS_PC16: { + uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return (Value - FinalAddress) >> 2; + } + case ELF::R_MAXIS_PC19_S2: { + uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return (Value - (FinalAddress & ~0x3)) >> 2; + } + case ELF::R_MAXIS_PC21_S2: { + uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return (Value - FinalAddress) >> 2; + } + case ELF::R_MAXIS_PC26_S2: { + uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return (Value - FinalAddress) >> 2; + } + case ELF::R_MAXIS_PCHI16: { + uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return (Value - FinalAddress + 0x8000) >> 16; + } + case ELF::R_MAXIS_PCLO16: { + uint32_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return Value - FinalAddress; + } + } +} + +int64_t RuntimeDyldELFMaxis::evaluateMAXIS64Relocation( + const SectionEntry &Section, uint64_t Offset, uint64_t Value, uint32_t Type, + int64_t Addend, uint64_t SymOffset, SID SectionID) { + + DEBUG(dbgs() << "evaluateMAXIS64Relocation, LocalAddress: 0x" + << format("%llx", Section.getAddressWithOffset(Offset)) + << " FinalAddress: 0x" + << format("%llx", Section.getLoadAddressWithOffset(Offset)) + << " Value: 0x" << format("%llx", Value) << " Type: 0x" + << format("%x", Type) << " Addend: 0x" << format("%llx", Addend) + << " Offset: " << format("%llx" PRIx64, Offset) + << " SID: " << format("%d", SectionID) + << " SymOffset: " << format("%x", SymOffset) << "\n"); + + switch (Type) { + default: + llvm_unreachable("Not implemented relocation type!"); + break; + case ELF::R_MAXIS_JALR: + case ELF::R_MAXIS_NONE: + break; + case ELF::R_MAXIS_32: + case ELF::R_MAXIS_64: + return Value + Addend; + case ELF::R_MAXIS_26: + return ((Value + Addend) >> 2) & 0x3ffffff; + case ELF::R_MAXIS_GPREL16: { + uint64_t GOTAddr = getSectionLoadAddress(SectionToGOTMap[SectionID]); + return Value + Addend - (GOTAddr + 0x7ff0); + } + case ELF::R_MAXIS_SUB: + return Value - Addend; + case ELF::R_MAXIS_HI16: + // Get the higher 16-bits. Also add 1 if bit 15 is 1. + return ((Value + Addend + 0x8000) >> 16) & 0xffff; + case ELF::R_MAXIS_LO16: + return (Value + Addend) & 0xffff; + case ELF::R_MAXIS_HIGHER: + return ((Value + Addend + 0x80008000) >> 32) & 0xffff; + case ELF::R_MAXIS_HIGHEST: + return ((Value + Addend + 0x800080008000) >> 48) & 0xffff; + case ELF::R_MAXIS_CALL16: + case ELF::R_MAXIS_GOT_DISP: + case ELF::R_MAXIS_GOT_PAGE: { + uint8_t *LocalGOTAddr = + getSectionAddress(SectionToGOTMap[SectionID]) + SymOffset; + uint64_t GOTEntry = readBytesUnaligned(LocalGOTAddr, getGOTEntrySize()); + + Value += Addend; + if (Type == ELF::R_MAXIS_GOT_PAGE) + Value = (Value + 0x8000) & ~0xffff; + + if (GOTEntry) + assert(GOTEntry == Value && + "GOT entry has two different addresses."); + else + writeBytesUnaligned(Value, LocalGOTAddr, getGOTEntrySize()); + + return (SymOffset - 0x7ff0) & 0xffff; + } + case ELF::R_MAXIS_GOT_OFST: { + int64_t page = (Value + Addend + 0x8000) & ~0xffff; + return (Value + Addend - page) & 0xffff; + } + case ELF::R_MAXIS_GPREL32: { + uint64_t GOTAddr = getSectionLoadAddress(SectionToGOTMap[SectionID]); + return Value + Addend - (GOTAddr + 0x7ff0); + } + case ELF::R_MAXIS_PC16: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return ((Value + Addend - FinalAddress) >> 2) & 0xffff; + } + case ELF::R_MAXIS_PC32: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return Value + Addend - FinalAddress; + } + case ELF::R_MAXIS_PC18_S3: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return ((Value + Addend - (FinalAddress & ~0x7)) >> 3) & 0x3ffff; + } + case ELF::R_MAXIS_PC19_S2: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return ((Value + Addend - (FinalAddress & ~0x3)) >> 2) & 0x7ffff; + } + case ELF::R_MAXIS_PC21_S2: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return ((Value + Addend - FinalAddress) >> 2) & 0x1fffff; + } + case ELF::R_MAXIS_PC26_S2: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return ((Value + Addend - FinalAddress) >> 2) & 0x3ffffff; + } + case ELF::R_MAXIS_PCHI16: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return ((Value + Addend - FinalAddress + 0x8000) >> 16) & 0xffff; + } + case ELF::R_MAXIS_PCLO16: { + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + return (Value + Addend - FinalAddress) & 0xffff; + } + } + return 0; +} + +void RuntimeDyldELFMaxis::applyMAXISRelocation(uint8_t *TargetPtr, int64_t Value, + uint32_t Type) { + uint32_t Insn = readBytesUnaligned(TargetPtr, 4); + + switch (Type) { + default: + llvm_unreachable("Unknown relocation type!"); + break; + case ELF::R_MAXIS_GPREL16: + case ELF::R_MAXIS_HI16: + case ELF::R_MAXIS_LO16: + case ELF::R_MAXIS_HIGHER: + case ELF::R_MAXIS_HIGHEST: + case ELF::R_MAXIS_PC16: + case ELF::R_MAXIS_PCHI16: + case ELF::R_MAXIS_PCLO16: + case ELF::R_MAXIS_CALL16: + case ELF::R_MAXIS_GOT_DISP: + case ELF::R_MAXIS_GOT_PAGE: + case ELF::R_MAXIS_GOT_OFST: + Insn = (Insn & 0xffff0000) | (Value & 0x0000ffff); + writeBytesUnaligned(Insn, TargetPtr, 4); + break; + case ELF::R_MAXIS_PC18_S3: + Insn = (Insn & 0xfffc0000) | (Value & 0x0003ffff); + writeBytesUnaligned(Insn, TargetPtr, 4); + break; + case ELF::R_MAXIS_PC19_S2: + Insn = (Insn & 0xfff80000) | (Value & 0x0007ffff); + writeBytesUnaligned(Insn, TargetPtr, 4); + break; + case ELF::R_MAXIS_PC21_S2: + Insn = (Insn & 0xffe00000) | (Value & 0x001fffff); + writeBytesUnaligned(Insn, TargetPtr, 4); + break; + case ELF::R_MAXIS_26: + case ELF::R_MAXIS_PC26_S2: + Insn = (Insn & 0xfc000000) | (Value & 0x03ffffff); + writeBytesUnaligned(Insn, TargetPtr, 4); + break; + case ELF::R_MAXIS_32: + case ELF::R_MAXIS_GPREL32: + case ELF::R_MAXIS_PC32: + writeBytesUnaligned(Value & 0xffffffff, TargetPtr, 4); + break; + case ELF::R_MAXIS_64: + case ELF::R_MAXIS_SUB: + writeBytesUnaligned(Value, TargetPtr, 8); + break; + } +} + +void RuntimeDyldELFMaxis::resolveMAXISN32Relocation( + const SectionEntry &Section, uint64_t Offset, uint64_t Value, uint32_t Type, + int64_t Addend, uint64_t SymOffset, SID SectionID) { + int64_t CalculatedValue = evaluateMAXIS64Relocation( + Section, Offset, Value, Type, Addend, SymOffset, SectionID); + applyMAXISRelocation(Section.getAddressWithOffset(Offset), CalculatedValue, + Type); +} + +void RuntimeDyldELFMaxis::resolveMAXISN64Relocation( + const SectionEntry &Section, uint64_t Offset, uint64_t Value, uint32_t Type, + int64_t Addend, uint64_t SymOffset, SID SectionID) { + uint32_t r_type = Type & 0xff; + uint32_t r_type2 = (Type >> 8) & 0xff; + uint32_t r_type3 = (Type >> 16) & 0xff; + + // RelType is used to keep information for which relocation type we are + // applying relocation. + uint32_t RelType = r_type; + int64_t CalculatedValue = evaluateMAXIS64Relocation(Section, Offset, Value, + RelType, Addend, + SymOffset, SectionID); + if (r_type2 != ELF::R_MAXIS_NONE) { + RelType = r_type2; + CalculatedValue = evaluateMAXIS64Relocation(Section, Offset, 0, RelType, + CalculatedValue, SymOffset, + SectionID); + } + if (r_type3 != ELF::R_MAXIS_NONE) { + RelType = r_type3; + CalculatedValue = evaluateMAXIS64Relocation(Section, Offset, 0, RelType, + CalculatedValue, SymOffset, + SectionID); + } + applyMAXISRelocation(Section.getAddressWithOffset(Offset), CalculatedValue, + RelType); +} + +void RuntimeDyldELFMaxis::resolveMAXISO32Relocation(const SectionEntry &Section, + uint64_t Offset, + uint32_t Value, uint32_t Type, + int32_t Addend) { + uint8_t *TargetPtr = Section.getAddressWithOffset(Offset); + Value += Addend; + + DEBUG(dbgs() << "resolveMAXISO32Relocation, LocalAddress: " + << Section.getAddressWithOffset(Offset) << " FinalAddress: " + << format("%p", Section.getLoadAddressWithOffset(Offset)) + << " Value: " << format("%x", Value) + << " Type: " << format("%x", Type) + << " Addend: " << format("%x", Addend) + << " SymOffset: " << format("%x", Offset) << "\n"); + + Value = evaluateMAXIS32Relocation(Section, Offset, Value, Type); + + applyMAXISRelocation(TargetPtr, Value, Type); +} diff --git a/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldELFMaxis.h b/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldELFMaxis.h new file mode 100644 index 00000000..606f6ba3 --- /dev/null +++ b/lib/ExecutionEngine/RuntimeDyld/Targets/RuntimeDyldELFMaxis.h @@ -0,0 +1,68 @@ +//===-- RuntimeDyldELFMaxis.h ---- ELF/Maxis specific code. -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDELFMAXIS_H +#define LLVM_LIB_EXECUTIONENGINE_RUNTIMEDYLD_TARGETS_RUNTIMEDYLDELFMAXIS_H + +#include "../RuntimeDyldELF.h" +#include + +#define DEBUG_TYPE "dyld" + +namespace llvm { + +class RuntimeDyldELFMaxis : public RuntimeDyldELF { +public: + + typedef uint64_t TargetPtrT; + + RuntimeDyldELFMaxis(RuntimeDyld::MemoryManager &MM, + JITSymbolResolver &Resolver) + : RuntimeDyldELF(MM, Resolver) {} + + void resolveRelocation(const RelocationEntry &RE, uint64_t Value) override; + +protected: + void resolveMAXISO32Relocation(const SectionEntry &Section, uint64_t Offset, + uint32_t Value, uint32_t Type, int32_t Addend); + void resolveMAXISN32Relocation(const SectionEntry &Section, uint64_t Offset, + uint64_t Value, uint32_t Type, int64_t Addend, + uint64_t SymOffset, SID SectionID); + void resolveMAXISN64Relocation(const SectionEntry &Section, uint64_t Offset, + uint64_t Value, uint32_t Type, int64_t Addend, + uint64_t SymOffset, SID SectionID); + +private: + /// \brief A object file specific relocation resolver + /// \param RE The relocation to be resolved + /// \param Value Target symbol address to apply the relocation action + uint64_t evaluateRelocation(const RelocationEntry &RE, uint64_t Value, + uint64_t Addend); + + /// \brief A object file specific relocation resolver + /// \param RE The relocation to be resolved + /// \param Value Target symbol address to apply the relocation action + void applyRelocation(const RelocationEntry &RE, uint64_t Value); + + int64_t evaluateMAXIS32Relocation(const SectionEntry &Section, uint64_t Offset, + uint64_t Value, uint32_t Type); + int64_t evaluateMAXIS64Relocation(const SectionEntry &Section, + uint64_t Offset, uint64_t Value, + uint32_t Type, int64_t Addend, + uint64_t SymOffset, SID SectionID); + + void applyMAXISRelocation(uint8_t *TargetPtr, int64_t CalculatedValue, + uint32_t Type); + +}; +} + +#undef DEBUG_TYPE + +#endif diff --git a/lib/IR/DataLayout.cpp b/lib/IR/DataLayout.cpp index f4dddeb3..7de8d319 100644 --- a/lib/IR/DataLayout.cpp +++ b/lib/IR/DataLayout.cpp @@ -380,6 +380,9 @@ void DataLayout::parseSpecifier(StringRef Desc) { case 'o': ManglingMode = MM_MachO; break; + case 'a': + ManglingMode = MM_Maxis; + break; case 'm': ManglingMode = MM_Mips; break; diff --git a/lib/MC/ELFObjectWriter.cpp b/lib/MC/ELFObjectWriter.cpp index e11eaaa3..d4c405d7 100644 --- a/lib/MC/ELFObjectWriter.cpp +++ b/lib/MC/ELFObjectWriter.cpp @@ -1100,7 +1100,7 @@ void ELFObjectWriter::writeRelocations(const MCAssembler &Asm, // (.eh_frame) or specific relocations (TLS optimizations on SystemZ). std::reverse(Relocs.begin(), Relocs.end()); - // Sort the relocation entries. MIPS needs this. + // Sort the relocation entries. MAXIS/MIPS needs this. TargetObjectWriter->sortRelocs(Asm, Relocs); for (unsigned i = 0, e = Relocs.size(); i != e; ++i) { @@ -1109,7 +1109,8 @@ void ELFObjectWriter::writeRelocations(const MCAssembler &Asm, if (is64Bit()) { write(Entry.Offset); - if (TargetObjectWriter->getEMachine() == ELF::EM_MIPS) { + if (TargetObjectWriter->getEMachine() == ELF::EM_MAXIS + || TargetObjectWriter->getEMachine() == ELF::EM_MIPS) { write(uint32_t(Index)); write(TargetObjectWriter->getRSsym(Entry.Type)); @@ -1133,7 +1134,8 @@ void ELFObjectWriter::writeRelocations(const MCAssembler &Asm, if (hasRelocationAddend()) write(uint32_t(Entry.Addend)); - if (TargetObjectWriter->getEMachine() == ELF::EM_MIPS) { + if (TargetObjectWriter->getEMachine() == ELF::EM_MAXIS + || TargetObjectWriter->getEMachine() == ELF::EM_MIPS) { if (uint32_t RType = TargetObjectWriter->getRType2(Entry.Type)) { write(uint32_t(Entry.Offset)); diff --git a/lib/MC/MCDwarf.cpp b/lib/MC/MCDwarf.cpp index 9e5d9ff7..d13372dc 100644 --- a/lib/MC/MCDwarf.cpp +++ b/lib/MC/MCDwarf.cpp @@ -836,8 +836,9 @@ static void EmitGenDwarfInfo(MCStreamer *MCOS, MCOS->EmitBytes(StringRef("llvm-mc (based on LLVM " PACKAGE_VERSION ")")); MCOS->EmitIntValue(0, 1); // NULL byte to terminate the string. - // AT_language, a 4 byte value. We use DW_LANG_Mips_Assembler as the dwarf2 + // AT_language, a 4 byte value. We use DW_LANG_Maxis_Assembler/DW_LANG_Mips_Assembler as the dwarf2 // draft has no standard code for assembler. + MCOS->EmitIntValue(dwarf::DW_LANG_Maxis_Assembler, 2); MCOS->EmitIntValue(dwarf::DW_LANG_Mips_Assembler, 2); // Third part: the list of label DIEs. diff --git a/lib/MC/MCObjectFileInfo.cpp b/lib/MC/MCObjectFileInfo.cpp index a6b5c43f..a295ee85 100644 --- a/lib/MC/MCObjectFileInfo.cpp +++ b/lib/MC/MCObjectFileInfo.cpp @@ -281,10 +281,14 @@ void MCObjectFileInfo::initMachOMCObjectFileInfo(const Triple &T) { void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) { switch (T.getArch()) { + case Triple::maxis: + case Triple::maxisel: case Triple::mips: case Triple::mipsel: FDECFIEncoding = dwarf::DW_EH_PE_sdata4; break; + case Triple::maxis64: + case Triple::maxis64el: case Triple::mips64: case Triple::mips64el: FDECFIEncoding = dwarf::DW_EH_PE_sdata8; @@ -376,11 +380,15 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) { PersonalityEncoding = dwarf::DW_EH_PE_absptr; TTypeEncoding = dwarf::DW_EH_PE_absptr; break; + case Triple::maxis: + case Triple::maxisel: + case Triple::maxis64: + case Triple::maxis64el: case Triple::mips: case Triple::mipsel: case Triple::mips64: case Triple::mips64el: - // MIPS uses indirect pointer to refer personality functions and types, so + // MAXIS/MIPS uses indirect pointer to refer personality functions and types, so // that the eh_frame section can be read-only. DW.ref.personality will be // generated for relocation. PersonalityEncoding = dwarf::DW_EH_PE_indirect; @@ -515,10 +523,13 @@ void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) { unsigned DebugSecType = ELF::SHT_PROGBITS; - // MIPS .debug_* sections should have SHT_MIPS_DWARF section type + // MAXIS/MIPS .debug_* sections should have SHT_MAXIS_DWARF/SHT_MIPS_DWARF section type // to distinguish among sections contain DWARF and ECOFF debug formats. // Sections with ECOFF debug format are obsoleted and marked by SHT_PROGBITS. - if (T.getArch() == Triple::mips || T.getArch() == Triple::mipsel || + if (T.getArch() == Triple::maxis || T.getArch() == Triple::maxisel || + T.getArch() == Triple::maxis64 || T.getArch() == Triple::maxis64el) + DebugSecType = ELF::SHT_MAXIS_DWARF; + else if (T.getArch() == Triple::mips || T.getArch() == Triple::mipsel || T.getArch() == Triple::mips64 || T.getArch() == Triple::mips64el) DebugSecType = ELF::SHT_MIPS_DWARF; diff --git a/lib/MC/MCParser/AsmLexer.cpp b/lib/MC/MCParser/AsmLexer.cpp index 74835fd7..0bf9f5fd 100644 --- a/lib/MC/MCParser/AsmLexer.cpp +++ b/lib/MC/MCParser/AsmLexer.cpp @@ -662,7 +662,7 @@ AsmToken AsmLexer::LexToken() { } return AsmToken(AsmToken::Exclaim, StringRef(TokStart, 1)); case '%': - if (MAI.hasMipsExpressions()) { + if (MAI.hasMaxisExpressions() || MAI.hasMipsExpressions()) { AsmToken::TokenKind Operator; unsigned OperatorLength; diff --git a/lib/MC/MCParser/AsmParser.cpp b/lib/MC/MCParser/AsmParser.cpp index ce3b70be..236c8c75 100644 --- a/lib/MC/MCParser/AsmParser.cpp +++ b/lib/MC/MCParser/AsmParser.cpp @@ -1188,8 +1188,8 @@ bool AsmParser::parsePrimaryExpr(const MCExpr *&Res, SMLoc &EndLoc) { return true; Res = MCUnaryExpr::createNot(Res, getContext(), FirstTokenLoc); return false; - // MIPS unary expression operators. The lexer won't generate these tokens if - // MCAsmInfo::HasMipsExpressions is false for the target. + // MAXIS/MIPS unary expression operators. The lexer won't generate these tokens if + // MCAsmInfo::HasMaxisExpressions/MCAsmInfo::HasMipsExpressions are false for the target. case AsmToken::PercentCall16: case AsmToken::PercentCall_Hi: case AsmToken::PercentCall_Lo: diff --git a/lib/MC/MCSectionELF.cpp b/lib/MC/MCSectionELF.cpp index bf1fcb03..50857280 100644 --- a/lib/MC/MCSectionELF.cpp +++ b/lib/MC/MCSectionELF.cpp @@ -142,6 +142,10 @@ void MCSectionELF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, OS << "progbits"; else if (Type == ELF::SHT_X86_64_UNWIND) OS << "unwind"; + else if (Type == ELF::SHT_MAXIS_DWARF) + // Print hex value of the flag while we do not have + // any standard symbolic representation of the flag. + OS << "0x7000001e"; else if (Type == ELF::SHT_MIPS_DWARF) // Print hex value of the flag while we do not have // any standard symbolic representation of the flag. diff --git a/lib/Object/Archive.cpp b/lib/Object/Archive.cpp index b17eefd2..aa38ea55 100644 --- a/lib/Object/Archive.cpp +++ b/lib/Object/Archive.cpp @@ -665,7 +665,7 @@ Archive::Archive(MemoryBufferRef Source, Error &Err) return; } - // MIPS 64-bit ELF archives use a special format of a symbol table. + // MAXIS/MIPS 64-bit ELF archives use a special format of a symbol table. // This format is marked by `ar_name` field equals to "/SYM64/". // For detailed description see page 96 in the following document: // http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf diff --git a/lib/Object/ELF.cpp b/lib/Object/ELF.cpp index 5906dc5f..d7e2faf2 100644 --- a/lib/Object/ELF.cpp +++ b/lib/Object/ELF.cpp @@ -38,6 +38,13 @@ StringRef llvm::object::getELFRelocationTypeName(uint32_t Machine, break; } break; + case ELF::EM_MAXIS: + switch (Type) { +#include "llvm/BinaryFormat/ELFRelocs/Maxis.def" + default: + break; + } + break; case ELF::EM_MIPS: switch (Type) { #include "llvm/BinaryFormat/ELFRelocs/Mips.def" diff --git a/lib/Object/ELFObjectFile.cpp b/lib/Object/ELFObjectFile.cpp index 0aad1c89..58c74280 100644 --- a/lib/Object/ELFObjectFile.cpp +++ b/lib/Object/ELFObjectFile.cpp @@ -74,6 +74,67 @@ ObjectFile::createELFObjectFile(MemoryBufferRef Obj) { return createError("Invalid ELF class"); } +SubtargetFeatures ELFObjectFileBase::getMAXISFeatures() const { + SubtargetFeatures Features; + unsigned PlatformFlags; + getPlatformFlags(PlatformFlags); + + switch (PlatformFlags & ELF::EF_MAXIS_ARCH) { + case ELF::EF_MAXIS_ARCH_1: + break; + case ELF::EF_MAXIS_ARCH_2: + Features.AddFeature("maxis2"); + break; + case ELF::EF_MAXIS_ARCH_3: + Features.AddFeature("maxis3"); + break; + case ELF::EF_MAXIS_ARCH_4: + Features.AddFeature("maxis4"); + break; + case ELF::EF_MAXIS_ARCH_5: + Features.AddFeature("maxis5"); + break; + case ELF::EF_MAXIS_ARCH_32: + Features.AddFeature("maxis32"); + break; + case ELF::EF_MAXIS_ARCH_64: + Features.AddFeature("maxis64"); + break; + case ELF::EF_MAXIS_ARCH_32R2: + Features.AddFeature("maxis32r2"); + break; + case ELF::EF_MAXIS_ARCH_64R2: + Features.AddFeature("maxis64r2"); + break; + case ELF::EF_MAXIS_ARCH_32R6: + Features.AddFeature("maxis32r6"); + break; + case ELF::EF_MAXIS_ARCH_64R6: + Features.AddFeature("maxis64r6"); + break; + default: + llvm_unreachable("Unknown EF_MAXIS_ARCH value"); + } + + switch (PlatformFlags & ELF::EF_MAXIS_MACH) { + case ELF::EF_MAXIS_MACH_NONE: + // No feature associated with this value. + break; + case ELF::EF_MAXIS_MACH_OCTEON: + Features.AddFeature("cnmaxis"); + break; + default: + llvm_unreachable("Unknown EF_MAXIS_ARCH value"); + } + + if (PlatformFlags & ELF::EF_MAXIS_ARCH_ASE_M16) + Features.AddFeature("maxis16"); + if (PlatformFlags & ELF::EF_MAXIS_MICROMAXIS) + Features.AddFeature("micromaxis"); + + return Features; +} + SubtargetFeatures ELFObjectFileBase::getMIPSFeatures() const { SubtargetFeatures Features; unsigned PlatformFlags; diff --git a/lib/ObjectYAML/COFFYAML.cpp b/lib/ObjectYAML/COFFYAML.cpp index 937b8dc0..6ea1fde9 100644 --- a/lib/ObjectYAML/COFFYAML.cpp +++ b/lib/ObjectYAML/COFFYAML.cpp @@ -69,6 +69,9 @@ void ScalarEnumerationTraits::enumeration( ECase(IMAGE_FILE_MACHINE_I386); ECase(IMAGE_FILE_MACHINE_IA64); ECase(IMAGE_FILE_MACHINE_M32R); + ECase(IMAGE_FILE_MACHINE_MAXIS16); + ECase(IMAGE_FILE_MACHINE_MAXISFPU); + ECase(IMAGE_FILE_MACHINE_MAXISFPU16); ECase(IMAGE_FILE_MACHINE_MIPS16); ECase(IMAGE_FILE_MACHINE_MIPSFPU); ECase(IMAGE_FILE_MACHINE_MIPSFPU16); @@ -80,6 +83,7 @@ void ScalarEnumerationTraits::enumeration( ECase(IMAGE_FILE_MACHINE_SH4); ECase(IMAGE_FILE_MACHINE_SH5); ECase(IMAGE_FILE_MACHINE_THUMB); + ECase(IMAGE_FILE_MACHINE_WCEMAXISV2); ECase(IMAGE_FILE_MACHINE_WCEMIPSV2); } diff --git a/lib/ObjectYAML/CodeViewYAMLTypes.cpp b/lib/ObjectYAML/CodeViewYAMLTypes.cpp index ba4ad938..52db82dd 100644 --- a/lib/ObjectYAML/CodeViewYAMLTypes.cpp +++ b/lib/ObjectYAML/CodeViewYAMLTypes.cpp @@ -242,6 +242,7 @@ void ScalarEnumerationTraits::enumeration( IO.enumCase(Value, "NearSysCall", CallingConvention::NearSysCall); IO.enumCase(Value, "FarSysCall", CallingConvention::FarSysCall); IO.enumCase(Value, "ThisCall", CallingConvention::ThisCall); + IO.enumCase(Value, "MaxisCall", CallingConvention::MaxisCall); IO.enumCase(Value, "MipsCall", CallingConvention::MipsCall); IO.enumCase(Value, "Generic", CallingConvention::Generic); IO.enumCase(Value, "AlphaCall", CallingConvention::AlphaCall); diff --git a/lib/ObjectYAML/ELFYAML.cpp b/lib/ObjectYAML/ELFYAML.cpp index 7e7f3d1f..41a50c43 100644 --- a/lib/ObjectYAML/ELFYAML.cpp +++ b/lib/ObjectYAML/ELFYAML.cpp @@ -16,6 +16,7 @@ #include "llvm/BinaryFormat/ELF.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MaxisABIFlags.h" #include "llvm/Support/MipsABIFlags.h" #include "llvm/Support/YAMLTraits.h" #include @@ -65,8 +66,10 @@ void ScalarEnumerationTraits::enumeration( ECase(EM_88K); ECase(EM_IAMCU); ECase(EM_860); + ECase(EM_MAXIS); ECase(EM_MIPS); ECase(EM_S370); + ECase(EM_MAXIS_RS3_LE); ECase(EM_MIPS_RS3_LE); ECase(EM_PARISC); ECase(EM_VPP500); @@ -91,6 +94,7 @@ void ScalarEnumerationTraits::enumeration( ECase(EM_H8S); ECase(EM_H8_500); ECase(EM_IA_64); + ECase(EM_MAXIS_X); ECase(EM_MIPS_X); ECase(EM_COLDFIRE); ECase(EM_68HC12); @@ -286,6 +290,51 @@ void ScalarBitSetTraits::bitset(IO &IO, BCaseMask(EF_ARM_EABI_VER4, EF_ARM_EABIMASK); BCaseMask(EF_ARM_EABI_VER5, EF_ARM_EABIMASK); break; + case ELF::EM_MAXIS: + BCase(EF_MAXIS_NOREORDER); + BCase(EF_MAXIS_PIC); + BCase(EF_MAXIS_CPIC); + BCase(EF_MAXIS_ABI2); + BCase(EF_MAXIS_32BITMODE); + BCase(EF_MAXIS_FP64); + BCase(EF_MAXIS_NAN2008); + BCase(EF_MAXIS_MICROMAXIS); + BCase(EF_MAXIS_ARCH_ASE_M16); + BCase(EF_MAXIS_ARCH_ASE_MDMX); + BCaseMask(EF_MAXIS_ABI_O32, EF_MAXIS_ABI); + BCaseMask(EF_MAXIS_ABI_O64, EF_MAXIS_ABI); + BCaseMask(EF_MAXIS_ABI_EABI32, EF_MAXIS_ABI); + BCaseMask(EF_MAXIS_ABI_EABI64, EF_MAXIS_ABI); + BCaseMask(EF_MAXIS_MACH_3900, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_4010, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_4100, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_4650, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_4120, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_4111, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_SB1, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_OCTEON, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_XLR, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_OCTEON2, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_OCTEON3, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_5400, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_5900, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_5500, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_9000, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_LS2E, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_LS2F, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_MACH_LS3A, EF_MAXIS_MACH); + BCaseMask(EF_MAXIS_ARCH_1, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_2, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_3, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_4, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_5, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_32, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_64, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_32R2, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_64R2, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_32R6, EF_MAXIS_ARCH); + BCaseMask(EF_MAXIS_ARCH_64R6, EF_MAXIS_ARCH); + break; case ELF::EM_MIPS: BCase(EF_MIPS_NOREORDER); BCase(EF_MIPS_PIC); @@ -429,6 +478,11 @@ void ScalarEnumerationTraits::enumeration( case ELF::EM_X86_64: ECase(SHT_X86_64_UNWIND); break; + case ELF::EM_MAXIS: + ECase(SHT_MAXIS_REGINFO); + ECase(SHT_MAXIS_OPTIONS); + ECase(SHT_MAXIS_ABIFLAGS); + break; case ELF::EM_MIPS: ECase(SHT_MIPS_REGINFO); ECase(SHT_MIPS_OPTIONS); @@ -472,6 +526,16 @@ void ScalarBitSetTraits::bitset(IO &IO, case ELF::EM_HEXAGON: BCase(SHF_HEX_GPREL); break; + case ELF::EM_MAXIS: + BCase(SHF_MAXIS_NODUPES); + BCase(SHF_MAXIS_NAMES); + BCase(SHF_MAXIS_LOCAL); + BCase(SHF_MAXIS_NOSTRIP); + BCase(SHF_MAXIS_GPREL); + BCase(SHF_MAXIS_MERGE); + BCase(SHF_MAXIS_ADDR); + BCase(SHF_MAXIS_STRING); + break; case ELF::EM_MIPS: BCase(SHF_MIPS_NODUPES); BCase(SHF_MIPS_NAMES); @@ -544,6 +608,12 @@ void ScalarBitSetTraits::bitset(IO &IO, assert(Object && "The IO context is not initialized"); #define BCase(X) IO.bitSetCase(Value, #X, ELF::X) switch (Object->Header.Machine) { + case ELF::EM_MAXIS: + BCase(STO_MAXIS_OPTIONAL); + BCase(STO_MAXIS_PLT); + BCase(STO_MAXIS_PIC); + BCase(STO_MAXIS_MICROMAXIS); + break; case ELF::EM_MIPS: BCase(STO_MIPS_OPTIONAL); BCase(STO_MIPS_PLT); @@ -576,6 +646,9 @@ void ScalarEnumerationTraits::enumeration( case ELF::EM_X86_64: #include "llvm/BinaryFormat/ELFRelocs/x86_64.def" break; + case ELF::EM_MAXIS: +#include "llvm/BinaryFormat/ELFRelocs/Maxis.def" + break; case ELF::EM_MIPS: #include "llvm/BinaryFormat/ELFRelocs/Mips.def" break; @@ -614,6 +687,93 @@ void ScalarEnumerationTraits::enumeration( IO.enumFallback(Value); } +void ScalarEnumerationTraits::enumeration( + IO &IO, ELFYAML::MAXIS_AFL_REG &Value) { +#define ECase(X) IO.enumCase(Value, #X, Maxis::AFL_##X) + ECase(REG_NONE); + ECase(REG_32); + ECase(REG_64); + ECase(REG_128); +#undef ECase +} + +void ScalarEnumerationTraits::enumeration( + IO &IO, ELFYAML::MAXIS_ABI_FP &Value) { +#define ECase(X) IO.enumCase(Value, #X, Maxis::Val_GNU_MAXIS_ABI_##X) + ECase(FP_ANY); + ECase(FP_DOUBLE); + ECase(FP_SINGLE); + ECase(FP_SOFT); + ECase(FP_OLD_64); + ECase(FP_XX); + ECase(FP_64); + ECase(FP_64A); +#undef ECase +} + +void ScalarEnumerationTraits::enumeration( + IO &IO, ELFYAML::MAXIS_AFL_EXT &Value) { +#define ECase(X) IO.enumCase(Value, #X, Maxis::AFL_##X) + ECase(EXT_NONE); + ECase(EXT_XLR); + ECase(EXT_OCTEON2); + ECase(EXT_OCTEONP); + ECase(EXT_LOONGSON_3A); + ECase(EXT_OCTEON); + ECase(EXT_5900); + ECase(EXT_4650); + ECase(EXT_4010); + ECase(EXT_4100); + ECase(EXT_3900); + ECase(EXT_10000); + ECase(EXT_SB1); + ECase(EXT_4111); + ECase(EXT_4120); + ECase(EXT_5400); + ECase(EXT_5500); + ECase(EXT_LOONGSON_2E); + ECase(EXT_LOONGSON_2F); + ECase(EXT_OCTEON3); +#undef ECase +} + +void ScalarEnumerationTraits::enumeration( + IO &IO, ELFYAML::MAXIS_ISA &Value) { + IO.enumCase(Value, "MAXIS1", 1); + IO.enumCase(Value, "MAXIS2", 2); + IO.enumCase(Value, "MAXIS3", 3); + IO.enumCase(Value, "MAXIS4", 4); + IO.enumCase(Value, "MAXIS5", 5); + IO.enumCase(Value, "MAXIS32", 32); + IO.enumCase(Value, "MAXIS64", 64); +} + +void ScalarBitSetTraits::bitset( + IO &IO, ELFYAML::MAXIS_AFL_ASE &Value) { +#define BCase(X) IO.bitSetCase(Value, #X, Maxis::AFL_ASE_##X) + BCase(DSP); + BCase(DSPR2); + BCase(EVA); + BCase(MCU); + BCase(MDMX); + BCase(MAXIS3D); + BCase(MT); + BCase(SMARTMAXIS); + BCase(VIRT); + BCase(MSA); + BCase(MAXIS16); + BCase(MICROMAXIS); + BCase(XPA); +#undef BCase +} + +void ScalarBitSetTraits::bitset( + IO &IO, ELFYAML::MAXIS_AFL_FLAGS1 &Value) { +#define BCase(X) IO.bitSetCase(Value, #X, Maxis::AFL_FLAGS1_##X) + BCase(ODDSPREG); +#undef BCase +} + void ScalarEnumerationTraits::enumeration( IO &IO, ELFYAML::MIPS_AFL_REG &Value) { #define ECase(X) IO.enumCase(Value, #X, Mips::AFL_##X) @@ -813,6 +973,26 @@ void MappingTraits::mapping( IO.mapRequired("Section", sectionName.Section); } +static void sectionMapping(IO &IO, ELFYAML::MaxisABIFlags &Section) { + commonSectionMapping(IO, Section); + IO.mapOptional("Version", Section.Version, Hex16(0)); + IO.mapRequired("ISA", Section.ISALevel); + IO.mapOptional("ISARevision", Section.ISARevision, Hex8(0)); + IO.mapOptional("ISAExtension", Section.ISAExtension, + ELFYAML::MAXIS_AFL_EXT(Maxis::AFL_EXT_NONE)); + IO.mapOptional("ASEs", Section.ASEs, ELFYAML::MAXIS_AFL_ASE(0)); + IO.mapOptional("FpABI", Section.FpABI, + ELFYAML::MAXIS_ABI_FP(Maxis::Val_GNU_MAXIS_ABI_FP_ANY)); + IO.mapOptional("GPRSize", Section.GPRSize, + ELFYAML::MAXIS_AFL_REG(Maxis::AFL_REG_NONE)); + IO.mapOptional("CPR1Size", Section.CPR1Size, + ELFYAML::MAXIS_AFL_REG(Maxis::AFL_REG_NONE)); + IO.mapOptional("CPR2Size", Section.CPR2Size, + ELFYAML::MAXIS_AFL_REG(Maxis::AFL_REG_NONE)); + IO.mapOptional("Flags1", Section.Flags1, ELFYAML::MAXIS_AFL_FLAGS1(0)); + IO.mapOptional("Flags2", Section.Flags2, Hex32(0)); +} + static void sectionMapping(IO &IO, ELFYAML::MipsABIFlags &Section) { commonSectionMapping(IO, Section); IO.mapOptional("Version", Section.Version, Hex16(0)); @@ -858,6 +1038,11 @@ void MappingTraits>::mapping( Section.reset(new ELFYAML::NoBitsSection()); sectionMapping(IO, *cast(Section.get())); break; + case ELF::SHT_MAXIS_ABIFLAGS: + if (!IO.outputting()) + Section.reset(new ELFYAML::MaxisABIFlags()); + sectionMapping(IO, *cast(Section.get())); + break; case ELF::SHT_MIPS_ABIFLAGS: if (!IO.outputting()) Section.reset(new ELFYAML::MipsABIFlags()); @@ -880,6 +1065,27 @@ StringRef MappingTraits>::validate( namespace { +struct NormalizedMaxis64RelType { + NormalizedMaxis64RelType(IO &) + : Type(ELFYAML::ELF_REL(ELF::R_MAXIS_NONE)), + Type2(ELFYAML::ELF_REL(ELF::R_MAXIS_NONE)), + Type3(ELFYAML::ELF_REL(ELF::R_MAXIS_NONE)), + SpecSym(ELFYAML::ELF_REL(ELF::RSS_UNDEF)) {} + NormalizedMaxis64RelType(IO &, ELFYAML::ELF_REL Original) + : Type(Original & 0xFF), Type2(Original >> 8 & 0xFF), + Type3(Original >> 16 & 0xFF), SpecSym(Original >> 24 & 0xFF) {} + + ELFYAML::ELF_REL denormalize(IO &) { + ELFYAML::ELF_REL Res = Type | Type2 << 8 | Type3 << 16 | SpecSym << 24; + return Res; + } + + ELFYAML::ELF_REL Type; + ELFYAML::ELF_REL Type2; + ELFYAML::ELF_REL Type3; + ELFYAML::ELF_RSS SpecSym; +}; + struct NormalizedMips64RelType { NormalizedMips64RelType(IO &) : Type(ELFYAML::ELF_REL(ELF::R_MIPS_NONE)), @@ -911,7 +1117,15 @@ void MappingTraits::mapping(IO &IO, IO.mapRequired("Offset", Rel.Offset); IO.mapOptional("Symbol", Rel.Symbol); - if (Object->Header.Machine == ELFYAML::ELF_EM(ELF::EM_MIPS) && + if (Object->Header.Machine == ELFYAML::ELF_EM(ELF::EM_MAXIS) && + Object->Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64)) { + MappingNormalization Key( + IO, Rel.Type); + IO.mapRequired("Type", Key->Type); + IO.mapOptional("Type2", Key->Type2, ELFYAML::ELF_REL(ELF::R_MAXIS_NONE)); + IO.mapOptional("Type3", Key->Type3, ELFYAML::ELF_REL(ELF::R_MAXIS_NONE)); + IO.mapOptional("SpecSym", Key->SpecSym, ELFYAML::ELF_RSS(ELF::RSS_UNDEF)); + } else if (Object->Header.Machine == ELFYAML::ELF_EM(ELF::EM_MIPS) && Object->Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64)) { MappingNormalization Key( IO, Rel.Type); @@ -937,6 +1151,12 @@ void MappingTraits::mapping(IO &IO, ELFYAML::Object &Object) { IO.setContext(nullptr); } +LLVM_YAML_STRONG_TYPEDEF(uint8_t, MAXIS_AFL_REG) +LLVM_YAML_STRONG_TYPEDEF(uint8_t, MAXIS_ABI_FP) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, MAXIS_AFL_EXT) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, MAXIS_AFL_ASE) +LLVM_YAML_STRONG_TYPEDEF(uint32_t, MAXIS_AFL_FLAGS1) + LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_AFL_REG) LLVM_YAML_STRONG_TYPEDEF(uint8_t, MIPS_ABI_FP) LLVM_YAML_STRONG_TYPEDEF(uint32_t, MIPS_AFL_EXT) diff --git a/lib/Support/Triple.cpp b/lib/Support/Triple.cpp index 4f0a3004..c9eb5dbd 100644 --- a/lib/Support/Triple.cpp +++ b/lib/Support/Triple.cpp @@ -30,6 +30,10 @@ StringRef Triple::getArchTypeName(ArchType Kind) { case bpfel: return "bpfel"; case bpfeb: return "bpfeb"; case hexagon: return "hexagon"; + case maxis: return "maxis"; + case maxisel: return "maxisel"; + case maxis64: return "maxis64"; + case maxis64el: return "maxis64el"; case mips: return "mips"; case mipsel: return "mipsel"; case mips64: return "mips64"; @@ -97,6 +101,11 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) { case ppc64le: case ppc: return "ppc"; + case maxis: + case maxisel: + case maxis64: + case maxis64el: return "maxis"; + case mips: case mipsel: case mips64: @@ -161,6 +170,7 @@ StringRef Triple::getVendorTypeName(VendorType Kind) { case Freescale: return "fsl"; case IBM: return "ibm"; case ImaginationTechnologies: return "img"; + case MaxisTechnologies: return "ati"; case MipsTechnologies: return "mti"; case NVIDIA: return "nvidia"; case CSR: return "csr"; @@ -267,6 +277,10 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { .Case("armeb", armeb) .Case("avr", avr) .StartsWith("bpf", BPFArch) + .Case("maxis", maxis) + .Case("maxisel", maxisel) + .Case("maxis64", maxis64) + .Case("maxis64el", maxis64el) .Case("mips", mips) .Case("mipsel", mipsel) .Case("mips64", mips64) @@ -399,6 +413,10 @@ static Triple::ArchType parseArch(StringRef ArchName) { .Case("thumbeb", Triple::thumbeb) .Case("avr", Triple::avr) .Case("msp430", Triple::msp430) + .Cases("maxis", "maxiseb", "maxisallegrex", Triple::maxis) + .Cases("maxisel", "maxisallegrexel", Triple::maxisel) + .Cases("maxis64", "maxis64eb", Triple::maxis64) + .Case("maxis64el", Triple::maxis64el) .Cases("mips", "mipseb", "mipsallegrex", Triple::mips) .Cases("mipsel", "mipsallegrexel", Triple::mipsel) .Cases("mips64", "mips64eb", Triple::mips64) @@ -458,6 +476,7 @@ static Triple::VendorType parseVendor(StringRef VendorName) { .Case("fsl", Triple::Freescale) .Case("ibm", Triple::IBM) .Case("img", Triple::ImaginationTechnologies) + .Case("ati", Triple::MaxisTechnologies) .Case("mti", Triple::MipsTechnologies) .Case("nvidia", Triple::NVIDIA) .Case("csr", Triple::CSR) @@ -646,6 +665,10 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { case Triple::kalimba: case Triple::le32: case Triple::le64: + case Triple::maxis: + case Triple::maxis64: + case Triple::maxis64el: + case Triple::maxisel: case Triple::mips: case Triple::mips64: case Triple::mips64el: @@ -1190,6 +1213,8 @@ static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::armeb: case llvm::Triple::hexagon: case llvm::Triple::le32: + case llvm::Triple::maxis: + case llvm::Triple::maxisel: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::nios2: @@ -1221,6 +1246,8 @@ static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::bpfel: case llvm::Triple::bpfeb: case llvm::Triple::le64: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::nvptx64: @@ -1275,6 +1302,8 @@ Triple Triple::get32BitArchVariant() const { case Triple::hexagon: case Triple::kalimba: case Triple::le32: + case Triple::maxis: + case Triple::maxisel: case Triple::mips: case Triple::mipsel: case Triple::nios2: @@ -1300,6 +1329,8 @@ Triple Triple::get32BitArchVariant() const { case Triple::aarch64: T.setArch(Triple::arm); break; case Triple::aarch64_be: T.setArch(Triple::armeb); break; case Triple::le64: T.setArch(Triple::le32); break; + case Triple::maxis64: T.setArch(Triple::maxis); break; + case Triple::maxis64el: T.setArch(Triple::maxisel); break; case Triple::mips64: T.setArch(Triple::mips); break; case Triple::mips64el: T.setArch(Triple::mipsel); break; case Triple::nvptx64: T.setArch(Triple::nvptx); break; @@ -1345,6 +1376,8 @@ Triple Triple::get64BitArchVariant() const { case Triple::amdgcn: case Triple::hsail64: case Triple::spir64: + case Triple::maxis64: + case Triple::maxis64el: case Triple::mips64: case Triple::mips64el: case Triple::nvptx64: @@ -1362,6 +1395,8 @@ Triple Triple::get64BitArchVariant() const { case Triple::arm: T.setArch(Triple::aarch64); break; case Triple::armeb: T.setArch(Triple::aarch64_be); break; case Triple::le32: T.setArch(Triple::le64); break; + case Triple::maxis: T.setArch(Triple::maxis64); break; + case Triple::maxisel: T.setArch(Triple::maxis64el); break; case Triple::mips: T.setArch(Triple::mips64); break; case Triple::mipsel: T.setArch(Triple::mips64el); break; case Triple::nvptx: T.setArch(Triple::nvptx64); break; @@ -1425,6 +1460,8 @@ Triple Triple::getBigEndianArchVariant() const { case Triple::tcele: T.setArch(Triple::tce); break; case Triple::aarch64: T.setArch(Triple::aarch64_be); break; case Triple::bpfel: T.setArch(Triple::bpfeb); break; + case Triple::maxis64el:T.setArch(Triple::maxis64); break; + case Triple::maxisel: T.setArch(Triple::maxis); break; case Triple::mips64el:T.setArch(Triple::mips64); break; case Triple::mipsel: T.setArch(Triple::mips); break; case Triple::ppc64le: T.setArch(Triple::ppc64); break; @@ -1457,6 +1494,8 @@ Triple Triple::getLittleEndianArchVariant() const { case Triple::tce: T.setArch(Triple::tcele); break; case Triple::aarch64_be: T.setArch(Triple::aarch64); break; case Triple::bpfeb: T.setArch(Triple::bpfel); break; + case Triple::maxis64: T.setArch(Triple::maxis64el); break; + case Triple::maxis: T.setArch(Triple::maxisel); break; case Triple::mips64: T.setArch(Triple::mips64el); break; case Triple::mips: T.setArch(Triple::mipsel); break; case Triple::ppc64: T.setArch(Triple::ppc64le); break; @@ -1482,6 +1521,8 @@ bool Triple::isLittleEndian() const { case Triple::kalimba: case Triple::le32: case Triple::le64: + case Triple::maxis64el: + case Triple::maxisel: case Triple::mips64el: case Triple::mipsel: case Triple::msp430: diff --git a/lib/Support/Unix/Memory.inc b/lib/Support/Unix/Memory.inc index 848548d1..2d1854d0 100644 --- a/lib/Support/Unix/Memory.inc +++ b/lib/Support/Unix/Memory.inc @@ -222,7 +222,7 @@ void Memory::InvalidateInstructionCache(const void *Addr, for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize) asm volatile("icbi 0, %0" : : "r"(Line)); asm volatile("isync"); -# elif (defined(__arm__) || defined(__aarch64__) || defined(__mips__)) && \ +# elif (defined(__arm__) || defined(__aarch64__) || defined(__maxis__) || defined(__mips__)) && \ defined(__GNUC__) // FIXME: Can we safely always call this for __GNUC__ everywhere? const char *Start = static_cast(Addr); diff --git a/lib/Target/LLVMBuild.txt b/lib/Target/LLVMBuild.txt index 0d899a9c..aa8e6f23 100644 --- a/lib/Target/LLVMBuild.txt +++ b/lib/Target/LLVMBuild.txt @@ -29,6 +29,7 @@ subdirectories = Hexagon MSP430 NVPTX + Maxis Mips Nios2 PowerPC diff --git a/lib/Target/Maxis/AsmParser/CMakeLists.txt b/lib/Target/Maxis/AsmParser/CMakeLists.txt new file mode 100644 index 00000000..9955d0e9 --- /dev/null +++ b/lib/Target/Maxis/AsmParser/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMMaxisAsmParser + MaxisAsmParser.cpp + ) diff --git a/lib/Target/Maxis/AsmParser/LLVMBuild.txt b/lib/Target/Maxis/AsmParser/LLVMBuild.txt new file mode 100644 index 00000000..8048e82c --- /dev/null +++ b/lib/Target/Maxis/AsmParser/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/Maxis/AsmParser/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 = MaxisAsmParser +parent = Maxis +required_libraries = MC MCParser MaxisDesc MaxisInfo Support +add_to_library_groups = Maxis diff --git a/lib/Target/Maxis/AsmParser/MaxisAsmParser.cpp b/lib/Target/Maxis/AsmParser/MaxisAsmParser.cpp new file mode 100644 index 00000000..3627c95a --- /dev/null +++ b/lib/Target/Maxis/AsmParser/MaxisAsmParser.cpp @@ -0,0 +1,8020 @@ +//===-- MaxisAsmParser.cpp - Parse Maxis assembly to MCInst instructions ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/MaxisABIFlagsSection.h" +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MCTargetDesc/MaxisMCExpr.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "MaxisTargetStreamer.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCParser/MCAsmLexer.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCAsmParserExtension.h" +#include "llvm/MC/MCParser/MCParsedAsmOperand.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/MCValue.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-asm-parser" + +namespace llvm { + +class MCInstrInfo; + +} // end namespace llvm + +namespace { + +class MaxisAssemblerOptions { +public: + MaxisAssemblerOptions(const FeatureBitset &Features_) : Features(Features_) {} + + MaxisAssemblerOptions(const MaxisAssemblerOptions *Opts) { + ATReg = Opts->getATRegIndex(); + Reorder = Opts->isReorder(); + Macro = Opts->isMacro(); + Features = Opts->getFeatures(); + } + + unsigned getATRegIndex() const { return ATReg; } + bool setATRegIndex(unsigned Reg) { + if (Reg > 31) + return false; + + ATReg = Reg; + return true; + } + + bool isReorder() const { return Reorder; } + void setReorder() { Reorder = true; } + void setNoReorder() { Reorder = false; } + + bool isMacro() const { return Macro; } + void setMacro() { Macro = true; } + void setNoMacro() { Macro = false; } + + const FeatureBitset &getFeatures() const { return Features; } + void setFeatures(const FeatureBitset &Features_) { Features = Features_; } + + // Set of features that are either architecture features or referenced + // by them (e.g.: FeatureNaN2008 implied by FeatureMaxis32r6). + // The full table can be found in MaxisGenSubtargetInfo.inc (MaxisFeatureKV[]). + // The reason we need this mask is explained in the selectArch function. + // FIXME: Ideally we would like TableGen to generate this information. + static const FeatureBitset AllArchRelatedMask; + +private: + unsigned ATReg = 1; + bool Reorder = true; + bool Macro = true; + FeatureBitset Features; +}; + +} // end anonymous namespace + +const FeatureBitset MaxisAssemblerOptions::AllArchRelatedMask = { + Maxis::FeatureMaxis1, Maxis::FeatureMaxis2, Maxis::FeatureMaxis3, + Maxis::FeatureMaxis3_32, Maxis::FeatureMaxis3_32r2, Maxis::FeatureMaxis4, + Maxis::FeatureMaxis4_32, Maxis::FeatureMaxis4_32r2, Maxis::FeatureMaxis5, + Maxis::FeatureMaxis5_32r2, Maxis::FeatureMaxis32, Maxis::FeatureMaxis32r2, + Maxis::FeatureMaxis32r3, Maxis::FeatureMaxis32r5, Maxis::FeatureMaxis32r6, + Maxis::FeatureMaxis64, Maxis::FeatureMaxis64r2, Maxis::FeatureMaxis64r3, + Maxis::FeatureMaxis64r5, Maxis::FeatureMaxis64r6, Maxis::FeatureCnMaxis, + Maxis::FeatureFP64Bit, Maxis::FeatureGP64Bit, Maxis::FeatureNaN2008 +}; + +namespace { + +class MaxisAsmParser : public MCTargetAsmParser { + MaxisTargetStreamer &getTargetStreamer() { + MCTargetStreamer &TS = *getParser().getStreamer().getTargetStreamer(); + return static_cast(TS); + } + + MaxisABIInfo ABI; + SmallVector, 2> AssemblerOptions; + MCSymbol *CurrentFn; // Pointer to the function being parsed. It may be a + // nullptr, which indicates that no function is currently + // selected. This usually happens after an '.end func' + // directive. + bool IsLittleEndian; + bool IsPicEnabled; + bool IsCpRestoreSet; + int CpRestoreOffset; + unsigned CpSaveLocation; + /// If true, then CpSaveLocation is a register, otherwise it's an offset. + bool CpSaveLocationIsRegister; + + // Print a warning along with its fix-it message at the given range. + void printWarningWithFixIt(const Twine &Msg, const Twine &FixMsg, + SMRange Range, bool ShowColors = true); + +#define GET_ASSEMBLER_HEADER +#include "MaxisGenAsmMatcher.inc" + + unsigned + checkEarlyTargetMatchPredicate(MCInst &Inst, + const OperandVector &Operands) override; + unsigned checkTargetMatchPredicate(MCInst &Inst) override; + + bool MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, MCStreamer &Out, + uint64_t &ErrorInfo, + bool MatchingInlineAsm) override; + + /// Parse a register as used in CFI directives + bool ParseRegister(unsigned &RegNo, SMLoc &StartLoc, SMLoc &EndLoc) override; + + bool parseParenSuffix(StringRef Name, OperandVector &Operands); + + bool parseBracketSuffix(StringRef Name, OperandVector &Operands); + + bool mnemonicIsValid(StringRef Mnemonic, unsigned VariantID); + + bool ParseInstruction(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands) override; + + bool ParseDirective(AsmToken DirectiveID) override; + + OperandMatchResultTy parseMemOperand(OperandVector &Operands); + OperandMatchResultTy + matchAnyRegisterNameWithoutDollar(OperandVector &Operands, + StringRef Identifier, SMLoc S); + OperandMatchResultTy matchAnyRegisterWithoutDollar(OperandVector &Operands, + SMLoc S); + OperandMatchResultTy parseAnyRegister(OperandVector &Operands); + OperandMatchResultTy parseImm(OperandVector &Operands); + OperandMatchResultTy parseJumpTarget(OperandVector &Operands); + OperandMatchResultTy parseInvNum(OperandVector &Operands); + OperandMatchResultTy parseRegisterPair(OperandVector &Operands); + OperandMatchResultTy parseMovePRegPair(OperandVector &Operands); + OperandMatchResultTy parseRegisterList(OperandVector &Operands); + + bool searchSymbolAlias(OperandVector &Operands); + + bool parseOperand(OperandVector &, StringRef Mnemonic); + + enum MacroExpanderResultTy { + MER_NotAMacro, + MER_Success, + MER_Fail, + }; + + // Expands assembly pseudo instructions. + MacroExpanderResultTy tryExpandInstruction(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandJalWithRegs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool loadImmediate(int64_t ImmValue, unsigned DstReg, unsigned SrcReg, + bool Is32BitImm, bool IsAddress, SMLoc IDLoc, + MCStreamer &Out, const MCSubtargetInfo *STI); + + bool loadAndAddSymbolAddress(const MCExpr *SymExpr, unsigned DstReg, + unsigned SrcReg, bool Is32BitSym, SMLoc IDLoc, + MCStreamer &Out, const MCSubtargetInfo *STI); + + bool emitPartialAddress(MaxisTargetStreamer &TOut, SMLoc IDLoc, MCSymbol *Sym); + + bool expandLoadImm(MCInst &Inst, bool Is32BitImm, SMLoc IDLoc, + MCStreamer &Out, const MCSubtargetInfo *STI); + + bool expandLoadImmReal(MCInst &Inst, bool IsSingle, bool IsGPR, bool Is64FPU, + SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandLoadAddress(unsigned DstReg, unsigned BaseReg, + const MCOperand &Offset, bool Is32BitAddress, + SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandUncondBranchMMPseudo(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + void expandMemInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsLoad, bool IsImmOpnd); + + void expandLoadInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsImmOpnd); + + void expandStoreInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsImmOpnd); + + bool expandLoadStoreMultiple(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandAliasImmediate(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandBranchImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandCondBranches(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandDiv(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, const bool IsMaxis64, + const bool Signed); + + bool expandTrunc(MCInst &Inst, bool IsDouble, bool Is64FPU, SMLoc IDLoc, + MCStreamer &Out, const MCSubtargetInfo *STI); + + bool expandUlh(MCInst &Inst, bool Signed, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandUsh(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandUxw(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandRotation(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, const MCSubtargetInfo *STI); + bool expandRotationImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + bool expandDRotation(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + bool expandDRotationImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandMulImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandMulO(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandMulOU(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandDMULMacro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandLoadStoreDMacro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsLoad); + + bool expandSeq(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandSeqI(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool expandMXTRAlias(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + bool reportParseError(Twine ErrorMsg); + bool reportParseError(SMLoc Loc, Twine ErrorMsg); + + bool parseMemOffset(const MCExpr *&Res, bool isParenExpr); + + bool isEvaluated(const MCExpr *Expr); + bool parseSetMaxis0Directive(); + bool parseSetArchDirective(); + bool parseSetFeature(uint64_t Feature); + bool isPicAndNotNxxAbi(); // Used by .cpload, .cprestore, and .cpsetup. + bool parseDirectiveCpLoad(SMLoc Loc); + bool parseDirectiveCpRestore(SMLoc Loc); + bool parseDirectiveCPSetup(); + bool parseDirectiveCPReturn(); + bool parseDirectiveNaN(); + bool parseDirectiveSet(); + bool parseDirectiveOption(); + bool parseInsnDirective(); + bool parseRSectionDirective(StringRef Section); + bool parseSSectionDirective(StringRef Section, unsigned Type); + + bool parseSetAtDirective(); + bool parseSetNoAtDirective(); + bool parseSetMacroDirective(); + bool parseSetNoMacroDirective(); + bool parseSetMsaDirective(); + bool parseSetNoMsaDirective(); + bool parseSetNoDspDirective(); + bool parseSetReorderDirective(); + bool parseSetNoReorderDirective(); + bool parseSetMaxis16Directive(); + bool parseSetNoMaxis16Directive(); + bool parseSetFpDirective(); + bool parseSetOddSPRegDirective(); + bool parseSetNoOddSPRegDirective(); + bool parseSetPopDirective(); + bool parseSetPushDirective(); + bool parseSetSoftFloatDirective(); + bool parseSetHardFloatDirective(); + bool parseSetMtDirective(); + bool parseSetNoMtDirective(); + + bool parseSetAssignment(); + + bool parseDataDirective(unsigned Size, SMLoc L); + bool parseDirectiveGpWord(); + bool parseDirectiveGpDWord(); + bool parseDirectiveDtpRelWord(); + bool parseDirectiveDtpRelDWord(); + bool parseDirectiveTpRelWord(); + bool parseDirectiveTpRelDWord(); + bool parseDirectiveModule(); + bool parseDirectiveModuleFP(); + bool parseFpABIValue(MaxisABIFlagsSection::FpABIKind &FpABI, + StringRef Directive); + + bool parseInternalDirectiveReallowModule(); + + bool eatComma(StringRef ErrorStr); + + int matchCPURegisterName(StringRef Symbol); + + int matchHWRegsRegisterName(StringRef Symbol); + + int matchFPURegisterName(StringRef Name); + + int matchFCCRegisterName(StringRef Name); + + int matchACRegisterName(StringRef Name); + + int matchMSA128RegisterName(StringRef Name); + + int matchMSA128CtrlRegisterName(StringRef Name); + + unsigned getReg(int RC, int RegNo); + + /// Returns the internal register number for the current AT. Also checks if + /// the current AT is unavailable (set to $0) and gives an error if it is. + /// This should be used in pseudo-instruction expansions which need AT. + unsigned getATReg(SMLoc Loc); + + bool canUseATReg(); + + bool processInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI); + + // Helper function that checks if the value of a vector index is within the + // boundaries of accepted values for each RegisterKind + // Example: INSERT.B $w0[n], $1 => 16 > n >= 0 + bool validateMSAIndex(int Val, int RegKind); + + // Selects a new architecture by updating the FeatureBits with the necessary + // info including implied dependencies. + // Internally, it clears all the feature bits related to *any* architecture + // and selects the new one using the ToggleFeature functionality of the + // MCSubtargetInfo object that handles implied dependencies. The reason we + // clear all the arch related bits manually is because ToggleFeature only + // clears the features that imply the feature being cleared and not the + // features implied by the feature being cleared. This is easier to see + // with an example: + // -------------------------------------------------- + // | Feature | Implies | + // | -------------------------------------------------| + // | FeatureMaxis1 | None | + // | FeatureMaxis2 | FeatureMaxis1 | + // | FeatureMaxis3 | FeatureMaxis2 | FeatureMaxisGP64 | + // | FeatureMaxis4 | FeatureMaxis3 | + // | ... | | + // -------------------------------------------------- + // + // Setting Maxis3 is equivalent to set: (FeatureMaxis3 | FeatureMaxis2 | + // FeatureMaxisGP64 | FeatureMaxis1) + // Clearing Maxis3 is equivalent to clear (FeatureMaxis3 | FeatureMaxis4). + void selectArch(StringRef ArchFeature) { + MCSubtargetInfo &STI = copySTI(); + FeatureBitset FeatureBits = STI.getFeatureBits(); + FeatureBits &= ~MaxisAssemblerOptions::AllArchRelatedMask; + STI.setFeatureBits(FeatureBits); + setAvailableFeatures( + ComputeAvailableFeatures(STI.ToggleFeature(ArchFeature))); + AssemblerOptions.back()->setFeatures(STI.getFeatureBits()); + } + + void setFeatureBits(uint64_t Feature, StringRef FeatureString) { + if (!(getSTI().getFeatureBits()[Feature])) { + MCSubtargetInfo &STI = copySTI(); + setAvailableFeatures( + ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); + AssemblerOptions.back()->setFeatures(STI.getFeatureBits()); + } + } + + void clearFeatureBits(uint64_t Feature, StringRef FeatureString) { + if (getSTI().getFeatureBits()[Feature]) { + MCSubtargetInfo &STI = copySTI(); + setAvailableFeatures( + ComputeAvailableFeatures(STI.ToggleFeature(FeatureString))); + AssemblerOptions.back()->setFeatures(STI.getFeatureBits()); + } + } + + void setModuleFeatureBits(uint64_t Feature, StringRef FeatureString) { + setFeatureBits(Feature, FeatureString); + AssemblerOptions.front()->setFeatures(getSTI().getFeatureBits()); + } + + void clearModuleFeatureBits(uint64_t Feature, StringRef FeatureString) { + clearFeatureBits(Feature, FeatureString); + AssemblerOptions.front()->setFeatures(getSTI().getFeatureBits()); + } + +public: + enum MaxisMatchResultTy { + Match_RequiresDifferentSrcAndDst = FIRST_TARGET_MATCH_RESULT_TY, + Match_RequiresDifferentOperands, + Match_RequiresNoZeroRegister, + Match_RequiresSameSrcAndDst, + Match_NoFCCRegisterForCurrentISA, + Match_NonZeroOperandForSync, + Match_RequiresPosSizeRange0_32, + Match_RequiresPosSizeRange33_64, + Match_RequiresPosSizeUImm6, +#define GET_OPERAND_DIAGNOSTIC_TYPES +#include "MaxisGenAsmMatcher.inc" +#undef GET_OPERAND_DIAGNOSTIC_TYPES + }; + + MaxisAsmParser(const MCSubtargetInfo &sti, MCAsmParser &parser, + const MCInstrInfo &MII, const MCTargetOptions &Options) + : MCTargetAsmParser(Options, sti, MII), + ABI(MaxisABIInfo::computeTargetABI(Triple(sti.getTargetTriple()), + sti.getCPU(), Options)) { + MCAsmParserExtension::Initialize(parser); + + parser.addAliasForDirective(".asciiz", ".asciz"); + + // Initialize the set of available features. + setAvailableFeatures(ComputeAvailableFeatures(getSTI().getFeatureBits())); + + // Remember the initial assembler options. The user can not modify these. + AssemblerOptions.push_back( + llvm::make_unique(getSTI().getFeatureBits())); + + // Create an assembler options environment for the user to modify. + AssemblerOptions.push_back( + llvm::make_unique(getSTI().getFeatureBits())); + + getTargetStreamer().updateABIInfo(*this); + + if (!isABI_O32() && !useOddSPReg() != 0) + report_fatal_error("-mno-odd-spreg requires the O32 ABI"); + + CurrentFn = nullptr; + + IsPicEnabled = getContext().getObjectFileInfo()->isPositionIndependent(); + + IsCpRestoreSet = false; + CpRestoreOffset = -1; + + const Triple &TheTriple = sti.getTargetTriple(); + if ((TheTriple.getArch() == Triple::maxis) || + (TheTriple.getArch() == Triple::maxis64)) + IsLittleEndian = false; + else + IsLittleEndian = true; + + if (getSTI().getCPU() == "maxis64r6" && inMicroMaxisMode()) + report_fatal_error("microMAXIS64R6 is not supported", false); + } + + /// True if all of $fcc0 - $fcc7 exist for the current ISA. + bool hasEightFccRegisters() const { return hasMaxis4() || hasMaxis32(); } + + bool isGP64bit() const { + return getSTI().getFeatureBits()[Maxis::FeatureGP64Bit]; + } + + bool isFP64bit() const { + return getSTI().getFeatureBits()[Maxis::FeatureFP64Bit]; + } + + const MaxisABIInfo &getABI() const { return ABI; } + bool isABI_N32() const { return ABI.IsN32(); } + bool isABI_N64() const { return ABI.IsN64(); } + bool isABI_O32() const { return ABI.IsO32(); } + bool isABI_FPXX() const { + return getSTI().getFeatureBits()[Maxis::FeatureFPXX]; + } + + bool useOddSPReg() const { + return !(getSTI().getFeatureBits()[Maxis::FeatureNoOddSPReg]); + } + + bool inMicroMaxisMode() const { + return getSTI().getFeatureBits()[Maxis::FeatureMicroMaxis]; + } + + bool hasMaxis1() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis1]; + } + + bool hasMaxis2() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis2]; + } + + bool hasMaxis3() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis3]; + } + + bool hasMaxis4() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis4]; + } + + bool hasMaxis5() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis5]; + } + + bool hasMaxis32() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis32]; + } + + bool hasMaxis64() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis64]; + } + + bool hasMaxis32r2() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis32r2]; + } + + bool hasMaxis64r2() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis64r2]; + } + + bool hasMaxis32r3() const { + return (getSTI().getFeatureBits()[Maxis::FeatureMaxis32r3]); + } + + bool hasMaxis64r3() const { + return (getSTI().getFeatureBits()[Maxis::FeatureMaxis64r3]); + } + + bool hasMaxis32r5() const { + return (getSTI().getFeatureBits()[Maxis::FeatureMaxis32r5]); + } + + bool hasMaxis64r5() const { + return (getSTI().getFeatureBits()[Maxis::FeatureMaxis64r5]); + } + + bool hasMaxis32r6() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis32r6]; + } + + bool hasMaxis64r6() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis64r6]; + } + + bool hasDSP() const { + return getSTI().getFeatureBits()[Maxis::FeatureDSP]; + } + + bool hasDSPR2() const { + return getSTI().getFeatureBits()[Maxis::FeatureDSPR2]; + } + + bool hasDSPR3() const { + return getSTI().getFeatureBits()[Maxis::FeatureDSPR3]; + } + + bool hasMSA() const { + return getSTI().getFeatureBits()[Maxis::FeatureMSA]; + } + + bool hasCnMaxis() const { + return (getSTI().getFeatureBits()[Maxis::FeatureCnMaxis]); + } + + bool inPicMode() { + return IsPicEnabled; + } + + bool inMaxis16Mode() const { + return getSTI().getFeatureBits()[Maxis::FeatureMaxis16]; + } + + bool useTraps() const { + return getSTI().getFeatureBits()[Maxis::FeatureUseTCCInDIV]; + } + + bool useSoftFloat() const { + return getSTI().getFeatureBits()[Maxis::FeatureSoftFloat]; + } + bool hasMT() const { + return getSTI().getFeatureBits()[Maxis::FeatureMT]; + } + + /// Warn if RegIndex is the same as the current AT. + void warnIfRegIndexIsAT(unsigned RegIndex, SMLoc Loc); + + void warnIfNoMacro(SMLoc Loc); + + bool isLittle() const { return IsLittleEndian; } + + const MCExpr *createTargetUnaryExpr(const MCExpr *E, + AsmToken::TokenKind OperatorToken, + MCContext &Ctx) override { + switch(OperatorToken) { + default: + llvm_unreachable("Unknown token"); + return nullptr; + case AsmToken::PercentCall16: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_CALL, E, Ctx); + case AsmToken::PercentCall_Hi: + return MaxisMCExpr::create(MaxisMCExpr::MEK_CALL_HI16, E, Ctx); + case AsmToken::PercentCall_Lo: + return MaxisMCExpr::create(MaxisMCExpr::MEK_CALL_LO16, E, Ctx); + case AsmToken::PercentDtprel_Hi: + return MaxisMCExpr::create(MaxisMCExpr::MEK_DTPREL_HI, E, Ctx); + case AsmToken::PercentDtprel_Lo: + return MaxisMCExpr::create(MaxisMCExpr::MEK_DTPREL_LO, E, Ctx); + case AsmToken::PercentGot: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GOT, E, Ctx); + case AsmToken::PercentGot_Disp: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_DISP, E, Ctx); + case AsmToken::PercentGot_Hi: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_HI16, E, Ctx); + case AsmToken::PercentGot_Lo: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_LO16, E, Ctx); + case AsmToken::PercentGot_Ofst: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_OFST, E, Ctx); + case AsmToken::PercentGot_Page: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_PAGE, E, Ctx); + case AsmToken::PercentGottprel: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GOTTPREL, E, Ctx); + case AsmToken::PercentGp_Rel: + return MaxisMCExpr::create(MaxisMCExpr::MEK_GPREL, E, Ctx); + case AsmToken::PercentHi: + return MaxisMCExpr::create(MaxisMCExpr::MEK_HI, E, Ctx); + case AsmToken::PercentHigher: + return MaxisMCExpr::create(MaxisMCExpr::MEK_HIGHER, E, Ctx); + case AsmToken::PercentHighest: + return MaxisMCExpr::create(MaxisMCExpr::MEK_HIGHEST, E, Ctx); + case AsmToken::PercentLo: + return MaxisMCExpr::create(MaxisMCExpr::MEK_LO, E, Ctx); + case AsmToken::PercentNeg: + return MaxisMCExpr::create(MaxisMCExpr::MEK_NEG, E, Ctx); + case AsmToken::PercentPcrel_Hi: + return MaxisMCExpr::create(MaxisMCExpr::MEK_PCREL_HI16, E, Ctx); + case AsmToken::PercentPcrel_Lo: + return MaxisMCExpr::create(MaxisMCExpr::MEK_PCREL_LO16, E, Ctx); + case AsmToken::PercentTlsgd: + return MaxisMCExpr::create(MaxisMCExpr::MEK_TLSGD, E, Ctx); + case AsmToken::PercentTlsldm: + return MaxisMCExpr::create(MaxisMCExpr::MEK_TLSLDM, E, Ctx); + case AsmToken::PercentTprel_Hi: + return MaxisMCExpr::create(MaxisMCExpr::MEK_TPREL_HI, E, Ctx); + case AsmToken::PercentTprel_Lo: + return MaxisMCExpr::create(MaxisMCExpr::MEK_TPREL_LO, E, Ctx); + } + } +}; + +/// MaxisOperand - Instances of this class represent a parsed Maxis machine +/// instruction. +class MaxisOperand : public MCParsedAsmOperand { +public: + /// Broad categories of register classes + /// The exact class is finalized by the render method. + enum RegKind { + RegKind_GPR = 1, /// GPR32 and GPR64 (depending on isGP64bit()) + RegKind_FGR = 2, /// FGR32, FGR64, AFGR64 (depending on context and + /// isFP64bit()) + RegKind_FCC = 4, /// FCC + RegKind_MSA128 = 8, /// MSA128[BHWD] (makes no difference which) + RegKind_MSACtrl = 16, /// MSA control registers + RegKind_COP2 = 32, /// COP2 + RegKind_ACC = 64, /// HI32DSP, LO32DSP, and ACC64DSP (depending on + /// context). + RegKind_CCR = 128, /// CCR + RegKind_HWRegs = 256, /// HWRegs + RegKind_COP3 = 512, /// COP3 + RegKind_COP0 = 1024, /// COP0 + /// Potentially any (e.g. $1) + RegKind_Numeric = RegKind_GPR | RegKind_FGR | RegKind_FCC | RegKind_MSA128 | + RegKind_MSACtrl | RegKind_COP2 | RegKind_ACC | + RegKind_CCR | RegKind_HWRegs | RegKind_COP3 | RegKind_COP0 + }; + +private: + enum KindTy { + k_Immediate, /// An immediate (possibly involving symbol references) + k_Memory, /// Base + Offset Memory Address + k_RegisterIndex, /// A register index in one or more RegKind. + k_Token, /// A simple token + k_RegList, /// A physical register list + k_RegPair /// A pair of physical register + } Kind; + +public: + MaxisOperand(KindTy K, MaxisAsmParser &Parser) + : MCParsedAsmOperand(), Kind(K), AsmParser(Parser) {} + + ~MaxisOperand() override { + switch (Kind) { + case k_Immediate: + break; + case k_Memory: + delete Mem.Base; + break; + case k_RegList: + delete RegList.List; + case k_RegisterIndex: + case k_Token: + case k_RegPair: + break; + } + } + +private: + /// For diagnostics, and checking the assembler temporary + MaxisAsmParser &AsmParser; + + struct Token { + const char *Data; + unsigned Length; + }; + + struct RegIdxOp { + unsigned Index; /// Index into the register class + RegKind Kind; /// Bitfield of the kinds it could possibly be + struct Token Tok; /// The input token this operand originated from. + const MCRegisterInfo *RegInfo; + }; + + struct ImmOp { + const MCExpr *Val; + }; + + struct MemOp { + MaxisOperand *Base; + const MCExpr *Off; + }; + + struct RegListOp { + SmallVector *List; + }; + + union { + struct Token Tok; + struct RegIdxOp RegIdx; + struct ImmOp Imm; + struct MemOp Mem; + struct RegListOp RegList; + }; + + SMLoc StartLoc, EndLoc; + + /// Internal constructor for register kinds + static std::unique_ptr CreateReg(unsigned Index, StringRef Str, + RegKind RegKind, + const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, + MaxisAsmParser &Parser) { + auto Op = llvm::make_unique(k_RegisterIndex, Parser); + Op->RegIdx.Index = Index; + Op->RegIdx.RegInfo = RegInfo; + Op->RegIdx.Kind = RegKind; + Op->RegIdx.Tok.Data = Str.data(); + Op->RegIdx.Tok.Length = Str.size(); + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + +public: + /// Coerce the register to GPR32 and return the real register for the current + /// target. + unsigned getGPR32Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_GPR) && "Invalid access!"); + AsmParser.warnIfRegIndexIsAT(RegIdx.Index, StartLoc); + unsigned ClassID = Maxis::GPR32RegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to GPR32 and return the real register for the current + /// target. + unsigned getGPRMM16Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_GPR) && "Invalid access!"); + unsigned ClassID = Maxis::GPR32RegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to GPR64 and return the real register for the current + /// target. + unsigned getGPR64Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_GPR) && "Invalid access!"); + unsigned ClassID = Maxis::GPR64RegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + +private: + /// Coerce the register to AFGR64 and return the real register for the current + /// target. + unsigned getAFGR64Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_FGR) && "Invalid access!"); + if (RegIdx.Index % 2 != 0) + AsmParser.Warning(StartLoc, "Float register should be even."); + return RegIdx.RegInfo->getRegClass(Maxis::AFGR64RegClassID) + .getRegister(RegIdx.Index / 2); + } + + /// Coerce the register to FGR64 and return the real register for the current + /// target. + unsigned getFGR64Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_FGR) && "Invalid access!"); + return RegIdx.RegInfo->getRegClass(Maxis::FGR64RegClassID) + .getRegister(RegIdx.Index); + } + + /// Coerce the register to FGR32 and return the real register for the current + /// target. + unsigned getFGR32Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_FGR) && "Invalid access!"); + return RegIdx.RegInfo->getRegClass(Maxis::FGR32RegClassID) + .getRegister(RegIdx.Index); + } + + /// Coerce the register to FGRH32 and return the real register for the current + /// target. + unsigned getFGRH32Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_FGR) && "Invalid access!"); + return RegIdx.RegInfo->getRegClass(Maxis::FGRH32RegClassID) + .getRegister(RegIdx.Index); + } + + /// Coerce the register to FCC and return the real register for the current + /// target. + unsigned getFCCReg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_FCC) && "Invalid access!"); + return RegIdx.RegInfo->getRegClass(Maxis::FCCRegClassID) + .getRegister(RegIdx.Index); + } + + /// Coerce the register to MSA128 and return the real register for the current + /// target. + unsigned getMSA128Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_MSA128) && "Invalid access!"); + // It doesn't matter which of the MSA128[BHWD] classes we use. They are all + // identical + unsigned ClassID = Maxis::MSA128BRegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to MSACtrl and return the real register for the + /// current target. + unsigned getMSACtrlReg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_MSACtrl) && "Invalid access!"); + unsigned ClassID = Maxis::MSACtrlRegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to COP0 and return the real register for the + /// current target. + unsigned getCOP0Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_COP0) && "Invalid access!"); + unsigned ClassID = Maxis::COP0RegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to COP2 and return the real register for the + /// current target. + unsigned getCOP2Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_COP2) && "Invalid access!"); + unsigned ClassID = Maxis::COP2RegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to COP3 and return the real register for the + /// current target. + unsigned getCOP3Reg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_COP3) && "Invalid access!"); + unsigned ClassID = Maxis::COP3RegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to ACC64DSP and return the real register for the + /// current target. + unsigned getACC64DSPReg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_ACC) && "Invalid access!"); + unsigned ClassID = Maxis::ACC64DSPRegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to HI32DSP and return the real register for the + /// current target. + unsigned getHI32DSPReg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_ACC) && "Invalid access!"); + unsigned ClassID = Maxis::HI32DSPRegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to LO32DSP and return the real register for the + /// current target. + unsigned getLO32DSPReg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_ACC) && "Invalid access!"); + unsigned ClassID = Maxis::LO32DSPRegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to CCR and return the real register for the + /// current target. + unsigned getCCRReg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_CCR) && "Invalid access!"); + unsigned ClassID = Maxis::CCRRegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + + /// Coerce the register to HWRegs and return the real register for the + /// current target. + unsigned getHWRegsReg() const { + assert(isRegIdx() && (RegIdx.Kind & RegKind_HWRegs) && "Invalid access!"); + unsigned ClassID = Maxis::HWRegsRegClassID; + return RegIdx.RegInfo->getRegClass(ClassID).getRegister(RegIdx.Index); + } + +public: + void addExpr(MCInst &Inst, const MCExpr *Expr) const { + // Add as immediate when possible. Null MCExpr = 0. + if (!Expr) + Inst.addOperand(MCOperand::createImm(0)); + else if (const MCConstantExpr *CE = dyn_cast(Expr)) + Inst.addOperand(MCOperand::createImm(CE->getValue())); + else + Inst.addOperand(MCOperand::createExpr(Expr)); + } + + void addRegOperands(MCInst &Inst, unsigned N) const { + llvm_unreachable("Use a custom parser instead"); + } + + /// Render the operand to an MCInst as a GPR32 + /// Asserts if the wrong number of operands are requested, or the operand + /// is not a k_RegisterIndex compatible with RegKind_GPR + void addGPR32ZeroAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getGPR32Reg())); + } + + void addGPR32NonZeroAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getGPR32Reg())); + } + + void addGPR32AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getGPR32Reg())); + } + + void addGPRMM16AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getGPRMM16Reg())); + } + + void addGPRMM16AsmRegZeroOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getGPRMM16Reg())); + } + + void addGPRMM16AsmRegMovePOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getGPRMM16Reg())); + } + + /// Render the operand to an MCInst as a GPR64 + /// Asserts if the wrong number of operands are requested, or the operand + /// is not a k_RegisterIndex compatible with RegKind_GPR + void addGPR64AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getGPR64Reg())); + } + + void addAFGR64AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getAFGR64Reg())); + } + + void addStrictlyAFGR64AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getAFGR64Reg())); + } + + void addStrictlyFGR64AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getFGR64Reg())); + } + + void addFGR64AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getFGR64Reg())); + } + + void addFGR32AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getFGR32Reg())); + // FIXME: We ought to do this for -integrated-as without -via-file-asm too. + // FIXME: This should propagate failure up to parseStatement. + if (!AsmParser.useOddSPReg() && RegIdx.Index & 1) + AsmParser.getParser().printError( + StartLoc, "-mno-odd-spreg prohibits the use of odd FPU " + "registers"); + } + + void addStrictlyFGR32AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getFGR32Reg())); + // FIXME: We ought to do this for -integrated-as without -via-file-asm too. + if (!AsmParser.useOddSPReg() && RegIdx.Index & 1) + AsmParser.Error(StartLoc, "-mno-odd-spreg prohibits the use of odd FPU " + "registers"); + } + + void addFGRH32AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getFGRH32Reg())); + } + + void addFCCAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getFCCReg())); + } + + void addMSA128AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getMSA128Reg())); + } + + void addMSACtrlAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getMSACtrlReg())); + } + + void addCOP0AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getCOP0Reg())); + } + + void addCOP2AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getCOP2Reg())); + } + + void addCOP3AsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getCOP3Reg())); + } + + void addACC64DSPAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getACC64DSPReg())); + } + + void addHI32DSPAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getHI32DSPReg())); + } + + void addLO32DSPAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getLO32DSPReg())); + } + + void addCCRAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getCCRReg())); + } + + void addHWRegsAsmRegOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + Inst.addOperand(MCOperand::createReg(getHWRegsReg())); + } + + template + void addConstantUImmOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + uint64_t Imm = getConstantImm() - Offset; + Imm &= (1ULL << Bits) - 1; + Imm += Offset; + Imm += AdjustOffset; + Inst.addOperand(MCOperand::createImm(Imm)); + } + + template + void addSImmOperands(MCInst &Inst, unsigned N) const { + if (isImm() && !isConstantImm()) { + addExpr(Inst, getImm()); + return; + } + addConstantSImmOperands(Inst, N); + } + + template + void addUImmOperands(MCInst &Inst, unsigned N) const { + if (isImm() && !isConstantImm()) { + addExpr(Inst, getImm()); + return; + } + addConstantUImmOperands(Inst, N); + } + + template + void addConstantSImmOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + int64_t Imm = getConstantImm() - Offset; + Imm = SignExtend64(Imm); + Imm += Offset; + Imm += AdjustOffset; + Inst.addOperand(MCOperand::createImm(Imm)); + } + + void addImmOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + const MCExpr *Expr = getImm(); + addExpr(Inst, Expr); + } + + void addMemOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + + Inst.addOperand(MCOperand::createReg(AsmParser.getABI().ArePtrs64bit() + ? getMemBase()->getGPR64Reg() + : getMemBase()->getGPR32Reg())); + + const MCExpr *Expr = getMemOff(); + addExpr(Inst, Expr); + } + + void addMicroMaxisMemOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + + Inst.addOperand(MCOperand::createReg(getMemBase()->getGPRMM16Reg())); + + const MCExpr *Expr = getMemOff(); + addExpr(Inst, Expr); + } + + void addRegListOperands(MCInst &Inst, unsigned N) const { + assert(N == 1 && "Invalid number of operands!"); + + for (auto RegNo : getRegList()) + Inst.addOperand(MCOperand::createReg(RegNo)); + } + + void addRegPairOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + assert((RegIdx.Kind & RegKind_GPR) && "Invalid access!"); + unsigned RegNo = getRegPair(); + AsmParser.warnIfRegIndexIsAT(RegNo, StartLoc); + Inst.addOperand(MCOperand::createReg( + RegIdx.RegInfo->getRegClass( + AsmParser.getABI().AreGprs64bit() + ? Maxis::GPR64RegClassID + : Maxis::GPR32RegClassID).getRegister(RegNo++))); + Inst.addOperand(MCOperand::createReg( + RegIdx.RegInfo->getRegClass( + AsmParser.getABI().AreGprs64bit() + ? Maxis::GPR64RegClassID + : Maxis::GPR32RegClassID).getRegister(RegNo))); + } + + void addMovePRegPairOperands(MCInst &Inst, unsigned N) const { + assert(N == 2 && "Invalid number of operands!"); + for (auto RegNo : getRegList()) + Inst.addOperand(MCOperand::createReg(RegNo)); + } + + bool isReg() const override { + // As a special case until we sort out the definition of div/divu, accept + // $0/$zero here so that MCK_ZERO works correctly. + return isGPRAsmReg() && RegIdx.Index == 0; + } + + bool isRegIdx() const { return Kind == k_RegisterIndex; } + bool isImm() const override { return Kind == k_Immediate; } + + bool isConstantImm() const { + int64_t Res; + return isImm() && getImm()->evaluateAsAbsolute(Res); + } + + bool isConstantImmz() const { + return isConstantImm() && getConstantImm() == 0; + } + + template bool isConstantUImm() const { + return isConstantImm() && isUInt(getConstantImm() - Offset); + } + + template bool isSImm() const { + return isConstantImm() ? isInt(getConstantImm()) : isImm(); + } + + template bool isUImm() const { + return isConstantImm() ? isUInt(getConstantImm()) : isImm(); + } + + template bool isAnyImm() const { + return isConstantImm() ? (isInt(getConstantImm()) || + isUInt(getConstantImm())) + : isImm(); + } + + template bool isConstantSImm() const { + return isConstantImm() && isInt(getConstantImm() - Offset); + } + + template bool isConstantUImmRange() const { + return isConstantImm() && getConstantImm() >= Bottom && + getConstantImm() <= Top; + } + + bool isToken() const override { + // Note: It's not possible to pretend that other operand kinds are tokens. + // The matcher emitter checks tokens first. + return Kind == k_Token; + } + + bool isMem() const override { return Kind == k_Memory; } + + bool isConstantMemOff() const { + return isMem() && isa(getMemOff()); + } + + // Allow relocation operators. + // FIXME: This predicate and others need to look through binary expressions + // and determine whether a Value is a constant or not. + template + bool isMemWithSimmOffset() const { + if (!isMem()) + return false; + if (!getMemBase()->isGPRAsmReg()) + return false; + if (isa(getMemOff()) || + (isConstantMemOff() && + isShiftedInt(getConstantMemOff()))) + return true; + MCValue Res; + bool IsReloc = getMemOff()->evaluateAsRelocatable(Res, nullptr, nullptr); + return IsReloc && isShiftedInt(Res.getConstant()); + } + + bool isMemWithGRPMM16Base() const { + return isMem() && getMemBase()->isMM16AsmReg(); + } + + template bool isMemWithUimmOffsetSP() const { + return isMem() && isConstantMemOff() && isUInt(getConstantMemOff()) + && getMemBase()->isRegIdx() && (getMemBase()->getGPR32Reg() == Maxis::SP); + } + + template bool isMemWithUimmWordAlignedOffsetSP() const { + return isMem() && isConstantMemOff() && isUInt(getConstantMemOff()) + && (getConstantMemOff() % 4 == 0) && getMemBase()->isRegIdx() + && (getMemBase()->getGPR32Reg() == Maxis::SP); + } + + template bool isMemWithSimmWordAlignedOffsetGP() const { + return isMem() && isConstantMemOff() && isInt(getConstantMemOff()) + && (getConstantMemOff() % 4 == 0) && getMemBase()->isRegIdx() + && (getMemBase()->getGPR32Reg() == Maxis::GP); + } + + template + bool isScaledUImm() const { + return isConstantImm() && + isShiftedUInt(getConstantImm()); + } + + template + bool isScaledSImm() const { + if (isConstantImm() && isShiftedInt(getConstantImm())) + return true; + // Operand can also be a symbol or symbol plus offset in case of relocations. + if (Kind != k_Immediate) + return false; + MCValue Res; + bool Success = getImm()->evaluateAsRelocatable(Res, nullptr, nullptr); + return Success && isShiftedInt(Res.getConstant()); + } + + bool isRegList16() const { + if (!isRegList()) + return false; + + int Size = RegList.List->size(); + if (Size < 2 || Size > 5) + return false; + + unsigned R0 = RegList.List->front(); + unsigned R1 = RegList.List->back(); + if (!((R0 == Maxis::S0 && R1 == Maxis::RA) || + (R0 == Maxis::S0_64 && R1 == Maxis::RA_64))) + return false; + + int PrevReg = *RegList.List->begin(); + for (int i = 1; i < Size - 1; i++) { + int Reg = (*(RegList.List))[i]; + if ( Reg != PrevReg + 1) + return false; + PrevReg = Reg; + } + + return true; + } + + bool isInvNum() const { return Kind == k_Immediate; } + + bool isLSAImm() const { + if (!isConstantImm()) + return false; + int64_t Val = getConstantImm(); + return 1 <= Val && Val <= 4; + } + + bool isRegList() const { return Kind == k_RegList; } + + bool isMovePRegPair() const { + if (Kind != k_RegList || RegList.List->size() != 2) + return false; + + unsigned R0 = RegList.List->front(); + unsigned R1 = RegList.List->back(); + + if ((R0 == Maxis::A1 && R1 == Maxis::A2) || + (R0 == Maxis::A1 && R1 == Maxis::A3) || + (R0 == Maxis::A2 && R1 == Maxis::A3) || + (R0 == Maxis::A0 && R1 == Maxis::S5) || + (R0 == Maxis::A0 && R1 == Maxis::S6) || + (R0 == Maxis::A0 && R1 == Maxis::A1) || + (R0 == Maxis::A0 && R1 == Maxis::A2) || + (R0 == Maxis::A0 && R1 == Maxis::A3) || + (R0 == Maxis::A1_64 && R1 == Maxis::A2_64) || + (R0 == Maxis::A1_64 && R1 == Maxis::A3_64) || + (R0 == Maxis::A2_64 && R1 == Maxis::A3_64) || + (R0 == Maxis::A0_64 && R1 == Maxis::S5_64) || + (R0 == Maxis::A0_64 && R1 == Maxis::S6_64) || + (R0 == Maxis::A0_64 && R1 == Maxis::A1_64) || + (R0 == Maxis::A0_64 && R1 == Maxis::A2_64) || + (R0 == Maxis::A0_64 && R1 == Maxis::A3_64)) + return true; + + return false; + } + + StringRef getToken() const { + assert(Kind == k_Token && "Invalid access!"); + return StringRef(Tok.Data, Tok.Length); + } + + bool isRegPair() const { + return Kind == k_RegPair && RegIdx.Index <= 30; + } + + unsigned getReg() const override { + // As a special case until we sort out the definition of div/divu, accept + // $0/$zero here so that MCK_ZERO works correctly. + if (Kind == k_RegisterIndex && RegIdx.Index == 0 && + RegIdx.Kind & RegKind_GPR) + return getGPR32Reg(); // FIXME: GPR64 too + + llvm_unreachable("Invalid access!"); + return 0; + } + + const MCExpr *getImm() const { + assert((Kind == k_Immediate) && "Invalid access!"); + return Imm.Val; + } + + int64_t getConstantImm() const { + const MCExpr *Val = getImm(); + int64_t Value = 0; + (void)Val->evaluateAsAbsolute(Value); + return Value; + } + + MaxisOperand *getMemBase() const { + assert((Kind == k_Memory) && "Invalid access!"); + return Mem.Base; + } + + const MCExpr *getMemOff() const { + assert((Kind == k_Memory) && "Invalid access!"); + return Mem.Off; + } + + int64_t getConstantMemOff() const { + return static_cast(getMemOff())->getValue(); + } + + const SmallVectorImpl &getRegList() const { + assert((Kind == k_RegList) && "Invalid access!"); + return *(RegList.List); + } + + unsigned getRegPair() const { + assert((Kind == k_RegPair) && "Invalid access!"); + return RegIdx.Index; + } + + static std::unique_ptr CreateToken(StringRef Str, SMLoc S, + MaxisAsmParser &Parser) { + auto Op = llvm::make_unique(k_Token, Parser); + Op->Tok.Data = Str.data(); + Op->Tok.Length = Str.size(); + Op->StartLoc = S; + Op->EndLoc = S; + return Op; + } + + /// Create a numeric register (e.g. $1). The exact register remains + /// unresolved until an instruction successfully matches + static std::unique_ptr + createNumericReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + DEBUG(dbgs() << "createNumericReg(" << Index << ", ...)\n"); + return CreateReg(Index, Str, RegKind_Numeric, RegInfo, S, E, Parser); + } + + /// Create a register that is definitely a GPR. + /// This is typically only used for named registers such as $gp. + static std::unique_ptr + createGPRReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + return CreateReg(Index, Str, RegKind_GPR, RegInfo, S, E, Parser); + } + + /// Create a register that is definitely a FGR. + /// This is typically only used for named registers such as $f0. + static std::unique_ptr + createFGRReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + return CreateReg(Index, Str, RegKind_FGR, RegInfo, S, E, Parser); + } + + /// Create a register that is definitely a HWReg. + /// This is typically only used for named registers such as $hwr_cpunum. + static std::unique_ptr + createHWRegsReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + return CreateReg(Index, Str, RegKind_HWRegs, RegInfo, S, E, Parser); + } + + /// Create a register that is definitely an FCC. + /// This is typically only used for named registers such as $fcc0. + static std::unique_ptr + createFCCReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + return CreateReg(Index, Str, RegKind_FCC, RegInfo, S, E, Parser); + } + + /// Create a register that is definitely an ACC. + /// This is typically only used for named registers such as $ac0. + static std::unique_ptr + createACCReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + return CreateReg(Index, Str, RegKind_ACC, RegInfo, S, E, Parser); + } + + /// Create a register that is definitely an MSA128. + /// This is typically only used for named registers such as $w0. + static std::unique_ptr + createMSA128Reg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + return CreateReg(Index, Str, RegKind_MSA128, RegInfo, S, E, Parser); + } + + /// Create a register that is definitely an MSACtrl. + /// This is typically only used for named registers such as $msaaccess. + static std::unique_ptr + createMSACtrlReg(unsigned Index, StringRef Str, const MCRegisterInfo *RegInfo, + SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + return CreateReg(Index, Str, RegKind_MSACtrl, RegInfo, S, E, Parser); + } + + static std::unique_ptr + CreateImm(const MCExpr *Val, SMLoc S, SMLoc E, MaxisAsmParser &Parser) { + auto Op = llvm::make_unique(k_Immediate, Parser); + Op->Imm.Val = Val; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + + static std::unique_ptr + CreateMem(std::unique_ptr Base, const MCExpr *Off, SMLoc S, + SMLoc E, MaxisAsmParser &Parser) { + auto Op = llvm::make_unique(k_Memory, Parser); + Op->Mem.Base = Base.release(); + Op->Mem.Off = Off; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + + static std::unique_ptr + CreateRegList(SmallVectorImpl &Regs, SMLoc StartLoc, SMLoc EndLoc, + MaxisAsmParser &Parser) { + assert(Regs.size() > 0 && "Empty list not allowed"); + + auto Op = llvm::make_unique(k_RegList, Parser); + Op->RegList.List = new SmallVector(Regs.begin(), Regs.end()); + Op->StartLoc = StartLoc; + Op->EndLoc = EndLoc; + return Op; + } + + static std::unique_ptr CreateRegPair(const MaxisOperand &MOP, + SMLoc S, SMLoc E, + MaxisAsmParser &Parser) { + auto Op = llvm::make_unique(k_RegPair, Parser); + Op->RegIdx.Index = MOP.RegIdx.Index; + Op->RegIdx.RegInfo = MOP.RegIdx.RegInfo; + Op->RegIdx.Kind = MOP.RegIdx.Kind; + Op->StartLoc = S; + Op->EndLoc = E; + return Op; + } + + bool isGPRZeroAsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index == 0; + } + + bool isGPRNonZeroAsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index > 0 && + RegIdx.Index <= 31; + } + + bool isGPRAsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_GPR && RegIdx.Index <= 31; + } + + bool isMM16AsmReg() const { + if (!(isRegIdx() && RegIdx.Kind)) + return false; + return ((RegIdx.Index >= 2 && RegIdx.Index <= 7) + || RegIdx.Index == 16 || RegIdx.Index == 17); + + } + bool isMM16AsmRegZero() const { + if (!(isRegIdx() && RegIdx.Kind)) + return false; + return (RegIdx.Index == 0 || + (RegIdx.Index >= 2 && RegIdx.Index <= 7) || + RegIdx.Index == 17); + } + + bool isMM16AsmRegMoveP() const { + if (!(isRegIdx() && RegIdx.Kind)) + return false; + return (RegIdx.Index == 0 || (RegIdx.Index >= 2 && RegIdx.Index <= 3) || + (RegIdx.Index >= 16 && RegIdx.Index <= 20)); + } + + bool isFGRAsmReg() const { + // AFGR64 is $0-$15 but we handle this in getAFGR64() + return isRegIdx() && RegIdx.Kind & RegKind_FGR && RegIdx.Index <= 31; + } + + bool isStrictlyFGRAsmReg() const { + // AFGR64 is $0-$15 but we handle this in getAFGR64() + return isRegIdx() && RegIdx.Kind == RegKind_FGR && RegIdx.Index <= 31; + } + + bool isHWRegsAsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_HWRegs && RegIdx.Index <= 31; + } + + bool isCCRAsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_CCR && RegIdx.Index <= 31; + } + + bool isFCCAsmReg() const { + if (!(isRegIdx() && RegIdx.Kind & RegKind_FCC)) + return false; + return RegIdx.Index <= 7; + } + + bool isACCAsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_ACC && RegIdx.Index <= 3; + } + + bool isCOP0AsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_COP0 && RegIdx.Index <= 31; + } + + bool isCOP2AsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_COP2 && RegIdx.Index <= 31; + } + + bool isCOP3AsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_COP3 && RegIdx.Index <= 31; + } + + bool isMSA128AsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_MSA128 && RegIdx.Index <= 31; + } + + bool isMSACtrlAsmReg() const { + return isRegIdx() && RegIdx.Kind & RegKind_MSACtrl && RegIdx.Index <= 7; + } + + /// getStartLoc - Get the location of the first token of this operand. + SMLoc getStartLoc() const override { return StartLoc; } + /// getEndLoc - Get the location of the last token of this operand. + SMLoc getEndLoc() const override { return EndLoc; } + + void print(raw_ostream &OS) const override { + switch (Kind) { + case k_Immediate: + OS << "Imm<"; + OS << *Imm.Val; + OS << ">"; + break; + case k_Memory: + OS << "Mem<"; + Mem.Base->print(OS); + OS << ", "; + OS << *Mem.Off; + OS << ">"; + break; + case k_RegisterIndex: + OS << "RegIdx<" << RegIdx.Index << ":" << RegIdx.Kind << ", " + << StringRef(RegIdx.Tok.Data, RegIdx.Tok.Length) << ">"; + break; + case k_Token: + OS << getToken(); + break; + case k_RegList: + OS << "RegList< "; + for (auto Reg : (*RegList.List)) + OS << Reg << " "; + OS << ">"; + break; + case k_RegPair: + OS << "RegPair<" << RegIdx.Index << "," << RegIdx.Index + 1 << ">"; + break; + } + } + + bool isValidForTie(const MaxisOperand &Other) const { + if (Kind != Other.Kind) + return false; + + switch (Kind) { + default: + llvm_unreachable("Unexpected kind"); + return false; + case k_RegisterIndex: { + StringRef Token(RegIdx.Tok.Data, RegIdx.Tok.Length); + StringRef OtherToken(Other.RegIdx.Tok.Data, Other.RegIdx.Tok.Length); + return Token == OtherToken; + } + } + } +}; // class MaxisOperand + +} // end anonymous namespace + +namespace llvm { + +extern const MCInstrDesc MaxisInsts[]; + +} // end namespace llvm + +static const MCInstrDesc &getInstDesc(unsigned Opcode) { + return MaxisInsts[Opcode]; +} + +static bool hasShortDelaySlot(unsigned Opcode) { + switch (Opcode) { + case Maxis::JALS_MM: + case Maxis::JALRS_MM: + case Maxis::JALRS16_MM: + case Maxis::BGEZALS_MM: + case Maxis::BLTZALS_MM: + return true; + default: + return false; + } +} + +static const MCSymbol *getSingleMCSymbol(const MCExpr *Expr) { + if (const MCSymbolRefExpr *SRExpr = dyn_cast(Expr)) { + return &SRExpr->getSymbol(); + } + + if (const MCBinaryExpr *BExpr = dyn_cast(Expr)) { + const MCSymbol *LHSSym = getSingleMCSymbol(BExpr->getLHS()); + const MCSymbol *RHSSym = getSingleMCSymbol(BExpr->getRHS()); + + if (LHSSym) + return LHSSym; + + if (RHSSym) + return RHSSym; + + return nullptr; + } + + if (const MCUnaryExpr *UExpr = dyn_cast(Expr)) + return getSingleMCSymbol(UExpr->getSubExpr()); + + return nullptr; +} + +static unsigned countMCSymbolRefExpr(const MCExpr *Expr) { + if (isa(Expr)) + return 1; + + if (const MCBinaryExpr *BExpr = dyn_cast(Expr)) + return countMCSymbolRefExpr(BExpr->getLHS()) + + countMCSymbolRefExpr(BExpr->getRHS()); + + if (const MCUnaryExpr *UExpr = dyn_cast(Expr)) + return countMCSymbolRefExpr(UExpr->getSubExpr()); + + return 0; +} + +bool MaxisAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + const MCInstrDesc &MCID = getInstDesc(Inst.getOpcode()); + bool ExpandedJalSym = false; + + Inst.setLoc(IDLoc); + + if (MCID.isBranch() || MCID.isCall()) { + const unsigned Opcode = Inst.getOpcode(); + MCOperand Offset; + + switch (Opcode) { + default: + break; + case Maxis::BBIT0: + case Maxis::BBIT032: + case Maxis::BBIT1: + case Maxis::BBIT132: + assert(hasCnMaxis() && "instruction only valid for octeon cpus"); + LLVM_FALLTHROUGH; + + case Maxis::BEQ: + case Maxis::BNE: + case Maxis::BEQ_MM: + case Maxis::BNE_MM: + assert(MCID.getNumOperands() == 3 && "unexpected number of operands"); + Offset = Inst.getOperand(2); + if (!Offset.isImm()) + break; // We'll deal with this situation later on when applying fixups. + if (!isIntN(inMicroMaxisMode() ? 17 : 18, Offset.getImm())) + return Error(IDLoc, "branch target out of range"); + if (OffsetToAlignment(Offset.getImm(), + 1LL << (inMicroMaxisMode() ? 1 : 2))) + return Error(IDLoc, "branch to misaligned address"); + break; + case Maxis::BGEZ: + case Maxis::BGTZ: + case Maxis::BLEZ: + case Maxis::BLTZ: + case Maxis::BGEZAL: + case Maxis::BLTZAL: + case Maxis::BC1F: + case Maxis::BC1T: + case Maxis::BGEZ_MM: + case Maxis::BGTZ_MM: + case Maxis::BLEZ_MM: + case Maxis::BLTZ_MM: + case Maxis::BGEZAL_MM: + case Maxis::BLTZAL_MM: + case Maxis::BC1F_MM: + case Maxis::BC1T_MM: + case Maxis::BC1EQZC_MMR6: + case Maxis::BC1NEZC_MMR6: + case Maxis::BC2EQZC_MMR6: + case Maxis::BC2NEZC_MMR6: + assert(MCID.getNumOperands() == 2 && "unexpected number of operands"); + Offset = Inst.getOperand(1); + if (!Offset.isImm()) + break; // We'll deal with this situation later on when applying fixups. + if (!isIntN(inMicroMaxisMode() ? 17 : 18, Offset.getImm())) + return Error(IDLoc, "branch target out of range"); + if (OffsetToAlignment(Offset.getImm(), + 1LL << (inMicroMaxisMode() ? 1 : 2))) + return Error(IDLoc, "branch to misaligned address"); + break; + case Maxis::BGEC: case Maxis::BGEC_MMR6: + case Maxis::BLTC: case Maxis::BLTC_MMR6: + case Maxis::BGEUC: case Maxis::BGEUC_MMR6: + case Maxis::BLTUC: case Maxis::BLTUC_MMR6: + case Maxis::BEQC: case Maxis::BEQC_MMR6: + case Maxis::BNEC: case Maxis::BNEC_MMR6: + assert(MCID.getNumOperands() == 3 && "unexpected number of operands"); + Offset = Inst.getOperand(2); + if (!Offset.isImm()) + break; // We'll deal with this situation later on when applying fixups. + if (!isIntN(18, Offset.getImm())) + return Error(IDLoc, "branch target out of range"); + if (OffsetToAlignment(Offset.getImm(), 1LL << 2)) + return Error(IDLoc, "branch to misaligned address"); + break; + case Maxis::BLEZC: case Maxis::BLEZC_MMR6: + case Maxis::BGEZC: case Maxis::BGEZC_MMR6: + case Maxis::BGTZC: case Maxis::BGTZC_MMR6: + case Maxis::BLTZC: case Maxis::BLTZC_MMR6: + assert(MCID.getNumOperands() == 2 && "unexpected number of operands"); + Offset = Inst.getOperand(1); + if (!Offset.isImm()) + break; // We'll deal with this situation later on when applying fixups. + if (!isIntN(18, Offset.getImm())) + return Error(IDLoc, "branch target out of range"); + if (OffsetToAlignment(Offset.getImm(), 1LL << 2)) + return Error(IDLoc, "branch to misaligned address"); + break; + case Maxis::BEQZC: case Maxis::BEQZC_MMR6: + case Maxis::BNEZC: case Maxis::BNEZC_MMR6: + assert(MCID.getNumOperands() == 2 && "unexpected number of operands"); + Offset = Inst.getOperand(1); + if (!Offset.isImm()) + break; // We'll deal with this situation later on when applying fixups. + if (!isIntN(23, Offset.getImm())) + return Error(IDLoc, "branch target out of range"); + if (OffsetToAlignment(Offset.getImm(), 1LL << 2)) + return Error(IDLoc, "branch to misaligned address"); + break; + case Maxis::BEQZ16_MM: + case Maxis::BEQZC16_MMR6: + case Maxis::BNEZ16_MM: + case Maxis::BNEZC16_MMR6: + assert(MCID.getNumOperands() == 2 && "unexpected number of operands"); + Offset = Inst.getOperand(1); + if (!Offset.isImm()) + break; // We'll deal with this situation later on when applying fixups. + if (!isInt<8>(Offset.getImm())) + return Error(IDLoc, "branch target out of range"); + if (OffsetToAlignment(Offset.getImm(), 2LL)) + return Error(IDLoc, "branch to misaligned address"); + break; + } + } + + // SSNOP is deprecated on MAXIS32r6/MAXIS64r6 + // We still accept it but it is a normal nop. + if (hasMaxis32r6() && Inst.getOpcode() == Maxis::SSNOP) { + std::string ISA = hasMaxis64r6() ? "MAXIS64r6" : "MAXIS32r6"; + Warning(IDLoc, "ssnop is deprecated for " + ISA + " and is equivalent to a " + "nop instruction"); + } + + if (hasCnMaxis()) { + const unsigned Opcode = Inst.getOpcode(); + MCOperand Opnd; + int Imm; + + switch (Opcode) { + default: + break; + + case Maxis::BBIT0: + case Maxis::BBIT032: + case Maxis::BBIT1: + case Maxis::BBIT132: + assert(MCID.getNumOperands() == 3 && "unexpected number of operands"); + // The offset is handled above + Opnd = Inst.getOperand(1); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (Imm < 0 || Imm > (Opcode == Maxis::BBIT0 || + Opcode == Maxis::BBIT1 ? 63 : 31)) + return Error(IDLoc, "immediate operand value out of range"); + if (Imm > 31) { + Inst.setOpcode(Opcode == Maxis::BBIT0 ? Maxis::BBIT032 + : Maxis::BBIT132); + Inst.getOperand(1).setImm(Imm - 32); + } + break; + + case Maxis::SEQi: + case Maxis::SNEi: + assert(MCID.getNumOperands() == 3 && "unexpected number of operands"); + Opnd = Inst.getOperand(2); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (!isInt<10>(Imm)) + return Error(IDLoc, "immediate operand value out of range"); + break; + } + } + + // Warn on division by zero. We're checking here as all instructions get + // processed here, not just the macros that need expansion. + // + // The MAXIS backend models most of the divison instructions and macros as + // three operand instructions. The pre-R6 divide instructions however have + // two operands and explicitly define HI/LO as part of the instruction, + // not in the operands. + unsigned FirstOp = 1; + unsigned SecondOp = 2; + switch (Inst.getOpcode()) { + default: + break; + case Maxis::SDivIMacro: + case Maxis::UDivIMacro: + case Maxis::DSDivIMacro: + case Maxis::DUDivIMacro: + if (Inst.getOperand(2).getImm() == 0) { + if (Inst.getOperand(1).getReg() == Maxis::ZERO || + Inst.getOperand(1).getReg() == Maxis::ZERO_64) + Warning(IDLoc, "dividing zero by zero"); + else + Warning(IDLoc, "division by zero"); + } + break; + case Maxis::DSDIV: + case Maxis::SDIV: + case Maxis::UDIV: + case Maxis::DUDIV: + case Maxis::UDIV_MM: + case Maxis::SDIV_MM: + FirstOp = 0; + SecondOp = 1; + LLVM_FALLTHROUGH; + case Maxis::SDivMacro: + case Maxis::DSDivMacro: + case Maxis::UDivMacro: + case Maxis::DUDivMacro: + case Maxis::DIV: + case Maxis::DIVU: + case Maxis::DDIV: + case Maxis::DDIVU: + case Maxis::DIVU_MMR6: + case Maxis::DIV_MMR6: + if (Inst.getOperand(SecondOp).getReg() == Maxis::ZERO || + Inst.getOperand(SecondOp).getReg() == Maxis::ZERO_64) { + if (Inst.getOperand(FirstOp).getReg() == Maxis::ZERO || + Inst.getOperand(FirstOp).getReg() == Maxis::ZERO_64) + Warning(IDLoc, "dividing zero by zero"); + else + Warning(IDLoc, "division by zero"); + } + break; + } + + // For PIC code convert unconditional jump to unconditional branch. + if ((Inst.getOpcode() == Maxis::J || Inst.getOpcode() == Maxis::J_MM) && + inPicMode()) { + MCInst BInst; + BInst.setOpcode(inMicroMaxisMode() ? Maxis::BEQ_MM : Maxis::BEQ); + BInst.addOperand(MCOperand::createReg(Maxis::ZERO)); + BInst.addOperand(MCOperand::createReg(Maxis::ZERO)); + BInst.addOperand(Inst.getOperand(0)); + Inst = BInst; + } + + // This expansion is not in a function called by tryExpandInstruction() + // because the pseudo-instruction doesn't have a distinct opcode. + if ((Inst.getOpcode() == Maxis::JAL || Inst.getOpcode() == Maxis::JAL_MM) && + inPicMode()) { + warnIfNoMacro(IDLoc); + + const MCExpr *JalExpr = Inst.getOperand(0).getExpr(); + + // We can do this expansion if there's only 1 symbol in the argument + // expression. + if (countMCSymbolRefExpr(JalExpr) > 1) + return Error(IDLoc, "jal doesn't support multiple symbols in PIC mode"); + + // FIXME: This is checking the expression can be handled by the later stages + // of the assembler. We ought to leave it to those later stages. + const MCSymbol *JalSym = getSingleMCSymbol(JalExpr); + + // FIXME: Add support for label+offset operands (currently causes an error). + // FIXME: Add support for forward-declared local symbols. + // FIXME: Add expansion for when the LargeGOT option is enabled. + if (JalSym->isInSection() || JalSym->isTemporary() || + (JalSym->isELF() && cast(JalSym)->getBinding() == ELF::STB_LOCAL)) { + if (isABI_O32()) { + // If it's a local symbol and the O32 ABI is being used, we expand to: + // lw $25, 0($gp) + // R_(MICRO)MAXIS_GOT16 label + // addiu $25, $25, 0 + // R_(MICRO)MAXIS_LO16 label + // jalr $25 + const MCExpr *Got16RelocExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_GOT, JalExpr, getContext()); + const MCExpr *Lo16RelocExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_LO, JalExpr, getContext()); + + TOut.emitRRX(Maxis::LW, Maxis::T9, Maxis::GP, + MCOperand::createExpr(Got16RelocExpr), IDLoc, STI); + TOut.emitRRX(Maxis::ADDiu, Maxis::T9, Maxis::T9, + MCOperand::createExpr(Lo16RelocExpr), IDLoc, STI); + } else if (isABI_N32() || isABI_N64()) { + // If it's a local symbol and the N32/N64 ABIs are being used, + // we expand to: + // lw/ld $25, 0($gp) + // R_(MICRO)MAXIS_GOT_DISP label + // jalr $25 + const MCExpr *GotDispRelocExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_DISP, JalExpr, getContext()); + + TOut.emitRRX(ABI.ArePtrs64bit() ? Maxis::LD : Maxis::LW, Maxis::T9, + Maxis::GP, MCOperand::createExpr(GotDispRelocExpr), IDLoc, + STI); + } + } else { + // If it's an external/weak symbol, we expand to: + // lw/ld $25, 0($gp) + // R_(MICRO)MAXIS_CALL16 label + // jalr $25 + const MCExpr *Call16RelocExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_CALL, JalExpr, getContext()); + + TOut.emitRRX(ABI.ArePtrs64bit() ? Maxis::LD : Maxis::LW, Maxis::T9, Maxis::GP, + MCOperand::createExpr(Call16RelocExpr), IDLoc, STI); + } + + MCInst JalrInst; + if (IsCpRestoreSet && inMicroMaxisMode()) + JalrInst.setOpcode(Maxis::JALRS_MM); + else + JalrInst.setOpcode(inMicroMaxisMode() ? Maxis::JALR_MM : Maxis::JALR); + JalrInst.addOperand(MCOperand::createReg(Maxis::RA)); + JalrInst.addOperand(MCOperand::createReg(Maxis::T9)); + + // FIXME: Add an R_(MICRO)MAXIS_JALR relocation after the JALR. + // This relocation is supposed to be an optimization hint for the linker + // and is not necessary for correctness. + + Inst = JalrInst; + ExpandedJalSym = true; + } + + bool IsPCRelativeLoad = (MCID.TSFlags & MaxisII::IsPCRelativeLoad) != 0; + if ((MCID.mayLoad() || MCID.mayStore()) && !IsPCRelativeLoad) { + // Check the offset of memory operand, if it is a symbol + // reference or immediate we may have to expand instructions. + for (unsigned i = 0; i < MCID.getNumOperands(); i++) { + const MCOperandInfo &OpInfo = MCID.OpInfo[i]; + if ((OpInfo.OperandType == MCOI::OPERAND_MEMORY) || + (OpInfo.OperandType == MCOI::OPERAND_UNKNOWN)) { + MCOperand &Op = Inst.getOperand(i); + if (Op.isImm()) { + int MemOffset = Op.getImm(); + if (MemOffset < -32768 || MemOffset > 32767) { + // Offset can't exceed 16bit value. + expandMemInst(Inst, IDLoc, Out, STI, MCID.mayLoad(), true); + return getParser().hasPendingError(); + } + } else if (Op.isExpr()) { + const MCExpr *Expr = Op.getExpr(); + if (Expr->getKind() == MCExpr::SymbolRef) { + const MCSymbolRefExpr *SR = + static_cast(Expr); + if (SR->getKind() == MCSymbolRefExpr::VK_None) { + // Expand symbol. + expandMemInst(Inst, IDLoc, Out, STI, MCID.mayLoad(), false); + return getParser().hasPendingError(); + } + } else if (!isEvaluated(Expr)) { + expandMemInst(Inst, IDLoc, Out, STI, MCID.mayLoad(), false); + return getParser().hasPendingError(); + } + } + } + } // for + } // if load/store + + if (inMicroMaxisMode()) { + if (MCID.mayLoad()) { + // Try to create 16-bit GP relative load instruction. + for (unsigned i = 0; i < MCID.getNumOperands(); i++) { + const MCOperandInfo &OpInfo = MCID.OpInfo[i]; + if ((OpInfo.OperandType == MCOI::OPERAND_MEMORY) || + (OpInfo.OperandType == MCOI::OPERAND_UNKNOWN)) { + MCOperand &Op = Inst.getOperand(i); + if (Op.isImm()) { + int MemOffset = Op.getImm(); + MCOperand &DstReg = Inst.getOperand(0); + MCOperand &BaseReg = Inst.getOperand(1); + if (isInt<9>(MemOffset) && (MemOffset % 4 == 0) && + getContext().getRegisterInfo()->getRegClass( + Maxis::GPRMM16RegClassID).contains(DstReg.getReg()) && + (BaseReg.getReg() == Maxis::GP || + BaseReg.getReg() == Maxis::GP_64)) { + + TOut.emitRRI(Maxis::LWGP_MM, DstReg.getReg(), Maxis::GP, MemOffset, + IDLoc, STI); + return false; + } + } + } + } // for + } // if load + + // TODO: Handle this with the AsmOperandClass.PredicateMethod. + + MCOperand Opnd; + int Imm; + + switch (Inst.getOpcode()) { + default: + break; + case Maxis::ADDIUSP_MM: + Opnd = Inst.getOperand(0); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (Imm < -1032 || Imm > 1028 || (Imm < 8 && Imm > -12) || + Imm % 4 != 0) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::SLL16_MM: + case Maxis::SRL16_MM: + Opnd = Inst.getOperand(2); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (Imm < 1 || Imm > 8) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::LI16_MM: + Opnd = Inst.getOperand(1); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (Imm < -1 || Imm > 126) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::ADDIUR2_MM: + Opnd = Inst.getOperand(2); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (!(Imm == 1 || Imm == -1 || + ((Imm % 4 == 0) && Imm < 28 && Imm > 0))) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::ANDI16_MM: + Opnd = Inst.getOperand(2); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (!(Imm == 128 || (Imm >= 1 && Imm <= 4) || Imm == 7 || Imm == 8 || + Imm == 15 || Imm == 16 || Imm == 31 || Imm == 32 || Imm == 63 || + Imm == 64 || Imm == 255 || Imm == 32768 || Imm == 65535)) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::LBU16_MM: + Opnd = Inst.getOperand(2); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (Imm < -1 || Imm > 14) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::SB16_MM: + case Maxis::SB16_MMR6: + Opnd = Inst.getOperand(2); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (Imm < 0 || Imm > 15) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::LHU16_MM: + case Maxis::SH16_MM: + case Maxis::SH16_MMR6: + Opnd = Inst.getOperand(2); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (Imm < 0 || Imm > 30 || (Imm % 2 != 0)) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::LW16_MM: + case Maxis::SW16_MM: + case Maxis::SW16_MMR6: + Opnd = Inst.getOperand(2); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + Imm = Opnd.getImm(); + if (Imm < 0 || Imm > 60 || (Imm % 4 != 0)) + return Error(IDLoc, "immediate operand value out of range"); + break; + case Maxis::ADDIUPC_MM: + MCOperand Opnd = Inst.getOperand(1); + if (!Opnd.isImm()) + return Error(IDLoc, "expected immediate operand kind"); + int Imm = Opnd.getImm(); + if ((Imm % 4 != 0) || !isInt<25>(Imm)) + return Error(IDLoc, "immediate operand value out of range"); + break; + } + } + + bool FillDelaySlot = + MCID.hasDelaySlot() && AssemblerOptions.back()->isReorder(); + if (FillDelaySlot) + TOut.emitDirectiveSetNoReorder(); + + MacroExpanderResultTy ExpandResult = + tryExpandInstruction(Inst, IDLoc, Out, STI); + switch (ExpandResult) { + case MER_NotAMacro: + Out.EmitInstruction(Inst, *STI); + break; + case MER_Success: + break; + case MER_Fail: + return true; + } + + // We know we emitted an instruction on the MER_NotAMacro or MER_Success path. + // If we're in microMAXIS mode then we must also set EF_MAXIS_MICROMAXIS. + if (inMicroMaxisMode()) { + TOut.setUsesMicroMaxis(); + TOut.updateABIInfo(*this); + } + + // If this instruction has a delay slot and .set reorder is active, + // emit a NOP after it. + if (FillDelaySlot) { + TOut.emitEmptyDelaySlot(hasShortDelaySlot(Inst.getOpcode()), IDLoc, STI); + TOut.emitDirectiveSetReorder(); + } + + if ((Inst.getOpcode() == Maxis::JalOneReg || + Inst.getOpcode() == Maxis::JalTwoReg || ExpandedJalSym) && + isPicAndNotNxxAbi()) { + if (IsCpRestoreSet) { + // We need a NOP between the JALR and the LW: + // If .set reorder has been used, we've already emitted a NOP. + // If .set noreorder has been used, we need to emit a NOP at this point. + if (!AssemblerOptions.back()->isReorder()) + TOut.emitEmptyDelaySlot(hasShortDelaySlot(Inst.getOpcode()), IDLoc, + STI); + + // Load the $gp from the stack. + TOut.emitGPRestore(CpRestoreOffset, IDLoc, STI); + } else + Warning(IDLoc, "no .cprestore used in PIC mode"); + } + + return false; +} + +MaxisAsmParser::MacroExpanderResultTy +MaxisAsmParser::tryExpandInstruction(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + switch (Inst.getOpcode()) { + default: + return MER_NotAMacro; + case Maxis::LoadImm32: + return expandLoadImm(Inst, true, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::LoadImm64: + return expandLoadImm(Inst, false, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::LoadAddrImm32: + case Maxis::LoadAddrImm64: + assert(Inst.getOperand(0).isReg() && "expected register operand kind"); + assert((Inst.getOperand(1).isImm() || Inst.getOperand(1).isExpr()) && + "expected immediate operand kind"); + + return expandLoadAddress(Inst.getOperand(0).getReg(), Maxis::NoRegister, + Inst.getOperand(1), + Inst.getOpcode() == Maxis::LoadAddrImm32, IDLoc, + Out, STI) + ? MER_Fail + : MER_Success; + case Maxis::LoadAddrReg32: + case Maxis::LoadAddrReg64: + assert(Inst.getOperand(0).isReg() && "expected register operand kind"); + assert(Inst.getOperand(1).isReg() && "expected register operand kind"); + assert((Inst.getOperand(2).isImm() || Inst.getOperand(2).isExpr()) && + "expected immediate operand kind"); + + return expandLoadAddress(Inst.getOperand(0).getReg(), + Inst.getOperand(1).getReg(), Inst.getOperand(2), + Inst.getOpcode() == Maxis::LoadAddrReg32, IDLoc, + Out, STI) + ? MER_Fail + : MER_Success; + case Maxis::B_MM_Pseudo: + case Maxis::B_MMR6_Pseudo: + return expandUncondBranchMMPseudo(Inst, IDLoc, Out, STI) ? MER_Fail + : MER_Success; + case Maxis::SWM_MM: + case Maxis::LWM_MM: + return expandLoadStoreMultiple(Inst, IDLoc, Out, STI) ? MER_Fail + : MER_Success; + case Maxis::JalOneReg: + case Maxis::JalTwoReg: + return expandJalWithRegs(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::BneImm: + case Maxis::BeqImm: + case Maxis::BEQLImmMacro: + case Maxis::BNELImmMacro: + return expandBranchImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::BLT: + case Maxis::BLE: + case Maxis::BGE: + case Maxis::BGT: + case Maxis::BLTU: + case Maxis::BLEU: + case Maxis::BGEU: + case Maxis::BGTU: + case Maxis::BLTL: + case Maxis::BLEL: + case Maxis::BGEL: + case Maxis::BGTL: + case Maxis::BLTUL: + case Maxis::BLEUL: + case Maxis::BGEUL: + case Maxis::BGTUL: + case Maxis::BLTImmMacro: + case Maxis::BLEImmMacro: + case Maxis::BGEImmMacro: + case Maxis::BGTImmMacro: + case Maxis::BLTUImmMacro: + case Maxis::BLEUImmMacro: + case Maxis::BGEUImmMacro: + case Maxis::BGTUImmMacro: + case Maxis::BLTLImmMacro: + case Maxis::BLELImmMacro: + case Maxis::BGELImmMacro: + case Maxis::BGTLImmMacro: + case Maxis::BLTULImmMacro: + case Maxis::BLEULImmMacro: + case Maxis::BGEULImmMacro: + case Maxis::BGTULImmMacro: + return expandCondBranches(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::SDivMacro: + case Maxis::SDivIMacro: + return expandDiv(Inst, IDLoc, Out, STI, false, true) ? MER_Fail + : MER_Success; + case Maxis::DSDivMacro: + case Maxis::DSDivIMacro: + return expandDiv(Inst, IDLoc, Out, STI, true, true) ? MER_Fail + : MER_Success; + case Maxis::UDivMacro: + case Maxis::UDivIMacro: + return expandDiv(Inst, IDLoc, Out, STI, false, false) ? MER_Fail + : MER_Success; + case Maxis::DUDivMacro: + case Maxis::DUDivIMacro: + return expandDiv(Inst, IDLoc, Out, STI, true, false) ? MER_Fail + : MER_Success; + case Maxis::PseudoTRUNC_W_S: + return expandTrunc(Inst, false, false, IDLoc, Out, STI) ? MER_Fail + : MER_Success; + case Maxis::PseudoTRUNC_W_D32: + return expandTrunc(Inst, true, false, IDLoc, Out, STI) ? MER_Fail + : MER_Success; + case Maxis::PseudoTRUNC_W_D: + return expandTrunc(Inst, true, true, IDLoc, Out, STI) ? MER_Fail + : MER_Success; + + case Maxis::LoadImmSingleGPR: + return expandLoadImmReal(Inst, true, true, false, IDLoc, Out, STI) + ? MER_Fail + : MER_Success; + case Maxis::LoadImmSingleFGR: + return expandLoadImmReal(Inst, true, false, false, IDLoc, Out, STI) + ? MER_Fail + : MER_Success; + case Maxis::LoadImmDoubleGPR: + return expandLoadImmReal(Inst, false, true, false, IDLoc, Out, STI) + ? MER_Fail + : MER_Success; + case Maxis::LoadImmDoubleFGR: + return expandLoadImmReal(Inst, false, false, true, IDLoc, Out, STI) + ? MER_Fail + : MER_Success; + case Maxis::LoadImmDoubleFGR_32: + return expandLoadImmReal(Inst, false, false, false, IDLoc, Out, STI) + ? MER_Fail + : MER_Success; + case Maxis::Ulh: + return expandUlh(Inst, true, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::Ulhu: + return expandUlh(Inst, false, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::Ush: + return expandUsh(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::Ulw: + case Maxis::Usw: + return expandUxw(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::NORImm: + case Maxis::NORImm64: + return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::SLTImm64: + if (isInt<16>(Inst.getOperand(2).getImm())) { + Inst.setOpcode(Maxis::SLTi64); + return MER_NotAMacro; + } + return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::SLTUImm64: + if (isInt<16>(Inst.getOperand(2).getImm())) { + Inst.setOpcode(Maxis::SLTiu64); + return MER_NotAMacro; + } + return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::ADDi: case Maxis::ADDi_MM: + case Maxis::ADDiu: case Maxis::ADDiu_MM: + case Maxis::SLTi: case Maxis::SLTi_MM: + case Maxis::SLTiu: case Maxis::SLTiu_MM: + if ((Inst.getNumOperands() == 3) && Inst.getOperand(0).isReg() && + Inst.getOperand(1).isReg() && Inst.getOperand(2).isImm()) { + int64_t ImmValue = Inst.getOperand(2).getImm(); + if (isInt<16>(ImmValue)) + return MER_NotAMacro; + return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail + : MER_Success; + } + return MER_NotAMacro; + case Maxis::ANDi: case Maxis::ANDi_MM: case Maxis::ANDi64: + case Maxis::ORi: case Maxis::ORi_MM: case Maxis::ORi64: + case Maxis::XORi: case Maxis::XORi_MM: case Maxis::XORi64: + if ((Inst.getNumOperands() == 3) && Inst.getOperand(0).isReg() && + Inst.getOperand(1).isReg() && Inst.getOperand(2).isImm()) { + int64_t ImmValue = Inst.getOperand(2).getImm(); + if (isUInt<16>(ImmValue)) + return MER_NotAMacro; + return expandAliasImmediate(Inst, IDLoc, Out, STI) ? MER_Fail + : MER_Success; + } + return MER_NotAMacro; + case Maxis::ROL: + case Maxis::ROR: + return expandRotation(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::ROLImm: + case Maxis::RORImm: + return expandRotationImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::DROL: + case Maxis::DROR: + return expandDRotation(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::DROLImm: + case Maxis::DRORImm: + return expandDRotationImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::ABSMacro: + return expandAbs(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::MULImmMacro: + case Maxis::DMULImmMacro: + return expandMulImm(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::MULOMacro: + case Maxis::DMULOMacro: + return expandMulO(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::MULOUMacro: + case Maxis::DMULOUMacro: + return expandMulOU(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::DMULMacro: + return expandDMULMacro(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::LDMacro: + case Maxis::SDMacro: + return expandLoadStoreDMacro(Inst, IDLoc, Out, STI, + Inst.getOpcode() == Maxis::LDMacro) + ? MER_Fail + : MER_Success; + case Maxis::SEQMacro: + return expandSeq(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::SEQIMacro: + return expandSeqI(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + case Maxis::MFTC0: case Maxis::MTTC0: + case Maxis::MFTGPR: case Maxis::MTTGPR: + case Maxis::MFTLO: case Maxis::MTTLO: + case Maxis::MFTHI: case Maxis::MTTHI: + case Maxis::MFTACX: case Maxis::MTTACX: + case Maxis::MFTDSP: case Maxis::MTTDSP: + case Maxis::MFTC1: case Maxis::MTTC1: + case Maxis::MFTHC1: case Maxis::MTTHC1: + case Maxis::CFTC1: case Maxis::CTTC1: + return expandMXTRAlias(Inst, IDLoc, Out, STI) ? MER_Fail : MER_Success; + } +} + +bool MaxisAsmParser::expandJalWithRegs(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + + // Create a JALR instruction which is going to replace the pseudo-JAL. + MCInst JalrInst; + JalrInst.setLoc(IDLoc); + const MCOperand FirstRegOp = Inst.getOperand(0); + const unsigned Opcode = Inst.getOpcode(); + + if (Opcode == Maxis::JalOneReg) { + // jal $rs => jalr $rs + if (IsCpRestoreSet && inMicroMaxisMode()) { + JalrInst.setOpcode(Maxis::JALRS16_MM); + JalrInst.addOperand(FirstRegOp); + } else if (inMicroMaxisMode()) { + JalrInst.setOpcode(hasMaxis32r6() ? Maxis::JALRC16_MMR6 : Maxis::JALR16_MM); + JalrInst.addOperand(FirstRegOp); + } else { + JalrInst.setOpcode(Maxis::JALR); + JalrInst.addOperand(MCOperand::createReg(Maxis::RA)); + JalrInst.addOperand(FirstRegOp); + } + } else if (Opcode == Maxis::JalTwoReg) { + // jal $rd, $rs => jalr $rd, $rs + if (IsCpRestoreSet && inMicroMaxisMode()) + JalrInst.setOpcode(Maxis::JALRS_MM); + else + JalrInst.setOpcode(inMicroMaxisMode() ? Maxis::JALR_MM : Maxis::JALR); + JalrInst.addOperand(FirstRegOp); + const MCOperand SecondRegOp = Inst.getOperand(1); + JalrInst.addOperand(SecondRegOp); + } + Out.EmitInstruction(JalrInst, *STI); + + // If .set reorder is active and branch instruction has a delay slot, + // emit a NOP after it. + const MCInstrDesc &MCID = getInstDesc(JalrInst.getOpcode()); + if (MCID.hasDelaySlot() && AssemblerOptions.back()->isReorder()) + TOut.emitEmptyDelaySlot(hasShortDelaySlot(JalrInst.getOpcode()), IDLoc, + STI); + + return false; +} + +/// Can the value be represented by a unsigned N-bit value and a shift left? +template static bool isShiftedUIntAtAnyPosition(uint64_t x) { + unsigned BitNum = findFirstSet(x); + + return (x == x >> BitNum << BitNum) && isUInt(x >> BitNum); +} + +/// Load (or add) an immediate into a register. +/// +/// @param ImmValue The immediate to load. +/// @param DstReg The register that will hold the immediate. +/// @param SrcReg A register to add to the immediate or Maxis::NoRegister +/// for a simple initialization. +/// @param Is32BitImm Is ImmValue 32-bit or 64-bit? +/// @param IsAddress True if the immediate represents an address. False if it +/// is an integer. +/// @param IDLoc Location of the immediate in the source file. +bool MaxisAsmParser::loadImmediate(int64_t ImmValue, unsigned DstReg, + unsigned SrcReg, bool Is32BitImm, + bool IsAddress, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + + if (!Is32BitImm && !isGP64bit()) { + Error(IDLoc, "instruction requires a 64-bit architecture"); + return true; + } + + if (Is32BitImm) { + if (isInt<32>(ImmValue) || isUInt<32>(ImmValue)) { + // Sign extend up to 64-bit so that the predicates match the hardware + // behaviour. In particular, isInt<16>(0xffff8000) and similar should be + // true. + ImmValue = SignExtend64<32>(ImmValue); + } else { + Error(IDLoc, "instruction requires a 32-bit immediate"); + return true; + } + } + + unsigned ZeroReg = IsAddress ? ABI.GetNullPtr() : ABI.GetZeroReg(); + unsigned AdduOp = !Is32BitImm ? Maxis::DADDu : Maxis::ADDu; + + bool UseSrcReg = false; + if (SrcReg != Maxis::NoRegister) + UseSrcReg = true; + + unsigned TmpReg = DstReg; + if (UseSrcReg && + getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, SrcReg)) { + // At this point we need AT to perform the expansions and we exit if it is + // not available. + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + TmpReg = ATReg; + } + + if (isInt<16>(ImmValue)) { + if (!UseSrcReg) + SrcReg = ZeroReg; + + // This doesn't quite follow the usual ABI expectations for N32 but matches + // traditional assembler behaviour. N32 would normally use addiu for both + // integers and addresses. + if (IsAddress && !Is32BitImm) { + TOut.emitRRI(Maxis::DADDiu, DstReg, SrcReg, ImmValue, IDLoc, STI); + return false; + } + + TOut.emitRRI(Maxis::ADDiu, DstReg, SrcReg, ImmValue, IDLoc, STI); + return false; + } + + if (isUInt<16>(ImmValue)) { + unsigned TmpReg = DstReg; + if (SrcReg == DstReg) { + TmpReg = getATReg(IDLoc); + if (!TmpReg) + return true; + } + + TOut.emitRRI(Maxis::ORi, TmpReg, ZeroReg, ImmValue, IDLoc, STI); + if (UseSrcReg) + TOut.emitRRR(ABI.GetPtrAdduOp(), DstReg, TmpReg, SrcReg, IDLoc, STI); + return false; + } + + if (isInt<32>(ImmValue) || isUInt<32>(ImmValue)) { + warnIfNoMacro(IDLoc); + + uint16_t Bits31To16 = (ImmValue >> 16) & 0xffff; + uint16_t Bits15To0 = ImmValue & 0xffff; + if (!Is32BitImm && !isInt<32>(ImmValue)) { + // Traditional behaviour seems to special case this particular value. It's + // not clear why other masks are handled differently. + if (ImmValue == 0xffffffff) { + TOut.emitRI(Maxis::LUi, TmpReg, 0xffff, IDLoc, STI); + TOut.emitRRI(Maxis::DSRL32, TmpReg, TmpReg, 0, IDLoc, STI); + if (UseSrcReg) + TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); + return false; + } + + // Expand to an ORi instead of a LUi to avoid sign-extending into the + // upper 32 bits. + TOut.emitRRI(Maxis::ORi, TmpReg, ZeroReg, Bits31To16, IDLoc, STI); + TOut.emitRRI(Maxis::DSLL, TmpReg, TmpReg, 16, IDLoc, STI); + if (Bits15To0) + TOut.emitRRI(Maxis::ORi, TmpReg, TmpReg, Bits15To0, IDLoc, STI); + if (UseSrcReg) + TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); + return false; + } + + TOut.emitRI(Maxis::LUi, TmpReg, Bits31To16, IDLoc, STI); + if (Bits15To0) + TOut.emitRRI(Maxis::ORi, TmpReg, TmpReg, Bits15To0, IDLoc, STI); + if (UseSrcReg) + TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); + return false; + } + + if (isShiftedUIntAtAnyPosition<16>(ImmValue)) { + if (Is32BitImm) { + Error(IDLoc, "instruction requires a 32-bit immediate"); + return true; + } + + // Traditionally, these immediates are shifted as little as possible and as + // such we align the most significant bit to bit 15 of our temporary. + unsigned FirstSet = findFirstSet((uint64_t)ImmValue); + unsigned LastSet = findLastSet((uint64_t)ImmValue); + unsigned ShiftAmount = FirstSet - (15 - (LastSet - FirstSet)); + uint16_t Bits = (ImmValue >> ShiftAmount) & 0xffff; + TOut.emitRRI(Maxis::ORi, TmpReg, ZeroReg, Bits, IDLoc, STI); + TOut.emitRRI(Maxis::DSLL, TmpReg, TmpReg, ShiftAmount, IDLoc, STI); + + if (UseSrcReg) + TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); + + return false; + } + + warnIfNoMacro(IDLoc); + + // The remaining case is packed with a sequence of dsll and ori with zeros + // being omitted and any neighbouring dsll's being coalesced. + // The highest 32-bit's are equivalent to a 32-bit immediate load. + + // Load bits 32-63 of ImmValue into bits 0-31 of the temporary register. + if (loadImmediate(ImmValue >> 32, TmpReg, Maxis::NoRegister, true, false, + IDLoc, Out, STI)) + return false; + + // Shift and accumulate into the register. If a 16-bit chunk is zero, then + // skip it and defer the shift to the next chunk. + unsigned ShiftCarriedForwards = 16; + for (int BitNum = 16; BitNum >= 0; BitNum -= 16) { + uint16_t ImmChunk = (ImmValue >> BitNum) & 0xffff; + + if (ImmChunk != 0) { + TOut.emitDSLL(TmpReg, TmpReg, ShiftCarriedForwards, IDLoc, STI); + TOut.emitRRI(Maxis::ORi, TmpReg, TmpReg, ImmChunk, IDLoc, STI); + ShiftCarriedForwards = 0; + } + + ShiftCarriedForwards += 16; + } + ShiftCarriedForwards -= 16; + + // Finish any remaining shifts left by trailing zeros. + if (ShiftCarriedForwards) + TOut.emitDSLL(TmpReg, TmpReg, ShiftCarriedForwards, IDLoc, STI); + + if (UseSrcReg) + TOut.emitRRR(AdduOp, DstReg, TmpReg, SrcReg, IDLoc, STI); + + return false; +} + +bool MaxisAsmParser::expandLoadImm(MCInst &Inst, bool Is32BitImm, SMLoc IDLoc, + MCStreamer &Out, const MCSubtargetInfo *STI) { + const MCOperand &ImmOp = Inst.getOperand(1); + assert(ImmOp.isImm() && "expected immediate operand kind"); + const MCOperand &DstRegOp = Inst.getOperand(0); + assert(DstRegOp.isReg() && "expected register operand kind"); + + if (loadImmediate(ImmOp.getImm(), DstRegOp.getReg(), Maxis::NoRegister, + Is32BitImm, false, IDLoc, Out, STI)) + return true; + + return false; +} + +bool MaxisAsmParser::expandLoadAddress(unsigned DstReg, unsigned BaseReg, + const MCOperand &Offset, + bool Is32BitAddress, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + // la can't produce a usable address when addresses are 64-bit. + if (Is32BitAddress && ABI.ArePtrs64bit()) { + // FIXME: Demote this to a warning and continue as if we had 'dla' instead. + // We currently can't do this because we depend on the equality + // operator and N64 can end up with a GPR32/GPR64 mismatch. + Error(IDLoc, "la used to load 64-bit address"); + // Continue as if we had 'dla' instead. + Is32BitAddress = false; + return true; + } + + // dla requires 64-bit addresses. + if (!Is32BitAddress && !hasMaxis3()) { + Error(IDLoc, "instruction requires a 64-bit architecture"); + return true; + } + + if (!Offset.isImm()) + return loadAndAddSymbolAddress(Offset.getExpr(), DstReg, BaseReg, + Is32BitAddress, IDLoc, Out, STI); + + if (!ABI.ArePtrs64bit()) { + // Continue as if we had 'la' whether we had 'la' or 'dla'. + Is32BitAddress = true; + } + + return loadImmediate(Offset.getImm(), DstReg, BaseReg, Is32BitAddress, true, + IDLoc, Out, STI); +} + +bool MaxisAsmParser::loadAndAddSymbolAddress(const MCExpr *SymExpr, + unsigned DstReg, unsigned SrcReg, + bool Is32BitSym, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + // FIXME: These expansions do not respect -mxgot. + MaxisTargetStreamer &TOut = getTargetStreamer(); + bool UseSrcReg = SrcReg != Maxis::NoRegister; + warnIfNoMacro(IDLoc); + + if (inPicMode() && ABI.IsO32()) { + MCValue Res; + if (!SymExpr->evaluateAsRelocatable(Res, nullptr, nullptr)) { + Error(IDLoc, "expected relocatable expression"); + return true; + } + if (Res.getSymB() != nullptr) { + Error(IDLoc, "expected relocatable expression with only one symbol"); + return true; + } + + // The case where the result register is $25 is somewhat special. If the + // symbol in the final relocation is external and not modified with a + // constant then we must use R_MAXIS_CALL16 instead of R_MAXIS_GOT16. + if ((DstReg == Maxis::T9 || DstReg == Maxis::T9_64) && !UseSrcReg && + Res.getConstant() == 0 && + !(Res.getSymA()->getSymbol().isInSection() || + Res.getSymA()->getSymbol().isTemporary() || + (Res.getSymA()->getSymbol().isELF() && + cast(Res.getSymA()->getSymbol()).getBinding() == + ELF::STB_LOCAL))) { + const MCExpr *CallExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_CALL, SymExpr, getContext()); + TOut.emitRRX(Maxis::LW, DstReg, ABI.GetGlobalPtr(), + MCOperand::createExpr(CallExpr), IDLoc, STI); + return false; + } + + // The remaining cases are: + // External GOT: lw $tmp, %got(symbol+offset)($gp) + // >addiu $tmp, $tmp, %lo(offset) + // >addiu $rd, $tmp, $rs + // Local GOT: lw $tmp, %got(symbol+offset)($gp) + // addiu $tmp, $tmp, %lo(symbol+offset)($gp) + // >addiu $rd, $tmp, $rs + // The addiu's marked with a '>' may be omitted if they are redundant. If + // this happens then the last instruction must use $rd as the result + // register. + const MaxisMCExpr *GotExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_GOT, SymExpr, getContext()); + const MCExpr *LoExpr = nullptr; + if (Res.getSymA()->getSymbol().isInSection() || + Res.getSymA()->getSymbol().isTemporary()) + LoExpr = MaxisMCExpr::create(MaxisMCExpr::MEK_LO, SymExpr, getContext()); + else if (Res.getConstant() != 0) { + // External symbols fully resolve the symbol with just the %got(symbol) + // but we must still account for any offset to the symbol for expressions + // like symbol+8. + LoExpr = MCConstantExpr::create(Res.getConstant(), getContext()); + } + + unsigned TmpReg = DstReg; + if (UseSrcReg && + getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, + SrcReg)) { + // If $rs is the same as $rd, we need to use AT. + // If it is not available we exit. + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + TmpReg = ATReg; + } + + TOut.emitRRX(Maxis::LW, TmpReg, ABI.GetGlobalPtr(), + MCOperand::createExpr(GotExpr), IDLoc, STI); + + if (LoExpr) + TOut.emitRRX(Maxis::ADDiu, TmpReg, TmpReg, MCOperand::createExpr(LoExpr), + IDLoc, STI); + + if (UseSrcReg) + TOut.emitRRR(Maxis::ADDu, DstReg, TmpReg, SrcReg, IDLoc, STI); + + return false; + } + + if (inPicMode() && ABI.ArePtrs64bit()) { + MCValue Res; + if (!SymExpr->evaluateAsRelocatable(Res, nullptr, nullptr)) { + Error(IDLoc, "expected relocatable expression"); + return true; + } + if (Res.getSymB() != nullptr) { + Error(IDLoc, "expected relocatable expression with only one symbol"); + return true; + } + + // The case where the result register is $25 is somewhat special. If the + // symbol in the final relocation is external and not modified with a + // constant then we must use R_MAXIS_CALL16 instead of R_MAXIS_GOT_DISP. + if ((DstReg == Maxis::T9 || DstReg == Maxis::T9_64) && !UseSrcReg && + Res.getConstant() == 0 && + !(Res.getSymA()->getSymbol().isInSection() || + Res.getSymA()->getSymbol().isTemporary() || + (Res.getSymA()->getSymbol().isELF() && + cast(Res.getSymA()->getSymbol()).getBinding() == + ELF::STB_LOCAL))) { + const MCExpr *CallExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_CALL, SymExpr, getContext()); + TOut.emitRRX(Maxis::LD, DstReg, ABI.GetGlobalPtr(), + MCOperand::createExpr(CallExpr), IDLoc, STI); + return false; + } + + // The remaining cases are: + // Small offset: ld $tmp, %got_disp(symbol)($gp) + // >daddiu $tmp, $tmp, offset + // >daddu $rd, $tmp, $rs + // The daddiu's marked with a '>' may be omitted if they are redundant. If + // this happens then the last instruction must use $rd as the result + // register. + const MaxisMCExpr *GotExpr = MaxisMCExpr::create(MaxisMCExpr::MEK_GOT_DISP, + Res.getSymA(), + getContext()); + const MCExpr *LoExpr = nullptr; + if (Res.getConstant() != 0) { + // Symbols fully resolve with just the %got_disp(symbol) but we + // must still account for any offset to the symbol for + // expressions like symbol+8. + LoExpr = MCConstantExpr::create(Res.getConstant(), getContext()); + + // FIXME: Offsets greater than 16 bits are not yet implemented. + // FIXME: The correct range is a 32-bit sign-extended number. + if (Res.getConstant() < -0x8000 || Res.getConstant() > 0x7fff) { + Error(IDLoc, "macro instruction uses large offset, which is not " + "currently supported"); + return true; + } + } + + unsigned TmpReg = DstReg; + if (UseSrcReg && + getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, + SrcReg)) { + // If $rs is the same as $rd, we need to use AT. + // If it is not available we exit. + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + TmpReg = ATReg; + } + + TOut.emitRRX(Maxis::LD, TmpReg, ABI.GetGlobalPtr(), + MCOperand::createExpr(GotExpr), IDLoc, STI); + + if (LoExpr) + TOut.emitRRX(Maxis::DADDiu, TmpReg, TmpReg, MCOperand::createExpr(LoExpr), + IDLoc, STI); + + if (UseSrcReg) + TOut.emitRRR(Maxis::DADDu, DstReg, TmpReg, SrcReg, IDLoc, STI); + + return false; + } + + const MaxisMCExpr *HiExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_HI, SymExpr, getContext()); + const MaxisMCExpr *LoExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_LO, SymExpr, getContext()); + + // This is the 64-bit symbol address expansion. + if (ABI.ArePtrs64bit() && isGP64bit()) { + // We need AT for the 64-bit expansion in the cases where the optional + // source register is the destination register and for the superscalar + // scheduled form. + // + // If it is not available we exit if the destination is the same as the + // source register. + + const MaxisMCExpr *HighestExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_HIGHEST, SymExpr, getContext()); + const MaxisMCExpr *HigherExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_HIGHER, SymExpr, getContext()); + + bool RdRegIsRsReg = + getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, SrcReg); + + if (canUseATReg() && UseSrcReg && RdRegIsRsReg) { + unsigned ATReg = getATReg(IDLoc); + + // If $rs is the same as $rd: + // (d)la $rd, sym($rd) => lui $at, %highest(sym) + // daddiu $at, $at, %higher(sym) + // dsll $at, $at, 16 + // daddiu $at, $at, %hi(sym) + // dsll $at, $at, 16 + // daddiu $at, $at, %lo(sym) + // daddu $rd, $at, $rd + TOut.emitRX(Maxis::LUi, ATReg, MCOperand::createExpr(HighestExpr), IDLoc, + STI); + TOut.emitRRX(Maxis::DADDiu, ATReg, ATReg, + MCOperand::createExpr(HigherExpr), IDLoc, STI); + TOut.emitRRI(Maxis::DSLL, ATReg, ATReg, 16, IDLoc, STI); + TOut.emitRRX(Maxis::DADDiu, ATReg, ATReg, MCOperand::createExpr(HiExpr), + IDLoc, STI); + TOut.emitRRI(Maxis::DSLL, ATReg, ATReg, 16, IDLoc, STI); + TOut.emitRRX(Maxis::DADDiu, ATReg, ATReg, MCOperand::createExpr(LoExpr), + IDLoc, STI); + TOut.emitRRR(Maxis::DADDu, DstReg, ATReg, SrcReg, IDLoc, STI); + + return false; + } else if (canUseATReg() && !RdRegIsRsReg) { + unsigned ATReg = getATReg(IDLoc); + + // If the $rs is different from $rd or if $rs isn't specified and we + // have $at available: + // (d)la $rd, sym/sym($rs) => lui $rd, %highest(sym) + // lui $at, %hi(sym) + // daddiu $rd, $rd, %higher(sym) + // daddiu $at, $at, %lo(sym) + // dsll32 $rd, $rd, 0 + // daddu $rd, $rd, $at + // (daddu $rd, $rd, $rs) + // + // Which is preferred for superscalar issue. + TOut.emitRX(Maxis::LUi, DstReg, MCOperand::createExpr(HighestExpr), IDLoc, + STI); + TOut.emitRX(Maxis::LUi, ATReg, MCOperand::createExpr(HiExpr), IDLoc, STI); + TOut.emitRRX(Maxis::DADDiu, DstReg, DstReg, + MCOperand::createExpr(HigherExpr), IDLoc, STI); + TOut.emitRRX(Maxis::DADDiu, ATReg, ATReg, MCOperand::createExpr(LoExpr), + IDLoc, STI); + TOut.emitRRI(Maxis::DSLL32, DstReg, DstReg, 0, IDLoc, STI); + TOut.emitRRR(Maxis::DADDu, DstReg, DstReg, ATReg, IDLoc, STI); + if (UseSrcReg) + TOut.emitRRR(Maxis::DADDu, DstReg, DstReg, SrcReg, IDLoc, STI); + + return false; + } else if (!canUseATReg() && !RdRegIsRsReg) { + // Otherwise, synthesize the address in the destination register + // serially: + // (d)la $rd, sym/sym($rs) => lui $rd, %highest(sym) + // daddiu $rd, $rd, %higher(sym) + // dsll $rd, $rd, 16 + // daddiu $rd, $rd, %hi(sym) + // dsll $rd, $rd, 16 + // daddiu $rd, $rd, %lo(sym) + TOut.emitRX(Maxis::LUi, DstReg, MCOperand::createExpr(HighestExpr), IDLoc, + STI); + TOut.emitRRX(Maxis::DADDiu, DstReg, DstReg, + MCOperand::createExpr(HigherExpr), IDLoc, STI); + TOut.emitRRI(Maxis::DSLL, DstReg, DstReg, 16, IDLoc, STI); + TOut.emitRRX(Maxis::DADDiu, DstReg, DstReg, + MCOperand::createExpr(HiExpr), IDLoc, STI); + TOut.emitRRI(Maxis::DSLL, DstReg, DstReg, 16, IDLoc, STI); + TOut.emitRRX(Maxis::DADDiu, DstReg, DstReg, + MCOperand::createExpr(LoExpr), IDLoc, STI); + if (UseSrcReg) + TOut.emitRRR(Maxis::DADDu, DstReg, DstReg, SrcReg, IDLoc, STI); + + return false; + } else { + // We have a case where SrcReg == DstReg and we don't have $at + // available. We can't expand this case, so error out appropriately. + assert(SrcReg == DstReg && !canUseATReg() && + "Could have expanded dla but didn't?"); + reportParseError(IDLoc, + "pseudo-instruction requires $at, which is not available"); + return true; + } + } + + // And now, the 32-bit symbol address expansion: + // If $rs is the same as $rd: + // (d)la $rd, sym($rd) => lui $at, %hi(sym) + // ori $at, $at, %lo(sym) + // addu $rd, $at, $rd + // Otherwise, if the $rs is different from $rd or if $rs isn't specified: + // (d)la $rd, sym/sym($rs) => lui $rd, %hi(sym) + // ori $rd, $rd, %lo(sym) + // (addu $rd, $rd, $rs) + unsigned TmpReg = DstReg; + if (UseSrcReg && + getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, SrcReg)) { + // If $rs is the same as $rd, we need to use AT. + // If it is not available we exit. + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + TmpReg = ATReg; + } + + TOut.emitRX(Maxis::LUi, TmpReg, MCOperand::createExpr(HiExpr), IDLoc, STI); + TOut.emitRRX(Maxis::ADDiu, TmpReg, TmpReg, MCOperand::createExpr(LoExpr), + IDLoc, STI); + + if (UseSrcReg) + TOut.emitRRR(Maxis::ADDu, DstReg, TmpReg, SrcReg, IDLoc, STI); + else + assert( + getContext().getRegisterInfo()->isSuperOrSubRegisterEq(DstReg, TmpReg)); + + return false; +} + +// Each double-precision register DO-D15 overlaps with two of the single +// precision registers F0-F31. As an example, all of the following hold true: +// D0 + 1 == F1, F1 + 1 == D1, F1 + 1 == F2, depending on the context. +static unsigned nextReg(unsigned Reg) { + if (MaxisMCRegisterClasses[Maxis::FGR32RegClassID].contains(Reg)) + return Reg == (unsigned)Maxis::F31 ? (unsigned)Maxis::F0 : Reg + 1; + switch (Reg) { + default: llvm_unreachable("Unknown register in assembly macro expansion!"); + case Maxis::ZERO: return Maxis::AT; + case Maxis::AT: return Maxis::V0; + case Maxis::V0: return Maxis::V1; + case Maxis::V1: return Maxis::A0; + case Maxis::A0: return Maxis::A1; + case Maxis::A1: return Maxis::A2; + case Maxis::A2: return Maxis::A3; + case Maxis::A3: return Maxis::T0; + case Maxis::T0: return Maxis::T1; + case Maxis::T1: return Maxis::T2; + case Maxis::T2: return Maxis::T3; + case Maxis::T3: return Maxis::T4; + case Maxis::T4: return Maxis::T5; + case Maxis::T5: return Maxis::T6; + case Maxis::T6: return Maxis::T7; + case Maxis::T7: return Maxis::S0; + case Maxis::S0: return Maxis::S1; + case Maxis::S1: return Maxis::S2; + case Maxis::S2: return Maxis::S3; + case Maxis::S3: return Maxis::S4; + case Maxis::S4: return Maxis::S5; + case Maxis::S5: return Maxis::S6; + case Maxis::S6: return Maxis::S7; + case Maxis::S7: return Maxis::T8; + case Maxis::T8: return Maxis::T9; + case Maxis::T9: return Maxis::K0; + case Maxis::K0: return Maxis::K1; + case Maxis::K1: return Maxis::GP; + case Maxis::GP: return Maxis::SP; + case Maxis::SP: return Maxis::FP; + case Maxis::FP: return Maxis::RA; + case Maxis::RA: return Maxis::ZERO; + case Maxis::D0: return Maxis::F1; + case Maxis::D1: return Maxis::F3; + case Maxis::D2: return Maxis::F5; + case Maxis::D3: return Maxis::F7; + case Maxis::D4: return Maxis::F9; + case Maxis::D5: return Maxis::F11; + case Maxis::D6: return Maxis::F13; + case Maxis::D7: return Maxis::F15; + case Maxis::D8: return Maxis::F17; + case Maxis::D9: return Maxis::F19; + case Maxis::D10: return Maxis::F21; + case Maxis::D11: return Maxis::F23; + case Maxis::D12: return Maxis::F25; + case Maxis::D13: return Maxis::F27; + case Maxis::D14: return Maxis::F29; + case Maxis::D15: return Maxis::F31; + } +} + +// FIXME: This method is too general. In principle we should compute the number +// of instructions required to synthesize the immediate inline compared to +// synthesizing the address inline and relying on non .text sections. +// For static O32 and N32 this may yield a small benefit, for static N64 this is +// likely to yield a much larger benefit as we have to synthesize a 64bit +// address to load a 64 bit value. +bool MaxisAsmParser::emitPartialAddress(MaxisTargetStreamer &TOut, SMLoc IDLoc, + MCSymbol *Sym) { + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + if(IsPicEnabled) { + const MCExpr *GotSym = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MaxisMCExpr *GotExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_GOT, GotSym, getContext()); + + if(isABI_O32() || isABI_N32()) { + TOut.emitRRX(Maxis::LW, ATReg, Maxis::GP, MCOperand::createExpr(GotExpr), + IDLoc, STI); + } else { //isABI_N64() + TOut.emitRRX(Maxis::LD, ATReg, Maxis::GP, MCOperand::createExpr(GotExpr), + IDLoc, STI); + } + } else { //!IsPicEnabled + const MCExpr *HiSym = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MaxisMCExpr *HiExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_HI, HiSym, getContext()); + + // FIXME: This is technically correct but gives a different result to gas, + // but gas is incomplete there (it has a fixme noting it doesn't work with + // 64-bit addresses). + // FIXME: With -msym32 option, the address expansion for N64 should probably + // use the O32 / N32 case. It's safe to use the 64 address expansion as the + // symbol's value is considered sign extended. + if(isABI_O32() || isABI_N32()) { + TOut.emitRX(Maxis::LUi, ATReg, MCOperand::createExpr(HiExpr), IDLoc, STI); + } else { //isABI_N64() + const MCExpr *HighestSym = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MaxisMCExpr *HighestExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_HIGHEST, HighestSym, getContext()); + const MCExpr *HigherSym = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MaxisMCExpr *HigherExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_HIGHER, HigherSym, getContext()); + + TOut.emitRX(Maxis::LUi, ATReg, MCOperand::createExpr(HighestExpr), IDLoc, + STI); + TOut.emitRRX(Maxis::DADDiu, ATReg, ATReg, + MCOperand::createExpr(HigherExpr), IDLoc, STI); + TOut.emitRRI(Maxis::DSLL, ATReg, ATReg, 16, IDLoc, STI); + TOut.emitRRX(Maxis::DADDiu, ATReg, ATReg, MCOperand::createExpr(HiExpr), + IDLoc, STI); + TOut.emitRRI(Maxis::DSLL, ATReg, ATReg, 16, IDLoc, STI); + } + } + return false; +} + +bool MaxisAsmParser::expandLoadImmReal(MCInst &Inst, bool IsSingle, bool IsGPR, + bool Is64FPU, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + assert(Inst.getNumOperands() == 2 && "Invalid operand count"); + assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isImm() && + "Invalid instruction operand."); + + unsigned FirstReg = Inst.getOperand(0).getReg(); + uint64_t ImmOp64 = Inst.getOperand(1).getImm(); + + uint32_t HiImmOp64 = (ImmOp64 & 0xffffffff00000000) >> 32; + // If ImmOp64 is AsmToken::Integer type (all bits set to zero in the + // exponent field), convert it to double (e.g. 1 to 1.0) + if ((HiImmOp64 & 0x7ff00000) == 0) { + APFloat RealVal(APFloat::IEEEdouble(), ImmOp64); + ImmOp64 = RealVal.bitcastToAPInt().getZExtValue(); + } + + uint32_t LoImmOp64 = ImmOp64 & 0xffffffff; + HiImmOp64 = (ImmOp64 & 0xffffffff00000000) >> 32; + + if (IsSingle) { + // Conversion of a double in an uint64_t to a float in a uint32_t, + // retaining the bit pattern of a float. + uint32_t ImmOp32; + double doubleImm = BitsToDouble(ImmOp64); + float tmp_float = static_cast(doubleImm); + ImmOp32 = FloatToBits(tmp_float); + + if (IsGPR) { + if (loadImmediate(ImmOp32, FirstReg, Maxis::NoRegister, true, true, IDLoc, + Out, STI)) + return true; + return false; + } else { + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + if (LoImmOp64 == 0) { + if (loadImmediate(ImmOp32, ATReg, Maxis::NoRegister, true, true, IDLoc, + Out, STI)) + return true; + TOut.emitRR(Maxis::MTC1, FirstReg, ATReg, IDLoc, STI); + return false; + } + + MCSection *CS = getStreamer().getCurrentSectionOnly(); + // FIXME: Enhance this expansion to use the .lit4 & .lit8 sections + // where appropriate. + MCSection *ReadOnlySection = getContext().getELFSection( + ".rodata", ELF::SHT_PROGBITS, ELF::SHF_ALLOC); + + MCSymbol *Sym = getContext().createTempSymbol(); + const MCExpr *LoSym = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MaxisMCExpr *LoExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_LO, LoSym, getContext()); + + getStreamer().SwitchSection(ReadOnlySection); + getStreamer().EmitLabel(Sym, IDLoc); + getStreamer().EmitIntValue(ImmOp32, 4); + getStreamer().SwitchSection(CS); + + if(emitPartialAddress(TOut, IDLoc, Sym)) + return true; + TOut.emitRRX(Maxis::LWC1, FirstReg, ATReg, + MCOperand::createExpr(LoExpr), IDLoc, STI); + } + return false; + } + + // if(!IsSingle) + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + if (IsGPR) { + if (LoImmOp64 == 0) { + if(isABI_N32() || isABI_N64()) { + if (loadImmediate(HiImmOp64, FirstReg, Maxis::NoRegister, false, true, + IDLoc, Out, STI)) + return true; + return false; + } else { + if (loadImmediate(HiImmOp64, FirstReg, Maxis::NoRegister, true, true, + IDLoc, Out, STI)) + return true; + + if (loadImmediate(0, nextReg(FirstReg), Maxis::NoRegister, true, true, + IDLoc, Out, STI)) + return true; + return false; + } + } + + MCSection *CS = getStreamer().getCurrentSectionOnly(); + MCSection *ReadOnlySection = getContext().getELFSection( + ".rodata", ELF::SHT_PROGBITS, ELF::SHF_ALLOC); + + MCSymbol *Sym = getContext().createTempSymbol(); + const MCExpr *LoSym = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MaxisMCExpr *LoExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_LO, LoSym, getContext()); + + getStreamer().SwitchSection(ReadOnlySection); + getStreamer().EmitLabel(Sym, IDLoc); + getStreamer().EmitIntValue(HiImmOp64, 4); + getStreamer().EmitIntValue(LoImmOp64, 4); + getStreamer().SwitchSection(CS); + + if(emitPartialAddress(TOut, IDLoc, Sym)) + return true; + if(isABI_N64()) + TOut.emitRRX(Maxis::DADDiu, ATReg, ATReg, + MCOperand::createExpr(LoExpr), IDLoc, STI); + else + TOut.emitRRX(Maxis::ADDiu, ATReg, ATReg, + MCOperand::createExpr(LoExpr), IDLoc, STI); + + if(isABI_N32() || isABI_N64()) + TOut.emitRRI(Maxis::LD, FirstReg, ATReg, 0, IDLoc, STI); + else { + TOut.emitRRI(Maxis::LW, FirstReg, ATReg, 0, IDLoc, STI); + TOut.emitRRI(Maxis::LW, nextReg(FirstReg), ATReg, 4, IDLoc, STI); + } + return false; + } else { // if(!IsGPR && !IsSingle) + if ((LoImmOp64 == 0) && + !((HiImmOp64 & 0xffff0000) && (HiImmOp64 & 0x0000ffff))) { + // FIXME: In the case where the constant is zero, we can load the + // register directly from the zero register. + if (loadImmediate(HiImmOp64, ATReg, Maxis::NoRegister, true, true, IDLoc, + Out, STI)) + return true; + if (isABI_N32() || isABI_N64()) + TOut.emitRR(Maxis::DMTC1, FirstReg, ATReg, IDLoc, STI); + else if (hasMaxis32r2()) { + TOut.emitRR(Maxis::MTC1, FirstReg, Maxis::ZERO, IDLoc, STI); + TOut.emitRRR(Maxis::MTHC1_D32, FirstReg, FirstReg, ATReg, IDLoc, STI); + } else { + TOut.emitRR(Maxis::MTC1, nextReg(FirstReg), ATReg, IDLoc, STI); + TOut.emitRR(Maxis::MTC1, FirstReg, Maxis::ZERO, IDLoc, STI); + } + return false; + } + + MCSection *CS = getStreamer().getCurrentSectionOnly(); + // FIXME: Enhance this expansion to use the .lit4 & .lit8 sections + // where appropriate. + MCSection *ReadOnlySection = getContext().getELFSection( + ".rodata", ELF::SHT_PROGBITS, ELF::SHF_ALLOC); + + MCSymbol *Sym = getContext().createTempSymbol(); + const MCExpr *LoSym = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + const MaxisMCExpr *LoExpr = + MaxisMCExpr::create(MaxisMCExpr::MEK_LO, LoSym, getContext()); + + getStreamer().SwitchSection(ReadOnlySection); + getStreamer().EmitLabel(Sym, IDLoc); + getStreamer().EmitIntValue(HiImmOp64, 4); + getStreamer().EmitIntValue(LoImmOp64, 4); + getStreamer().SwitchSection(CS); + + if(emitPartialAddress(TOut, IDLoc, Sym)) + return true; + TOut.emitRRX(Is64FPU ? Maxis::LDC164 : Maxis::LDC1, FirstReg, ATReg, + MCOperand::createExpr(LoExpr), IDLoc, STI); + } + return false; +} + +bool MaxisAsmParser::expandUncondBranchMMPseudo(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + + assert(getInstDesc(Inst.getOpcode()).getNumOperands() == 1 && + "unexpected number of operands"); + + MCOperand Offset = Inst.getOperand(0); + if (Offset.isExpr()) { + Inst.clear(); + Inst.setOpcode(Maxis::BEQ_MM); + Inst.addOperand(MCOperand::createReg(Maxis::ZERO)); + Inst.addOperand(MCOperand::createReg(Maxis::ZERO)); + Inst.addOperand(MCOperand::createExpr(Offset.getExpr())); + } else { + assert(Offset.isImm() && "expected immediate operand kind"); + if (isInt<11>(Offset.getImm())) { + // If offset fits into 11 bits then this instruction becomes microMAXIS + // 16-bit unconditional branch instruction. + if (inMicroMaxisMode()) + Inst.setOpcode(hasMaxis32r6() ? Maxis::BC16_MMR6 : Maxis::B16_MM); + } else { + if (!isInt<17>(Offset.getImm())) + return Error(IDLoc, "branch target out of range"); + if (OffsetToAlignment(Offset.getImm(), 1LL << 1)) + return Error(IDLoc, "branch to misaligned address"); + Inst.clear(); + Inst.setOpcode(Maxis::BEQ_MM); + Inst.addOperand(MCOperand::createReg(Maxis::ZERO)); + Inst.addOperand(MCOperand::createReg(Maxis::ZERO)); + Inst.addOperand(MCOperand::createImm(Offset.getImm())); + } + } + Out.EmitInstruction(Inst, *STI); + + // If .set reorder is active and branch instruction has a delay slot, + // emit a NOP after it. + const MCInstrDesc &MCID = getInstDesc(Inst.getOpcode()); + if (MCID.hasDelaySlot() && AssemblerOptions.back()->isReorder()) + TOut.emitEmptyDelaySlot(true, IDLoc, STI); + + return false; +} + +bool MaxisAsmParser::expandBranchImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + const MCOperand &DstRegOp = Inst.getOperand(0); + assert(DstRegOp.isReg() && "expected register operand kind"); + + const MCOperand &ImmOp = Inst.getOperand(1); + assert(ImmOp.isImm() && "expected immediate operand kind"); + + const MCOperand &MemOffsetOp = Inst.getOperand(2); + assert((MemOffsetOp.isImm() || MemOffsetOp.isExpr()) && + "expected immediate or expression operand"); + + bool IsLikely = false; + + unsigned OpCode = 0; + switch(Inst.getOpcode()) { + case Maxis::BneImm: + OpCode = Maxis::BNE; + break; + case Maxis::BeqImm: + OpCode = Maxis::BEQ; + break; + case Maxis::BEQLImmMacro: + OpCode = Maxis::BEQL; + IsLikely = true; + break; + case Maxis::BNELImmMacro: + OpCode = Maxis::BNEL; + IsLikely = true; + break; + default: + llvm_unreachable("Unknown immediate branch pseudo-instruction."); + break; + } + + int64_t ImmValue = ImmOp.getImm(); + if (ImmValue == 0) { + if (IsLikely) { + TOut.emitRRX(OpCode, DstRegOp.getReg(), Maxis::ZERO, + MCOperand::createExpr(MemOffsetOp.getExpr()), IDLoc, STI); + TOut.emitRRI(Maxis::SLL, Maxis::ZERO, Maxis::ZERO, 0, IDLoc, STI); + } else + TOut.emitRRX(OpCode, DstRegOp.getReg(), Maxis::ZERO, MemOffsetOp, IDLoc, + STI); + } else { + warnIfNoMacro(IDLoc); + + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + if (loadImmediate(ImmValue, ATReg, Maxis::NoRegister, !isGP64bit(), true, + IDLoc, Out, STI)) + return true; + + if (IsLikely) { + TOut.emitRRX(OpCode, DstRegOp.getReg(), ATReg, + MCOperand::createExpr(MemOffsetOp.getExpr()), IDLoc, STI); + TOut.emitRRI(Maxis::SLL, Maxis::ZERO, Maxis::ZERO, 0, IDLoc, STI); + } else + TOut.emitRRX(OpCode, DstRegOp.getReg(), ATReg, MemOffsetOp, IDLoc, STI); + } + return false; +} + +void MaxisAsmParser::expandMemInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsLoad, + bool IsImmOpnd) { + if (IsLoad) { + expandLoadInst(Inst, IDLoc, Out, STI, IsImmOpnd); + return; + } + expandStoreInst(Inst, IDLoc, Out, STI, IsImmOpnd); +} + +void MaxisAsmParser::expandLoadInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, bool IsImmOpnd) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + + unsigned DstReg = Inst.getOperand(0).getReg(); + unsigned BaseReg = Inst.getOperand(1).getReg(); + + const MCInstrDesc &Desc = getInstDesc(Inst.getOpcode()); + int16_t DstRegClass = Desc.OpInfo[0].RegClass; + unsigned DstRegClassID = + getContext().getRegisterInfo()->getRegClass(DstRegClass).getID(); + bool IsGPR = (DstRegClassID == Maxis::GPR32RegClassID) || + (DstRegClassID == Maxis::GPR64RegClassID); + + if (IsImmOpnd) { + // Try to use DstReg as the temporary. + if (IsGPR && (BaseReg != DstReg)) { + TOut.emitLoadWithImmOffset(Inst.getOpcode(), DstReg, BaseReg, + Inst.getOperand(2).getImm(), DstReg, IDLoc, + STI); + return; + } + + // At this point we need AT to perform the expansions and we exit if it is + // not available. + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return; + + TOut.emitLoadWithImmOffset(Inst.getOpcode(), DstReg, BaseReg, + Inst.getOperand(2).getImm(), ATReg, IDLoc, STI); + return; + } + + const MCExpr *ExprOffset = Inst.getOperand(2).getExpr(); + MCOperand LoOperand = MCOperand::createExpr( + MaxisMCExpr::create(MaxisMCExpr::MEK_LO, ExprOffset, getContext())); + MCOperand HiOperand = MCOperand::createExpr( + MaxisMCExpr::create(MaxisMCExpr::MEK_HI, ExprOffset, getContext())); + + // Try to use DstReg as the temporary. + if (IsGPR && (BaseReg != DstReg)) { + TOut.emitLoadWithSymOffset(Inst.getOpcode(), DstReg, BaseReg, HiOperand, + LoOperand, DstReg, IDLoc, STI); + return; + } + + // At this point we need AT to perform the expansions and we exit if it is + // not available. + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return; + + TOut.emitLoadWithSymOffset(Inst.getOpcode(), DstReg, BaseReg, HiOperand, + LoOperand, ATReg, IDLoc, STI); +} + +void MaxisAsmParser::expandStoreInst(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, + bool IsImmOpnd) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + + unsigned SrcReg = Inst.getOperand(0).getReg(); + unsigned BaseReg = Inst.getOperand(1).getReg(); + + if (IsImmOpnd) { + TOut.emitStoreWithImmOffset(Inst.getOpcode(), SrcReg, BaseReg, + Inst.getOperand(2).getImm(), + [&]() { return getATReg(IDLoc); }, IDLoc, STI); + return; + } + + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return; + + const MCExpr *ExprOffset = Inst.getOperand(2).getExpr(); + MCOperand LoOperand = MCOperand::createExpr( + MaxisMCExpr::create(MaxisMCExpr::MEK_LO, ExprOffset, getContext())); + MCOperand HiOperand = MCOperand::createExpr( + MaxisMCExpr::create(MaxisMCExpr::MEK_HI, ExprOffset, getContext())); + TOut.emitStoreWithSymOffset(Inst.getOpcode(), SrcReg, BaseReg, HiOperand, + LoOperand, ATReg, IDLoc, STI); +} + +bool MaxisAsmParser::expandLoadStoreMultiple(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + unsigned OpNum = Inst.getNumOperands(); + unsigned Opcode = Inst.getOpcode(); + unsigned NewOpcode = Opcode == Maxis::SWM_MM ? Maxis::SWM32_MM : Maxis::LWM32_MM; + + assert(Inst.getOperand(OpNum - 1).isImm() && + Inst.getOperand(OpNum - 2).isReg() && + Inst.getOperand(OpNum - 3).isReg() && "Invalid instruction operand."); + + if (OpNum < 8 && Inst.getOperand(OpNum - 1).getImm() <= 60 && + Inst.getOperand(OpNum - 1).getImm() >= 0 && + (Inst.getOperand(OpNum - 2).getReg() == Maxis::SP || + Inst.getOperand(OpNum - 2).getReg() == Maxis::SP_64) && + (Inst.getOperand(OpNum - 3).getReg() == Maxis::RA || + Inst.getOperand(OpNum - 3).getReg() == Maxis::RA_64)) { + // It can be implemented as SWM16 or LWM16 instruction. + if (inMicroMaxisMode() && hasMaxis32r6()) + NewOpcode = Opcode == Maxis::SWM_MM ? Maxis::SWM16_MMR6 : Maxis::LWM16_MMR6; + else + NewOpcode = Opcode == Maxis::SWM_MM ? Maxis::SWM16_MM : Maxis::LWM16_MM; + } + + Inst.setOpcode(NewOpcode); + Out.EmitInstruction(Inst, *STI); + return false; +} + +bool MaxisAsmParser::expandCondBranches(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + bool EmittedNoMacroWarning = false; + unsigned PseudoOpcode = Inst.getOpcode(); + unsigned SrcReg = Inst.getOperand(0).getReg(); + const MCOperand &TrgOp = Inst.getOperand(1); + const MCExpr *OffsetExpr = Inst.getOperand(2).getExpr(); + + unsigned ZeroSrcOpcode, ZeroTrgOpcode; + bool ReverseOrderSLT, IsUnsigned, IsLikely, AcceptsEquality; + + unsigned TrgReg; + if (TrgOp.isReg()) + TrgReg = TrgOp.getReg(); + else if (TrgOp.isImm()) { + warnIfNoMacro(IDLoc); + EmittedNoMacroWarning = true; + + TrgReg = getATReg(IDLoc); + if (!TrgReg) + return true; + + switch(PseudoOpcode) { + default: + llvm_unreachable("unknown opcode for branch pseudo-instruction"); + case Maxis::BLTImmMacro: + PseudoOpcode = Maxis::BLT; + break; + case Maxis::BLEImmMacro: + PseudoOpcode = Maxis::BLE; + break; + case Maxis::BGEImmMacro: + PseudoOpcode = Maxis::BGE; + break; + case Maxis::BGTImmMacro: + PseudoOpcode = Maxis::BGT; + break; + case Maxis::BLTUImmMacro: + PseudoOpcode = Maxis::BLTU; + break; + case Maxis::BLEUImmMacro: + PseudoOpcode = Maxis::BLEU; + break; + case Maxis::BGEUImmMacro: + PseudoOpcode = Maxis::BGEU; + break; + case Maxis::BGTUImmMacro: + PseudoOpcode = Maxis::BGTU; + break; + case Maxis::BLTLImmMacro: + PseudoOpcode = Maxis::BLTL; + break; + case Maxis::BLELImmMacro: + PseudoOpcode = Maxis::BLEL; + break; + case Maxis::BGELImmMacro: + PseudoOpcode = Maxis::BGEL; + break; + case Maxis::BGTLImmMacro: + PseudoOpcode = Maxis::BGTL; + break; + case Maxis::BLTULImmMacro: + PseudoOpcode = Maxis::BLTUL; + break; + case Maxis::BLEULImmMacro: + PseudoOpcode = Maxis::BLEUL; + break; + case Maxis::BGEULImmMacro: + PseudoOpcode = Maxis::BGEUL; + break; + case Maxis::BGTULImmMacro: + PseudoOpcode = Maxis::BGTUL; + break; + } + + if (loadImmediate(TrgOp.getImm(), TrgReg, Maxis::NoRegister, !isGP64bit(), + false, IDLoc, Out, STI)) + return true; + } + + switch (PseudoOpcode) { + case Maxis::BLT: + case Maxis::BLTU: + case Maxis::BLTL: + case Maxis::BLTUL: + AcceptsEquality = false; + ReverseOrderSLT = false; + IsUnsigned = ((PseudoOpcode == Maxis::BLTU) || (PseudoOpcode == Maxis::BLTUL)); + IsLikely = ((PseudoOpcode == Maxis::BLTL) || (PseudoOpcode == Maxis::BLTUL)); + ZeroSrcOpcode = Maxis::BGTZ; + ZeroTrgOpcode = Maxis::BLTZ; + break; + case Maxis::BLE: + case Maxis::BLEU: + case Maxis::BLEL: + case Maxis::BLEUL: + AcceptsEquality = true; + ReverseOrderSLT = true; + IsUnsigned = ((PseudoOpcode == Maxis::BLEU) || (PseudoOpcode == Maxis::BLEUL)); + IsLikely = ((PseudoOpcode == Maxis::BLEL) || (PseudoOpcode == Maxis::BLEUL)); + ZeroSrcOpcode = Maxis::BGEZ; + ZeroTrgOpcode = Maxis::BLEZ; + break; + case Maxis::BGE: + case Maxis::BGEU: + case Maxis::BGEL: + case Maxis::BGEUL: + AcceptsEquality = true; + ReverseOrderSLT = false; + IsUnsigned = ((PseudoOpcode == Maxis::BGEU) || (PseudoOpcode == Maxis::BGEUL)); + IsLikely = ((PseudoOpcode == Maxis::BGEL) || (PseudoOpcode == Maxis::BGEUL)); + ZeroSrcOpcode = Maxis::BLEZ; + ZeroTrgOpcode = Maxis::BGEZ; + break; + case Maxis::BGT: + case Maxis::BGTU: + case Maxis::BGTL: + case Maxis::BGTUL: + AcceptsEquality = false; + ReverseOrderSLT = true; + IsUnsigned = ((PseudoOpcode == Maxis::BGTU) || (PseudoOpcode == Maxis::BGTUL)); + IsLikely = ((PseudoOpcode == Maxis::BGTL) || (PseudoOpcode == Maxis::BGTUL)); + ZeroSrcOpcode = Maxis::BLTZ; + ZeroTrgOpcode = Maxis::BGTZ; + break; + default: + llvm_unreachable("unknown opcode for branch pseudo-instruction"); + } + + bool IsTrgRegZero = (TrgReg == Maxis::ZERO); + bool IsSrcRegZero = (SrcReg == Maxis::ZERO); + if (IsSrcRegZero && IsTrgRegZero) { + // FIXME: All of these Opcode-specific if's are needed for compatibility + // with GAS' behaviour. However, they may not generate the most efficient + // code in some circumstances. + if (PseudoOpcode == Maxis::BLT) { + TOut.emitRX(Maxis::BLTZ, Maxis::ZERO, MCOperand::createExpr(OffsetExpr), + IDLoc, STI); + return false; + } + if (PseudoOpcode == Maxis::BLE) { + TOut.emitRX(Maxis::BLEZ, Maxis::ZERO, MCOperand::createExpr(OffsetExpr), + IDLoc, STI); + Warning(IDLoc, "branch is always taken"); + return false; + } + if (PseudoOpcode == Maxis::BGE) { + TOut.emitRX(Maxis::BGEZ, Maxis::ZERO, MCOperand::createExpr(OffsetExpr), + IDLoc, STI); + Warning(IDLoc, "branch is always taken"); + return false; + } + if (PseudoOpcode == Maxis::BGT) { + TOut.emitRX(Maxis::BGTZ, Maxis::ZERO, MCOperand::createExpr(OffsetExpr), + IDLoc, STI); + return false; + } + if (PseudoOpcode == Maxis::BGTU) { + TOut.emitRRX(Maxis::BNE, Maxis::ZERO, Maxis::ZERO, + MCOperand::createExpr(OffsetExpr), IDLoc, STI); + return false; + } + if (AcceptsEquality) { + // If both registers are $0 and the pseudo-branch accepts equality, it + // will always be taken, so we emit an unconditional branch. + TOut.emitRRX(Maxis::BEQ, Maxis::ZERO, Maxis::ZERO, + MCOperand::createExpr(OffsetExpr), IDLoc, STI); + Warning(IDLoc, "branch is always taken"); + return false; + } + // If both registers are $0 and the pseudo-branch does not accept + // equality, it will never be taken, so we don't have to emit anything. + return false; + } + if (IsSrcRegZero || IsTrgRegZero) { + if ((IsSrcRegZero && PseudoOpcode == Maxis::BGTU) || + (IsTrgRegZero && PseudoOpcode == Maxis::BLTU)) { + // If the $rs is $0 and the pseudo-branch is BGTU (0 > x) or + // if the $rt is $0 and the pseudo-branch is BLTU (x < 0), + // the pseudo-branch will never be taken, so we don't emit anything. + // This only applies to unsigned pseudo-branches. + return false; + } + if ((IsSrcRegZero && PseudoOpcode == Maxis::BLEU) || + (IsTrgRegZero && PseudoOpcode == Maxis::BGEU)) { + // If the $rs is $0 and the pseudo-branch is BLEU (0 <= x) or + // if the $rt is $0 and the pseudo-branch is BGEU (x >= 0), + // the pseudo-branch will always be taken, so we emit an unconditional + // branch. + // This only applies to unsigned pseudo-branches. + TOut.emitRRX(Maxis::BEQ, Maxis::ZERO, Maxis::ZERO, + MCOperand::createExpr(OffsetExpr), IDLoc, STI); + Warning(IDLoc, "branch is always taken"); + return false; + } + if (IsUnsigned) { + // If the $rs is $0 and the pseudo-branch is BLTU (0 < x) or + // if the $rt is $0 and the pseudo-branch is BGTU (x > 0), + // the pseudo-branch will be taken only when the non-zero register is + // different from 0, so we emit a BNEZ. + // + // If the $rs is $0 and the pseudo-branch is BGEU (0 >= x) or + // if the $rt is $0 and the pseudo-branch is BLEU (x <= 0), + // the pseudo-branch will be taken only when the non-zero register is + // equal to 0, so we emit a BEQZ. + // + // Because only BLEU and BGEU branch on equality, we can use the + // AcceptsEquality variable to decide when to emit the BEQZ. + TOut.emitRRX(AcceptsEquality ? Maxis::BEQ : Maxis::BNE, + IsSrcRegZero ? TrgReg : SrcReg, Maxis::ZERO, + MCOperand::createExpr(OffsetExpr), IDLoc, STI); + return false; + } + // If we have a signed pseudo-branch and one of the registers is $0, + // we can use an appropriate compare-to-zero branch. We select which one + // to use in the switch statement above. + TOut.emitRX(IsSrcRegZero ? ZeroSrcOpcode : ZeroTrgOpcode, + IsSrcRegZero ? TrgReg : SrcReg, + MCOperand::createExpr(OffsetExpr), IDLoc, STI); + return false; + } + + // If neither the SrcReg nor the TrgReg are $0, we need AT to perform the + // expansions. If it is not available, we return. + unsigned ATRegNum = getATReg(IDLoc); + if (!ATRegNum) + return true; + + if (!EmittedNoMacroWarning) + warnIfNoMacro(IDLoc); + + // SLT fits well with 2 of our 4 pseudo-branches: + // BLT, where $rs < $rt, translates into "slt $at, $rs, $rt" and + // BGT, where $rs > $rt, translates into "slt $at, $rt, $rs". + // If the result of the SLT is 1, we branch, and if it's 0, we don't. + // This is accomplished by using a BNEZ with the result of the SLT. + // + // The other 2 pseudo-branches are opposites of the above 2 (BGE with BLT + // and BLE with BGT), so we change the BNEZ into a a BEQZ. + // Because only BGE and BLE branch on equality, we can use the + // AcceptsEquality variable to decide when to emit the BEQZ. + // Note that the order of the SLT arguments doesn't change between + // opposites. + // + // The same applies to the unsigned variants, except that SLTu is used + // instead of SLT. + TOut.emitRRR(IsUnsigned ? Maxis::SLTu : Maxis::SLT, ATRegNum, + ReverseOrderSLT ? TrgReg : SrcReg, + ReverseOrderSLT ? SrcReg : TrgReg, IDLoc, STI); + + TOut.emitRRX(IsLikely ? (AcceptsEquality ? Maxis::BEQL : Maxis::BNEL) + : (AcceptsEquality ? Maxis::BEQ : Maxis::BNE), + ATRegNum, Maxis::ZERO, MCOperand::createExpr(OffsetExpr), IDLoc, + STI); + return false; +} + +// Expand a integer division macro. +// +// Notably we don't have to emit a warning when encountering $rt as the $zero +// register, or 0 as an immediate. processInstruction() has already done that. +// +// The destination register can only be $zero when expanding (S)DivIMacro or +// D(S)DivMacro. + +bool MaxisAsmParser::expandDiv(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI, const bool IsMaxis64, + const bool Signed) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + + warnIfNoMacro(IDLoc); + + const MCOperand &RdRegOp = Inst.getOperand(0); + assert(RdRegOp.isReg() && "expected register operand kind"); + unsigned RdReg = RdRegOp.getReg(); + + const MCOperand &RsRegOp = Inst.getOperand(1); + assert(RsRegOp.isReg() && "expected register operand kind"); + unsigned RsReg = RsRegOp.getReg(); + + unsigned RtReg; + int64_t ImmValue; + + const MCOperand &RtOp = Inst.getOperand(2); + assert((RtOp.isReg() || RtOp.isImm()) && + "expected register or immediate operand kind"); + if (RtOp.isReg()) + RtReg = RtOp.getReg(); + else + ImmValue = RtOp.getImm(); + + unsigned DivOp; + unsigned ZeroReg; + unsigned SubOp; + + if (IsMaxis64) { + DivOp = Signed ? Maxis::DSDIV : Maxis::DUDIV; + ZeroReg = Maxis::ZERO_64; + SubOp = Maxis::DSUB; + } else { + DivOp = Signed ? Maxis::SDIV : Maxis::UDIV; + ZeroReg = Maxis::ZERO; + SubOp = Maxis::SUB; + } + + bool UseTraps = useTraps(); + + if (RtOp.isImm()) { + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + if (ImmValue == 0) { + if (UseTraps) + TOut.emitRRI(Maxis::TEQ, ZeroReg, ZeroReg, 0x7, IDLoc, STI); + else + TOut.emitII(Maxis::BREAK, 0x7, 0, IDLoc, STI); + return false; + } + + if (ImmValue == 1) { + TOut.emitRRR(Maxis::OR, RdReg, RsReg, Maxis::ZERO, IDLoc, STI); + return false; + } else if (Signed && ImmValue == -1) { + TOut.emitRRR(SubOp, RdReg, ZeroReg, RsReg, IDLoc, STI); + return false; + } else { + if (loadImmediate(ImmValue, ATReg, Maxis::NoRegister, isInt<32>(ImmValue), + false, Inst.getLoc(), Out, STI)) + return true; + TOut.emitRR(DivOp, RsReg, ATReg, IDLoc, STI); + TOut.emitR(Maxis::MFLO, RdReg, IDLoc, STI); + return false; + } + return true; + } + + // If the macro expansion of (d)div(u) would always trap or break, insert + // the trap/break and exit. This gives a different result to GAS. GAS has + // an inconsistency/missed optimization in that not all cases are handled + // equivalently. As the observed behaviour is the same, we're ok. + if (RtReg == Maxis::ZERO || RtReg == Maxis::ZERO_64) { + if (UseTraps) { + TOut.emitRRI(Maxis::TEQ, ZeroReg, ZeroReg, 0x7, IDLoc, STI); + return false; + } + TOut.emitII(Maxis::BREAK, 0x7, 0, IDLoc, STI); + return false; + } + + // Temporary label for first branch traget + MCContext &Context = TOut.getStreamer().getContext(); + MCSymbol *BrTarget; + MCOperand LabelOp; + + if (UseTraps) { + TOut.emitRRI(Maxis::TEQ, RtReg, ZeroReg, 0x7, IDLoc, STI); + } else { + // Branch to the li instruction. + BrTarget = Context.createTempSymbol(); + LabelOp = MCOperand::createExpr(MCSymbolRefExpr::create(BrTarget, Context)); + TOut.emitRRX(Maxis::BNE, RtReg, ZeroReg, LabelOp, IDLoc, STI); + } + + TOut.emitRR(DivOp, RsReg, RtReg, IDLoc, STI); + + if (!UseTraps) + TOut.emitII(Maxis::BREAK, 0x7, 0, IDLoc, STI); + + if (!Signed) { + if (!UseTraps) + TOut.getStreamer().EmitLabel(BrTarget); + + TOut.emitR(Maxis::MFLO, RdReg, IDLoc, STI); + return false; + } + + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + if (!UseTraps) + TOut.getStreamer().EmitLabel(BrTarget); + + TOut.emitRRI(Maxis::ADDiu, ATReg, ZeroReg, -1, IDLoc, STI); + + // Temporary label for the second branch target. + MCSymbol *BrTargetEnd = Context.createTempSymbol(); + MCOperand LabelOpEnd = + MCOperand::createExpr(MCSymbolRefExpr::create(BrTargetEnd, Context)); + + // Branch to the mflo instruction. + TOut.emitRRX(Maxis::BNE, RtReg, ATReg, LabelOpEnd, IDLoc, STI); + + if (IsMaxis64) { + TOut.emitRRI(Maxis::ADDiu, ATReg, ZeroReg, 1, IDLoc, STI); + TOut.emitRRI(Maxis::DSLL32, ATReg, ATReg, 0x1f, IDLoc, STI); + } else { + TOut.emitRI(Maxis::LUi, ATReg, (uint16_t)0x8000, IDLoc, STI); + } + + if (UseTraps) + TOut.emitRRI(Maxis::TEQ, RsReg, ATReg, 0x6, IDLoc, STI); + else { + // Branch to the mflo instruction. + TOut.emitRRX(Maxis::BNE, RsReg, ATReg, LabelOpEnd, IDLoc, STI); + TOut.emitRRI(Maxis::SLL, ZeroReg, ZeroReg, 0, IDLoc, STI); + TOut.emitII(Maxis::BREAK, 0x6, 0, IDLoc, STI); + } + + TOut.getStreamer().EmitLabel(BrTargetEnd); + TOut.emitR(Maxis::MFLO, RdReg, IDLoc, STI); + return false; +} + +bool MaxisAsmParser::expandTrunc(MCInst &Inst, bool IsDouble, bool Is64FPU, + SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + + assert(Inst.getNumOperands() == 3 && "Invalid operand count"); + assert(Inst.getOperand(0).isReg() && Inst.getOperand(1).isReg() && + Inst.getOperand(2).isReg() && "Invalid instruction operand."); + + unsigned FirstReg = Inst.getOperand(0).getReg(); + unsigned SecondReg = Inst.getOperand(1).getReg(); + unsigned ThirdReg = Inst.getOperand(2).getReg(); + + if (hasMaxis1() && !hasMaxis2()) { + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + TOut.emitRR(Maxis::CFC1, ThirdReg, Maxis::RA, IDLoc, STI); + TOut.emitRR(Maxis::CFC1, ThirdReg, Maxis::RA, IDLoc, STI); + TOut.emitNop(IDLoc, STI); + TOut.emitRRI(Maxis::ORi, ATReg, ThirdReg, 0x3, IDLoc, STI); + TOut.emitRRI(Maxis::XORi, ATReg, ATReg, 0x2, IDLoc, STI); + TOut.emitRR(Maxis::CTC1, Maxis::RA, ATReg, IDLoc, STI); + TOut.emitNop(IDLoc, STI); + TOut.emitRR(IsDouble ? (Is64FPU ? Maxis::CVT_W_D64 : Maxis::CVT_W_D32) + : Maxis::CVT_W_S, + FirstReg, SecondReg, IDLoc, STI); + TOut.emitRR(Maxis::CTC1, Maxis::RA, ThirdReg, IDLoc, STI); + TOut.emitNop(IDLoc, STI); + return false; + } + + TOut.emitRR(IsDouble ? (Is64FPU ? Maxis::TRUNC_W_D64 : Maxis::TRUNC_W_D32) + : Maxis::TRUNC_W_S, + FirstReg, SecondReg, IDLoc, STI); + + return false; +} + +bool MaxisAsmParser::expandUlh(MCInst &Inst, bool Signed, SMLoc IDLoc, + MCStreamer &Out, const MCSubtargetInfo *STI) { + if (hasMaxis32r6() || hasMaxis64r6()) { + return Error(IDLoc, "instruction not supported on maxis32r6 or maxis64r6"); + } + + const MCOperand &DstRegOp = Inst.getOperand(0); + assert(DstRegOp.isReg() && "expected register operand kind"); + const MCOperand &SrcRegOp = Inst.getOperand(1); + assert(SrcRegOp.isReg() && "expected register operand kind"); + const MCOperand &OffsetImmOp = Inst.getOperand(2); + assert(OffsetImmOp.isImm() && "expected immediate operand kind"); + + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned DstReg = DstRegOp.getReg(); + unsigned SrcReg = SrcRegOp.getReg(); + int64_t OffsetValue = OffsetImmOp.getImm(); + + // NOTE: We always need AT for ULHU, as it is always used as the source + // register for one of the LBu's. + warnIfNoMacro(IDLoc); + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + bool IsLargeOffset = !(isInt<16>(OffsetValue + 1) && isInt<16>(OffsetValue)); + if (IsLargeOffset) { + if (loadImmediate(OffsetValue, ATReg, SrcReg, !ABI.ArePtrs64bit(), true, + IDLoc, Out, STI)) + return true; + } + + int64_t FirstOffset = IsLargeOffset ? 0 : OffsetValue; + int64_t SecondOffset = IsLargeOffset ? 1 : (OffsetValue + 1); + if (isLittle()) + std::swap(FirstOffset, SecondOffset); + + unsigned FirstLbuDstReg = IsLargeOffset ? DstReg : ATReg; + unsigned SecondLbuDstReg = IsLargeOffset ? ATReg : DstReg; + + unsigned LbuSrcReg = IsLargeOffset ? ATReg : SrcReg; + unsigned SllReg = IsLargeOffset ? DstReg : ATReg; + + TOut.emitRRI(Signed ? Maxis::LB : Maxis::LBu, FirstLbuDstReg, LbuSrcReg, + FirstOffset, IDLoc, STI); + TOut.emitRRI(Maxis::LBu, SecondLbuDstReg, LbuSrcReg, SecondOffset, IDLoc, STI); + TOut.emitRRI(Maxis::SLL, SllReg, SllReg, 8, IDLoc, STI); + TOut.emitRRR(Maxis::OR, DstReg, DstReg, ATReg, IDLoc, STI); + + return false; +} + +bool MaxisAsmParser::expandUsh(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + if (hasMaxis32r6() || hasMaxis64r6()) { + return Error(IDLoc, "instruction not supported on maxis32r6 or maxis64r6"); + } + + const MCOperand &DstRegOp = Inst.getOperand(0); + assert(DstRegOp.isReg() && "expected register operand kind"); + const MCOperand &SrcRegOp = Inst.getOperand(1); + assert(SrcRegOp.isReg() && "expected register operand kind"); + const MCOperand &OffsetImmOp = Inst.getOperand(2); + assert(OffsetImmOp.isImm() && "expected immediate operand kind"); + + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned DstReg = DstRegOp.getReg(); + unsigned SrcReg = SrcRegOp.getReg(); + int64_t OffsetValue = OffsetImmOp.getImm(); + + warnIfNoMacro(IDLoc); + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + bool IsLargeOffset = !(isInt<16>(OffsetValue + 1) && isInt<16>(OffsetValue)); + if (IsLargeOffset) { + if (loadImmediate(OffsetValue, ATReg, SrcReg, !ABI.ArePtrs64bit(), true, + IDLoc, Out, STI)) + return true; + } + + int64_t FirstOffset = IsLargeOffset ? 1 : (OffsetValue + 1); + int64_t SecondOffset = IsLargeOffset ? 0 : OffsetValue; + if (isLittle()) + std::swap(FirstOffset, SecondOffset); + + if (IsLargeOffset) { + TOut.emitRRI(Maxis::SB, DstReg, ATReg, FirstOffset, IDLoc, STI); + TOut.emitRRI(Maxis::SRL, DstReg, DstReg, 8, IDLoc, STI); + TOut.emitRRI(Maxis::SB, DstReg, ATReg, SecondOffset, IDLoc, STI); + TOut.emitRRI(Maxis::LBu, ATReg, ATReg, 0, IDLoc, STI); + TOut.emitRRI(Maxis::SLL, DstReg, DstReg, 8, IDLoc, STI); + TOut.emitRRR(Maxis::OR, DstReg, DstReg, ATReg, IDLoc, STI); + } else { + TOut.emitRRI(Maxis::SB, DstReg, SrcReg, FirstOffset, IDLoc, STI); + TOut.emitRRI(Maxis::SRL, ATReg, DstReg, 8, IDLoc, STI); + TOut.emitRRI(Maxis::SB, ATReg, SrcReg, SecondOffset, IDLoc, STI); + } + + return false; +} + +bool MaxisAsmParser::expandUxw(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + if (hasMaxis32r6() || hasMaxis64r6()) { + return Error(IDLoc, "instruction not supported on maxis32r6 or maxis64r6"); + } + + const MCOperand &DstRegOp = Inst.getOperand(0); + assert(DstRegOp.isReg() && "expected register operand kind"); + const MCOperand &SrcRegOp = Inst.getOperand(1); + assert(SrcRegOp.isReg() && "expected register operand kind"); + const MCOperand &OffsetImmOp = Inst.getOperand(2); + assert(OffsetImmOp.isImm() && "expected immediate operand kind"); + + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned DstReg = DstRegOp.getReg(); + unsigned SrcReg = SrcRegOp.getReg(); + int64_t OffsetValue = OffsetImmOp.getImm(); + + // Compute left/right load/store offsets. + bool IsLargeOffset = !(isInt<16>(OffsetValue + 3) && isInt<16>(OffsetValue)); + int64_t LxlOffset = IsLargeOffset ? 0 : OffsetValue; + int64_t LxrOffset = IsLargeOffset ? 3 : (OffsetValue + 3); + if (isLittle()) + std::swap(LxlOffset, LxrOffset); + + bool IsLoadInst = (Inst.getOpcode() == Maxis::Ulw); + bool DoMove = IsLoadInst && (SrcReg == DstReg) && !IsLargeOffset; + unsigned TmpReg = SrcReg; + if (IsLargeOffset || DoMove) { + warnIfNoMacro(IDLoc); + TmpReg = getATReg(IDLoc); + if (!TmpReg) + return true; + } + + if (IsLargeOffset) { + if (loadImmediate(OffsetValue, TmpReg, SrcReg, !ABI.ArePtrs64bit(), true, + IDLoc, Out, STI)) + return true; + } + + if (DoMove) + std::swap(DstReg, TmpReg); + + unsigned XWL = IsLoadInst ? Maxis::LWL : Maxis::SWL; + unsigned XWR = IsLoadInst ? Maxis::LWR : Maxis::SWR; + TOut.emitRRI(XWL, DstReg, TmpReg, LxlOffset, IDLoc, STI); + TOut.emitRRI(XWR, DstReg, TmpReg, LxrOffset, IDLoc, STI); + + if (DoMove) + TOut.emitRRR(Maxis::OR, TmpReg, DstReg, Maxis::ZERO, IDLoc, STI); + + return false; +} + +bool MaxisAsmParser::expandAliasImmediate(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + + assert(Inst.getNumOperands() == 3 && "Invalid operand count"); + assert(Inst.getOperand(0).isReg() && + Inst.getOperand(1).isReg() && + Inst.getOperand(2).isImm() && "Invalid instruction operand."); + + unsigned ATReg = Maxis::NoRegister; + unsigned FinalDstReg = Maxis::NoRegister; + unsigned DstReg = Inst.getOperand(0).getReg(); + unsigned SrcReg = Inst.getOperand(1).getReg(); + int64_t ImmValue = Inst.getOperand(2).getImm(); + + bool Is32Bit = isInt<32>(ImmValue) || (!isGP64bit() && isUInt<32>(ImmValue)); + + unsigned FinalOpcode = Inst.getOpcode(); + + if (DstReg == SrcReg) { + ATReg = getATReg(Inst.getLoc()); + if (!ATReg) + return true; + FinalDstReg = DstReg; + DstReg = ATReg; + } + + if (!loadImmediate(ImmValue, DstReg, Maxis::NoRegister, Is32Bit, false, Inst.getLoc(), Out, STI)) { + switch (FinalOpcode) { + default: + llvm_unreachable("unimplemented expansion"); + case Maxis::ADDi: + FinalOpcode = Maxis::ADD; + break; + case Maxis::ADDiu: + FinalOpcode = Maxis::ADDu; + break; + case Maxis::ANDi: + FinalOpcode = Maxis::AND; + break; + case Maxis::NORImm: + FinalOpcode = Maxis::NOR; + break; + case Maxis::ORi: + FinalOpcode = Maxis::OR; + break; + case Maxis::SLTi: + FinalOpcode = Maxis::SLT; + break; + case Maxis::SLTiu: + FinalOpcode = Maxis::SLTu; + break; + case Maxis::XORi: + FinalOpcode = Maxis::XOR; + break; + case Maxis::ADDi_MM: + FinalOpcode = Maxis::ADD_MM; + break; + case Maxis::ADDiu_MM: + FinalOpcode = Maxis::ADDu_MM; + break; + case Maxis::ANDi_MM: + FinalOpcode = Maxis::AND_MM; + break; + case Maxis::ORi_MM: + FinalOpcode = Maxis::OR_MM; + break; + case Maxis::SLTi_MM: + FinalOpcode = Maxis::SLT_MM; + break; + case Maxis::SLTiu_MM: + FinalOpcode = Maxis::SLTu_MM; + break; + case Maxis::XORi_MM: + FinalOpcode = Maxis::XOR_MM; + break; + case Maxis::ANDi64: + FinalOpcode = Maxis::AND64; + break; + case Maxis::NORImm64: + FinalOpcode = Maxis::NOR64; + break; + case Maxis::ORi64: + FinalOpcode = Maxis::OR64; + break; + case Maxis::SLTImm64: + FinalOpcode = Maxis::SLT64; + break; + case Maxis::SLTUImm64: + FinalOpcode = Maxis::SLTu64; + break; + case Maxis::XORi64: + FinalOpcode = Maxis::XOR64; + break; + } + + if (FinalDstReg == Maxis::NoRegister) + TOut.emitRRR(FinalOpcode, DstReg, DstReg, SrcReg, IDLoc, STI); + else + TOut.emitRRR(FinalOpcode, FinalDstReg, FinalDstReg, DstReg, IDLoc, STI); + return false; + } + return true; +} + +bool MaxisAsmParser::expandRotation(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned ATReg = Maxis::NoRegister; + unsigned DReg = Inst.getOperand(0).getReg(); + unsigned SReg = Inst.getOperand(1).getReg(); + unsigned TReg = Inst.getOperand(2).getReg(); + unsigned TmpReg = DReg; + + unsigned FirstShift = Maxis::NOP; + unsigned SecondShift = Maxis::NOP; + + if (hasMaxis32r2()) { + if (DReg == SReg) { + TmpReg = getATReg(Inst.getLoc()); + if (!TmpReg) + return true; + } + + if (Inst.getOpcode() == Maxis::ROL) { + TOut.emitRRR(Maxis::SUBu, TmpReg, Maxis::ZERO, TReg, Inst.getLoc(), STI); + TOut.emitRRR(Maxis::ROTRV, DReg, SReg, TmpReg, Inst.getLoc(), STI); + return false; + } + + if (Inst.getOpcode() == Maxis::ROR) { + TOut.emitRRR(Maxis::ROTRV, DReg, SReg, TReg, Inst.getLoc(), STI); + return false; + } + + return true; + } + + if (hasMaxis32()) { + switch (Inst.getOpcode()) { + default: + llvm_unreachable("unexpected instruction opcode"); + case Maxis::ROL: + FirstShift = Maxis::SRLV; + SecondShift = Maxis::SLLV; + break; + case Maxis::ROR: + FirstShift = Maxis::SLLV; + SecondShift = Maxis::SRLV; + break; + } + + ATReg = getATReg(Inst.getLoc()); + if (!ATReg) + return true; + + TOut.emitRRR(Maxis::SUBu, ATReg, Maxis::ZERO, TReg, Inst.getLoc(), STI); + TOut.emitRRR(FirstShift, ATReg, SReg, ATReg, Inst.getLoc(), STI); + TOut.emitRRR(SecondShift, DReg, SReg, TReg, Inst.getLoc(), STI); + TOut.emitRRR(Maxis::OR, DReg, DReg, ATReg, Inst.getLoc(), STI); + + return false; + } + + return true; +} + +bool MaxisAsmParser::expandRotationImm(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned ATReg = Maxis::NoRegister; + unsigned DReg = Inst.getOperand(0).getReg(); + unsigned SReg = Inst.getOperand(1).getReg(); + int64_t ImmValue = Inst.getOperand(2).getImm(); + + unsigned FirstShift = Maxis::NOP; + unsigned SecondShift = Maxis::NOP; + + if (hasMaxis32r2()) { + if (Inst.getOpcode() == Maxis::ROLImm) { + uint64_t MaxShift = 32; + uint64_t ShiftValue = ImmValue; + if (ImmValue != 0) + ShiftValue = MaxShift - ImmValue; + TOut.emitRRI(Maxis::ROTR, DReg, SReg, ShiftValue, Inst.getLoc(), STI); + return false; + } + + if (Inst.getOpcode() == Maxis::RORImm) { + TOut.emitRRI(Maxis::ROTR, DReg, SReg, ImmValue, Inst.getLoc(), STI); + return false; + } + + return true; + } + + if (hasMaxis32()) { + if (ImmValue == 0) { + TOut.emitRRI(Maxis::SRL, DReg, SReg, 0, Inst.getLoc(), STI); + return false; + } + + switch (Inst.getOpcode()) { + default: + llvm_unreachable("unexpected instruction opcode"); + case Maxis::ROLImm: + FirstShift = Maxis::SLL; + SecondShift = Maxis::SRL; + break; + case Maxis::RORImm: + FirstShift = Maxis::SRL; + SecondShift = Maxis::SLL; + break; + } + + ATReg = getATReg(Inst.getLoc()); + if (!ATReg) + return true; + + TOut.emitRRI(FirstShift, ATReg, SReg, ImmValue, Inst.getLoc(), STI); + TOut.emitRRI(SecondShift, DReg, SReg, 32 - ImmValue, Inst.getLoc(), STI); + TOut.emitRRR(Maxis::OR, DReg, DReg, ATReg, Inst.getLoc(), STI); + + return false; + } + + return true; +} + +bool MaxisAsmParser::expandDRotation(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned ATReg = Maxis::NoRegister; + unsigned DReg = Inst.getOperand(0).getReg(); + unsigned SReg = Inst.getOperand(1).getReg(); + unsigned TReg = Inst.getOperand(2).getReg(); + unsigned TmpReg = DReg; + + unsigned FirstShift = Maxis::NOP; + unsigned SecondShift = Maxis::NOP; + + if (hasMaxis64r2()) { + if (TmpReg == SReg) { + TmpReg = getATReg(Inst.getLoc()); + if (!TmpReg) + return true; + } + + if (Inst.getOpcode() == Maxis::DROL) { + TOut.emitRRR(Maxis::DSUBu, TmpReg, Maxis::ZERO, TReg, Inst.getLoc(), STI); + TOut.emitRRR(Maxis::DROTRV, DReg, SReg, TmpReg, Inst.getLoc(), STI); + return false; + } + + if (Inst.getOpcode() == Maxis::DROR) { + TOut.emitRRR(Maxis::DROTRV, DReg, SReg, TReg, Inst.getLoc(), STI); + return false; + } + + return true; + } + + if (hasMaxis64()) { + switch (Inst.getOpcode()) { + default: + llvm_unreachable("unexpected instruction opcode"); + case Maxis::DROL: + FirstShift = Maxis::DSRLV; + SecondShift = Maxis::DSLLV; + break; + case Maxis::DROR: + FirstShift = Maxis::DSLLV; + SecondShift = Maxis::DSRLV; + break; + } + + ATReg = getATReg(Inst.getLoc()); + if (!ATReg) + return true; + + TOut.emitRRR(Maxis::DSUBu, ATReg, Maxis::ZERO, TReg, Inst.getLoc(), STI); + TOut.emitRRR(FirstShift, ATReg, SReg, ATReg, Inst.getLoc(), STI); + TOut.emitRRR(SecondShift, DReg, SReg, TReg, Inst.getLoc(), STI); + TOut.emitRRR(Maxis::OR, DReg, DReg, ATReg, Inst.getLoc(), STI); + + return false; + } + + return true; +} + +bool MaxisAsmParser::expandDRotationImm(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned ATReg = Maxis::NoRegister; + unsigned DReg = Inst.getOperand(0).getReg(); + unsigned SReg = Inst.getOperand(1).getReg(); + int64_t ImmValue = Inst.getOperand(2).getImm() % 64; + + unsigned FirstShift = Maxis::NOP; + unsigned SecondShift = Maxis::NOP; + + MCInst TmpInst; + + if (hasMaxis64r2()) { + unsigned FinalOpcode = Maxis::NOP; + if (ImmValue == 0) + FinalOpcode = Maxis::DROTR; + else if (ImmValue % 32 == 0) + FinalOpcode = Maxis::DROTR32; + else if ((ImmValue >= 1) && (ImmValue <= 32)) { + if (Inst.getOpcode() == Maxis::DROLImm) + FinalOpcode = Maxis::DROTR32; + else + FinalOpcode = Maxis::DROTR; + } else if (ImmValue >= 33) { + if (Inst.getOpcode() == Maxis::DROLImm) + FinalOpcode = Maxis::DROTR; + else + FinalOpcode = Maxis::DROTR32; + } + + uint64_t ShiftValue = ImmValue % 32; + if (Inst.getOpcode() == Maxis::DROLImm) + ShiftValue = (32 - ImmValue % 32) % 32; + + TOut.emitRRI(FinalOpcode, DReg, SReg, ShiftValue, Inst.getLoc(), STI); + + return false; + } + + if (hasMaxis64()) { + if (ImmValue == 0) { + TOut.emitRRI(Maxis::DSRL, DReg, SReg, 0, Inst.getLoc(), STI); + return false; + } + + switch (Inst.getOpcode()) { + default: + llvm_unreachable("unexpected instruction opcode"); + case Maxis::DROLImm: + if ((ImmValue >= 1) && (ImmValue <= 31)) { + FirstShift = Maxis::DSLL; + SecondShift = Maxis::DSRL32; + } + if (ImmValue == 32) { + FirstShift = Maxis::DSLL32; + SecondShift = Maxis::DSRL32; + } + if ((ImmValue >= 33) && (ImmValue <= 63)) { + FirstShift = Maxis::DSLL32; + SecondShift = Maxis::DSRL; + } + break; + case Maxis::DRORImm: + if ((ImmValue >= 1) && (ImmValue <= 31)) { + FirstShift = Maxis::DSRL; + SecondShift = Maxis::DSLL32; + } + if (ImmValue == 32) { + FirstShift = Maxis::DSRL32; + SecondShift = Maxis::DSLL32; + } + if ((ImmValue >= 33) && (ImmValue <= 63)) { + FirstShift = Maxis::DSRL32; + SecondShift = Maxis::DSLL; + } + break; + } + + ATReg = getATReg(Inst.getLoc()); + if (!ATReg) + return true; + + TOut.emitRRI(FirstShift, ATReg, SReg, ImmValue % 32, Inst.getLoc(), STI); + TOut.emitRRI(SecondShift, DReg, SReg, (32 - ImmValue % 32) % 32, + Inst.getLoc(), STI); + TOut.emitRRR(Maxis::OR, DReg, DReg, ATReg, Inst.getLoc(), STI); + + return false; + } + + return true; +} + +bool MaxisAsmParser::expandAbs(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned FirstRegOp = Inst.getOperand(0).getReg(); + unsigned SecondRegOp = Inst.getOperand(1).getReg(); + + TOut.emitRI(Maxis::BGEZ, SecondRegOp, 8, IDLoc, STI); + if (FirstRegOp != SecondRegOp) + TOut.emitRRR(Maxis::ADDu, FirstRegOp, SecondRegOp, Maxis::ZERO, IDLoc, STI); + else + TOut.emitEmptyDelaySlot(false, IDLoc, STI); + TOut.emitRRR(Maxis::SUB, FirstRegOp, Maxis::ZERO, SecondRegOp, IDLoc, STI); + + return false; +} + +bool MaxisAsmParser::expandMulImm(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned ATReg = Maxis::NoRegister; + unsigned DstReg = Inst.getOperand(0).getReg(); + unsigned SrcReg = Inst.getOperand(1).getReg(); + int32_t ImmValue = Inst.getOperand(2).getImm(); + + ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + loadImmediate(ImmValue, ATReg, Maxis::NoRegister, true, false, IDLoc, Out, STI); + + TOut.emitRR(Inst.getOpcode() == Maxis::MULImmMacro ? Maxis::MULT : Maxis::DMULT, + SrcReg, ATReg, IDLoc, STI); + + TOut.emitR(Maxis::MFLO, DstReg, IDLoc, STI); + + return false; +} + +bool MaxisAsmParser::expandMulO(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned ATReg = Maxis::NoRegister; + unsigned DstReg = Inst.getOperand(0).getReg(); + unsigned SrcReg = Inst.getOperand(1).getReg(); + unsigned TmpReg = Inst.getOperand(2).getReg(); + + ATReg = getATReg(Inst.getLoc()); + if (!ATReg) + return true; + + TOut.emitRR(Inst.getOpcode() == Maxis::MULOMacro ? Maxis::MULT : Maxis::DMULT, + SrcReg, TmpReg, IDLoc, STI); + + TOut.emitR(Maxis::MFLO, DstReg, IDLoc, STI); + + TOut.emitRRI(Inst.getOpcode() == Maxis::MULOMacro ? Maxis::SRA : Maxis::DSRA32, + DstReg, DstReg, 0x1F, IDLoc, STI); + + TOut.emitR(Maxis::MFHI, ATReg, IDLoc, STI); + + if (useTraps()) { + TOut.emitRRI(Maxis::TNE, DstReg, ATReg, 6, IDLoc, STI); + } else { + MCContext & Context = TOut.getStreamer().getContext(); + MCSymbol * BrTarget = Context.createTempSymbol(); + MCOperand LabelOp = + MCOperand::createExpr(MCSymbolRefExpr::create(BrTarget, Context)); + + TOut.emitRRX(Maxis::BEQ, DstReg, ATReg, LabelOp, IDLoc, STI); + if (AssemblerOptions.back()->isReorder()) + TOut.emitNop(IDLoc, STI); + TOut.emitII(Maxis::BREAK, 6, 0, IDLoc, STI); + + TOut.getStreamer().EmitLabel(BrTarget); + } + TOut.emitR(Maxis::MFLO, DstReg, IDLoc, STI); + + return false; +} + +bool MaxisAsmParser::expandMulOU(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned ATReg = Maxis::NoRegister; + unsigned DstReg = Inst.getOperand(0).getReg(); + unsigned SrcReg = Inst.getOperand(1).getReg(); + unsigned TmpReg = Inst.getOperand(2).getReg(); + + ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + TOut.emitRR(Inst.getOpcode() == Maxis::MULOUMacro ? Maxis::MULTu : Maxis::DMULTu, + SrcReg, TmpReg, IDLoc, STI); + + TOut.emitR(Maxis::MFHI, ATReg, IDLoc, STI); + TOut.emitR(Maxis::MFLO, DstReg, IDLoc, STI); + if (useTraps()) { + TOut.emitRRI(Maxis::TNE, ATReg, Maxis::ZERO, 6, IDLoc, STI); + } else { + MCContext & Context = TOut.getStreamer().getContext(); + MCSymbol * BrTarget = Context.createTempSymbol(); + MCOperand LabelOp = + MCOperand::createExpr(MCSymbolRefExpr::create(BrTarget, Context)); + + TOut.emitRRX(Maxis::BEQ, ATReg, Maxis::ZERO, LabelOp, IDLoc, STI); + if (AssemblerOptions.back()->isReorder()) + TOut.emitNop(IDLoc, STI); + TOut.emitII(Maxis::BREAK, 6, 0, IDLoc, STI); + + TOut.getStreamer().EmitLabel(BrTarget); + } + + return false; +} + +bool MaxisAsmParser::expandDMULMacro(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned DstReg = Inst.getOperand(0).getReg(); + unsigned SrcReg = Inst.getOperand(1).getReg(); + unsigned TmpReg = Inst.getOperand(2).getReg(); + + TOut.emitRR(Maxis::DMULTu, SrcReg, TmpReg, IDLoc, STI); + TOut.emitR(Maxis::MFLO, DstReg, IDLoc, STI); + + return false; +} + +// Expand 'ld $ offset($reg2)' to 'lw $, offset($reg2); +// lw $>, offset+4($reg2)' +// or expand 'sd $ offset($reg2)' to 'sw $, offset($reg2); +// sw $>, offset+4($reg2)' +// for O32. +bool MaxisAsmParser::expandLoadStoreDMacro(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out, + const MCSubtargetInfo *STI, + bool IsLoad) { + if (!isABI_O32()) + return true; + + warnIfNoMacro(IDLoc); + + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned Opcode = IsLoad ? Maxis::LW : Maxis::SW; + unsigned FirstReg = Inst.getOperand(0).getReg(); + unsigned SecondReg = nextReg(FirstReg); + unsigned BaseReg = Inst.getOperand(1).getReg(); + if (!SecondReg) + return true; + + warnIfRegIndexIsAT(FirstReg, IDLoc); + + assert(Inst.getOperand(2).isImm() && + "Offset for load macro is not immediate!"); + + MCOperand &FirstOffset = Inst.getOperand(2); + signed NextOffset = FirstOffset.getImm() + 4; + MCOperand SecondOffset = MCOperand::createImm(NextOffset); + + if (!isInt<16>(FirstOffset.getImm()) || !isInt<16>(NextOffset)) + return true; + + // For loads, clobber the base register with the second load instead of the + // first if the BaseReg == FirstReg. + if (FirstReg != BaseReg || !IsLoad) { + TOut.emitRRX(Opcode, FirstReg, BaseReg, FirstOffset, IDLoc, STI); + TOut.emitRRX(Opcode, SecondReg, BaseReg, SecondOffset, IDLoc, STI); + } else { + TOut.emitRRX(Opcode, SecondReg, BaseReg, SecondOffset, IDLoc, STI); + TOut.emitRRX(Opcode, FirstReg, BaseReg, FirstOffset, IDLoc, STI); + } + + return false; +} + +bool MaxisAsmParser::expandSeq(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + + warnIfNoMacro(IDLoc); + MaxisTargetStreamer &TOut = getTargetStreamer(); + + if (Inst.getOperand(1).getReg() != Maxis::ZERO && + Inst.getOperand(2).getReg() != Maxis::ZERO) { + TOut.emitRRR(Maxis::XOR, Inst.getOperand(0).getReg(), + Inst.getOperand(1).getReg(), Inst.getOperand(2).getReg(), + IDLoc, STI); + TOut.emitRRI(Maxis::SLTiu, Inst.getOperand(0).getReg(), + Inst.getOperand(0).getReg(), 1, IDLoc, STI); + return false; + } + + unsigned Reg = 0; + if (Inst.getOperand(1).getReg() == Maxis::ZERO) { + Reg = Inst.getOperand(2).getReg(); + } else { + Reg = Inst.getOperand(1).getReg(); + } + TOut.emitRRI(Maxis::SLTiu, Inst.getOperand(0).getReg(), Reg, 1, IDLoc, STI); + return false; +} + +bool MaxisAsmParser::expandSeqI(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + warnIfNoMacro(IDLoc); + MaxisTargetStreamer &TOut = getTargetStreamer(); + + unsigned Opc; + int64_t Imm = Inst.getOperand(2).getImm(); + unsigned Reg = Inst.getOperand(1).getReg(); + + if (Imm == 0) { + TOut.emitRRI(Maxis::SLTiu, Inst.getOperand(0).getReg(), + Inst.getOperand(1).getReg(), 1, IDLoc, STI); + return false; + } else { + + if (Reg == Maxis::ZERO) { + Warning(IDLoc, "comparison is always false"); + TOut.emitRRR(isGP64bit() ? Maxis::DADDu : Maxis::ADDu, + Inst.getOperand(0).getReg(), Reg, Reg, IDLoc, STI); + return false; + } + + if (Imm > -0x8000 && Imm < 0) { + Imm = -Imm; + Opc = isGP64bit() ? Maxis::DADDiu : Maxis::ADDiu; + } else { + Opc = Maxis::XORi; + } + } + if (!isUInt<16>(Imm)) { + unsigned ATReg = getATReg(IDLoc); + if (!ATReg) + return true; + + if (loadImmediate(Imm, ATReg, Maxis::NoRegister, true, isGP64bit(), IDLoc, + Out, STI)) + return true; + + TOut.emitRRR(Maxis::XOR, Inst.getOperand(0).getReg(), + Inst.getOperand(1).getReg(), ATReg, IDLoc, STI); + TOut.emitRRI(Maxis::SLTiu, Inst.getOperand(0).getReg(), + Inst.getOperand(0).getReg(), 1, IDLoc, STI); + return false; + } + + TOut.emitRRI(Opc, Inst.getOperand(0).getReg(), Inst.getOperand(1).getReg(), + Imm, IDLoc, STI); + TOut.emitRRI(Maxis::SLTiu, Inst.getOperand(0).getReg(), + Inst.getOperand(0).getReg(), 1, IDLoc, STI); + return false; +} + +// Map the DSP accumulator and control register to the corresponding gpr +// operand. Unlike the other alias, the m(f|t)t(lo|hi|acx) instructions +// do not map the DSP registers contigously to gpr registers. +static unsigned getRegisterForMxtrDSP(MCInst &Inst, bool IsMFDSP) { + switch (Inst.getOpcode()) { + case Maxis::MFTLO: + case Maxis::MTTLO: + switch (Inst.getOperand(IsMFDSP ? 1 : 0).getReg()) { + case Maxis::AC0: + return Maxis::ZERO; + case Maxis::AC1: + return Maxis::A0; + case Maxis::AC2: + return Maxis::T0; + case Maxis::AC3: + return Maxis::T4; + default: + llvm_unreachable("Unknown register for 'mttr' alias!"); + } + case Maxis::MFTHI: + case Maxis::MTTHI: + switch (Inst.getOperand(IsMFDSP ? 1 : 0).getReg()) { + case Maxis::AC0: + return Maxis::AT; + case Maxis::AC1: + return Maxis::A1; + case Maxis::AC2: + return Maxis::T1; + case Maxis::AC3: + return Maxis::T5; + default: + llvm_unreachable("Unknown register for 'mttr' alias!"); + } + case Maxis::MFTACX: + case Maxis::MTTACX: + switch (Inst.getOperand(IsMFDSP ? 1 : 0).getReg()) { + case Maxis::AC0: + return Maxis::V0; + case Maxis::AC1: + return Maxis::A2; + case Maxis::AC2: + return Maxis::T2; + case Maxis::AC3: + return Maxis::T6; + default: + llvm_unreachable("Unknown register for 'mttr' alias!"); + } + case Maxis::MFTDSP: + case Maxis::MTTDSP: + return Maxis::S0; + default: + llvm_unreachable("Unknown instruction for 'mttr' dsp alias!"); + } +} + +// Map the floating point register operand to the corresponding register +// operand. +static unsigned getRegisterForMxtrFP(MCInst &Inst, bool IsMFTC1) { + switch (Inst.getOperand(IsMFTC1 ? 1 : 0).getReg()) { + case Maxis::F0: return Maxis::ZERO; + case Maxis::F1: return Maxis::AT; + case Maxis::F2: return Maxis::V0; + case Maxis::F3: return Maxis::V1; + case Maxis::F4: return Maxis::A0; + case Maxis::F5: return Maxis::A1; + case Maxis::F6: return Maxis::A2; + case Maxis::F7: return Maxis::A3; + case Maxis::F8: return Maxis::T0; + case Maxis::F9: return Maxis::T1; + case Maxis::F10: return Maxis::T2; + case Maxis::F11: return Maxis::T3; + case Maxis::F12: return Maxis::T4; + case Maxis::F13: return Maxis::T5; + case Maxis::F14: return Maxis::T6; + case Maxis::F15: return Maxis::T7; + case Maxis::F16: return Maxis::S0; + case Maxis::F17: return Maxis::S1; + case Maxis::F18: return Maxis::S2; + case Maxis::F19: return Maxis::S3; + case Maxis::F20: return Maxis::S4; + case Maxis::F21: return Maxis::S5; + case Maxis::F22: return Maxis::S6; + case Maxis::F23: return Maxis::S7; + case Maxis::F24: return Maxis::T8; + case Maxis::F25: return Maxis::T9; + case Maxis::F26: return Maxis::K0; + case Maxis::F27: return Maxis::K1; + case Maxis::F28: return Maxis::GP; + case Maxis::F29: return Maxis::SP; + case Maxis::F30: return Maxis::FP; + case Maxis::F31: return Maxis::RA; + default: llvm_unreachable("Unknown register for mttc1 alias!"); + } +} + +// Map the coprocessor operand the corresponding gpr register operand. +static unsigned getRegisterForMxtrC0(MCInst &Inst, bool IsMFTC0) { + switch (Inst.getOperand(IsMFTC0 ? 1 : 0).getReg()) { + case Maxis::COP00: return Maxis::ZERO; + case Maxis::COP01: return Maxis::AT; + case Maxis::COP02: return Maxis::V0; + case Maxis::COP03: return Maxis::V1; + case Maxis::COP04: return Maxis::A0; + case Maxis::COP05: return Maxis::A1; + case Maxis::COP06: return Maxis::A2; + case Maxis::COP07: return Maxis::A3; + case Maxis::COP08: return Maxis::T0; + case Maxis::COP09: return Maxis::T1; + case Maxis::COP010: return Maxis::T2; + case Maxis::COP011: return Maxis::T3; + case Maxis::COP012: return Maxis::T4; + case Maxis::COP013: return Maxis::T5; + case Maxis::COP014: return Maxis::T6; + case Maxis::COP015: return Maxis::T7; + case Maxis::COP016: return Maxis::S0; + case Maxis::COP017: return Maxis::S1; + case Maxis::COP018: return Maxis::S2; + case Maxis::COP019: return Maxis::S3; + case Maxis::COP020: return Maxis::S4; + case Maxis::COP021: return Maxis::S5; + case Maxis::COP022: return Maxis::S6; + case Maxis::COP023: return Maxis::S7; + case Maxis::COP024: return Maxis::T8; + case Maxis::COP025: return Maxis::T9; + case Maxis::COP026: return Maxis::K0; + case Maxis::COP027: return Maxis::K1; + case Maxis::COP028: return Maxis::GP; + case Maxis::COP029: return Maxis::SP; + case Maxis::COP030: return Maxis::FP; + case Maxis::COP031: return Maxis::RA; + default: llvm_unreachable("Unknown register for mttc0 alias!"); + } +} + +/// Expand an alias of 'mftr' or 'mttr' into the full instruction, by producing +/// an mftr or mttr with the correctly mapped gpr register, u, sel and h bits. +bool MaxisAsmParser::expandMXTRAlias(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer &TOut = getTargetStreamer(); + unsigned rd = 0; + unsigned u = 1; + unsigned sel = 0; + unsigned h = 0; + bool IsMFTR = false; + switch (Inst.getOpcode()) { + case Maxis::MFTC0: + IsMFTR = true; + LLVM_FALLTHROUGH; + case Maxis::MTTC0: + u = 0; + rd = getRegisterForMxtrC0(Inst, IsMFTR); + sel = Inst.getOperand(2).getImm(); + break; + case Maxis::MFTGPR: + IsMFTR = true; + LLVM_FALLTHROUGH; + case Maxis::MTTGPR: + rd = Inst.getOperand(IsMFTR ? 1 : 0).getReg(); + break; + case Maxis::MFTLO: + case Maxis::MFTHI: + case Maxis::MFTACX: + case Maxis::MFTDSP: + IsMFTR = true; + LLVM_FALLTHROUGH; + case Maxis::MTTLO: + case Maxis::MTTHI: + case Maxis::MTTACX: + case Maxis::MTTDSP: + rd = getRegisterForMxtrDSP(Inst, IsMFTR); + sel = 1; + break; + case Maxis::MFTHC1: + h = 1; + LLVM_FALLTHROUGH; + case Maxis::MFTC1: + IsMFTR = true; + rd = getRegisterForMxtrFP(Inst, IsMFTR); + sel = 2; + break; + case Maxis::MTTHC1: + h = 1; + LLVM_FALLTHROUGH; + case Maxis::MTTC1: + rd = getRegisterForMxtrFP(Inst, IsMFTR); + sel = 2; + break; + case Maxis::CFTC1: + IsMFTR = true; + LLVM_FALLTHROUGH; + case Maxis::CTTC1: + rd = getRegisterForMxtrFP(Inst, IsMFTR); + sel = 3; + break; + } + unsigned Op0 = IsMFTR ? Inst.getOperand(0).getReg() : rd; + unsigned Op1 = + IsMFTR ? rd + : (Inst.getOpcode() != Maxis::MTTDSP ? Inst.getOperand(1).getReg() + : Inst.getOperand(0).getReg()); + + TOut.emitRRIII(IsMFTR ? Maxis::MFTR : Maxis::MTTR, Op0, Op1, u, sel, h, IDLoc, + STI); + return false; +} + +unsigned +MaxisAsmParser::checkEarlyTargetMatchPredicate(MCInst &Inst, + const OperandVector &Operands) { + switch (Inst.getOpcode()) { + default: + return Match_Success; + case Maxis::DATI: + case Maxis::DAHI: + if (static_cast(*Operands[1]) + .isValidForTie(static_cast(*Operands[2]))) + return Match_Success; + return Match_RequiresSameSrcAndDst; + } +} + +unsigned MaxisAsmParser::checkTargetMatchPredicate(MCInst &Inst) { + switch (Inst.getOpcode()) { + // As described by the MAXISR6 spec, daui must not use the zero operand for + // its source operand. + case Maxis::DAUI: + if (Inst.getOperand(1).getReg() == Maxis::ZERO || + Inst.getOperand(1).getReg() == Maxis::ZERO_64) + return Match_RequiresNoZeroRegister; + return Match_Success; + // As described by the Maxis32r2 spec, the registers Rd and Rs for + // jalr.hb must be different. + // It also applies for registers Rt and Rs of microMAXISr6 jalrc.hb instruction + // and registers Rd and Base for microMAXIS lwp instruction + case Maxis::JALR_HB: + case Maxis::JALRC_HB_MMR6: + case Maxis::JALRC_MMR6: + if (Inst.getOperand(0).getReg() == Inst.getOperand(1).getReg()) + return Match_RequiresDifferentSrcAndDst; + return Match_Success; + case Maxis::LWP_MM: + case Maxis::LWP_MMR6: + if (Inst.getOperand(0).getReg() == Inst.getOperand(2).getReg()) + return Match_RequiresDifferentSrcAndDst; + return Match_Success; + case Maxis::SYNC: + if (Inst.getOperand(0).getImm() != 0 && !hasMaxis32()) + return Match_NonZeroOperandForSync; + return Match_Success; + // As described the MAXISR6 spec, the compact branches that compare registers + // must: + // a) Not use the zero register. + // b) Not use the same register twice. + // c) rs < rt for bnec, beqc. + // NB: For this case, the encoding will swap the operands as their + // ordering doesn't matter. GAS performs this transformation too. + // Hence, that constraint does not have to be enforced. + // + // The compact branches that branch iff the signed addition of two registers + // would overflow must have rs >= rt. That can be handled like beqc/bnec with + // operand swapping. They do not have restriction of using the zero register. + case Maxis::BLEZC: case Maxis::BLEZC_MMR6: + case Maxis::BGEZC: case Maxis::BGEZC_MMR6: + case Maxis::BGTZC: case Maxis::BGTZC_MMR6: + case Maxis::BLTZC: case Maxis::BLTZC_MMR6: + case Maxis::BEQZC: case Maxis::BEQZC_MMR6: + case Maxis::BNEZC: case Maxis::BNEZC_MMR6: + case Maxis::BLEZC64: + case Maxis::BGEZC64: + case Maxis::BGTZC64: + case Maxis::BLTZC64: + case Maxis::BEQZC64: + case Maxis::BNEZC64: + if (Inst.getOperand(0).getReg() == Maxis::ZERO || + Inst.getOperand(0).getReg() == Maxis::ZERO_64) + return Match_RequiresNoZeroRegister; + return Match_Success; + case Maxis::BGEC: case Maxis::BGEC_MMR6: + case Maxis::BLTC: case Maxis::BLTC_MMR6: + case Maxis::BGEUC: case Maxis::BGEUC_MMR6: + case Maxis::BLTUC: case Maxis::BLTUC_MMR6: + case Maxis::BEQC: case Maxis::BEQC_MMR6: + case Maxis::BNEC: case Maxis::BNEC_MMR6: + case Maxis::BGEC64: + case Maxis::BLTC64: + case Maxis::BGEUC64: + case Maxis::BLTUC64: + case Maxis::BEQC64: + case Maxis::BNEC64: + if (Inst.getOperand(0).getReg() == Maxis::ZERO || + Inst.getOperand(0).getReg() == Maxis::ZERO_64) + return Match_RequiresNoZeroRegister; + if (Inst.getOperand(1).getReg() == Maxis::ZERO || + Inst.getOperand(1).getReg() == Maxis::ZERO_64) + return Match_RequiresNoZeroRegister; + if (Inst.getOperand(0).getReg() == Inst.getOperand(1).getReg()) + return Match_RequiresDifferentOperands; + return Match_Success; + case Maxis::DINS: { + assert(Inst.getOperand(2).isImm() && Inst.getOperand(3).isImm() && + "Operands must be immediates for dins!"); + const signed Pos = Inst.getOperand(2).getImm(); + const signed Size = Inst.getOperand(3).getImm(); + if ((0 > (Pos + Size)) || ((Pos + Size) > 32)) + return Match_RequiresPosSizeRange0_32; + return Match_Success; + } + case Maxis::DINSM: + case Maxis::DINSU: { + assert(Inst.getOperand(2).isImm() && Inst.getOperand(3).isImm() && + "Operands must be immediates for dinsm/dinsu!"); + const signed Pos = Inst.getOperand(2).getImm(); + const signed Size = Inst.getOperand(3).getImm(); + if ((32 >= (Pos + Size)) || ((Pos + Size) > 64)) + return Match_RequiresPosSizeRange33_64; + return Match_Success; + } + case Maxis::DEXT: { + assert(Inst.getOperand(2).isImm() && Inst.getOperand(3).isImm() && + "Operands must be immediates for DEXTM!"); + const signed Pos = Inst.getOperand(2).getImm(); + const signed Size = Inst.getOperand(3).getImm(); + if ((1 > (Pos + Size)) || ((Pos + Size) > 63)) + return Match_RequiresPosSizeUImm6; + return Match_Success; + } + case Maxis::DEXTM: + case Maxis::DEXTU: { + assert(Inst.getOperand(2).isImm() && Inst.getOperand(3).isImm() && + "Operands must be immediates for dextm/dextu!"); + const signed Pos = Inst.getOperand(2).getImm(); + const signed Size = Inst.getOperand(3).getImm(); + if ((32 > (Pos + Size)) || ((Pos + Size) > 64)) + return Match_RequiresPosSizeRange33_64; + return Match_Success; + } + } + + uint64_t TSFlags = getInstDesc(Inst.getOpcode()).TSFlags; + if ((TSFlags & MaxisII::HasFCCRegOperand) && + (Inst.getOperand(0).getReg() != Maxis::FCC0) && !hasEightFccRegisters()) + return Match_NoFCCRegisterForCurrentISA; + + return Match_Success; + +} + +static SMLoc RefineErrorLoc(const SMLoc Loc, const OperandVector &Operands, + uint64_t ErrorInfo) { + if (ErrorInfo != ~0ULL && ErrorInfo < Operands.size()) { + SMLoc ErrorLoc = Operands[ErrorInfo]->getStartLoc(); + if (ErrorLoc == SMLoc()) + return Loc; + return ErrorLoc; + } + return Loc; +} + +bool MaxisAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, + OperandVector &Operands, + MCStreamer &Out, + uint64_t &ErrorInfo, + bool MatchingInlineAsm) { + MCInst Inst; + unsigned MatchResult = + MatchInstructionImpl(Operands, Inst, ErrorInfo, MatchingInlineAsm); + + switch (MatchResult) { + case Match_Success: + if (processInstruction(Inst, IDLoc, Out, STI)) + return true; + return false; + case Match_MissingFeature: + Error(IDLoc, "instruction requires a CPU feature not currently enabled"); + return true; + case Match_InvalidOperand: { + SMLoc ErrorLoc = IDLoc; + if (ErrorInfo != ~0ULL) { + if (ErrorInfo >= Operands.size()) + return Error(IDLoc, "too few operands for instruction"); + + ErrorLoc = Operands[ErrorInfo]->getStartLoc(); + if (ErrorLoc == SMLoc()) + ErrorLoc = IDLoc; + } + + return Error(ErrorLoc, "invalid operand for instruction"); + } + case Match_NonZeroOperandForSync: + return Error(IDLoc, "s-type must be zero or unspecified for pre-MAXIS32 ISAs"); + case Match_MnemonicFail: + return Error(IDLoc, "invalid instruction"); + case Match_RequiresDifferentSrcAndDst: + return Error(IDLoc, "source and destination must be different"); + case Match_RequiresDifferentOperands: + return Error(IDLoc, "registers must be different"); + case Match_RequiresNoZeroRegister: + return Error(IDLoc, "invalid operand ($zero) for instruction"); + case Match_RequiresSameSrcAndDst: + return Error(IDLoc, "source and destination must match"); + case Match_NoFCCRegisterForCurrentISA: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "non-zero fcc register doesn't exist in current ISA level"); + case Match_Immz: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), "expected '0'"); + case Match_UImm1_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 1-bit unsigned immediate"); + case Match_UImm2_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 2-bit unsigned immediate"); + case Match_UImm2_1: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range 1 .. 4"); + case Match_UImm3_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 3-bit unsigned immediate"); + case Match_UImm4_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 4-bit unsigned immediate"); + case Match_SImm4_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 4-bit signed immediate"); + case Match_UImm5_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 5-bit unsigned immediate"); + case Match_SImm5_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 5-bit signed immediate"); + case Match_UImm5_1: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range 1 .. 32"); + case Match_UImm5_32: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range 32 .. 63"); + case Match_UImm5_33: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range 33 .. 64"); + case Match_UImm5_0_Report_UImm6: + // This is used on UImm5 operands that have a corresponding UImm5_32 + // operand to avoid confusing the user. + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 6-bit unsigned immediate"); + case Match_UImm5_Lsl2: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected both 7-bit unsigned immediate and multiple of 4"); + case Match_UImmRange2_64: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range 2 .. 64"); + case Match_UImm6_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 6-bit unsigned immediate"); + case Match_UImm6_Lsl2: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected both 8-bit unsigned immediate and multiple of 4"); + case Match_SImm6_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 6-bit signed immediate"); + case Match_UImm7_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 7-bit unsigned immediate"); + case Match_UImm7_N1: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected immediate in range -1 .. 126"); + case Match_SImm7_Lsl2: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected both 9-bit signed immediate and multiple of 4"); + case Match_UImm8_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 8-bit unsigned immediate"); + case Match_UImm10_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 10-bit unsigned immediate"); + case Match_SImm10_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 10-bit signed immediate"); + case Match_SImm11_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 11-bit signed immediate"); + case Match_UImm16: + case Match_UImm16_Relaxed: + case Match_UImm16_AltRelaxed: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 16-bit unsigned immediate"); + case Match_SImm16: + case Match_SImm16_Relaxed: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 16-bit signed immediate"); + case Match_SImm19_Lsl2: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected both 19-bit signed immediate and multiple of 4"); + case Match_UImm20_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 20-bit unsigned immediate"); + case Match_UImm26_0: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 26-bit unsigned immediate"); + case Match_SImm32: + case Match_SImm32_Relaxed: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 32-bit signed immediate"); + case Match_UImm32_Coerced: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected 32-bit immediate"); + case Match_MemSImm9: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected memory with 9-bit signed offset"); + case Match_MemSImm10: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected memory with 10-bit signed offset"); + case Match_MemSImm10Lsl1: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected memory with 11-bit signed offset and multiple of 2"); + case Match_MemSImm10Lsl2: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected memory with 12-bit signed offset and multiple of 4"); + case Match_MemSImm10Lsl3: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected memory with 13-bit signed offset and multiple of 8"); + case Match_MemSImm11: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected memory with 11-bit signed offset"); + case Match_MemSImm12: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected memory with 12-bit signed offset"); + case Match_MemSImm16: + return Error(RefineErrorLoc(IDLoc, Operands, ErrorInfo), + "expected memory with 16-bit signed offset"); + case Match_RequiresPosSizeRange0_32: { + SMLoc ErrorStart = Operands[3]->getStartLoc(); + SMLoc ErrorEnd = Operands[4]->getEndLoc(); + return Error(ErrorStart, "size plus position are not in the range 0 .. 32", + SMRange(ErrorStart, ErrorEnd)); + } + case Match_RequiresPosSizeUImm6: { + SMLoc ErrorStart = Operands[3]->getStartLoc(); + SMLoc ErrorEnd = Operands[4]->getEndLoc(); + return Error(ErrorStart, "size plus position are not in the range 1 .. 63", + SMRange(ErrorStart, ErrorEnd)); + } + case Match_RequiresPosSizeRange33_64: { + SMLoc ErrorStart = Operands[3]->getStartLoc(); + SMLoc ErrorEnd = Operands[4]->getEndLoc(); + return Error(ErrorStart, "size plus position are not in the range 33 .. 64", + SMRange(ErrorStart, ErrorEnd)); + } + } + + llvm_unreachable("Implement any new match types added!"); +} + +void MaxisAsmParser::warnIfRegIndexIsAT(unsigned RegIndex, SMLoc Loc) { + if (RegIndex != 0 && AssemblerOptions.back()->getATRegIndex() == RegIndex) + Warning(Loc, "used $at (currently $" + Twine(RegIndex) + + ") without \".set noat\""); +} + +void MaxisAsmParser::warnIfNoMacro(SMLoc Loc) { + if (!AssemblerOptions.back()->isMacro()) + Warning(Loc, "macro instruction expanded into multiple instructions"); +} + +void +MaxisAsmParser::printWarningWithFixIt(const Twine &Msg, const Twine &FixMsg, + SMRange Range, bool ShowColors) { + getSourceManager().PrintMessage(Range.Start, SourceMgr::DK_Warning, Msg, + Range, SMFixIt(Range, FixMsg), + ShowColors); +} + +int MaxisAsmParser::matchCPURegisterName(StringRef Name) { + int CC; + + CC = StringSwitch(Name) + .Case("zero", 0) + .Cases("at", "AT", 1) + .Case("a0", 4) + .Case("a1", 5) + .Case("a2", 6) + .Case("a3", 7) + .Case("v0", 2) + .Case("v1", 3) + .Case("s0", 16) + .Case("s1", 17) + .Case("s2", 18) + .Case("s3", 19) + .Case("s4", 20) + .Case("s5", 21) + .Case("s6", 22) + .Case("s7", 23) + .Case("k0", 26) + .Case("k1", 27) + .Case("gp", 28) + .Case("sp", 29) + .Case("fp", 30) + .Case("s8", 30) + .Case("ra", 31) + .Case("t0", 8) + .Case("t1", 9) + .Case("t2", 10) + .Case("t3", 11) + .Case("t4", 12) + .Case("t5", 13) + .Case("t6", 14) + .Case("t7", 15) + .Case("t8", 24) + .Case("t9", 25) + .Default(-1); + + if (!(isABI_N32() || isABI_N64())) + return CC; + + if (12 <= CC && CC <= 15) { + // Name is one of t4-t7 + AsmToken RegTok = getLexer().peekTok(); + SMRange RegRange = RegTok.getLocRange(); + + StringRef FixedName = StringSwitch(Name) + .Case("t4", "t0") + .Case("t5", "t1") + .Case("t6", "t2") + .Case("t7", "t3") + .Default(""); + assert(FixedName != "" && "Register name is not one of t4-t7."); + + printWarningWithFixIt("register names $t4-$t7 are only available in O32.", + "Did you mean $" + FixedName + "?", RegRange); + } + + // Although SGI documentation just cuts out t0-t3 for n32/n64, + // GNU pushes the values of t0-t3 to override the o32/o64 values for t4-t7 + // We are supporting both cases, so for t0-t3 we'll just push them to t4-t7. + if (8 <= CC && CC <= 11) + CC += 4; + + if (CC == -1) + CC = StringSwitch(Name) + .Case("a4", 8) + .Case("a5", 9) + .Case("a6", 10) + .Case("a7", 11) + .Case("kt0", 26) + .Case("kt1", 27) + .Default(-1); + + return CC; +} + +int MaxisAsmParser::matchHWRegsRegisterName(StringRef Name) { + int CC; + + CC = StringSwitch(Name) + .Case("hwr_cpunum", 0) + .Case("hwr_synci_step", 1) + .Case("hwr_cc", 2) + .Case("hwr_ccres", 3) + .Case("hwr_ulr", 29) + .Default(-1); + + return CC; +} + +int MaxisAsmParser::matchFPURegisterName(StringRef Name) { + if (Name[0] == 'f') { + StringRef NumString = Name.substr(1); + unsigned IntVal; + if (NumString.getAsInteger(10, IntVal)) + return -1; // This is not an integer. + if (IntVal > 31) // Maximum index for fpu register. + return -1; + return IntVal; + } + return -1; +} + +int MaxisAsmParser::matchFCCRegisterName(StringRef Name) { + if (Name.startswith("fcc")) { + StringRef NumString = Name.substr(3); + unsigned IntVal; + if (NumString.getAsInteger(10, IntVal)) + return -1; // This is not an integer. + if (IntVal > 7) // There are only 8 fcc registers. + return -1; + return IntVal; + } + return -1; +} + +int MaxisAsmParser::matchACRegisterName(StringRef Name) { + if (Name.startswith("ac")) { + StringRef NumString = Name.substr(2); + unsigned IntVal; + if (NumString.getAsInteger(10, IntVal)) + return -1; // This is not an integer. + if (IntVal > 3) // There are only 3 acc registers. + return -1; + return IntVal; + } + return -1; +} + +int MaxisAsmParser::matchMSA128RegisterName(StringRef Name) { + unsigned IntVal; + + if (Name.front() != 'w' || Name.drop_front(1).getAsInteger(10, IntVal)) + return -1; + + if (IntVal > 31) + return -1; + + return IntVal; +} + +int MaxisAsmParser::matchMSA128CtrlRegisterName(StringRef Name) { + int CC; + + CC = StringSwitch(Name) + .Case("msair", 0) + .Case("msacsr", 1) + .Case("msaaccess", 2) + .Case("msasave", 3) + .Case("msamodify", 4) + .Case("msarequest", 5) + .Case("msamap", 6) + .Case("msaunmap", 7) + .Default(-1); + + return CC; +} + +bool MaxisAsmParser::canUseATReg() { + return AssemblerOptions.back()->getATRegIndex() != 0; +} + +unsigned MaxisAsmParser::getATReg(SMLoc Loc) { + unsigned ATIndex = AssemblerOptions.back()->getATRegIndex(); + if (ATIndex == 0) { + reportParseError(Loc, + "pseudo-instruction requires $at, which is not available"); + return 0; + } + unsigned AT = getReg( + (isGP64bit()) ? Maxis::GPR64RegClassID : Maxis::GPR32RegClassID, ATIndex); + return AT; +} + +unsigned MaxisAsmParser::getReg(int RC, int RegNo) { + return *(getContext().getRegisterInfo()->getRegClass(RC).begin() + RegNo); +} + +bool MaxisAsmParser::parseOperand(OperandVector &Operands, StringRef Mnemonic) { + MCAsmParser &Parser = getParser(); + DEBUG(dbgs() << "parseOperand\n"); + + // Check if the current operand has a custom associated parser, if so, try to + // custom parse the operand, or fallback to the general approach. + OperandMatchResultTy ResTy = MatchOperandParserImpl(Operands, Mnemonic); + if (ResTy == MatchOperand_Success) + return false; + // If there wasn't a custom match, try the generic matcher below. Otherwise, + // there was a match, but an error occurred, in which case, just return that + // the operand parsing failed. + if (ResTy == MatchOperand_ParseFail) + return true; + + DEBUG(dbgs() << ".. Generic Parser\n"); + + switch (getLexer().getKind()) { + case AsmToken::Dollar: { + // Parse the register. + SMLoc S = Parser.getTok().getLoc(); + + // Almost all registers have been parsed by custom parsers. There is only + // one exception to this. $zero (and it's alias $0) will reach this point + // for div, divu, and similar instructions because it is not an operand + // to the instruction definition but an explicit register. Special case + // this situation for now. + if (parseAnyRegister(Operands) != MatchOperand_NoMatch) + return false; + + // Maybe it is a symbol reference. + StringRef Identifier; + if (Parser.parseIdentifier(Identifier)) + return true; + + SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + MCSymbol *Sym = getContext().getOrCreateSymbol("$" + Identifier); + // Otherwise create a symbol reference. + const MCExpr *Res = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + + Operands.push_back(MaxisOperand::CreateImm(Res, S, E, *this)); + return false; + } + default: { + DEBUG(dbgs() << ".. generic integer expression\n"); + + const MCExpr *Expr; + SMLoc S = Parser.getTok().getLoc(); // Start location of the operand. + if (getParser().parseExpression(Expr)) + return true; + + SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + + Operands.push_back(MaxisOperand::CreateImm(Expr, S, E, *this)); + return false; + } + } // switch(getLexer().getKind()) + return true; +} + +bool MaxisAsmParser::isEvaluated(const MCExpr *Expr) { + switch (Expr->getKind()) { + case MCExpr::Constant: + return true; + case MCExpr::SymbolRef: + return (cast(Expr)->getKind() != MCSymbolRefExpr::VK_None); + case MCExpr::Binary: { + const MCBinaryExpr *BE = cast(Expr); + if (!isEvaluated(BE->getLHS())) + return false; + return isEvaluated(BE->getRHS()); + } + case MCExpr::Unary: + return isEvaluated(cast(Expr)->getSubExpr()); + case MCExpr::Target: + return true; + } + return false; +} + +bool MaxisAsmParser::ParseRegister(unsigned &RegNo, SMLoc &StartLoc, + SMLoc &EndLoc) { + SmallVector, 1> Operands; + OperandMatchResultTy ResTy = parseAnyRegister(Operands); + if (ResTy == MatchOperand_Success) { + assert(Operands.size() == 1); + MaxisOperand &Operand = static_cast(*Operands.front()); + StartLoc = Operand.getStartLoc(); + EndLoc = Operand.getEndLoc(); + + // AFAIK, we only support numeric registers and named GPR's in CFI + // directives. + // Don't worry about eating tokens before failing. Using an unrecognised + // register is a parse error. + if (Operand.isGPRAsmReg()) { + // Resolve to GPR32 or GPR64 appropriately. + RegNo = isGP64bit() ? Operand.getGPR64Reg() : Operand.getGPR32Reg(); + } + + return (RegNo == (unsigned)-1); + } + + assert(Operands.size() == 0); + return (RegNo == (unsigned)-1); +} + +bool MaxisAsmParser::parseMemOffset(const MCExpr *&Res, bool isParenExpr) { + SMLoc S; + + if (isParenExpr) + return getParser().parseParenExprOfDepth(0, Res, S); + return getParser().parseExpression(Res); +} + +OperandMatchResultTy +MaxisAsmParser::parseMemOperand(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + DEBUG(dbgs() << "parseMemOperand\n"); + const MCExpr *IdVal = nullptr; + SMLoc S; + bool isParenExpr = false; + OperandMatchResultTy Res = MatchOperand_NoMatch; + // First operand is the offset. + S = Parser.getTok().getLoc(); + + if (getLexer().getKind() == AsmToken::LParen) { + Parser.Lex(); + isParenExpr = true; + } + + if (getLexer().getKind() != AsmToken::Dollar) { + if (parseMemOffset(IdVal, isParenExpr)) + return MatchOperand_ParseFail; + + const AsmToken &Tok = Parser.getTok(); // Get the next token. + if (Tok.isNot(AsmToken::LParen)) { + MaxisOperand &Mnemonic = static_cast(*Operands[0]); + if (Mnemonic.getToken() == "la" || Mnemonic.getToken() == "dla") { + SMLoc E = + SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + Operands.push_back(MaxisOperand::CreateImm(IdVal, S, E, *this)); + return MatchOperand_Success; + } + if (Tok.is(AsmToken::EndOfStatement)) { + SMLoc E = + SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + + // Zero register assumed, add a memory operand with ZERO as its base. + // "Base" will be managed by k_Memory. + auto Base = MaxisOperand::createGPRReg( + 0, "0", getContext().getRegisterInfo(), S, E, *this); + Operands.push_back( + MaxisOperand::CreateMem(std::move(Base), IdVal, S, E, *this)); + return MatchOperand_Success; + } + MCBinaryExpr::Opcode Opcode; + // GAS and LLVM treat comparison operators different. GAS will generate -1 + // or 0, while LLVM will generate 0 or 1. Since a comparsion operator is + // highly unlikely to be found in a memory offset expression, we don't + // handle them. + switch (Tok.getKind()) { + case AsmToken::Plus: + Opcode = MCBinaryExpr::Add; + Parser.Lex(); + break; + case AsmToken::Minus: + Opcode = MCBinaryExpr::Sub; + Parser.Lex(); + break; + case AsmToken::Star: + Opcode = MCBinaryExpr::Mul; + Parser.Lex(); + break; + case AsmToken::Pipe: + Opcode = MCBinaryExpr::Or; + Parser.Lex(); + break; + case AsmToken::Amp: + Opcode = MCBinaryExpr::And; + Parser.Lex(); + break; + case AsmToken::LessLess: + Opcode = MCBinaryExpr::Shl; + Parser.Lex(); + break; + case AsmToken::GreaterGreater: + Opcode = MCBinaryExpr::LShr; + Parser.Lex(); + break; + case AsmToken::Caret: + Opcode = MCBinaryExpr::Xor; + Parser.Lex(); + break; + case AsmToken::Slash: + Opcode = MCBinaryExpr::Div; + Parser.Lex(); + break; + case AsmToken::Percent: + Opcode = MCBinaryExpr::Mod; + Parser.Lex(); + break; + default: + Error(Parser.getTok().getLoc(), "'(' or expression expected"); + return MatchOperand_ParseFail; + } + const MCExpr * NextExpr; + if (getParser().parseExpression(NextExpr)) + return MatchOperand_ParseFail; + IdVal = MCBinaryExpr::create(Opcode, IdVal, NextExpr, getContext()); + } + + Parser.Lex(); // Eat the '(' token. + } + + Res = parseAnyRegister(Operands); + if (Res != MatchOperand_Success) + return Res; + + if (Parser.getTok().isNot(AsmToken::RParen)) { + Error(Parser.getTok().getLoc(), "')' expected"); + return MatchOperand_ParseFail; + } + + SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + + Parser.Lex(); // Eat the ')' token. + + if (!IdVal) + IdVal = MCConstantExpr::create(0, getContext()); + + // Replace the register operand with the memory operand. + std::unique_ptr op( + static_cast(Operands.back().release())); + // Remove the register from the operands. + // "op" will be managed by k_Memory. + Operands.pop_back(); + // Add the memory operand. + if (const MCBinaryExpr *BE = dyn_cast(IdVal)) { + int64_t Imm; + if (IdVal->evaluateAsAbsolute(Imm)) + IdVal = MCConstantExpr::create(Imm, getContext()); + else if (BE->getLHS()->getKind() != MCExpr::SymbolRef) + IdVal = MCBinaryExpr::create(BE->getOpcode(), BE->getRHS(), BE->getLHS(), + getContext()); + } + + Operands.push_back(MaxisOperand::CreateMem(std::move(op), IdVal, S, E, *this)); + return MatchOperand_Success; +} + +bool MaxisAsmParser::searchSymbolAlias(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + MCSymbol *Sym = getContext().lookupSymbol(Parser.getTok().getIdentifier()); + if (Sym) { + SMLoc S = Parser.getTok().getLoc(); + const MCExpr *Expr; + if (Sym->isVariable()) + Expr = Sym->getVariableValue(); + else + return false; + if (Expr->getKind() == MCExpr::SymbolRef) { + const MCSymbolRefExpr *Ref = static_cast(Expr); + StringRef DefSymbol = Ref->getSymbol().getName(); + if (DefSymbol.startswith("$")) { + OperandMatchResultTy ResTy = + matchAnyRegisterNameWithoutDollar(Operands, DefSymbol.substr(1), S); + if (ResTy == MatchOperand_Success) { + Parser.Lex(); + return true; + } else if (ResTy == MatchOperand_ParseFail) + llvm_unreachable("Should never ParseFail"); + return false; + } + } + } + return false; +} + +OperandMatchResultTy +MaxisAsmParser::matchAnyRegisterNameWithoutDollar(OperandVector &Operands, + StringRef Identifier, + SMLoc S) { + int Index = matchCPURegisterName(Identifier); + if (Index != -1) { + Operands.push_back(MaxisOperand::createGPRReg( + Index, Identifier, getContext().getRegisterInfo(), S, + getLexer().getLoc(), *this)); + return MatchOperand_Success; + } + + Index = matchHWRegsRegisterName(Identifier); + if (Index != -1) { + Operands.push_back(MaxisOperand::createHWRegsReg( + Index, Identifier, getContext().getRegisterInfo(), S, + getLexer().getLoc(), *this)); + return MatchOperand_Success; + } + + Index = matchFPURegisterName(Identifier); + if (Index != -1) { + Operands.push_back(MaxisOperand::createFGRReg( + Index, Identifier, getContext().getRegisterInfo(), S, + getLexer().getLoc(), *this)); + return MatchOperand_Success; + } + + Index = matchFCCRegisterName(Identifier); + if (Index != -1) { + Operands.push_back(MaxisOperand::createFCCReg( + Index, Identifier, getContext().getRegisterInfo(), S, + getLexer().getLoc(), *this)); + return MatchOperand_Success; + } + + Index = matchACRegisterName(Identifier); + if (Index != -1) { + Operands.push_back(MaxisOperand::createACCReg( + Index, Identifier, getContext().getRegisterInfo(), S, + getLexer().getLoc(), *this)); + return MatchOperand_Success; + } + + Index = matchMSA128RegisterName(Identifier); + if (Index != -1) { + Operands.push_back(MaxisOperand::createMSA128Reg( + Index, Identifier, getContext().getRegisterInfo(), S, + getLexer().getLoc(), *this)); + return MatchOperand_Success; + } + + Index = matchMSA128CtrlRegisterName(Identifier); + if (Index != -1) { + Operands.push_back(MaxisOperand::createMSACtrlReg( + Index, Identifier, getContext().getRegisterInfo(), S, + getLexer().getLoc(), *this)); + return MatchOperand_Success; + } + + return MatchOperand_NoMatch; +} + +OperandMatchResultTy +MaxisAsmParser::matchAnyRegisterWithoutDollar(OperandVector &Operands, SMLoc S) { + MCAsmParser &Parser = getParser(); + auto Token = Parser.getLexer().peekTok(false); + + if (Token.is(AsmToken::Identifier)) { + DEBUG(dbgs() << ".. identifier\n"); + StringRef Identifier = Token.getIdentifier(); + OperandMatchResultTy ResTy = + matchAnyRegisterNameWithoutDollar(Operands, Identifier, S); + return ResTy; + } else if (Token.is(AsmToken::Integer)) { + DEBUG(dbgs() << ".. integer\n"); + Operands.push_back(MaxisOperand::createNumericReg( + Token.getIntVal(), Token.getString(), getContext().getRegisterInfo(), S, + Token.getLoc(), *this)); + return MatchOperand_Success; + } + + DEBUG(dbgs() << Parser.getTok().getKind() << "\n"); + + return MatchOperand_NoMatch; +} + +OperandMatchResultTy +MaxisAsmParser::parseAnyRegister(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + DEBUG(dbgs() << "parseAnyRegister\n"); + + auto Token = Parser.getTok(); + + SMLoc S = Token.getLoc(); + + if (Token.isNot(AsmToken::Dollar)) { + DEBUG(dbgs() << ".. !$ -> try sym aliasing\n"); + if (Token.is(AsmToken::Identifier)) { + if (searchSymbolAlias(Operands)) + return MatchOperand_Success; + } + DEBUG(dbgs() << ".. !symalias -> NoMatch\n"); + return MatchOperand_NoMatch; + } + DEBUG(dbgs() << ".. $\n"); + + OperandMatchResultTy ResTy = matchAnyRegisterWithoutDollar(Operands, S); + if (ResTy == MatchOperand_Success) { + Parser.Lex(); // $ + Parser.Lex(); // identifier + } + return ResTy; +} + +OperandMatchResultTy +MaxisAsmParser::parseJumpTarget(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + DEBUG(dbgs() << "parseJumpTarget\n"); + + SMLoc S = getLexer().getLoc(); + + // Registers are a valid target and have priority over symbols. + OperandMatchResultTy ResTy = parseAnyRegister(Operands); + if (ResTy != MatchOperand_NoMatch) + return ResTy; + + // Integers and expressions are acceptable + const MCExpr *Expr = nullptr; + if (Parser.parseExpression(Expr)) { + // We have no way of knowing if a symbol was consumed so we must ParseFail + return MatchOperand_ParseFail; + } + Operands.push_back( + MaxisOperand::CreateImm(Expr, S, getLexer().getLoc(), *this)); + return MatchOperand_Success; +} + +OperandMatchResultTy +MaxisAsmParser::parseInvNum(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + const MCExpr *IdVal; + // If the first token is '$' we may have register operand. We have to reject + // cases where it is not a register. Complicating the matter is that + // register names are not reserved across all ABIs. + // Peek past the dollar to see if it's a register name for this ABI. + SMLoc S = Parser.getTok().getLoc(); + if (Parser.getTok().is(AsmToken::Dollar)) { + return matchCPURegisterName(Parser.getLexer().peekTok().getString()) == -1 + ? MatchOperand_ParseFail + : MatchOperand_NoMatch; + } + if (getParser().parseExpression(IdVal)) + return MatchOperand_ParseFail; + const MCConstantExpr *MCE = dyn_cast(IdVal); + if (!MCE) + return MatchOperand_NoMatch; + int64_t Val = MCE->getValue(); + SMLoc E = SMLoc::getFromPointer(Parser.getTok().getLoc().getPointer() - 1); + Operands.push_back(MaxisOperand::CreateImm( + MCConstantExpr::create(0 - Val, getContext()), S, E, *this)); + return MatchOperand_Success; +} + +OperandMatchResultTy +MaxisAsmParser::parseRegisterList(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + SmallVector Regs; + unsigned RegNo; + unsigned PrevReg = Maxis::NoRegister; + bool RegRange = false; + SmallVector, 8> TmpOperands; + + if (Parser.getTok().isNot(AsmToken::Dollar)) + return MatchOperand_ParseFail; + + SMLoc S = Parser.getTok().getLoc(); + while (parseAnyRegister(TmpOperands) == MatchOperand_Success) { + SMLoc E = getLexer().getLoc(); + MaxisOperand &Reg = static_cast(*TmpOperands.back()); + RegNo = isGP64bit() ? Reg.getGPR64Reg() : Reg.getGPR32Reg(); + if (RegRange) { + // Remove last register operand because registers from register range + // should be inserted first. + if ((isGP64bit() && RegNo == Maxis::RA_64) || + (!isGP64bit() && RegNo == Maxis::RA)) { + Regs.push_back(RegNo); + } else { + unsigned TmpReg = PrevReg + 1; + while (TmpReg <= RegNo) { + if ((((TmpReg < Maxis::S0) || (TmpReg > Maxis::S7)) && !isGP64bit()) || + (((TmpReg < Maxis::S0_64) || (TmpReg > Maxis::S7_64)) && + isGP64bit())) { + Error(E, "invalid register operand"); + return MatchOperand_ParseFail; + } + + PrevReg = TmpReg; + Regs.push_back(TmpReg++); + } + } + + RegRange = false; + } else { + if ((PrevReg == Maxis::NoRegister) && + ((isGP64bit() && (RegNo != Maxis::S0_64) && (RegNo != Maxis::RA_64)) || + (!isGP64bit() && (RegNo != Maxis::S0) && (RegNo != Maxis::RA)))) { + Error(E, "$16 or $31 expected"); + return MatchOperand_ParseFail; + } else if (!(((RegNo == Maxis::FP || RegNo == Maxis::RA || + (RegNo >= Maxis::S0 && RegNo <= Maxis::S7)) && + !isGP64bit()) || + ((RegNo == Maxis::FP_64 || RegNo == Maxis::RA_64 || + (RegNo >= Maxis::S0_64 && RegNo <= Maxis::S7_64)) && + isGP64bit()))) { + Error(E, "invalid register operand"); + return MatchOperand_ParseFail; + } else if ((PrevReg != Maxis::NoRegister) && (RegNo != PrevReg + 1) && + ((RegNo != Maxis::FP && RegNo != Maxis::RA && !isGP64bit()) || + (RegNo != Maxis::FP_64 && RegNo != Maxis::RA_64 && + isGP64bit()))) { + Error(E, "consecutive register numbers expected"); + return MatchOperand_ParseFail; + } + + Regs.push_back(RegNo); + } + + if (Parser.getTok().is(AsmToken::Minus)) + RegRange = true; + + if (!Parser.getTok().isNot(AsmToken::Minus) && + !Parser.getTok().isNot(AsmToken::Comma)) { + Error(E, "',' or '-' expected"); + return MatchOperand_ParseFail; + } + + Lex(); // Consume comma or minus + if (Parser.getTok().isNot(AsmToken::Dollar)) + break; + + PrevReg = RegNo; + } + + SMLoc E = Parser.getTok().getLoc(); + Operands.push_back(MaxisOperand::CreateRegList(Regs, S, E, *this)); + parseMemOperand(Operands); + return MatchOperand_Success; +} + +OperandMatchResultTy +MaxisAsmParser::parseRegisterPair(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + + SMLoc S = Parser.getTok().getLoc(); + if (parseAnyRegister(Operands) != MatchOperand_Success) + return MatchOperand_ParseFail; + + SMLoc E = Parser.getTok().getLoc(); + MaxisOperand Op = static_cast(*Operands.back()); + + Operands.pop_back(); + Operands.push_back(MaxisOperand::CreateRegPair(Op, S, E, *this)); + return MatchOperand_Success; +} + +OperandMatchResultTy +MaxisAsmParser::parseMovePRegPair(OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + SmallVector, 8> TmpOperands; + SmallVector Regs; + + if (Parser.getTok().isNot(AsmToken::Dollar)) + return MatchOperand_ParseFail; + + SMLoc S = Parser.getTok().getLoc(); + + if (parseAnyRegister(TmpOperands) != MatchOperand_Success) + return MatchOperand_ParseFail; + + MaxisOperand *Reg = &static_cast(*TmpOperands.back()); + unsigned RegNo = isGP64bit() ? Reg->getGPR64Reg() : Reg->getGPR32Reg(); + Regs.push_back(RegNo); + + SMLoc E = Parser.getTok().getLoc(); + if (Parser.getTok().isNot(AsmToken::Comma)) { + Error(E, "',' expected"); + return MatchOperand_ParseFail; + } + + // Remove comma. + Parser.Lex(); + + if (parseAnyRegister(TmpOperands) != MatchOperand_Success) + return MatchOperand_ParseFail; + + Reg = &static_cast(*TmpOperands.back()); + RegNo = isGP64bit() ? Reg->getGPR64Reg() : Reg->getGPR32Reg(); + Regs.push_back(RegNo); + + Operands.push_back(MaxisOperand::CreateRegList(Regs, S, E, *this)); + + return MatchOperand_Success; +} + +/// Sometimes (i.e. load/stores) the operand may be followed immediately by +/// either this. +/// ::= '(', register, ')' +/// handle it before we iterate so we don't get tripped up by the lack of +/// a comma. +bool MaxisAsmParser::parseParenSuffix(StringRef Name, OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + if (getLexer().is(AsmToken::LParen)) { + Operands.push_back( + MaxisOperand::CreateToken("(", getLexer().getLoc(), *this)); + Parser.Lex(); + if (parseOperand(Operands, Name)) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, "unexpected token in argument list"); + } + if (Parser.getTok().isNot(AsmToken::RParen)) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, "unexpected token, expected ')'"); + } + Operands.push_back( + MaxisOperand::CreateToken(")", getLexer().getLoc(), *this)); + Parser.Lex(); + } + return false; +} + +/// Sometimes (i.e. in MSA) the operand may be followed immediately by +/// either one of these. +/// ::= '[', register, ']' +/// ::= '[', integer, ']' +/// handle it before we iterate so we don't get tripped up by the lack of +/// a comma. +bool MaxisAsmParser::parseBracketSuffix(StringRef Name, + OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + if (getLexer().is(AsmToken::LBrac)) { + Operands.push_back( + MaxisOperand::CreateToken("[", getLexer().getLoc(), *this)); + Parser.Lex(); + if (parseOperand(Operands, Name)) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, "unexpected token in argument list"); + } + if (Parser.getTok().isNot(AsmToken::RBrac)) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, "unexpected token, expected ']'"); + } + Operands.push_back( + MaxisOperand::CreateToken("]", getLexer().getLoc(), *this)); + Parser.Lex(); + } + return false; +} + +bool MaxisAsmParser::ParseInstruction(ParseInstructionInfo &Info, StringRef Name, + SMLoc NameLoc, OperandVector &Operands) { + MCAsmParser &Parser = getParser(); + DEBUG(dbgs() << "ParseInstruction\n"); + + // We have reached first instruction, module directive are now forbidden. + getTargetStreamer().forbidModuleDirective(); + + // Check if we have valid mnemonic + if (!mnemonicIsValid(Name, 0)) { + return Error(NameLoc, "unknown instruction"); + } + // First operand in MCInst is instruction mnemonic. + Operands.push_back(MaxisOperand::CreateToken(Name, NameLoc, *this)); + + // Read the remaining operands. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + // Read the first operand. + if (parseOperand(Operands, Name)) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, "unexpected token in argument list"); + } + if (getLexer().is(AsmToken::LBrac) && parseBracketSuffix(Name, Operands)) + return true; + // AFAIK, parenthesis suffixes are never on the first operand + + while (getLexer().is(AsmToken::Comma)) { + Parser.Lex(); // Eat the comma. + // Parse and remember the operand. + if (parseOperand(Operands, Name)) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, "unexpected token in argument list"); + } + // Parse bracket and parenthesis suffixes before we iterate + if (getLexer().is(AsmToken::LBrac)) { + if (parseBracketSuffix(Name, Operands)) + return true; + } else if (getLexer().is(AsmToken::LParen) && + parseParenSuffix(Name, Operands)) + return true; + } + } + if (getLexer().isNot(AsmToken::EndOfStatement)) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, "unexpected token in argument list"); + } + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +// FIXME: Given that these have the same name, these should both be +// consistent on affecting the Parser. +bool MaxisAsmParser::reportParseError(Twine ErrorMsg) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, ErrorMsg); +} + +bool MaxisAsmParser::reportParseError(SMLoc Loc, Twine ErrorMsg) { + return Error(Loc, ErrorMsg); +} + +bool MaxisAsmParser::parseSetNoAtDirective() { + MCAsmParser &Parser = getParser(); + // Line should look like: ".set noat". + + // Set the $at register to $0. + AssemblerOptions.back()->setATRegIndex(0); + + Parser.Lex(); // Eat "noat". + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + getTargetStreamer().emitDirectiveSetNoAt(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetAtDirective() { + // Line can be: ".set at", which sets $at to $1 + // or ".set at=$reg", which sets $at to $reg. + MCAsmParser &Parser = getParser(); + Parser.Lex(); // Eat "at". + + if (getLexer().is(AsmToken::EndOfStatement)) { + // No register was specified, so we set $at to $1. + AssemblerOptions.back()->setATRegIndex(1); + + getTargetStreamer().emitDirectiveSetAt(); + Parser.Lex(); // Consume the EndOfStatement. + return false; + } + + if (getLexer().isNot(AsmToken::Equal)) { + reportParseError("unexpected token, expected equals sign"); + return false; + } + Parser.Lex(); // Eat "=". + + if (getLexer().isNot(AsmToken::Dollar)) { + if (getLexer().is(AsmToken::EndOfStatement)) { + reportParseError("no register specified"); + return false; + } else { + reportParseError("unexpected token, expected dollar sign '$'"); + return false; + } + } + Parser.Lex(); // Eat "$". + + // Find out what "reg" is. + unsigned AtRegNo; + const AsmToken &Reg = Parser.getTok(); + if (Reg.is(AsmToken::Identifier)) { + AtRegNo = matchCPURegisterName(Reg.getIdentifier()); + } else if (Reg.is(AsmToken::Integer)) { + AtRegNo = Reg.getIntVal(); + } else { + reportParseError("unexpected token, expected identifier or integer"); + return false; + } + + // Check if $reg is a valid register. If it is, set $at to $reg. + if (!AssemblerOptions.back()->setATRegIndex(AtRegNo)) { + reportParseError("invalid register"); + return false; + } + Parser.Lex(); // Eat "reg". + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + getTargetStreamer().emitDirectiveSetAtWithArg(AtRegNo); + + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetReorderDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + AssemblerOptions.back()->setReorder(); + getTargetStreamer().emitDirectiveSetReorder(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetNoReorderDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + AssemblerOptions.back()->setNoReorder(); + getTargetStreamer().emitDirectiveSetNoReorder(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetMacroDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + AssemblerOptions.back()->setMacro(); + getTargetStreamer().emitDirectiveSetMacro(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetNoMacroDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + if (AssemblerOptions.back()->isReorder()) { + reportParseError("`noreorder' must be set before `nomacro'"); + return false; + } + AssemblerOptions.back()->setNoMacro(); + getTargetStreamer().emitDirectiveSetNoMacro(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetMsaDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) + return reportParseError("unexpected token, expected end of statement"); + + setFeatureBits(Maxis::FeatureMSA, "msa"); + getTargetStreamer().emitDirectiveSetMsa(); + return false; +} + +bool MaxisAsmParser::parseSetNoMsaDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) + return reportParseError("unexpected token, expected end of statement"); + + clearFeatureBits(Maxis::FeatureMSA, "msa"); + getTargetStreamer().emitDirectiveSetNoMsa(); + return false; +} + +bool MaxisAsmParser::parseSetNoDspDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); // Eat "nodsp". + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + clearFeatureBits(Maxis::FeatureDSP, "dsp"); + getTargetStreamer().emitDirectiveSetNoDsp(); + return false; +} + +bool MaxisAsmParser::parseSetMaxis16Directive() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); // Eat "maxis16". + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + setFeatureBits(Maxis::FeatureMaxis16, "maxis16"); + getTargetStreamer().emitDirectiveSetMaxis16(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetNoMaxis16Directive() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); // Eat "nomaxis16". + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + clearFeatureBits(Maxis::FeatureMaxis16, "maxis16"); + getTargetStreamer().emitDirectiveSetNoMaxis16(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetFpDirective() { + MCAsmParser &Parser = getParser(); + MaxisABIFlagsSection::FpABIKind FpAbiVal; + // Line can be: .set fp=32 + // .set fp=xx + // .set fp=64 + Parser.Lex(); // Eat fp token + AsmToken Tok = Parser.getTok(); + if (Tok.isNot(AsmToken::Equal)) { + reportParseError("unexpected token, expected equals sign '='"); + return false; + } + Parser.Lex(); // Eat '=' token. + Tok = Parser.getTok(); + + if (!parseFpABIValue(FpAbiVal, ".set")) + return false; + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + getTargetStreamer().emitDirectiveSetFp(FpAbiVal); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetOddSPRegDirective() { + MCAsmParser &Parser = getParser(); + + Parser.Lex(); // Eat "oddspreg". + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + clearFeatureBits(Maxis::FeatureNoOddSPReg, "nooddspreg"); + getTargetStreamer().emitDirectiveSetOddSPReg(); + return false; +} + +bool MaxisAsmParser::parseSetNoOddSPRegDirective() { + MCAsmParser &Parser = getParser(); + + Parser.Lex(); // Eat "nooddspreg". + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + setFeatureBits(Maxis::FeatureNoOddSPReg, "nooddspreg"); + getTargetStreamer().emitDirectiveSetNoOddSPReg(); + return false; +} + +bool MaxisAsmParser::parseSetMtDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); // Eat "mt". + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + setFeatureBits(Maxis::FeatureMT, "mt"); + getTargetStreamer().emitDirectiveSetMt(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetNoMtDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); // Eat "nomt". + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + clearFeatureBits(Maxis::FeatureMT, "mt"); + + getTargetStreamer().emitDirectiveSetNoMt(); + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseSetPopDirective() { + MCAsmParser &Parser = getParser(); + SMLoc Loc = getLexer().getLoc(); + + Parser.Lex(); + if (getLexer().isNot(AsmToken::EndOfStatement)) + return reportParseError("unexpected token, expected end of statement"); + + // Always keep an element on the options "stack" to prevent the user + // from changing the initial options. This is how we remember them. + if (AssemblerOptions.size() == 2) + return reportParseError(Loc, ".set pop with no .set push"); + + MCSubtargetInfo &STI = copySTI(); + AssemblerOptions.pop_back(); + setAvailableFeatures( + ComputeAvailableFeatures(AssemblerOptions.back()->getFeatures())); + STI.setFeatureBits(AssemblerOptions.back()->getFeatures()); + + getTargetStreamer().emitDirectiveSetPop(); + return false; +} + +bool MaxisAsmParser::parseSetPushDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + if (getLexer().isNot(AsmToken::EndOfStatement)) + return reportParseError("unexpected token, expected end of statement"); + + // Create a copy of the current assembler options environment and push it. + AssemblerOptions.push_back( + llvm::make_unique(AssemblerOptions.back().get())); + + getTargetStreamer().emitDirectiveSetPush(); + return false; +} + +bool MaxisAsmParser::parseSetSoftFloatDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + if (getLexer().isNot(AsmToken::EndOfStatement)) + return reportParseError("unexpected token, expected end of statement"); + + setFeatureBits(Maxis::FeatureSoftFloat, "soft-float"); + getTargetStreamer().emitDirectiveSetSoftFloat(); + return false; +} + +bool MaxisAsmParser::parseSetHardFloatDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + if (getLexer().isNot(AsmToken::EndOfStatement)) + return reportParseError("unexpected token, expected end of statement"); + + clearFeatureBits(Maxis::FeatureSoftFloat, "soft-float"); + getTargetStreamer().emitDirectiveSetHardFloat(); + return false; +} + +bool MaxisAsmParser::parseSetAssignment() { + StringRef Name; + const MCExpr *Value; + MCAsmParser &Parser = getParser(); + + if (Parser.parseIdentifier(Name)) + reportParseError("expected identifier after .set"); + + if (getLexer().isNot(AsmToken::Comma)) + return reportParseError("unexpected token, expected comma"); + Lex(); // Eat comma + + if (Parser.parseExpression(Value)) + return reportParseError("expected valid expression after comma"); + + MCSymbol *Sym = getContext().getOrCreateSymbol(Name); + Sym->setVariableValue(Value); + + return false; +} + +bool MaxisAsmParser::parseSetMaxis0Directive() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + if (getLexer().isNot(AsmToken::EndOfStatement)) + return reportParseError("unexpected token, expected end of statement"); + + // Reset assembler options to their initial values. + MCSubtargetInfo &STI = copySTI(); + setAvailableFeatures( + ComputeAvailableFeatures(AssemblerOptions.front()->getFeatures())); + STI.setFeatureBits(AssemblerOptions.front()->getFeatures()); + AssemblerOptions.back()->setFeatures(AssemblerOptions.front()->getFeatures()); + + getTargetStreamer().emitDirectiveSetMaxis0(); + return false; +} + +bool MaxisAsmParser::parseSetArchDirective() { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + if (getLexer().isNot(AsmToken::Equal)) + return reportParseError("unexpected token, expected equals sign"); + + Parser.Lex(); + StringRef Arch; + if (Parser.parseIdentifier(Arch)) + return reportParseError("expected arch identifier"); + + StringRef ArchFeatureName = + StringSwitch(Arch) + .Case("maxis1", "maxis1") + .Case("maxis2", "maxis2") + .Case("maxis3", "maxis3") + .Case("maxis4", "maxis4") + .Case("maxis5", "maxis5") + .Case("maxis32", "maxis32") + .Case("maxis32r2", "maxis32r2") + .Case("maxis32r3", "maxis32r3") + .Case("maxis32r5", "maxis32r5") + .Case("maxis32r6", "maxis32r6") + .Case("maxis64", "maxis64") + .Case("maxis64r2", "maxis64r2") + .Case("maxis64r3", "maxis64r3") + .Case("maxis64r5", "maxis64r5") + .Case("maxis64r6", "maxis64r6") + .Case("octeon", "cnmaxis") + .Case("r4000", "maxis3") // This is an implementation of Maxis3. + .Default(""); + + if (ArchFeatureName.empty()) + return reportParseError("unsupported architecture"); + + if (ArchFeatureName == "maxis64r6" && inMicroMaxisMode()) + return reportParseError("maxis64r6 does not support microMAXIS"); + + selectArch(ArchFeatureName); + getTargetStreamer().emitDirectiveSetArch(Arch); + return false; +} + +bool MaxisAsmParser::parseSetFeature(uint64_t Feature) { + MCAsmParser &Parser = getParser(); + Parser.Lex(); + if (getLexer().isNot(AsmToken::EndOfStatement)) + return reportParseError("unexpected token, expected end of statement"); + + switch (Feature) { + default: + llvm_unreachable("Unimplemented feature"); + case Maxis::FeatureDSP: + setFeatureBits(Maxis::FeatureDSP, "dsp"); + getTargetStreamer().emitDirectiveSetDsp(); + break; + case Maxis::FeatureDSPR2: + setFeatureBits(Maxis::FeatureDSPR2, "dspr2"); + getTargetStreamer().emitDirectiveSetDspr2(); + break; + case Maxis::FeatureMicroMaxis: + setFeatureBits(Maxis::FeatureMicroMaxis, "micromaxis"); + getTargetStreamer().emitDirectiveSetMicroMaxis(); + break; + case Maxis::FeatureMaxis1: + selectArch("maxis1"); + getTargetStreamer().emitDirectiveSetMaxis1(); + break; + case Maxis::FeatureMaxis2: + selectArch("maxis2"); + getTargetStreamer().emitDirectiveSetMaxis2(); + break; + case Maxis::FeatureMaxis3: + selectArch("maxis3"); + getTargetStreamer().emitDirectiveSetMaxis3(); + break; + case Maxis::FeatureMaxis4: + selectArch("maxis4"); + getTargetStreamer().emitDirectiveSetMaxis4(); + break; + case Maxis::FeatureMaxis5: + selectArch("maxis5"); + getTargetStreamer().emitDirectiveSetMaxis5(); + break; + case Maxis::FeatureMaxis32: + selectArch("maxis32"); + getTargetStreamer().emitDirectiveSetMaxis32(); + break; + case Maxis::FeatureMaxis32r2: + selectArch("maxis32r2"); + getTargetStreamer().emitDirectiveSetMaxis32R2(); + break; + case Maxis::FeatureMaxis32r3: + selectArch("maxis32r3"); + getTargetStreamer().emitDirectiveSetMaxis32R3(); + break; + case Maxis::FeatureMaxis32r5: + selectArch("maxis32r5"); + getTargetStreamer().emitDirectiveSetMaxis32R5(); + break; + case Maxis::FeatureMaxis32r6: + selectArch("maxis32r6"); + getTargetStreamer().emitDirectiveSetMaxis32R6(); + break; + case Maxis::FeatureMaxis64: + selectArch("maxis64"); + getTargetStreamer().emitDirectiveSetMaxis64(); + break; + case Maxis::FeatureMaxis64r2: + selectArch("maxis64r2"); + getTargetStreamer().emitDirectiveSetMaxis64R2(); + break; + case Maxis::FeatureMaxis64r3: + selectArch("maxis64r3"); + getTargetStreamer().emitDirectiveSetMaxis64R3(); + break; + case Maxis::FeatureMaxis64r5: + selectArch("maxis64r5"); + getTargetStreamer().emitDirectiveSetMaxis64R5(); + break; + case Maxis::FeatureMaxis64r6: + selectArch("maxis64r6"); + getTargetStreamer().emitDirectiveSetMaxis64R6(); + break; + } + return false; +} + +bool MaxisAsmParser::eatComma(StringRef ErrorStr) { + MCAsmParser &Parser = getParser(); + if (getLexer().isNot(AsmToken::Comma)) { + SMLoc Loc = getLexer().getLoc(); + return Error(Loc, ErrorStr); + } + + Parser.Lex(); // Eat the comma. + return true; +} + +// Used to determine if .cpload, .cprestore, and .cpsetup have any effect. +// In this class, it is only used for .cprestore. +// FIXME: Only keep track of IsPicEnabled in one place, instead of in both +// MaxisTargetELFStreamer and MaxisAsmParser. +bool MaxisAsmParser::isPicAndNotNxxAbi() { + return inPicMode() && !(isABI_N32() || isABI_N64()); +} + +bool MaxisAsmParser::parseDirectiveCpLoad(SMLoc Loc) { + if (AssemblerOptions.back()->isReorder()) + Warning(Loc, ".cpload should be inside a noreorder section"); + + if (inMaxis16Mode()) { + reportParseError(".cpload is not supported in Maxis16 mode"); + return false; + } + + SmallVector, 1> Reg; + OperandMatchResultTy ResTy = parseAnyRegister(Reg); + if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { + reportParseError("expected register containing function address"); + return false; + } + + MaxisOperand &RegOpnd = static_cast(*Reg[0]); + if (!RegOpnd.isGPRAsmReg()) { + reportParseError(RegOpnd.getStartLoc(), "invalid register"); + return false; + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + getTargetStreamer().emitDirectiveCpLoad(RegOpnd.getGPR32Reg()); + return false; +} + +bool MaxisAsmParser::parseDirectiveCpRestore(SMLoc Loc) { + MCAsmParser &Parser = getParser(); + + // Note that .cprestore is ignored if used with the N32 and N64 ABIs or if it + // is used in non-PIC mode. + + if (inMaxis16Mode()) { + reportParseError(".cprestore is not supported in Maxis16 mode"); + return false; + } + + // Get the stack offset value. + const MCExpr *StackOffset; + int64_t StackOffsetVal; + if (Parser.parseExpression(StackOffset)) { + reportParseError("expected stack offset value"); + return false; + } + + if (!StackOffset->evaluateAsAbsolute(StackOffsetVal)) { + reportParseError("stack offset is not an absolute expression"); + return false; + } + + if (StackOffsetVal < 0) { + Warning(Loc, ".cprestore with negative stack offset has no effect"); + IsCpRestoreSet = false; + } else { + IsCpRestoreSet = true; + CpRestoreOffset = StackOffsetVal; + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + if (!getTargetStreamer().emitDirectiveCpRestore( + CpRestoreOffset, [&]() { return getATReg(Loc); }, Loc, STI)) + return true; + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseDirectiveCPSetup() { + MCAsmParser &Parser = getParser(); + unsigned FuncReg; + unsigned Save; + bool SaveIsReg = true; + + SmallVector, 1> TmpReg; + OperandMatchResultTy ResTy = parseAnyRegister(TmpReg); + if (ResTy == MatchOperand_NoMatch) { + reportParseError("expected register containing function address"); + return false; + } + + MaxisOperand &FuncRegOpnd = static_cast(*TmpReg[0]); + if (!FuncRegOpnd.isGPRAsmReg()) { + reportParseError(FuncRegOpnd.getStartLoc(), "invalid register"); + return false; + } + + FuncReg = FuncRegOpnd.getGPR32Reg(); + TmpReg.clear(); + + if (!eatComma("unexpected token, expected comma")) + return true; + + ResTy = parseAnyRegister(TmpReg); + if (ResTy == MatchOperand_NoMatch) { + const MCExpr *OffsetExpr; + int64_t OffsetVal; + SMLoc ExprLoc = getLexer().getLoc(); + + if (Parser.parseExpression(OffsetExpr) || + !OffsetExpr->evaluateAsAbsolute(OffsetVal)) { + reportParseError(ExprLoc, "expected save register or stack offset"); + return false; + } + + Save = OffsetVal; + SaveIsReg = false; + } else { + MaxisOperand &SaveOpnd = static_cast(*TmpReg[0]); + if (!SaveOpnd.isGPRAsmReg()) { + reportParseError(SaveOpnd.getStartLoc(), "invalid register"); + return false; + } + Save = SaveOpnd.getGPR32Reg(); + } + + if (!eatComma("unexpected token, expected comma")) + return true; + + const MCExpr *Expr; + if (Parser.parseExpression(Expr)) { + reportParseError("expected expression"); + return false; + } + + if (Expr->getKind() != MCExpr::SymbolRef) { + reportParseError("expected symbol"); + return false; + } + const MCSymbolRefExpr *Ref = static_cast(Expr); + + CpSaveLocation = Save; + CpSaveLocationIsRegister = SaveIsReg; + + getTargetStreamer().emitDirectiveCpsetup(FuncReg, Save, Ref->getSymbol(), + SaveIsReg); + return false; +} + +bool MaxisAsmParser::parseDirectiveCPReturn() { + getTargetStreamer().emitDirectiveCpreturn(CpSaveLocation, + CpSaveLocationIsRegister); + return false; +} + +bool MaxisAsmParser::parseDirectiveNaN() { + MCAsmParser &Parser = getParser(); + if (getLexer().isNot(AsmToken::EndOfStatement)) { + const AsmToken &Tok = Parser.getTok(); + + if (Tok.getString() == "2008") { + Parser.Lex(); + getTargetStreamer().emitDirectiveNaN2008(); + return false; + } else if (Tok.getString() == "legacy") { + Parser.Lex(); + getTargetStreamer().emitDirectiveNaNLegacy(); + return false; + } + } + // If we don't recognize the option passed to the .nan + // directive (e.g. no option or unknown option), emit an error. + reportParseError("invalid option in .nan directive"); + return false; +} + +bool MaxisAsmParser::parseDirectiveSet() { + MCAsmParser &Parser = getParser(); + // Get the next token. + const AsmToken &Tok = Parser.getTok(); + + if (Tok.getString() == "noat") { + return parseSetNoAtDirective(); + } else if (Tok.getString() == "at") { + return parseSetAtDirective(); + } else if (Tok.getString() == "arch") { + return parseSetArchDirective(); + } else if (Tok.getString() == "bopt") { + Warning(Tok.getLoc(), "'bopt' feature is unsupported"); + getParser().Lex(); + return false; + } else if (Tok.getString() == "nobopt") { + // We're already running in nobopt mode, so nothing to do. + getParser().Lex(); + return false; + } else if (Tok.getString() == "fp") { + return parseSetFpDirective(); + } else if (Tok.getString() == "oddspreg") { + return parseSetOddSPRegDirective(); + } else if (Tok.getString() == "nooddspreg") { + return parseSetNoOddSPRegDirective(); + } else if (Tok.getString() == "pop") { + return parseSetPopDirective(); + } else if (Tok.getString() == "push") { + return parseSetPushDirective(); + } else if (Tok.getString() == "reorder") { + return parseSetReorderDirective(); + } else if (Tok.getString() == "noreorder") { + return parseSetNoReorderDirective(); + } else if (Tok.getString() == "macro") { + return parseSetMacroDirective(); + } else if (Tok.getString() == "nomacro") { + return parseSetNoMacroDirective(); + } else if (Tok.getString() == "maxis16") { + return parseSetMaxis16Directive(); + } else if (Tok.getString() == "nomaxis16") { + return parseSetNoMaxis16Directive(); + } else if (Tok.getString() == "nomicromaxis") { + clearFeatureBits(Maxis::FeatureMicroMaxis, "micromaxis"); + getTargetStreamer().emitDirectiveSetNoMicroMaxis(); + Parser.eatToEndOfStatement(); + return false; + } else if (Tok.getString() == "micromaxis") { + if (hasMaxis64r6()) { + Error(Tok.getLoc(), ".set micromaxis directive is not supported with MAXIS64R6"); + return false; + } + return parseSetFeature(Maxis::FeatureMicroMaxis); + } else if (Tok.getString() == "maxis0") { + return parseSetMaxis0Directive(); + } else if (Tok.getString() == "maxis1") { + return parseSetFeature(Maxis::FeatureMaxis1); + } else if (Tok.getString() == "maxis2") { + return parseSetFeature(Maxis::FeatureMaxis2); + } else if (Tok.getString() == "maxis3") { + return parseSetFeature(Maxis::FeatureMaxis3); + } else if (Tok.getString() == "maxis4") { + return parseSetFeature(Maxis::FeatureMaxis4); + } else if (Tok.getString() == "maxis5") { + return parseSetFeature(Maxis::FeatureMaxis5); + } else if (Tok.getString() == "maxis32") { + return parseSetFeature(Maxis::FeatureMaxis32); + } else if (Tok.getString() == "maxis32r2") { + return parseSetFeature(Maxis::FeatureMaxis32r2); + } else if (Tok.getString() == "maxis32r3") { + return parseSetFeature(Maxis::FeatureMaxis32r3); + } else if (Tok.getString() == "maxis32r5") { + return parseSetFeature(Maxis::FeatureMaxis32r5); + } else if (Tok.getString() == "maxis32r6") { + return parseSetFeature(Maxis::FeatureMaxis32r6); + } else if (Tok.getString() == "maxis64") { + return parseSetFeature(Maxis::FeatureMaxis64); + } else if (Tok.getString() == "maxis64r2") { + return parseSetFeature(Maxis::FeatureMaxis64r2); + } else if (Tok.getString() == "maxis64r3") { + return parseSetFeature(Maxis::FeatureMaxis64r3); + } else if (Tok.getString() == "maxis64r5") { + return parseSetFeature(Maxis::FeatureMaxis64r5); + } else if (Tok.getString() == "maxis64r6") { + if (inMicroMaxisMode()) { + Error(Tok.getLoc(), "MAXIS64R6 is not supported with microMAXIS"); + return false; + } + return parseSetFeature(Maxis::FeatureMaxis64r6); + } else if (Tok.getString() == "dsp") { + return parseSetFeature(Maxis::FeatureDSP); + } else if (Tok.getString() == "dspr2") { + return parseSetFeature(Maxis::FeatureDSPR2); + } else if (Tok.getString() == "nodsp") { + return parseSetNoDspDirective(); + } else if (Tok.getString() == "msa") { + return parseSetMsaDirective(); + } else if (Tok.getString() == "nomsa") { + return parseSetNoMsaDirective(); + } else if (Tok.getString() == "mt") { + return parseSetMtDirective(); + } else if (Tok.getString() == "nomt") { + return parseSetNoMtDirective(); + } else if (Tok.getString() == "softfloat") { + return parseSetSoftFloatDirective(); + } else if (Tok.getString() == "hardfloat") { + return parseSetHardFloatDirective(); + } else { + // It is just an identifier, look for an assignment. + parseSetAssignment(); + return false; + } + + return true; +} + +/// parseDataDirective +/// ::= .word [ expression (, expression)* ] +bool MaxisAsmParser::parseDataDirective(unsigned Size, SMLoc L) { + MCAsmParser &Parser = getParser(); + if (getLexer().isNot(AsmToken::EndOfStatement)) { + while (true) { + const MCExpr *Value; + if (getParser().parseExpression(Value)) + return true; + + getParser().getStreamer().EmitValue(Value, Size); + + if (getLexer().is(AsmToken::EndOfStatement)) + break; + + if (getLexer().isNot(AsmToken::Comma)) + return Error(L, "unexpected token, expected comma"); + Parser.Lex(); + } + } + + Parser.Lex(); + return false; +} + +/// parseDirectiveGpWord +/// ::= .gpword local_sym +bool MaxisAsmParser::parseDirectiveGpWord() { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + // EmitGPRel32Value requires an expression, so we are using base class + // method to evaluate the expression. + if (getParser().parseExpression(Value)) + return true; + getParser().getStreamer().EmitGPRel32Value(Value); + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return Error(getLexer().getLoc(), + "unexpected token, expected end of statement"); + Parser.Lex(); // Eat EndOfStatement token. + return false; +} + +/// parseDirectiveGpDWord +/// ::= .gpdword local_sym +bool MaxisAsmParser::parseDirectiveGpDWord() { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + // EmitGPRel64Value requires an expression, so we are using base class + // method to evaluate the expression. + if (getParser().parseExpression(Value)) + return true; + getParser().getStreamer().EmitGPRel64Value(Value); + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return Error(getLexer().getLoc(), + "unexpected token, expected end of statement"); + Parser.Lex(); // Eat EndOfStatement token. + return false; +} + +/// parseDirectiveDtpRelWord +/// ::= .dtprelword tls_sym +bool MaxisAsmParser::parseDirectiveDtpRelWord() { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + // EmitDTPRel32Value requires an expression, so we are using base class + // method to evaluate the expression. + if (getParser().parseExpression(Value)) + return true; + getParser().getStreamer().EmitDTPRel32Value(Value); + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return Error(getLexer().getLoc(), + "unexpected token, expected end of statement"); + Parser.Lex(); // Eat EndOfStatement token. + return false; +} + +/// parseDirectiveDtpRelDWord +/// ::= .dtpreldword tls_sym +bool MaxisAsmParser::parseDirectiveDtpRelDWord() { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + // EmitDTPRel64Value requires an expression, so we are using base class + // method to evaluate the expression. + if (getParser().parseExpression(Value)) + return true; + getParser().getStreamer().EmitDTPRel64Value(Value); + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return Error(getLexer().getLoc(), + "unexpected token, expected end of statement"); + Parser.Lex(); // Eat EndOfStatement token. + return false; +} + +/// parseDirectiveTpRelWord +/// ::= .tprelword tls_sym +bool MaxisAsmParser::parseDirectiveTpRelWord() { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + // EmitTPRel32Value requires an expression, so we are using base class + // method to evaluate the expression. + if (getParser().parseExpression(Value)) + return true; + getParser().getStreamer().EmitTPRel32Value(Value); + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return Error(getLexer().getLoc(), + "unexpected token, expected end of statement"); + Parser.Lex(); // Eat EndOfStatement token. + return false; +} + +/// parseDirectiveTpRelDWord +/// ::= .tpreldword tls_sym +bool MaxisAsmParser::parseDirectiveTpRelDWord() { + MCAsmParser &Parser = getParser(); + const MCExpr *Value; + // EmitTPRel64Value requires an expression, so we are using base class + // method to evaluate the expression. + if (getParser().parseExpression(Value)) + return true; + getParser().getStreamer().EmitTPRel64Value(Value); + + if (getLexer().isNot(AsmToken::EndOfStatement)) + return Error(getLexer().getLoc(), + "unexpected token, expected end of statement"); + Parser.Lex(); // Eat EndOfStatement token. + return false; +} + +bool MaxisAsmParser::parseDirectiveOption() { + MCAsmParser &Parser = getParser(); + // Get the option token. + AsmToken Tok = Parser.getTok(); + // At the moment only identifiers are supported. + if (Tok.isNot(AsmToken::Identifier)) { + return Error(Parser.getTok().getLoc(), + "unexpected token, expected identifier"); + } + + StringRef Option = Tok.getIdentifier(); + + if (Option == "pic0") { + // MaxisAsmParser needs to know if the current PIC mode changes. + IsPicEnabled = false; + + getTargetStreamer().emitDirectiveOptionPic0(); + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) { + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + } + return false; + } + + if (Option == "pic2") { + // MaxisAsmParser needs to know if the current PIC mode changes. + IsPicEnabled = true; + + getTargetStreamer().emitDirectiveOptionPic2(); + Parser.Lex(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) { + return Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + } + return false; + } + + // Unknown option. + Warning(Parser.getTok().getLoc(), + "unknown option, expected 'pic0' or 'pic2'"); + Parser.eatToEndOfStatement(); + return false; +} + +/// parseInsnDirective +/// ::= .insn +bool MaxisAsmParser::parseInsnDirective() { + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + // The actual label marking happens in + // MaxisELFStreamer::createPendingLabelRelocs(). + getTargetStreamer().emitDirectiveInsn(); + + getParser().Lex(); // Eat EndOfStatement token. + return false; +} + +/// parseRSectionDirective +/// ::= .rdata +bool MaxisAsmParser::parseRSectionDirective(StringRef Section) { + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + MCSection *ELFSection = getContext().getELFSection( + Section, ELF::SHT_PROGBITS, ELF::SHF_ALLOC); + getParser().getStreamer().SwitchSection(ELFSection); + + getParser().Lex(); // Eat EndOfStatement token. + return false; +} + +/// parseSSectionDirective +/// ::= .sbss +/// ::= .sdata +bool MaxisAsmParser::parseSSectionDirective(StringRef Section, unsigned Type) { + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + MCSection *ELFSection = getContext().getELFSection( + Section, Type, ELF::SHF_WRITE | ELF::SHF_ALLOC | ELF::SHF_MAXIS_GPREL); + getParser().getStreamer().SwitchSection(ELFSection); + + getParser().Lex(); // Eat EndOfStatement token. + return false; +} + +/// parseDirectiveModule +/// ::= .module oddspreg +/// ::= .module nooddspreg +/// ::= .module fp=value +/// ::= .module softfloat +/// ::= .module hardfloat +/// ::= .module mt +bool MaxisAsmParser::parseDirectiveModule() { + MCAsmParser &Parser = getParser(); + MCAsmLexer &Lexer = getLexer(); + SMLoc L = Lexer.getLoc(); + + if (!getTargetStreamer().isModuleDirectiveAllowed()) { + // TODO : get a better message. + reportParseError(".module directive must appear before any code"); + return false; + } + + StringRef Option; + if (Parser.parseIdentifier(Option)) { + reportParseError("expected .module option identifier"); + return false; + } + + if (Option == "oddspreg") { + clearModuleFeatureBits(Maxis::FeatureNoOddSPReg, "nooddspreg"); + + // Synchronize the abiflags information with the FeatureBits information we + // changed above. + getTargetStreamer().updateABIInfo(*this); + + // If printing assembly, use the recently updated abiflags information. + // If generating ELF, don't do anything (the .MAXIS.abiflags section gets + // emitted at the end). + getTargetStreamer().emitDirectiveModuleOddSPReg(); + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + return false; // parseDirectiveModule has finished successfully. + } else if (Option == "nooddspreg") { + if (!isABI_O32()) { + return Error(L, "'.module nooddspreg' requires the O32 ABI"); + } + + setModuleFeatureBits(Maxis::FeatureNoOddSPReg, "nooddspreg"); + + // Synchronize the abiflags information with the FeatureBits information we + // changed above. + getTargetStreamer().updateABIInfo(*this); + + // If printing assembly, use the recently updated abiflags information. + // If generating ELF, don't do anything (the .MAXIS.abiflags section gets + // emitted at the end). + getTargetStreamer().emitDirectiveModuleOddSPReg(); + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + return false; // parseDirectiveModule has finished successfully. + } else if (Option == "fp") { + return parseDirectiveModuleFP(); + } else if (Option == "softfloat") { + setModuleFeatureBits(Maxis::FeatureSoftFloat, "soft-float"); + + // Synchronize the ABI Flags information with the FeatureBits information we + // updated above. + getTargetStreamer().updateABIInfo(*this); + + // If printing assembly, use the recently updated ABI Flags information. + // If generating ELF, don't do anything (the .MAXIS.abiflags section gets + // emitted later). + getTargetStreamer().emitDirectiveModuleSoftFloat(); + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + return false; // parseDirectiveModule has finished successfully. + } else if (Option == "hardfloat") { + clearModuleFeatureBits(Maxis::FeatureSoftFloat, "soft-float"); + + // Synchronize the ABI Flags information with the FeatureBits information we + // updated above. + getTargetStreamer().updateABIInfo(*this); + + // If printing assembly, use the recently updated ABI Flags information. + // If generating ELF, don't do anything (the .MAXIS.abiflags section gets + // emitted later). + getTargetStreamer().emitDirectiveModuleHardFloat(); + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + return false; // parseDirectiveModule has finished successfully. + } else if (Option == "mt") { + setModuleFeatureBits(Maxis::FeatureMT, "mt"); + + // Synchronize the ABI Flags information with the FeatureBits information we + // updated above. + getTargetStreamer().updateABIInfo(*this); + + // If printing assembly, use the recently updated ABI Flags information. + // If generating ELF, don't do anything (the .MAXIS.abiflags section gets + // emitted later). + getTargetStreamer().emitDirectiveModuleMT(); + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + return false; // parseDirectiveModule has finished successfully. + } else { + return Error(L, "'" + Twine(Option) + "' is not a valid .module option."); + } +} + +/// parseDirectiveModuleFP +/// ::= =32 +/// ::= =xx +/// ::= =64 +bool MaxisAsmParser::parseDirectiveModuleFP() { + MCAsmParser &Parser = getParser(); + MCAsmLexer &Lexer = getLexer(); + + if (Lexer.isNot(AsmToken::Equal)) { + reportParseError("unexpected token, expected equals sign '='"); + return false; + } + Parser.Lex(); // Eat '=' token. + + MaxisABIFlagsSection::FpABIKind FpABI; + if (!parseFpABIValue(FpABI, ".module")) + return false; + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + // Synchronize the abiflags information with the FeatureBits information we + // changed above. + getTargetStreamer().updateABIInfo(*this); + + // If printing assembly, use the recently updated abiflags information. + // If generating ELF, don't do anything (the .MAXIS.abiflags section gets + // emitted at the end). + getTargetStreamer().emitDirectiveModuleFP(); + + Parser.Lex(); // Consume the EndOfStatement. + return false; +} + +bool MaxisAsmParser::parseFpABIValue(MaxisABIFlagsSection::FpABIKind &FpABI, + StringRef Directive) { + MCAsmParser &Parser = getParser(); + MCAsmLexer &Lexer = getLexer(); + bool ModuleLevelOptions = Directive == ".module"; + + if (Lexer.is(AsmToken::Identifier)) { + StringRef Value = Parser.getTok().getString(); + Parser.Lex(); + + if (Value != "xx") { + reportParseError("unsupported value, expected 'xx', '32' or '64'"); + return false; + } + + if (!isABI_O32()) { + reportParseError("'" + Directive + " fp=xx' requires the O32 ABI"); + return false; + } + + FpABI = MaxisABIFlagsSection::FpABIKind::XX; + if (ModuleLevelOptions) { + setModuleFeatureBits(Maxis::FeatureFPXX, "fpxx"); + clearModuleFeatureBits(Maxis::FeatureFP64Bit, "fp64"); + } else { + setFeatureBits(Maxis::FeatureFPXX, "fpxx"); + clearFeatureBits(Maxis::FeatureFP64Bit, "fp64"); + } + return true; + } + + if (Lexer.is(AsmToken::Integer)) { + unsigned Value = Parser.getTok().getIntVal(); + Parser.Lex(); + + if (Value != 32 && Value != 64) { + reportParseError("unsupported value, expected 'xx', '32' or '64'"); + return false; + } + + if (Value == 32) { + if (!isABI_O32()) { + reportParseError("'" + Directive + " fp=32' requires the O32 ABI"); + return false; + } + + FpABI = MaxisABIFlagsSection::FpABIKind::S32; + if (ModuleLevelOptions) { + clearModuleFeatureBits(Maxis::FeatureFPXX, "fpxx"); + clearModuleFeatureBits(Maxis::FeatureFP64Bit, "fp64"); + } else { + clearFeatureBits(Maxis::FeatureFPXX, "fpxx"); + clearFeatureBits(Maxis::FeatureFP64Bit, "fp64"); + } + } else { + FpABI = MaxisABIFlagsSection::FpABIKind::S64; + if (ModuleLevelOptions) { + clearModuleFeatureBits(Maxis::FeatureFPXX, "fpxx"); + setModuleFeatureBits(Maxis::FeatureFP64Bit, "fp64"); + } else { + clearFeatureBits(Maxis::FeatureFPXX, "fpxx"); + setFeatureBits(Maxis::FeatureFP64Bit, "fp64"); + } + } + + return true; + } + + return false; +} + +bool MaxisAsmParser::ParseDirective(AsmToken DirectiveID) { + // This returns false if this function recognizes the directive + // regardless of whether it is successfully handles or reports an + // error. Otherwise it returns true to give the generic parser a + // chance at recognizing it. + + MCAsmParser &Parser = getParser(); + StringRef IDVal = DirectiveID.getString(); + + if (IDVal == ".cpload") { + parseDirectiveCpLoad(DirectiveID.getLoc()); + return false; + } + if (IDVal == ".cprestore") { + parseDirectiveCpRestore(DirectiveID.getLoc()); + return false; + } + if (IDVal == ".dword") { + parseDataDirective(8, DirectiveID.getLoc()); + return false; + } + if (IDVal == ".ent") { + StringRef SymbolName; + + if (Parser.parseIdentifier(SymbolName)) { + reportParseError("expected identifier after .ent"); + return false; + } + + // There's an undocumented extension that allows an integer to + // follow the name of the procedure which AFAICS is ignored by GAS. + // Example: .ent foo,2 + if (getLexer().isNot(AsmToken::EndOfStatement)) { + if (getLexer().isNot(AsmToken::Comma)) { + // Even though we accept this undocumented extension for compatibility + // reasons, the additional integer argument does not actually change + // the behaviour of the '.ent' directive, so we would like to discourage + // its use. We do this by not referring to the extended version in + // error messages which are not directly related to its use. + reportParseError("unexpected token, expected end of statement"); + return false; + } + Parser.Lex(); // Eat the comma. + const MCExpr *DummyNumber; + int64_t DummyNumberVal; + // If the user was explicitly trying to use the extended version, + // we still give helpful extension-related error messages. + if (Parser.parseExpression(DummyNumber)) { + reportParseError("expected number after comma"); + return false; + } + if (!DummyNumber->evaluateAsAbsolute(DummyNumberVal)) { + reportParseError("expected an absolute expression after comma"); + return false; + } + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + MCSymbol *Sym = getContext().getOrCreateSymbol(SymbolName); + + getTargetStreamer().emitDirectiveEnt(*Sym); + CurrentFn = Sym; + IsCpRestoreSet = false; + return false; + } + + if (IDVal == ".end") { + StringRef SymbolName; + + if (Parser.parseIdentifier(SymbolName)) { + reportParseError("expected identifier after .end"); + return false; + } + + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + if (CurrentFn == nullptr) { + reportParseError(".end used without .ent"); + return false; + } + + if ((SymbolName != CurrentFn->getName())) { + reportParseError(".end symbol does not match .ent symbol"); + return false; + } + + getTargetStreamer().emitDirectiveEnd(SymbolName); + CurrentFn = nullptr; + IsCpRestoreSet = false; + return false; + } + + if (IDVal == ".frame") { + // .frame $stack_reg, frame_size_in_bytes, $return_reg + SmallVector, 1> TmpReg; + OperandMatchResultTy ResTy = parseAnyRegister(TmpReg); + if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { + reportParseError("expected stack register"); + return false; + } + + MaxisOperand &StackRegOpnd = static_cast(*TmpReg[0]); + if (!StackRegOpnd.isGPRAsmReg()) { + reportParseError(StackRegOpnd.getStartLoc(), + "expected general purpose register"); + return false; + } + unsigned StackReg = StackRegOpnd.getGPR32Reg(); + + if (Parser.getTok().is(AsmToken::Comma)) + Parser.Lex(); + else { + reportParseError("unexpected token, expected comma"); + return false; + } + + // Parse the frame size. + const MCExpr *FrameSize; + int64_t FrameSizeVal; + + if (Parser.parseExpression(FrameSize)) { + reportParseError("expected frame size value"); + return false; + } + + if (!FrameSize->evaluateAsAbsolute(FrameSizeVal)) { + reportParseError("frame size not an absolute expression"); + return false; + } + + if (Parser.getTok().is(AsmToken::Comma)) + Parser.Lex(); + else { + reportParseError("unexpected token, expected comma"); + return false; + } + + // Parse the return register. + TmpReg.clear(); + ResTy = parseAnyRegister(TmpReg); + if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { + reportParseError("expected return register"); + return false; + } + + MaxisOperand &ReturnRegOpnd = static_cast(*TmpReg[0]); + if (!ReturnRegOpnd.isGPRAsmReg()) { + reportParseError(ReturnRegOpnd.getStartLoc(), + "expected general purpose register"); + return false; + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + getTargetStreamer().emitFrame(StackReg, FrameSizeVal, + ReturnRegOpnd.getGPR32Reg()); + IsCpRestoreSet = false; + return false; + } + + if (IDVal == ".set") { + parseDirectiveSet(); + return false; + } + + if (IDVal == ".mask" || IDVal == ".fmask") { + // .mask bitmask, frame_offset + // bitmask: One bit for each register used. + // frame_offset: Offset from Canonical Frame Address ($sp on entry) where + // first register is expected to be saved. + // Examples: + // .mask 0x80000000, -4 + // .fmask 0x80000000, -4 + // + + // Parse the bitmask + const MCExpr *BitMask; + int64_t BitMaskVal; + + if (Parser.parseExpression(BitMask)) { + reportParseError("expected bitmask value"); + return false; + } + + if (!BitMask->evaluateAsAbsolute(BitMaskVal)) { + reportParseError("bitmask not an absolute expression"); + return false; + } + + if (Parser.getTok().is(AsmToken::Comma)) + Parser.Lex(); + else { + reportParseError("unexpected token, expected comma"); + return false; + } + + // Parse the frame_offset + const MCExpr *FrameOffset; + int64_t FrameOffsetVal; + + if (Parser.parseExpression(FrameOffset)) { + reportParseError("expected frame offset value"); + return false; + } + + if (!FrameOffset->evaluateAsAbsolute(FrameOffsetVal)) { + reportParseError("frame offset not an absolute expression"); + return false; + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + if (IDVal == ".mask") + getTargetStreamer().emitMask(BitMaskVal, FrameOffsetVal); + else + getTargetStreamer().emitFMask(BitMaskVal, FrameOffsetVal); + return false; + } + + if (IDVal == ".nan") + return parseDirectiveNaN(); + + if (IDVal == ".gpword") { + parseDirectiveGpWord(); + return false; + } + + if (IDVal == ".gpdword") { + parseDirectiveGpDWord(); + return false; + } + + if (IDVal == ".dtprelword") { + parseDirectiveDtpRelWord(); + return false; + } + + if (IDVal == ".dtpreldword") { + parseDirectiveDtpRelDWord(); + return false; + } + + if (IDVal == ".tprelword") { + parseDirectiveTpRelWord(); + return false; + } + + if (IDVal == ".tpreldword") { + parseDirectiveTpRelDWord(); + return false; + } + + if (IDVal == ".word") { + parseDataDirective(4, DirectiveID.getLoc()); + return false; + } + + if (IDVal == ".hword") { + parseDataDirective(2, DirectiveID.getLoc()); + return false; + } + + if (IDVal == ".option") { + parseDirectiveOption(); + return false; + } + + if (IDVal == ".abicalls") { + getTargetStreamer().emitDirectiveAbiCalls(); + if (Parser.getTok().isNot(AsmToken::EndOfStatement)) { + Error(Parser.getTok().getLoc(), + "unexpected token, expected end of statement"); + } + return false; + } + + if (IDVal == ".cpsetup") { + parseDirectiveCPSetup(); + return false; + } + if (IDVal == ".cpreturn") { + parseDirectiveCPReturn(); + return false; + } + if (IDVal == ".module") { + parseDirectiveModule(); + return false; + } + if (IDVal == ".llvm_internal_maxis_reallow_module_directive") { + parseInternalDirectiveReallowModule(); + return false; + } + if (IDVal == ".insn") { + parseInsnDirective(); + return false; + } + if (IDVal == ".rdata") { + parseRSectionDirective(".rodata"); + return false; + } + if (IDVal == ".sbss") { + parseSSectionDirective(IDVal, ELF::SHT_NOBITS); + return false; + } + if (IDVal == ".sdata") { + parseSSectionDirective(IDVal, ELF::SHT_PROGBITS); + return false; + } + + return true; +} + +bool MaxisAsmParser::parseInternalDirectiveReallowModule() { + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + + getTargetStreamer().reallowModuleDirective(); + + getParser().Lex(); // Eat EndOfStatement token. + return false; +} + +extern "C" void LLVMInitializeMaxisAsmParser() { + RegisterMCAsmParser X(getTheMaxisTarget()); + RegisterMCAsmParser Y(getTheMaxiselTarget()); + RegisterMCAsmParser A(getTheMaxis64Target()); + RegisterMCAsmParser B(getTheMaxis64elTarget()); +} + +#define GET_REGISTER_MATCHER +#define GET_MATCHER_IMPLEMENTATION +#include "MaxisGenAsmMatcher.inc" + +bool MaxisAsmParser::mnemonicIsValid(StringRef Mnemonic, unsigned VariantID) { + // Find the appropriate table for this asm variant. + const MatchEntry *Start, *End; + switch (VariantID) { + default: llvm_unreachable("invalid variant!"); + case 0: Start = std::begin(MatchTable0); End = std::end(MatchTable0); break; + } + // Search the table. + auto MnemonicRange = std::equal_range(Start, End, Mnemonic, LessOpcode()); + return MnemonicRange.first != MnemonicRange.second; +} diff --git a/lib/Target/Maxis/CMakeLists.txt b/lib/Target/Maxis/CMakeLists.txt new file mode 100644 index 00000000..96546136 --- /dev/null +++ b/lib/Target/Maxis/CMakeLists.txt @@ -0,0 +1,58 @@ +set(LLVM_TARGET_DEFINITIONS Maxis.td) + +tablegen(LLVM MaxisGenRegisterInfo.inc -gen-register-info) +tablegen(LLVM MaxisGenInstrInfo.inc -gen-instr-info) +tablegen(LLVM MaxisGenDisassemblerTables.inc -gen-disassembler) +tablegen(LLVM MaxisGenMCCodeEmitter.inc -gen-emitter) +tablegen(LLVM MaxisGenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM MaxisGenDAGISel.inc -gen-dag-isel) +tablegen(LLVM MaxisGenFastISel.inc -gen-fast-isel) +tablegen(LLVM MaxisGenCallingConv.inc -gen-callingconv) +tablegen(LLVM MaxisGenSubtargetInfo.inc -gen-subtarget) +tablegen(LLVM MaxisGenAsmMatcher.inc -gen-asm-matcher) +tablegen(LLVM MaxisGenMCPseudoLowering.inc -gen-pseudo-lowering) +add_public_tablegen_target(MaxisCommonTableGen) + +add_llvm_target(MaxisCodeGen + Maxis16FrameLowering.cpp + Maxis16HardFloat.cpp + Maxis16HardFloatInfo.cpp + Maxis16InstrInfo.cpp + Maxis16ISelDAGToDAG.cpp + Maxis16ISelLowering.cpp + Maxis16RegisterInfo.cpp + MaxisAnalyzeImmediate.cpp + MaxisAsmPrinter.cpp + MaxisCCState.cpp + MaxisConstantIslandPass.cpp + MaxisDelaySlotFiller.cpp + MaxisFastISel.cpp + MaxisHazardSchedule.cpp + MaxisInstrInfo.cpp + MaxisISelDAGToDAG.cpp + MaxisISelLowering.cpp + MaxisFrameLowering.cpp + MaxisLongBranch.cpp + MaxisMCInstLower.cpp + MaxisMachineFunction.cpp + MaxisModuleISelDAGToDAG.cpp + MaxisOptimizePICCall.cpp + MaxisOs16.cpp + MaxisRegisterInfo.cpp + MaxisSEFrameLowering.cpp + MaxisSEInstrInfo.cpp + MaxisSEISelDAGToDAG.cpp + MaxisSEISelLowering.cpp + MaxisSERegisterInfo.cpp + MaxisSubtarget.cpp + MaxisTargetMachine.cpp + MaxisTargetObjectFile.cpp + MicroMaxisSizeReduction.cpp + ) + +add_subdirectory(InstPrinter) +add_subdirectory(Disassembler) +add_subdirectory(TargetInfo) +add_subdirectory(MCTargetDesc) +add_subdirectory(AsmParser) + diff --git a/lib/Target/Maxis/Disassembler/CMakeLists.txt b/lib/Target/Maxis/Disassembler/CMakeLists.txt new file mode 100644 index 00000000..598021ec --- /dev/null +++ b/lib/Target/Maxis/Disassembler/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMMaxisDisassembler + MaxisDisassembler.cpp + ) diff --git a/lib/Target/Maxis/Disassembler/LLVMBuild.txt b/lib/Target/Maxis/Disassembler/LLVMBuild.txt new file mode 100644 index 00000000..630e478f --- /dev/null +++ b/lib/Target/Maxis/Disassembler/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/Maxis/Disassembler/LLVMBuild.txt -------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = MaxisDisassembler +parent = Maxis +required_libraries = MCDisassembler MaxisInfo Support +add_to_library_groups = Maxis diff --git a/lib/Target/Maxis/Disassembler/MaxisDisassembler.cpp b/lib/Target/Maxis/Disassembler/MaxisDisassembler.cpp new file mode 100644 index 00000000..1edbbaf9 --- /dev/null +++ b/lib/Target/Maxis/Disassembler/MaxisDisassembler.cpp @@ -0,0 +1,2594 @@ +//===- MaxisDisassembler.cpp - Disassembler for Maxis -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is part of the Maxis Disassembler. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "Maxis.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCFixedLenDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-disassembler" + +using DecodeStatus = MCDisassembler::DecodeStatus; + +namespace { + +class MaxisDisassembler : public MCDisassembler { + bool IsMicroMaxis; + bool IsBigEndian; + +public: + MaxisDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx, bool IsBigEndian) + : MCDisassembler(STI, Ctx), + IsMicroMaxis(STI.getFeatureBits()[Maxis::FeatureMicroMaxis]), + IsBigEndian(IsBigEndian) {} + + bool hasMaxis2() const { return STI.getFeatureBits()[Maxis::FeatureMaxis2]; } + bool hasMaxis3() const { return STI.getFeatureBits()[Maxis::FeatureMaxis3]; } + bool hasMaxis32() const { return STI.getFeatureBits()[Maxis::FeatureMaxis32]; } + + bool hasMaxis32r6() const { + return STI.getFeatureBits()[Maxis::FeatureMaxis32r6]; + } + + bool isFP64() const { return STI.getFeatureBits()[Maxis::FeatureFP64Bit]; } + + bool isGP64() const { return STI.getFeatureBits()[Maxis::FeatureGP64Bit]; } + + bool isPTR64() const { return STI.getFeatureBits()[Maxis::FeaturePTR64Bit]; } + + bool hasCnMaxis() const { return STI.getFeatureBits()[Maxis::FeatureCnMaxis]; } + + bool hasCOP3() const { + // Only present in MAXIS-I and MAXIS-II + return !hasMaxis32() && !hasMaxis3(); + } + + DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const override; +}; + +} // end anonymous namespace + +// Forward declare these because the autogenerated code will reference them. +// Definitions are further down. +static DecodeStatus DecodeGPR64RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeCPU16RegsRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeGPRMM16RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeGPRMM16ZeroRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeGPRMM16MovePRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeGPR32RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodePtrRegisterClass(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeDSPRRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFGR64RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFGR32RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeCCRRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFCCRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFGRCCRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeHWRegsRegisterClass(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeAFGR64RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeACC64DSPRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeHI32DSPRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeLO32DSPRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMSA128BRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMSA128HRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMSA128WRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMSA128DRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMSACtrlRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeCOP0RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeCOP2RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeBranchTarget(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeBranchTarget1SImm16(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeJumpTarget(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeBranchTarget21(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeBranchTarget21MM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeBranchTarget26(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +// DecodeBranchTarget7MM - Decode microMAXIS branch offset, which is +// shifted left by 1 bit. +static DecodeStatus DecodeBranchTarget7MM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +// DecodeBranchTarget10MM - Decode microMAXIS branch offset, which is +// shifted left by 1 bit. +static DecodeStatus DecodeBranchTarget10MM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +// DecodeBranchTargetMM - Decode microMAXIS branch offset, which is +// shifted left by 1 bit. +static DecodeStatus DecodeBranchTargetMM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +// DecodeBranchTarget26MM - Decode microMAXIS branch offset, which is +// shifted left by 1 bit. +static DecodeStatus DecodeBranchTarget26MM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder); + +// DecodeJumpTargetMM - Decode microMAXIS jump target, which is +// shifted left by 1 bit. +static DecodeStatus DecodeJumpTargetMM(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMem(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMemEVA(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeLoadByte9(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeLoadByte15(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeCacheOp(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeCacheeOp_CacheOpR6(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeCacheOpMM(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeStoreEvaOpMM(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodePrefeOpMM(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeSyncI(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeSynciR6(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMSA128Mem(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); + +static DecodeStatus DecodeMemMMImm4(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMemMMSPImm5Lsl2(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMemMMGPImm7Lsl2(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMemMMReglistImm4Lsl2(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMemMMImm9(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMemMMImm12(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMemMMImm16(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFMem(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFMemMMR2(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFMem2(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFMem3(MCInst &Inst, unsigned Insn, uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeFMemCop2R6(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); + +static DecodeStatus DecodeFMemCop2MMR6(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeSpecial3LlSc(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeAddiur2Simm7(MCInst &Inst, + unsigned Value, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeLi16Imm(MCInst &Inst, + unsigned Value, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodePOOL16BEncodedField(MCInst &Inst, + unsigned Value, + uint64_t Address, + const void *Decoder); + +template +static DecodeStatus DecodeUImmWithOffsetAndScale(MCInst &Inst, unsigned Value, + uint64_t Address, + const void *Decoder); + +template +static DecodeStatus DecodeUImmWithOffset(MCInst &Inst, unsigned Value, + uint64_t Address, + const void *Decoder) { + return DecodeUImmWithOffsetAndScale(Inst, Value, Address, + Decoder); +} + +template +static DecodeStatus DecodeSImmWithOffsetAndScale(MCInst &Inst, unsigned Value, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeInsSize(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeSimm19Lsl2(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); + +static DecodeStatus DecodeSimm18Lsl3(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); + +static DecodeStatus DecodeSimm9SP(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); + +static DecodeStatus DecodeANDI16Imm(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); + +static DecodeStatus DecodeSimm23Lsl2(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder); + +/// INSVE_[BHWD] have an implicit operand that the generated decoder doesn't +/// handle. +template +static DecodeStatus DecodeINSVE_DF(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus DecodeDAHIDATIMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus DecodeDAHIDATI(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus DecodeDAHIDATIMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus DecodeDAHIDATI(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodeAddiGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodePOP35GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodeDaddiGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodePOP37GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodePOP65GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodePOP75GroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodeBlezlGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodeBgtzlGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodeBgtzGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodeBlezGroupBranch(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodeBgtzGroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus +DecodeBlezGroupBranchMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus DecodeDINS(MCInst &MI, InsnType Insn, uint64_t Address, + const void *Decoder); + +template +static DecodeStatus DecodeDEXT(MCInst &MI, InsnType Insn, uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeRegListOperand(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeRegListOperand16(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder); + +static DecodeStatus DecodeMovePRegPair(MCInst &Inst, unsigned RegPair, + uint64_t Address, + const void *Decoder); + +namespace llvm { + +Target &getTheMaxiselTarget(); +Target &getTheMaxisTarget(); +Target &getTheMaxis64Target(); +Target &getTheMaxis64elTarget(); + +} // end namespace llvm + +static MCDisassembler *createMaxisDisassembler( + const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new MaxisDisassembler(STI, Ctx, true); +} + +static MCDisassembler *createMaxiselDisassembler( + const Target &T, + const MCSubtargetInfo &STI, + MCContext &Ctx) { + return new MaxisDisassembler(STI, Ctx, false); +} + +extern "C" void LLVMInitializeMaxisDisassembler() { + // Register the disassembler. + TargetRegistry::RegisterMCDisassembler(getTheMaxisTarget(), + createMaxisDisassembler); + TargetRegistry::RegisterMCDisassembler(getTheMaxiselTarget(), + createMaxiselDisassembler); + TargetRegistry::RegisterMCDisassembler(getTheMaxis64Target(), + createMaxisDisassembler); + TargetRegistry::RegisterMCDisassembler(getTheMaxis64elTarget(), + createMaxiselDisassembler); +} + +#include "MaxisGenDisassemblerTables.inc" + +static unsigned getReg(const void *D, unsigned RC, unsigned RegNo) { + const MaxisDisassembler *Dis = static_cast(D); + const MCRegisterInfo *RegInfo = Dis->getContext().getRegisterInfo(); + return *(RegInfo->getRegClass(RC).begin() + RegNo); +} + +template +static DecodeStatus DecodeINSVE_DF(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder) { + using DecodeFN = DecodeStatus (*)(MCInst &, unsigned, uint64_t, const void *); + + // The size of the n field depends on the element size + // The register class also depends on this. + InsnType tmp = fieldFromInstruction(insn, 17, 5); + unsigned NSize = 0; + DecodeFN RegDecoder = nullptr; + if ((tmp & 0x18) == 0x00) { // INSVE_B + NSize = 4; + RegDecoder = DecodeMSA128BRegisterClass; + } else if ((tmp & 0x1c) == 0x10) { // INSVE_H + NSize = 3; + RegDecoder = DecodeMSA128HRegisterClass; + } else if ((tmp & 0x1e) == 0x18) { // INSVE_W + NSize = 2; + RegDecoder = DecodeMSA128WRegisterClass; + } else if ((tmp & 0x1f) == 0x1c) { // INSVE_D + NSize = 1; + RegDecoder = DecodeMSA128DRegisterClass; + } else + llvm_unreachable("Invalid encoding"); + + assert(NSize != 0 && RegDecoder != nullptr); + + // $wd + tmp = fieldFromInstruction(insn, 6, 5); + if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler::Fail) + return MCDisassembler::Fail; + // $wd_in + if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler::Fail) + return MCDisassembler::Fail; + // $n + tmp = fieldFromInstruction(insn, 16, NSize); + MI.addOperand(MCOperand::createImm(tmp)); + // $ws + tmp = fieldFromInstruction(insn, 11, 5); + if (RegDecoder(MI, tmp, Address, Decoder) == MCDisassembler::Fail) + return MCDisassembler::Fail; + // $n2 + MI.addOperand(MCOperand::createImm(0)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeDAHIDATIMMR6(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder) { + InsnType Rs = fieldFromInstruction(insn, 16, 5); + InsnType Imm = fieldFromInstruction(insn, 0, 16); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR64RegClassID, + Rs))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR64RegClassID, + Rs))); + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeDAHIDATI(MCInst &MI, InsnType insn, uint64_t Address, + const void *Decoder) { + InsnType Rs = fieldFromInstruction(insn, 21, 5); + InsnType Imm = fieldFromInstruction(insn, 0, 16); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR64RegClassID, + Rs))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR64RegClassID, + Rs))); + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeAddiGroupBranch(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // If we are called then we can assume that MAXIS32r6/MAXIS64r6 is enabled + // (otherwise we would have matched the ADDI instruction from the earlier + // ISA's instead). + // + // We have: + // 0b001000 sssss ttttt iiiiiiiiiiiiiiii + // BOVC if rs >= rt + // BEQZALC if rs == 0 && rt != 0 + // BEQC if rs < rt && rs != 0 + + InsnType Rs = fieldFromInstruction(insn, 21, 5); + InsnType Rt = fieldFromInstruction(insn, 16, 5); + int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + bool HasRs = false; + + if (Rs >= Rt) { + MI.setOpcode(Maxis::BOVC); + HasRs = true; + } else if (Rs != 0 && Rs < Rt) { + MI.setOpcode(Maxis::BEQC); + HasRs = true; + } else + MI.setOpcode(Maxis::BEQZALC); + + if (HasRs) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodePOP35GroupBranchMMR6(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + InsnType Rt = fieldFromInstruction(insn, 21, 5); + InsnType Rs = fieldFromInstruction(insn, 16, 5); + int64_t Imm = 0; + + if (Rs >= Rt) { + MI.setOpcode(Maxis::BOVC_MMR6); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; + } else if (Rs != 0 && Rs < Rt) { + MI.setOpcode(Maxis::BEQC_MMR6); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + } else { + MI.setOpcode(Maxis::BEQZALC_MMR6); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; + } + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeDaddiGroupBranch(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // If we are called then we can assume that MAXIS32r6/MAXIS64r6 is enabled + // (otherwise we would have matched the ADDI instruction from the earlier + // ISA's instead). + // + // We have: + // 0b011000 sssss ttttt iiiiiiiiiiiiiiii + // BNVC if rs >= rt + // BNEZALC if rs == 0 && rt != 0 + // BNEC if rs < rt && rs != 0 + + InsnType Rs = fieldFromInstruction(insn, 21, 5); + InsnType Rt = fieldFromInstruction(insn, 16, 5); + int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + bool HasRs = false; + + if (Rs >= Rt) { + MI.setOpcode(Maxis::BNVC); + HasRs = true; + } else if (Rs != 0 && Rs < Rt) { + MI.setOpcode(Maxis::BNEC); + HasRs = true; + } else + MI.setOpcode(Maxis::BNEZALC); + + if (HasRs) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodePOP37GroupBranchMMR6(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + InsnType Rt = fieldFromInstruction(insn, 21, 5); + InsnType Rs = fieldFromInstruction(insn, 16, 5); + int64_t Imm = 0; + + if (Rs >= Rt) { + MI.setOpcode(Maxis::BNVC_MMR6); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; + } else if (Rs != 0 && Rs < Rt) { + MI.setOpcode(Maxis::BNEC_MMR6); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + } else { + MI.setOpcode(Maxis::BNEZALC_MMR6); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; + } + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodePOP65GroupBranchMMR6(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // We have: + // 0b110101 ttttt sssss iiiiiiiiiiiiiiii + // Invalid if rt == 0 + // BGTZC_MMR6 if rs == 0 && rt != 0 + // BLTZC_MMR6 if rs == rt && rt != 0 + // BLTC_MMR6 if rs != rt && rs != 0 && rt != 0 + + InsnType Rt = fieldFromInstruction(insn, 21, 5); + InsnType Rs = fieldFromInstruction(insn, 16, 5); + int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + bool HasRs = false; + + if (Rt == 0) + return MCDisassembler::Fail; + else if (Rs == 0) + MI.setOpcode(Maxis::BGTZC_MMR6); + else if (Rs == Rt) + MI.setOpcode(Maxis::BLTZC_MMR6); + else { + MI.setOpcode(Maxis::BLTC_MMR6); + HasRs = true; + } + + if (HasRs) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodePOP75GroupBranchMMR6(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // We have: + // 0b111101 ttttt sssss iiiiiiiiiiiiiiii + // Invalid if rt == 0 + // BLEZC_MMR6 if rs == 0 && rt != 0 + // BGEZC_MMR6 if rs == rt && rt != 0 + // BGEC_MMR6 if rs != rt && rs != 0 && rt != 0 + + InsnType Rt = fieldFromInstruction(insn, 21, 5); + InsnType Rs = fieldFromInstruction(insn, 16, 5); + int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + bool HasRs = false; + + if (Rt == 0) + return MCDisassembler::Fail; + else if (Rs == 0) + MI.setOpcode(Maxis::BLEZC_MMR6); + else if (Rs == Rt) + MI.setOpcode(Maxis::BGEZC_MMR6); + else { + HasRs = true; + MI.setOpcode(Maxis::BGEC_MMR6); + } + + if (HasRs) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeBlezlGroupBranch(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // If we are called then we can assume that MAXIS32r6/MAXIS64r6 is enabled + // (otherwise we would have matched the BLEZL instruction from the earlier + // ISA's instead). + // + // We have: + // 0b010110 sssss ttttt iiiiiiiiiiiiiiii + // Invalid if rs == 0 + // BLEZC if rs == 0 && rt != 0 + // BGEZC if rs == rt && rt != 0 + // BGEC if rs != rt && rs != 0 && rt != 0 + + InsnType Rs = fieldFromInstruction(insn, 21, 5); + InsnType Rt = fieldFromInstruction(insn, 16, 5); + int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + bool HasRs = false; + + if (Rt == 0) + return MCDisassembler::Fail; + else if (Rs == 0) + MI.setOpcode(Maxis::BLEZC); + else if (Rs == Rt) + MI.setOpcode(Maxis::BGEZC); + else { + HasRs = true; + MI.setOpcode(Maxis::BGEC); + } + + if (HasRs) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeBgtzlGroupBranch(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // If we are called then we can assume that MAXIS32r6/MAXIS64r6 is enabled + // (otherwise we would have matched the BGTZL instruction from the earlier + // ISA's instead). + // + // We have: + // 0b010111 sssss ttttt iiiiiiiiiiiiiiii + // Invalid if rs == 0 + // BGTZC if rs == 0 && rt != 0 + // BLTZC if rs == rt && rt != 0 + // BLTC if rs != rt && rs != 0 && rt != 0 + + bool HasRs = false; + + InsnType Rs = fieldFromInstruction(insn, 21, 5); + InsnType Rt = fieldFromInstruction(insn, 16, 5); + int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + + if (Rt == 0) + return MCDisassembler::Fail; + else if (Rs == 0) + MI.setOpcode(Maxis::BGTZC); + else if (Rs == Rt) + MI.setOpcode(Maxis::BLTZC); + else { + MI.setOpcode(Maxis::BLTC); + HasRs = true; + } + + if (HasRs) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeBgtzGroupBranch(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // If we are called then we can assume that MAXIS32r6/MAXIS64r6 is enabled + // (otherwise we would have matched the BGTZ instruction from the earlier + // ISA's instead). + // + // We have: + // 0b000111 sssss ttttt iiiiiiiiiiiiiiii + // BGTZ if rt == 0 + // BGTZALC if rs == 0 && rt != 0 + // BLTZALC if rs != 0 && rs == rt + // BLTUC if rs != 0 && rs != rt + + InsnType Rs = fieldFromInstruction(insn, 21, 5); + InsnType Rt = fieldFromInstruction(insn, 16, 5); + int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + bool HasRs = false; + bool HasRt = false; + + if (Rt == 0) { + MI.setOpcode(Maxis::BGTZ); + HasRs = true; + } else if (Rs == 0) { + MI.setOpcode(Maxis::BGTZALC); + HasRt = true; + } else if (Rs == Rt) { + MI.setOpcode(Maxis::BLTZALC); + HasRs = true; + } else { + MI.setOpcode(Maxis::BLTUC); + HasRs = true; + HasRt = true; + } + + if (HasRs) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + + if (HasRt) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeBlezGroupBranch(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // If we are called then we can assume that MAXIS32r6/MAXIS64r6 is enabled + // (otherwise we would have matched the BLEZL instruction from the earlier + // ISA's instead). + // + // We have: + // 0b000110 sssss ttttt iiiiiiiiiiiiiiii + // Invalid if rs == 0 + // BLEZALC if rs == 0 && rt != 0 + // BGEZALC if rs == rt && rt != 0 + // BGEUC if rs != rt && rs != 0 && rt != 0 + + InsnType Rs = fieldFromInstruction(insn, 21, 5); + InsnType Rt = fieldFromInstruction(insn, 16, 5); + int64_t Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + bool HasRs = false; + + if (Rt == 0) + return MCDisassembler::Fail; + else if (Rs == 0) + MI.setOpcode(Maxis::BLEZALC); + else if (Rs == Rt) + MI.setOpcode(Maxis::BGEZALC); + else { + HasRs = true; + MI.setOpcode(Maxis::BGEUC); + } + + if (HasRs) + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rs))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, + Rt))); + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +// Override the generated disassembler to produce DEXT all the time. This is +// for feature / behaviour parity with binutils. +template +static DecodeStatus DecodeDEXT(MCInst &MI, InsnType Insn, uint64_t Address, + const void *Decoder) { + unsigned Msbd = fieldFromInstruction(Insn, 11, 5); + unsigned Lsb = fieldFromInstruction(Insn, 6, 5); + unsigned Size = 0; + unsigned Pos = 0; + + switch (MI.getOpcode()) { + case Maxis::DEXT: + Pos = Lsb; + Size = Msbd + 1; + break; + case Maxis::DEXTM: + Pos = Lsb; + Size = Msbd + 1 + 32; + break; + case Maxis::DEXTU: + Pos = Lsb + 32; + Size = Msbd + 1; + break; + default: + llvm_unreachable("Unknown DEXT instruction!"); + } + + MI.setOpcode(Maxis::DEXT); + + InsnType Rs = fieldFromInstruction(Insn, 21, 5); + InsnType Rt = fieldFromInstruction(Insn, 16, 5); + + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR64RegClassID, Rt))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR64RegClassID, Rs))); + MI.addOperand(MCOperand::createImm(Pos)); + MI.addOperand(MCOperand::createImm(Size)); + + return MCDisassembler::Success; +} + +// Override the generated disassembler to produce DINS all the time. This is +// for feature / behaviour parity with binutils. +template +static DecodeStatus DecodeDINS(MCInst &MI, InsnType Insn, uint64_t Address, + const void *Decoder) { + unsigned Msbd = fieldFromInstruction(Insn, 11, 5); + unsigned Lsb = fieldFromInstruction(Insn, 6, 5); + unsigned Size = 0; + unsigned Pos = 0; + + switch (MI.getOpcode()) { + case Maxis::DINS: + Pos = Lsb; + Size = Msbd + 1 - Pos; + break; + case Maxis::DINSM: + Pos = Lsb; + Size = Msbd + 33 - Pos; + break; + case Maxis::DINSU: + Pos = Lsb + 32; + // mbsd = pos + size - 33 + // mbsd - pos + 33 = size + Size = Msbd + 33 - Pos; + break; + default: + llvm_unreachable("Unknown DINS instruction!"); + } + + InsnType Rs = fieldFromInstruction(Insn, 21, 5); + InsnType Rt = fieldFromInstruction(Insn, 16, 5); + + MI.setOpcode(Maxis::DINS); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR64RegClassID, Rt))); + MI.addOperand(MCOperand::createReg(getReg(Decoder, Maxis::GPR64RegClassID, Rs))); + MI.addOperand(MCOperand::createImm(Pos)); + MI.addOperand(MCOperand::createImm(Size)); + + return MCDisassembler::Success; +} +/// Read two bytes from the ArrayRef and return 16 bit halfword sorted +/// according to the given endianness. +static DecodeStatus readInstruction16(ArrayRef Bytes, uint64_t Address, + uint64_t &Size, uint32_t &Insn, + bool IsBigEndian) { + // We want to read exactly 2 Bytes of data. + if (Bytes.size() < 2) { + Size = 0; + return MCDisassembler::Fail; + } + + if (IsBigEndian) { + Insn = (Bytes[0] << 8) | Bytes[1]; + } else { + Insn = (Bytes[1] << 8) | Bytes[0]; + } + + return MCDisassembler::Success; +} + +/// Read four bytes from the ArrayRef and return 32 bit word sorted +/// according to the given endianness. +static DecodeStatus readInstruction32(ArrayRef Bytes, uint64_t Address, + uint64_t &Size, uint32_t &Insn, + bool IsBigEndian, bool IsMicroMaxis) { + // We want to read exactly 4 Bytes of data. + if (Bytes.size() < 4) { + Size = 0; + return MCDisassembler::Fail; + } + + // High 16 bits of a 32-bit microMAXIS instruction (where the opcode is) + // always precede the low 16 bits in the instruction stream (that is, they + // are placed at lower addresses in the instruction stream). + // + // microMAXIS byte ordering: + // Big-endian: 0 | 1 | 2 | 3 + // Little-endian: 1 | 0 | 3 | 2 + + if (IsBigEndian) { + // Encoded as a big-endian 32-bit word in the stream. + Insn = + (Bytes[3] << 0) | (Bytes[2] << 8) | (Bytes[1] << 16) | (Bytes[0] << 24); + } else { + if (IsMicroMaxis) { + Insn = (Bytes[2] << 0) | (Bytes[3] << 8) | (Bytes[0] << 16) | + (Bytes[1] << 24); + } else { + Insn = (Bytes[0] << 0) | (Bytes[1] << 8) | (Bytes[2] << 16) | + (Bytes[3] << 24); + } + } + + return MCDisassembler::Success; +} + +DecodeStatus MaxisDisassembler::getInstruction(MCInst &Instr, uint64_t &Size, + ArrayRef Bytes, + uint64_t Address, + raw_ostream &VStream, + raw_ostream &CStream) const { + uint32_t Insn; + DecodeStatus Result; + Size = 0; + + if (IsMicroMaxis) { + Result = readInstruction16(Bytes, Address, Size, Insn, IsBigEndian); + if (Result == MCDisassembler::Fail) + return MCDisassembler::Fail; + + if (hasMaxis32r6()) { + DEBUG(dbgs() << "Trying MicroMaxisR616 table (16-bit instructions):\n"); + // Calling the auto-generated decoder function for microMAXIS32R6 + // 16-bit instructions. + Result = decodeInstruction(DecoderTableMicroMaxisR616, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) { + Size = 2; + return Result; + } + } + + DEBUG(dbgs() << "Trying MicroMaxis16 table (16-bit instructions):\n"); + // Calling the auto-generated decoder function for microMAXIS 16-bit + // instructions. + Result = decodeInstruction(DecoderTableMicroMaxis16, Instr, Insn, Address, + this, STI); + if (Result != MCDisassembler::Fail) { + Size = 2; + return Result; + } + + Result = readInstruction32(Bytes, Address, Size, Insn, IsBigEndian, true); + if (Result == MCDisassembler::Fail) + return MCDisassembler::Fail; + + if (hasMaxis32r6()) { + DEBUG(dbgs() << "Trying MicroMaxis32r632 table (32-bit instructions):\n"); + // Calling the auto-generated decoder function. + Result = decodeInstruction(DecoderTableMicroMaxisR632, Instr, Insn, Address, + this, STI); + if (Result != MCDisassembler::Fail) { + Size = 4; + return Result; + } + } + + DEBUG(dbgs() << "Trying MicroMaxis32 table (32-bit instructions):\n"); + // Calling the auto-generated decoder function. + Result = decodeInstruction(DecoderTableMicroMaxis32, Instr, Insn, Address, + this, STI); + if (Result != MCDisassembler::Fail) { + Size = 4; + return Result; + } + + if (isFP64()) { + DEBUG(dbgs() << "Trying MicroMaxisFP64 table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableMicroMaxisFP6432, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) { + Size = 4; + return Result; + } + } + + // This is an invalid instruction. Claim that the Size is 2 bytes. Since + // microMAXIS instructions have a minimum alignment of 2, the next 2 bytes + // could form a valid instruction. The two bytes we rejected as an + // instruction could have actually beeen an inline constant pool that is + // unconditionally branched over. + Size = 2; + return MCDisassembler::Fail; + } + + // Attempt to read the instruction so that we can attempt to decode it. If + // the buffer is not 4 bytes long, let the higher level logic figure out + // what to do with a size of zero and MCDisassembler::Fail. + Result = readInstruction32(Bytes, Address, Size, Insn, IsBigEndian, false); + if (Result == MCDisassembler::Fail) + return MCDisassembler::Fail; + + // The only instruction size for standard encoded MAXIS. + Size = 4; + + if (hasCOP3()) { + DEBUG(dbgs() << "Trying COP3_ table (32-bit opcodes):\n"); + Result = + decodeInstruction(DecoderTableCOP3_32, Instr, Insn, Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + if (hasMaxis32r6() && isGP64()) { + DEBUG(dbgs() << "Trying Maxis32r6_64r6 (GPR64) table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableMaxis32r6_64r6_GP6432, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + if (hasMaxis32r6() && isPTR64()) { + DEBUG(dbgs() << "Trying Maxis32r6_64r6 (PTR64) table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableMaxis32r6_64r6_PTR6432, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + if (hasMaxis32r6()) { + DEBUG(dbgs() << "Trying Maxis32r6_64r6 table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableMaxis32r6_64r632, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + if (hasMaxis2() && isPTR64()) { + DEBUG(dbgs() << "Trying Maxis32r6_64r6 (PTR64) table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableMaxis32_64_PTR6432, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + if (hasCnMaxis()) { + DEBUG(dbgs() << "Trying CnMaxis table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableCnMaxis32, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + if (isGP64()) { + DEBUG(dbgs() << "Trying Maxis64 (GPR64) table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableMaxis6432, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + if (isFP64()) { + DEBUG(dbgs() << "Trying MaxisFP64 (64 bit FPU) table (32-bit opcodes):\n"); + Result = decodeInstruction(DecoderTableMaxisFP6432, Instr, Insn, + Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + } + + DEBUG(dbgs() << "Trying Maxis table (32-bit opcodes):\n"); + // Calling the auto-generated decoder function. + Result = + decodeInstruction(DecoderTableMaxis32, Instr, Insn, Address, this, STI); + if (Result != MCDisassembler::Fail) + return Result; + + return MCDisassembler::Fail; +} + +static DecodeStatus DecodeCPU16RegsRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + return MCDisassembler::Fail; +} + +static DecodeStatus DecodeGPR64RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::GPR64RegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeGPRMM16RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 7) + return MCDisassembler::Fail; + unsigned Reg = getReg(Decoder, Maxis::GPRMM16RegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeGPRMM16ZeroRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 7) + return MCDisassembler::Fail; + unsigned Reg = getReg(Decoder, Maxis::GPRMM16ZeroRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeGPRMM16MovePRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 7) + return MCDisassembler::Fail; + unsigned Reg = getReg(Decoder, Maxis::GPRMM16MovePRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeGPR32RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + unsigned Reg = getReg(Decoder, Maxis::GPR32RegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodePtrRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (static_cast(Decoder)->isGP64()) + return DecodeGPR64RegisterClass(Inst, RegNo, Address, Decoder); + + return DecodeGPR32RegisterClass(Inst, RegNo, Address, Decoder); +} + +static DecodeStatus DecodeDSPRRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + return DecodeGPR32RegisterClass(Inst, RegNo, Address, Decoder); +} + +static DecodeStatus DecodeFGR64RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::FGR64RegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFGR32RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::FGR32RegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeCCRRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + unsigned Reg = getReg(Decoder, Maxis::CCRRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFCCRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 7) + return MCDisassembler::Fail; + unsigned Reg = getReg(Decoder, Maxis::FCCRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFGRCCRegisterClass(MCInst &Inst, unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::FGRCCRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMem(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Reg = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + if (Inst.getOpcode() == Maxis::SC || + Inst.getOpcode() == Maxis::SCD) + Inst.addOperand(MCOperand::createReg(Reg)); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMemEVA(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<9>(Insn >> 7); + unsigned Reg = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + if (Inst.getOpcode() == Maxis::SCE) + Inst.addOperand(MCOperand::createReg(Reg)); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeLoadByte9(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<9>(Insn & 0x1ff); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + unsigned Reg = fieldFromInstruction(Insn, 21, 5); + + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeLoadByte15(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + unsigned Reg = fieldFromInstruction(Insn, 21, 5); + + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeCacheOp(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Hint = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + Inst.addOperand(MCOperand::createImm(Hint)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeCacheOpMM(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<12>(Insn & 0xfff); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + unsigned Hint = fieldFromInstruction(Insn, 21, 5); + + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + Inst.addOperand(MCOperand::createImm(Hint)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodePrefeOpMM(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<9>(Insn & 0x1ff); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + unsigned Hint = fieldFromInstruction(Insn, 21, 5); + + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + Inst.addOperand(MCOperand::createImm(Hint)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeCacheeOp_CacheOpR6(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<9>(Insn >> 7); + unsigned Hint = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + Inst.addOperand(MCOperand::createImm(Hint)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeStoreEvaOpMM(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<9>(Insn & 0x1ff); + unsigned Reg = fieldFromInstruction(Insn, 21, 5); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeSyncI(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeSynciR6(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Immediate = SignExtend32<16>(Insn & 0xffff); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Immediate)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMSA128Mem(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + int Offset = SignExtend32<10>(fieldFromInstruction(Insn, 16, 10)); + unsigned Reg = fieldFromInstruction(Insn, 6, 5); + unsigned Base = fieldFromInstruction(Insn, 11, 5); + + Reg = getReg(Decoder, Maxis::MSA128BRegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + + // The immediate field of an LD/ST instruction is scaled which means it must + // be multiplied (when decoding) by the size (in bytes) of the instructions' + // data format. + // .b - 1 byte + // .h - 2 bytes + // .w - 4 bytes + // .d - 8 bytes + switch(Inst.getOpcode()) + { + default: + assert(false && "Unexpected instruction"); + return MCDisassembler::Fail; + break; + case Maxis::LD_B: + case Maxis::ST_B: + Inst.addOperand(MCOperand::createImm(Offset)); + break; + case Maxis::LD_H: + case Maxis::ST_H: + Inst.addOperand(MCOperand::createImm(Offset * 2)); + break; + case Maxis::LD_W: + case Maxis::ST_W: + Inst.addOperand(MCOperand::createImm(Offset * 4)); + break; + case Maxis::LD_D: + case Maxis::ST_D: + Inst.addOperand(MCOperand::createImm(Offset * 8)); + break; + } + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMemMMImm4(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + unsigned Offset = Insn & 0xf; + unsigned Reg = fieldFromInstruction(Insn, 7, 3); + unsigned Base = fieldFromInstruction(Insn, 4, 3); + + switch (Inst.getOpcode()) { + case Maxis::LBU16_MM: + case Maxis::LHU16_MM: + case Maxis::LW16_MM: + if (DecodeGPRMM16RegisterClass(Inst, Reg, Address, Decoder) + == MCDisassembler::Fail) + return MCDisassembler::Fail; + break; + case Maxis::SB16_MM: + case Maxis::SB16_MMR6: + case Maxis::SH16_MM: + case Maxis::SH16_MMR6: + case Maxis::SW16_MM: + case Maxis::SW16_MMR6: + if (DecodeGPRMM16ZeroRegisterClass(Inst, Reg, Address, Decoder) + == MCDisassembler::Fail) + return MCDisassembler::Fail; + break; + } + + if (DecodeGPRMM16RegisterClass(Inst, Base, Address, Decoder) + == MCDisassembler::Fail) + return MCDisassembler::Fail; + + switch (Inst.getOpcode()) { + case Maxis::LBU16_MM: + if (Offset == 0xf) + Inst.addOperand(MCOperand::createImm(-1)); + else + Inst.addOperand(MCOperand::createImm(Offset)); + break; + case Maxis::SB16_MM: + case Maxis::SB16_MMR6: + Inst.addOperand(MCOperand::createImm(Offset)); + break; + case Maxis::LHU16_MM: + case Maxis::SH16_MM: + case Maxis::SH16_MMR6: + Inst.addOperand(MCOperand::createImm(Offset << 1)); + break; + case Maxis::LW16_MM: + case Maxis::SW16_MM: + case Maxis::SW16_MMR6: + Inst.addOperand(MCOperand::createImm(Offset << 2)); + break; + } + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMemMMSPImm5Lsl2(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + unsigned Offset = Insn & 0x1F; + unsigned Reg = fieldFromInstruction(Insn, 5, 5); + + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Maxis::SP)); + Inst.addOperand(MCOperand::createImm(Offset << 2)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMemMMGPImm7Lsl2(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + unsigned Offset = Insn & 0x7F; + unsigned Reg = fieldFromInstruction(Insn, 7, 3); + + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Maxis::GP)); + Inst.addOperand(MCOperand::createImm(Offset << 2)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMemMMReglistImm4Lsl2(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset; + switch (Inst.getOpcode()) { + case Maxis::LWM16_MMR6: + case Maxis::SWM16_MMR6: + Offset = fieldFromInstruction(Insn, 4, 4); + break; + default: + Offset = SignExtend32<4>(Insn & 0xf); + break; + } + + if (DecodeRegListOperand16(Inst, Insn, Address, Decoder) + == MCDisassembler::Fail) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createReg(Maxis::SP)); + Inst.addOperand(MCOperand::createImm(Offset << 2)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMemMMImm9(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<9>(Insn & 0x1ff); + unsigned Reg = fieldFromInstruction(Insn, 21, 5); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + if (Inst.getOpcode() == Maxis::SCE_MM) + Inst.addOperand(MCOperand::createReg(Reg)); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMemMMImm12(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<12>(Insn & 0x0fff); + unsigned Reg = fieldFromInstruction(Insn, 21, 5); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + switch (Inst.getOpcode()) { + case Maxis::SWM32_MM: + case Maxis::LWM32_MM: + if (DecodeRegListOperand(Inst, Insn, Address, Decoder) + == MCDisassembler::Fail) + return MCDisassembler::Fail; + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + break; + case Maxis::SC_MM: + Inst.addOperand(MCOperand::createReg(Reg)); + LLVM_FALLTHROUGH; + default: + Inst.addOperand(MCOperand::createReg(Reg)); + if (Inst.getOpcode() == Maxis::LWP_MM || Inst.getOpcode() == Maxis::SWP_MM || + Inst.getOpcode() == Maxis::LWP_MMR6 || Inst.getOpcode() == Maxis::SWP_MMR6) + Inst.addOperand(MCOperand::createReg(Reg+1)); + + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + } + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMemMMImm16(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Reg = fieldFromInstruction(Insn, 21, 5); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + + Reg = getReg(Decoder, Maxis::GPR32RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFMem(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Reg = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Reg = getReg(Decoder, Maxis::FGR64RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFMemMMR2(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + // This function is the same as DecodeFMem but with the Reg and Base fields + // swapped according to microMAXIS spec. + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + unsigned Reg = fieldFromInstruction(Insn, 21, 5); + + Reg = getReg(Decoder, Maxis::FGR64RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFMem2(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Reg = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Reg = getReg(Decoder, Maxis::COP2RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFMem3(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<16>(Insn & 0xffff); + unsigned Reg = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Reg = getReg(Decoder, Maxis::COP3RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFMemCop2R6(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int Offset = SignExtend32<11>(Insn & 0x07ff); + unsigned Reg = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 11, 5); + + Reg = getReg(Decoder, Maxis::COP2RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeFMemCop2MMR6(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + int Offset = SignExtend32<11>(Insn & 0x07ff); + unsigned Reg = fieldFromInstruction(Insn, 21, 5); + unsigned Base = fieldFromInstruction(Insn, 16, 5); + + Reg = getReg(Decoder, Maxis::COP2RegClassID, Reg); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + Inst.addOperand(MCOperand::createReg(Reg)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeSpecial3LlSc(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + int64_t Offset = SignExtend64<9>((Insn >> 7) & 0x1ff); + unsigned Rt = fieldFromInstruction(Insn, 16, 5); + unsigned Base = fieldFromInstruction(Insn, 21, 5); + + Rt = getReg(Decoder, Maxis::GPR32RegClassID, Rt); + Base = getReg(Decoder, Maxis::GPR32RegClassID, Base); + + if(Inst.getOpcode() == Maxis::SC_R6 || Inst.getOpcode() == Maxis::SCD_R6){ + Inst.addOperand(MCOperand::createReg(Rt)); + } + + Inst.addOperand(MCOperand::createReg(Rt)); + Inst.addOperand(MCOperand::createReg(Base)); + Inst.addOperand(MCOperand::createImm(Offset)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeHWRegsRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + // Currently only hardware register 29 is supported. + if (RegNo != 29) + return MCDisassembler::Fail; + Inst.addOperand(MCOperand::createReg(Maxis::HWR29)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeAFGR64RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 30 || RegNo %2) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::AFGR64RegClassID, RegNo /2); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeACC64DSPRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= 4) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::ACC64DSPRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeHI32DSPRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= 4) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::HI32DSPRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeLO32DSPRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo >= 4) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::LO32DSPRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMSA128BRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::MSA128BRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMSA128HRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::MSA128HRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMSA128WRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::MSA128WRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMSA128DRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::MSA128DRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMSACtrlRegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 7) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::MSACtrlRegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeCOP0RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::COP0RegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeCOP2RegisterClass(MCInst &Inst, + unsigned RegNo, + uint64_t Address, + const void *Decoder) { + if (RegNo > 31) + return MCDisassembler::Fail; + + unsigned Reg = getReg(Decoder, Maxis::COP2RegClassID, RegNo); + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTarget(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = (SignExtend32<16>(Offset) * 4) + 4; + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTarget1SImm16(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = (SignExtend32<16>(Offset) * 2); + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeJumpTarget(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + unsigned JumpOffset = fieldFromInstruction(Insn, 0, 26) << 2; + Inst.addOperand(MCOperand::createImm(JumpOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTarget21(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = SignExtend32<21>(Offset) * 4 + 4; + + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTarget21MM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = SignExtend32<21>(Offset) * 4 + 4; + + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTarget26(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = SignExtend32<26>(Offset) * 4 + 4; + + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTarget7MM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = SignExtend32<8>(Offset << 1); + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTarget10MM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = SignExtend32<11>(Offset << 1); + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTargetMM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = SignExtend32<16>(Offset) * 2 + 4; + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeBranchTarget26MM(MCInst &Inst, + unsigned Offset, + uint64_t Address, + const void *Decoder) { + int32_t BranchOffset = SignExtend32<27>(Offset << 1); + + Inst.addOperand(MCOperand::createImm(BranchOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeJumpTargetMM(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + unsigned JumpOffset = fieldFromInstruction(Insn, 0, 26) << 1; + Inst.addOperand(MCOperand::createImm(JumpOffset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeAddiur2Simm7(MCInst &Inst, + unsigned Value, + uint64_t Address, + const void *Decoder) { + if (Value == 0) + Inst.addOperand(MCOperand::createImm(1)); + else if (Value == 0x7) + Inst.addOperand(MCOperand::createImm(-1)); + else + Inst.addOperand(MCOperand::createImm(Value << 2)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeLi16Imm(MCInst &Inst, + unsigned Value, + uint64_t Address, + const void *Decoder) { + if (Value == 0x7F) + Inst.addOperand(MCOperand::createImm(-1)); + else + Inst.addOperand(MCOperand::createImm(Value)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodePOOL16BEncodedField(MCInst &Inst, + unsigned Value, + uint64_t Address, + const void *Decoder) { + Inst.addOperand(MCOperand::createImm(Value == 0x0 ? 8 : Value)); + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeUImmWithOffsetAndScale(MCInst &Inst, unsigned Value, + uint64_t Address, + const void *Decoder) { + Value &= ((1 << Bits) - 1); + Value *= Scale; + Inst.addOperand(MCOperand::createImm(Value + Offset)); + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeSImmWithOffsetAndScale(MCInst &Inst, unsigned Value, + uint64_t Address, + const void *Decoder) { + int32_t Imm = SignExtend32(Value) * ScaleBy; + Inst.addOperand(MCOperand::createImm(Imm + Offset)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeInsSize(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + // First we need to grab the pos(lsb) from MCInst. + // This function only handles the 32 bit variants of ins, as dins + // variants are handled differently. + int Pos = Inst.getOperand(2).getImm(); + int Size = (int) Insn - Pos + 1; + Inst.addOperand(MCOperand::createImm(SignExtend32<16>(Size))); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeSimm19Lsl2(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + Inst.addOperand(MCOperand::createImm(SignExtend32<19>(Insn) * 4)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeSimm18Lsl3(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + Inst.addOperand(MCOperand::createImm(SignExtend32<18>(Insn) * 8)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeSimm9SP(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + int32_t DecodedValue; + switch (Insn) { + case 0: DecodedValue = 256; break; + case 1: DecodedValue = 257; break; + case 510: DecodedValue = -258; break; + case 511: DecodedValue = -257; break; + default: DecodedValue = SignExtend32<9>(Insn); break; + } + Inst.addOperand(MCOperand::createImm(DecodedValue * 4)); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeANDI16Imm(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + // Insn must be >= 0, since it is unsigned that condition is always true. + assert(Insn < 16); + int32_t DecodedValues[] = {128, 1, 2, 3, 4, 7, 8, 15, 16, 31, 32, 63, 64, + 255, 32768, 65535}; + Inst.addOperand(MCOperand::createImm(DecodedValues[Insn])); + return MCDisassembler::Success; +} + +static DecodeStatus DecodeRegListOperand(MCInst &Inst, + unsigned Insn, + uint64_t Address, + const void *Decoder) { + unsigned Regs[] = {Maxis::S0, Maxis::S1, Maxis::S2, Maxis::S3, Maxis::S4, Maxis::S5, + Maxis::S6, Maxis::S7, Maxis::FP}; + unsigned RegNum; + + unsigned RegLst = fieldFromInstruction(Insn, 21, 5); + + // Empty register lists are not allowed. + if (RegLst == 0) + return MCDisassembler::Fail; + + RegNum = RegLst & 0xf; + + // RegLst values 10-15, and 26-31 are reserved. + if (RegNum > 9) + return MCDisassembler::Fail; + + for (unsigned i = 0; i < RegNum; i++) + Inst.addOperand(MCOperand::createReg(Regs[i])); + + if (RegLst & 0x10) + Inst.addOperand(MCOperand::createReg(Maxis::RA)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeRegListOperand16(MCInst &Inst, unsigned Insn, + uint64_t Address, + const void *Decoder) { + unsigned Regs[] = {Maxis::S0, Maxis::S1, Maxis::S2, Maxis::S3}; + unsigned RegLst; + switch(Inst.getOpcode()) { + default: + RegLst = fieldFromInstruction(Insn, 4, 2); + break; + case Maxis::LWM16_MMR6: + case Maxis::SWM16_MMR6: + RegLst = fieldFromInstruction(Insn, 8, 2); + break; + } + unsigned RegNum = RegLst & 0x3; + + for (unsigned i = 0; i <= RegNum; i++) + Inst.addOperand(MCOperand::createReg(Regs[i])); + + Inst.addOperand(MCOperand::createReg(Maxis::RA)); + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeMovePRegPair(MCInst &Inst, unsigned RegPair, + uint64_t Address, const void *Decoder) { + switch (RegPair) { + default: + return MCDisassembler::Fail; + case 0: + Inst.addOperand(MCOperand::createReg(Maxis::A1)); + Inst.addOperand(MCOperand::createReg(Maxis::A2)); + break; + case 1: + Inst.addOperand(MCOperand::createReg(Maxis::A1)); + Inst.addOperand(MCOperand::createReg(Maxis::A3)); + break; + case 2: + Inst.addOperand(MCOperand::createReg(Maxis::A2)); + Inst.addOperand(MCOperand::createReg(Maxis::A3)); + break; + case 3: + Inst.addOperand(MCOperand::createReg(Maxis::A0)); + Inst.addOperand(MCOperand::createReg(Maxis::S5)); + break; + case 4: + Inst.addOperand(MCOperand::createReg(Maxis::A0)); + Inst.addOperand(MCOperand::createReg(Maxis::S6)); + break; + case 5: + Inst.addOperand(MCOperand::createReg(Maxis::A0)); + Inst.addOperand(MCOperand::createReg(Maxis::A1)); + break; + case 6: + Inst.addOperand(MCOperand::createReg(Maxis::A0)); + Inst.addOperand(MCOperand::createReg(Maxis::A2)); + break; + case 7: + Inst.addOperand(MCOperand::createReg(Maxis::A0)); + Inst.addOperand(MCOperand::createReg(Maxis::A3)); + break; + } + + return MCDisassembler::Success; +} + +static DecodeStatus DecodeSimm23Lsl2(MCInst &Inst, unsigned Insn, + uint64_t Address, const void *Decoder) { + Inst.addOperand(MCOperand::createImm(SignExtend32<25>(Insn << 2))); + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeBgtzGroupBranchMMR6(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // We have: + // 0b000111 ttttt sssss iiiiiiiiiiiiiiii + // Invalid if rt == 0 + // BGTZALC_MMR6 if rs == 0 && rt != 0 + // BLTZALC_MMR6 if rs != 0 && rs == rt + // BLTUC_MMR6 if rs != 0 && rs != rt + + InsnType Rt = fieldFromInstruction(insn, 21, 5); + InsnType Rs = fieldFromInstruction(insn, 16, 5); + InsnType Imm = 0; + bool HasRs = false; + bool HasRt = false; + + if (Rt == 0) + return MCDisassembler::Fail; + else if (Rs == 0) { + MI.setOpcode(Maxis::BGTZALC_MMR6); + HasRt = true; + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; + } + else if (Rs == Rt) { + MI.setOpcode(Maxis::BLTZALC_MMR6); + HasRs = true; + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; + } + else { + MI.setOpcode(Maxis::BLTUC_MMR6); + HasRs = true; + HasRt = true; + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + } + + if (HasRs) + MI.addOperand( + MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, Rs))); + + if (HasRt) + MI.addOperand( + MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, Rt))); + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} + +template +static DecodeStatus DecodeBlezGroupBranchMMR6(MCInst &MI, InsnType insn, + uint64_t Address, + const void *Decoder) { + // We have: + // 0b000110 ttttt sssss iiiiiiiiiiiiiiii + // Invalid if rt == 0 + // BLEZALC_MMR6 if rs == 0 && rt != 0 + // BGEZALC_MMR6 if rs == rt && rt != 0 + // BGEUC_MMR6 if rs != rt && rs != 0 && rt != 0 + + InsnType Rt = fieldFromInstruction(insn, 21, 5); + InsnType Rs = fieldFromInstruction(insn, 16, 5); + InsnType Imm = 0; + bool HasRs = false; + + if (Rt == 0) + return MCDisassembler::Fail; + else if (Rs == 0) { + MI.setOpcode(Maxis::BLEZALC_MMR6); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; + } + else if (Rs == Rt) { + MI.setOpcode(Maxis::BGEZALC_MMR6); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 2 + 4; + } + else { + HasRs = true; + MI.setOpcode(Maxis::BGEUC_MMR6); + Imm = SignExtend64(fieldFromInstruction(insn, 0, 16), 16) * 4 + 4; + } + + if (HasRs) + MI.addOperand( + MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, Rs))); + MI.addOperand( + MCOperand::createReg(getReg(Decoder, Maxis::GPR32RegClassID, Rt))); + + MI.addOperand(MCOperand::createImm(Imm)); + + return MCDisassembler::Success; +} diff --git a/lib/Target/Maxis/InstPrinter/CMakeLists.txt b/lib/Target/Maxis/InstPrinter/CMakeLists.txt new file mode 100644 index 00000000..3d470be3 --- /dev/null +++ b/lib/Target/Maxis/InstPrinter/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMMaxisAsmPrinter + MaxisInstPrinter.cpp + ) diff --git a/lib/Target/Maxis/InstPrinter/LLVMBuild.txt b/lib/Target/Maxis/InstPrinter/LLVMBuild.txt new file mode 100644 index 00000000..2d72a139 --- /dev/null +++ b/lib/Target/Maxis/InstPrinter/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/Maxis/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 = MaxisAsmPrinter +parent = Maxis +required_libraries = MC Support +add_to_library_groups = Maxis diff --git a/lib/Target/Maxis/InstPrinter/MaxisInstPrinter.cpp b/lib/Target/Maxis/InstPrinter/MaxisInstPrinter.cpp new file mode 100644 index 00000000..52ff7c53 --- /dev/null +++ b/lib/Target/Maxis/InstPrinter/MaxisInstPrinter.cpp @@ -0,0 +1,293 @@ +//===-- MaxisInstPrinter.cpp - Convert Maxis MCInst to assembly syntax ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an Maxis MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "MaxisInstPrinter.h" +#include "MCTargetDesc/MaxisMCExpr.h" +#include "MaxisInstrInfo.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 "MaxisGenAsmWriter.inc" + +template +static bool isReg(const MCInst &MI, unsigned OpNo) { + assert(MI.getOperand(OpNo).isReg() && "Register operand expected."); + return MI.getOperand(OpNo).getReg() == R; +} + +const char* Maxis::MaxisFCCToString(Maxis::CondCode CC) { + switch (CC) { + case FCOND_F: + case FCOND_T: return "f"; + case FCOND_UN: + case FCOND_OR: return "un"; + case FCOND_OEQ: + case FCOND_UNE: return "eq"; + case FCOND_UEQ: + case FCOND_ONE: return "ueq"; + case FCOND_OLT: + case FCOND_UGE: return "olt"; + case FCOND_ULT: + case FCOND_OGE: return "ult"; + case FCOND_OLE: + case FCOND_UGT: return "ole"; + case FCOND_ULE: + case FCOND_OGT: return "ule"; + case FCOND_SF: + case FCOND_ST: return "sf"; + case FCOND_NGLE: + case FCOND_GLE: return "ngle"; + case FCOND_SEQ: + case FCOND_SNE: return "seq"; + case FCOND_NGL: + case FCOND_GL: return "ngl"; + case FCOND_LT: + case FCOND_NLT: return "lt"; + case FCOND_NGE: + case FCOND_GE: return "nge"; + case FCOND_LE: + case FCOND_NLE: return "le"; + case FCOND_NGT: + case FCOND_GT: return "ngt"; + } + llvm_unreachable("Impossible condition code!"); +} + +void MaxisInstPrinter::printRegName(raw_ostream &OS, unsigned RegNo) const { + OS << '$' << StringRef(getRegisterName(RegNo)).lower(); +} + +void MaxisInstPrinter::printInst(const MCInst *MI, raw_ostream &O, + StringRef Annot, const MCSubtargetInfo &STI) { + switch (MI->getOpcode()) { + default: + break; + case Maxis::RDHWR: + case Maxis::RDHWR64: + O << "\t.set\tpush\n"; + O << "\t.set\tmaxis32r2\n"; + break; + case Maxis::Save16: + O << "\tsave\t"; + printSaveRestore(MI, O); + O << " # 16 bit inst\n"; + return; + case Maxis::SaveX16: + O << "\tsave\t"; + printSaveRestore(MI, O); + O << "\n"; + return; + case Maxis::Restore16: + O << "\trestore\t"; + printSaveRestore(MI, O); + O << " # 16 bit inst\n"; + return; + case Maxis::RestoreX16: + O << "\trestore\t"; + printSaveRestore(MI, O); + O << "\n"; + return; + } + + // Try to print any aliases first. + if (!printAliasInstr(MI, O) && !printAlias(*MI, O)) + printInstruction(MI, O); + printAnnotation(O, Annot); + + switch (MI->getOpcode()) { + default: + break; + case Maxis::RDHWR: + case Maxis::RDHWR64: + O << "\n\t.set\tpop"; + } +} + +void MaxisInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &O) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isReg()) { + printRegName(O, Op.getReg()); + return; + } + + if (Op.isImm()) { + O << formatImm(Op.getImm()); + return; + } + + assert(Op.isExpr() && "unknown operand kind in printOperand"); + Op.getExpr()->print(O, &MAI, true); +} + +template +void MaxisInstPrinter::printUImm(const MCInst *MI, int opNum, raw_ostream &O) { + const MCOperand &MO = MI->getOperand(opNum); + if (MO.isImm()) { + uint64_t Imm = MO.getImm(); + Imm -= Offset; + Imm &= (1 << Bits) - 1; + Imm += Offset; + O << formatImm(Imm); + return; + } + + printOperand(MI, opNum, O); +} + +void MaxisInstPrinter:: +printMemOperand(const MCInst *MI, int opNum, raw_ostream &O) { + // Load/Store memory operands -- imm($reg) + // If PIC target the target is loaded as the + // pattern lw $25,%call16($28) + + // opNum can be invalid if instruction had reglist as operand. + // MemOperand is always last operand of instruction (base + offset). + switch (MI->getOpcode()) { + default: + break; + case Maxis::SWM32_MM: + case Maxis::LWM32_MM: + case Maxis::SWM16_MM: + case Maxis::SWM16_MMR6: + case Maxis::LWM16_MM: + case Maxis::LWM16_MMR6: + opNum = MI->getNumOperands() - 2; + break; + } + + printOperand(MI, opNum+1, O); + O << "("; + printOperand(MI, opNum, O); + O << ")"; +} + +void MaxisInstPrinter:: +printMemOperandEA(const MCInst *MI, int opNum, raw_ostream &O) { + // when using stack locations for not load/store instructions + // print the same way as all normal 3 operand instructions. + printOperand(MI, opNum, O); + O << ", "; + printOperand(MI, opNum+1, O); +} + +void MaxisInstPrinter:: +printFCCOperand(const MCInst *MI, int opNum, raw_ostream &O) { + const MCOperand& MO = MI->getOperand(opNum); + O << MaxisFCCToString((Maxis::CondCode)MO.getImm()); +} + +void MaxisInstPrinter:: +printRegisterPair(const MCInst *MI, int opNum, raw_ostream &O) { + printRegName(O, MI->getOperand(opNum).getReg()); +} + +void MaxisInstPrinter:: +printSHFMask(const MCInst *MI, int opNum, raw_ostream &O) { + llvm_unreachable("TODO"); +} + +bool MaxisInstPrinter::printAlias(const char *Str, const MCInst &MI, + unsigned OpNo, raw_ostream &OS) { + OS << "\t" << Str << "\t"; + printOperand(&MI, OpNo, OS); + return true; +} + +bool MaxisInstPrinter::printAlias(const char *Str, const MCInst &MI, + unsigned OpNo0, unsigned OpNo1, + raw_ostream &OS) { + printAlias(Str, MI, OpNo0, OS); + OS << ", "; + printOperand(&MI, OpNo1, OS); + return true; +} + +bool MaxisInstPrinter::printAlias(const MCInst &MI, raw_ostream &OS) { + switch (MI.getOpcode()) { + case Maxis::BEQ: + case Maxis::BEQ_MM: + // beq $zero, $zero, $L2 => b $L2 + // beq $r0, $zero, $L2 => beqz $r0, $L2 + return (isReg(MI, 0) && isReg(MI, 1) && + printAlias("b", MI, 2, OS)) || + (isReg(MI, 1) && printAlias("beqz", MI, 0, 2, OS)); + case Maxis::BEQ64: + // beq $r0, $zero, $L2 => beqz $r0, $L2 + return isReg(MI, 1) && printAlias("beqz", MI, 0, 2, OS); + case Maxis::BNE: + case Maxis::BNE_MM: + // bne $r0, $zero, $L2 => bnez $r0, $L2 + return isReg(MI, 1) && printAlias("bnez", MI, 0, 2, OS); + case Maxis::BNE64: + // bne $r0, $zero, $L2 => bnez $r0, $L2 + return isReg(MI, 1) && printAlias("bnez", MI, 0, 2, OS); + case Maxis::BGEZAL: + // bgezal $zero, $L1 => bal $L1 + return isReg(MI, 0) && printAlias("bal", MI, 1, OS); + case Maxis::BC1T: + // bc1t $fcc0, $L1 => bc1t $L1 + return isReg(MI, 0) && printAlias("bc1t", MI, 1, OS); + case Maxis::BC1F: + // bc1f $fcc0, $L1 => bc1f $L1 + return isReg(MI, 0) && printAlias("bc1f", MI, 1, OS); + case Maxis::JALR: + // jalr $ra, $r1 => jalr $r1 + return isReg(MI, 0) && printAlias("jalr", MI, 1, OS); + case Maxis::JALR64: + // jalr $ra, $r1 => jalr $r1 + return isReg(MI, 0) && printAlias("jalr", MI, 1, OS); + case Maxis::NOR: + case Maxis::NOR_MM: + case Maxis::NOR_MMR6: + // nor $r0, $r1, $zero => not $r0, $r1 + return isReg(MI, 2) && printAlias("not", MI, 0, 1, OS); + case Maxis::NOR64: + // nor $r0, $r1, $zero => not $r0, $r1 + return isReg(MI, 2) && printAlias("not", MI, 0, 1, OS); + case Maxis::OR: + // or $r0, $r1, $zero => move $r0, $r1 + return isReg(MI, 2) && printAlias("move", MI, 0, 1, OS); + default: return false; + } +} + +void MaxisInstPrinter::printSaveRestore(const MCInst *MI, raw_ostream &O) { + for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { + if (i != 0) O << ", "; + if (MI->getOperand(i).isReg()) + printRegName(O, MI->getOperand(i).getReg()); + else + printUImm<16>(MI, i, O); + } +} + +void MaxisInstPrinter:: +printRegisterList(const MCInst *MI, int opNum, raw_ostream &O) { + // - 2 because register List is always first operand of instruction and it is + // always followed by memory operand (base + offset). + for (int i = opNum, e = MI->getNumOperands() - 2; i != e; ++i) { + if (i != opNum) + O << ", "; + printRegName(O, MI->getOperand(i).getReg()); + } +} diff --git a/lib/Target/Maxis/InstPrinter/MaxisInstPrinter.h b/lib/Target/Maxis/InstPrinter/MaxisInstPrinter.h new file mode 100644 index 00000000..f9058d55 --- /dev/null +++ b/lib/Target/Maxis/InstPrinter/MaxisInstPrinter.h @@ -0,0 +1,114 @@ +//=== MaxisInstPrinter.h - Convert Maxis MCInst to assembly syntax -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints a Maxis MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_INSTPRINTER_MAXISINSTPRINTER_H +#define LLVM_LIB_TARGET_MAXIS_INSTPRINTER_MAXISINSTPRINTER_H +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { +// These enumeration declarations were originally in MaxisInstrInfo.h but +// had to be moved here to avoid circular dependencies between +// LLVMMaxisCodeGen and LLVMMaxisAsmPrinter. +namespace Maxis { +// Maxis Branch Codes +enum FPBranchCode { + BRANCH_F, + BRANCH_T, + BRANCH_FL, + BRANCH_TL, + BRANCH_INVALID +}; + +// Maxis Condition Codes +enum CondCode { + // To be used with float branch True + FCOND_F, + FCOND_UN, + FCOND_OEQ, + FCOND_UEQ, + FCOND_OLT, + FCOND_ULT, + FCOND_OLE, + FCOND_ULE, + FCOND_SF, + FCOND_NGLE, + FCOND_SEQ, + FCOND_NGL, + FCOND_LT, + FCOND_NGE, + FCOND_LE, + FCOND_NGT, + + // To be used with float branch False + // This conditions have the same mnemonic as the + // above ones, but are used with a branch False; + FCOND_T, + FCOND_OR, + FCOND_UNE, + FCOND_ONE, + FCOND_UGE, + FCOND_OGE, + FCOND_UGT, + FCOND_OGT, + FCOND_ST, + FCOND_GLE, + FCOND_SNE, + FCOND_GL, + FCOND_NLT, + FCOND_GE, + FCOND_NLE, + FCOND_GT +}; + +const char *MaxisFCCToString(Maxis::CondCode CC); +} // end namespace Maxis + +class MaxisInstPrinter : public MCInstPrinter { +public: + MaxisInstPrinter(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 OpNo, raw_ostream &O); + template + void printUImm(const MCInst *MI, int opNum, raw_ostream &O); + void printMemOperand(const MCInst *MI, int opNum, raw_ostream &O); + void printMemOperandEA(const MCInst *MI, int opNum, raw_ostream &O); + void printFCCOperand(const MCInst *MI, int opNum, raw_ostream &O); + void printRegisterPair(const MCInst *MI, int opNum, raw_ostream &O); + void printSHFMask(const MCInst *MI, int opNum, raw_ostream &O); + + bool printAlias(const char *Str, const MCInst &MI, unsigned OpNo, + raw_ostream &OS); + bool printAlias(const char *Str, const MCInst &MI, unsigned OpNo0, + unsigned OpNo1, raw_ostream &OS); + bool printAlias(const MCInst &MI, raw_ostream &OS); + void printSaveRestore(const MCInst *MI, raw_ostream &O); + void printRegisterList(const MCInst *MI, int opNum, raw_ostream &O); +}; +} // end namespace llvm + +#endif diff --git a/lib/Target/Maxis/LLVMBuild.txt b/lib/Target/Maxis/LLVMBuild.txt new file mode 100644 index 00000000..c51768f8 --- /dev/null +++ b/lib/Target/Maxis/LLVMBuild.txt @@ -0,0 +1,46 @@ +;===- ./lib/Target/Maxis/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 = AsmParser Disassembler InstPrinter MCTargetDesc TargetInfo + +[component_0] +type = TargetGroup +name = Maxis +parent = Target +has_asmparser = 1 +has_asmprinter = 1 +has_disassembler = 1 +has_jit = 1 + +[component_1] +type = Library +name = MaxisCodeGen +parent = Maxis +required_libraries = + Analysis + AsmPrinter + CodeGen + Core + MC + MaxisAsmPrinter + MaxisDesc + MaxisInfo + SelectionDAG + Support + Target +add_to_library_groups = Maxis diff --git a/lib/Target/Maxis/MCTargetDesc/CMakeLists.txt b/lib/Target/Maxis/MCTargetDesc/CMakeLists.txt new file mode 100644 index 00000000..7ed1962a --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,14 @@ +add_llvm_library(LLVMMaxisDesc + MaxisABIInfo.cpp + MaxisABIFlagsSection.cpp + MaxisAsmBackend.cpp + MaxisELFObjectWriter.cpp + MaxisELFStreamer.cpp + MaxisMCAsmInfo.cpp + MaxisMCCodeEmitter.cpp + MaxisMCExpr.cpp + MaxisMCTargetDesc.cpp + MaxisNaClELFStreamer.cpp + MaxisOptionRecord.cpp + MaxisTargetStreamer.cpp + ) diff --git a/lib/Target/Maxis/MCTargetDesc/LLVMBuild.txt b/lib/Target/Maxis/MCTargetDesc/LLVMBuild.txt new file mode 100644 index 00000000..83be704a --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/Maxis/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 = MaxisDesc +parent = Maxis +required_libraries = MC MaxisAsmPrinter MaxisInfo Support +add_to_library_groups = Maxis diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisABIFlagsSection.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisABIFlagsSection.cpp new file mode 100644 index 00000000..5b404781 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisABIFlagsSection.cpp @@ -0,0 +1,75 @@ +//===- MaxisABIFlagsSection.cpp - Maxis ELF ABI Flags Section ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/MaxisABIFlagsSection.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MaxisABIFlags.h" + +using namespace llvm; + +uint8_t MaxisABIFlagsSection::getFpABIValue() { + switch (FpABI) { + case FpABIKind::ANY: + return Maxis::Val_GNU_MAXIS_ABI_FP_ANY; + case FpABIKind::SOFT: + return Maxis::Val_GNU_MAXIS_ABI_FP_SOFT; + case FpABIKind::XX: + return Maxis::Val_GNU_MAXIS_ABI_FP_XX; + case FpABIKind::S32: + return Maxis::Val_GNU_MAXIS_ABI_FP_DOUBLE; + case FpABIKind::S64: + if (Is32BitABI) + return OddSPReg ? Maxis::Val_GNU_MAXIS_ABI_FP_64 + : Maxis::Val_GNU_MAXIS_ABI_FP_64A; + return Maxis::Val_GNU_MAXIS_ABI_FP_DOUBLE; + } + + llvm_unreachable("unexpected fp abi value"); +} + +StringRef MaxisABIFlagsSection::getFpABIString(FpABIKind Value) { + switch (Value) { + case FpABIKind::XX: + return "xx"; + case FpABIKind::S32: + return "32"; + case FpABIKind::S64: + return "64"; + default: + llvm_unreachable("unsupported fp abi value"); + } +} + +uint8_t MaxisABIFlagsSection::getCPR1SizeValue() { + if (FpABI == FpABIKind::XX) + return (uint8_t)Maxis::AFL_REG_32; + return (uint8_t)CPR1Size; +} + +namespace llvm { + +MCStreamer &operator<<(MCStreamer &OS, MaxisABIFlagsSection &ABIFlagsSection) { + // Write out a Elf_Internal_ABIFlags_v0 struct + OS.EmitIntValue(ABIFlagsSection.getVersionValue(), 2); // version + OS.EmitIntValue(ABIFlagsSection.getISALevelValue(), 1); // isa_level + OS.EmitIntValue(ABIFlagsSection.getISARevisionValue(), 1); // isa_rev + OS.EmitIntValue(ABIFlagsSection.getGPRSizeValue(), 1); // gpr_size + OS.EmitIntValue(ABIFlagsSection.getCPR1SizeValue(), 1); // cpr1_size + OS.EmitIntValue(ABIFlagsSection.getCPR2SizeValue(), 1); // cpr2_size + OS.EmitIntValue(ABIFlagsSection.getFpABIValue(), 1); // fp_abi + OS.EmitIntValue(ABIFlagsSection.getISAExtensionValue(), 4); // isa_ext + OS.EmitIntValue(ABIFlagsSection.getASESetValue(), 4); // ases + OS.EmitIntValue(ABIFlagsSection.getFlags1Value(), 4); // flags1 + OS.EmitIntValue(ABIFlagsSection.getFlags2Value(), 4); // flags2 + return OS; +} + +} // end namespace llvm diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisABIFlagsSection.h b/lib/Target/Maxis/MCTargetDesc/MaxisABIFlagsSection.h new file mode 100644 index 00000000..9d33f94a --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisABIFlagsSection.h @@ -0,0 +1,201 @@ +//===- MaxisABIFlagsSection.h - Maxis ELF ABI Flags Section -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISABIFLAGSSECTION_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISABIFLAGSSECTION_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MaxisABIFlags.h" +#include + +namespace llvm { + +class MCStreamer; + +struct MaxisABIFlagsSection { + // Internal representation of the fp_abi related values used in .module. + enum class FpABIKind { ANY, XX, S32, S64, SOFT }; + + // Version of flags structure. + uint16_t Version = 0; + // The level of the ISA: 1-5, 32, 64. + uint8_t ISALevel = 0; + // The revision of ISA: 0 for MAXIS V and below, 1-n otherwise. + uint8_t ISARevision = 0; + // The size of general purpose registers. + Maxis::AFL_REG GPRSize = Maxis::AFL_REG_NONE; + // The size of co-processor 1 registers. + Maxis::AFL_REG CPR1Size = Maxis::AFL_REG_NONE; + // The size of co-processor 2 registers. + Maxis::AFL_REG CPR2Size = Maxis::AFL_REG_NONE; + // Processor-specific extension. + Maxis::AFL_EXT ISAExtension = Maxis::AFL_EXT_NONE; + // Mask of ASEs used. + uint32_t ASESet = 0; + + bool OddSPReg = false; + + bool Is32BitABI = false; + +protected: + // The floating-point ABI. + FpABIKind FpABI = FpABIKind::ANY; + +public: + MaxisABIFlagsSection() = default; + + uint16_t getVersionValue() { return (uint16_t)Version; } + uint8_t getISALevelValue() { return (uint8_t)ISALevel; } + uint8_t getISARevisionValue() { return (uint8_t)ISARevision; } + uint8_t getGPRSizeValue() { return (uint8_t)GPRSize; } + uint8_t getCPR1SizeValue(); + uint8_t getCPR2SizeValue() { return (uint8_t)CPR2Size; } + uint8_t getFpABIValue(); + uint32_t getISAExtensionValue() { return (uint32_t)ISAExtension; } + uint32_t getASESetValue() { return (uint32_t)ASESet; } + + uint32_t getFlags1Value() { + uint32_t Value = 0; + + if (OddSPReg) + Value |= (uint32_t)Maxis::AFL_FLAGS1_ODDSPREG; + + return Value; + } + + uint32_t getFlags2Value() { return 0; } + + FpABIKind getFpABI() { return FpABI; } + void setFpABI(FpABIKind Value, bool IsABI32Bit) { + FpABI = Value; + Is32BitABI = IsABI32Bit; + } + + StringRef getFpABIString(FpABIKind Value); + + template + void setISALevelAndRevisionFromPredicates(const PredicateLibrary &P) { + if (P.hasMaxis64()) { + ISALevel = 64; + if (P.hasMaxis64r6()) + ISARevision = 6; + else if (P.hasMaxis64r5()) + ISARevision = 5; + else if (P.hasMaxis64r3()) + ISARevision = 3; + else if (P.hasMaxis64r2()) + ISARevision = 2; + else + ISARevision = 1; + } else if (P.hasMaxis32()) { + ISALevel = 32; + if (P.hasMaxis32r6()) + ISARevision = 6; + else if (P.hasMaxis32r5()) + ISARevision = 5; + else if (P.hasMaxis32r3()) + ISARevision = 3; + else if (P.hasMaxis32r2()) + ISARevision = 2; + else + ISARevision = 1; + } else { + ISARevision = 0; + if (P.hasMaxis5()) + ISALevel = 5; + else if (P.hasMaxis4()) + ISALevel = 4; + else if (P.hasMaxis3()) + ISALevel = 3; + else if (P.hasMaxis2()) + ISALevel = 2; + else if (P.hasMaxis1()) + ISALevel = 1; + else + llvm_unreachable("Unknown ISA level!"); + } + } + + template + void setGPRSizeFromPredicates(const PredicateLibrary &P) { + GPRSize = P.isGP64bit() ? Maxis::AFL_REG_64 : Maxis::AFL_REG_32; + } + + template + void setCPR1SizeFromPredicates(const PredicateLibrary &P) { + if (P.useSoftFloat()) + CPR1Size = Maxis::AFL_REG_NONE; + else if (P.hasMSA()) + CPR1Size = Maxis::AFL_REG_128; + else + CPR1Size = P.isFP64bit() ? Maxis::AFL_REG_64 : Maxis::AFL_REG_32; + } + + template + void setISAExtensionFromPredicates(const PredicateLibrary &P) { + if (P.hasCnMaxis()) + ISAExtension = Maxis::AFL_EXT_OCTEON; + else + ISAExtension = Maxis::AFL_EXT_NONE; + } + + template + void setASESetFromPredicates(const PredicateLibrary &P) { + ASESet = 0; + if (P.hasDSP()) + ASESet |= Maxis::AFL_ASE_DSP; + if (P.hasDSPR2()) + ASESet |= Maxis::AFL_ASE_DSPR2; + if (P.hasMSA()) + ASESet |= Maxis::AFL_ASE_MSA; + if (P.inMicroMaxisMode()) + ASESet |= Maxis::AFL_ASE_MICROMAXIS; + if (P.inMaxis16Mode()) + ASESet |= Maxis::AFL_ASE_MAXIS16; + if (P.hasMT()) + ASESet |= Maxis::AFL_ASE_MT; + } + + template + void setFpAbiFromPredicates(const PredicateLibrary &P) { + Is32BitABI = P.isABI_O32(); + + FpABI = FpABIKind::ANY; + if (P.useSoftFloat()) + FpABI = FpABIKind::SOFT; + else if (P.isABI_N32() || P.isABI_N64()) + FpABI = FpABIKind::S64; + else if (P.isABI_O32()) { + if (P.isABI_FPXX()) + FpABI = FpABIKind::XX; + else if (P.isFP64bit()) + FpABI = FpABIKind::S64; + else + FpABI = FpABIKind::S32; + } + } + + template + void setAllFromPredicates(const PredicateLibrary &P) { + setISALevelAndRevisionFromPredicates(P); + setGPRSizeFromPredicates(P); + setCPR1SizeFromPredicates(P); + setISAExtensionFromPredicates(P); + setASESetFromPredicates(P); + setFpAbiFromPredicates(P); + OddSPReg = P.useOddSPReg(); + } +}; + +MCStreamer &operator<<(MCStreamer &OS, MaxisABIFlagsSection &ABIFlagsSection); + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISABIFLAGSSECTION_H diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisABIInfo.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisABIInfo.cpp new file mode 100644 index 00000000..03259e9a --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisABIInfo.cpp @@ -0,0 +1,119 @@ +//===---- MaxisABIInfo.cpp - Information about MAXIS ABI's ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisABIInfo.h" +#include "MaxisRegisterInfo.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/MC/MCTargetOptions.h" + +using namespace llvm; + +namespace { +static const MCPhysReg O32IntRegs[4] = {Maxis::A0, Maxis::A1, Maxis::A2, Maxis::A3}; + +static const MCPhysReg Maxis64IntRegs[8] = { + Maxis::A0_64, Maxis::A1_64, Maxis::A2_64, Maxis::A3_64, + Maxis::T0_64, Maxis::T1_64, Maxis::T2_64, Maxis::T3_64}; +} + +ArrayRef MaxisABIInfo::GetByValArgRegs() const { + if (IsO32()) + return makeArrayRef(O32IntRegs); + if (IsN32() || IsN64()) + return makeArrayRef(Maxis64IntRegs); + llvm_unreachable("Unhandled ABI"); +} + +ArrayRef MaxisABIInfo::GetVarArgRegs() const { + if (IsO32()) + return makeArrayRef(O32IntRegs); + if (IsN32() || IsN64()) + return makeArrayRef(Maxis64IntRegs); + llvm_unreachable("Unhandled ABI"); +} + +unsigned MaxisABIInfo::GetCalleeAllocdArgSizeInBytes(CallingConv::ID CC) const { + if (IsO32()) + return CC != CallingConv::Fast ? 16 : 0; + if (IsN32() || IsN64()) + return 0; + llvm_unreachable("Unhandled ABI"); +} + +MaxisABIInfo MaxisABIInfo::computeTargetABI(const Triple &TT, StringRef CPU, + const MCTargetOptions &Options) { + if (Options.getABIName().startswith("o32")) + return MaxisABIInfo::O32(); + if (Options.getABIName().startswith("n32")) + return MaxisABIInfo::N32(); + if (Options.getABIName().startswith("n64")) + return MaxisABIInfo::N64(); + assert(Options.getABIName().empty() && "Unknown ABI option for MAXIS"); + + if (TT.getArch() == Triple::maxis64 || TT.getArch() == Triple::maxis64el) + return MaxisABIInfo::N64(); + return MaxisABIInfo::O32(); +} + +unsigned MaxisABIInfo::GetStackPtr() const { + return ArePtrs64bit() ? Maxis::SP_64 : Maxis::SP; +} + +unsigned MaxisABIInfo::GetFramePtr() const { + return ArePtrs64bit() ? Maxis::FP_64 : Maxis::FP; +} + +unsigned MaxisABIInfo::GetBasePtr() const { + return ArePtrs64bit() ? Maxis::S7_64 : Maxis::S7; +} + +unsigned MaxisABIInfo::GetGlobalPtr() const { + return ArePtrs64bit() ? Maxis::GP_64 : Maxis::GP; +} + +unsigned MaxisABIInfo::GetNullPtr() const { + return ArePtrs64bit() ? Maxis::ZERO_64 : Maxis::ZERO; +} + +unsigned MaxisABIInfo::GetZeroReg() const { + return AreGprs64bit() ? Maxis::ZERO_64 : Maxis::ZERO; +} + +unsigned MaxisABIInfo::GetPtrAdduOp() const { + return ArePtrs64bit() ? Maxis::DADDu : Maxis::ADDu; +} + +unsigned MaxisABIInfo::GetPtrAddiuOp() const { + return ArePtrs64bit() ? Maxis::DADDiu : Maxis::ADDiu; +} + +unsigned MaxisABIInfo::GetPtrSubuOp() const { + return ArePtrs64bit() ? Maxis::DSUBu : Maxis::SUBu; +} + +unsigned MaxisABIInfo::GetPtrAndOp() const { + return ArePtrs64bit() ? Maxis::AND64 : Maxis::AND; +} + +unsigned MaxisABIInfo::GetGPRMoveOp() const { + return ArePtrs64bit() ? Maxis::OR64 : Maxis::OR; +} + +unsigned MaxisABIInfo::GetEhDataReg(unsigned I) const { + static const unsigned EhDataReg[] = { + Maxis::A0, Maxis::A1, Maxis::A2, Maxis::A3 + }; + static const unsigned EhDataReg64[] = { + Maxis::A0_64, Maxis::A1_64, Maxis::A2_64, Maxis::A3_64 + }; + + return IsN64() ? EhDataReg64[I] : EhDataReg[I]; +} + diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisABIInfo.h b/lib/Target/Maxis/MCTargetDesc/MaxisABIInfo.h new file mode 100644 index 00000000..783673d5 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisABIInfo.h @@ -0,0 +1,82 @@ +//===---- MaxisABIInfo.h - Information about MAXIS ABI's --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISABIINFO_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISABIINFO_H + +#include "llvm/ADT/Triple.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/MC/MCRegisterInfo.h" + +namespace llvm { + +template class ArrayRef; +class MCTargetOptions; +class StringRef; +class TargetRegisterClass; + +class MaxisABIInfo { +public: + enum class ABI { Unknown, O32, N32, N64 }; + +protected: + ABI ThisABI; + +public: + MaxisABIInfo(ABI ThisABI) : ThisABI(ThisABI) {} + + static MaxisABIInfo Unknown() { return MaxisABIInfo(ABI::Unknown); } + static MaxisABIInfo O32() { return MaxisABIInfo(ABI::O32); } + static MaxisABIInfo N32() { return MaxisABIInfo(ABI::N32); } + static MaxisABIInfo N64() { return MaxisABIInfo(ABI::N64); } + static MaxisABIInfo computeTargetABI(const Triple &TT, StringRef CPU, + const MCTargetOptions &Options); + + bool IsKnown() const { return ThisABI != ABI::Unknown; } + bool IsO32() const { return ThisABI == ABI::O32; } + bool IsN32() const { return ThisABI == ABI::N32; } + bool IsN64() const { return ThisABI == ABI::N64; } + ABI GetEnumValue() const { return ThisABI; } + + /// The registers to use for byval arguments. + ArrayRef GetByValArgRegs() const; + + /// The registers to use for the variable argument list. + ArrayRef GetVarArgRegs() const; + + /// Obtain the size of the area allocated by the callee for arguments. + /// CallingConv::FastCall affects the value for O32. + unsigned GetCalleeAllocdArgSizeInBytes(CallingConv::ID CC) const; + + /// Ordering of ABI's + /// MaxisGenSubtargetInfo.inc will use this to resolve conflicts when given + /// multiple ABI options. + bool operator<(const MaxisABIInfo Other) const { + return ThisABI < Other.GetEnumValue(); + } + + unsigned GetStackPtr() const; + unsigned GetFramePtr() const; + unsigned GetBasePtr() const; + unsigned GetGlobalPtr() const; + unsigned GetNullPtr() const; + unsigned GetZeroReg() const; + unsigned GetPtrAdduOp() const; + unsigned GetPtrAddiuOp() const; + unsigned GetPtrSubuOp() const; + unsigned GetPtrAndOp() const; + unsigned GetGPRMoveOp() const; + inline bool ArePtrs64bit() const { return IsN64(); } + inline bool AreGprs64bit() const { return IsN32() || IsN64(); } + + unsigned GetEhDataReg(unsigned I) const; +}; +} + +#endif diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisAsmBackend.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisAsmBackend.cpp new file mode 100644 index 00000000..dc189145 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisAsmBackend.cpp @@ -0,0 +1,484 @@ +//===-- MaxisAsmBackend.cpp - Maxis Asm Backend ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the MaxisAsmBackend class. +// +//===----------------------------------------------------------------------===// +// + +#include "MCTargetDesc/MaxisAsmBackend.h" +#include "MCTargetDesc/MaxisFixupKinds.h" +#include "MCTargetDesc/MaxisMCExpr.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +// Prepare value for the target space for it +static unsigned adjustFixupValue(const MCFixup &Fixup, uint64_t Value, + MCContext &Ctx) { + + unsigned Kind = Fixup.getKind(); + + // Add/subtract and shift + switch (Kind) { + default: + return 0; + case FK_Data_2: + case Maxis::fixup_Maxis_LO16: + case Maxis::fixup_Maxis_GPREL16: + case Maxis::fixup_Maxis_GPOFF_HI: + case Maxis::fixup_Maxis_GPOFF_LO: + case Maxis::fixup_Maxis_GOT_PAGE: + case Maxis::fixup_Maxis_GOT_OFST: + case Maxis::fixup_Maxis_GOT_DISP: + case Maxis::fixup_Maxis_GOT_LO16: + case Maxis::fixup_Maxis_CALL_LO16: + case Maxis::fixup_MICROMAXIS_LO16: + case Maxis::fixup_MICROMAXIS_GOT_PAGE: + case Maxis::fixup_MICROMAXIS_GOT_OFST: + case Maxis::fixup_MICROMAXIS_GOT_DISP: + case Maxis::fixup_MAXIS_PCLO16: + Value &= 0xffff; + break; + case FK_DTPRel_4: + case FK_DTPRel_8: + case FK_TPRel_4: + case FK_TPRel_8: + case FK_GPRel_4: + case FK_Data_4: + case FK_Data_8: + case Maxis::fixup_Maxis_SUB: + case Maxis::fixup_MICROMAXIS_SUB: + break; + case Maxis::fixup_Maxis_PC16: + // The displacement is then divided by 4 to give us an 18 bit + // address range. Forcing a signed division because Value can be negative. + Value = (int64_t)Value / 4; + // We now check if Value can be encoded as a 16-bit signed immediate. + if (!isInt<16>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC16 fixup"); + return 0; + } + break; + case Maxis::fixup_MAXIS_PC19_S2: + case Maxis::fixup_MICROMAXIS_PC19_S2: + // Forcing a signed division because Value can be negative. + Value = (int64_t)Value / 4; + // We now check if Value can be encoded as a 19-bit signed immediate. + if (!isInt<19>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC19 fixup"); + return 0; + } + break; + case Maxis::fixup_Maxis_26: + // So far we are only using this type for jumps. + // The displacement is then divided by 4 to give us an 28 bit + // address range. + Value >>= 2; + break; + case Maxis::fixup_Maxis_HI16: + case Maxis::fixup_Maxis_GOT: + case Maxis::fixup_MICROMAXIS_GOT16: + case Maxis::fixup_Maxis_GOT_HI16: + case Maxis::fixup_Maxis_CALL_HI16: + case Maxis::fixup_MICROMAXIS_HI16: + case Maxis::fixup_MAXIS_PCHI16: + // Get the 2nd 16-bits. Also add 1 if bit 15 is 1. + Value = ((Value + 0x8000) >> 16) & 0xffff; + break; + case Maxis::fixup_Maxis_HIGHER: + // Get the 3rd 16-bits. + Value = ((Value + 0x80008000LL) >> 32) & 0xffff; + break; + case Maxis::fixup_Maxis_HIGHEST: + // Get the 4th 16-bits. + Value = ((Value + 0x800080008000LL) >> 48) & 0xffff; + break; + case Maxis::fixup_MICROMAXIS_26_S1: + Value >>= 1; + break; + case Maxis::fixup_MICROMAXIS_PC7_S1: + Value -= 4; + // Forcing a signed division because Value can be negative. + Value = (int64_t) Value / 2; + // We now check if Value can be encoded as a 7-bit signed immediate. + if (!isInt<7>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC7 fixup"); + return 0; + } + break; + case Maxis::fixup_MICROMAXIS_PC10_S1: + Value -= 2; + // Forcing a signed division because Value can be negative. + Value = (int64_t) Value / 2; + // We now check if Value can be encoded as a 10-bit signed immediate. + if (!isInt<10>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC10 fixup"); + return 0; + } + break; + case Maxis::fixup_MICROMAXIS_PC16_S1: + Value -= 4; + // Forcing a signed division because Value can be negative. + Value = (int64_t)Value / 2; + // We now check if Value can be encoded as a 16-bit signed immediate. + if (!isInt<16>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC16 fixup"); + return 0; + } + break; + case Maxis::fixup_MAXIS_PC18_S3: + // Forcing a signed division because Value can be negative. + Value = (int64_t)Value / 8; + // We now check if Value can be encoded as a 18-bit signed immediate. + if (!isInt<18>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC18 fixup"); + return 0; + } + break; + case Maxis::fixup_MICROMAXIS_PC18_S3: + // Check alignment. + if ((Value & 7)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC18 fixup"); + } + // Forcing a signed division because Value can be negative. + Value = (int64_t)Value / 8; + // We now check if Value can be encoded as a 18-bit signed immediate. + if (!isInt<18>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC18 fixup"); + return 0; + } + break; + case Maxis::fixup_MAXIS_PC21_S2: + // Forcing a signed division because Value can be negative. + Value = (int64_t) Value / 4; + // We now check if Value can be encoded as a 21-bit signed immediate. + if (!isInt<21>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC21 fixup"); + return 0; + } + break; + case Maxis::fixup_MAXIS_PC26_S2: + // Forcing a signed division because Value can be negative. + Value = (int64_t) Value / 4; + // We now check if Value can be encoded as a 26-bit signed immediate. + if (!isInt<26>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC26 fixup"); + return 0; + } + break; + case Maxis::fixup_MICROMAXIS_PC26_S1: + // Forcing a signed division because Value can be negative. + Value = (int64_t)Value / 2; + // We now check if Value can be encoded as a 26-bit signed immediate. + if (!isInt<26>(Value)) { + Ctx.reportFatalError(Fixup.getLoc(), "out of range PC26 fixup"); + return 0; + } + break; + case Maxis::fixup_MICROMAXIS_PC21_S1: + // Forcing a signed division because Value can be negative. + Value = (int64_t)Value / 2; + // We now check if Value can be encoded as a 21-bit signed immediate. + if (!isInt<21>(Value)) { + Ctx.reportError(Fixup.getLoc(), "out of range PC21 fixup"); + return 0; + } + break; + } + + return Value; +} + +std::unique_ptr +MaxisAsmBackend::createObjectWriter(raw_pwrite_stream &OS) const { + return createMaxisELFObjectWriter(OS, TheTriple, IsN32); +} + +// Little-endian fixup data byte ordering: +// maxis32r2: a | b | x | x +// microMAXIS: x | x | a | b + +static bool needsMMLEByteOrder(unsigned Kind) { + return Kind != Maxis::fixup_MICROMAXIS_PC10_S1 && + Kind >= Maxis::fixup_MICROMAXIS_26_S1 && + Kind < Maxis::LastTargetFixupKind; +} + +// Calculate index for microMAXIS specific little endian byte order +static unsigned calculateMMLEIndex(unsigned i) { + assert(i <= 3 && "Index out of range!"); + + return (1 - i / 2) * 2 + i % 2; +} + +/// ApplyFixup - Apply the \p Value for given \p Fixup into the provided +/// data fragment, at the offset specified by the fixup and following the +/// fixup kind as appropriate. +void MaxisAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, + MutableArrayRef Data, uint64_t Value, + bool IsResolved) const { + MCFixupKind Kind = Fixup.getKind(); + MCContext &Ctx = Asm.getContext(); + Value = adjustFixupValue(Fixup, Value, Ctx); + + if (!Value) + return; // Doesn't change encoding. + + // Where do we start in the object + unsigned Offset = Fixup.getOffset(); + // Number of bytes we need to fixup + unsigned NumBytes = (getFixupKindInfo(Kind).TargetSize + 7) / 8; + // Used to point to big endian bytes + unsigned FullSize; + + switch ((unsigned)Kind) { + case FK_Data_2: + case Maxis::fixup_Maxis_16: + case Maxis::fixup_MICROMAXIS_PC10_S1: + FullSize = 2; + break; + case FK_Data_8: + case Maxis::fixup_Maxis_64: + FullSize = 8; + break; + case FK_Data_4: + default: + FullSize = 4; + break; + } + + // Grab current value, if any, from bits. + uint64_t CurVal = 0; + + bool microMaxisLEByteOrder = needsMMLEByteOrder((unsigned) Kind); + + for (unsigned i = 0; i != NumBytes; ++i) { + unsigned Idx = IsLittle ? (microMaxisLEByteOrder ? calculateMMLEIndex(i) + : i) + : (FullSize - 1 - i); + CurVal |= (uint64_t)((uint8_t)Data[Offset + Idx]) << (i*8); + } + + uint64_t Mask = ((uint64_t)(-1) >> + (64 - getFixupKindInfo(Kind).TargetSize)); + CurVal |= Value & Mask; + + // Write out the fixed up bytes back to the code/data bits. + for (unsigned i = 0; i != NumBytes; ++i) { + unsigned Idx = IsLittle ? (microMaxisLEByteOrder ? calculateMMLEIndex(i) + : i) + : (FullSize - 1 - i); + Data[Offset + Idx] = (uint8_t)((CurVal >> (i*8)) & 0xff); + } +} + +Optional MaxisAsmBackend::getFixupKind(StringRef Name) const { + return StringSwitch>(Name) + .Case("R_MAXIS_NONE", (MCFixupKind)Maxis::fixup_Maxis_NONE) + .Case("R_MAXIS_32", FK_Data_4) + .Default(MCAsmBackend::getFixupKind(Name)); +} + +const MCFixupKindInfo &MaxisAsmBackend:: +getFixupKindInfo(MCFixupKind Kind) const { + const static MCFixupKindInfo LittleEndianInfos[Maxis::NumTargetFixupKinds] = { + // This table *must* be in same the order of fixup_* kinds in + // MaxisFixupKinds.h. + // + // name offset bits flags + { "fixup_Maxis_NONE", 0, 0, 0 }, + { "fixup_Maxis_16", 0, 16, 0 }, + { "fixup_Maxis_32", 0, 32, 0 }, + { "fixup_Maxis_REL32", 0, 32, 0 }, + { "fixup_Maxis_26", 0, 26, 0 }, + { "fixup_Maxis_HI16", 0, 16, 0 }, + { "fixup_Maxis_LO16", 0, 16, 0 }, + { "fixup_Maxis_GPREL16", 0, 16, 0 }, + { "fixup_Maxis_LITERAL", 0, 16, 0 }, + { "fixup_Maxis_GOT", 0, 16, 0 }, + { "fixup_Maxis_PC16", 0, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_Maxis_CALL16", 0, 16, 0 }, + { "fixup_Maxis_GPREL32", 0, 32, 0 }, + { "fixup_Maxis_SHIFT5", 6, 5, 0 }, + { "fixup_Maxis_SHIFT6", 6, 5, 0 }, + { "fixup_Maxis_64", 0, 64, 0 }, + { "fixup_Maxis_TLSGD", 0, 16, 0 }, + { "fixup_Maxis_GOTTPREL", 0, 16, 0 }, + { "fixup_Maxis_TPREL_HI", 0, 16, 0 }, + { "fixup_Maxis_TPREL_LO", 0, 16, 0 }, + { "fixup_Maxis_TLSLDM", 0, 16, 0 }, + { "fixup_Maxis_DTPREL_HI", 0, 16, 0 }, + { "fixup_Maxis_DTPREL_LO", 0, 16, 0 }, + { "fixup_Maxis_Branch_PCRel", 0, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_Maxis_GPOFF_HI", 0, 16, 0 }, + { "fixup_Maxis_GPOFF_LO", 0, 16, 0 }, + { "fixup_Maxis_GOT_PAGE", 0, 16, 0 }, + { "fixup_Maxis_GOT_OFST", 0, 16, 0 }, + { "fixup_Maxis_GOT_DISP", 0, 16, 0 }, + { "fixup_Maxis_HIGHER", 0, 16, 0 }, + { "fixup_Maxis_HIGHEST", 0, 16, 0 }, + { "fixup_Maxis_GOT_HI16", 0, 16, 0 }, + { "fixup_Maxis_GOT_LO16", 0, 16, 0 }, + { "fixup_Maxis_CALL_HI16", 0, 16, 0 }, + { "fixup_Maxis_CALL_LO16", 0, 16, 0 }, + { "fixup_Maxis_PC18_S3", 0, 18, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PC19_S2", 0, 19, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PC21_S2", 0, 21, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PC26_S2", 0, 26, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PCHI16", 0, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PCLO16", 0, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_26_S1", 0, 26, 0 }, + { "fixup_MICROMAXIS_HI16", 0, 16, 0 }, + { "fixup_MICROMAXIS_LO16", 0, 16, 0 }, + { "fixup_MICROMAXIS_GOT16", 0, 16, 0 }, + { "fixup_MICROMAXIS_PC7_S1", 0, 7, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC10_S1", 0, 10, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC16_S1", 0, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC26_S1", 0, 26, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC19_S2", 0, 19, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC18_S3", 0, 18, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC21_S1", 0, 21, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_CALL16", 0, 16, 0 }, + { "fixup_MICROMAXIS_GOT_DISP", 0, 16, 0 }, + { "fixup_MICROMAXIS_GOT_PAGE", 0, 16, 0 }, + { "fixup_MICROMAXIS_GOT_OFST", 0, 16, 0 }, + { "fixup_MICROMAXIS_TLS_GD", 0, 16, 0 }, + { "fixup_MICROMAXIS_TLS_LDM", 0, 16, 0 }, + { "fixup_MICROMAXIS_TLS_DTPREL_HI16", 0, 16, 0 }, + { "fixup_MICROMAXIS_TLS_DTPREL_LO16", 0, 16, 0 }, + { "fixup_MICROMAXIS_GOTTPREL", 0, 16, 0 }, + { "fixup_MICROMAXIS_TLS_TPREL_HI16", 0, 16, 0 }, + { "fixup_MICROMAXIS_TLS_TPREL_LO16", 0, 16, 0 }, + { "fixup_Maxis_SUB", 0, 64, 0 }, + { "fixup_MICROMAXIS_SUB", 0, 64, 0 } + }; + + const static MCFixupKindInfo BigEndianInfos[Maxis::NumTargetFixupKinds] = { + // This table *must* be in same the order of fixup_* kinds in + // MaxisFixupKinds.h. + // + // name offset bits flags + { "fixup_Maxis_NONE", 0, 0, 0 }, + { "fixup_Maxis_16", 16, 16, 0 }, + { "fixup_Maxis_32", 0, 32, 0 }, + { "fixup_Maxis_REL32", 0, 32, 0 }, + { "fixup_Maxis_26", 6, 26, 0 }, + { "fixup_Maxis_HI16", 16, 16, 0 }, + { "fixup_Maxis_LO16", 16, 16, 0 }, + { "fixup_Maxis_GPREL16", 16, 16, 0 }, + { "fixup_Maxis_LITERAL", 16, 16, 0 }, + { "fixup_Maxis_GOT", 16, 16, 0 }, + { "fixup_Maxis_PC16", 16, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_Maxis_CALL16", 16, 16, 0 }, + { "fixup_Maxis_GPREL32", 0, 32, 0 }, + { "fixup_Maxis_SHIFT5", 21, 5, 0 }, + { "fixup_Maxis_SHIFT6", 21, 5, 0 }, + { "fixup_Maxis_64", 0, 64, 0 }, + { "fixup_Maxis_TLSGD", 16, 16, 0 }, + { "fixup_Maxis_GOTTPREL", 16, 16, 0 }, + { "fixup_Maxis_TPREL_HI", 16, 16, 0 }, + { "fixup_Maxis_TPREL_LO", 16, 16, 0 }, + { "fixup_Maxis_TLSLDM", 16, 16, 0 }, + { "fixup_Maxis_DTPREL_HI", 16, 16, 0 }, + { "fixup_Maxis_DTPREL_LO", 16, 16, 0 }, + { "fixup_Maxis_Branch_PCRel",16, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_Maxis_GPOFF_HI", 16, 16, 0 }, + { "fixup_Maxis_GPOFF_LO", 16, 16, 0 }, + { "fixup_Maxis_GOT_PAGE", 16, 16, 0 }, + { "fixup_Maxis_GOT_OFST", 16, 16, 0 }, + { "fixup_Maxis_GOT_DISP", 16, 16, 0 }, + { "fixup_Maxis_HIGHER", 16, 16, 0 }, + { "fixup_Maxis_HIGHEST", 16, 16, 0 }, + { "fixup_Maxis_GOT_HI16", 16, 16, 0 }, + { "fixup_Maxis_GOT_LO16", 16, 16, 0 }, + { "fixup_Maxis_CALL_HI16", 16, 16, 0 }, + { "fixup_Maxis_CALL_LO16", 16, 16, 0 }, + { "fixup_Maxis_PC18_S3", 14, 18, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PC19_S2", 13, 19, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PC21_S2", 11, 21, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PC26_S2", 6, 26, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PCHI16", 16, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MAXIS_PCLO16", 16, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_26_S1", 6, 26, 0 }, + { "fixup_MICROMAXIS_HI16", 16, 16, 0 }, + { "fixup_MICROMAXIS_LO16", 16, 16, 0 }, + { "fixup_MICROMAXIS_GOT16", 16, 16, 0 }, + { "fixup_MICROMAXIS_PC7_S1", 9, 7, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC10_S1", 6, 10, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC16_S1",16, 16, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC26_S1", 6, 26, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC19_S2",13, 19, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC18_S3",14, 18, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_PC21_S1",11, 21, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_MICROMAXIS_CALL16", 16, 16, 0 }, + { "fixup_MICROMAXIS_GOT_DISP", 16, 16, 0 }, + { "fixup_MICROMAXIS_GOT_PAGE", 16, 16, 0 }, + { "fixup_MICROMAXIS_GOT_OFST", 16, 16, 0 }, + { "fixup_MICROMAXIS_TLS_GD", 16, 16, 0 }, + { "fixup_MICROMAXIS_TLS_LDM", 16, 16, 0 }, + { "fixup_MICROMAXIS_TLS_DTPREL_HI16", 16, 16, 0 }, + { "fixup_MICROMAXIS_TLS_DTPREL_LO16", 16, 16, 0 }, + { "fixup_MICROMAXIS_GOTTPREL", 16, 16, 0 }, + { "fixup_MICROMAXIS_TLS_TPREL_HI16", 16, 16, 0 }, + { "fixup_MICROMAXIS_TLS_TPREL_LO16", 16, 16, 0 }, + { "fixup_Maxis_SUB", 0, 64, 0 }, + { "fixup_MICROMAXIS_SUB", 0, 64, 0 } + }; + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + + if (IsLittle) + return LittleEndianInfos[Kind - FirstTargetFixupKind]; + return BigEndianInfos[Kind - FirstTargetFixupKind]; +} + +/// WriteNopData - Write an (optimal) nop sequence of Count bytes +/// to the given output. If the target cannot generate such a sequence, +/// it should return an error. +/// +/// \return - True on success. +bool MaxisAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const { + // Check for a less than instruction size number of bytes + // FIXME: 16 bit instructions are not handled yet here. + // We shouldn't be using a hard coded number for instruction size. + + // If the count is not 4-byte aligned, we must be writing data into the text + // section (otherwise we have unaligned instructions, and thus have far + // bigger problems), so just write zeros instead. + OW->WriteZeros(Count); + return true; +} + +MCAsmBackend *llvm::createMaxisAsmBackend(const Target &T, + const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options) { + return new MaxisAsmBackend(T, MRI, STI.getTargetTriple(), STI.getCPU(), + Options.ABIName == "n32"); +} diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisAsmBackend.h b/lib/Target/Maxis/MCTargetDesc/MaxisAsmBackend.h new file mode 100644 index 00000000..cb8debad --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisAsmBackend.h @@ -0,0 +1,92 @@ +//===-- MaxisAsmBackend.h - Maxis Asm Backend ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the MaxisAsmBackend class. +// +//===----------------------------------------------------------------------===// +// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISASMBACKEND_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISASMBACKEND_H + +#include "MCTargetDesc/MaxisFixupKinds.h" +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmBackend.h" + +namespace llvm { + +class MCAssembler; +struct MCFixupKindInfo; +class MCObjectWriter; +class MCRegisterInfo; +class Target; + +class MaxisAsmBackend : public MCAsmBackend { + Triple TheTriple; + bool IsLittle; // Big or little endian + bool IsN32; + +public: + MaxisAsmBackend(const Target &T, const MCRegisterInfo &MRI, const Triple &TT, + StringRef CPU, bool N32) + : TheTriple(TT), IsLittle(TT.isLittleEndian()), IsN32(N32) {} + + std::unique_ptr + createObjectWriter(raw_pwrite_stream &OS) const override; + + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved) const override; + + Optional getFixupKind(StringRef Name) const override; + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + + unsigned getNumFixupKinds() const override { + return Maxis::NumTargetFixupKinds; + } + + /// @name Target Relaxation Interfaces + /// @{ + + /// MayNeedRelaxation - Check whether the given instruction may need + /// relaxation. + /// + /// \param Inst - The instruction to test. + bool mayNeedRelaxation(const MCInst &Inst) const override { + return false; + } + + /// fixupNeedsRelaxation - Target specific predicate for whether a given + /// fixup requires the associated instruction to be relaxed. + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const override { + // FIXME. + llvm_unreachable("RelaxInstruction() unimplemented"); + return false; + } + + /// RelaxInstruction - Relax the instruction in the given fragment + /// to the next wider instruction. + /// + /// \param Inst - The instruction to relax, which may be the same + /// as the output. + /// \param [out] Res On return, the relaxed instruction. + void relaxInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, + MCInst &Res) const override {} + + /// @} + + bool writeNopData(uint64_t Count, MCObjectWriter *OW) const override; + +}; // class MaxisAsmBackend + +} // namespace + +#endif diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisBaseInfo.h b/lib/Target/Maxis/MCTargetDesc/MaxisBaseInfo.h new file mode 100644 index 00000000..2ac9472e --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisBaseInfo.h @@ -0,0 +1,134 @@ +//===-- MaxisBaseInfo.h - Top level definitions for MAXIS MC ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains small standalone helper functions and enum definitions for +// the Maxis target useful for the compiler back-end and the MC libraries. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISBASEINFO_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISBASEINFO_H + +#include "MaxisFixupKinds.h" +#include "MaxisMCTargetDesc.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/ErrorHandling.h" + +namespace llvm { + +/// MaxisII - This namespace holds all of the target specific flags that +/// instruction info tracks. +/// +namespace MaxisII { + /// Target Operand Flag enum. + enum TOF { + //===------------------------------------------------------------------===// + // Maxis Specific MachineOperand flags. + + MO_NO_FLAG, + + /// MO_GOT - Represents the offset into the global offset table at which + /// the address the relocation entry symbol resides during execution. + MO_GOT, + + /// MO_GOT_CALL - Represents the offset into the global offset table at + /// which the address of a call site relocation entry symbol resides + /// during execution. This is different from the above since this flag + /// can only be present in call instructions. + MO_GOT_CALL, + + /// MO_GPREL - Represents the offset from the current gp value to be used + /// for the relocatable object file being produced. + MO_GPREL, + + /// MO_ABS_HI/LO - Represents the hi or low part of an absolute symbol + /// address. + MO_ABS_HI, + MO_ABS_LO, + + /// MO_TLSGD - Represents the offset into the global offset table at which + // the module ID and TSL block offset reside during execution (General + // Dynamic TLS). + MO_TLSGD, + + /// MO_TLSLDM - Represents the offset into the global offset table at which + // the module ID and TSL block offset reside during execution (Local + // Dynamic TLS). + MO_TLSLDM, + MO_DTPREL_HI, + MO_DTPREL_LO, + + /// MO_GOTTPREL - Represents the offset from the thread pointer (Initial + // Exec TLS). + MO_GOTTPREL, + + /// MO_TPREL_HI/LO - Represents the hi and low part of the offset from + // the thread pointer (Local Exec TLS). + MO_TPREL_HI, + MO_TPREL_LO, + + // N32/64 Flags. + MO_GPOFF_HI, + MO_GPOFF_LO, + MO_GOT_DISP, + MO_GOT_PAGE, + MO_GOT_OFST, + + /// MO_HIGHER/HIGHEST - Represents the highest or higher half word of a + /// 64-bit symbol address. + MO_HIGHER, + MO_HIGHEST, + + /// MO_GOT_HI16/LO16, MO_CALL_HI16/LO16 - Relocations used for large GOTs. + MO_GOT_HI16, + MO_GOT_LO16, + MO_CALL_HI16, + MO_CALL_LO16 + }; + + enum { + //===------------------------------------------------------------------===// + // Instruction encodings. These are the standard/most common forms for + // Maxis instructions. + // + + // Pseudo - This represents an instruction that is a pseudo instruction + // or one that has not been implemented yet. It is illegal to code generate + // it, but tolerated for intermediate implementation stages. + Pseudo = 0, + + /// FrmR - This form is for instructions of the format R. + FrmR = 1, + /// FrmI - This form is for instructions of the format I. + FrmI = 2, + /// FrmJ - This form is for instructions of the format J. + FrmJ = 3, + /// FrmFR - This form is for instructions of the format FR. + FrmFR = 4, + /// FrmFI - This form is for instructions of the format FI. + FrmFI = 5, + /// FrmOther - This form is for instructions that have no specific format. + FrmOther = 6, + + FormMask = 15, + /// IsCTI - Instruction is a Control Transfer Instruction. + IsCTI = 1 << 4, + /// HasForbiddenSlot - Instruction has a forbidden slot. + HasForbiddenSlot = 1 << 5, + /// IsPCRelativeLoad - A Load instruction with implicit source register + /// ($pc) with explicit offset and destination register + IsPCRelativeLoad = 1 << 6, + /// HasFCCRegOperand - Instruction uses an $fcc register. + HasFCCRegOperand = 1 << 7 + + }; +} +} + +#endif diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisELFObjectWriter.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisELFObjectWriter.cpp new file mode 100644 index 00000000..b47c4f6d --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisELFObjectWriter.cpp @@ -0,0 +1,668 @@ +//===-- MaxisELFObjectWriter.cpp - Maxis ELF Writer -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/MaxisFixupKinds.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include + +#define DEBUG_TYPE "maxis-elf-object-writer" + +using namespace llvm; + +namespace { + +/// Holds additional information needed by the relocation ordering algorithm. +struct MaxisRelocationEntry { + const ELFRelocationEntry R; ///< The relocation. + bool Matched = false; ///< Is this relocation part of a match. + + MaxisRelocationEntry(const ELFRelocationEntry &R) : R(R) {} + + void print(raw_ostream &Out) const { + R.print(Out); + Out << ", Matched=" << Matched; + } +}; + +#ifndef NDEBUG +raw_ostream &operator<<(raw_ostream &OS, const MaxisRelocationEntry &RHS) { + RHS.print(OS); + return OS; +} +#endif + +class MaxisELFObjectWriter : public MCELFObjectTargetWriter { +public: + MaxisELFObjectWriter(uint8_t OSABI, bool HasRelocationAddend, bool Is64, + bool IsLittleEndian); + + ~MaxisELFObjectWriter() override = default; + + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const override; + bool needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const override; + void sortRelocs(const MCAssembler &Asm, + std::vector &Relocs) override; +}; + +/// The possible results of the Predicate function used by find_best. +enum FindBestPredicateResult { + FindBest_NoMatch = 0, ///< The current element is not a match. + FindBest_Match, ///< The current element is a match but better ones are + /// possible. + FindBest_PerfectMatch, ///< The current element is an unbeatable match. +}; + +} // end anonymous namespace + +/// Copy elements in the range [First, Last) to d1 when the predicate is true or +/// d2 when the predicate is false. This is essentially both std::copy_if and +/// std::remove_copy_if combined into a single pass. +template +static std::pair copy_if_else(InputIt First, InputIt Last, + OutputIt1 d1, OutputIt2 d2, + UnaryPredicate Predicate) { + for (InputIt I = First; I != Last; ++I) { + if (Predicate(*I)) { + *d1 = *I; + d1++; + } else { + *d2 = *I; + d2++; + } + } + + return std::make_pair(d1, d2); +} + +/// Find the best match in the range [First, Last). +/// +/// An element matches when Predicate(X) returns FindBest_Match or +/// FindBest_PerfectMatch. A value of FindBest_PerfectMatch also terminates +/// the search. BetterThan(A, B) is a comparator that returns true when A is a +/// better match than B. The return value is the position of the best match. +/// +/// This is similar to std::find_if but finds the best of multiple possible +/// matches. +template +static InputIt find_best(InputIt First, InputIt Last, UnaryPredicate Predicate, + Comparator BetterThan) { + InputIt Best = Last; + + for (InputIt I = First; I != Last; ++I) { + unsigned Matched = Predicate(*I); + if (Matched != FindBest_NoMatch) { + DEBUG(dbgs() << std::distance(First, I) << " is a match ("; + I->print(dbgs()); dbgs() << ")\n"); + if (Best == Last || BetterThan(*I, *Best)) { + DEBUG(dbgs() << ".. and it beats the last one\n"); + Best = I; + } + } + if (Matched == FindBest_PerfectMatch) { + DEBUG(dbgs() << ".. and it is unbeatable\n"); + break; + } + } + + return Best; +} + +/// Determine the low relocation that matches the given relocation. +/// If the relocation does not need a low relocation then the return value +/// is ELF::R_MAXIS_NONE. +/// +/// The relocations that need a matching low part are +/// R_(MAXIS|MICROMAXIS|MAXIS16)_HI16 for all symbols and +/// R_(MAXIS|MICROMAXIS|MAXIS16)_GOT16 for local symbols only. +static unsigned getMatchingLoType(const ELFRelocationEntry &Reloc) { + unsigned Type = Reloc.Type; + if (Type == ELF::R_MAXIS_HI16) + return ELF::R_MAXIS_LO16; + if (Type == ELF::R_MICROMAXIS_HI16) + return ELF::R_MICROMAXIS_LO16; + if (Type == ELF::R_MAXIS16_HI16) + return ELF::R_MAXIS16_LO16; + + if (Reloc.OriginalSymbol->getBinding() != ELF::STB_LOCAL) + return ELF::R_MAXIS_NONE; + + if (Type == ELF::R_MAXIS_GOT16) + return ELF::R_MAXIS_LO16; + if (Type == ELF::R_MICROMAXIS_GOT16) + return ELF::R_MICROMAXIS_LO16; + if (Type == ELF::R_MAXIS16_GOT16) + return ELF::R_MAXIS16_LO16; + + return ELF::R_MAXIS_NONE; +} + +/// Determine whether a relocation (X) matches the one given in R. +/// +/// A relocation matches if: +/// - It's type matches that of a corresponding low part. This is provided in +/// MatchingType for efficiency. +/// - It's based on the same symbol. +/// - It's offset of greater or equal to that of the one given in R. +/// It should be noted that this rule assumes the programmer does not use +/// offsets that exceed the alignment of the symbol. The carry-bit will be +/// incorrect if this is not true. +/// +/// A matching relocation is unbeatable if: +/// - It is not already involved in a match. +/// - It's offset is exactly that of the one given in R. +static FindBestPredicateResult isMatchingReloc(const MaxisRelocationEntry &X, + const ELFRelocationEntry &R, + unsigned MatchingType) { + if (X.R.Type == MatchingType && X.R.OriginalSymbol == R.OriginalSymbol) { + if (!X.Matched && + X.R.OriginalAddend == R.OriginalAddend) + return FindBest_PerfectMatch; + else if (X.R.OriginalAddend >= R.OriginalAddend) + return FindBest_Match; + } + return FindBest_NoMatch; +} + +/// Determine whether Candidate or PreviousBest is the better match. +/// The return value is true if Candidate is the better match. +/// +/// A matching relocation is a better match if: +/// - It has a smaller addend. +/// - It is not already involved in a match. +static bool compareMatchingRelocs(const MaxisRelocationEntry &Candidate, + const MaxisRelocationEntry &PreviousBest) { + if (Candidate.R.OriginalAddend != PreviousBest.R.OriginalAddend) + return Candidate.R.OriginalAddend < PreviousBest.R.OriginalAddend; + return PreviousBest.Matched && !Candidate.Matched; +} + +#ifndef NDEBUG +/// Print all the relocations. +template +static void dumpRelocs(const char *Prefix, const Container &Relocs) { + for (const auto &R : Relocs) + dbgs() << Prefix << R << "\n"; +} +#endif + +MaxisELFObjectWriter::MaxisELFObjectWriter(uint8_t OSABI, + bool HasRelocationAddend, bool Is64, + bool IsLittleEndian) + : MCELFObjectTargetWriter(Is64, OSABI, ELF::EM_MAXIS, HasRelocationAddend) {} + +unsigned MaxisELFObjectWriter::getRelocType(MCContext &Ctx, + const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + // Determine the type of the relocation. + unsigned Kind = (unsigned)Fixup.getKind(); + + switch (Kind) { + case Maxis::fixup_Maxis_NONE: + return ELF::R_MAXIS_NONE; + case Maxis::fixup_Maxis_16: + case FK_Data_2: + return IsPCRel ? ELF::R_MAXIS_PC16 : ELF::R_MAXIS_16; + case Maxis::fixup_Maxis_32: + case FK_Data_4: + return IsPCRel ? ELF::R_MAXIS_PC32 : ELF::R_MAXIS_32; + } + + if (IsPCRel) { + switch (Kind) { + case Maxis::fixup_Maxis_Branch_PCRel: + case Maxis::fixup_Maxis_PC16: + return ELF::R_MAXIS_PC16; + case Maxis::fixup_MICROMAXIS_PC7_S1: + return ELF::R_MICROMAXIS_PC7_S1; + case Maxis::fixup_MICROMAXIS_PC10_S1: + return ELF::R_MICROMAXIS_PC10_S1; + case Maxis::fixup_MICROMAXIS_PC16_S1: + return ELF::R_MICROMAXIS_PC16_S1; + case Maxis::fixup_MICROMAXIS_PC26_S1: + return ELF::R_MICROMAXIS_PC26_S1; + case Maxis::fixup_MICROMAXIS_PC19_S2: + return ELF::R_MICROMAXIS_PC19_S2; + case Maxis::fixup_MICROMAXIS_PC18_S3: + return ELF::R_MICROMAXIS_PC18_S3; + case Maxis::fixup_MICROMAXIS_PC21_S1: + return ELF::R_MICROMAXIS_PC21_S1; + case Maxis::fixup_MAXIS_PC19_S2: + return ELF::R_MAXIS_PC19_S2; + case Maxis::fixup_MAXIS_PC18_S3: + return ELF::R_MAXIS_PC18_S3; + case Maxis::fixup_MAXIS_PC21_S2: + return ELF::R_MAXIS_PC21_S2; + case Maxis::fixup_MAXIS_PC26_S2: + return ELF::R_MAXIS_PC26_S2; + case Maxis::fixup_MAXIS_PCHI16: + return ELF::R_MAXIS_PCHI16; + case Maxis::fixup_MAXIS_PCLO16: + return ELF::R_MAXIS_PCLO16; + } + + llvm_unreachable("invalid PC-relative fixup kind!"); + } + + switch (Kind) { + case Maxis::fixup_Maxis_64: + case FK_Data_8: + return ELF::R_MAXIS_64; + case FK_DTPRel_4: + return ELF::R_MAXIS_TLS_DTPREL32; + case FK_DTPRel_8: + return ELF::R_MAXIS_TLS_DTPREL64; + case FK_TPRel_4: + return ELF::R_MAXIS_TLS_TPREL32; + case FK_TPRel_8: + return ELF::R_MAXIS_TLS_TPREL64; + case FK_GPRel_4: + if (is64Bit()) { + unsigned Type = (unsigned)ELF::R_MAXIS_NONE; + Type = setRType((unsigned)ELF::R_MAXIS_GPREL32, Type); + Type = setRType2((unsigned)ELF::R_MAXIS_64, Type); + Type = setRType3((unsigned)ELF::R_MAXIS_NONE, Type); + return Type; + } + return ELF::R_MAXIS_GPREL32; + case Maxis::fixup_Maxis_GPREL16: + return ELF::R_MAXIS_GPREL16; + case Maxis::fixup_Maxis_26: + return ELF::R_MAXIS_26; + case Maxis::fixup_Maxis_CALL16: + return ELF::R_MAXIS_CALL16; + case Maxis::fixup_Maxis_GOT: + return ELF::R_MAXIS_GOT16; + case Maxis::fixup_Maxis_HI16: + return ELF::R_MAXIS_HI16; + case Maxis::fixup_Maxis_LO16: + return ELF::R_MAXIS_LO16; + case Maxis::fixup_Maxis_TLSGD: + return ELF::R_MAXIS_TLS_GD; + case Maxis::fixup_Maxis_GOTTPREL: + return ELF::R_MAXIS_TLS_GOTTPREL; + case Maxis::fixup_Maxis_TPREL_HI: + return ELF::R_MAXIS_TLS_TPREL_HI16; + case Maxis::fixup_Maxis_TPREL_LO: + return ELF::R_MAXIS_TLS_TPREL_LO16; + case Maxis::fixup_Maxis_TLSLDM: + return ELF::R_MAXIS_TLS_LDM; + case Maxis::fixup_Maxis_DTPREL_HI: + return ELF::R_MAXIS_TLS_DTPREL_HI16; + case Maxis::fixup_Maxis_DTPREL_LO: + return ELF::R_MAXIS_TLS_DTPREL_LO16; + case Maxis::fixup_Maxis_GOT_PAGE: + return ELF::R_MAXIS_GOT_PAGE; + case Maxis::fixup_Maxis_GOT_OFST: + return ELF::R_MAXIS_GOT_OFST; + case Maxis::fixup_Maxis_GOT_DISP: + return ELF::R_MAXIS_GOT_DISP; + case Maxis::fixup_Maxis_GPOFF_HI: { + unsigned Type = (unsigned)ELF::R_MAXIS_NONE; + Type = setRType((unsigned)ELF::R_MAXIS_GPREL16, Type); + Type = setRType2((unsigned)ELF::R_MAXIS_SUB, Type); + Type = setRType3((unsigned)ELF::R_MAXIS_HI16, Type); + return Type; + } + case Maxis::fixup_Maxis_GPOFF_LO: { + unsigned Type = (unsigned)ELF::R_MAXIS_NONE; + Type = setRType((unsigned)ELF::R_MAXIS_GPREL16, Type); + Type = setRType2((unsigned)ELF::R_MAXIS_SUB, Type); + Type = setRType3((unsigned)ELF::R_MAXIS_LO16, Type); + return Type; + } + case Maxis::fixup_Maxis_HIGHER: + return ELF::R_MAXIS_HIGHER; + case Maxis::fixup_Maxis_HIGHEST: + return ELF::R_MAXIS_HIGHEST; + case Maxis::fixup_Maxis_SUB: + return ELF::R_MAXIS_SUB; + case Maxis::fixup_Maxis_GOT_HI16: + return ELF::R_MAXIS_GOT_HI16; + case Maxis::fixup_Maxis_GOT_LO16: + return ELF::R_MAXIS_GOT_LO16; + case Maxis::fixup_Maxis_CALL_HI16: + return ELF::R_MAXIS_CALL_HI16; + case Maxis::fixup_Maxis_CALL_LO16: + return ELF::R_MAXIS_CALL_LO16; + case Maxis::fixup_MICROMAXIS_26_S1: + return ELF::R_MICROMAXIS_26_S1; + case Maxis::fixup_MICROMAXIS_HI16: + return ELF::R_MICROMAXIS_HI16; + case Maxis::fixup_MICROMAXIS_LO16: + return ELF::R_MICROMAXIS_LO16; + case Maxis::fixup_MICROMAXIS_GOT16: + return ELF::R_MICROMAXIS_GOT16; + case Maxis::fixup_MICROMAXIS_CALL16: + return ELF::R_MICROMAXIS_CALL16; + case Maxis::fixup_MICROMAXIS_GOT_DISP: + return ELF::R_MICROMAXIS_GOT_DISP; + case Maxis::fixup_MICROMAXIS_GOT_PAGE: + return ELF::R_MICROMAXIS_GOT_PAGE; + case Maxis::fixup_MICROMAXIS_GOT_OFST: + return ELF::R_MICROMAXIS_GOT_OFST; + case Maxis::fixup_MICROMAXIS_TLS_GD: + return ELF::R_MICROMAXIS_TLS_GD; + case Maxis::fixup_MICROMAXIS_TLS_LDM: + return ELF::R_MICROMAXIS_TLS_LDM; + case Maxis::fixup_MICROMAXIS_TLS_DTPREL_HI16: + return ELF::R_MICROMAXIS_TLS_DTPREL_HI16; + case Maxis::fixup_MICROMAXIS_TLS_DTPREL_LO16: + return ELF::R_MICROMAXIS_TLS_DTPREL_LO16; + case Maxis::fixup_MICROMAXIS_GOTTPREL: + return ELF::R_MICROMAXIS_TLS_GOTTPREL; + case Maxis::fixup_MICROMAXIS_TLS_TPREL_HI16: + return ELF::R_MICROMAXIS_TLS_TPREL_HI16; + case Maxis::fixup_MICROMAXIS_TLS_TPREL_LO16: + return ELF::R_MICROMAXIS_TLS_TPREL_LO16; + case Maxis::fixup_MICROMAXIS_SUB: + return ELF::R_MICROMAXIS_SUB; + } + + llvm_unreachable("invalid fixup kind!"); +} + +/// Sort relocation table entries by offset except where another order is +/// required by the MAXIS ABI. +/// +/// MAXIS has a few relocations that have an AHL component in the expression used +/// to evaluate them. This AHL component is an addend with the same number of +/// bits as a symbol value but not all of our ABI's are able to supply a +/// sufficiently sized addend in a single relocation. +/// +/// The O32 ABI for example, uses REL relocations which store the addend in the +/// section data. All the relocations with AHL components affect 16-bit fields +/// so the addend for a single relocation is limited to 16-bit. This ABI +/// resolves the limitation by linking relocations (e.g. R_MAXIS_HI16 and +/// R_MAXIS_LO16) and distributing the addend between the linked relocations. The +/// ABI mandates that such relocations must be next to each other in a +/// particular order (e.g. R_MAXIS_HI16 must be immediately followed by a +/// matching R_MAXIS_LO16) but the rule is less strict in practice. +/// +/// The de facto standard is lenient in the following ways: +/// - 'Immediately following' does not refer to the next relocation entry but +/// the next matching relocation. +/// - There may be multiple high parts relocations for one low part relocation. +/// - There may be multiple low part relocations for one high part relocation. +/// - The AHL addend in each part does not have to be exactly equal as long as +/// the difference does not affect the carry bit from bit 15 into 16. This is +/// to allow, for example, the use of %lo(foo) and %lo(foo+4) when loading +/// both halves of a long long. +/// +/// See getMatchingLoType() for a description of which high part relocations +/// match which low part relocations. One particular thing to note is that +/// R_MAXIS_GOT16 and similar only have AHL addends if they refer to local +/// symbols. +/// +/// It should also be noted that this function is not affected by whether +/// the symbol was kept or rewritten into a section-relative equivalent. We +/// always match using the expressions from the source. +void MaxisELFObjectWriter::sortRelocs(const MCAssembler &Asm, + std::vector &Relocs) { + // We do not need to sort the relocation table for RELA relocations which + // N32/N64 uses as the relocation addend contains the value we require, + // rather than it being split across a pair of relocations. + if (hasRelocationAddend()) + return; + + if (Relocs.size() < 2) + return; + + // Sort relocations by the address they are applied to. + std::sort(Relocs.begin(), Relocs.end(), + [](const ELFRelocationEntry &A, const ELFRelocationEntry &B) { + return A.Offset < B.Offset; + }); + + std::list Sorted; + std::list Remainder; + + DEBUG(dumpRelocs("R: ", Relocs)); + + // Separate the movable relocations (AHL relocations using the high bits) from + // the immobile relocations (everything else). This does not preserve high/low + // matches that already existed in the input. + copy_if_else(Relocs.begin(), Relocs.end(), std::back_inserter(Remainder), + std::back_inserter(Sorted), [](const ELFRelocationEntry &Reloc) { + return getMatchingLoType(Reloc) != ELF::R_MAXIS_NONE; + }); + + for (auto &R : Remainder) { + DEBUG(dbgs() << "Matching: " << R << "\n"); + + unsigned MatchingType = getMatchingLoType(R); + assert(MatchingType != ELF::R_MAXIS_NONE && + "Wrong list for reloc that doesn't need a match"); + + // Find the best matching relocation for the current high part. + // See isMatchingReloc for a description of a matching relocation and + // compareMatchingRelocs for a description of what 'best' means. + auto InsertionPoint = + find_best(Sorted.begin(), Sorted.end(), + [&R, &MatchingType](const MaxisRelocationEntry &X) { + return isMatchingReloc(X, R, MatchingType); + }, + compareMatchingRelocs); + + // If we matched then insert the high part in front of the match and mark + // both relocations as being involved in a match. We only mark the high + // part for cosmetic reasons in the debug output. + // + // If we failed to find a match then the high part is orphaned. This is not + // permitted since the relocation cannot be evaluated without knowing the + // carry-in. We can sometimes handle this using a matching low part that is + // already used in a match but we already cover that case in + // isMatchingReloc and compareMatchingRelocs. For the remaining cases we + // should insert the high part at the end of the list. This will cause the + // linker to fail but the alternative is to cause the linker to bind the + // high part to a semi-matching low part and silently calculate the wrong + // value. Unfortunately we have no means to warn the user that we did this + // so leave it up to the linker to complain about it. + if (InsertionPoint != Sorted.end()) + InsertionPoint->Matched = true; + Sorted.insert(InsertionPoint, R)->Matched = true; + } + + DEBUG(dumpRelocs("S: ", Sorted)); + + assert(Relocs.size() == Sorted.size() && "Some relocs were not consumed"); + + // Overwrite the original vector with the sorted elements. The caller expects + // them in reverse order. + unsigned CopyTo = 0; + for (const auto &R : reverse(Sorted)) + Relocs[CopyTo++] = R.R; +} + +bool MaxisELFObjectWriter::needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const { + // If it's a compound relocation for N64 then we need the relocation if any + // sub-relocation needs it. + if (!isUInt<8>(Type)) + return needsRelocateWithSymbol(Sym, Type & 0xff) || + needsRelocateWithSymbol(Sym, (Type >> 8) & 0xff) || + needsRelocateWithSymbol(Sym, (Type >> 16) & 0xff); + + switch (Type) { + default: + errs() << Type << "\n"; + llvm_unreachable("Unexpected relocation"); + return true; + + // This relocation doesn't affect the section data. + case ELF::R_MAXIS_NONE: + return false; + + // On REL ABI's (e.g. O32), these relocations form pairs. The pairing is done + // by the static linker by matching the symbol and offset. + // We only see one relocation at a time but it's still safe to relocate with + // the section so long as both relocations make the same decision. + // + // Some older linkers may require the symbol for particular cases. Such cases + // are not supported yet but can be added as required. + case ELF::R_MAXIS_GOT16: + case ELF::R_MAXIS16_GOT16: + case ELF::R_MICROMAXIS_GOT16: + case ELF::R_MAXIS_HIGHER: + case ELF::R_MAXIS_HIGHEST: + case ELF::R_MAXIS_HI16: + case ELF::R_MAXIS16_HI16: + case ELF::R_MICROMAXIS_HI16: + case ELF::R_MAXIS_LO16: + case ELF::R_MAXIS16_LO16: + case ELF::R_MICROMAXIS_LO16: + // FIXME: It should be safe to return false for the STO_MAXIS_MICROMAXIS but + // we neglect to handle the adjustment to the LSB of the addend that + // it causes in applyFixup() and similar. + if (cast(Sym).getOther() & ELF::STO_MAXIS_MICROMAXIS) + return true; + return false; + + case ELF::R_MAXIS_GOT_PAGE: + case ELF::R_MICROMAXIS_GOT_PAGE: + case ELF::R_MAXIS_GOT_OFST: + case ELF::R_MICROMAXIS_GOT_OFST: + case ELF::R_MAXIS_16: + case ELF::R_MAXIS_32: + case ELF::R_MAXIS_GPREL32: + if (cast(Sym).getOther() & ELF::STO_MAXIS_MICROMAXIS) + return true; + LLVM_FALLTHROUGH; + case ELF::R_MAXIS_26: + case ELF::R_MAXIS_64: + case ELF::R_MAXIS_GPREL16: + case ELF::R_MAXIS_PC16: + case ELF::R_MAXIS_SUB: + return false; + + // FIXME: Many of these relocations should probably return false but this + // hasn't been confirmed to be safe yet. + case ELF::R_MAXIS_REL32: + case ELF::R_MAXIS_LITERAL: + case ELF::R_MAXIS_CALL16: + case ELF::R_MAXIS_SHIFT5: + case ELF::R_MAXIS_SHIFT6: + case ELF::R_MAXIS_GOT_DISP: + case ELF::R_MAXIS_GOT_HI16: + case ELF::R_MAXIS_GOT_LO16: + case ELF::R_MAXIS_INSERT_A: + case ELF::R_MAXIS_INSERT_B: + case ELF::R_MAXIS_DELETE: + case ELF::R_MAXIS_CALL_HI16: + case ELF::R_MAXIS_CALL_LO16: + case ELF::R_MAXIS_SCN_DISP: + case ELF::R_MAXIS_REL16: + case ELF::R_MAXIS_ADD_IMMEDIATE: + case ELF::R_MAXIS_PJUMP: + case ELF::R_MAXIS_RELGOT: + case ELF::R_MAXIS_JALR: + case ELF::R_MAXIS_TLS_DTPMOD32: + case ELF::R_MAXIS_TLS_DTPREL32: + case ELF::R_MAXIS_TLS_DTPMOD64: + case ELF::R_MAXIS_TLS_DTPREL64: + case ELF::R_MAXIS_TLS_GD: + case ELF::R_MAXIS_TLS_LDM: + case ELF::R_MAXIS_TLS_DTPREL_HI16: + case ELF::R_MAXIS_TLS_DTPREL_LO16: + case ELF::R_MAXIS_TLS_GOTTPREL: + case ELF::R_MAXIS_TLS_TPREL32: + case ELF::R_MAXIS_TLS_TPREL64: + case ELF::R_MAXIS_TLS_TPREL_HI16: + case ELF::R_MAXIS_TLS_TPREL_LO16: + case ELF::R_MAXIS_GLOB_DAT: + case ELF::R_MAXIS_PC21_S2: + case ELF::R_MAXIS_PC26_S2: + case ELF::R_MAXIS_PC18_S3: + case ELF::R_MAXIS_PC19_S2: + case ELF::R_MAXIS_PCHI16: + case ELF::R_MAXIS_PCLO16: + case ELF::R_MAXIS_COPY: + case ELF::R_MAXIS_JUMP_SLOT: + case ELF::R_MAXIS_NUM: + case ELF::R_MAXIS_PC32: + case ELF::R_MAXIS_EH: + case ELF::R_MICROMAXIS_26_S1: + case ELF::R_MICROMAXIS_GPREL16: + case ELF::R_MICROMAXIS_LITERAL: + case ELF::R_MICROMAXIS_PC7_S1: + case ELF::R_MICROMAXIS_PC10_S1: + case ELF::R_MICROMAXIS_PC16_S1: + case ELF::R_MICROMAXIS_CALL16: + case ELF::R_MICROMAXIS_GOT_DISP: + case ELF::R_MICROMAXIS_GOT_HI16: + case ELF::R_MICROMAXIS_GOT_LO16: + case ELF::R_MICROMAXIS_SUB: + case ELF::R_MICROMAXIS_HIGHER: + case ELF::R_MICROMAXIS_HIGHEST: + case ELF::R_MICROMAXIS_CALL_HI16: + case ELF::R_MICROMAXIS_CALL_LO16: + case ELF::R_MICROMAXIS_SCN_DISP: + case ELF::R_MICROMAXIS_JALR: + case ELF::R_MICROMAXIS_HI0_LO16: + case ELF::R_MICROMAXIS_TLS_GD: + case ELF::R_MICROMAXIS_TLS_LDM: + case ELF::R_MICROMAXIS_TLS_DTPREL_HI16: + case ELF::R_MICROMAXIS_TLS_DTPREL_LO16: + case ELF::R_MICROMAXIS_TLS_GOTTPREL: + case ELF::R_MICROMAXIS_TLS_TPREL_HI16: + case ELF::R_MICROMAXIS_TLS_TPREL_LO16: + case ELF::R_MICROMAXIS_GPREL7_S2: + case ELF::R_MICROMAXIS_PC23_S2: + case ELF::R_MICROMAXIS_PC21_S1: + case ELF::R_MICROMAXIS_PC26_S1: + case ELF::R_MICROMAXIS_PC18_S3: + case ELF::R_MICROMAXIS_PC19_S2: + return true; + + // FIXME: Many of these should probably return false but MAXIS16 isn't + // supported by the integrated assembler. + case ELF::R_MAXIS16_26: + case ELF::R_MAXIS16_GPREL: + case ELF::R_MAXIS16_CALL16: + case ELF::R_MAXIS16_TLS_GD: + case ELF::R_MAXIS16_TLS_LDM: + case ELF::R_MAXIS16_TLS_DTPREL_HI16: + case ELF::R_MAXIS16_TLS_DTPREL_LO16: + case ELF::R_MAXIS16_TLS_GOTTPREL: + case ELF::R_MAXIS16_TLS_TPREL_HI16: + case ELF::R_MAXIS16_TLS_TPREL_LO16: + llvm_unreachable("Unsupported MAXIS16 relocation"); + return true; + } +} + +std::unique_ptr +llvm::createMaxisELFObjectWriter(raw_pwrite_stream &OS, const Triple &TT, + bool IsN32) { + uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TT.getOS()); + bool IsN64 = TT.isArch64Bit() && !IsN32; + bool HasRelocationAddend = TT.isArch64Bit(); + auto MOTW = llvm::make_unique( + OSABI, HasRelocationAddend, IsN64, TT.isLittleEndian()); + return createELFObjectWriter(std::move(MOTW), OS, TT.isLittleEndian()); +} diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisELFStreamer.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisELFStreamer.cpp new file mode 100644 index 00000000..6c8cbdd5 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisELFStreamer.cpp @@ -0,0 +1,97 @@ +//===-------- MaxisELFStreamer.cpp - ELF Object Output ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisELFStreamer.h" +#include "MaxisOptionRecord.h" +#include "MaxisTargetStreamer.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/Casting.h" + +using namespace llvm; + +MaxisELFStreamer::MaxisELFStreamer(MCContext &Context, + std::unique_ptr MAB, + raw_pwrite_stream &OS, + std::unique_ptr Emitter) + : MCELFStreamer(Context, std::move(MAB), OS, std::move(Emitter)) { + RegInfoRecord = new MaxisRegInfoRecord(this, Context); + MaxisOptionRecords.push_back( + std::unique_ptr(RegInfoRecord)); +} + +void MaxisELFStreamer::EmitInstruction(const MCInst &Inst, + const MCSubtargetInfo &STI, bool) { + MCELFStreamer::EmitInstruction(Inst, STI); + + MCContext &Context = getContext(); + const MCRegisterInfo *MCRegInfo = Context.getRegisterInfo(); + + for (unsigned OpIndex = 0; OpIndex < Inst.getNumOperands(); ++OpIndex) { + const MCOperand &Op = Inst.getOperand(OpIndex); + + if (!Op.isReg()) + continue; + + unsigned Reg = Op.getReg(); + RegInfoRecord->SetPhysRegUsed(Reg, MCRegInfo); + } + + createPendingLabelRelocs(); +} + +void MaxisELFStreamer::createPendingLabelRelocs() { + MaxisTargetELFStreamer *ELFTargetStreamer = + static_cast(getTargetStreamer()); + + // FIXME: Also mark labels when in MAXIS16 mode. + if (ELFTargetStreamer->isMicroMaxisEnabled()) { + for (auto *L : Labels) { + auto *Label = cast(L); + getAssembler().registerSymbol(*Label); + Label->setOther(ELF::STO_MAXIS_MICROMAXIS); + } + } + + Labels.clear(); +} + +void MaxisELFStreamer::EmitLabel(MCSymbol *Symbol, SMLoc Loc) { + MCELFStreamer::EmitLabel(Symbol); + Labels.push_back(Symbol); +} + +void MaxisELFStreamer::SwitchSection(MCSection *Section, + const MCExpr *Subsection) { + MCELFStreamer::SwitchSection(Section, Subsection); + Labels.clear(); +} + +void MaxisELFStreamer::EmitValueImpl(const MCExpr *Value, unsigned Size, + SMLoc Loc) { + MCELFStreamer::EmitValueImpl(Value, Size, Loc); + Labels.clear(); +} + +void MaxisELFStreamer::EmitMaxisOptionRecords() { + for (const auto &I : MaxisOptionRecords) + I->EmitMaxisOptionRecord(); +} + +MCELFStreamer *llvm::createMaxisELFStreamer( + MCContext &Context, std::unique_ptr MAB, + raw_pwrite_stream &OS, std::unique_ptr Emitter, + bool RelaxAll) { + return new MaxisELFStreamer(Context, std::move(MAB), OS, std::move(Emitter)); +} diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisELFStreamer.h b/lib/Target/Maxis/MCTargetDesc/MaxisELFStreamer.h new file mode 100644 index 00000000..cfaaa862 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisELFStreamer.h @@ -0,0 +1,75 @@ +//===- MaxisELFStreamer.h - ELF Object Output --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a custom MCELFStreamer which allows us to insert some hooks before +// emitting data into an actual object file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISELFSTREAMER_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISELFSTREAMER_H + +#include "MaxisOptionRecord.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCELFStreamer.h" +#include + +namespace llvm { + +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCSubtargetInfo; + +class MaxisELFStreamer : public MCELFStreamer { + SmallVector, 8> MaxisOptionRecords; + MaxisRegInfoRecord *RegInfoRecord; + SmallVector Labels; + +public: + MaxisELFStreamer(MCContext &Context, std::unique_ptr MAB, + raw_pwrite_stream &OS, + std::unique_ptr Emitter); + + /// Overriding this function allows us to add arbitrary behaviour before the + /// \p Inst is actually emitted. For example, we can inspect the operands and + /// gather sufficient information that allows us to reason about the register + /// usage for the translation unit. + void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, + bool = false) override; + + /// Overriding this function allows us to record all labels that should be + /// marked as microMAXIS. Based on this data marking is done in + /// EmitInstruction. + void EmitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override; + + /// Overriding this function allows us to dismiss all labels that are + /// candidates for marking as microMAXIS when .section directive is processed. + void SwitchSection(MCSection *Section, + const MCExpr *Subsection = nullptr) override; + + /// Overriding this function allows us to dismiss all labels that are + /// candidates for marking as microMAXIS when .word directive is emitted. + void EmitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) override; + + /// Emits all the option records stored up until the point it's called. + void EmitMaxisOptionRecords(); + + /// Mark labels as microMAXIS, if necessary for the subtarget. + void createPendingLabelRelocs(); +}; + +MCELFStreamer *createMaxisELFStreamer(MCContext &Context, + std::unique_ptr MAB, + raw_pwrite_stream &OS, + std::unique_ptr Emitter, + bool RelaxAll); +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISELFSTREAMER_H diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisFixupKinds.h b/lib/Target/Maxis/MCTargetDesc/MaxisFixupKinds.h new file mode 100644 index 00000000..ecc4d021 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisFixupKinds.h @@ -0,0 +1,227 @@ +//===-- MaxisFixupKinds.h - Maxis 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISFIXUPKINDS_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISFIXUPKINDS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +namespace Maxis { + // Although most of the current fixup types reflect a unique relocation + // one can have multiple fixup types for a given relocation and thus need + // to be uniquely named. + // + // This table *must* be in the same order of + // MCFixupKindInfo Infos[Maxis::NumTargetFixupKinds] + // in MaxisAsmBackend.cpp. + // + enum Fixups { + // Branch fixups resulting in R_MAXIS_NONE. + fixup_Maxis_NONE = FirstTargetFixupKind, + + // Branch fixups resulting in R_MAXIS_16. + fixup_Maxis_16, + + // Pure 32 bit data fixup resulting in - R_MAXIS_32. + fixup_Maxis_32, + + // Full 32 bit data relative data fixup resulting in - R_MAXIS_REL32. + fixup_Maxis_REL32, + + // Jump 26 bit fixup resulting in - R_MAXIS_26. + fixup_Maxis_26, + + // Pure upper 16 bit fixup resulting in - R_MAXIS_HI16. + fixup_Maxis_HI16, + + // Pure lower 16 bit fixup resulting in - R_MAXIS_LO16. + fixup_Maxis_LO16, + + // 16 bit fixup for GP offest resulting in - R_MAXIS_GPREL16. + fixup_Maxis_GPREL16, + + // 16 bit literal fixup resulting in - R_MAXIS_LITERAL. + fixup_Maxis_LITERAL, + + // Symbol fixup resulting in - R_MAXIS_GOT16. + fixup_Maxis_GOT, + + // PC relative branch fixup resulting in - R_MAXIS_PC16. + fixup_Maxis_PC16, + + // resulting in - R_MAXIS_CALL16. + fixup_Maxis_CALL16, + + // resulting in - R_MAXIS_GPREL32. + fixup_Maxis_GPREL32, + + // resulting in - R_MAXIS_SHIFT5. + fixup_Maxis_SHIFT5, + + // resulting in - R_MAXIS_SHIFT6. + fixup_Maxis_SHIFT6, + + // Pure 64 bit data fixup resulting in - R_MAXIS_64. + fixup_Maxis_64, + + // resulting in - R_MAXIS_TLS_GD. + fixup_Maxis_TLSGD, + + // resulting in - R_MAXIS_TLS_GOTTPREL. + fixup_Maxis_GOTTPREL, + + // resulting in - R_MAXIS_TLS_TPREL_HI16. + fixup_Maxis_TPREL_HI, + + // resulting in - R_MAXIS_TLS_TPREL_LO16. + fixup_Maxis_TPREL_LO, + + // resulting in - R_MAXIS_TLS_LDM. + fixup_Maxis_TLSLDM, + + // resulting in - R_MAXIS_TLS_DTPREL_HI16. + fixup_Maxis_DTPREL_HI, + + // resulting in - R_MAXIS_TLS_DTPREL_LO16. + fixup_Maxis_DTPREL_LO, + + // PC relative branch fixup resulting in - R_MAXIS_PC16 + fixup_Maxis_Branch_PCRel, + + // resulting in - R_MAXIS_GPREL16/R_MAXIS_SUB/R_MAXIS_HI16 + fixup_Maxis_GPOFF_HI, + + // resulting in - R_MAXIS_GPREL16/R_MAXIS_SUB/R_MAXIS_LO16 + fixup_Maxis_GPOFF_LO, + + // resulting in - R_MAXIS_PAGE + fixup_Maxis_GOT_PAGE, + + // resulting in - R_MAXIS_GOT_OFST + fixup_Maxis_GOT_OFST, + + // resulting in - R_MAXIS_GOT_DISP + fixup_Maxis_GOT_DISP, + + // resulting in - R_MAXIS_GOT_HIGHER + fixup_Maxis_HIGHER, + + // resulting in - R_MAXIS_HIGHEST + fixup_Maxis_HIGHEST, + + // resulting in - R_MAXIS_GOT_HI16 + fixup_Maxis_GOT_HI16, + + // resulting in - R_MAXIS_GOT_LO16 + fixup_Maxis_GOT_LO16, + + // resulting in - R_MAXIS_CALL_HI16 + fixup_Maxis_CALL_HI16, + + // resulting in - R_MAXIS_CALL_LO16 + fixup_Maxis_CALL_LO16, + + // resulting in - R_MAXIS_PC18_S3 + fixup_MAXIS_PC18_S3, + + // resulting in - R_MAXIS_PC19_S2 + fixup_MAXIS_PC19_S2, + + // resulting in - R_MAXIS_PC21_S2 + fixup_MAXIS_PC21_S2, + + // resulting in - R_MAXIS_PC26_S2 + fixup_MAXIS_PC26_S2, + + // resulting in - R_MAXIS_PCHI16 + fixup_MAXIS_PCHI16, + + // resulting in - R_MAXIS_PCLO16 + fixup_MAXIS_PCLO16, + + // resulting in - R_MICROMAXIS_26_S1 + fixup_MICROMAXIS_26_S1, + + // resulting in - R_MICROMAXIS_HI16 + fixup_MICROMAXIS_HI16, + + // resulting in - R_MICROMAXIS_LO16 + fixup_MICROMAXIS_LO16, + + // resulting in - R_MICROMAXIS_GOT16 + fixup_MICROMAXIS_GOT16, + + // resulting in - R_MICROMAXIS_PC7_S1 + fixup_MICROMAXIS_PC7_S1, + + // resulting in - R_MICROMAXIS_PC10_S1 + fixup_MICROMAXIS_PC10_S1, + + // resulting in - R_MICROMAXIS_PC16_S1 + fixup_MICROMAXIS_PC16_S1, + + // resulting in - R_MICROMAXIS_PC26_S1 + fixup_MICROMAXIS_PC26_S1, + + // resulting in - R_MICROMAXIS_PC19_S2 + fixup_MICROMAXIS_PC19_S2, + + // resulting in - R_MICROMAXIS_PC18_S3 + fixup_MICROMAXIS_PC18_S3, + + // resulting in - R_MICROMAXIS_PC21_S1 + fixup_MICROMAXIS_PC21_S1, + + // resulting in - R_MICROMAXIS_CALL16 + fixup_MICROMAXIS_CALL16, + + // resulting in - R_MICROMAXIS_GOT_DISP + fixup_MICROMAXIS_GOT_DISP, + + // resulting in - R_MICROMAXIS_GOT_PAGE + fixup_MICROMAXIS_GOT_PAGE, + + // resulting in - R_MICROMAXIS_GOT_OFST + fixup_MICROMAXIS_GOT_OFST, + + // resulting in - R_MICROMAXIS_TLS_GD + fixup_MICROMAXIS_TLS_GD, + + // resulting in - R_MICROMAXIS_TLS_LDM + fixup_MICROMAXIS_TLS_LDM, + + // resulting in - R_MICROMAXIS_TLS_DTPREL_HI16 + fixup_MICROMAXIS_TLS_DTPREL_HI16, + + // resulting in - R_MICROMAXIS_TLS_DTPREL_LO16 + fixup_MICROMAXIS_TLS_DTPREL_LO16, + + // resulting in - R_MICROMAXIS_TLS_GOTTPREL. + fixup_MICROMAXIS_GOTTPREL, + + // resulting in - R_MICROMAXIS_TLS_TPREL_HI16 + fixup_MICROMAXIS_TLS_TPREL_HI16, + + // resulting in - R_MICROMAXIS_TLS_TPREL_LO16 + fixup_MICROMAXIS_TLS_TPREL_LO16, + + // resulting in - R_MAXIS_SUB/R_MICROMAXIS_SUB + fixup_Maxis_SUB, + fixup_MICROMAXIS_SUB, + + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind + }; +} // namespace Maxis +} // namespace llvm + + +#endif diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCAsmInfo.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisMCAsmInfo.cpp new file mode 100644 index 00000000..14fbc1fa --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCAsmInfo.cpp @@ -0,0 +1,68 @@ +//===-- MaxisMCAsmInfo.cpp - Maxis Asm Properties ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declarations of the MaxisMCAsmInfo properties. +// +//===----------------------------------------------------------------------===// + +#include "MaxisMCAsmInfo.h" +#include "llvm/ADT/Triple.h" + +using namespace llvm; + +void MaxisMCAsmInfo::anchor() { } + +MaxisMCAsmInfo::MaxisMCAsmInfo(const Triple &TheTriple) { + IsLittleEndian = TheTriple.isLittleEndian(); + + if ((TheTriple.getArch() == Triple::maxis64el) || + (TheTriple.getArch() == Triple::maxis64)) { + CodePointerSize = CalleeSaveStackSlotSize = 8; + } + + // FIXME: This condition isn't quite right but it's the best we can do until + // this object can identify the ABI. It will misbehave when using O32 + // on a maxis64*-* triple. + if ((TheTriple.getArch() == Triple::maxisel) || + (TheTriple.getArch() == Triple::maxis)) { + PrivateGlobalPrefix = "$"; + PrivateLabelPrefix = "$"; + } + + AlignmentIsInBytes = false; + Data16bitsDirective = "\t.2byte\t"; + Data32bitsDirective = "\t.4byte\t"; + Data64bitsDirective = "\t.8byte\t"; + CommentString = "#"; + ZeroDirective = "\t.space\t"; + GPRel32Directive = "\t.gpword\t"; + GPRel64Directive = "\t.gpdword\t"; + DTPRel32Directive = "\t.dtprelword\t"; + DTPRel64Directive = "\t.dtpreldword\t"; + TPRel32Directive = "\t.tprelword\t"; + TPRel64Directive = "\t.tpreldword\t"; + UseAssignmentForEHBegin = true; + SupportsDebugInformation = true; + ExceptionsType = ExceptionHandling::DwarfCFI; + DwarfRegNumForCFI = true; + HasMaxisExpressions = true; + + // Enable IAS by default for O32. + if (TheTriple.getArch() == Triple::maxis || + TheTriple.getArch() == Triple::maxisel) + UseIntegratedAssembler = true; + + // Enable IAS by default for Debian maxis64/maxis64el. + if (TheTriple.getEnvironment() == Triple::GNUABI64) + UseIntegratedAssembler = true; + + // Enable IAS by default for Android maxis64el that uses N64 ABI. + if (TheTriple.getArch() == Triple::maxis64el && TheTriple.isAndroid()) + UseIntegratedAssembler = true; +} diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCAsmInfo.h b/lib/Target/Maxis/MCTargetDesc/MaxisMCAsmInfo.h new file mode 100644 index 00000000..c2a2c1c8 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCAsmInfo.h @@ -0,0 +1,31 @@ +//===-- MaxisMCAsmInfo.h - Maxis Asm Info ------------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declaration of the MaxisMCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCASMINFO_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCASMINFO_H + +#include "llvm/MC/MCAsmInfoELF.h" + +namespace llvm { +class Triple; + +class MaxisMCAsmInfo : public MCAsmInfoELF { + void anchor() override; + +public: + explicit MaxisMCAsmInfo(const Triple &TheTriple); +}; + +} // namespace llvm + +#endif diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCCodeEmitter.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisMCCodeEmitter.cpp new file mode 100644 index 00000000..a8255334 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCCodeEmitter.cpp @@ -0,0 +1,1136 @@ +//===-- MaxisMCCodeEmitter.cpp - Convert Maxis Code to Machine Code ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the MaxisMCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#include "MaxisMCCodeEmitter.h" +#include "MCTargetDesc/MaxisFixupKinds.h" +#include "MCTargetDesc/MaxisMCExpr.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "mccodeemitter" + +#define GET_INSTRMAP_INFO +#include "MaxisGenInstrInfo.inc" +#undef GET_INSTRMAP_INFO + +namespace llvm { + +MCCodeEmitter *createMaxisMCCodeEmitterEB(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new MaxisMCCodeEmitter(MCII, Ctx, false); +} + +MCCodeEmitter *createMaxisMCCodeEmitterEL(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new MaxisMCCodeEmitter(MCII, Ctx, true); +} + +} // end namespace llvm + +// If the D instruction has a shift amount that is greater +// than 31 (checked in calling routine), lower it to a D32 instruction +static void LowerLargeShift(MCInst& Inst) { + assert(Inst.getNumOperands() == 3 && "Invalid no. of operands for shift!"); + assert(Inst.getOperand(2).isImm()); + + int64_t Shift = Inst.getOperand(2).getImm(); + if (Shift <= 31) + return; // Do nothing + Shift -= 32; + + // saminus32 + Inst.getOperand(2).setImm(Shift); + + switch (Inst.getOpcode()) { + default: + // Calling function is not synchronized + llvm_unreachable("Unexpected shift instruction"); + case Maxis::DSLL: + Inst.setOpcode(Maxis::DSLL32); + return; + case Maxis::DSRL: + Inst.setOpcode(Maxis::DSRL32); + return; + case Maxis::DSRA: + Inst.setOpcode(Maxis::DSRA32); + return; + case Maxis::DROTR: + Inst.setOpcode(Maxis::DROTR32); + return; + } +} + +// Fix a bad compact branch encoding for beqc/bnec. +void MaxisMCCodeEmitter::LowerCompactBranch(MCInst& Inst) const { + // Encoding may be illegal !(rs < rt), but this situation is + // easily fixed. + unsigned RegOp0 = Inst.getOperand(0).getReg(); + unsigned RegOp1 = Inst.getOperand(1).getReg(); + + unsigned Reg0 = Ctx.getRegisterInfo()->getEncodingValue(RegOp0); + unsigned Reg1 = Ctx.getRegisterInfo()->getEncodingValue(RegOp1); + + if (Inst.getOpcode() == Maxis::BNEC || Inst.getOpcode() == Maxis::BEQC || + Inst.getOpcode() == Maxis::BNEC64 || Inst.getOpcode() == Maxis::BEQC64) { + assert(Reg0 != Reg1 && "Instruction has bad operands ($rs == $rt)!"); + if (Reg0 < Reg1) + return; + } else if (Inst.getOpcode() == Maxis::BNVC || Inst.getOpcode() == Maxis::BOVC) { + if (Reg0 >= Reg1) + return; + } else if (Inst.getOpcode() == Maxis::BNVC_MMR6 || + Inst.getOpcode() == Maxis::BOVC_MMR6) { + if (Reg1 >= Reg0) + return; + } else + llvm_unreachable("Cannot rewrite unknown branch!"); + + Inst.getOperand(0).setReg(RegOp1); + Inst.getOperand(1).setReg(RegOp0); +} + +bool MaxisMCCodeEmitter::isMicroMaxis(const MCSubtargetInfo &STI) const { + return STI.getFeatureBits()[Maxis::FeatureMicroMaxis]; +} + +bool MaxisMCCodeEmitter::isMaxis32r6(const MCSubtargetInfo &STI) const { + return STI.getFeatureBits()[Maxis::FeatureMaxis32r6]; +} + +void MaxisMCCodeEmitter::EmitByte(unsigned char C, raw_ostream &OS) const { + OS << (char)C; +} + +void MaxisMCCodeEmitter::EmitInstruction(uint64_t Val, unsigned Size, + const MCSubtargetInfo &STI, + raw_ostream &OS) const { + // Output the instruction encoding in little endian byte order. + // Little-endian byte ordering: + // maxis32r2: 4 | 3 | 2 | 1 + // microMAXIS: 2 | 1 | 4 | 3 + if (IsLittleEndian && Size == 4 && isMicroMaxis(STI)) { + EmitInstruction(Val >> 16, 2, STI, OS); + EmitInstruction(Val, 2, STI, OS); + } else { + for (unsigned i = 0; i < Size; ++i) { + unsigned Shift = IsLittleEndian ? i * 8 : (Size - 1 - i) * 8; + EmitByte((Val >> Shift) & 0xff, OS); + } + } +} + +/// encodeInstruction - Emit the instruction. +/// Size the instruction with Desc.getSize(). +void MaxisMCCodeEmitter:: +encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const +{ + // Non-pseudo instructions that get changed for direct object + // only based on operand values. + // If this list of instructions get much longer we will move + // the check to a function call. Until then, this is more efficient. + MCInst TmpInst = MI; + switch (MI.getOpcode()) { + // If shift amount is >= 32 it the inst needs to be lowered further + case Maxis::DSLL: + case Maxis::DSRL: + case Maxis::DSRA: + case Maxis::DROTR: + LowerLargeShift(TmpInst); + break; + // Compact branches, enforce encoding restrictions. + case Maxis::BEQC: + case Maxis::BNEC: + case Maxis::BEQC64: + case Maxis::BNEC64: + case Maxis::BOVC: + case Maxis::BOVC_MMR6: + case Maxis::BNVC: + case Maxis::BNVC_MMR6: + LowerCompactBranch(TmpInst); + } + + unsigned long N = Fixups.size(); + uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + + // Check for unimplemented opcodes. + // Unfortunately in MAXIS both NOP and SLL will come in with Binary == 0 + // so we have to special check for them. + unsigned Opcode = TmpInst.getOpcode(); + if ((Opcode != Maxis::NOP) && (Opcode != Maxis::SLL) && + (Opcode != Maxis::SLL_MM) && (Opcode != Maxis::SLL_MMR6) && !Binary) + llvm_unreachable("unimplemented opcode in encodeInstruction()"); + + int NewOpcode = -1; + if (isMicroMaxis(STI)) { + if (isMaxis32r6(STI)) { + NewOpcode = Maxis::MaxisR62MicroMaxisR6(Opcode, Maxis::Arch_micromaxisr6); + if (NewOpcode == -1) + NewOpcode = Maxis::Std2MicroMaxisR6(Opcode, Maxis::Arch_micromaxisr6); + } + else + NewOpcode = Maxis::Std2MicroMaxis(Opcode, Maxis::Arch_micromaxis); + + // Check whether it is Dsp instruction. + if (NewOpcode == -1) + NewOpcode = Maxis::Dsp2MicroMaxis(Opcode, Maxis::Arch_mmdsp); + + if (NewOpcode != -1) { + if (Fixups.size() > N) + Fixups.pop_back(); + + Opcode = NewOpcode; + TmpInst.setOpcode (NewOpcode); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + } + } + + const MCInstrDesc &Desc = MCII.get(TmpInst.getOpcode()); + + // Get byte count of instruction + unsigned Size = Desc.getSize(); + if (!Size) + llvm_unreachable("Desc.getSize() returns 0"); + + EmitInstruction(Binary, Size, STI, OS); +} + +/// getBranchTargetOpValue - Return binary encoding of the branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTargetOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 4. + if (MO.isImm()) return MO.getImm() >> 2; + + assert(MO.isExpr() && + "getBranchTargetOpValue expects only expressions or immediates"); + + const MCExpr *FixupExpression = MCBinaryExpr::createAdd( + MO.getExpr(), MCConstantExpr::create(-4, Ctx), Ctx); + Fixups.push_back(MCFixup::create(0, FixupExpression, + MCFixupKind(Maxis::fixup_Maxis_PC16))); + return 0; +} + +/// getBranchTargetOpValue1SImm16 - Return binary encoding of the branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTargetOpValue1SImm16(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 2. + if (MO.isImm()) return MO.getImm() >> 1; + + assert(MO.isExpr() && + "getBranchTargetOpValue expects only expressions or immediates"); + + const MCExpr *FixupExpression = MCBinaryExpr::createAdd( + MO.getExpr(), MCConstantExpr::create(-4, Ctx), Ctx); + Fixups.push_back(MCFixup::create(0, FixupExpression, + MCFixupKind(Maxis::fixup_Maxis_PC16))); + return 0; +} + +/// getBranchTargetOpValueMMR6 - Return binary encoding of the branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTargetOpValueMMR6(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 2. + if (MO.isImm()) + return MO.getImm() >> 1; + + assert(MO.isExpr() && + "getBranchTargetOpValueMMR6 expects only expressions or immediates"); + + const MCExpr *FixupExpression = MCBinaryExpr::createAdd( + MO.getExpr(), MCConstantExpr::create(-2, Ctx), Ctx); + Fixups.push_back(MCFixup::create(0, FixupExpression, + MCFixupKind(Maxis::fixup_Maxis_PC16))); + return 0; +} + +/// getBranchTargetOpValueLsl2MMR6 - Return binary encoding of the branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTargetOpValueLsl2MMR6(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 4. + if (MO.isImm()) + return MO.getImm() >> 2; + + assert(MO.isExpr() && + "getBranchTargetOpValueLsl2MMR6 expects only expressions or immediates"); + + const MCExpr *FixupExpression = MCBinaryExpr::createAdd( + MO.getExpr(), MCConstantExpr::create(-4, Ctx), Ctx); + Fixups.push_back(MCFixup::create(0, FixupExpression, + MCFixupKind(Maxis::fixup_Maxis_PC16))); + return 0; +} + +/// getBranchTarget7OpValueMM - Return binary encoding of the microMAXIS branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTarget7OpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 2. + if (MO.isImm()) return MO.getImm() >> 1; + + assert(MO.isExpr() && + "getBranchTargetOpValueMM expects only expressions or immediates"); + + const MCExpr *Expr = MO.getExpr(); + Fixups.push_back(MCFixup::create(0, Expr, + MCFixupKind(Maxis::fixup_MICROMAXIS_PC7_S1))); + return 0; +} + +/// getBranchTargetOpValueMMPC10 - Return binary encoding of the microMAXIS +/// 10-bit branch target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTargetOpValueMMPC10(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 2. + if (MO.isImm()) return MO.getImm() >> 1; + + assert(MO.isExpr() && + "getBranchTargetOpValuePC10 expects only expressions or immediates"); + + const MCExpr *Expr = MO.getExpr(); + Fixups.push_back(MCFixup::create(0, Expr, + MCFixupKind(Maxis::fixup_MICROMAXIS_PC10_S1))); + return 0; +} + +/// getBranchTargetOpValue - Return binary encoding of the microMAXIS branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTargetOpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 2. + if (MO.isImm()) return MO.getImm() >> 1; + + assert(MO.isExpr() && + "getBranchTargetOpValueMM expects only expressions or immediates"); + + const MCExpr *Expr = MO.getExpr(); + Fixups.push_back(MCFixup::create(0, Expr, + MCFixupKind(Maxis:: + fixup_MICROMAXIS_PC16_S1))); + return 0; +} + +/// getBranchTarget21OpValue - Return binary encoding of the branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTarget21OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 4. + if (MO.isImm()) return MO.getImm() >> 2; + + assert(MO.isExpr() && + "getBranchTarget21OpValue expects only expressions or immediates"); + + const MCExpr *FixupExpression = MCBinaryExpr::createAdd( + MO.getExpr(), MCConstantExpr::create(-4, Ctx), Ctx); + Fixups.push_back(MCFixup::create(0, FixupExpression, + MCFixupKind(Maxis::fixup_MAXIS_PC21_S2))); + return 0; +} + +/// getBranchTarget21OpValueMM - Return binary encoding of the branch +/// target operand for microMAXIS. If the machine operand requires +/// relocation, record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTarget21OpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 4. + if (MO.isImm()) return MO.getImm() >> 2; + + assert(MO.isExpr() && + "getBranchTarget21OpValueMM expects only expressions or immediates"); + + const MCExpr *FixupExpression = MCBinaryExpr::createAdd( + MO.getExpr(), MCConstantExpr::create(-4, Ctx), Ctx); + Fixups.push_back(MCFixup::create(0, FixupExpression, + MCFixupKind(Maxis::fixup_MICROMAXIS_PC21_S1))); + return 0; +} + +/// getBranchTarget26OpValue - Return binary encoding of the branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getBranchTarget26OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 4. + if (MO.isImm()) return MO.getImm() >> 2; + + assert(MO.isExpr() && + "getBranchTarget26OpValue expects only expressions or immediates"); + + const MCExpr *FixupExpression = MCBinaryExpr::createAdd( + MO.getExpr(), MCConstantExpr::create(-4, Ctx), Ctx); + Fixups.push_back(MCFixup::create(0, FixupExpression, + MCFixupKind(Maxis::fixup_MAXIS_PC26_S2))); + return 0; +} + +/// getBranchTarget26OpValueMM - Return binary encoding of the branch +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter::getBranchTarget26OpValueMM( + const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + // If the destination is an immediate, divide by 2. + if (MO.isImm()) + return MO.getImm() >> 1; + + assert(MO.isExpr() && + "getBranchTarget26OpValueMM expects only expressions or immediates"); + + const MCExpr *FixupExpression = MCBinaryExpr::createAdd( + MO.getExpr(), MCConstantExpr::create(-4, Ctx), Ctx); + Fixups.push_back(MCFixup::create(0, FixupExpression, + MCFixupKind(Maxis::fixup_MICROMAXIS_PC26_S1))); + return 0; +} + +/// getJumpOffset16OpValue - Return binary encoding of the jump +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getJumpOffset16OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + + if (MO.isImm()) return MO.getImm(); + + assert(MO.isExpr() && + "getJumpOffset16OpValue expects only expressions or an immediate"); + + // TODO: Push fixup. + return 0; +} + +/// getJumpTargetOpValue - Return binary encoding of the jump +/// target operand. If the machine operand requires relocation, +/// record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getJumpTargetOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + // If the destination is an immediate, divide by 4. + if (MO.isImm()) return MO.getImm()>>2; + + assert(MO.isExpr() && + "getJumpTargetOpValue expects only expressions or an immediate"); + + const MCExpr *Expr = MO.getExpr(); + Fixups.push_back(MCFixup::create(0, Expr, + MCFixupKind(Maxis::fixup_Maxis_26))); + return 0; +} + +unsigned MaxisMCCodeEmitter:: +getJumpTargetOpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + // If the destination is an immediate, divide by 2. + if (MO.isImm()) return MO.getImm() >> 1; + + assert(MO.isExpr() && + "getJumpTargetOpValueMM expects only expressions or an immediate"); + + const MCExpr *Expr = MO.getExpr(); + Fixups.push_back(MCFixup::create(0, Expr, + MCFixupKind(Maxis::fixup_MICROMAXIS_26_S1))); + return 0; +} + +unsigned MaxisMCCodeEmitter:: +getUImm5Lsl2Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + if (MO.isImm()) { + // The immediate is encoded as 'immediate << 2'. + unsigned Res = getMachineOpValue(MI, MO, Fixups, STI); + assert((Res & 3) == 0); + return Res >> 2; + } + + assert(MO.isExpr() && + "getUImm5Lsl2Encoding expects only expressions or an immediate"); + + return 0; +} + +unsigned MaxisMCCodeEmitter:: +getSImm3Lsa2Value(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + if (MO.isImm()) { + int Value = MO.getImm(); + return Value >> 2; + } + + return 0; +} + +unsigned MaxisMCCodeEmitter:: +getUImm6Lsl2Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + if (MO.isImm()) { + unsigned Value = MO.getImm(); + return Value >> 2; + } + + return 0; +} + +unsigned MaxisMCCodeEmitter:: +getSImm9AddiuspValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + if (MO.isImm()) { + unsigned Binary = (MO.getImm() >> 2) & 0x0000ffff; + return (((Binary & 0x8000) >> 7) | (Binary & 0x00ff)); + } + + return 0; +} + +unsigned MaxisMCCodeEmitter:: +getExprOpValue(const MCExpr *Expr, SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + int64_t Res; + + if (Expr->evaluateAsAbsolute(Res)) + return Res; + + MCExpr::ExprKind Kind = Expr->getKind(); + if (Kind == MCExpr::Constant) { + return cast(Expr)->getValue(); + } + + if (Kind == MCExpr::Binary) { + unsigned Res = getExprOpValue(cast(Expr)->getLHS(), Fixups, STI); + Res += getExprOpValue(cast(Expr)->getRHS(), Fixups, STI); + return Res; + } + + if (Kind == MCExpr::Target) { + const MaxisMCExpr *MaxisExpr = cast(Expr); + + Maxis::Fixups FixupKind = Maxis::Fixups(0); + switch (MaxisExpr->getKind()) { + case MaxisMCExpr::MEK_None: + case MaxisMCExpr::MEK_Special: + llvm_unreachable("Unhandled fixup kind!"); + break; + case MaxisMCExpr::MEK_CALL_HI16: + FixupKind = Maxis::fixup_Maxis_CALL_HI16; + break; + case MaxisMCExpr::MEK_CALL_LO16: + FixupKind = Maxis::fixup_Maxis_CALL_LO16; + break; + case MaxisMCExpr::MEK_DTPREL_HI: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_TLS_DTPREL_HI16 + : Maxis::fixup_Maxis_DTPREL_HI; + break; + case MaxisMCExpr::MEK_DTPREL_LO: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_TLS_DTPREL_LO16 + : Maxis::fixup_Maxis_DTPREL_LO; + break; + case MaxisMCExpr::MEK_GOTTPREL: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_GOTTPREL + : Maxis::fixup_Maxis_GOTTPREL; + break; + case MaxisMCExpr::MEK_GOT: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_GOT16 + : Maxis::fixup_Maxis_GOT; + break; + case MaxisMCExpr::MEK_GOT_CALL: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_CALL16 + : Maxis::fixup_Maxis_CALL16; + break; + case MaxisMCExpr::MEK_GOT_DISP: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_GOT_DISP + : Maxis::fixup_Maxis_GOT_DISP; + break; + case MaxisMCExpr::MEK_GOT_HI16: + FixupKind = Maxis::fixup_Maxis_GOT_HI16; + break; + case MaxisMCExpr::MEK_GOT_LO16: + FixupKind = Maxis::fixup_Maxis_GOT_LO16; + break; + case MaxisMCExpr::MEK_GOT_PAGE: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_GOT_PAGE + : Maxis::fixup_Maxis_GOT_PAGE; + break; + case MaxisMCExpr::MEK_GOT_OFST: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_GOT_OFST + : Maxis::fixup_Maxis_GOT_OFST; + break; + case MaxisMCExpr::MEK_GPREL: + FixupKind = Maxis::fixup_Maxis_GPREL16; + break; + case MaxisMCExpr::MEK_LO: + // Check for %lo(%neg(%gp_rel(X))) + if (MaxisExpr->isGpOff()) { + FixupKind = Maxis::fixup_Maxis_GPOFF_LO; + break; + } + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_LO16 + : Maxis::fixup_Maxis_LO16; + break; + case MaxisMCExpr::MEK_HIGHEST: + FixupKind = Maxis::fixup_Maxis_HIGHEST; + break; + case MaxisMCExpr::MEK_HIGHER: + FixupKind = Maxis::fixup_Maxis_HIGHER; + break; + case MaxisMCExpr::MEK_HI: + // Check for %hi(%neg(%gp_rel(X))) + if (MaxisExpr->isGpOff()) { + FixupKind = Maxis::fixup_Maxis_GPOFF_HI; + break; + } + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_HI16 + : Maxis::fixup_Maxis_HI16; + break; + case MaxisMCExpr::MEK_PCREL_HI16: + FixupKind = Maxis::fixup_MAXIS_PCHI16; + break; + case MaxisMCExpr::MEK_PCREL_LO16: + FixupKind = Maxis::fixup_MAXIS_PCLO16; + break; + case MaxisMCExpr::MEK_TLSGD: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_TLS_GD + : Maxis::fixup_Maxis_TLSGD; + break; + case MaxisMCExpr::MEK_TLSLDM: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_TLS_LDM + : Maxis::fixup_Maxis_TLSLDM; + break; + case MaxisMCExpr::MEK_TPREL_HI: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_TLS_TPREL_HI16 + : Maxis::fixup_Maxis_TPREL_HI; + break; + case MaxisMCExpr::MEK_TPREL_LO: + FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_TLS_TPREL_LO16 + : Maxis::fixup_Maxis_TPREL_LO; + break; + case MaxisMCExpr::MEK_NEG: + FixupKind = + isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_SUB : Maxis::fixup_Maxis_SUB; + break; + } + Fixups.push_back(MCFixup::create(0, MaxisExpr, MCFixupKind(FixupKind))); + return 0; + } + + if (Kind == MCExpr::SymbolRef) { + Maxis::Fixups FixupKind = Maxis::Fixups(0); + + switch(cast(Expr)->getKind()) { + default: llvm_unreachable("Unknown fixup kind!"); + break; + case MCSymbolRefExpr::VK_None: + FixupKind = Maxis::fixup_Maxis_32; // FIXME: This is ok for O32/N32 but not N64. + break; + } // switch + + Fixups.push_back(MCFixup::create(0, Expr, MCFixupKind(FixupKind))); + return 0; + } + return 0; +} + +/// getMachineOpValue - Return binary encoding of operand. If the machine +/// operand requires relocation, record the relocation and return zero. +unsigned MaxisMCCodeEmitter:: +getMachineOpValue(const MCInst &MI, const MCOperand &MO, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + if (MO.isReg()) { + unsigned Reg = MO.getReg(); + unsigned RegNo = Ctx.getRegisterInfo()->getEncodingValue(Reg); + return RegNo; + } else if (MO.isImm()) { + return static_cast(MO.getImm()); + } else if (MO.isFPImm()) { + return static_cast(APFloat(MO.getFPImm()) + .bitcastToAPInt().getHiBits(32).getLimitedValue()); + } + // MO must be an Expr. + assert(MO.isExpr()); + return getExprOpValue(MO.getExpr(),Fixups, STI); +} + +/// Return binary encoding of memory related operand. +/// If the offset operand requires relocation, record the relocation. +template +unsigned MaxisMCCodeEmitter::getMemEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Base register is encoded in bits 20-16, offset is encoded in bits 15-0. + assert(MI.getOperand(OpNo).isReg()); + unsigned RegBits = getMachineOpValue(MI, MI.getOperand(OpNo),Fixups, STI) << 16; + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), Fixups, STI); + + // Apply the scale factor if there is one. + OffBits >>= ShiftAmount; + + return (OffBits & 0xFFFF) | RegBits; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMImm4(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Base register is encoded in bits 6-4, offset is encoded in bits 3-0. + assert(MI.getOperand(OpNo).isReg()); + unsigned RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), + Fixups, STI) << 4; + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), + Fixups, STI); + + return (OffBits & 0xF) | RegBits; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMImm4Lsl1(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Base register is encoded in bits 6-4, offset is encoded in bits 3-0. + assert(MI.getOperand(OpNo).isReg()); + unsigned RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), + Fixups, STI) << 4; + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), + Fixups, STI) >> 1; + + return (OffBits & 0xF) | RegBits; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMImm4Lsl2(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Base register is encoded in bits 6-4, offset is encoded in bits 3-0. + assert(MI.getOperand(OpNo).isReg()); + unsigned RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), + Fixups, STI) << 4; + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), + Fixups, STI) >> 2; + + return (OffBits & 0xF) | RegBits; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMSPImm5Lsl2(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Register is encoded in bits 9-5, offset is encoded in bits 4-0. + assert(MI.getOperand(OpNo).isReg() && + (MI.getOperand(OpNo).getReg() == Maxis::SP || + MI.getOperand(OpNo).getReg() == Maxis::SP_64) && + "Unexpected base register!"); + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), + Fixups, STI) >> 2; + + return OffBits & 0x1F; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMGPImm7Lsl2(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Register is encoded in bits 9-7, offset is encoded in bits 6-0. + assert(MI.getOperand(OpNo).isReg() && + MI.getOperand(OpNo).getReg() == Maxis::GP && + "Unexpected base register!"); + + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), + Fixups, STI) >> 2; + + return OffBits & 0x7F; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMImm9(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Base register is encoded in bits 20-16, offset is encoded in bits 8-0. + assert(MI.getOperand(OpNo).isReg()); + unsigned RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, + STI) << 16; + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo + 1), Fixups, STI); + + return (OffBits & 0x1FF) | RegBits; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMImm11(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Base register is encoded in bits 20-16, offset is encoded in bits 10-0. + assert(MI.getOperand(OpNo).isReg()); + unsigned RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, + STI) << 16; + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), Fixups, STI); + + return (OffBits & 0x07FF) | RegBits; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMImm12(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // opNum can be invalid if instruction had reglist as operand. + // MemOperand is always last operand of instruction (base + offset). + switch (MI.getOpcode()) { + default: + break; + case Maxis::SWM32_MM: + case Maxis::LWM32_MM: + OpNo = MI.getNumOperands() - 2; + break; + } + + // Base register is encoded in bits 20-16, offset is encoded in bits 11-0. + assert(MI.getOperand(OpNo).isReg()); + unsigned RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI) << 16; + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), Fixups, STI); + + return (OffBits & 0x0FFF) | RegBits; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMImm16(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Base register is encoded in bits 20-16, offset is encoded in bits 15-0. + assert(MI.getOperand(OpNo).isReg()); + unsigned RegBits = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, + STI) << 16; + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), Fixups, STI); + + return (OffBits & 0xFFFF) | RegBits; +} + +unsigned MaxisMCCodeEmitter:: +getMemEncodingMMImm4sp(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // opNum can be invalid if instruction had reglist as operand + // MemOperand is always last operand of instruction (base + offset) + switch (MI.getOpcode()) { + default: + break; + case Maxis::SWM16_MM: + case Maxis::SWM16_MMR6: + case Maxis::LWM16_MM: + case Maxis::LWM16_MMR6: + OpNo = MI.getNumOperands() - 2; + break; + } + + // Offset is encoded in bits 4-0. + assert(MI.getOperand(OpNo).isReg()); + // Base register is always SP - thus it is not encoded. + assert(MI.getOperand(OpNo+1).isImm()); + unsigned OffBits = getMachineOpValue(MI, MI.getOperand(OpNo+1), Fixups, STI); + + return ((OffBits >> 2) & 0x0F); +} + +// FIXME: should be called getMSBEncoding +// +unsigned +MaxisMCCodeEmitter::getSizeInsEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + assert(MI.getOperand(OpNo-1).isImm()); + assert(MI.getOperand(OpNo).isImm()); + unsigned Position = getMachineOpValue(MI, MI.getOperand(OpNo-1), Fixups, STI); + unsigned Size = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI); + + return Position + Size - 1; +} + +template +unsigned +MaxisMCCodeEmitter::getUImmWithOffsetEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + assert(MI.getOperand(OpNo).isImm()); + unsigned Value = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI); + Value -= Offset; + return Value; +} + +unsigned +MaxisMCCodeEmitter::getSimm19Lsl2Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + if (MO.isImm()) { + // The immediate is encoded as 'immediate << 2'. + unsigned Res = getMachineOpValue(MI, MO, Fixups, STI); + assert((Res & 3) == 0); + return Res >> 2; + } + + assert(MO.isExpr() && + "getSimm19Lsl2Encoding expects only expressions or an immediate"); + + const MCExpr *Expr = MO.getExpr(); + Maxis::Fixups FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_PC19_S2 + : Maxis::fixup_MAXIS_PC19_S2; + Fixups.push_back(MCFixup::create(0, Expr, MCFixupKind(FixupKind))); + return 0; +} + +unsigned +MaxisMCCodeEmitter::getSimm18Lsl3Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + if (MO.isImm()) { + // The immediate is encoded as 'immediate << 3'. + unsigned Res = getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI); + assert((Res & 7) == 0); + return Res >> 3; + } + + assert(MO.isExpr() && + "getSimm18Lsl2Encoding expects only expressions or an immediate"); + + const MCExpr *Expr = MO.getExpr(); + Maxis::Fixups FixupKind = isMicroMaxis(STI) ? Maxis::fixup_MICROMAXIS_PC18_S3 + : Maxis::fixup_MAXIS_PC18_S3; + Fixups.push_back(MCFixup::create(0, Expr, MCFixupKind(FixupKind))); + return 0; +} + +unsigned +MaxisMCCodeEmitter::getUImm3Mod8Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + assert(MI.getOperand(OpNo).isImm()); + const MCOperand &MO = MI.getOperand(OpNo); + return MO.getImm() % 8; +} + +unsigned +MaxisMCCodeEmitter::getUImm4AndValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + assert(MI.getOperand(OpNo).isImm()); + const MCOperand &MO = MI.getOperand(OpNo); + unsigned Value = MO.getImm(); + switch (Value) { + case 128: return 0x0; + case 1: return 0x1; + case 2: return 0x2; + case 3: return 0x3; + case 4: return 0x4; + case 7: return 0x5; + case 8: return 0x6; + case 15: return 0x7; + case 16: return 0x8; + case 31: return 0x9; + case 32: return 0xa; + case 63: return 0xb; + case 64: return 0xc; + case 255: return 0xd; + case 32768: return 0xe; + case 65535: return 0xf; + } + llvm_unreachable("Unexpected value"); +} + +unsigned +MaxisMCCodeEmitter::getRegisterListOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned res = 0; + + // Register list operand is always first operand of instruction and it is + // placed before memory operand (register + imm). + + for (unsigned I = OpNo, E = MI.getNumOperands() - 2; I < E; ++I) { + unsigned Reg = MI.getOperand(I).getReg(); + unsigned RegNo = Ctx.getRegisterInfo()->getEncodingValue(Reg); + if (RegNo != 31) + res++; + else + res |= 0x10; + } + return res; +} + +unsigned +MaxisMCCodeEmitter::getRegisterListOpValue16(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + return (MI.getNumOperands() - 4); +} + +unsigned +MaxisMCCodeEmitter::getRegisterPairOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + return getMachineOpValue(MI, MI.getOperand(OpNo), Fixups, STI); +} + +unsigned +MaxisMCCodeEmitter::getMovePRegPairOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned res = 0; + + if (MI.getOperand(0).getReg() == Maxis::A1 && + MI.getOperand(1).getReg() == Maxis::A2) + res = 0; + else if (MI.getOperand(0).getReg() == Maxis::A1 && + MI.getOperand(1).getReg() == Maxis::A3) + res = 1; + else if (MI.getOperand(0).getReg() == Maxis::A2 && + MI.getOperand(1).getReg() == Maxis::A3) + res = 2; + else if (MI.getOperand(0).getReg() == Maxis::A0 && + MI.getOperand(1).getReg() == Maxis::S5) + res = 3; + else if (MI.getOperand(0).getReg() == Maxis::A0 && + MI.getOperand(1).getReg() == Maxis::S6) + res = 4; + else if (MI.getOperand(0).getReg() == Maxis::A0 && + MI.getOperand(1).getReg() == Maxis::A1) + res = 5; + else if (MI.getOperand(0).getReg() == Maxis::A0 && + MI.getOperand(1).getReg() == Maxis::A2) + res = 6; + else if (MI.getOperand(0).getReg() == Maxis::A0 && + MI.getOperand(1).getReg() == Maxis::A3) + res = 7; + + return res; +} + +unsigned +MaxisMCCodeEmitter::getMovePRegSingleOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + assert(((OpNo == 2) || (OpNo == 3)) && + "Unexpected OpNo for movep operand encoding!"); + + MCOperand Op = MI.getOperand(OpNo); + assert(Op.isReg() && "Operand of movep is not a register!"); + switch (Op.getReg()) { + default: + llvm_unreachable("Unknown register for movep!"); + case Maxis::ZERO: return 0; + case Maxis::S1: return 1; + case Maxis::V0: return 2; + case Maxis::V1: return 3; + case Maxis::S0: return 4; + case Maxis::S2: return 5; + case Maxis::S3: return 6; + case Maxis::S4: return 7; + } +} + +unsigned +MaxisMCCodeEmitter::getSimm23Lsl2Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + const MCOperand &MO = MI.getOperand(OpNo); + assert(MO.isImm() && "getSimm23Lsl2Encoding expects only an immediate"); + // The immediate is encoded as 'immediate >> 2'. + unsigned Res = static_cast(MO.getImm()); + assert((Res & 3) == 0); + return Res >> 2; +} + +#include "MaxisGenMCCodeEmitter.inc" diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCCodeEmitter.h b/lib/Target/Maxis/MCTargetDesc/MaxisMCCodeEmitter.h new file mode 100644 index 00000000..1ccbabf1 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCCodeEmitter.h @@ -0,0 +1,280 @@ +//===- MaxisMCCodeEmitter.h - Convert Maxis Code to Machine Code --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the MaxisMCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCCODEEMITTER_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCCODEEMITTER_H + +#include "llvm/MC/MCCodeEmitter.h" +#include + +namespace llvm { + +class MCContext; +class MCExpr; +class MCFixup; +class MCInst; +class MCInstrInfo; +class MCOperand; +class MCSubtargetInfo; +class raw_ostream; + +class MaxisMCCodeEmitter : public MCCodeEmitter { + const MCInstrInfo &MCII; + MCContext &Ctx; + bool IsLittleEndian; + + bool isMicroMaxis(const MCSubtargetInfo &STI) const; + bool isMaxis32r6(const MCSubtargetInfo &STI) const; + +public: + MaxisMCCodeEmitter(const MCInstrInfo &mcii, MCContext &Ctx_, bool IsLittle) + : MCII(mcii), Ctx(Ctx_), IsLittleEndian(IsLittle) {} + MaxisMCCodeEmitter(const MaxisMCCodeEmitter &) = delete; + MaxisMCCodeEmitter &operator=(const MaxisMCCodeEmitter &) = delete; + ~MaxisMCCodeEmitter() override = default; + + void EmitByte(unsigned char C, raw_ostream &OS) const; + + void EmitInstruction(uint64_t Val, unsigned Size, const MCSubtargetInfo &STI, + raw_ostream &OS) const; + + void encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; + + // getBinaryCodeForInstr - TableGen'erated function for getting the + // binary encoding for an instruction. + uint64_t getBinaryCodeForInstr(const MCInst &MI, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getJumpTargetOpValue - Return binary encoding of the jump + // target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getJumpTargetOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchJumpOpValueMM - Return binary encoding of the microMAXIS jump + // target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getJumpTargetOpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getUImm5Lsl2Encoding - Return binary encoding of the microMAXIS jump + // target operand. + unsigned getUImm5Lsl2Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getSImm3Lsa2Value(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getUImm6Lsl2Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getSImm9AddiuspValue - Return binary encoding of the microMAXIS addiusp + // instruction immediate operand. + unsigned getSImm9AddiuspValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTargetOpValue - Return binary encoding of the branch + // target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTargetOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTargetOpValue1SImm16 - Return binary encoding of the branch + // target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTargetOpValue1SImm16(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTargetOpValueMMR6 - Return binary encoding of the branch + // target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTargetOpValueMMR6(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTargetOpValueLsl2MMR6 - Return binary encoding of the branch + // target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTargetOpValueLsl2MMR6(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTarget7OpValue - Return binary encoding of the microMAXIS branch + // target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTarget7OpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTargetOpValueMMPC10 - Return binary encoding of the microMAXIS + // 10-bit branch target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTargetOpValueMMPC10(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTargetOpValue - Return binary encoding of the microMAXIS branch + // target operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTargetOpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTarget21OpValue - Return binary encoding of the branch + // offset operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTarget21OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTarget21OpValueMM - Return binary encoding of the branch + // offset operand for microMAXIS. If the machine operand requires + // relocation,record the relocation and return zero. + unsigned getBranchTarget21OpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTarget26OpValue - Return binary encoding of the branch + // offset operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTarget26OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getBranchTarget26OpValueMM - Return binary encoding of the branch + // offset operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getBranchTarget26OpValueMM(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getJumpOffset16OpValue - Return binary encoding of the jump + // offset operand. If the machine operand requires relocation, + // record the relocation and return zero. + unsigned getJumpOffset16OpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + // getMachineOpValue - Return binary encoding of operand. If the machin + // operand requires relocation, record the relocation and return zero. + unsigned getMachineOpValue(const MCInst &MI, const MCOperand &MO, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getMSAMemEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + template + unsigned getMemEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMImm4(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMImm4Lsl1(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMImm4Lsl2(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMSPImm5Lsl2(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMGPImm7Lsl2(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMImm9(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMImm11(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMImm12(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMImm16(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMemEncodingMMImm4sp(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getSizeInsEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + /// Subtract Offset then encode as a N-bit unsigned integer. + template + unsigned getUImmWithOffsetEncoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getSimm19Lsl2Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getSimm18Lsl3Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getUImm3Mod8Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getUImm4AndValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getRegisterPairOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getMovePRegPairOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getMovePRegSingleOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getSimm23Lsl2Encoding(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getExprOpValue(const MCExpr *Expr, SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getRegisterListOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + + unsigned getRegisterListOpValue16(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + +private: + void LowerCompactBranch(MCInst& Inst) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCCODEEMITTER_H diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCExpr.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisMCExpr.cpp new file mode 100644 index 00000000..5c0bfd31 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCExpr.cpp @@ -0,0 +1,293 @@ +//===-- MaxisMCExpr.cpp - Maxis specific MC expression classes --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisMCExpr.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxismcexpr" + +const MaxisMCExpr *MaxisMCExpr::create(MaxisMCExpr::MaxisExprKind Kind, + const MCExpr *Expr, MCContext &Ctx) { + return new (Ctx) MaxisMCExpr(Kind, Expr); +} + +const MaxisMCExpr *MaxisMCExpr::createGpOff(MaxisMCExpr::MaxisExprKind Kind, + const MCExpr *Expr, MCContext &Ctx) { + return create(Kind, create(MEK_NEG, create(MEK_GPREL, Expr, Ctx), Ctx), Ctx); +} + +void MaxisMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + int64_t AbsVal; + + switch (Kind) { + case MEK_None: + case MEK_Special: + llvm_unreachable("MEK_None and MEK_Special are invalid"); + break; + case MEK_CALL_HI16: + OS << "%call_hi"; + break; + case MEK_CALL_LO16: + OS << "%call_lo"; + break; + case MEK_DTPREL_HI: + OS << "%dtprel_hi"; + break; + case MEK_DTPREL_LO: + OS << "%dtprel_lo"; + break; + case MEK_GOT: + OS << "%got"; + break; + case MEK_GOTTPREL: + OS << "%gottprel"; + break; + case MEK_GOT_CALL: + OS << "%call16"; + break; + case MEK_GOT_DISP: + OS << "%got_disp"; + break; + case MEK_GOT_HI16: + OS << "%got_hi"; + break; + case MEK_GOT_LO16: + OS << "%got_lo"; + break; + case MEK_GOT_PAGE: + OS << "%got_page"; + break; + case MEK_GOT_OFST: + OS << "%got_ofst"; + break; + case MEK_GPREL: + OS << "%gp_rel"; + break; + case MEK_HI: + OS << "%hi"; + break; + case MEK_HIGHER: + OS << "%higher"; + break; + case MEK_HIGHEST: + OS << "%highest"; + break; + case MEK_LO: + OS << "%lo"; + break; + case MEK_NEG: + OS << "%neg"; + break; + case MEK_PCREL_HI16: + OS << "%pcrel_hi"; + break; + case MEK_PCREL_LO16: + OS << "%pcrel_lo"; + break; + case MEK_TLSGD: + OS << "%tlsgd"; + break; + case MEK_TLSLDM: + OS << "%tlsldm"; + break; + case MEK_TPREL_HI: + OS << "%tprel_hi"; + break; + case MEK_TPREL_LO: + OS << "%tprel_lo"; + break; + } + + OS << '('; + if (Expr->evaluateAsAbsolute(AbsVal)) + OS << AbsVal; + else + Expr->print(OS, MAI, true); + OS << ')'; +} + +bool +MaxisMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + // Look for the %hi(%neg(%gp_rel(X))) and %lo(%neg(%gp_rel(X))) special cases. + if (isGpOff()) { + const MCExpr *SubExpr = + cast(cast(getSubExpr())->getSubExpr()) + ->getSubExpr(); + if (!SubExpr->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + Res = MCValue::get(Res.getSymA(), Res.getSymB(), Res.getConstant(), + MEK_Special); + return true; + } + + if (!getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup)) + return false; + + if (Res.getRefKind() != MCSymbolRefExpr::VK_None) + return false; + + // evaluateAsAbsolute() and evaluateAsValue() require that we evaluate the + // %hi/%lo/etc. here. Fixup is a null pointer when either of these is the + // caller. + if (Res.isAbsolute() && Fixup == nullptr) { + int64_t AbsVal = Res.getConstant(); + switch (Kind) { + case MEK_None: + case MEK_Special: + llvm_unreachable("MEK_None and MEK_Special are invalid"); + case MEK_DTPREL_HI: + case MEK_DTPREL_LO: + case MEK_GOT: + case MEK_GOTTPREL: + case MEK_GOT_CALL: + case MEK_GOT_DISP: + case MEK_GOT_HI16: + case MEK_GOT_LO16: + case MEK_GOT_OFST: + case MEK_GOT_PAGE: + case MEK_GPREL: + case MEK_PCREL_HI16: + case MEK_PCREL_LO16: + case MEK_TLSGD: + case MEK_TLSLDM: + case MEK_TPREL_HI: + case MEK_TPREL_LO: + return false; + case MEK_LO: + case MEK_CALL_LO16: + AbsVal = SignExtend64<16>(AbsVal); + break; + case MEK_CALL_HI16: + case MEK_HI: + AbsVal = SignExtend64<16>((AbsVal + 0x8000) >> 16); + break; + case MEK_HIGHER: + AbsVal = SignExtend64<16>((AbsVal + 0x80008000LL) >> 32); + break; + case MEK_HIGHEST: + AbsVal = SignExtend64<16>((AbsVal + 0x800080008000LL) >> 48); + break; + case MEK_NEG: + AbsVal = -AbsVal; + break; + } + Res = MCValue::get(AbsVal); + return true; + } + + // We want to defer it for relocatable expressions since the constant is + // applied to the whole symbol value. + // + // The value of getKind() that is given to MCValue is only intended to aid + // debugging when inspecting MCValue objects. It shouldn't be relied upon + // for decision making. + Res = MCValue::get(Res.getSymA(), Res.getSymB(), Res.getConstant(), getKind()); + + return true; +} + +void MaxisMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +static void fixELFSymbolsInTLSFixupsImpl(const MCExpr *Expr, MCAssembler &Asm) { + switch (Expr->getKind()) { + case MCExpr::Target: + fixELFSymbolsInTLSFixupsImpl(cast(Expr)->getSubExpr(), Asm); + break; + case MCExpr::Constant: + break; + case MCExpr::Binary: { + const MCBinaryExpr *BE = cast(Expr); + fixELFSymbolsInTLSFixupsImpl(BE->getLHS(), Asm); + fixELFSymbolsInTLSFixupsImpl(BE->getRHS(), Asm); + break; + } + case MCExpr::SymbolRef: { + // We're known to be under a TLS fixup, so any symbol should be + // modified. There should be only one. + const MCSymbolRefExpr &SymRef = *cast(Expr); + cast(SymRef.getSymbol()).setType(ELF::STT_TLS); + break; + } + case MCExpr::Unary: + fixELFSymbolsInTLSFixupsImpl(cast(Expr)->getSubExpr(), Asm); + break; + } +} + +void MaxisMCExpr::fixELFSymbolsInTLSFixups(MCAssembler &Asm) const { + switch (getKind()) { + case MEK_None: + case MEK_Special: + llvm_unreachable("MEK_None and MEK_Special are invalid"); + break; + case MEK_CALL_HI16: + case MEK_CALL_LO16: + case MEK_GOT: + case MEK_GOT_CALL: + case MEK_GOT_DISP: + case MEK_GOT_HI16: + case MEK_GOT_LO16: + case MEK_GOT_OFST: + case MEK_GOT_PAGE: + case MEK_GPREL: + case MEK_HI: + case MEK_HIGHER: + case MEK_HIGHEST: + case MEK_LO: + case MEK_NEG: + case MEK_PCREL_HI16: + case MEK_PCREL_LO16: + // If we do have nested target-specific expressions, they will be in + // a consecutive chain. + if (const MaxisMCExpr *E = dyn_cast(getSubExpr())) + E->fixELFSymbolsInTLSFixups(Asm); + break; + case MEK_DTPREL_HI: + case MEK_DTPREL_LO: + case MEK_TLSLDM: + case MEK_TLSGD: + case MEK_GOTTPREL: + case MEK_TPREL_HI: + case MEK_TPREL_LO: + fixELFSymbolsInTLSFixupsImpl(getSubExpr(), Asm); + break; + } +} + +bool MaxisMCExpr::isGpOff(MaxisExprKind &Kind) const { + if (getKind() == MEK_HI || getKind() == MEK_LO) { + if (const MaxisMCExpr *S1 = dyn_cast(getSubExpr())) { + if (const MaxisMCExpr *S2 = dyn_cast(S1->getSubExpr())) { + if (S1->getKind() == MEK_NEG && S2->getKind() == MEK_GPREL) { + Kind = getKind(); + return true; + } + } + } + } + return false; +} diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCExpr.h b/lib/Target/Maxis/MCTargetDesc/MaxisMCExpr.h new file mode 100644 index 00000000..80cbaac6 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCExpr.h @@ -0,0 +1,93 @@ +//===- MaxisMCExpr.h - Maxis specific MC expression classes -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCEXPR_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCEXPR_H + +#include "llvm/MC/MCAsmLayout.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCValue.h" + +namespace llvm { + +class MaxisMCExpr : public MCTargetExpr { +public: + enum MaxisExprKind { + MEK_None, + MEK_CALL_HI16, + MEK_CALL_LO16, + MEK_DTPREL_HI, + MEK_DTPREL_LO, + MEK_GOT, + MEK_GOTTPREL, + MEK_GOT_CALL, + MEK_GOT_DISP, + MEK_GOT_HI16, + MEK_GOT_LO16, + MEK_GOT_OFST, + MEK_GOT_PAGE, + MEK_GPREL, + MEK_HI, + MEK_HIGHER, + MEK_HIGHEST, + MEK_LO, + MEK_NEG, + MEK_PCREL_HI16, + MEK_PCREL_LO16, + MEK_TLSGD, + MEK_TLSLDM, + MEK_TPREL_HI, + MEK_TPREL_LO, + MEK_Special, + }; + +private: + const MaxisExprKind Kind; + const MCExpr *Expr; + + explicit MaxisMCExpr(MaxisExprKind Kind, const MCExpr *Expr) + : Kind(Kind), Expr(Expr) {} + +public: + static const MaxisMCExpr *create(MaxisExprKind Kind, const MCExpr *Expr, + MCContext &Ctx); + static const MaxisMCExpr *createGpOff(MaxisExprKind Kind, const MCExpr *Expr, + MCContext &Ctx); + + /// Get the kind of this expression. + MaxisExprKind getKind() const { return Kind; } + + /// Get the child of this expression. + const MCExpr *getSubExpr() const { return Expr; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + void visitUsedExpr(MCStreamer &Streamer) const override; + + MCFragment *findAssociatedFragment() const override { + return getSubExpr()->findAssociatedFragment(); + } + + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override; + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } + + bool isGpOff(MaxisExprKind &Kind) const; + bool isGpOff() const { + MaxisExprKind Kind; + return isGpOff(Kind); + } +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCEXPR_H diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCNaCl.h b/lib/Target/Maxis/MCTargetDesc/MaxisMCNaCl.h new file mode 100644 index 00000000..a7e1cd5f --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCNaCl.h @@ -0,0 +1,32 @@ +//===-- MaxisMCNaCl.h - NaCl-related declarations --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCNACL_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCNACL_H + +#include "llvm/MC/MCELFStreamer.h" + +namespace llvm { + +// Log2 of the NaCl MAXIS sandbox's instruction bundle size. +static const unsigned MAXIS_NACL_BUNDLE_ALIGN = 4u; + +bool isBasePlusOffsetMemoryAccess(unsigned Opcode, unsigned *AddrIdx, + bool *IsStore = nullptr); +bool baseRegNeedsLoadStoreMask(unsigned Reg); + +// This function creates an MCELFStreamer for Maxis NaCl. +MCELFStreamer *createMaxisNaClELFStreamer(MCContext &Context, + std::unique_ptr TAB, + raw_pwrite_stream &OS, + std::unique_ptr Emitter, + bool RelaxAll); +} + +#endif diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCTargetDesc.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisMCTargetDesc.cpp new file mode 100644 index 00000000..14de8216 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCTargetDesc.cpp @@ -0,0 +1,200 @@ +//===-- MaxisMCTargetDesc.cpp - Maxis Target Descriptions -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Maxis specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#include "MaxisMCTargetDesc.h" +#include "InstPrinter/MaxisInstPrinter.h" +#include "MaxisAsmBackend.h" +#include "MaxisELFStreamer.h" +#include "MaxisMCAsmInfo.h" +#include "MaxisMCNaCl.h" +#include "MaxisTargetStreamer.h" +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCInstrAnalysis.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/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +#define GET_INSTRINFO_MC_DESC +#include "MaxisGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "MaxisGenSubtargetInfo.inc" + +#define GET_REGINFO_MC_DESC +#include "MaxisGenRegisterInfo.inc" + +/// Select the Maxis CPU for the given triple and cpu name. +/// FIXME: Merge with the copy in MaxisSubtarget.cpp +StringRef MAXIS_MC::selectMaxisCPU(const Triple &TT, StringRef CPU) { + if (CPU.empty() || CPU == "generic") { + if (TT.getArch() == Triple::maxis || TT.getArch() == Triple::maxisel) + CPU = "maxis32"; + else + CPU = "maxis64"; + } + return CPU; +} + +static MCInstrInfo *createMaxisMCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitMaxisMCInstrInfo(X); + return X; +} + +static MCRegisterInfo *createMaxisMCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitMaxisMCRegisterInfo(X, Maxis::RA); + return X; +} + +static MCSubtargetInfo *createMaxisMCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + CPU = MAXIS_MC::selectMaxisCPU(TT, CPU); + return createMaxisMCSubtargetInfoImpl(TT, CPU, FS); +} + +static MCAsmInfo *createMaxisMCAsmInfo(const MCRegisterInfo &MRI, + const Triple &TT) { + MCAsmInfo *MAI = new MaxisMCAsmInfo(TT); + + unsigned SP = MRI.getDwarfRegNum(Maxis::SP, true); + MCCFIInstruction Inst = MCCFIInstruction::createDefCfa(nullptr, SP, 0); + MAI->addInitialFrameState(Inst); + + return MAI; +} + +static MCInstPrinter *createMaxisMCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + return new MaxisInstPrinter(MAI, MII, MRI); +} + +static MCStreamer *createMCStreamer(const Triple &T, MCContext &Context, + std::unique_ptr &&MAB, + raw_pwrite_stream &OS, + std::unique_ptr &&Emitter, + bool RelaxAll) { + MCStreamer *S; + if (!T.isOSNaCl()) + S = createMaxisELFStreamer(Context, std::move(MAB), OS, std::move(Emitter), + RelaxAll); + else + S = createMaxisNaClELFStreamer(Context, std::move(MAB), OS, + std::move(Emitter), RelaxAll); + return S; +} + +static MCTargetStreamer *createMaxisAsmTargetStreamer(MCStreamer &S, + formatted_raw_ostream &OS, + MCInstPrinter *InstPrint, + bool isVerboseAsm) { + return new MaxisTargetAsmStreamer(S, OS); +} + +static MCTargetStreamer *createMaxisNullTargetStreamer(MCStreamer &S) { + return new MaxisTargetStreamer(S); +} + +static MCTargetStreamer * +createMaxisObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + return new MaxisTargetELFStreamer(S, STI); +} + +namespace { + +class MaxisMCInstrAnalysis : public MCInstrAnalysis { +public: + MaxisMCInstrAnalysis(const MCInstrInfo *Info) : MCInstrAnalysis(Info) {} + + bool evaluateBranch(const MCInst &Inst, uint64_t Addr, uint64_t Size, + uint64_t &Target) const override { + unsigned NumOps = Inst.getNumOperands(); + if (NumOps == 0) + return false; + switch (Info->get(Inst.getOpcode()).OpInfo[NumOps - 1].OperandType) { + case MCOI::OPERAND_UNKNOWN: + case MCOI::OPERAND_IMMEDIATE: + // jal, bal ... + Target = Inst.getOperand(NumOps - 1).getImm(); + return true; + case MCOI::OPERAND_PCREL: + // b, j, beq ... + Target = Addr + Inst.getOperand(NumOps - 1).getImm(); + return true; + default: + return false; + } + } +}; +} + +static MCInstrAnalysis *createMaxisMCInstrAnalysis(const MCInstrInfo *Info) { + return new MaxisMCInstrAnalysis(Info); +} + +extern "C" void LLVMInitializeMaxisTargetMC() { + for (Target *T : {&getTheMaxisTarget(), &getTheMaxiselTarget(), + &getTheMaxis64Target(), &getTheMaxis64elTarget()}) { + // Register the MC asm info. + RegisterMCAsmInfoFn X(*T, createMaxisMCAsmInfo); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(*T, createMaxisMCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(*T, createMaxisMCRegisterInfo); + + // Register the elf streamer. + TargetRegistry::RegisterELFStreamer(*T, createMCStreamer); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(*T, createMaxisAsmTargetStreamer); + + TargetRegistry::RegisterNullTargetStreamer(*T, + createMaxisNullTargetStreamer); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(*T, createMaxisMCSubtargetInfo); + + // Register the MC instruction analyzer. + TargetRegistry::RegisterMCInstrAnalysis(*T, createMaxisMCInstrAnalysis); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(*T, createMaxisMCInstPrinter); + + TargetRegistry::RegisterObjectTargetStreamer( + *T, createMaxisObjectTargetStreamer); + + // Register the asm backend. + TargetRegistry::RegisterMCAsmBackend(*T, createMaxisAsmBackend); + } + + // Register the MC Code Emitter + for (Target *T : {&getTheMaxisTarget(), &getTheMaxis64Target()}) + TargetRegistry::RegisterMCCodeEmitter(*T, createMaxisMCCodeEmitterEB); + + for (Target *T : {&getTheMaxiselTarget(), &getTheMaxis64elTarget()}) + TargetRegistry::RegisterMCCodeEmitter(*T, createMaxisMCCodeEmitterEL); +} diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisMCTargetDesc.h b/lib/Target/Maxis/MCTargetDesc/MaxisMCTargetDesc.h new file mode 100644 index 00000000..7d6174ff --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisMCTargetDesc.h @@ -0,0 +1,73 @@ +//===-- MaxisMCTargetDesc.h - Maxis Target Descriptions -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Maxis specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCTARGETDESC_H +#define LLVM_LIB_TARGET_MAXIS_MCTARGETDESC_MAXISMCTARGETDESC_H + +#include "llvm/Support/DataTypes.h" + +#include + +namespace llvm { +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCInstrInfo; +class MCObjectWriter; +class MCRegisterInfo; +class MCSubtargetInfo; +class MCTargetOptions; +class StringRef; +class Target; +class Triple; +class raw_ostream; +class raw_pwrite_stream; + +Target &getTheMaxisTarget(); +Target &getTheMaxiselTarget(); +Target &getTheMaxis64Target(); +Target &getTheMaxis64elTarget(); + +MCCodeEmitter *createMaxisMCCodeEmitterEB(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx); +MCCodeEmitter *createMaxisMCCodeEmitterEL(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx); + +MCAsmBackend *createMaxisAsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const MCTargetOptions &Options); + +std::unique_ptr +createMaxisELFObjectWriter(raw_pwrite_stream &OS, const Triple &TT, bool IsN32); + +namespace MAXIS_MC { +StringRef selectMaxisCPU(const Triple &TT, StringRef CPU); +} + +} // End llvm namespace + +// Defines symbolic names for Maxis registers. This defines a mapping from +// register name to register number. +#define GET_REGINFO_ENUM +#include "MaxisGenRegisterInfo.inc" + +// Defines symbolic names for the Maxis instructions. +#define GET_INSTRINFO_ENUM +#include "MaxisGenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "MaxisGenSubtargetInfo.inc" + +#endif diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisNaClELFStreamer.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisNaClELFStreamer.cpp new file mode 100644 index 00000000..64549039 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisNaClELFStreamer.cpp @@ -0,0 +1,277 @@ +//===-- MaxisNaClELFStreamer.cpp - ELF Object Output for Maxis NaCl ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements MCELFStreamer for Maxis NaCl. It emits .o object files +// as required by NaCl's SFI sandbox. It inserts address-masking instructions +// before dangerous control-flow and memory access instructions. It inserts +// address-masking instructions after instructions that change the stack +// pointer. It ensures that the mask and the dangerous instruction are always +// emitted in the same bundle. It aligns call + branch delay to the bundle end, +// so that return address is always aligned to the start of next bundle. +// +//===----------------------------------------------------------------------===// + +#include "Maxis.h" +#include "MaxisELFStreamer.h" +#include "MaxisMCNaCl.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-mc-nacl" + +namespace { + +const unsigned IndirectBranchMaskReg = Maxis::T6; +const unsigned LoadStoreStackMaskReg = Maxis::T7; + +/// Extend the generic MCELFStreamer class so that it can mask dangerous +/// instructions. + +class MaxisNaClELFStreamer : public MaxisELFStreamer { +public: + MaxisNaClELFStreamer(MCContext &Context, std::unique_ptr TAB, + raw_pwrite_stream &OS, + std::unique_ptr Emitter) + : MaxisELFStreamer(Context, std::move(TAB), OS, std::move(Emitter)) {} + + ~MaxisNaClELFStreamer() override = default; + +private: + // Whether we started the sandboxing sequence for calls. Calls are bundled + // with branch delays and aligned to the bundle end. + bool PendingCall = false; + + bool isIndirectJump(const MCInst &MI) { + if (MI.getOpcode() == Maxis::JALR) { + // MAXIS32r6/MAXIS64r6 doesn't have a JR instruction and uses JALR instead. + // JALR is an indirect branch if the link register is $0. + assert(MI.getOperand(0).isReg()); + return MI.getOperand(0).getReg() == Maxis::ZERO; + } + return MI.getOpcode() == Maxis::JR; + } + + bool isStackPointerFirstOperand(const MCInst &MI) { + return (MI.getNumOperands() > 0 && MI.getOperand(0).isReg() + && MI.getOperand(0).getReg() == Maxis::SP); + } + + bool isCall(const MCInst &MI, bool *IsIndirectCall) { + unsigned Opcode = MI.getOpcode(); + + *IsIndirectCall = false; + + switch (Opcode) { + default: + return false; + + case Maxis::JAL: + case Maxis::BAL: + case Maxis::BAL_BR: + case Maxis::BLTZAL: + case Maxis::BGEZAL: + return true; + + case Maxis::JALR: + // JALR is only a call if the link register is not $0. Otherwise it's an + // indirect branch. + assert(MI.getOperand(0).isReg()); + if (MI.getOperand(0).getReg() == Maxis::ZERO) + return false; + + *IsIndirectCall = true; + return true; + } + } + + void emitMask(unsigned AddrReg, unsigned MaskReg, + const MCSubtargetInfo &STI) { + MCInst MaskInst; + MaskInst.setOpcode(Maxis::AND); + MaskInst.addOperand(MCOperand::createReg(AddrReg)); + MaskInst.addOperand(MCOperand::createReg(AddrReg)); + MaskInst.addOperand(MCOperand::createReg(MaskReg)); + MaxisELFStreamer::EmitInstruction(MaskInst, STI); + } + + // Sandbox indirect branch or return instruction by inserting mask operation + // before it. + void sandboxIndirectJump(const MCInst &MI, const MCSubtargetInfo &STI) { + unsigned AddrReg = MI.getOperand(0).getReg(); + + EmitBundleLock(false); + emitMask(AddrReg, IndirectBranchMaskReg, STI); + MaxisELFStreamer::EmitInstruction(MI, STI); + EmitBundleUnlock(); + } + + // Sandbox memory access or SP change. Insert mask operation before and/or + // after the instruction. + void sandboxLoadStoreStackChange(const MCInst &MI, unsigned AddrIdx, + const MCSubtargetInfo &STI, bool MaskBefore, + bool MaskAfter) { + EmitBundleLock(false); + if (MaskBefore) { + // Sandbox memory access. + unsigned BaseReg = MI.getOperand(AddrIdx).getReg(); + emitMask(BaseReg, LoadStoreStackMaskReg, STI); + } + MaxisELFStreamer::EmitInstruction(MI, STI); + if (MaskAfter) { + // Sandbox SP change. + unsigned SPReg = MI.getOperand(0).getReg(); + assert((Maxis::SP == SPReg) && "Unexpected stack-pointer register."); + emitMask(SPReg, LoadStoreStackMaskReg, STI); + } + EmitBundleUnlock(); + } + +public: + /// This function is the one used to emit instruction data into the ELF + /// streamer. We override it to mask dangerous instructions. + void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI, + bool) override { + // Sandbox indirect jumps. + if (isIndirectJump(Inst)) { + if (PendingCall) + report_fatal_error("Dangerous instruction in branch delay slot!"); + sandboxIndirectJump(Inst, STI); + return; + } + + // Sandbox loads, stores and SP changes. + unsigned AddrIdx; + bool IsStore; + bool IsMemAccess = isBasePlusOffsetMemoryAccess(Inst.getOpcode(), &AddrIdx, + &IsStore); + bool IsSPFirstOperand = isStackPointerFirstOperand(Inst); + if (IsMemAccess || IsSPFirstOperand) { + bool MaskBefore = (IsMemAccess + && baseRegNeedsLoadStoreMask(Inst.getOperand(AddrIdx) + .getReg())); + bool MaskAfter = IsSPFirstOperand && !IsStore; + if (MaskBefore || MaskAfter) { + if (PendingCall) + report_fatal_error("Dangerous instruction in branch delay slot!"); + sandboxLoadStoreStackChange(Inst, AddrIdx, STI, MaskBefore, MaskAfter); + return; + } + // fallthrough + } + + // Sandbox calls by aligning call and branch delay to the bundle end. + // For indirect calls, emit the mask before the call. + bool IsIndirectCall; + if (isCall(Inst, &IsIndirectCall)) { + if (PendingCall) + report_fatal_error("Dangerous instruction in branch delay slot!"); + + // Start the sandboxing sequence by emitting call. + EmitBundleLock(true); + if (IsIndirectCall) { + unsigned TargetReg = Inst.getOperand(1).getReg(); + emitMask(TargetReg, IndirectBranchMaskReg, STI); + } + MaxisELFStreamer::EmitInstruction(Inst, STI); + PendingCall = true; + return; + } + if (PendingCall) { + // Finish the sandboxing sequence by emitting branch delay. + MaxisELFStreamer::EmitInstruction(Inst, STI); + EmitBundleUnlock(); + PendingCall = false; + return; + } + + // None of the sandboxing applies, just emit the instruction. + MaxisELFStreamer::EmitInstruction(Inst, STI); + } +}; + +} // end anonymous namespace + +namespace llvm { + +bool isBasePlusOffsetMemoryAccess(unsigned Opcode, unsigned *AddrIdx, + bool *IsStore) { + if (IsStore) + *IsStore = false; + + switch (Opcode) { + default: + return false; + + // Load instructions with base address register in position 1. + case Maxis::LB: + case Maxis::LBu: + case Maxis::LH: + case Maxis::LHu: + case Maxis::LW: + case Maxis::LWC1: + case Maxis::LDC1: + case Maxis::LL: + case Maxis::LL_R6: + case Maxis::LWL: + case Maxis::LWR: + *AddrIdx = 1; + return true; + + // Store instructions with base address register in position 1. + case Maxis::SB: + case Maxis::SH: + case Maxis::SW: + case Maxis::SWC1: + case Maxis::SDC1: + case Maxis::SWL: + case Maxis::SWR: + *AddrIdx = 1; + if (IsStore) + *IsStore = true; + return true; + + // Store instructions with base address register in position 2. + case Maxis::SC: + case Maxis::SC_R6: + *AddrIdx = 2; + if (IsStore) + *IsStore = true; + return true; + } +} + +bool baseRegNeedsLoadStoreMask(unsigned Reg) { + // The contents of SP and thread pointer register do not require masking. + return Reg != Maxis::SP && Reg != Maxis::T8; +} + +MCELFStreamer *createMaxisNaClELFStreamer(MCContext &Context, + std::unique_ptr TAB, + raw_pwrite_stream &OS, + std::unique_ptr Emitter, + bool RelaxAll) { + MaxisNaClELFStreamer *S = + new MaxisNaClELFStreamer(Context, std::move(TAB), OS, std::move(Emitter)); + if (RelaxAll) + S->getAssembler().setRelaxAll(true); + + // Set bundle-alignment as required by the NaCl ABI for the target. + S->EmitBundleAlignMode(MAXIS_NACL_BUNDLE_ALIGN); + + return S; +} + +} // end namespace llvm diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisOptionRecord.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisOptionRecord.cpp new file mode 100644 index 00000000..85011771 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisOptionRecord.cpp @@ -0,0 +1,101 @@ +//===- MaxisOptionRecord.cpp - Abstraction for storing information ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisOptionRecord.h" +#include "MaxisABIInfo.h" +#include "MaxisELFStreamer.h" +#include "MaxisTargetStreamer.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSectionELF.h" +#include + +using namespace llvm; + +void MaxisRegInfoRecord::EmitMaxisOptionRecord() { + MCAssembler &MCA = Streamer->getAssembler(); + MaxisTargetStreamer *MTS = + static_cast(Streamer->getTargetStreamer()); + + Streamer->PushSection(); + + // We need to distinguish between N64 and the rest because at the moment + // we don't emit .Maxis.options for other ELFs other than N64. + // Since .reginfo has the same information as .Maxis.options (ODK_REGINFO), + // we can use the same abstraction (MaxisRegInfoRecord class) to handle both. + if (MTS->getABI().IsN64()) { + // The EntrySize value of 1 seems strange since the records are neither + // 1-byte long nor fixed length but it matches the value GAS emits. + MCSectionELF *Sec = + Context.getELFSection(".MAXIS.options", ELF::SHT_MAXIS_OPTIONS, + ELF::SHF_ALLOC | ELF::SHF_MAXIS_NOSTRIP, 1, ""); + MCA.registerSection(*Sec); + Sec->setAlignment(8); + Streamer->SwitchSection(Sec); + + Streamer->EmitIntValue(ELF::ODK_REGINFO, 1); // kind + Streamer->EmitIntValue(40, 1); // size + Streamer->EmitIntValue(0, 2); // section + Streamer->EmitIntValue(0, 4); // info + Streamer->EmitIntValue(ri_gprmask, 4); + Streamer->EmitIntValue(0, 4); // pad + Streamer->EmitIntValue(ri_cprmask[0], 4); + Streamer->EmitIntValue(ri_cprmask[1], 4); + Streamer->EmitIntValue(ri_cprmask[2], 4); + Streamer->EmitIntValue(ri_cprmask[3], 4); + Streamer->EmitIntValue(ri_gp_value, 8); + } else { + MCSectionELF *Sec = Context.getELFSection(".reginfo", ELF::SHT_MAXIS_REGINFO, + ELF::SHF_ALLOC, 24, ""); + MCA.registerSection(*Sec); + Sec->setAlignment(MTS->getABI().IsN32() ? 8 : 4); + Streamer->SwitchSection(Sec); + + Streamer->EmitIntValue(ri_gprmask, 4); + Streamer->EmitIntValue(ri_cprmask[0], 4); + Streamer->EmitIntValue(ri_cprmask[1], 4); + Streamer->EmitIntValue(ri_cprmask[2], 4); + Streamer->EmitIntValue(ri_cprmask[3], 4); + assert((ri_gp_value & 0xffffffff) == ri_gp_value); + Streamer->EmitIntValue(ri_gp_value, 4); + } + + Streamer->PopSection(); +} + +void MaxisRegInfoRecord::SetPhysRegUsed(unsigned Reg, + const MCRegisterInfo *MCRegInfo) { + unsigned Value = 0; + + for (MCSubRegIterator SubRegIt(Reg, MCRegInfo, true); SubRegIt.isValid(); + ++SubRegIt) { + unsigned CurrentSubReg = *SubRegIt; + + unsigned EncVal = MCRegInfo->getEncodingValue(CurrentSubReg); + Value |= 1 << EncVal; + + if (GPR32RegClass->contains(CurrentSubReg) || + GPR64RegClass->contains(CurrentSubReg)) + ri_gprmask |= Value; + else if (COP0RegClass->contains(CurrentSubReg)) + ri_cprmask[0] |= Value; + // MAXIS COP1 is the FPU. + else if (FGR32RegClass->contains(CurrentSubReg) || + FGR64RegClass->contains(CurrentSubReg) || + AFGR64RegClass->contains(CurrentSubReg) || + MSA128BRegClass->contains(CurrentSubReg)) + ri_cprmask[1] |= Value; + else if (COP2RegClass->contains(CurrentSubReg)) + ri_cprmask[2] |= Value; + else if (COP3RegClass->contains(CurrentSubReg)) + ri_cprmask[3] |= Value; + } +} diff --git a/lib/Target/Maxis/MCTargetDesc/MaxisTargetStreamer.cpp b/lib/Target/Maxis/MCTargetDesc/MaxisTargetStreamer.cpp new file mode 100644 index 00000000..c79ea6f1 --- /dev/null +++ b/lib/Target/Maxis/MCTargetDesc/MaxisTargetStreamer.cpp @@ -0,0 +1,1214 @@ +//===-- MaxisTargetStreamer.cpp - Maxis Target Streamer Methods -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Maxis specific target streamer methods. +// +//===----------------------------------------------------------------------===// + +#include "MaxisTargetStreamer.h" +#include "InstPrinter/MaxisInstPrinter.h" +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MaxisELFStreamer.h" +#include "MaxisMCExpr.h" +#include "MaxisMCTargetDesc.h" +#include "MaxisTargetObjectFile.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" + +using namespace llvm; + +namespace { +static cl::opt RoundSectionSizes( + "maxis-round-section-sizes", cl::init(false), + cl::desc("Round section sizes up to the section alignment"), cl::Hidden); +} // end anonymous namespace + +MaxisTargetStreamer::MaxisTargetStreamer(MCStreamer &S) + : MCTargetStreamer(S), ModuleDirectiveAllowed(true) { + GPRInfoSet = FPRInfoSet = FrameInfoSet = false; +} +void MaxisTargetStreamer::emitDirectiveSetMicroMaxis() {} +void MaxisTargetStreamer::emitDirectiveSetNoMicroMaxis() {} +void MaxisTargetStreamer::setUsesMicroMaxis() {} +void MaxisTargetStreamer::emitDirectiveSetMaxis16() {} +void MaxisTargetStreamer::emitDirectiveSetNoMaxis16() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetReorder() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetNoReorder() {} +void MaxisTargetStreamer::emitDirectiveSetMacro() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetNoMacro() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMsa() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetNoMsa() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMt() {} +void MaxisTargetStreamer::emitDirectiveSetNoMt() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetAt() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetAtWithArg(unsigned RegNo) { + forbidModuleDirective(); +} +void MaxisTargetStreamer::emitDirectiveSetNoAt() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveEnd(StringRef Name) {} +void MaxisTargetStreamer::emitDirectiveEnt(const MCSymbol &Symbol) {} +void MaxisTargetStreamer::emitDirectiveAbiCalls() {} +void MaxisTargetStreamer::emitDirectiveNaN2008() {} +void MaxisTargetStreamer::emitDirectiveNaNLegacy() {} +void MaxisTargetStreamer::emitDirectiveOptionPic0() {} +void MaxisTargetStreamer::emitDirectiveOptionPic2() {} +void MaxisTargetStreamer::emitDirectiveInsn() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitFrame(unsigned StackReg, unsigned StackSize, + unsigned ReturnReg) {} +void MaxisTargetStreamer::emitMask(unsigned CPUBitmask, int CPUTopSavedRegOff) {} +void MaxisTargetStreamer::emitFMask(unsigned FPUBitmask, int FPUTopSavedRegOff) { +} +void MaxisTargetStreamer::emitDirectiveSetArch(StringRef Arch) { + forbidModuleDirective(); +} +void MaxisTargetStreamer::emitDirectiveSetMaxis0() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis1() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis2() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis3() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis4() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis5() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis32() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis32R2() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis32R3() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis32R5() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis32R6() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis64() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis64R2() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis64R3() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis64R5() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetMaxis64R6() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetPop() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetPush() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetSoftFloat() { + forbidModuleDirective(); +} +void MaxisTargetStreamer::emitDirectiveSetHardFloat() { + forbidModuleDirective(); +} +void MaxisTargetStreamer::emitDirectiveSetDsp() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetDspr2() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetNoDsp() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveCpLoad(unsigned RegNo) {} +bool MaxisTargetStreamer::emitDirectiveCpRestore( + int Offset, function_ref GetATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + forbidModuleDirective(); + return true; +} +void MaxisTargetStreamer::emitDirectiveCpsetup(unsigned RegNo, int RegOrOffset, + const MCSymbol &Sym, bool IsReg) { +} +void MaxisTargetStreamer::emitDirectiveCpreturn(unsigned SaveLocation, + bool SaveLocationIsRegister) {} + +void MaxisTargetStreamer::emitDirectiveModuleFP() {} + +void MaxisTargetStreamer::emitDirectiveModuleOddSPReg() { + if (!ABIFlagsSection.OddSPReg && !ABIFlagsSection.Is32BitABI) + report_fatal_error("+nooddspreg is only valid for O32"); +} +void MaxisTargetStreamer::emitDirectiveModuleSoftFloat() {} +void MaxisTargetStreamer::emitDirectiveModuleHardFloat() {} +void MaxisTargetStreamer::emitDirectiveModuleMT() {} +void MaxisTargetStreamer::emitDirectiveSetFp( + MaxisABIFlagsSection::FpABIKind Value) { + forbidModuleDirective(); +} +void MaxisTargetStreamer::emitDirectiveSetOddSPReg() { forbidModuleDirective(); } +void MaxisTargetStreamer::emitDirectiveSetNoOddSPReg() { + forbidModuleDirective(); +} + +void MaxisTargetStreamer::emitR(unsigned Opcode, unsigned Reg0, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + MCInst TmpInst; + TmpInst.setOpcode(Opcode); + TmpInst.addOperand(MCOperand::createReg(Reg0)); + TmpInst.setLoc(IDLoc); + getStreamer().EmitInstruction(TmpInst, *STI); +} + +void MaxisTargetStreamer::emitRX(unsigned Opcode, unsigned Reg0, MCOperand Op1, + SMLoc IDLoc, const MCSubtargetInfo *STI) { + MCInst TmpInst; + TmpInst.setOpcode(Opcode); + TmpInst.addOperand(MCOperand::createReg(Reg0)); + TmpInst.addOperand(Op1); + TmpInst.setLoc(IDLoc); + getStreamer().EmitInstruction(TmpInst, *STI); +} + +void MaxisTargetStreamer::emitRI(unsigned Opcode, unsigned Reg0, int32_t Imm, + SMLoc IDLoc, const MCSubtargetInfo *STI) { + emitRX(Opcode, Reg0, MCOperand::createImm(Imm), IDLoc, STI); +} + +void MaxisTargetStreamer::emitRR(unsigned Opcode, unsigned Reg0, unsigned Reg1, + SMLoc IDLoc, const MCSubtargetInfo *STI) { + emitRX(Opcode, Reg0, MCOperand::createReg(Reg1), IDLoc, STI); +} + +void MaxisTargetStreamer::emitII(unsigned Opcode, int16_t Imm1, int16_t Imm2, + SMLoc IDLoc, const MCSubtargetInfo *STI) { + MCInst TmpInst; + TmpInst.setOpcode(Opcode); + TmpInst.addOperand(MCOperand::createImm(Imm1)); + TmpInst.addOperand(MCOperand::createImm(Imm2)); + TmpInst.setLoc(IDLoc); + getStreamer().EmitInstruction(TmpInst, *STI); +} + +void MaxisTargetStreamer::emitRRX(unsigned Opcode, unsigned Reg0, unsigned Reg1, + MCOperand Op2, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + MCInst TmpInst; + TmpInst.setOpcode(Opcode); + TmpInst.addOperand(MCOperand::createReg(Reg0)); + TmpInst.addOperand(MCOperand::createReg(Reg1)); + TmpInst.addOperand(Op2); + TmpInst.setLoc(IDLoc); + getStreamer().EmitInstruction(TmpInst, *STI); +} + +void MaxisTargetStreamer::emitRRR(unsigned Opcode, unsigned Reg0, unsigned Reg1, + unsigned Reg2, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + emitRRX(Opcode, Reg0, Reg1, MCOperand::createReg(Reg2), IDLoc, STI); +} + +void MaxisTargetStreamer::emitRRI(unsigned Opcode, unsigned Reg0, unsigned Reg1, + int16_t Imm, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + emitRRX(Opcode, Reg0, Reg1, MCOperand::createImm(Imm), IDLoc, STI); +} + +void MaxisTargetStreamer::emitRRIII(unsigned Opcode, unsigned Reg0, + unsigned Reg1, int16_t Imm0, int16_t Imm1, + int16_t Imm2, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + MCInst TmpInst; + TmpInst.setOpcode(Opcode); + TmpInst.addOperand(MCOperand::createReg(Reg0)); + TmpInst.addOperand(MCOperand::createReg(Reg1)); + TmpInst.addOperand(MCOperand::createImm(Imm0)); + TmpInst.addOperand(MCOperand::createImm(Imm1)); + TmpInst.addOperand(MCOperand::createImm(Imm2)); + TmpInst.setLoc(IDLoc); + getStreamer().EmitInstruction(TmpInst, *STI); +} + +void MaxisTargetStreamer::emitAddu(unsigned DstReg, unsigned SrcReg, + unsigned TrgReg, bool Is64Bit, + const MCSubtargetInfo *STI) { + emitRRR(Is64Bit ? Maxis::DADDu : Maxis::ADDu, DstReg, SrcReg, TrgReg, SMLoc(), + STI); +} + +void MaxisTargetStreamer::emitDSLL(unsigned DstReg, unsigned SrcReg, + int16_t ShiftAmount, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + if (ShiftAmount >= 32) { + emitRRI(Maxis::DSLL32, DstReg, SrcReg, ShiftAmount - 32, IDLoc, STI); + return; + } + + emitRRI(Maxis::DSLL, DstReg, SrcReg, ShiftAmount, IDLoc, STI); +} + +void MaxisTargetStreamer::emitEmptyDelaySlot(bool hasShortDelaySlot, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + if (hasShortDelaySlot) + emitRR(Maxis::MOVE16_MM, Maxis::ZERO, Maxis::ZERO, IDLoc, STI); + else + emitRRI(Maxis::SLL, Maxis::ZERO, Maxis::ZERO, 0, IDLoc, STI); +} + +void MaxisTargetStreamer::emitNop(SMLoc IDLoc, const MCSubtargetInfo *STI) { + emitRRI(Maxis::SLL, Maxis::ZERO, Maxis::ZERO, 0, IDLoc, STI); +} + +/// Emit the $gp restore operation for .cprestore. +void MaxisTargetStreamer::emitGPRestore(int Offset, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + emitLoadWithImmOffset(Maxis::LW, Maxis::GP, Maxis::SP, Offset, Maxis::GP, IDLoc, + STI); +} + +/// Emit a store instruction with an immediate offset. +void MaxisTargetStreamer::emitStoreWithImmOffset( + unsigned Opcode, unsigned SrcReg, unsigned BaseReg, int64_t Offset, + function_ref GetATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + if (isInt<16>(Offset)) { + emitRRI(Opcode, SrcReg, BaseReg, Offset, IDLoc, STI); + return; + } + + // sw $8, offset($8) => lui $at, %hi(offset) + // add $at, $at, $8 + // sw $8, %lo(offset)($at) + + unsigned ATReg = GetATReg(); + if (!ATReg) + return; + + unsigned LoOffset = Offset & 0x0000ffff; + unsigned HiOffset = (Offset & 0xffff0000) >> 16; + + // If msb of LoOffset is 1(negative number) we must increment HiOffset + // to account for the sign-extension of the low part. + if (LoOffset & 0x8000) + HiOffset++; + + // Generate the base address in ATReg. + emitRI(Maxis::LUi, ATReg, HiOffset, IDLoc, STI); + if (BaseReg != Maxis::ZERO) + emitRRR(Maxis::ADDu, ATReg, ATReg, BaseReg, IDLoc, STI); + // Emit the store with the adjusted base and offset. + emitRRI(Opcode, SrcReg, ATReg, LoOffset, IDLoc, STI); +} + +/// Emit a store instruction with an symbol offset. Symbols are assumed to be +/// out of range for a simm16 will be expanded to appropriate instructions. +void MaxisTargetStreamer::emitStoreWithSymOffset( + unsigned Opcode, unsigned SrcReg, unsigned BaseReg, MCOperand &HiOperand, + MCOperand &LoOperand, unsigned ATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + // sw $8, sym => lui $at, %hi(sym) + // sw $8, %lo(sym)($at) + + // Generate the base address in ATReg. + emitRX(Maxis::LUi, ATReg, HiOperand, IDLoc, STI); + if (BaseReg != Maxis::ZERO) + emitRRR(Maxis::ADDu, ATReg, ATReg, BaseReg, IDLoc, STI); + // Emit the store with the adjusted base and offset. + emitRRX(Opcode, SrcReg, ATReg, LoOperand, IDLoc, STI); +} + +/// Emit a load instruction with an immediate offset. DstReg and TmpReg are +/// permitted to be the same register iff DstReg is distinct from BaseReg and +/// DstReg is a GPR. It is the callers responsibility to identify such cases +/// and pass the appropriate register in TmpReg. +void MaxisTargetStreamer::emitLoadWithImmOffset(unsigned Opcode, unsigned DstReg, + unsigned BaseReg, int64_t Offset, + unsigned TmpReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + if (isInt<16>(Offset)) { + emitRRI(Opcode, DstReg, BaseReg, Offset, IDLoc, STI); + return; + } + + // 1) lw $8, offset($9) => lui $8, %hi(offset) + // add $8, $8, $9 + // lw $8, %lo(offset)($9) + // 2) lw $8, offset($8) => lui $at, %hi(offset) + // add $at, $at, $8 + // lw $8, %lo(offset)($at) + + unsigned LoOffset = Offset & 0x0000ffff; + unsigned HiOffset = (Offset & 0xffff0000) >> 16; + + // If msb of LoOffset is 1(negative number) we must increment HiOffset + // to account for the sign-extension of the low part. + if (LoOffset & 0x8000) + HiOffset++; + + // Generate the base address in TmpReg. + emitRI(Maxis::LUi, TmpReg, HiOffset, IDLoc, STI); + if (BaseReg != Maxis::ZERO) + emitRRR(Maxis::ADDu, TmpReg, TmpReg, BaseReg, IDLoc, STI); + // Emit the load with the adjusted base and offset. + emitRRI(Opcode, DstReg, TmpReg, LoOffset, IDLoc, STI); +} + +/// Emit a load instruction with an symbol offset. Symbols are assumed to be +/// out of range for a simm16 will be expanded to appropriate instructions. +/// DstReg and TmpReg are permitted to be the same register iff DstReg is a +/// GPR. It is the callers responsibility to identify such cases and pass the +/// appropriate register in TmpReg. +void MaxisTargetStreamer::emitLoadWithSymOffset(unsigned Opcode, unsigned DstReg, + unsigned BaseReg, + MCOperand &HiOperand, + MCOperand &LoOperand, + unsigned TmpReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + // 1) lw $8, sym => lui $8, %hi(sym) + // lw $8, %lo(sym)($8) + // 2) ldc1 $f0, sym => lui $at, %hi(sym) + // ldc1 $f0, %lo(sym)($at) + + // Generate the base address in TmpReg. + emitRX(Maxis::LUi, TmpReg, HiOperand, IDLoc, STI); + if (BaseReg != Maxis::ZERO) + emitRRR(Maxis::ADDu, TmpReg, TmpReg, BaseReg, IDLoc, STI); + // Emit the load with the adjusted base and offset. + emitRRX(Opcode, DstReg, TmpReg, LoOperand, IDLoc, STI); +} + +MaxisTargetAsmStreamer::MaxisTargetAsmStreamer(MCStreamer &S, + formatted_raw_ostream &OS) + : MaxisTargetStreamer(S), OS(OS) {} + +void MaxisTargetAsmStreamer::emitDirectiveSetMicroMaxis() { + OS << "\t.set\tmicromaxis\n"; + forbidModuleDirective(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoMicroMaxis() { + OS << "\t.set\tnomicromaxis\n"; + forbidModuleDirective(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis16() { + OS << "\t.set\tmaxis16\n"; + forbidModuleDirective(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoMaxis16() { + OS << "\t.set\tnomaxis16\n"; + MaxisTargetStreamer::emitDirectiveSetNoMaxis16(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetReorder() { + OS << "\t.set\treorder\n"; + MaxisTargetStreamer::emitDirectiveSetReorder(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoReorder() { + OS << "\t.set\tnoreorder\n"; + forbidModuleDirective(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMacro() { + OS << "\t.set\tmacro\n"; + MaxisTargetStreamer::emitDirectiveSetMacro(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoMacro() { + OS << "\t.set\tnomacro\n"; + MaxisTargetStreamer::emitDirectiveSetNoMacro(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMsa() { + OS << "\t.set\tmsa\n"; + MaxisTargetStreamer::emitDirectiveSetMsa(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoMsa() { + OS << "\t.set\tnomsa\n"; + MaxisTargetStreamer::emitDirectiveSetNoMsa(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMt() { + OS << "\t.set\tmt\n"; + MaxisTargetStreamer::emitDirectiveSetMt(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoMt() { + OS << "\t.set\tnomt\n"; + MaxisTargetStreamer::emitDirectiveSetNoMt(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetAt() { + OS << "\t.set\tat\n"; + MaxisTargetStreamer::emitDirectiveSetAt(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetAtWithArg(unsigned RegNo) { + OS << "\t.set\tat=$" << Twine(RegNo) << "\n"; + MaxisTargetStreamer::emitDirectiveSetAtWithArg(RegNo); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoAt() { + OS << "\t.set\tnoat\n"; + MaxisTargetStreamer::emitDirectiveSetNoAt(); +} + +void MaxisTargetAsmStreamer::emitDirectiveEnd(StringRef Name) { + OS << "\t.end\t" << Name << '\n'; +} + +void MaxisTargetAsmStreamer::emitDirectiveEnt(const MCSymbol &Symbol) { + OS << "\t.ent\t" << Symbol.getName() << '\n'; +} + +void MaxisTargetAsmStreamer::emitDirectiveAbiCalls() { OS << "\t.abicalls\n"; } + +void MaxisTargetAsmStreamer::emitDirectiveNaN2008() { OS << "\t.nan\t2008\n"; } + +void MaxisTargetAsmStreamer::emitDirectiveNaNLegacy() { + OS << "\t.nan\tlegacy\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveOptionPic0() { + OS << "\t.option\tpic0\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveOptionPic2() { + OS << "\t.option\tpic2\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveInsn() { + MaxisTargetStreamer::emitDirectiveInsn(); + OS << "\t.insn\n"; +} + +void MaxisTargetAsmStreamer::emitFrame(unsigned StackReg, unsigned StackSize, + unsigned ReturnReg) { + OS << "\t.frame\t$" + << StringRef(MaxisInstPrinter::getRegisterName(StackReg)).lower() << "," + << StackSize << ",$" + << StringRef(MaxisInstPrinter::getRegisterName(ReturnReg)).lower() << '\n'; +} + +void MaxisTargetAsmStreamer::emitDirectiveSetArch(StringRef Arch) { + OS << "\t.set arch=" << Arch << "\n"; + MaxisTargetStreamer::emitDirectiveSetArch(Arch); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis0() { + OS << "\t.set\tmaxis0\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis0(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis1() { + OS << "\t.set\tmaxis1\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis1(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis2() { + OS << "\t.set\tmaxis2\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis2(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis3() { + OS << "\t.set\tmaxis3\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis3(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis4() { + OS << "\t.set\tmaxis4\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis4(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis5() { + OS << "\t.set\tmaxis5\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis5(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis32() { + OS << "\t.set\tmaxis32\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis32(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis32R2() { + OS << "\t.set\tmaxis32r2\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis32R2(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis32R3() { + OS << "\t.set\tmaxis32r3\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis32R3(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis32R5() { + OS << "\t.set\tmaxis32r5\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis32R5(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis32R6() { + OS << "\t.set\tmaxis32r6\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis32R6(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis64() { + OS << "\t.set\tmaxis64\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis64(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis64R2() { + OS << "\t.set\tmaxis64r2\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis64R2(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis64R3() { + OS << "\t.set\tmaxis64r3\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis64R3(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis64R5() { + OS << "\t.set\tmaxis64r5\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis64R5(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetMaxis64R6() { + OS << "\t.set\tmaxis64r6\n"; + MaxisTargetStreamer::emitDirectiveSetMaxis64R6(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetDsp() { + OS << "\t.set\tdsp\n"; + MaxisTargetStreamer::emitDirectiveSetDsp(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetDspr2() { + OS << "\t.set\tdspr2\n"; + MaxisTargetStreamer::emitDirectiveSetDspr2(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoDsp() { + OS << "\t.set\tnodsp\n"; + MaxisTargetStreamer::emitDirectiveSetNoDsp(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetPop() { + OS << "\t.set\tpop\n"; + MaxisTargetStreamer::emitDirectiveSetPop(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetPush() { + OS << "\t.set\tpush\n"; + MaxisTargetStreamer::emitDirectiveSetPush(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetSoftFloat() { + OS << "\t.set\tsoftfloat\n"; + MaxisTargetStreamer::emitDirectiveSetSoftFloat(); +} + +void MaxisTargetAsmStreamer::emitDirectiveSetHardFloat() { + OS << "\t.set\thardfloat\n"; + MaxisTargetStreamer::emitDirectiveSetHardFloat(); +} + +// Print a 32 bit hex number with all numbers. +static void printHex32(unsigned Value, raw_ostream &OS) { + OS << "0x"; + for (int i = 7; i >= 0; i--) + OS.write_hex((Value & (0xF << (i * 4))) >> (i * 4)); +} + +void MaxisTargetAsmStreamer::emitMask(unsigned CPUBitmask, + int CPUTopSavedRegOff) { + OS << "\t.mask \t"; + printHex32(CPUBitmask, OS); + OS << ',' << CPUTopSavedRegOff << '\n'; +} + +void MaxisTargetAsmStreamer::emitFMask(unsigned FPUBitmask, + int FPUTopSavedRegOff) { + OS << "\t.fmask\t"; + printHex32(FPUBitmask, OS); + OS << "," << FPUTopSavedRegOff << '\n'; +} + +void MaxisTargetAsmStreamer::emitDirectiveCpLoad(unsigned RegNo) { + OS << "\t.cpload\t$" + << StringRef(MaxisInstPrinter::getRegisterName(RegNo)).lower() << "\n"; + forbidModuleDirective(); +} + +bool MaxisTargetAsmStreamer::emitDirectiveCpRestore( + int Offset, function_ref GetATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer::emitDirectiveCpRestore(Offset, GetATReg, IDLoc, STI); + OS << "\t.cprestore\t" << Offset << "\n"; + return true; +} + +void MaxisTargetAsmStreamer::emitDirectiveCpsetup(unsigned RegNo, + int RegOrOffset, + const MCSymbol &Sym, + bool IsReg) { + OS << "\t.cpsetup\t$" + << StringRef(MaxisInstPrinter::getRegisterName(RegNo)).lower() << ", "; + + if (IsReg) + OS << "$" + << StringRef(MaxisInstPrinter::getRegisterName(RegOrOffset)).lower(); + else + OS << RegOrOffset; + + OS << ", "; + + OS << Sym.getName(); + forbidModuleDirective(); +} + +void MaxisTargetAsmStreamer::emitDirectiveCpreturn(unsigned SaveLocation, + bool SaveLocationIsRegister) { + OS << "\t.cpreturn"; + forbidModuleDirective(); +} + +void MaxisTargetAsmStreamer::emitDirectiveModuleFP() { + OS << "\t.module\tfp="; + OS << ABIFlagsSection.getFpABIString(ABIFlagsSection.getFpABI()) << "\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveSetFp( + MaxisABIFlagsSection::FpABIKind Value) { + MaxisTargetStreamer::emitDirectiveSetFp(Value); + + OS << "\t.set\tfp="; + OS << ABIFlagsSection.getFpABIString(Value) << "\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveModuleOddSPReg() { + MaxisTargetStreamer::emitDirectiveModuleOddSPReg(); + + OS << "\t.module\t" << (ABIFlagsSection.OddSPReg ? "" : "no") << "oddspreg\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveSetOddSPReg() { + MaxisTargetStreamer::emitDirectiveSetOddSPReg(); + OS << "\t.set\toddspreg\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveSetNoOddSPReg() { + MaxisTargetStreamer::emitDirectiveSetNoOddSPReg(); + OS << "\t.set\tnooddspreg\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveModuleSoftFloat() { + OS << "\t.module\tsoftfloat\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveModuleHardFloat() { + OS << "\t.module\thardfloat\n"; +} + +void MaxisTargetAsmStreamer::emitDirectiveModuleMT() { + OS << "\t.module\tmt\n"; +} + +// This part is for ELF object output. +MaxisTargetELFStreamer::MaxisTargetELFStreamer(MCStreamer &S, + const MCSubtargetInfo &STI) + : MaxisTargetStreamer(S), MicroMaxisEnabled(false), STI(STI) { + MCAssembler &MCA = getStreamer().getAssembler(); + + // It's possible that MCObjectFileInfo isn't fully initialized at this point + // due to an initialization order problem where LLVMTargetMachine creates the + // target streamer before TargetLoweringObjectFile calls + // InitializeMCObjectFileInfo. There doesn't seem to be a single place that + // covers all cases so this statement covers most cases and direct object + // emission must call setPic() once MCObjectFileInfo has been initialized. The + // cases we don't handle here are covered by MaxisAsmPrinter. + Pic = MCA.getContext().getObjectFileInfo()->isPositionIndependent(); + + const FeatureBitset &Features = STI.getFeatureBits(); + + // Set the header flags that we can in the constructor. + // FIXME: This is a fairly terrible hack. We set the rest + // of these in the destructor. The problem here is two-fold: + // + // a: Some of the eflags can be set/reset by directives. + // b: There aren't any usage paths that initialize the ABI + // pointer until after we initialize either an assembler + // or the target machine. + // We can fix this by making the target streamer construct + // the ABI, but this is fraught with wide ranging dependency + // issues as well. + unsigned EFlags = MCA.getELFHeaderEFlags(); + + // FIXME: Fix a dependency issue by instantiating the ABI object to some + // default based off the triple. The triple doesn't describe the target + // fully, but any external user of the API that uses the MCTargetStreamer + // would otherwise crash on assertion failure. + + ABI = MaxisABIInfo( + STI.getTargetTriple().getArch() == Triple::ArchType::maxisel || + STI.getTargetTriple().getArch() == Triple::ArchType::maxis + ? MaxisABIInfo::O32() + : MaxisABIInfo::N64()); + + // Architecture + if (Features[Maxis::FeatureMaxis64r6]) + EFlags |= ELF::EF_MAXIS_ARCH_64R6; + else if (Features[Maxis::FeatureMaxis64r2] || + Features[Maxis::FeatureMaxis64r3] || + Features[Maxis::FeatureMaxis64r5]) + EFlags |= ELF::EF_MAXIS_ARCH_64R2; + else if (Features[Maxis::FeatureMaxis64]) + EFlags |= ELF::EF_MAXIS_ARCH_64; + else if (Features[Maxis::FeatureMaxis5]) + EFlags |= ELF::EF_MAXIS_ARCH_5; + else if (Features[Maxis::FeatureMaxis4]) + EFlags |= ELF::EF_MAXIS_ARCH_4; + else if (Features[Maxis::FeatureMaxis3]) + EFlags |= ELF::EF_MAXIS_ARCH_3; + else if (Features[Maxis::FeatureMaxis32r6]) + EFlags |= ELF::EF_MAXIS_ARCH_32R6; + else if (Features[Maxis::FeatureMaxis32r2] || + Features[Maxis::FeatureMaxis32r3] || + Features[Maxis::FeatureMaxis32r5]) + EFlags |= ELF::EF_MAXIS_ARCH_32R2; + else if (Features[Maxis::FeatureMaxis32]) + EFlags |= ELF::EF_MAXIS_ARCH_32; + else if (Features[Maxis::FeatureMaxis2]) + EFlags |= ELF::EF_MAXIS_ARCH_2; + else + EFlags |= ELF::EF_MAXIS_ARCH_1; + + // Machine + if (Features[Maxis::FeatureCnMaxis]) + EFlags |= ELF::EF_MAXIS_MACH_OCTEON; + + // Other options. + if (Features[Maxis::FeatureNaN2008]) + EFlags |= ELF::EF_MAXIS_NAN2008; + + MCA.setELFHeaderEFlags(EFlags); +} + +void MaxisTargetELFStreamer::emitLabel(MCSymbol *S) { + auto *Symbol = cast(S); + getStreamer().getAssembler().registerSymbol(*Symbol); + uint8_t Type = Symbol->getType(); + if (Type != ELF::STT_FUNC) + return; + + if (isMicroMaxisEnabled()) + Symbol->setOther(ELF::STO_MAXIS_MICROMAXIS); +} + +void MaxisTargetELFStreamer::finish() { + MCAssembler &MCA = getStreamer().getAssembler(); + const MCObjectFileInfo &OFI = *MCA.getContext().getObjectFileInfo(); + + // .bss, .text and .data are always at least 16-byte aligned. + MCSection &TextSection = *OFI.getTextSection(); + MCA.registerSection(TextSection); + MCSection &DataSection = *OFI.getDataSection(); + MCA.registerSection(DataSection); + MCSection &BSSSection = *OFI.getBSSSection(); + MCA.registerSection(BSSSection); + + TextSection.setAlignment(std::max(16u, TextSection.getAlignment())); + DataSection.setAlignment(std::max(16u, DataSection.getAlignment())); + BSSSection.setAlignment(std::max(16u, BSSSection.getAlignment())); + + if (RoundSectionSizes) { + // Make sections sizes a multiple of the alignment. This is useful for + // verifying the output of IAS against the output of other assemblers but + // it's not necessary to produce a correct object and increases section + // size. + MCStreamer &OS = getStreamer(); + for (MCSection &S : MCA) { + MCSectionELF &Section = static_cast(S); + + unsigned Alignment = Section.getAlignment(); + if (Alignment) { + OS.SwitchSection(&Section); + if (Section.UseCodeAlign()) + OS.EmitCodeAlignment(Alignment, Alignment); + else + OS.EmitValueToAlignment(Alignment, 0, 1, Alignment); + } + } + } + + const FeatureBitset &Features = STI.getFeatureBits(); + + // Update e_header flags. See the FIXME and comment above in + // the constructor for a full rundown on this. + unsigned EFlags = MCA.getELFHeaderEFlags(); + + // ABI + // N64 does not require any ABI bits. + if (getABI().IsO32()) + EFlags |= ELF::EF_MAXIS_ABI_O32; + else if (getABI().IsN32()) + EFlags |= ELF::EF_MAXIS_ABI2; + + if (Features[Maxis::FeatureGP64Bit]) { + if (getABI().IsO32()) + EFlags |= ELF::EF_MAXIS_32BITMODE; /* Compatibility Mode */ + } else if (Features[Maxis::FeatureMaxis64r2] || Features[Maxis::FeatureMaxis64]) + EFlags |= ELF::EF_MAXIS_32BITMODE; + + // -mplt is not implemented but we should act as if it was + // given. + if (!Features[Maxis::FeatureNoABICalls]) + EFlags |= ELF::EF_MAXIS_CPIC; + + if (Pic) + EFlags |= ELF::EF_MAXIS_PIC | ELF::EF_MAXIS_CPIC; + + MCA.setELFHeaderEFlags(EFlags); + + // Emit all the option records. + // At the moment we are only emitting .Maxis.options (ODK_REGINFO) and + // .reginfo. + MaxisELFStreamer &MEF = static_cast(Streamer); + MEF.EmitMaxisOptionRecords(); + + emitMaxisAbiFlags(); +} + +void MaxisTargetELFStreamer::emitAssignment(MCSymbol *S, const MCExpr *Value) { + auto *Symbol = cast(S); + // If on rhs is micromaxis symbol then mark Symbol as microMaxis. + if (Value->getKind() != MCExpr::SymbolRef) + return; + const auto &RhsSym = cast( + static_cast(Value)->getSymbol()); + + if (!(RhsSym.getOther() & ELF::STO_MAXIS_MICROMAXIS)) + return; + + Symbol->setOther(ELF::STO_MAXIS_MICROMAXIS); +} + +MCELFStreamer &MaxisTargetELFStreamer::getStreamer() { + return static_cast(Streamer); +} + +void MaxisTargetELFStreamer::emitDirectiveSetMicroMaxis() { + MicroMaxisEnabled = true; + forbidModuleDirective(); +} + +void MaxisTargetELFStreamer::emitDirectiveSetNoMicroMaxis() { + MicroMaxisEnabled = false; + forbidModuleDirective(); +} + +void MaxisTargetELFStreamer::setUsesMicroMaxis() { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned Flags = MCA.getELFHeaderEFlags(); + Flags |= ELF::EF_MAXIS_MICROMAXIS; + MCA.setELFHeaderEFlags(Flags); +} + +void MaxisTargetELFStreamer::emitDirectiveSetMaxis16() { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned Flags = MCA.getELFHeaderEFlags(); + Flags |= ELF::EF_MAXIS_ARCH_ASE_M16; + MCA.setELFHeaderEFlags(Flags); + forbidModuleDirective(); +} + +void MaxisTargetELFStreamer::emitDirectiveSetNoReorder() { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned Flags = MCA.getELFHeaderEFlags(); + Flags |= ELF::EF_MAXIS_NOREORDER; + MCA.setELFHeaderEFlags(Flags); + forbidModuleDirective(); +} + +void MaxisTargetELFStreamer::emitDirectiveEnd(StringRef Name) { + MCAssembler &MCA = getStreamer().getAssembler(); + MCContext &Context = MCA.getContext(); + MCStreamer &OS = getStreamer(); + + MCSectionELF *Sec = Context.getELFSection(".pdr", ELF::SHT_PROGBITS, 0); + + MCSymbol *Sym = Context.getOrCreateSymbol(Name); + const MCSymbolRefExpr *ExprRef = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, Context); + + MCA.registerSection(*Sec); + Sec->setAlignment(4); + + OS.PushSection(); + + OS.SwitchSection(Sec); + + OS.EmitValueImpl(ExprRef, 4); + + OS.EmitIntValue(GPRInfoSet ? GPRBitMask : 0, 4); // reg_mask + OS.EmitIntValue(GPRInfoSet ? GPROffset : 0, 4); // reg_offset + + OS.EmitIntValue(FPRInfoSet ? FPRBitMask : 0, 4); // fpreg_mask + OS.EmitIntValue(FPRInfoSet ? FPROffset : 0, 4); // fpreg_offset + + OS.EmitIntValue(FrameInfoSet ? FrameOffset : 0, 4); // frame_offset + OS.EmitIntValue(FrameInfoSet ? FrameReg : 0, 4); // frame_reg + OS.EmitIntValue(FrameInfoSet ? ReturnReg : 0, 4); // return_reg + + // The .end directive marks the end of a procedure. Invalidate + // the information gathered up until this point. + GPRInfoSet = FPRInfoSet = FrameInfoSet = false; + + OS.PopSection(); + + // .end also implicitly sets the size. + MCSymbol *CurPCSym = Context.createTempSymbol(); + OS.EmitLabel(CurPCSym); + const MCExpr *Size = MCBinaryExpr::createSub( + MCSymbolRefExpr::create(CurPCSym, MCSymbolRefExpr::VK_None, Context), + ExprRef, Context); + + // The ELFObjectWriter can determine the absolute size as it has access to + // the layout information of the assembly file, so a size expression rather + // than an absolute value is ok here. + static_cast(Sym)->setSize(Size); +} + +void MaxisTargetELFStreamer::emitDirectiveEnt(const MCSymbol &Symbol) { + GPRInfoSet = FPRInfoSet = FrameInfoSet = false; + + // .ent also acts like an implicit '.type symbol, STT_FUNC' + static_cast(Symbol).setType(ELF::STT_FUNC); +} + +void MaxisTargetELFStreamer::emitDirectiveAbiCalls() { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned Flags = MCA.getELFHeaderEFlags(); + Flags |= ELF::EF_MAXIS_CPIC | ELF::EF_MAXIS_PIC; + MCA.setELFHeaderEFlags(Flags); +} + +void MaxisTargetELFStreamer::emitDirectiveNaN2008() { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned Flags = MCA.getELFHeaderEFlags(); + Flags |= ELF::EF_MAXIS_NAN2008; + MCA.setELFHeaderEFlags(Flags); +} + +void MaxisTargetELFStreamer::emitDirectiveNaNLegacy() { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned Flags = MCA.getELFHeaderEFlags(); + Flags &= ~ELF::EF_MAXIS_NAN2008; + MCA.setELFHeaderEFlags(Flags); +} + +void MaxisTargetELFStreamer::emitDirectiveOptionPic0() { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned Flags = MCA.getELFHeaderEFlags(); + // This option overrides other PIC options like -KPIC. + Pic = false; + Flags &= ~ELF::EF_MAXIS_PIC; + MCA.setELFHeaderEFlags(Flags); +} + +void MaxisTargetELFStreamer::emitDirectiveOptionPic2() { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned Flags = MCA.getELFHeaderEFlags(); + Pic = true; + // NOTE: We are following the GAS behaviour here which means the directive + // 'pic2' also sets the CPIC bit in the ELF header. This is different from + // what is stated in the SYSV ABI which consider the bits EF_MAXIS_PIC and + // EF_MAXIS_CPIC to be mutually exclusive. + Flags |= ELF::EF_MAXIS_PIC | ELF::EF_MAXIS_CPIC; + MCA.setELFHeaderEFlags(Flags); +} + +void MaxisTargetELFStreamer::emitDirectiveInsn() { + MaxisTargetStreamer::emitDirectiveInsn(); + MaxisELFStreamer &MEF = static_cast(Streamer); + MEF.createPendingLabelRelocs(); +} + +void MaxisTargetELFStreamer::emitFrame(unsigned StackReg, unsigned StackSize, + unsigned ReturnReg_) { + MCContext &Context = getStreamer().getAssembler().getContext(); + const MCRegisterInfo *RegInfo = Context.getRegisterInfo(); + + FrameInfoSet = true; + FrameReg = RegInfo->getEncodingValue(StackReg); + FrameOffset = StackSize; + ReturnReg = RegInfo->getEncodingValue(ReturnReg_); +} + +void MaxisTargetELFStreamer::emitMask(unsigned CPUBitmask, + int CPUTopSavedRegOff) { + GPRInfoSet = true; + GPRBitMask = CPUBitmask; + GPROffset = CPUTopSavedRegOff; +} + +void MaxisTargetELFStreamer::emitFMask(unsigned FPUBitmask, + int FPUTopSavedRegOff) { + FPRInfoSet = true; + FPRBitMask = FPUBitmask; + FPROffset = FPUTopSavedRegOff; +} + +void MaxisTargetELFStreamer::emitDirectiveCpLoad(unsigned RegNo) { + // .cpload $reg + // This directive expands to: + // lui $gp, %hi(_gp_disp) + // addui $gp, $gp, %lo(_gp_disp) + // addu $gp, $gp, $reg + // when support for position independent code is enabled. + if (!Pic || (getABI().IsN32() || getABI().IsN64())) + return; + + // There's a GNU extension controlled by -mno-shared that allows + // locally-binding symbols to be accessed using absolute addresses. + // This is currently not supported. When supported -mno-shared makes + // .cpload expand to: + // lui $gp, %hi(__gnu_local_gp) + // addiu $gp, $gp, %lo(__gnu_local_gp) + + StringRef SymName("_gp_disp"); + MCAssembler &MCA = getStreamer().getAssembler(); + MCSymbol *GP_Disp = MCA.getContext().getOrCreateSymbol(SymName); + MCA.registerSymbol(*GP_Disp); + + MCInst TmpInst; + TmpInst.setOpcode(Maxis::LUi); + TmpInst.addOperand(MCOperand::createReg(Maxis::GP)); + const MCExpr *HiSym = MaxisMCExpr::create( + MaxisMCExpr::MEK_HI, + MCSymbolRefExpr::create("_gp_disp", MCSymbolRefExpr::VK_None, + MCA.getContext()), + MCA.getContext()); + TmpInst.addOperand(MCOperand::createExpr(HiSym)); + getStreamer().EmitInstruction(TmpInst, STI); + + TmpInst.clear(); + + TmpInst.setOpcode(Maxis::ADDiu); + TmpInst.addOperand(MCOperand::createReg(Maxis::GP)); + TmpInst.addOperand(MCOperand::createReg(Maxis::GP)); + const MCExpr *LoSym = MaxisMCExpr::create( + MaxisMCExpr::MEK_LO, + MCSymbolRefExpr::create("_gp_disp", MCSymbolRefExpr::VK_None, + MCA.getContext()), + MCA.getContext()); + TmpInst.addOperand(MCOperand::createExpr(LoSym)); + getStreamer().EmitInstruction(TmpInst, STI); + + TmpInst.clear(); + + TmpInst.setOpcode(Maxis::ADDu); + TmpInst.addOperand(MCOperand::createReg(Maxis::GP)); + TmpInst.addOperand(MCOperand::createReg(Maxis::GP)); + TmpInst.addOperand(MCOperand::createReg(RegNo)); + getStreamer().EmitInstruction(TmpInst, STI); + + forbidModuleDirective(); +} + +bool MaxisTargetELFStreamer::emitDirectiveCpRestore( + int Offset, function_ref GetATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI) { + MaxisTargetStreamer::emitDirectiveCpRestore(Offset, GetATReg, IDLoc, STI); + // .cprestore offset + // When PIC mode is enabled and the O32 ABI is used, this directive expands + // to: + // sw $gp, offset($sp) + // and adds a corresponding LW after every JAL. + + // Note that .cprestore is ignored if used with the N32 and N64 ABIs or if it + // is used in non-PIC mode. + if (!Pic || (getABI().IsN32() || getABI().IsN64())) + return true; + + // Store the $gp on the stack. + emitStoreWithImmOffset(Maxis::SW, Maxis::GP, Maxis::SP, Offset, GetATReg, IDLoc, + STI); + return true; +} + +void MaxisTargetELFStreamer::emitDirectiveCpsetup(unsigned RegNo, + int RegOrOffset, + const MCSymbol &Sym, + bool IsReg) { + // Only N32 and N64 emit anything for .cpsetup iff PIC is set. + if (!Pic || !(getABI().IsN32() || getABI().IsN64())) + return; + + forbidModuleDirective(); + + MCAssembler &MCA = getStreamer().getAssembler(); + MCInst Inst; + + // Either store the old $gp in a register or on the stack + if (IsReg) { + // move $save, $gpreg + emitRRR(Maxis::OR64, RegOrOffset, Maxis::GP, Maxis::ZERO, SMLoc(), &STI); + } else { + // sd $gpreg, offset($sp) + emitRRI(Maxis::SD, Maxis::GP, Maxis::SP, RegOrOffset, SMLoc(), &STI); + } + + if (getABI().IsN32()) { + MCSymbol *GPSym = MCA.getContext().getOrCreateSymbol("__gnu_local_gp"); + const MaxisMCExpr *HiExpr = MaxisMCExpr::create( + MaxisMCExpr::MEK_HI, MCSymbolRefExpr::create(GPSym, MCA.getContext()), + MCA.getContext()); + const MaxisMCExpr *LoExpr = MaxisMCExpr::create( + MaxisMCExpr::MEK_LO, MCSymbolRefExpr::create(GPSym, MCA.getContext()), + MCA.getContext()); + + // lui $gp, %hi(__gnu_local_gp) + emitRX(Maxis::LUi, Maxis::GP, MCOperand::createExpr(HiExpr), SMLoc(), &STI); + + // addiu $gp, $gp, %lo(__gnu_local_gp) + emitRRX(Maxis::ADDiu, Maxis::GP, Maxis::GP, MCOperand::createExpr(LoExpr), + SMLoc(), &STI); + + return; + } + + const MaxisMCExpr *HiExpr = MaxisMCExpr::createGpOff( + MaxisMCExpr::MEK_HI, MCSymbolRefExpr::create(&Sym, MCA.getContext()), + MCA.getContext()); + const MaxisMCExpr *LoExpr = MaxisMCExpr::createGpOff( + MaxisMCExpr::MEK_LO, MCSymbolRefExpr::create(&Sym, MCA.getContext()), + MCA.getContext()); + + // lui $gp, %hi(%neg(%gp_rel(funcSym))) + emitRX(Maxis::LUi, Maxis::GP, MCOperand::createExpr(HiExpr), SMLoc(), &STI); + + // addiu $gp, $gp, %lo(%neg(%gp_rel(funcSym))) + emitRRX(Maxis::ADDiu, Maxis::GP, Maxis::GP, MCOperand::createExpr(LoExpr), + SMLoc(), &STI); + + // daddu $gp, $gp, $funcreg + emitRRR(Maxis::DADDu, Maxis::GP, Maxis::GP, RegNo, SMLoc(), &STI); +} + +void MaxisTargetELFStreamer::emitDirectiveCpreturn(unsigned SaveLocation, + bool SaveLocationIsRegister) { + // Only N32 and N64 emit anything for .cpreturn iff PIC is set. + if (!Pic || !(getABI().IsN32() || getABI().IsN64())) + return; + + MCInst Inst; + // Either restore the old $gp from a register or on the stack + if (SaveLocationIsRegister) { + Inst.setOpcode(Maxis::OR); + Inst.addOperand(MCOperand::createReg(Maxis::GP)); + Inst.addOperand(MCOperand::createReg(SaveLocation)); + Inst.addOperand(MCOperand::createReg(Maxis::ZERO)); + } else { + Inst.setOpcode(Maxis::LD); + Inst.addOperand(MCOperand::createReg(Maxis::GP)); + Inst.addOperand(MCOperand::createReg(Maxis::SP)); + Inst.addOperand(MCOperand::createImm(SaveLocation)); + } + getStreamer().EmitInstruction(Inst, STI); + + forbidModuleDirective(); +} + +void MaxisTargetELFStreamer::emitMaxisAbiFlags() { + MCAssembler &MCA = getStreamer().getAssembler(); + MCContext &Context = MCA.getContext(); + MCStreamer &OS = getStreamer(); + MCSectionELF *Sec = Context.getELFSection( + ".MAXIS.abiflags", ELF::SHT_MAXIS_ABIFLAGS, ELF::SHF_ALLOC, 24, ""); + MCA.registerSection(*Sec); + Sec->setAlignment(8); + OS.SwitchSection(Sec); + + OS << ABIFlagsSection; +} diff --git a/lib/Target/Maxis/MSA.txt b/lib/Target/Maxis/MSA.txt new file mode 100644 index 00000000..c10da2d0 --- /dev/null +++ b/lib/Target/Maxis/MSA.txt @@ -0,0 +1,83 @@ +Code Generation Notes for MSA +============================= + +Intrinsics are lowered to SelectionDAG nodes where possible in order to enable +optimisation, reduce the size of the ISel matcher, and reduce repetition in +the implementation. In a small number of cases, this can cause different +(semantically equivalent) instructions to be used in place of the requested +instruction, even when no optimisation has taken place. + +Instructions +============ + +This section describes any quirks of instruction selection for MSA. For +example, two instructions might be equally valid for some given IR and one is +chosen in preference to the other. + +bclri.b: + It is not possible to emit bclri.b since andi.b covers exactly the + same cases. andi.b should use fractionally less power than bclri.b in + most hardware implementations so it is used in preference to bclri.b. + +vshf.w: + It is not possible to emit vshf.w when the shuffle description is + constant since shf.w covers exactly the same cases. shf.w is used + instead. It is also impossible for the shuffle description to be + unknown at compile-time due to the definition of shufflevector in + LLVM IR. + +vshf.[bhwd] + When the shuffle description describes a splat operation, splat.[bhwd] + instructions will be selected instead of vshf.[bhwd]. Unlike the ilv*, + and pck* instructions, this is matched from MaxisISD::VSHF instead of + a special-case MaxisISD node. + +ilvl.d, pckev.d: + It is not possible to emit ilvl.d, or pckev.d since ilvev.d covers the + same shuffle. ilvev.d will be emitted instead. + +ilvr.d, ilvod.d, pckod.d: + It is not possible to emit ilvr.d, or pckod.d since ilvod.d covers the + same shuffle. ilvod.d will be emitted instead. + +splat.[bhwd] + The intrinsic will work as expected. However, unlike other intrinsics + it lowers directly to MaxisISD::VSHF instead of using common IR. + +splati.w: + It is not possible to emit splati.w since shf.w covers the same cases. + shf.w will be emitted instead. + +copy_s.w: + On MAXIS32, the copy_u.d intrinsic will emit this instruction instead of + copy_u.w. This is semantically equivalent since the general-purpose + register file is 32-bits wide. + +binsri.[bhwd], binsli.[bhwd]: + These two operations are equivalent to each other with the operands + swapped and condition inverted. The compiler may use either one as + appropriate. + Furthermore, the compiler may use bsel.[bhwd] for some masks that do + not survive the legalization process (this is a bug and will be fixed). + +bmnz.v, bmz.v, bsel.v: + These three operations differ only in the operand that is tied to the + result and the order of the operands. + It is (currently) not possible to emit bmz.v, or bsel.v since bmnz.v is + the same operation and will be emitted instead. + In future, the compiler may choose between these three instructions + according to register allocation. + These three operations can be very confusing so here is a mapping + between the instructions and the vselect node in one place: + bmz.v wd, ws, wt/i8 -> (vselect wt/i8, wd, ws) + bmnz.v wd, ws, wt/i8 -> (vselect wt/i8, ws, wd) + bsel.v wd, ws, wt/i8 -> (vselect wd, wt/i8, ws) + +bmnzi.b, bmzi.b: + Like their non-immediate counterparts, bmnzi.v and bmzi.v are the same + operation with the operands swapped. bmnzi.v will (currently) be emitted + for both cases. + +bseli.v: + Unlike the non-immediate versions, bseli.v is distinguishable from + bmnzi.b and bmzi.b and can be emitted. diff --git a/lib/Target/Maxis/Maxis.h b/lib/Target/Maxis/Maxis.h new file mode 100644 index 00000000..d19a8120 --- /dev/null +++ b/lib/Target/Maxis/Maxis.h @@ -0,0 +1,38 @@ +//===-- Maxis.h - Top-level interface for Maxis representation ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the entry points for global functions defined in +// the LLVM Maxis back-end. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXIS_H +#define LLVM_LIB_TARGET_MAXIS_MAXIS_H + +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + class MaxisTargetMachine; + class ModulePass; + class FunctionPass; + + ModulePass *createMaxisOs16Pass(); + ModulePass *createMaxis16HardFloatPass(); + + FunctionPass *createMaxisModuleISelDagPass(); + FunctionPass *createMaxisOptimizePICCallPass(); + FunctionPass *createMaxisDelaySlotFillerPass(); + FunctionPass *createMaxisHazardSchedule(); + FunctionPass *createMaxisLongBranchPass(); + FunctionPass *createMaxisConstantIslandPass(); + FunctionPass *createMicroMaxisSizeReductionPass(); +} // end namespace llvm; + +#endif diff --git a/lib/Target/Maxis/Maxis.td b/lib/Target/Maxis/Maxis.td new file mode 100644 index 00000000..b71c65dd --- /dev/null +++ b/lib/Target/Maxis/Maxis.td @@ -0,0 +1,241 @@ +//===-- Maxis.td - Describe the Maxis Target Machine ---------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This is the top level entry point for the Maxis target. +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Target-independent interfaces +//===----------------------------------------------------------------------===// + +include "llvm/Target/Target.td" + +// The overall idea of the PredicateControl class is to chop the Predicates list +// into subsets that are usually overridden independently. This allows +// subclasses to partially override the predicates of their superclasses without +// having to re-add all the existing predicates. +class PredicateControl { + // Predicates for the encoding scheme in use such as HasStdEnc + list EncodingPredicates = []; + // Predicates for the GPR size such as IsGP64bit + list GPRPredicates = []; + // Predicates for the PTR size such as IsPTR64bit + list PTRPredicates = []; + // Predicates for the FGR size and layout such as IsFP64bit + list FGRPredicates = []; + // Predicates for the instruction group membership such as ISA's and ASE's + list InsnPredicates = []; + // Predicate for marking the instruction as usable in hard-float mode only. + list HardFloatPredicate = []; + // Predicates for anything else + list AdditionalPredicates = []; + list Predicates = !listconcat(EncodingPredicates, + GPRPredicates, + PTRPredicates, + FGRPredicates, + InsnPredicates, + HardFloatPredicate, + AdditionalPredicates); +} + +// Like Requires<> but for the AdditionalPredicates list +class AdditionalRequires preds> { + list AdditionalPredicates = preds; +} + +//===----------------------------------------------------------------------===// +// Register File, Calling Conv, Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "MaxisRegisterInfo.td" +include "MaxisSchedule.td" +include "MaxisInstrInfo.td" +include "MaxisCallingConv.td" + +// Avoid forward declaration issues. +include "MaxisScheduleP5600.td" +include "MaxisScheduleGeneric.td" + +def MaxisInstrInfo : InstrInfo; + +//===----------------------------------------------------------------------===// +// Maxis Subtarget features // +//===----------------------------------------------------------------------===// + +def FeatureNoABICalls : SubtargetFeature<"noabicalls", "NoABICalls", "true", + "Disable SVR4-style position-independent code">; +def FeaturePTR64Bit : SubtargetFeature<"ptr64", "IsPTR64bit", "true", + "Pointers are 64-bit wide">; +def FeatureGP64Bit : SubtargetFeature<"gp64", "IsGP64bit", "true", + "General Purpose Registers are 64-bit wide">; +def FeatureFP64Bit : SubtargetFeature<"fp64", "IsFP64bit", "true", + "Support 64-bit FP registers">; +def FeatureFPXX : SubtargetFeature<"fpxx", "IsFPXX", "true", + "Support for FPXX">; +def FeatureNaN2008 : SubtargetFeature<"nan2008", "IsNaN2008bit", "true", + "IEEE 754-2008 NaN encoding">; +def FeatureSingleFloat : SubtargetFeature<"single-float", "IsSingleFloat", + "true", "Only supports single precision float">; +def FeatureSoftFloat : SubtargetFeature<"soft-float", "IsSoftFloat", "true", + "Does not support floating point instructions">; +def FeatureNoOddSPReg : SubtargetFeature<"nooddspreg", "UseOddSPReg", "false", + "Disable odd numbered single-precision " + "registers">; +def FeatureVFPU : SubtargetFeature<"vfpu", "HasVFPU", + "true", "Enable vector FPU instructions">; +def FeatureMaxis1 : SubtargetFeature<"maxis1", "MaxisArchVersion", "Maxis1", + "Maxis I ISA Support [highly experimental]">; +def FeatureMaxis2 : SubtargetFeature<"maxis2", "MaxisArchVersion", "Maxis2", + "Maxis II ISA Support [highly experimental]", + [FeatureMaxis1]>; +def FeatureMaxis3_32 : SubtargetFeature<"maxis3_32", "HasMaxis3_32", "true", + "Subset of MAXIS-III that is also in MAXIS32 " + "[highly experimental]">; +def FeatureMaxis3_32r2 : SubtargetFeature<"maxis3_32r2", "HasMaxis3_32r2", "true", + "Subset of MAXIS-III that is also in MAXIS32r2 " + "[highly experimental]">; +def FeatureMaxis3 : SubtargetFeature<"maxis3", "MaxisArchVersion", "Maxis3", + "MAXIS III ISA Support [highly experimental]", + [FeatureMaxis2, FeatureMaxis3_32, + FeatureMaxis3_32r2, FeatureGP64Bit, + FeatureFP64Bit]>; +def FeatureMaxis4_32 : SubtargetFeature<"maxis4_32", "HasMaxis4_32", "true", + "Subset of MAXIS-IV that is also in MAXIS32 " + "[highly experimental]">; +def FeatureMaxis4_32r2 : SubtargetFeature<"maxis4_32r2", "HasMaxis4_32r2", "true", + "Subset of MAXIS-IV that is also in MAXIS32r2 " + "[highly experimental]">; +def FeatureMaxis4 : SubtargetFeature<"maxis4", "MaxisArchVersion", + "Maxis4", "MAXIS IV ISA Support", + [FeatureMaxis3, FeatureMaxis4_32, + FeatureMaxis4_32r2]>; +def FeatureMaxis5_32r2 : SubtargetFeature<"maxis5_32r2", "HasMaxis5_32r2", "true", + "Subset of MAXIS-V that is also in MAXIS32r2 " + "[highly experimental]">; +def FeatureMaxis5 : SubtargetFeature<"maxis5", "MaxisArchVersion", "Maxis5", + "MAXIS V ISA Support [highly experimental]", + [FeatureMaxis4, FeatureMaxis5_32r2]>; +def FeatureMaxis32 : SubtargetFeature<"maxis32", "MaxisArchVersion", "Maxis32", + "Maxis32 ISA Support", + [FeatureMaxis2, FeatureMaxis3_32, + FeatureMaxis4_32]>; +def FeatureMaxis32r2 : SubtargetFeature<"maxis32r2", "MaxisArchVersion", + "Maxis32r2", "Maxis32r2 ISA Support", + [FeatureMaxis3_32r2, FeatureMaxis4_32r2, + FeatureMaxis5_32r2, FeatureMaxis32]>; +def FeatureMaxis32r3 : SubtargetFeature<"maxis32r3", "MaxisArchVersion", + "Maxis32r3", "Maxis32r3 ISA Support", + [FeatureMaxis32r2]>; +def FeatureMaxis32r5 : SubtargetFeature<"maxis32r5", "MaxisArchVersion", + "Maxis32r5", "Maxis32r5 ISA Support", + [FeatureMaxis32r3]>; +def FeatureMaxis32r6 : SubtargetFeature<"maxis32r6", "MaxisArchVersion", + "Maxis32r6", + "Maxis32r6 ISA Support [experimental]", + [FeatureMaxis32r5, FeatureFP64Bit, + FeatureNaN2008]>; +def FeatureMaxis64 : SubtargetFeature<"maxis64", "MaxisArchVersion", + "Maxis64", "Maxis64 ISA Support", + [FeatureMaxis5, FeatureMaxis32]>; +def FeatureMaxis64r2 : SubtargetFeature<"maxis64r2", "MaxisArchVersion", + "Maxis64r2", "Maxis64r2 ISA Support", + [FeatureMaxis64, FeatureMaxis32r2]>; +def FeatureMaxis64r3 : SubtargetFeature<"maxis64r3", "MaxisArchVersion", + "Maxis64r3", "Maxis64r3 ISA Support", + [FeatureMaxis64r2, FeatureMaxis32r3]>; +def FeatureMaxis64r5 : SubtargetFeature<"maxis64r5", "MaxisArchVersion", + "Maxis64r5", "Maxis64r5 ISA Support", + [FeatureMaxis64r3, FeatureMaxis32r5]>; +def FeatureMaxis64r6 : SubtargetFeature<"maxis64r6", "MaxisArchVersion", + "Maxis64r6", + "Maxis64r6 ISA Support [experimental]", + [FeatureMaxis32r6, FeatureMaxis64r5, + FeatureNaN2008]>; +def FeatureSym32 : SubtargetFeature<"sym32", "HasSym32", "true", + "Symbols are 32 bit on Maxis64">; + +def FeatureMaxis16 : SubtargetFeature<"maxis16", "InMaxis16Mode", "true", + "Maxis16 mode">; + +def FeatureDSP : SubtargetFeature<"dsp", "HasDSP", "true", "Maxis DSP ASE">; +def FeatureDSPR2 : SubtargetFeature<"dspr2", "HasDSPR2", "true", + "Maxis DSP-R2 ASE", [FeatureDSP]>; +def FeatureDSPR3 + : SubtargetFeature<"dspr3", "HasDSPR3", "true", "Maxis DSP-R3 ASE", + [ FeatureDSP, FeatureDSPR2 ]>; + +def FeatureMSA : SubtargetFeature<"msa", "HasMSA", "true", "Maxis MSA ASE">; + +def FeatureEVA : SubtargetFeature<"eva", "HasEVA", "true", "Maxis EVA ASE">; + +def FeatureMicroMaxis : SubtargetFeature<"micromaxis", "InMicroMaxisMode", "true", + "microMaxis mode">; + +def FeatureCnMaxis : SubtargetFeature<"cnmaxis", "HasCnMaxis", + "true", "Octeon cnMAXIS Support", + [FeatureMaxis64r2]>; + +def FeatureUseTCCInDIV : SubtargetFeature< + "use-tcc-in-div", + "UseTCCInDIV", "false", + "Force the assembler to use trapping">; + +def FeatureMadd4 : SubtargetFeature<"nomadd4", "DisableMadd4", "true", + "Disable 4-operand madd.fmt and related instructions">; + +def FeatureMT : SubtargetFeature<"mt", "HasMT", "true", "Maxis MT ASE">; + +def FeatureLongCalls : SubtargetFeature<"long-calls", "UseLongCalls", "true", + "Disable use of the jal instruction">; + +//===----------------------------------------------------------------------===// +// Maxis processors supported. +//===----------------------------------------------------------------------===// + +def ImplP5600 : SubtargetFeature<"p5600", "ProcImpl", + "MaxisSubtarget::CPU::P5600", + "The P5600 Processor", [FeatureMaxis32r5]>; + +class Proc Features> + : ProcessorModel; + +def : Proc<"maxis1", [FeatureMaxis1]>; +def : Proc<"maxis2", [FeatureMaxis2]>; +def : Proc<"maxis32", [FeatureMaxis32]>; +def : Proc<"maxis32r2", [FeatureMaxis32r2]>; +def : Proc<"maxis32r3", [FeatureMaxis32r3]>; +def : Proc<"maxis32r5", [FeatureMaxis32r5]>; +def : Proc<"maxis32r6", [FeatureMaxis32r6]>; + +def : Proc<"maxis3", [FeatureMaxis3]>; +def : Proc<"maxis4", [FeatureMaxis4]>; +def : Proc<"maxis5", [FeatureMaxis5]>; +def : Proc<"maxis64", [FeatureMaxis64]>; +def : Proc<"maxis64r2", [FeatureMaxis64r2]>; +def : Proc<"maxis64r3", [FeatureMaxis64r3]>; +def : Proc<"maxis64r5", [FeatureMaxis64r5]>; +def : Proc<"maxis64r6", [FeatureMaxis64r6]>; +def : Proc<"octeon", [FeatureMaxis64r2, FeatureCnMaxis]>; +def : ProcessorModel<"p5600", MaxisP5600Model, [ImplP5600]>; + +def MaxisAsmParser : AsmParser { + let ShouldEmitMatchRegisterName = 0; +} + +def MaxisAsmParserVariant : AsmParserVariant { + int Variant = 0; + + // Recognize hard coded registers. + string RegisterPrefix = "$"; +} + +def Maxis : Target { + let InstructionSet = MaxisInstrInfo; + let AssemblyParsers = [MaxisAsmParser]; + let AssemblyParserVariants = [MaxisAsmParserVariant]; +} diff --git a/lib/Target/Maxis/Maxis16FrameLowering.cpp b/lib/Target/Maxis/Maxis16FrameLowering.cpp new file mode 100644 index 00000000..97015339 --- /dev/null +++ b/lib/Target/Maxis/Maxis16FrameLowering.cpp @@ -0,0 +1,183 @@ +//===- Maxis16FrameLowering.cpp - Maxis16 Frame Information -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis16 implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "Maxis16FrameLowering.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "Maxis16InstrInfo.h" +#include "MaxisInstrInfo.h" +#include "MaxisRegisterInfo.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MachineLocation.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include +#include +#include + +using namespace llvm; + +Maxis16FrameLowering::Maxis16FrameLowering(const MaxisSubtarget &STI) + : MaxisFrameLowering(STI, STI.getStackAlignment()) {} + +void Maxis16FrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + assert(&MF.front() == &MBB && "Shrink-wrapping not yet supported"); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const Maxis16InstrInfo &TII = + *static_cast(STI.getInstrInfo()); + MachineBasicBlock::iterator MBBI = MBB.begin(); + + // Debug location must be unknown since the first debug location is used + // to determine the end of the prologue. + DebugLoc dl; + + uint64_t StackSize = MFI.getStackSize(); + + // No need to allocate space on the stack. + if (StackSize == 0 && !MFI.adjustsStack()) return; + + MachineModuleInfo &MMI = MF.getMMI(); + const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo(); + + // Adjust stack. + TII.makeFrame(Maxis::SP, StackSize, MBB, MBBI); + + // emit ".cfi_def_cfa_offset StackSize" + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createDefCfaOffset(nullptr, -StackSize)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + + const std::vector &CSI = MFI.getCalleeSavedInfo(); + + if (!CSI.empty()) { + const std::vector &CSI = MFI.getCalleeSavedInfo(); + + 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 DReg = MRI->getDwarfRegNum(Reg, true); + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createOffset(nullptr, DReg, Offset)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } + } + if (hasFP(MF)) + BuildMI(MBB, MBBI, dl, TII.get(Maxis::MoveR3216), Maxis::S0) + .addReg(Maxis::SP).setMIFlag(MachineInstr::FrameSetup); +} + +void Maxis16FrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const Maxis16InstrInfo &TII = + *static_cast(STI.getInstrInfo()); + DebugLoc dl = MBBI->getDebugLoc(); + uint64_t StackSize = MFI.getStackSize(); + + if (!StackSize) + return; + + if (hasFP(MF)) + BuildMI(MBB, MBBI, dl, TII.get(Maxis::Move32R16), Maxis::SP) + .addReg(Maxis::S0); + + // Adjust stack. + // assumes stacksize multiple of 8 + TII.restoreFrame(Maxis::SP, StackSize, MBB, MBBI); +} + +bool Maxis16FrameLowering:: +spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const { + MachineFunction *MF = MBB.getParent(); + MachineBasicBlock *EntryBlock = &MF->front(); + + // + // Registers RA, S0,S1 are the callee saved registers and they + // will be saved with the "save" instruction + // during emitPrologue + // + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + // Add the callee-saved register as live-in. Do not add if the register is + // RA and return address is taken, because it has already been added in + // method MaxisTargetLowering::lowerRETURNADDR. + // It's killed at the spill, unless the register is RA and return address + // is taken. + unsigned Reg = CSI[i].getReg(); + bool IsRAAndRetAddrIsTaken = (Reg == Maxis::RA) + && MF->getFrameInfo().isReturnAddressTaken(); + if (!IsRAAndRetAddrIsTaken) + EntryBlock->addLiveIn(Reg); + } + + return true; +} + +bool Maxis16FrameLowering::restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + std::vector &CSI, + const TargetRegisterInfo *TRI) const { + // + // Registers RA,S0,S1 are the callee saved registers and they will be restored + // with the restore instruction during emitEpilogue. + // We need to override this virtual function, otherwise llvm will try and + // restore the registers on it's on from the stack. + // + + return true; +} + +bool +Maxis16FrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + // Reserve call frame if the size of the maximum call frame fits into 15-bit + // immediate field and there are no variable sized objects on the stack. + return isInt<15>(MFI.getMaxCallFrameSize()) && !MFI.hasVarSizedObjects(); +} + +void Maxis16FrameLowering::determineCalleeSaves(MachineFunction &MF, + BitVector &SavedRegs, + RegScavenger *RS) const { + TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); + const Maxis16InstrInfo &TII = + *static_cast(STI.getInstrInfo()); + const MaxisRegisterInfo &RI = TII.getRegisterInfo(); + const BitVector Reserved = RI.getReservedRegs(MF); + bool SaveS2 = Reserved[Maxis::S2]; + if (SaveS2) + SavedRegs.set(Maxis::S2); + if (hasFP(MF)) + SavedRegs.set(Maxis::S0); +} + +const MaxisFrameLowering * +llvm::createMaxis16FrameLowering(const MaxisSubtarget &ST) { + return new Maxis16FrameLowering(ST); +} diff --git a/lib/Target/Maxis/Maxis16FrameLowering.h b/lib/Target/Maxis/Maxis16FrameLowering.h new file mode 100644 index 00000000..d380425b --- /dev/null +++ b/lib/Target/Maxis/Maxis16FrameLowering.h @@ -0,0 +1,47 @@ +//===-- Maxis16FrameLowering.h - Maxis16 frame lowering ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXIS16FRAMELOWERING_H +#define LLVM_LIB_TARGET_MAXIS_MAXIS16FRAMELOWERING_H + +#include "MaxisFrameLowering.h" + +namespace llvm { +class Maxis16FrameLowering : public MaxisFrameLowering { +public: + explicit Maxis16FrameLowering(const MaxisSubtarget &STI); + + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into + /// the function. + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const override; + + bool restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + std::vector &CSI, + const TargetRegisterInfo *TRI) const override; + + bool hasReservedCallFrame(const MachineFunction &MF) const override; + + void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, + RegScavenger *RS) const override; +}; + +} // End llvm namespace + +#endif diff --git a/lib/Target/Maxis/Maxis16HardFloat.cpp b/lib/Target/Maxis/Maxis16HardFloat.cpp new file mode 100644 index 00000000..60596ee7 --- /dev/null +++ b/lib/Target/Maxis/Maxis16HardFloat.cpp @@ -0,0 +1,535 @@ +//===- Maxis16HardFloat.cpp for Maxis16 Hard Float --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a pass needed for Maxis16 Hard Float +// +//===----------------------------------------------------------------------===// + +#include "MaxisTargetMachine.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis16-hard-float" + +namespace { + + class Maxis16HardFloat : public ModulePass { + public: + static char ID; + + Maxis16HardFloat() : ModulePass(ID) {} + + StringRef getPassName() const override { return "MAXIS16 Hard Float Pass"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + ModulePass::getAnalysisUsage(AU); + } + + bool runOnModule(Module &M) override; + }; + +} // end anonymous namespace + +static void EmitInlineAsm(LLVMContext &C, BasicBlock *BB, StringRef AsmText) { + std::vector AsmArgTypes; + std::vector AsmArgs; + + FunctionType *AsmFTy = + FunctionType::get(Type::getVoidTy(C), AsmArgTypes, false); + InlineAsm *IA = InlineAsm::get(AsmFTy, AsmText, "", true, + /* IsAlignStack */ false, InlineAsm::AD_ATT); + CallInst::Create(IA, AsmArgs, "", BB); +} + +char Maxis16HardFloat::ID = 0; + +// +// Return types that matter for hard float are: +// float, double, complex float, and complex double +// +enum FPReturnVariant { + FRet, DRet, CFRet, CDRet, NoFPRet +}; + +// +// Determine which FP return type this function has +// +static FPReturnVariant whichFPReturnVariant(Type *T) { + switch (T->getTypeID()) { + case Type::FloatTyID: + return FRet; + case Type::DoubleTyID: + return DRet; + case Type::StructTyID: + if (T->getStructNumElements() != 2) + break; + if ((T->getContainedType(0)->isFloatTy()) && + (T->getContainedType(1)->isFloatTy())) + return CFRet; + if ((T->getContainedType(0)->isDoubleTy()) && + (T->getContainedType(1)->isDoubleTy())) + return CDRet; + break; + default: + break; + } + return NoFPRet; +} + +// Parameter type that matter are float, (float, float), (float, double), +// double, (double, double), (double, float) +enum FPParamVariant { + FSig, FFSig, FDSig, + DSig, DDSig, DFSig, NoSig +}; + +// which floating point parameter signature variant we are dealing with +using TypeID = Type::TypeID; +const Type::TypeID FloatTyID = Type::FloatTyID; +const Type::TypeID DoubleTyID = Type::DoubleTyID; + +static FPParamVariant whichFPParamVariantNeeded(Function &F) { + switch (F.arg_size()) { + case 0: + return NoSig; + case 1:{ + TypeID ArgTypeID = F.getFunctionType()->getParamType(0)->getTypeID(); + switch (ArgTypeID) { + case FloatTyID: + return FSig; + case DoubleTyID: + return DSig; + default: + return NoSig; + } + } + default: { + TypeID ArgTypeID0 = F.getFunctionType()->getParamType(0)->getTypeID(); + TypeID ArgTypeID1 = F.getFunctionType()->getParamType(1)->getTypeID(); + switch(ArgTypeID0) { + case FloatTyID: { + switch (ArgTypeID1) { + case FloatTyID: + return FFSig; + case DoubleTyID: + return FDSig; + default: + return FSig; + } + } + case DoubleTyID: { + switch (ArgTypeID1) { + case FloatTyID: + return DFSig; + case DoubleTyID: + return DDSig; + default: + return DSig; + } + } + default: + return NoSig; + } + } + } + llvm_unreachable("can't get here"); +} + +// Figure out if we need float point based on the function parameters. +// We need to move variables in and/or out of floating point +// registers because of the ABI +static bool needsFPStubFromParams(Function &F) { + if (F.arg_size() >=1) { + Type *ArgType = F.getFunctionType()->getParamType(0); + switch (ArgType->getTypeID()) { + case Type::FloatTyID: + case Type::DoubleTyID: + return true; + default: + break; + } + } + return false; +} + +static bool needsFPReturnHelper(Function &F) { + Type* RetType = F.getReturnType(); + return whichFPReturnVariant(RetType) != NoFPRet; +} + +static bool needsFPReturnHelper(FunctionType &FT) { + Type* RetType = FT.getReturnType(); + return whichFPReturnVariant(RetType) != NoFPRet; +} + +static bool needsFPHelperFromSig(Function &F) { + return needsFPStubFromParams(F) || needsFPReturnHelper(F); +} + +// We swap between FP and Integer registers to allow Maxis16 and Maxis32 to +// interoperate +static std::string swapFPIntParams(FPParamVariant PV, Module *M, bool LE, + bool ToFP) { + std::string MI = ToFP ? "mtc1 ": "mfc1 "; + std::string AsmText; + + switch (PV) { + case FSig: + AsmText += MI + "$$4, $$f12\n"; + break; + + case FFSig: + AsmText += MI + "$$4, $$f12\n"; + AsmText += MI + "$$5, $$f14\n"; + break; + + case FDSig: + AsmText += MI + "$$4, $$f12\n"; + if (LE) { + AsmText += MI + "$$6, $$f14\n"; + AsmText += MI + "$$7, $$f15\n"; + } else { + AsmText += MI + "$$7, $$f14\n"; + AsmText += MI + "$$6, $$f15\n"; + } + break; + + case DSig: + if (LE) { + AsmText += MI + "$$4, $$f12\n"; + AsmText += MI + "$$5, $$f13\n"; + } else { + AsmText += MI + "$$5, $$f12\n"; + AsmText += MI + "$$4, $$f13\n"; + } + break; + + case DDSig: + if (LE) { + AsmText += MI + "$$4, $$f12\n"; + AsmText += MI + "$$5, $$f13\n"; + AsmText += MI + "$$6, $$f14\n"; + AsmText += MI + "$$7, $$f15\n"; + } else { + AsmText += MI + "$$5, $$f12\n"; + AsmText += MI + "$$4, $$f13\n"; + AsmText += MI + "$$7, $$f14\n"; + AsmText += MI + "$$6, $$f15\n"; + } + break; + + case DFSig: + if (LE) { + AsmText += MI + "$$4, $$f12\n"; + AsmText += MI + "$$5, $$f13\n"; + } else { + AsmText += MI + "$$5, $$f12\n"; + AsmText += MI + "$$4, $$f13\n"; + } + AsmText += MI + "$$6, $$f14\n"; + break; + + case NoSig: + break; + } + + return AsmText; +} + +// Make sure that we know we already need a stub for this function. +// Having called needsFPHelperFromSig +static void assureFPCallStub(Function &F, Module *M, + const MaxisTargetMachine &TM) { + // for now we only need them for static relocation + if (TM.isPositionIndependent()) + return; + LLVMContext &Context = M->getContext(); + bool LE = TM.isLittleEndian(); + std::string Name = F.getName(); + std::string SectionName = ".maxis16.call.fp." + Name; + std::string StubName = "__call_stub_fp_" + Name; + // + // see if we already have the stub + // + Function *FStub = M->getFunction(StubName); + if (FStub && !FStub->isDeclaration()) return; + FStub = Function::Create(F.getFunctionType(), + Function::InternalLinkage, StubName, M); + FStub->addFnAttr("maxis16_fp_stub"); + FStub->addFnAttr(Attribute::Naked); + FStub->addFnAttr(Attribute::NoInline); + FStub->addFnAttr(Attribute::NoUnwind); + FStub->addFnAttr("nomaxis16"); + FStub->setSection(SectionName); + BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub); + FPReturnVariant RV = whichFPReturnVariant(FStub->getReturnType()); + FPParamVariant PV = whichFPParamVariantNeeded(F); + + std::string AsmText; + AsmText += ".set reorder\n"; + AsmText += swapFPIntParams(PV, M, LE, true); + if (RV != NoFPRet) { + AsmText += "move $$18, $$31\n"; + AsmText += "jal " + Name + "\n"; + } else { + AsmText += "lui $$25, %hi(" + Name + ")\n"; + AsmText += "addiu $$25, $$25, %lo(" + Name + ")\n"; + } + + switch (RV) { + case FRet: + AsmText += "mfc1 $$2, $$f0\n"; + break; + + case DRet: + if (LE) { + AsmText += "mfc1 $$2, $$f0\n"; + AsmText += "mfc1 $$3, $$f1\n"; + } else { + AsmText += "mfc1 $$3, $$f0\n"; + AsmText += "mfc1 $$2, $$f1\n"; + } + break; + + case CFRet: + if (LE) { + AsmText += "mfc1 $$2, $$f0\n"; + AsmText += "mfc1 $$3, $$f2\n"; + } else { + AsmText += "mfc1 $$3, $$f0\n"; + AsmText += "mfc1 $$3, $$f2\n"; + } + break; + + case CDRet: + if (LE) { + AsmText += "mfc1 $$4, $$f2\n"; + AsmText += "mfc1 $$5, $$f3\n"; + AsmText += "mfc1 $$2, $$f0\n"; + AsmText += "mfc1 $$3, $$f1\n"; + + } else { + AsmText += "mfc1 $$5, $$f2\n"; + AsmText += "mfc1 $$4, $$f3\n"; + AsmText += "mfc1 $$3, $$f0\n"; + AsmText += "mfc1 $$2, $$f1\n"; + } + break; + + case NoFPRet: + break; + } + + if (RV != NoFPRet) + AsmText += "jr $$18\n"; + else + AsmText += "jr $$25\n"; + EmitInlineAsm(Context, BB, AsmText); + + new UnreachableInst(Context, BB); +} + +// Functions that are llvm intrinsics and don't need helpers. +static const char *const IntrinsicInline[] = { + "fabs", "fabsf", + "llvm.ceil.f32", "llvm.ceil.f64", + "llvm.copysign.f32", "llvm.copysign.f64", + "llvm.cos.f32", "llvm.cos.f64", + "llvm.exp.f32", "llvm.exp.f64", + "llvm.exp2.f32", "llvm.exp2.f64", + "llvm.fabs.f32", "llvm.fabs.f64", + "llvm.floor.f32", "llvm.floor.f64", + "llvm.fma.f32", "llvm.fma.f64", + "llvm.log.f32", "llvm.log.f64", + "llvm.log10.f32", "llvm.log10.f64", + "llvm.nearbyint.f32", "llvm.nearbyint.f64", + "llvm.pow.f32", "llvm.pow.f64", + "llvm.powi.f32", "llvm.powi.f64", + "llvm.rint.f32", "llvm.rint.f64", + "llvm.round.f32", "llvm.round.f64", + "llvm.sin.f32", "llvm.sin.f64", + "llvm.sqrt.f32", "llvm.sqrt.f64", + "llvm.trunc.f32", "llvm.trunc.f64", +}; + +static bool isIntrinsicInline(Function *F) { + return std::binary_search(std::begin(IntrinsicInline), + std::end(IntrinsicInline), F->getName()); +} + +// Returns of float, double and complex need to be handled with a helper +// function. +static bool fixupFPReturnAndCall(Function &F, Module *M, + const MaxisTargetMachine &TM) { + bool Modified = false; + LLVMContext &C = M->getContext(); + Type *MyVoid = Type::getVoidTy(C); + for (auto &BB: F) + for (auto &I: BB) { + if (const ReturnInst *RI = dyn_cast(&I)) { + Value *RVal = RI->getReturnValue(); + if (!RVal) continue; + // + // If there is a return value and it needs a helper function, + // figure out which one and add a call before the actual + // return to this helper. The purpose of the helper is to move + // floating point values from their soft float return mapping to + // where they would have been mapped to in floating point registers. + // + Type *T = RVal->getType(); + FPReturnVariant RV = whichFPReturnVariant(T); + if (RV == NoFPRet) continue; + static const char *const Helper[NoFPRet] = { + "__maxis16_ret_sf", "__maxis16_ret_df", "__maxis16_ret_sc", + "__maxis16_ret_dc" + }; + const char *Name = Helper[RV]; + AttributeList A; + Value *Params[] = {RVal}; + Modified = true; + // + // These helper functions have a different calling ABI so + // this __Maxis16RetHelper indicates that so that later + // during call setup, the proper call lowering to the helper + // functions will take place. + // + A = A.addAttribute(C, AttributeList::FunctionIndex, + "__Maxis16RetHelper"); + A = A.addAttribute(C, AttributeList::FunctionIndex, + Attribute::ReadNone); + A = A.addAttribute(C, AttributeList::FunctionIndex, + Attribute::NoInline); + Value *F = (M->getOrInsertFunction(Name, A, MyVoid, T)); + CallInst::Create(F, Params, "", &I); + } else if (const CallInst *CI = dyn_cast(&I)) { + FunctionType *FT = CI->getFunctionType(); + Function *F_ = CI->getCalledFunction(); + if (needsFPReturnHelper(*FT) && + !(F_ && isIntrinsicInline(F_))) { + Modified=true; + F.addFnAttr("saveS2"); + } + if (F_ && !isIntrinsicInline(F_)) { + // pic mode calls are handled by already defined + // helper functions + if (needsFPReturnHelper(*F_)) { + Modified=true; + F.addFnAttr("saveS2"); + } + if (!TM.isPositionIndependent()) { + if (needsFPHelperFromSig(*F_)) { + assureFPCallStub(*F_, M, TM); + Modified=true; + } + } + } + } + } + return Modified; +} + +static void createFPFnStub(Function *F, Module *M, FPParamVariant PV, + const MaxisTargetMachine &TM) { + bool PicMode = TM.isPositionIndependent(); + bool LE = TM.isLittleEndian(); + LLVMContext &Context = M->getContext(); + std::string Name = F->getName(); + std::string SectionName = ".maxis16.fn." + Name; + std::string StubName = "__fn_stub_" + Name; + std::string LocalName = "$$__fn_local_" + Name; + Function *FStub = Function::Create + (F->getFunctionType(), + Function::InternalLinkage, StubName, M); + FStub->addFnAttr("maxis16_fp_stub"); + FStub->addFnAttr(Attribute::Naked); + FStub->addFnAttr(Attribute::NoUnwind); + FStub->addFnAttr(Attribute::NoInline); + FStub->addFnAttr("nomaxis16"); + FStub->setSection(SectionName); + BasicBlock *BB = BasicBlock::Create(Context, "entry", FStub); + + std::string AsmText; + if (PicMode) { + AsmText += ".set noreorder\n"; + AsmText += ".cpload $$25\n"; + AsmText += ".set reorder\n"; + AsmText += ".reloc 0, R_MAXIS_NONE, " + Name + "\n"; + AsmText += "la $$25, " + LocalName + "\n"; + } else + AsmText += "la $$25, " + Name + "\n"; + AsmText += swapFPIntParams(PV, M, LE, false); + AsmText += "jr $$25\n"; + AsmText += LocalName + " = " + Name + "\n"; + EmitInlineAsm(Context, BB, AsmText); + + new UnreachableInst(FStub->getContext(), BB); +} + +// remove the use-soft-float attribute +static void removeUseSoftFloat(Function &F) { + AttrBuilder B; + DEBUG(errs() << "removing -use-soft-float\n"); + B.addAttribute("use-soft-float", "false"); + F.removeAttributes(AttributeList::FunctionIndex, B); + if (F.hasFnAttribute("use-soft-float")) { + DEBUG(errs() << "still has -use-soft-float\n"); + } + F.addAttributes(AttributeList::FunctionIndex, B); +} + +// This pass only makes sense when the underlying chip has floating point but +// we are compiling as maxis16. +// For all maxis16 functions (that are not stubs we have already generated), or +// declared via attributes as nomaxis16, we must: +// 1) fixup all returns of float, double, single and double complex +// by calling a helper function before the actual return. +// 2) generate helper functions (stubs) that can be called by maxis32 +// functions that will move parameters passed normally passed in +// floating point +// registers the soft float equivalents. +// 3) in the case of static relocation, generate helper functions so that +// maxis16 functions can call extern functions of unknown type (maxis16 or +// maxis32). +// 4) TBD. For pic, calls to extern functions of unknown type are handled by +// predefined helper functions in libc but this work is currently done +// during call lowering but it should be moved here in the future. +bool Maxis16HardFloat::runOnModule(Module &M) { + auto &TM = static_cast( + getAnalysis().getTM()); + DEBUG(errs() << "Run on Module Maxis16HardFloat\n"); + bool Modified = false; + for (Module::iterator F = M.begin(), E = M.end(); F != E; ++F) { + if (F->hasFnAttribute("nomaxis16") && + F->hasFnAttribute("use-soft-float")) { + removeUseSoftFloat(*F); + continue; + } + if (F->isDeclaration() || F->hasFnAttribute("maxis16_fp_stub") || + F->hasFnAttribute("nomaxis16")) continue; + Modified |= fixupFPReturnAndCall(*F, &M, TM); + FPParamVariant V = whichFPParamVariantNeeded(*F); + if (V != NoSig) { + Modified = true; + createFPFnStub(&*F, &M, V, TM); + } + } + return Modified; +} + +ModulePass *llvm::createMaxis16HardFloatPass() { + return new Maxis16HardFloat(); +} diff --git a/lib/Target/Maxis/Maxis16HardFloatInfo.cpp b/lib/Target/Maxis/Maxis16HardFloatInfo.cpp new file mode 100644 index 00000000..258506d1 --- /dev/null +++ b/lib/Target/Maxis/Maxis16HardFloatInfo.cpp @@ -0,0 +1,50 @@ +//===---- Maxis16HardFloatInfo.cpp for Maxis16 Hard Float -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis16 implementation of Maxis16HardFloatInfo +// namespace. +// +//===----------------------------------------------------------------------===// + +#include "Maxis16HardFloatInfo.h" +#include + +namespace llvm { + +namespace Maxis16HardFloatInfo { + +const FuncNameSignature PredefinedFuncs[] = { + { "__floatdidf", { NoSig, DRet } }, + { "__floatdisf", { NoSig, FRet } }, + { "__floatundidf", { NoSig, DRet } }, + { "__fixsfdi", { FSig, NoFPRet } }, + { "__fixunsdfsi", { DSig, NoFPRet } }, + { "__fixunsdfdi", { DSig, NoFPRet } }, + { "__fixdfdi", { DSig, NoFPRet } }, + { "__fixunssfsi", { FSig, NoFPRet } }, + { "__fixunssfdi", { FSig, NoFPRet } }, + { "__floatundisf", { NoSig, FRet } }, + { nullptr, { NoSig, NoFPRet } } +}; + +// just do a search for now. there are very few of these special cases. +// +extern FuncSignature const *findFuncSignature(const char *name) { + const char *name_; + int i = 0; + while (PredefinedFuncs[i].Name) { + name_ = PredefinedFuncs[i].Name; + if (strcmp(name, name_) == 0) + return &PredefinedFuncs[i].Signature; + i++; + } + return nullptr; +} +} +} diff --git a/lib/Target/Maxis/Maxis16HardFloatInfo.h b/lib/Target/Maxis/Maxis16HardFloatInfo.h new file mode 100644 index 00000000..cbc35bcc --- /dev/null +++ b/lib/Target/Maxis/Maxis16HardFloatInfo.h @@ -0,0 +1,50 @@ +//===---- Maxis16HardFloatInfo.h for Maxis16 Hard Float --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines some data structures relevant to the implementation of +// Maxis16 hard float. +// +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXIS16HARDFLOATINFO_H +#define LLVM_LIB_TARGET_MAXIS_MAXIS16HARDFLOATINFO_H + +namespace llvm { + +namespace Maxis16HardFloatInfo { + +// Return types that matter for hard float are: +// float, double, complex float, and complex double +// +enum FPReturnVariant { FRet, DRet, CFRet, CDRet, NoFPRet }; + +// +// Parameter type that matter are float, (float, float), (float, double), +// double, (double, double), (double, float) +// +enum FPParamVariant { FSig, FFSig, FDSig, DSig, DDSig, DFSig, NoSig }; + +struct FuncSignature { + FPParamVariant ParamSig; + FPReturnVariant RetSig; +}; + +struct FuncNameSignature { + const char *Name; + FuncSignature Signature; +}; + +extern const FuncNameSignature PredefinedFuncs[]; + +extern FuncSignature const *findFuncSignature(const char *name); +} +} + +#endif diff --git a/lib/Target/Maxis/Maxis16ISelDAGToDAG.cpp b/lib/Target/Maxis/Maxis16ISelDAGToDAG.cpp new file mode 100644 index 00000000..3eead6a3 --- /dev/null +++ b/lib/Target/Maxis/Maxis16ISelDAGToDAG.cpp @@ -0,0 +1,261 @@ +//===-- Maxis16ISelDAGToDAG.cpp - A Dag to Dag Inst Selector for Maxis16 ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Subclass of MaxisDAGToDAGISel specialized for maxis16. +// +//===----------------------------------------------------------------------===// + +#include "Maxis16ISelDAGToDAG.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "Maxis.h" +#include "MaxisMachineFunction.h" +#include "MaxisRegisterInfo.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +#define DEBUG_TYPE "maxis-isel" + +bool Maxis16DAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { + Subtarget = &static_cast(MF.getSubtarget()); + if (!Subtarget->inMaxis16Mode()) + return false; + return MaxisDAGToDAGISel::runOnMachineFunction(MF); +} +/// Select multiply instructions. +std::pair +Maxis16DAGToDAGISel::selectMULT(SDNode *N, unsigned Opc, const SDLoc &DL, EVT Ty, + bool HasLo, bool HasHi) { + SDNode *Lo = nullptr, *Hi = nullptr; + SDNode *Mul = CurDAG->getMachineNode(Opc, DL, MVT::Glue, N->getOperand(0), + N->getOperand(1)); + SDValue InFlag = SDValue(Mul, 0); + + if (HasLo) { + unsigned Opcode = Maxis::Mflo16; + Lo = CurDAG->getMachineNode(Opcode, DL, Ty, MVT::Glue, InFlag); + InFlag = SDValue(Lo, 1); + } + if (HasHi) { + unsigned Opcode = Maxis::Mfhi16; + Hi = CurDAG->getMachineNode(Opcode, DL, Ty, InFlag); + } + return std::make_pair(Lo, Hi); +} + +void Maxis16DAGToDAGISel::initGlobalBaseReg(MachineFunction &MF) { + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + if (!MaxisFI->globalBaseRegSet()) + return; + + MachineBasicBlock &MBB = MF.front(); + MachineBasicBlock::iterator I = MBB.begin(); + MachineRegisterInfo &RegInfo = MF.getRegInfo(); + const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); + DebugLoc DL; + unsigned V0, V1, V2, GlobalBaseReg = MaxisFI->getGlobalBaseReg(); + const TargetRegisterClass *RC = &Maxis::CPU16RegsRegClass; + + V0 = RegInfo.createVirtualRegister(RC); + V1 = RegInfo.createVirtualRegister(RC); + V2 = RegInfo.createVirtualRegister(RC); + + + BuildMI(MBB, I, DL, TII.get(Maxis::LiRxImmX16), V0) + .addExternalSymbol("_gp_disp", MaxisII::MO_ABS_HI); + BuildMI(MBB, I, DL, TII.get(Maxis::AddiuRxPcImmX16), V1) + .addExternalSymbol("_gp_disp", MaxisII::MO_ABS_LO); + + BuildMI(MBB, I, DL, TII.get(Maxis::SllX16), V2).addReg(V0).addImm(16); + BuildMI(MBB, I, DL, TII.get(Maxis::AdduRxRyRz16), GlobalBaseReg) + .addReg(V1) + .addReg(V2); +} + +void Maxis16DAGToDAGISel::processFunctionAfterISel(MachineFunction &MF) { + initGlobalBaseReg(MF); +} + +bool Maxis16DAGToDAGISel::selectAddr(bool SPAllowed, SDValue Addr, SDValue &Base, + SDValue &Offset) { + SDLoc DL(Addr); + EVT ValTy = Addr.getValueType(); + + // if Address is FI, get the TargetFrameIndex. + if (SPAllowed) { + if (FrameIndexSDNode *FIN = dyn_cast(Addr)) { + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy); + Offset = CurDAG->getTargetConstant(0, DL, ValTy); + return true; + } + } + // on PIC code Load GA + if (Addr.getOpcode() == MaxisISD::Wrapper) { + Base = Addr.getOperand(0); + Offset = Addr.getOperand(1); + return true; + } + if (!TM.isPositionIndependent()) { + if ((Addr.getOpcode() == ISD::TargetExternalSymbol || + Addr.getOpcode() == ISD::TargetGlobalAddress)) + return false; + } + // Addresses of the form FI+const or FI|const + if (CurDAG->isBaseWithConstantOffset(Addr)) { + ConstantSDNode *CN = dyn_cast(Addr.getOperand(1)); + if (isInt<16>(CN->getSExtValue())) { + // If the first operand is a FI, get the TargetFI Node + if (SPAllowed) { + if (FrameIndexSDNode *FIN = + dyn_cast(Addr.getOperand(0))) { + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy); + Offset = CurDAG->getTargetConstant(CN->getZExtValue(), DL, ValTy); + return true; + } + } + + Base = Addr.getOperand(0); + Offset = CurDAG->getTargetConstant(CN->getZExtValue(), DL, ValTy); + return true; + } + } + // Operand is a result from an ADD. + if (Addr.getOpcode() == ISD::ADD) { + // When loading from constant pools, load the lower address part in + // the instruction itself. Example, instead of: + // lui $2, %hi($CPI1_0) + // addiu $2, $2, %lo($CPI1_0) + // lwc1 $f0, 0($2) + // Generate: + // lui $2, %hi($CPI1_0) + // lwc1 $f0, %lo($CPI1_0)($2) + if (Addr.getOperand(1).getOpcode() == MaxisISD::Lo || + Addr.getOperand(1).getOpcode() == MaxisISD::GPRel) { + SDValue Opnd0 = Addr.getOperand(1).getOperand(0); + if (isa(Opnd0) || isa(Opnd0) || + isa(Opnd0)) { + Base = Addr.getOperand(0); + Offset = Opnd0; + return true; + } + } + } + Base = Addr; + Offset = CurDAG->getTargetConstant(0, DL, ValTy); + return true; +} + +bool Maxis16DAGToDAGISel::selectAddr16(SDValue Addr, SDValue &Base, + SDValue &Offset) { + return selectAddr(false, Addr, Base, Offset); +} + +bool Maxis16DAGToDAGISel::selectAddr16SP(SDValue Addr, SDValue &Base, + SDValue &Offset) { + return selectAddr(true, Addr, Base, Offset); +} + +/// Select instructions not customized! Used for +/// expanded, promoted and normal instructions +bool Maxis16DAGToDAGISel::trySelect(SDNode *Node) { + unsigned Opcode = Node->getOpcode(); + SDLoc DL(Node); + + /// + // Instruction Selection not handled by the auto-generated + // tablegen selection should be handled here. + /// + EVT NodeTy = Node->getValueType(0); + unsigned MultOpc; + + switch (Opcode) { + default: + break; + + case ISD::SUBE: + case ISD::ADDE: { + SDValue InFlag = Node->getOperand(2), CmpLHS; + unsigned Opc = InFlag.getOpcode(); + (void)Opc; + assert(((Opc == ISD::ADDC || Opc == ISD::ADDE) || + (Opc == ISD::SUBC || Opc == ISD::SUBE)) && + "(ADD|SUB)E flag operand must come from (ADD|SUB)C/E insn"); + + unsigned MOp; + if (Opcode == ISD::ADDE) { + CmpLHS = InFlag.getValue(0); + MOp = Maxis::AdduRxRyRz16; + } else { + CmpLHS = InFlag.getOperand(0); + MOp = Maxis::SubuRxRyRz16; + } + + SDValue Ops[] = {CmpLHS, InFlag.getOperand(1)}; + + SDValue LHS = Node->getOperand(0); + SDValue RHS = Node->getOperand(1); + + EVT VT = LHS.getValueType(); + + unsigned Sltu_op = Maxis::SltuRxRyRz16; + SDNode *Carry = CurDAG->getMachineNode(Sltu_op, DL, VT, Ops); + unsigned Addu_op = Maxis::AdduRxRyRz16; + SDNode *AddCarry = + CurDAG->getMachineNode(Addu_op, DL, VT, SDValue(Carry, 0), RHS); + + CurDAG->SelectNodeTo(Node, MOp, VT, MVT::Glue, LHS, SDValue(AddCarry, 0)); + return true; + } + + /// Mul with two results + case ISD::SMUL_LOHI: + case ISD::UMUL_LOHI: { + MultOpc = (Opcode == ISD::UMUL_LOHI ? Maxis::MultuRxRy16 : Maxis::MultRxRy16); + std::pair LoHi = + selectMULT(Node, MultOpc, DL, NodeTy, true, true); + if (!SDValue(Node, 0).use_empty()) + ReplaceUses(SDValue(Node, 0), SDValue(LoHi.first, 0)); + + if (!SDValue(Node, 1).use_empty()) + ReplaceUses(SDValue(Node, 1), SDValue(LoHi.second, 0)); + + CurDAG->RemoveDeadNode(Node); + return true; + } + + case ISD::MULHS: + case ISD::MULHU: { + MultOpc = (Opcode == ISD::MULHU ? Maxis::MultuRxRy16 : Maxis::MultRxRy16); + auto LoHi = selectMULT(Node, MultOpc, DL, NodeTy, false, true); + ReplaceNode(Node, LoHi.second); + return true; + } + } + + return false; +} + +FunctionPass *llvm::createMaxis16ISelDag(MaxisTargetMachine &TM, + CodeGenOpt::Level OptLevel) { + return new Maxis16DAGToDAGISel(TM, OptLevel); +} diff --git a/lib/Target/Maxis/Maxis16ISelDAGToDAG.h b/lib/Target/Maxis/Maxis16ISelDAGToDAG.h new file mode 100644 index 00000000..2692bde1 --- /dev/null +++ b/lib/Target/Maxis/Maxis16ISelDAGToDAG.h @@ -0,0 +1,55 @@ +//===---- Maxis16ISelDAGToDAG.h - A Dag to Dag Inst Selector for Maxis ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Subclass of MaxisDAGToDAGISel specialized for maxis16. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXIS16ISELDAGTODAG_H +#define LLVM_LIB_TARGET_MAXIS_MAXIS16ISELDAGTODAG_H + +#include "MaxisISelDAGToDAG.h" + +namespace llvm { + +class Maxis16DAGToDAGISel : public MaxisDAGToDAGISel { +public: + explicit Maxis16DAGToDAGISel(MaxisTargetMachine &TM, CodeGenOpt::Level OL) + : MaxisDAGToDAGISel(TM, OL) {} + +private: + std::pair selectMULT(SDNode *N, unsigned Opc, + const SDLoc &DL, EVT Ty, bool HasLo, + bool HasHi); + + bool runOnMachineFunction(MachineFunction &MF) override; + + bool selectAddr(bool SPAllowed, SDValue Addr, SDValue &Base, + SDValue &Offset); + bool selectAddr16(SDValue Addr, SDValue &Base, + SDValue &Offset) override; + bool selectAddr16SP(SDValue Addr, SDValue &Base, + SDValue &Offset) override; + + bool trySelect(SDNode *Node) override; + + void processFunctionAfterISel(MachineFunction &MF) override; + + // Insert instructions to initialize the global base register in the + // first MBB of the function. + void initGlobalBaseReg(MachineFunction &MF); + + void initMaxis16SPAliasReg(MachineFunction &MF); +}; + +FunctionPass *createMaxis16ISelDag(MaxisTargetMachine &TM, + CodeGenOpt::Level OptLevel); +} + +#endif diff --git a/lib/Target/Maxis/Maxis16ISelLowering.cpp b/lib/Target/Maxis/Maxis16ISelLowering.cpp new file mode 100644 index 00000000..d5f0c07d --- /dev/null +++ b/lib/Target/Maxis/Maxis16ISelLowering.cpp @@ -0,0 +1,800 @@ +//===-- Maxis16ISelLowering.h - Maxis16 DAG Lowering Interface ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Subclass of MaxisTargetLowering specialized for maxis16. +// +//===----------------------------------------------------------------------===// +#include "Maxis16ISelLowering.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "Maxis16HardFloatInfo.h" +#include "MaxisMachineFunction.h" +#include "MaxisRegisterInfo.h" +#include "MaxisTargetMachine.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; + +#define DEBUG_TYPE "maxis-lower" + +static cl::opt DontExpandCondPseudos16( + "maxis16-dont-expand-cond-pseudo", + cl::init(false), + cl::desc("Don't expand conditional move related " + "pseudos for Maxis 16"), + cl::Hidden); + +namespace { +struct Maxis16Libcall { + RTLIB::Libcall Libcall; + const char *Name; + + bool operator<(const Maxis16Libcall &RHS) const { + return std::strcmp(Name, RHS.Name) < 0; + } +}; + +struct Maxis16IntrinsicHelperType{ + const char* Name; + const char* Helper; + + bool operator<(const Maxis16IntrinsicHelperType &RHS) const { + return std::strcmp(Name, RHS.Name) < 0; + } + bool operator==(const Maxis16IntrinsicHelperType &RHS) const { + return std::strcmp(Name, RHS.Name) == 0; + } +}; +} + +// Libcalls for which no helper is generated. Sorted by name for binary search. +static const Maxis16Libcall HardFloatLibCalls[] = { + { RTLIB::ADD_F64, "__maxis16_adddf3" }, + { RTLIB::ADD_F32, "__maxis16_addsf3" }, + { RTLIB::DIV_F64, "__maxis16_divdf3" }, + { RTLIB::DIV_F32, "__maxis16_divsf3" }, + { RTLIB::OEQ_F64, "__maxis16_eqdf2" }, + { RTLIB::OEQ_F32, "__maxis16_eqsf2" }, + { RTLIB::FPEXT_F32_F64, "__maxis16_extendsfdf2" }, + { RTLIB::FPTOSINT_F64_I32, "__maxis16_fix_truncdfsi" }, + { RTLIB::FPTOSINT_F32_I32, "__maxis16_fix_truncsfsi" }, + { RTLIB::SINTTOFP_I32_F64, "__maxis16_floatsidf" }, + { RTLIB::SINTTOFP_I32_F32, "__maxis16_floatsisf" }, + { RTLIB::UINTTOFP_I32_F64, "__maxis16_floatunsidf" }, + { RTLIB::UINTTOFP_I32_F32, "__maxis16_floatunsisf" }, + { RTLIB::OGE_F64, "__maxis16_gedf2" }, + { RTLIB::OGE_F32, "__maxis16_gesf2" }, + { RTLIB::OGT_F64, "__maxis16_gtdf2" }, + { RTLIB::OGT_F32, "__maxis16_gtsf2" }, + { RTLIB::OLE_F64, "__maxis16_ledf2" }, + { RTLIB::OLE_F32, "__maxis16_lesf2" }, + { RTLIB::OLT_F64, "__maxis16_ltdf2" }, + { RTLIB::OLT_F32, "__maxis16_ltsf2" }, + { RTLIB::MUL_F64, "__maxis16_muldf3" }, + { RTLIB::MUL_F32, "__maxis16_mulsf3" }, + { RTLIB::UNE_F64, "__maxis16_nedf2" }, + { RTLIB::UNE_F32, "__maxis16_nesf2" }, + { RTLIB::UNKNOWN_LIBCALL, "__maxis16_ret_dc" }, // No associated libcall. + { RTLIB::UNKNOWN_LIBCALL, "__maxis16_ret_df" }, // No associated libcall. + { RTLIB::UNKNOWN_LIBCALL, "__maxis16_ret_sc" }, // No associated libcall. + { RTLIB::UNKNOWN_LIBCALL, "__maxis16_ret_sf" }, // No associated libcall. + { RTLIB::SUB_F64, "__maxis16_subdf3" }, + { RTLIB::SUB_F32, "__maxis16_subsf3" }, + { RTLIB::FPROUND_F64_F32, "__maxis16_truncdfsf2" }, + { RTLIB::UO_F64, "__maxis16_unorddf2" }, + { RTLIB::UO_F32, "__maxis16_unordsf2" } +}; + +static const Maxis16IntrinsicHelperType Maxis16IntrinsicHelper[] = { + {"__fixunsdfsi", "__maxis16_call_stub_2" }, + {"ceil", "__maxis16_call_stub_df_2"}, + {"ceilf", "__maxis16_call_stub_sf_1"}, + {"copysign", "__maxis16_call_stub_df_10"}, + {"copysignf", "__maxis16_call_stub_sf_5"}, + {"cos", "__maxis16_call_stub_df_2"}, + {"cosf", "__maxis16_call_stub_sf_1"}, + {"exp2", "__maxis16_call_stub_df_2"}, + {"exp2f", "__maxis16_call_stub_sf_1"}, + {"floor", "__maxis16_call_stub_df_2"}, + {"floorf", "__maxis16_call_stub_sf_1"}, + {"log2", "__maxis16_call_stub_df_2"}, + {"log2f", "__maxis16_call_stub_sf_1"}, + {"nearbyint", "__maxis16_call_stub_df_2"}, + {"nearbyintf", "__maxis16_call_stub_sf_1"}, + {"rint", "__maxis16_call_stub_df_2"}, + {"rintf", "__maxis16_call_stub_sf_1"}, + {"sin", "__maxis16_call_stub_df_2"}, + {"sinf", "__maxis16_call_stub_sf_1"}, + {"sqrt", "__maxis16_call_stub_df_2"}, + {"sqrtf", "__maxis16_call_stub_sf_1"}, + {"trunc", "__maxis16_call_stub_df_2"}, + {"truncf", "__maxis16_call_stub_sf_1"}, +}; + +Maxis16TargetLowering::Maxis16TargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI) + : MaxisTargetLowering(TM, STI) { + + // Set up the register classes + addRegisterClass(MVT::i32, &Maxis::CPU16RegsRegClass); + + if (!Subtarget.useSoftFloat()) + setMaxis16HardFloatLibCalls(); + + setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Expand); + setOperationAction(ISD::ATOMIC_CMP_SWAP, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_SWAP, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_ADD, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_SUB, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_AND, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_OR, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_XOR, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_NAND, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_MIN, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_MAX, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_UMIN, MVT::i32, Expand); + setOperationAction(ISD::ATOMIC_LOAD_UMAX, MVT::i32, Expand); + + setOperationAction(ISD::ROTR, MVT::i32, Expand); + setOperationAction(ISD::ROTR, MVT::i64, Expand); + setOperationAction(ISD::BSWAP, MVT::i32, Expand); + setOperationAction(ISD::BSWAP, MVT::i64, Expand); + + computeRegisterProperties(STI.getRegisterInfo()); +} + +const MaxisTargetLowering * +llvm::createMaxis16TargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI) { + return new Maxis16TargetLowering(TM, STI); +} + +bool +Maxis16TargetLowering::allowsMisalignedMemoryAccesses(EVT VT, + unsigned, + unsigned, + bool *Fast) const { + return false; +} + +MachineBasicBlock * +Maxis16TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + switch (MI.getOpcode()) { + default: + return MaxisTargetLowering::EmitInstrWithCustomInserter(MI, BB); + case Maxis::SelBeqZ: + return emitSel16(Maxis::BeqzRxImm16, MI, BB); + case Maxis::SelBneZ: + return emitSel16(Maxis::BnezRxImm16, MI, BB); + case Maxis::SelTBteqZCmpi: + return emitSeliT16(Maxis::Bteqz16, Maxis::CmpiRxImmX16, MI, BB); + case Maxis::SelTBteqZSlti: + return emitSeliT16(Maxis::Bteqz16, Maxis::SltiRxImmX16, MI, BB); + case Maxis::SelTBteqZSltiu: + return emitSeliT16(Maxis::Bteqz16, Maxis::SltiuRxImmX16, MI, BB); + case Maxis::SelTBtneZCmpi: + return emitSeliT16(Maxis::Btnez16, Maxis::CmpiRxImmX16, MI, BB); + case Maxis::SelTBtneZSlti: + return emitSeliT16(Maxis::Btnez16, Maxis::SltiRxImmX16, MI, BB); + case Maxis::SelTBtneZSltiu: + return emitSeliT16(Maxis::Btnez16, Maxis::SltiuRxImmX16, MI, BB); + case Maxis::SelTBteqZCmp: + return emitSelT16(Maxis::Bteqz16, Maxis::CmpRxRy16, MI, BB); + case Maxis::SelTBteqZSlt: + return emitSelT16(Maxis::Bteqz16, Maxis::SltRxRy16, MI, BB); + case Maxis::SelTBteqZSltu: + return emitSelT16(Maxis::Bteqz16, Maxis::SltuRxRy16, MI, BB); + case Maxis::SelTBtneZCmp: + return emitSelT16(Maxis::Btnez16, Maxis::CmpRxRy16, MI, BB); + case Maxis::SelTBtneZSlt: + return emitSelT16(Maxis::Btnez16, Maxis::SltRxRy16, MI, BB); + case Maxis::SelTBtneZSltu: + return emitSelT16(Maxis::Btnez16, Maxis::SltuRxRy16, MI, BB); + case Maxis::BteqzT8CmpX16: + return emitFEXT_T8I816_ins(Maxis::Bteqz16, Maxis::CmpRxRy16, MI, BB); + case Maxis::BteqzT8SltX16: + return emitFEXT_T8I816_ins(Maxis::Bteqz16, Maxis::SltRxRy16, MI, BB); + case Maxis::BteqzT8SltuX16: + // TBD: figure out a way to get this or remove the instruction + // altogether. + return emitFEXT_T8I816_ins(Maxis::Bteqz16, Maxis::SltuRxRy16, MI, BB); + case Maxis::BtnezT8CmpX16: + return emitFEXT_T8I816_ins(Maxis::Btnez16, Maxis::CmpRxRy16, MI, BB); + case Maxis::BtnezT8SltX16: + return emitFEXT_T8I816_ins(Maxis::Btnez16, Maxis::SltRxRy16, MI, BB); + case Maxis::BtnezT8SltuX16: + // TBD: figure out a way to get this or remove the instruction + // altogether. + return emitFEXT_T8I816_ins(Maxis::Btnez16, Maxis::SltuRxRy16, MI, BB); + case Maxis::BteqzT8CmpiX16: return emitFEXT_T8I8I16_ins( + Maxis::Bteqz16, Maxis::CmpiRxImm16, Maxis::CmpiRxImmX16, false, MI, BB); + case Maxis::BteqzT8SltiX16: return emitFEXT_T8I8I16_ins( + Maxis::Bteqz16, Maxis::SltiRxImm16, Maxis::SltiRxImmX16, true, MI, BB); + case Maxis::BteqzT8SltiuX16: return emitFEXT_T8I8I16_ins( + Maxis::Bteqz16, Maxis::SltiuRxImm16, Maxis::SltiuRxImmX16, false, MI, BB); + case Maxis::BtnezT8CmpiX16: return emitFEXT_T8I8I16_ins( + Maxis::Btnez16, Maxis::CmpiRxImm16, Maxis::CmpiRxImmX16, false, MI, BB); + case Maxis::BtnezT8SltiX16: return emitFEXT_T8I8I16_ins( + Maxis::Btnez16, Maxis::SltiRxImm16, Maxis::SltiRxImmX16, true, MI, BB); + case Maxis::BtnezT8SltiuX16: return emitFEXT_T8I8I16_ins( + Maxis::Btnez16, Maxis::SltiuRxImm16, Maxis::SltiuRxImmX16, false, MI, BB); + break; + case Maxis::SltCCRxRy16: + return emitFEXT_CCRX16_ins(Maxis::SltRxRy16, MI, BB); + break; + case Maxis::SltiCCRxImmX16: + return emitFEXT_CCRXI16_ins + (Maxis::SltiRxImm16, Maxis::SltiRxImmX16, MI, BB); + case Maxis::SltiuCCRxImmX16: + return emitFEXT_CCRXI16_ins + (Maxis::SltiuRxImm16, Maxis::SltiuRxImmX16, MI, BB); + case Maxis::SltuCCRxRy16: + return emitFEXT_CCRX16_ins + (Maxis::SltuRxRy16, MI, BB); + } +} + +bool Maxis16TargetLowering::isEligibleForTailCallOptimization( + const CCState &CCInfo, unsigned NextStackOffset, + const MaxisFunctionInfo &FI) const { + // No tail call optimization for maxis16. + return false; +} + +void Maxis16TargetLowering::setMaxis16HardFloatLibCalls() { + for (unsigned I = 0; I != array_lengthof(HardFloatLibCalls); ++I) { + assert((I == 0 || HardFloatLibCalls[I - 1] < HardFloatLibCalls[I]) && + "Array not sorted!"); + if (HardFloatLibCalls[I].Libcall != RTLIB::UNKNOWN_LIBCALL) + setLibcallName(HardFloatLibCalls[I].Libcall, HardFloatLibCalls[I].Name); + } + + setLibcallName(RTLIB::O_F64, "__maxis16_unorddf2"); + setLibcallName(RTLIB::O_F32, "__maxis16_unordsf2"); +} + +// +// The Maxis16 hard float is a crazy quilt inherited from gcc. I have a much +// cleaner way to do all of this but it will have to wait until the traditional +// gcc mechanism is completed. +// +// For Pic, in order for Maxis16 code to call Maxis32 code which according the abi +// have either arguments or returned values placed in floating point registers, +// we use a set of helper functions. (This includes functions which return type +// complex which on Maxis are returned in a pair of floating point registers). +// +// This is an encoding that we inherited from gcc. +// In Maxis traditional O32, N32 ABI, floating point numbers are passed in +// floating point argument registers 1,2 only when the first and optionally +// the second arguments are float (sf) or double (df). +// For Maxis16 we are only concerned with the situations where floating point +// arguments are being passed in floating point registers by the ABI, because +// Maxis16 mode code cannot execute floating point instructions to load those +// values and hence helper functions are needed. +// The possibilities are (), (sf), (sf, sf), (sf, df), (df), (df, sf), (df, df) +// the helper function suffixs for these are: +// 0, 1, 5, 9, 2, 6, 10 +// this suffix can then be calculated as follows: +// for a given argument Arg: +// Arg1x, Arg2x = 1 : Arg is sf +// 2 : Arg is df +// 0: Arg is neither sf or df +// So this stub is the string for number Arg1x + Arg2x*4. +// However not all numbers between 0 and 10 are possible, we check anyway and +// assert if the impossible exists. +// + +unsigned int Maxis16TargetLowering::getMaxis16HelperFunctionStubNumber + (ArgListTy &Args) const { + unsigned int resultNum = 0; + if (Args.size() >= 1) { + Type *t = Args[0].Ty; + if (t->isFloatTy()) { + resultNum = 1; + } + else if (t->isDoubleTy()) { + resultNum = 2; + } + } + if (resultNum) { + if (Args.size() >=2) { + Type *t = Args[1].Ty; + if (t->isFloatTy()) { + resultNum += 4; + } + else if (t->isDoubleTy()) { + resultNum += 8; + } + } + } + return resultNum; +} + +// +// Prefixes are attached to stub numbers depending on the return type. +// return type: float sf_ +// double df_ +// single complex sc_ +// double complext dc_ +// others NO PREFIX +// +// +// The full name of a helper function is__maxis16_call_stub + +// return type dependent prefix + stub number +// +// FIXME: This is something that probably should be in a different source file +// and perhaps done differently but my main purpose is to not waste runtime +// on something that we can enumerate in the source. Another possibility is +// to have a python script to generate these mapping tables. This will do +// for now. There are a whole series of helper function mapping arrays, one +// for each return type class as outlined above. There there are 11 possible +// entries. Ones with 0 are ones which should never be selected. +// +// All the arrays are similar except for ones which return neither +// sf, df, sc, dc, in which we only care about ones which have sf or df as a +// first parameter. +// +#define P_ "__maxis16_call_stub_" +#define MAX_STUB_NUMBER 10 +#define T1 P "1", P "2", 0, 0, P "5", P "6", 0, 0, P "9", P "10" +#define T P "0" , T1 +#define P P_ +static char const * vMaxis16Helper[MAX_STUB_NUMBER+1] = + {nullptr, T1 }; +#undef P +#define P P_ "sf_" +static char const * sfMaxis16Helper[MAX_STUB_NUMBER+1] = + { T }; +#undef P +#define P P_ "df_" +static char const * dfMaxis16Helper[MAX_STUB_NUMBER+1] = + { T }; +#undef P +#define P P_ "sc_" +static char const * scMaxis16Helper[MAX_STUB_NUMBER+1] = + { T }; +#undef P +#define P P_ "dc_" +static char const * dcMaxis16Helper[MAX_STUB_NUMBER+1] = + { T }; +#undef P +#undef P_ + + +const char* Maxis16TargetLowering:: + getMaxis16HelperFunction + (Type* RetTy, ArgListTy &Args, bool &needHelper) const { + const unsigned int stubNum = getMaxis16HelperFunctionStubNumber(Args); +#ifndef NDEBUG + const unsigned int maxStubNum = 10; + assert(stubNum <= maxStubNum); + const bool validStubNum[maxStubNum+1] = + {true, true, true, false, false, true, true, false, false, true, true}; + assert(validStubNum[stubNum]); +#endif + const char *result; + if (RetTy->isFloatTy()) { + result = sfMaxis16Helper[stubNum]; + } + else if (RetTy ->isDoubleTy()) { + result = dfMaxis16Helper[stubNum]; + } + else if (RetTy->isStructTy()) { + // check if it's complex + if (RetTy->getNumContainedTypes() == 2) { + if ((RetTy->getContainedType(0)->isFloatTy()) && + (RetTy->getContainedType(1)->isFloatTy())) { + result = scMaxis16Helper[stubNum]; + } + else if ((RetTy->getContainedType(0)->isDoubleTy()) && + (RetTy->getContainedType(1)->isDoubleTy())) { + result = dcMaxis16Helper[stubNum]; + } + else { + llvm_unreachable("Uncovered condition"); + } + } + else { + llvm_unreachable("Uncovered condition"); + } + } + else { + if (stubNum == 0) { + needHelper = false; + return ""; + } + result = vMaxis16Helper[stubNum]; + } + needHelper = true; + return result; +} + +void Maxis16TargetLowering:: +getOpndList(SmallVectorImpl &Ops, + std::deque< std::pair > &RegsToPass, + bool IsPICCall, bool GlobalOrExternal, bool InternalLinkage, + bool IsCallReloc, CallLoweringInfo &CLI, SDValue Callee, + SDValue Chain) const { + SelectionDAG &DAG = CLI.DAG; + MachineFunction &MF = DAG.getMachineFunction(); + MaxisFunctionInfo *FuncInfo = MF.getInfo(); + const char* Maxis16HelperFunction = nullptr; + bool NeedMaxis16Helper = false; + + if (Subtarget.inMaxis16HardFloat()) { + // + // currently we don't have symbols tagged with the maxis16 or maxis32 + // qualifier so we will assume that we don't know what kind it is. + // and generate the helper + // + bool LookupHelper = true; + if (ExternalSymbolSDNode *S = dyn_cast(CLI.Callee)) { + Maxis16Libcall Find = { RTLIB::UNKNOWN_LIBCALL, S->getSymbol() }; + + if (std::binary_search(std::begin(HardFloatLibCalls), + std::end(HardFloatLibCalls), Find)) + LookupHelper = false; + else { + const char *Symbol = S->getSymbol(); + Maxis16IntrinsicHelperType IntrinsicFind = { Symbol, "" }; + const Maxis16HardFloatInfo::FuncSignature *Signature = + Maxis16HardFloatInfo::findFuncSignature(Symbol); + if (!IsPICCall && (Signature && (FuncInfo->StubsNeeded.find(Symbol) == + FuncInfo->StubsNeeded.end()))) { + FuncInfo->StubsNeeded[Symbol] = Signature; + // + // S2 is normally saved if the stub is for a function which + // returns a float or double value and is not otherwise. This is + // because more work is required after the function the stub + // is calling completes, and so the stub cannot directly return + // and the stub has no stack space to store the return address so + // S2 is used for that purpose. + // In order to take advantage of not saving S2, we need to also + // optimize the call in the stub and this requires some further + // functionality in MaxisAsmPrinter which we don't have yet. + // So for now we always save S2. The optimization will be done + // in a follow-on patch. + // + if (1 || (Signature->RetSig != Maxis16HardFloatInfo::NoFPRet)) + FuncInfo->setSaveS2(); + } + // one more look at list of intrinsics + const Maxis16IntrinsicHelperType *Helper = + std::lower_bound(std::begin(Maxis16IntrinsicHelper), + std::end(Maxis16IntrinsicHelper), IntrinsicFind); + if (Helper != std::end(Maxis16IntrinsicHelper) && + *Helper == IntrinsicFind) { + Maxis16HelperFunction = Helper->Helper; + NeedMaxis16Helper = true; + LookupHelper = false; + } + + } + } else if (GlobalAddressSDNode *G = + dyn_cast(CLI.Callee)) { + Maxis16Libcall Find = { RTLIB::UNKNOWN_LIBCALL, + G->getGlobal()->getName().data() }; + + if (std::binary_search(std::begin(HardFloatLibCalls), + std::end(HardFloatLibCalls), Find)) + LookupHelper = false; + } + if (LookupHelper) + Maxis16HelperFunction = + getMaxis16HelperFunction(CLI.RetTy, CLI.getArgs(), NeedMaxis16Helper); + } + + SDValue JumpTarget = Callee; + + // T9 should contain the address of the callee function if + // -relocation-model=pic or it is an indirect call. + if (IsPICCall || !GlobalOrExternal) { + unsigned V0Reg = Maxis::V0; + if (NeedMaxis16Helper) { + RegsToPass.push_front(std::make_pair(V0Reg, Callee)); + JumpTarget = DAG.getExternalSymbol(Maxis16HelperFunction, + getPointerTy(DAG.getDataLayout())); + ExternalSymbolSDNode *S = cast(JumpTarget); + JumpTarget = getAddrGlobal(S, CLI.DL, JumpTarget.getValueType(), DAG, + MaxisII::MO_GOT, Chain, + FuncInfo->callPtrInfo(S->getSymbol())); + } else + RegsToPass.push_front(std::make_pair((unsigned)Maxis::T9, Callee)); + } + + Ops.push_back(JumpTarget); + + MaxisTargetLowering::getOpndList(Ops, RegsToPass, IsPICCall, GlobalOrExternal, + InternalLinkage, IsCallReloc, CLI, Callee, + Chain); +} + +MachineBasicBlock * +Maxis16TargetLowering::emitSel16(unsigned Opc, MachineInstr &MI, + MachineBasicBlock *BB) const { + if (DontExpandCondPseudos16) + return BB; + 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 = ... + // setcc r1, r2, r3 + // bNE r1, r0, copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, copy0MBB); + F->insert(It, sinkMBB); + + // Transfer the remainder of BB and its successor edges to sinkMBB. + sinkMBB->splice(sinkMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + sinkMBB->transferSuccessorsAndUpdatePHIs(BB); + + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(copy0MBB); + BB->addSuccessor(sinkMBB); + + BuildMI(BB, DL, TII->get(Opc)) + .addReg(MI.getOperand(3).getReg()) + .addMBB(sinkMBB); + + // copy0MBB: + // %FalseValue = ... + // # fallthrough to sinkMBB + BB = copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(sinkMBB); + + // sinkMBB: + // %Result = phi [ %TrueValue, thisMBB ], [ %FalseValue, copy0MBB ] + // ... + BB = sinkMBB; + + BuildMI(*BB, BB->begin(), DL, TII->get(Maxis::PHI), MI.getOperand(0).getReg()) + .addReg(MI.getOperand(1).getReg()) + .addMBB(thisMBB) + .addReg(MI.getOperand(2).getReg()) + .addMBB(copy0MBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +MachineBasicBlock * +Maxis16TargetLowering::emitSelT16(unsigned Opc1, unsigned Opc2, MachineInstr &MI, + MachineBasicBlock *BB) const { + if (DontExpandCondPseudos16) + return BB; + 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 = ... + // setcc r1, r2, r3 + // bNE r1, r0, copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, copy0MBB); + F->insert(It, sinkMBB); + + // Transfer the remainder of BB and its successor edges to sinkMBB. + sinkMBB->splice(sinkMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + sinkMBB->transferSuccessorsAndUpdatePHIs(BB); + + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(copy0MBB); + BB->addSuccessor(sinkMBB); + + BuildMI(BB, DL, TII->get(Opc2)) + .addReg(MI.getOperand(3).getReg()) + .addReg(MI.getOperand(4).getReg()); + BuildMI(BB, DL, TII->get(Opc1)).addMBB(sinkMBB); + + // copy0MBB: + // %FalseValue = ... + // # fallthrough to sinkMBB + BB = copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(sinkMBB); + + // sinkMBB: + // %Result = phi [ %TrueValue, thisMBB ], [ %FalseValue, copy0MBB ] + // ... + BB = sinkMBB; + + BuildMI(*BB, BB->begin(), DL, TII->get(Maxis::PHI), MI.getOperand(0).getReg()) + .addReg(MI.getOperand(1).getReg()) + .addMBB(thisMBB) + .addReg(MI.getOperand(2).getReg()) + .addMBB(copy0MBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; + +} + +MachineBasicBlock * +Maxis16TargetLowering::emitSeliT16(unsigned Opc1, unsigned Opc2, + MachineInstr &MI, + MachineBasicBlock *BB) const { + if (DontExpandCondPseudos16) + return BB; + 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 = ... + // setcc r1, r2, r3 + // bNE r1, r0, copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, copy0MBB); + F->insert(It, sinkMBB); + + // Transfer the remainder of BB and its successor edges to sinkMBB. + sinkMBB->splice(sinkMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + sinkMBB->transferSuccessorsAndUpdatePHIs(BB); + + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(copy0MBB); + BB->addSuccessor(sinkMBB); + + BuildMI(BB, DL, TII->get(Opc2)) + .addReg(MI.getOperand(3).getReg()) + .addImm(MI.getOperand(4).getImm()); + BuildMI(BB, DL, TII->get(Opc1)).addMBB(sinkMBB); + + // copy0MBB: + // %FalseValue = ... + // # fallthrough to sinkMBB + BB = copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(sinkMBB); + + // sinkMBB: + // %Result = phi [ %TrueValue, thisMBB ], [ %FalseValue, copy0MBB ] + // ... + BB = sinkMBB; + + BuildMI(*BB, BB->begin(), DL, TII->get(Maxis::PHI), MI.getOperand(0).getReg()) + .addReg(MI.getOperand(1).getReg()) + .addMBB(thisMBB) + .addReg(MI.getOperand(2).getReg()) + .addMBB(copy0MBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; + +} + +MachineBasicBlock * +Maxis16TargetLowering::emitFEXT_T8I816_ins(unsigned BtOpc, unsigned CmpOpc, + MachineInstr &MI, + MachineBasicBlock *BB) const { + if (DontExpandCondPseudos16) + return BB; + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + unsigned regX = MI.getOperand(0).getReg(); + unsigned regY = MI.getOperand(1).getReg(); + MachineBasicBlock *target = MI.getOperand(2).getMBB(); + BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(CmpOpc)) + .addReg(regX) + .addReg(regY); + BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(BtOpc)).addMBB(target); + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +MachineBasicBlock *Maxis16TargetLowering::emitFEXT_T8I8I16_ins( + unsigned BtOpc, unsigned CmpiOpc, unsigned CmpiXOpc, bool ImmSigned, + MachineInstr &MI, MachineBasicBlock *BB) const { + if (DontExpandCondPseudos16) + return BB; + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + unsigned regX = MI.getOperand(0).getReg(); + int64_t imm = MI.getOperand(1).getImm(); + MachineBasicBlock *target = MI.getOperand(2).getMBB(); + unsigned CmpOpc; + if (isUInt<8>(imm)) + CmpOpc = CmpiOpc; + else if ((!ImmSigned && isUInt<16>(imm)) || + (ImmSigned && isInt<16>(imm))) + CmpOpc = CmpiXOpc; + else + llvm_unreachable("immediate field not usable"); + BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(CmpOpc)).addReg(regX).addImm(imm); + BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(BtOpc)).addMBB(target); + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +static unsigned Maxis16WhichOp8uOr16simm + (unsigned shortOp, unsigned longOp, int64_t Imm) { + if (isUInt<8>(Imm)) + return shortOp; + else if (isInt<16>(Imm)) + return longOp; + else + llvm_unreachable("immediate field not usable"); +} + +MachineBasicBlock * +Maxis16TargetLowering::emitFEXT_CCRX16_ins(unsigned SltOpc, MachineInstr &MI, + MachineBasicBlock *BB) const { + if (DontExpandCondPseudos16) + return BB; + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + unsigned CC = MI.getOperand(0).getReg(); + unsigned regX = MI.getOperand(1).getReg(); + unsigned regY = MI.getOperand(2).getReg(); + BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(SltOpc)) + .addReg(regX) + .addReg(regY); + BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(Maxis::MoveR3216), CC) + .addReg(Maxis::T8); + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +MachineBasicBlock * +Maxis16TargetLowering::emitFEXT_CCRXI16_ins(unsigned SltiOpc, unsigned SltiXOpc, + MachineInstr &MI, + MachineBasicBlock *BB) const { + if (DontExpandCondPseudos16) + return BB; + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + unsigned CC = MI.getOperand(0).getReg(); + unsigned regX = MI.getOperand(1).getReg(); + int64_t Imm = MI.getOperand(2).getImm(); + unsigned SltOpc = Maxis16WhichOp8uOr16simm(SltiOpc, SltiXOpc, Imm); + BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(SltOpc)).addReg(regX).addImm(Imm); + BuildMI(*BB, MI, MI.getDebugLoc(), TII->get(Maxis::MoveR3216), CC) + .addReg(Maxis::T8); + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; + +} diff --git a/lib/Target/Maxis/Maxis16ISelLowering.h b/lib/Target/Maxis/Maxis16ISelLowering.h new file mode 100644 index 00000000..ae0f32aa --- /dev/null +++ b/lib/Target/Maxis/Maxis16ISelLowering.h @@ -0,0 +1,82 @@ +//===-- Maxis16ISelLowering.h - Maxis16 DAG Lowering Interface ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Subclass of MaxisTargetLowering specialized for maxis16. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXIS16ISELLOWERING_H +#define LLVM_LIB_TARGET_MAXIS_MAXIS16ISELLOWERING_H + +#include "MaxisISelLowering.h" + +namespace llvm { + class Maxis16TargetLowering : public MaxisTargetLowering { + public: + explicit Maxis16TargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI); + + bool allowsMisalignedMemoryAccesses(EVT VT, unsigned AddrSpace, + unsigned Align, + bool *Fast) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *MBB) const override; + + private: + bool isEligibleForTailCallOptimization( + const CCState &CCInfo, unsigned NextStackOffset, + const MaxisFunctionInfo &FI) const override; + + void setMaxis16HardFloatLibCalls(); + + unsigned int + getMaxis16HelperFunctionStubNumber(ArgListTy &Args) const; + + const char *getMaxis16HelperFunction + (Type* RetTy, ArgListTy &Args, bool &needHelper) const; + + void + getOpndList(SmallVectorImpl &Ops, + std::deque< std::pair > &RegsToPass, + bool IsPICCall, bool GlobalOrExternal, bool InternalLinkage, + bool IsCallReloc, CallLoweringInfo &CLI, SDValue Callee, + SDValue Chain) const override; + + MachineBasicBlock *emitSel16(unsigned Opc, MachineInstr &MI, + MachineBasicBlock *BB) const; + + MachineBasicBlock *emitSeliT16(unsigned Opc1, unsigned Opc2, + MachineInstr &MI, + MachineBasicBlock *BB) const; + + MachineBasicBlock *emitSelT16(unsigned Opc1, unsigned Opc2, + MachineInstr &MI, + MachineBasicBlock *BB) const; + + MachineBasicBlock *emitFEXT_T8I816_ins(unsigned BtOpc, unsigned CmpOpc, + MachineInstr &MI, + MachineBasicBlock *BB) const; + + MachineBasicBlock *emitFEXT_T8I8I16_ins(unsigned BtOpc, unsigned CmpiOpc, + unsigned CmpiXOpc, bool ImmSigned, + MachineInstr &MI, + MachineBasicBlock *BB) const; + + MachineBasicBlock *emitFEXT_CCRX16_ins(unsigned SltOpc, MachineInstr &MI, + MachineBasicBlock *BB) const; + + MachineBasicBlock *emitFEXT_CCRXI16_ins(unsigned SltiOpc, unsigned SltiXOpc, + MachineInstr &MI, + MachineBasicBlock *BB) const; + }; +} + +#endif diff --git a/lib/Target/Maxis/Maxis16InstrFormats.td b/lib/Target/Maxis/Maxis16InstrFormats.td new file mode 100644 index 00000000..1a6cfb08 --- /dev/null +++ b/lib/Target/Maxis/Maxis16InstrFormats.td @@ -0,0 +1,640 @@ +//===- Maxis16InstrFormats.td - Maxis Instruction Formats ----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Describe MAXIS instructions format +// +// CPU INSTRUCTION FORMATS +// +// funct or f Function field +// +// immediate 4-,5-,8- or 11-bit immediate, branch displacement, or +// or imm address displacement +// +// op 5-bit major operation code +// +// rx 3-bit source or destination register +// +// ry 3-bit source or destination register +// +// rz 3-bit source or destination register +// +// sa 3- or 5-bit shift amount +// +//===----------------------------------------------------------------------===// + + +// Base class for Maxis 16 Format +// This class does not depend on the instruction size +// +class MaxisInst16_Base pattern, + InstrItinClass itin>: Instruction +{ + + let Namespace = "Maxis"; + + let OutOperandList = outs; + let InOperandList = ins; + + let AsmString = asmstr; + let Pattern = pattern; + let Itinerary = itin; + + let Predicates = [InMaxis16Mode]; +} + +// +// Generic Maxis 16 Format +// +class MaxisInst16 pattern, + InstrItinClass itin>: + MaxisInst16_Base +{ + field bits<16> Inst; + bits<5> Opcode = 0; + + // Top 5 bits are the 'opcode' field + let Inst{15-11} = Opcode; + + let Size=2; + field bits<16> SoftFail = 0; +} + +// +// For 32 bit extended instruction forms. +// +class MaxisInst16_32 pattern, + InstrItinClass itin>: + MaxisInst16_Base +{ + field bits<32> Inst; + + let Size=4; + field bits<32> SoftFail = 0; +} + +class MaxisInst16_EXTEND pattern, + InstrItinClass itin>: + MaxisInst16_32 +{ + let Inst{31-27} = 0b11110; +} + + + +// Maxis Pseudo Instructions Format +class MaxisPseudo16 pattern>: + MaxisInst16 { + let isCodeGenOnly = 1; + let isPseudo = 1; +} + + +//===----------------------------------------------------------------------===// +// Format I instruction class in Maxis : <|opcode|imm11|> +//===----------------------------------------------------------------------===// + +class FI16 op, dag outs, dag ins, string asmstr, list pattern, + InstrItinClass itin>: + MaxisInst16 +{ + bits<11> imm11; + + let Opcode = op; + + let Inst{10-0} = imm11; +} + +//===----------------------------------------------------------------------===// +// Format RI instruction class in Maxis : <|opcode|rx|imm8|> +//===----------------------------------------------------------------------===// + +class FRI16 op, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> rx; + bits<8> imm8; + + let Opcode = op; + + let Inst{10-8} = rx; + let Inst{7-0} = imm8; +} + +//===----------------------------------------------------------------------===// +// Format RR instruction class in Maxis : <|opcode|rx|ry|funct|> +//===----------------------------------------------------------------------===// + +class FRR16 _funct, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> rx; + bits<3> ry; + bits<5> funct; + + let Opcode = 0b11101; + let funct = _funct; + + let Inst{10-8} = rx; + let Inst{7-5} = ry; + let Inst{4-0} = funct; +} + +class FRRBreak16 pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<6> Code; + bits<5> funct; + + let Opcode = 0b11101; + let funct = 0b00101; + + let Inst{10-5} = Code; + let Inst{4-0} = funct; +} + +// +// For conversion functions. +// +class FRR_SF16 _funct, bits<3> _subfunct, dag outs, dag ins, + string asmstr, list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> rx; + bits<3> subfunct; + bits<5> funct; + + let Opcode = 0b11101; // RR + let funct = _funct; + let subfunct = _subfunct; + + let Inst{10-8} = rx; + let Inst{7-5} = subfunct; + let Inst{4-0} = funct; +} + +// +// just used for breakpoint (hardware and software) instructions. +// +class FC16 _funct, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<6> _code; // code is a keyword in tablegen + bits<5> funct; + + let Opcode = 0b11101; // RR + let funct = _funct; + + let Inst{10-5} = _code; + let Inst{4-0} = funct; +} + +// +// J(AL)R(C) subformat +// +class FRR16_JALRC _nd, bits<1> _l, bits<1> r_a, + dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> rx; + bits<1> nd; + bits<1> l; + bits<1> ra; + + let nd = _nd; + let l = _l; + let ra = r_a; + + let Opcode = 0b11101; + + let Inst{10-8} = rx; + let Inst{7} = nd; + let Inst{6} = l; + let Inst{5} = ra; + let Inst{4-0} = 0; +} + +//===----------------------------------------------------------------------===// +// Format RRI instruction class in Maxis : <|opcode|rx|ry|imm5|> +//===----------------------------------------------------------------------===// + +class FRRI16 op, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> rx; + bits<3> ry; + bits<5> imm5; + + let Opcode = op; + + + let Inst{10-8} = rx; + let Inst{7-5} = ry; + let Inst{4-0} = imm5; +} + +//===----------------------------------------------------------------------===// +// Format RRR instruction class in Maxis : <|opcode|rx|ry|rz|f|> +//===----------------------------------------------------------------------===// + +class FRRR16 _f, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> rx; + bits<3> ry; + bits<3> rz; + bits<2> f; + + let Opcode = 0b11100; + let f = _f; + + let Inst{10-8} = rx; + let Inst{7-5} = ry; + let Inst{4-2} = rz; + let Inst{1-0} = f; +} + +//===----------------------------------------------------------------------===// +// Format RRI-A instruction class in Maxis : <|opcode|rx|ry|f|imm4|> +//===----------------------------------------------------------------------===// + +class FRRI_A16 _f, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> rx; + bits<3> ry; + bits<1> f; + bits<4> imm4; + + let Opcode = 0b01000; + let f = _f; + + let Inst{10-8} = rx; + let Inst{7-5} = ry; + let Inst{4} = f; + let Inst{3-0} = imm4; +} + +//===----------------------------------------------------------------------===// +// Format Shift instruction class in Maxis : <|opcode|rx|ry|sa|f|> +//===----------------------------------------------------------------------===// + +class FSHIFT16 _f, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> rx; + bits<3> ry; + bits<3> sa; + bits<2> f; + + let Opcode = 0b00110; + let f = _f; + + let Inst{10-8} = rx; + let Inst{7-5} = ry; + let Inst{4-2} = sa; + let Inst{1-0} = f; +} + +//===----------------------------------------------------------------------===// +// Format i8 instruction class in Maxis : <|opcode|funct|imm8> +//===----------------------------------------------------------------------===// + +class FI816 _func, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<3> func; + bits<8> imm8; + + let Opcode = 0b01100; + let func = _func; + + let Inst{10-8} = func; + let Inst{7-0} = imm8; +} + +//===----------------------------------------------------------------------===// +// Format i8_MOVR32 instruction class in Maxis : <|opcode|func|ry|r32> +//===----------------------------------------------------------------------===// + +class FI8_MOVR3216 pattern, InstrItinClass itin>: + MaxisInst16 +{ + + bits<4> ry; + bits<4> r32; + + let Opcode = 0b01100; + + let Inst{10-8} = 0b111; + let Inst{7-4} = ry; + let Inst{3-0} = r32; + +} + + + +//===----------------------------------------------------------------------===// +// Format i8_MOV32R instruction class in Maxis : <|opcode|func|r32|rz> +//===----------------------------------------------------------------------===// + +class FI8_MOV32R16 pattern, InstrItinClass itin>: + MaxisInst16 +{ + + bits<3> func; + bits<5> r32; + bits<3> rz; + + + let Opcode = 0b01100; + + let Inst{10-8} = 0b101; + let Inst{7-5} = r32{2-0}; + let Inst{4-3} = r32{4-3}; + let Inst{2-0} = rz; + +} + +//===----------------------------------------------------------------------===// +// Format i8_SVRS instruction class in Maxis : +// <|opcode|svrs|s|ra|s0|s1|framesize> +//===----------------------------------------------------------------------===// + +class FI8_SVRS16 _s, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16 +{ + bits<1> s; + bits<1> ra = 0; + bits<1> s0 = 0; + bits<1> s1 = 0; + bits<4> framesize = 0; + + let s =_s; + let Opcode = 0b01100; + + let Inst{10-8} = 0b100; + let Inst{7} = s; + let Inst{6} = ra; + let Inst{5} = s0; + let Inst{4} = s1; + let Inst{3-0} = framesize; + +} + +//===----------------------------------------------------------------------===// +// Format JAL instruction class in Maxis16 : +// <|opcode|svrs|s|ra|s0|s1|framesize> +//===----------------------------------------------------------------------===// + +class FJAL16 _X, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16_32 +{ + bits<1> X; + bits<26> imm26; + + + let X = _X; + + let Inst{31-27} = 0b00011; + let Inst{26} = X; + let Inst{25-21} = imm26{20-16}; + let Inst{20-16} = imm26{25-21}; + let Inst{15-0} = imm26{15-0}; + +} + +//===----------------------------------------------------------------------===// +// Format EXT-I instruction class in Maxis16 : +// <|EXTEND|imm10:5|imm15:11|op|0|0|0|0|0|0|imm4:0> +//===----------------------------------------------------------------------===// + +class FEXT_I16 _eop, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16_EXTEND +{ + bits<16> imm16; + bits<5> eop; + + let eop = _eop; + + let Inst{26-21} = imm16{10-5}; + let Inst{20-16} = imm16{15-11}; + let Inst{15-11} = eop; + let Inst{10-5} = 0; + let Inst{4-0} = imm16{4-0}; + +} + +//===----------------------------------------------------------------------===// +// Format ASMACRO instruction class in Maxis16 : +// +//===----------------------------------------------------------------------===// + +class FASMACRO16 pattern, InstrItinClass itin>: + MaxisInst16_EXTEND +{ + bits<3> select; + bits<3> p4; + bits<5> p3; + bits<5> RRR = 0b11100; + bits<3> p2; + bits<3> p1; + bits<5> p0; + + + let Inst{26-24} = select; + let Inst{23-21} = p4; + let Inst{20-16} = p3; + let Inst{15-11} = RRR; + let Inst{10-8} = p2; + let Inst{7-5} = p1; + let Inst{4-0} = p0; + +} + + +//===----------------------------------------------------------------------===// +// Format EXT-RI instruction class in Maxis16 : +// <|EXTEND|imm10:5|imm15:11|op|rx|0|0|0|imm4:0> +//===----------------------------------------------------------------------===// + +class FEXT_RI16 _op, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16_EXTEND +{ + bits<16> imm16; + bits<5> op; + bits<3> rx; + + let op = _op; + + let Inst{26-21} = imm16{10-5}; + let Inst{20-16} = imm16{15-11}; + let Inst{15-11} = op; + let Inst{10-8} = rx; + let Inst{7-5} = 0; + let Inst{4-0} = imm16{4-0}; + +} + +//===----------------------------------------------------------------------===// +// Format EXT-RRI instruction class in Maxis16 : +// <|EXTEND|imm10:5|imm15:11|op|rx|ry|imm4:0> +//===----------------------------------------------------------------------===// + +class FEXT_RRI16 _op, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16_EXTEND +{ + bits<5> op; + bits<16> imm16; + bits<3> rx; + bits<3> ry; + + let op=_op; + + let Inst{26-21} = imm16{10-5}; + let Inst{20-16} = imm16{15-11}; + let Inst{15-11} = op; + let Inst{10-8} = rx; + let Inst{7-5} = ry; + let Inst{4-0} = imm16{4-0}; + +} + +//===----------------------------------------------------------------------===// +// Format EXT-RRI-A instruction class in Maxis16 : +// <|EXTEND|imm10:4|imm14:11|RRI-A|rx|ry|f|imm3:0> +//===----------------------------------------------------------------------===// + +class FEXT_RRI_A16 _f, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16_EXTEND +{ + bits<15> imm15; + bits<3> rx; + bits<3> ry; + bits<1> f; + + let f = _f; + + let Inst{26-20} = imm15{10-4}; + let Inst{19-16} = imm15{14-11}; + let Inst{15-11} = 0b01000; + let Inst{10-8} = rx; + let Inst{7-5} = ry; + let Inst{4} = f; + let Inst{3-0} = imm15{3-0}; + +} + +//===----------------------------------------------------------------------===// +// Format EXT-SHIFT instruction class in Maxis16 : +// <|EXTEND|sa 4:0|s5|0|SHIFT|rx|ry|0|f> +//===----------------------------------------------------------------------===// + +class FEXT_SHIFT16 _f, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16_EXTEND +{ + bits<6> sa6; + bits<3> rx; + bits<3> ry; + bits<2> f; + + let f = _f; + + let Inst{26-22} = sa6{4-0}; + let Inst{21} = sa6{5}; + let Inst{20-16} = 0; + let Inst{15-11} = 0b00110; + let Inst{10-8} = rx; + let Inst{7-5} = ry; + let Inst{4-2} = 0; + let Inst{1-0} = f; + +} + +//===----------------------------------------------------------------------===// +// Format EXT-I8 instruction class in Maxis16 : +// <|EXTEND|imm10:5|imm15:11|I8|funct|0|imm4:0> +//===----------------------------------------------------------------------===// + +class FEXT_I816 _funct, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16_EXTEND +{ + bits<16> imm16; + bits<5> I8; + bits<3> funct; + + let funct = _funct; + let I8 = 0b00110; + + let Inst{26-21} = imm16{10-5}; + let Inst{20-16} = imm16{15-11}; + let Inst{15-11} = I8; + let Inst{10-8} = funct; + let Inst{7-5} = 0; + let Inst{4-0} = imm16{4-0}; + +} + +//===----------------------------------------------------------------------===// +// Format EXT-I8_SVRS instruction class in Maxis16 : +// <|EXTEND|xsregs|framesize7:4|aregs|I8|SVRS|s|ra|s0|s1|framesize3:0> +//===----------------------------------------------------------------------===// + +class FEXT_I8_SVRS16 s_, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + MaxisInst16_EXTEND +{ + bits<3> xsregs =0; + bits<8> framesize =0; + bits<3> aregs =0; + bits<5> I8 = 0b01100; + bits<3> SVRS = 0b100; + bits<1> s; + bits<1> ra = 0; + bits<1> s0 = 0; + bits<1> s1 = 0; + + let s= s_; + + let Inst{26-24} = xsregs; + let Inst{23-20} = framesize{7-4}; + let Inst{19} = 0; + let Inst{18-16} = aregs; + let Inst{15-11} = I8; + let Inst{10-8} = SVRS; + let Inst{7} = s; + let Inst{6} = ra; + let Inst{5} = s0; + let Inst{4} = s1; + let Inst{3-0} = framesize{3-0}; + + +} + diff --git a/lib/Target/Maxis/Maxis16InstrInfo.cpp b/lib/Target/Maxis/Maxis16InstrInfo.cpp new file mode 100644 index 00000000..b32ad88b --- /dev/null +++ b/lib/Target/Maxis/Maxis16InstrInfo.cpp @@ -0,0 +1,485 @@ +//===- Maxis16InstrInfo.cpp - Maxis16 Instruction Information ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis16 implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "Maxis16InstrInfo.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/CodeGen/MachineBasicBlock.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/CodeGen/RegisterScavenging.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis16-instrinfo" + +Maxis16InstrInfo::Maxis16InstrInfo(const MaxisSubtarget &STI) + : MaxisInstrInfo(STI, Maxis::Bimm16) {} + +const MaxisRegisterInfo &Maxis16InstrInfo::getRegisterInfo() const { + return RI; +} + +/// isLoadFromStackSlot - If the specified machine instruction is a direct +/// load from a stack slot, return the virtual or physical register number of +/// the destination along with the FrameIndex of the loaded stack slot. If +/// not, return 0. This predicate must return 0 if the instruction has +/// any side effects other than loading from the stack slot. +unsigned Maxis16InstrInfo::isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + return 0; +} + +/// isStoreToStackSlot - If the specified machine instruction is a direct +/// store to a stack slot, return the virtual or physical register number of +/// the source reg along with the FrameIndex of the loaded stack slot. If +/// not, return 0. This predicate must return 0 if the instruction has +/// any side effects other than storing to the stack slot. +unsigned Maxis16InstrInfo::isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + return 0; +} + +void Maxis16InstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + const DebugLoc &DL, unsigned DestReg, + unsigned SrcReg, bool KillSrc) const { + unsigned Opc = 0; + + if (Maxis::CPU16RegsRegClass.contains(DestReg) && + Maxis::GPR32RegClass.contains(SrcReg)) + Opc = Maxis::MoveR3216; + else if (Maxis::GPR32RegClass.contains(DestReg) && + Maxis::CPU16RegsRegClass.contains(SrcReg)) + Opc = Maxis::Move32R16; + else if ((SrcReg == Maxis::HI0) && + (Maxis::CPU16RegsRegClass.contains(DestReg))) + Opc = Maxis::Mfhi16, SrcReg = 0; + else if ((SrcReg == Maxis::LO0) && + (Maxis::CPU16RegsRegClass.contains(DestReg))) + Opc = Maxis::Mflo16, SrcReg = 0; + + assert(Opc && "Cannot copy registers"); + + MachineInstrBuilder MIB = BuildMI(MBB, I, DL, get(Opc)); + + if (DestReg) + MIB.addReg(DestReg, RegState::Define); + + if (SrcReg) + MIB.addReg(SrcReg, getKillRegState(KillSrc)); +} + +void Maxis16InstrInfo::storeRegToStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned SrcReg, bool isKill, int FI, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + int64_t Offset) const { + DebugLoc DL; + if (I != MBB.end()) DL = I->getDebugLoc(); + MachineMemOperand *MMO = GetMemOperand(MBB, FI, MachineMemOperand::MOStore); + unsigned Opc = 0; + if (Maxis::CPU16RegsRegClass.hasSubClassEq(RC)) + Opc = Maxis::SwRxSpImmX16; + assert(Opc && "Register class not handled!"); + BuildMI(MBB, I, DL, get(Opc)).addReg(SrcReg, getKillRegState(isKill)). + addFrameIndex(FI).addImm(Offset) + .addMemOperand(MMO); +} + +void Maxis16InstrInfo::loadRegFromStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned DestReg, int FI, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + int64_t Offset) const { + DebugLoc DL; + if (I != MBB.end()) DL = I->getDebugLoc(); + MachineMemOperand *MMO = GetMemOperand(MBB, FI, MachineMemOperand::MOLoad); + unsigned Opc = 0; + + if (Maxis::CPU16RegsRegClass.hasSubClassEq(RC)) + Opc = Maxis::LwRxSpImmX16; + assert(Opc && "Register class not handled!"); + BuildMI(MBB, I, DL, get(Opc), DestReg).addFrameIndex(FI).addImm(Offset) + .addMemOperand(MMO); +} + +bool Maxis16InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { + MachineBasicBlock &MBB = *MI.getParent(); + switch (MI.getDesc().getOpcode()) { + default: + return false; + case Maxis::RetRA16: + ExpandRetRA16(MBB, MI, Maxis::JrcRa16); + break; + } + + MBB.erase(MI.getIterator()); + return true; +} + +/// GetOppositeBranchOpc - Return the inverse of the specified +/// opcode, e.g. turning BEQ to BNE. +unsigned Maxis16InstrInfo::getOppositeBranchOpc(unsigned Opc) const { + switch (Opc) { + case Maxis::BeqzRxImmX16: return Maxis::BnezRxImmX16; + case Maxis::BnezRxImmX16: return Maxis::BeqzRxImmX16; + case Maxis::BeqzRxImm16: return Maxis::BnezRxImm16; + case Maxis::BnezRxImm16: return Maxis::BeqzRxImm16; + case Maxis::BteqzT8CmpX16: return Maxis::BtnezT8CmpX16; + case Maxis::BteqzT8SltX16: return Maxis::BtnezT8SltX16; + case Maxis::BteqzT8SltiX16: return Maxis::BtnezT8SltiX16; + case Maxis::Btnez16: return Maxis::Bteqz16; + case Maxis::BtnezX16: return Maxis::BteqzX16; + case Maxis::BtnezT8CmpiX16: return Maxis::BteqzT8CmpiX16; + case Maxis::BtnezT8SltuX16: return Maxis::BteqzT8SltuX16; + case Maxis::BtnezT8SltiuX16: return Maxis::BteqzT8SltiuX16; + case Maxis::Bteqz16: return Maxis::Btnez16; + case Maxis::BteqzX16: return Maxis::BtnezX16; + case Maxis::BteqzT8CmpiX16: return Maxis::BtnezT8CmpiX16; + case Maxis::BteqzT8SltuX16: return Maxis::BtnezT8SltuX16; + case Maxis::BteqzT8SltiuX16: return Maxis::BtnezT8SltiuX16; + case Maxis::BtnezT8CmpX16: return Maxis::BteqzT8CmpX16; + case Maxis::BtnezT8SltX16: return Maxis::BteqzT8SltX16; + case Maxis::BtnezT8SltiX16: return Maxis::BteqzT8SltiX16; + } + llvm_unreachable("Illegal opcode!"); +} + +static void addSaveRestoreRegs(MachineInstrBuilder &MIB, + const std::vector &CSI, + unsigned Flags = 0) { + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + // Add the callee-saved register as live-in. Do not add if the register is + // RA and return address is taken, because it has already been added in + // method MaxisTargetLowering::lowerRETURNADDR. + // It's killed at the spill, unless the register is RA and return address + // is taken. + unsigned Reg = CSI[e-i-1].getReg(); + switch (Reg) { + case Maxis::RA: + case Maxis::S0: + case Maxis::S1: + MIB.addReg(Reg, Flags); + break; + case Maxis::S2: + break; + default: + llvm_unreachable("unexpected maxis16 callee saved register"); + + } + } +} + +// Adjust SP by FrameSize bytes. Save RA, S0, S1 +void Maxis16InstrInfo::makeFrame(unsigned SP, int64_t FrameSize, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + DebugLoc DL; + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const BitVector Reserved = RI.getReservedRegs(MF); + bool SaveS2 = Reserved[Maxis::S2]; + MachineInstrBuilder MIB; + unsigned Opc = ((FrameSize <= 128) && !SaveS2)? Maxis::Save16:Maxis::SaveX16; + MIB = BuildMI(MBB, I, DL, get(Opc)); + const std::vector &CSI = MFI.getCalleeSavedInfo(); + addSaveRestoreRegs(MIB, CSI); + if (SaveS2) + MIB.addReg(Maxis::S2); + if (isUInt<11>(FrameSize)) + MIB.addImm(FrameSize); + else { + int Base = 2040; // should create template function like isUInt that + // returns largest possible n bit unsigned integer + int64_t Remainder = FrameSize - Base; + MIB.addImm(Base); + if (isInt<16>(-Remainder)) + BuildAddiuSpImm(MBB, I, -Remainder); + else + adjustStackPtrBig(SP, -Remainder, MBB, I, Maxis::V0, Maxis::V1); + } +} + +// Adjust SP by FrameSize bytes. Restore RA, S0, S1 +void Maxis16InstrInfo::restoreFrame(unsigned SP, int64_t FrameSize, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + DebugLoc DL = I != MBB.end() ? I->getDebugLoc() : DebugLoc(); + MachineFunction *MF = MBB.getParent(); + MachineFrameInfo &MFI = MF->getFrameInfo(); + const BitVector Reserved = RI.getReservedRegs(*MF); + bool SaveS2 = Reserved[Maxis::S2]; + MachineInstrBuilder MIB; + unsigned Opc = ((FrameSize <= 128) && !SaveS2)? + Maxis::Restore16:Maxis::RestoreX16; + + if (!isUInt<11>(FrameSize)) { + unsigned Base = 2040; + int64_t Remainder = FrameSize - Base; + FrameSize = Base; // should create template function like isUInt that + // returns largest possible n bit unsigned integer + + if (isInt<16>(Remainder)) + BuildAddiuSpImm(MBB, I, Remainder); + else + adjustStackPtrBig(SP, Remainder, MBB, I, Maxis::A0, Maxis::A1); + } + MIB = BuildMI(MBB, I, DL, get(Opc)); + const std::vector &CSI = MFI.getCalleeSavedInfo(); + addSaveRestoreRegs(MIB, CSI, RegState::Define); + if (SaveS2) + MIB.addReg(Maxis::S2, RegState::Define); + MIB.addImm(FrameSize); +} + +// Adjust SP by Amount bytes where bytes can be up to 32bit number. +// This can only be called at times that we know that there is at least one free +// register. +// This is clearly safe at prologue and epilogue. +void Maxis16InstrInfo::adjustStackPtrBig(unsigned SP, int64_t Amount, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned Reg1, unsigned Reg2) const { + DebugLoc DL; + // + // li reg1, constant + // move reg2, sp + // add reg1, reg1, reg2 + // move sp, reg1 + // + // + MachineInstrBuilder MIB1 = BuildMI(MBB, I, DL, get(Maxis::LwConstant32), Reg1); + MIB1.addImm(Amount).addImm(-1); + MachineInstrBuilder MIB2 = BuildMI(MBB, I, DL, get(Maxis::MoveR3216), Reg2); + MIB2.addReg(Maxis::SP, RegState::Kill); + MachineInstrBuilder MIB3 = BuildMI(MBB, I, DL, get(Maxis::AdduRxRyRz16), Reg1); + MIB3.addReg(Reg1); + MIB3.addReg(Reg2, RegState::Kill); + MachineInstrBuilder MIB4 = BuildMI(MBB, I, DL, get(Maxis::Move32R16), + Maxis::SP); + MIB4.addReg(Reg1, RegState::Kill); +} + +void Maxis16InstrInfo::adjustStackPtrBigUnrestricted( + unsigned SP, int64_t Amount, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + llvm_unreachable("adjust stack pointer amount exceeded"); +} + +/// Adjust SP by Amount bytes. +void Maxis16InstrInfo::adjustStackPtr(unsigned SP, int64_t Amount, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + if (Amount == 0) + return; + + if (isInt<16>(Amount)) // need to change to addiu sp, ....and isInt<16> + BuildAddiuSpImm(MBB, I, Amount); + else + adjustStackPtrBigUnrestricted(SP, Amount, MBB, I); +} + +/// This function generates the sequence of instructions needed to get the +/// result of adding register REG and immediate IMM. +unsigned Maxis16InstrInfo::loadImmediate(unsigned FrameReg, int64_t Imm, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator II, + const DebugLoc &DL, + unsigned &NewImm) const { + // + // given original instruction is: + // Instr rx, T[offset] where offset is too big. + // + // lo = offset & 0xFFFF + // hi = ((offset >> 16) + (lo >> 15)) & 0xFFFF; + // + // let T = temporary register + // li T, hi + // shl T, 16 + // add T, Rx, T + // + RegScavenger rs; + int32_t lo = Imm & 0xFFFF; + NewImm = lo; + int Reg =0; + int SpReg = 0; + + rs.enterBasicBlock(MBB); + rs.forward(II); + // + // We need to know which registers can be used, in the case where there + // are not enough free registers. We exclude all registers that + // are used in the instruction that we are helping. + // // Consider all allocatable registers in the register class initially + BitVector Candidates = + RI.getAllocatableSet + (*II->getParent()->getParent(), &Maxis::CPU16RegsRegClass); + // Exclude all the registers being used by the instruction. + for (unsigned i = 0, e = II->getNumOperands(); i != e; ++i) { + MachineOperand &MO = II->getOperand(i); + if (MO.isReg() && MO.getReg() != 0 && !MO.isDef() && + !TargetRegisterInfo::isVirtualRegister(MO.getReg())) + Candidates.reset(MO.getReg()); + } + + // If the same register was used and defined in an instruction, then + // it will not be in the list of candidates. + // + // we need to analyze the instruction that we are helping. + // we need to know if it defines register x but register x is not + // present as an operand of the instruction. this tells + // whether the register is live before the instruction. if it's not + // then we don't need to save it in case there are no free registers. + int DefReg = 0; + for (unsigned i = 0, e = II->getNumOperands(); i != e; ++i) { + MachineOperand &MO = II->getOperand(i); + if (MO.isReg() && MO.isDef()) { + DefReg = MO.getReg(); + break; + } + } + + BitVector Available = rs.getRegsAvailable(&Maxis::CPU16RegsRegClass); + Available &= Candidates; + // + // we use T0 for the first register, if we need to save something away. + // we use T1 for the second register, if we need to save something away. + // + unsigned FirstRegSaved =0, SecondRegSaved=0; + unsigned FirstRegSavedTo = 0, SecondRegSavedTo = 0; + + Reg = Available.find_first(); + + if (Reg == -1) { + Reg = Candidates.find_first(); + Candidates.reset(Reg); + if (DefReg != Reg) { + FirstRegSaved = Reg; + FirstRegSavedTo = Maxis::T0; + copyPhysReg(MBB, II, DL, FirstRegSavedTo, FirstRegSaved, true); + } + } + else + Available.reset(Reg); + BuildMI(MBB, II, DL, get(Maxis::LwConstant32), Reg).addImm(Imm).addImm(-1); + NewImm = 0; + if (FrameReg == Maxis::SP) { + SpReg = Available.find_first(); + if (SpReg == -1) { + SpReg = Candidates.find_first(); + // Candidates.reset(SpReg); // not really needed + if (DefReg!= SpReg) { + SecondRegSaved = SpReg; + SecondRegSavedTo = Maxis::T1; + } + if (SecondRegSaved) + copyPhysReg(MBB, II, DL, SecondRegSavedTo, SecondRegSaved, true); + } + else + Available.reset(SpReg); + copyPhysReg(MBB, II, DL, SpReg, Maxis::SP, false); + BuildMI(MBB, II, DL, get(Maxis:: AdduRxRyRz16), Reg).addReg(SpReg, RegState::Kill) + .addReg(Reg); + } + else + BuildMI(MBB, II, DL, get(Maxis:: AdduRxRyRz16), Reg).addReg(FrameReg) + .addReg(Reg, RegState::Kill); + if (FirstRegSaved || SecondRegSaved) { + II = std::next(II); + if (FirstRegSaved) + copyPhysReg(MBB, II, DL, FirstRegSaved, FirstRegSavedTo, true); + if (SecondRegSaved) + copyPhysReg(MBB, II, DL, SecondRegSaved, SecondRegSavedTo, true); + } + return Reg; +} + +unsigned Maxis16InstrInfo::getAnalyzableBrOpc(unsigned Opc) const { + return (Opc == Maxis::BeqzRxImmX16 || Opc == Maxis::BimmX16 || + Opc == Maxis::Bimm16 || + Opc == Maxis::Bteqz16 || Opc == Maxis::Btnez16 || + Opc == Maxis::BeqzRxImm16 || Opc == Maxis::BnezRxImm16 || + Opc == Maxis::BnezRxImmX16 || Opc == Maxis::BteqzX16 || + Opc == Maxis::BteqzT8CmpX16 || Opc == Maxis::BteqzT8CmpiX16 || + Opc == Maxis::BteqzT8SltX16 || Opc == Maxis::BteqzT8SltuX16 || + Opc == Maxis::BteqzT8SltiX16 || Opc == Maxis::BteqzT8SltiuX16 || + Opc == Maxis::BtnezX16 || Opc == Maxis::BtnezT8CmpX16 || + Opc == Maxis::BtnezT8CmpiX16 || Opc == Maxis::BtnezT8SltX16 || + Opc == Maxis::BtnezT8SltuX16 || Opc == Maxis::BtnezT8SltiX16 || + Opc == Maxis::BtnezT8SltiuX16 ) ? Opc : 0; +} + +void Maxis16InstrInfo::ExpandRetRA16(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned Opc) const { + BuildMI(MBB, I, I->getDebugLoc(), get(Opc)); +} + +const MCInstrDesc &Maxis16InstrInfo::AddiuSpImm(int64_t Imm) const { + if (validSpImm8(Imm)) + return get(Maxis::AddiuSpImm16); + else + return get(Maxis::AddiuSpImmX16); +} + +void Maxis16InstrInfo::BuildAddiuSpImm + (MachineBasicBlock &MBB, MachineBasicBlock::iterator I, int64_t Imm) const { + DebugLoc DL; + BuildMI(MBB, I, DL, AddiuSpImm(Imm)).addImm(Imm); +} + +const MaxisInstrInfo *llvm::createMaxis16InstrInfo(const MaxisSubtarget &STI) { + return new Maxis16InstrInfo(STI); +} + +bool Maxis16InstrInfo::validImmediate(unsigned Opcode, unsigned Reg, + int64_t Amount) { + switch (Opcode) { + case Maxis::LbRxRyOffMemX16: + case Maxis::LbuRxRyOffMemX16: + case Maxis::LhRxRyOffMemX16: + case Maxis::LhuRxRyOffMemX16: + case Maxis::SbRxRyOffMemX16: + case Maxis::ShRxRyOffMemX16: + case Maxis::LwRxRyOffMemX16: + case Maxis::SwRxRyOffMemX16: + case Maxis::SwRxSpImmX16: + case Maxis::LwRxSpImmX16: + return isInt<16>(Amount); + case Maxis::AddiuRxRyOffMemX16: + if ((Reg == Maxis::PC) || (Reg == Maxis::SP)) + return isInt<16>(Amount); + return isInt<15>(Amount); + } + llvm_unreachable("unexpected Opcode in validImmediate"); +} diff --git a/lib/Target/Maxis/Maxis16InstrInfo.h b/lib/Target/Maxis/Maxis16InstrInfo.h new file mode 100644 index 00000000..4731bd0f --- /dev/null +++ b/lib/Target/Maxis/Maxis16InstrInfo.h @@ -0,0 +1,124 @@ +//===- Maxis16InstrInfo.h - Maxis16 Instruction Information -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis16 implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXIS16INSTRINFO_H +#define LLVM_LIB_TARGET_MAXIS_MAXIS16INSTRINFO_H + +#include "Maxis16RegisterInfo.h" +#include "MaxisInstrInfo.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/Support/MathExtras.h" +#include + +namespace llvm { + +class MCInstrDesc; +class MaxisSubtarget; + +class Maxis16InstrInfo : public MaxisInstrInfo { + const Maxis16RegisterInfo RI; + +public: + explicit Maxis16InstrInfo(const MaxisSubtarget &STI); + + const MaxisRegisterInfo &getRegisterInfo() const override; + + /// isLoadFromStackSlot - If the specified machine instruction is a direct + /// load from a stack slot, return the virtual or physical register number of + /// the destination along with the FrameIndex of the loaded stack slot. If + /// not, return 0. This predicate must return 0 if the instruction has + /// any side effects other than loading from the stack slot. + unsigned isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + + /// isStoreToStackSlot - If the specified machine instruction is a direct + /// store to a stack slot, return the virtual or physical register number of + /// the source reg along with the FrameIndex of the loaded stack slot. If + /// not, return 0. This predicate must return 0 if the instruction has + /// any side effects other than storing to the stack slot. + unsigned isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, + bool KillSrc) const override; + + void storeRegToStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned SrcReg, bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + int64_t Offset) const override; + + void loadRegFromStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned DestReg, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + int64_t Offset) const override; + + bool expandPostRAPseudo(MachineInstr &MI) const override; + + unsigned getOppositeBranchOpc(unsigned Opc) const override; + + // Adjust SP by FrameSize bytes. Save RA, S0, S1 + void makeFrame(unsigned SP, int64_t FrameSize, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const; + + // Adjust SP by FrameSize bytes. Restore RA, S0, S1 + void restoreFrame(unsigned SP, int64_t FrameSize, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const; + + /// Adjust SP by Amount bytes. + void adjustStackPtr(unsigned SP, int64_t Amount, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const override; + + /// Emit a series of instructions to load an immediate. + // This is to adjust some FrameReg. We return the new register to be used + // in place of FrameReg and the adjusted immediate field (&NewImm) + unsigned loadImmediate(unsigned FrameReg, int64_t Imm, MachineBasicBlock &MBB, + MachineBasicBlock::iterator II, const DebugLoc &DL, + unsigned &NewImm) const; + + static bool validImmediate(unsigned Opcode, unsigned Reg, int64_t Amount); + + static bool validSpImm8(int offset) { + return ((offset & 7) == 0) && isInt<11>(offset); + } + + // build the proper one based on the Imm field + + const MCInstrDesc& AddiuSpImm(int64_t Imm) const; + + void BuildAddiuSpImm + (MachineBasicBlock &MBB, MachineBasicBlock::iterator I, int64_t Imm) const; +private: + unsigned getAnalyzableBrOpc(unsigned Opc) const override; + + void ExpandRetRA16(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + unsigned Opc) const; + + // Adjust SP by Amount bytes where bytes can be up to 32bit number. + void adjustStackPtrBig(unsigned SP, int64_t Amount, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned Reg1, unsigned Reg2) const; + + // Adjust SP by Amount bytes where bytes can be up to 32bit number. + void adjustStackPtrBigUnrestricted(unsigned SP, int64_t Amount, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXIS16INSTRINFO_H diff --git a/lib/Target/Maxis/Maxis16InstrInfo.td b/lib/Target/Maxis/Maxis16InstrInfo.td new file mode 100644 index 00000000..d7245a6a --- /dev/null +++ b/lib/Target/Maxis/Maxis16InstrInfo.td @@ -0,0 +1,1912 @@ +//===- Maxis16InstrInfo.td - Target Description for Maxis16 -*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis16 instructions. +// +//===----------------------------------------------------------------------===// +// +// +// Maxis Address +// +def addr16 : ComplexPattern; +def addr16sp : ComplexPattern; + +// +// Address operand +def mem16 : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops CPU16Regs, simm16); + let EncoderMethod = "getMemEncoding"; +} + +def mem16sp : Operand { + let PrintMethod = "printMemOperand"; + // This should be CPUSPReg but the MAXIS16 subtarget isn't good enough at + // keeping the sp-relative load and the other varieties separate at the + // moment. This lie fixes the problem sufficiently well to fix the errors + // emitted by -verify-machineinstrs and the output ends up correct as long + // as we use an external assembler (which is already a requirement for MAXIS16 + // for several other reasons). + let MIOperandInfo = (ops CPU16RegsPlusSP, simm16); + let EncoderMethod = "getMemEncoding"; +} + +def mem16_ea : Operand { + let PrintMethod = "printMemOperandEA"; + let MIOperandInfo = (ops CPU16RegsPlusSP, simm16); + let EncoderMethod = "getMemEncoding"; +} + +def pcrel16 : Operand; + +// +// I-type instruction format +// +// this is only used by bimm. the actual assembly value is a 12 bit signed +// number +// +class FI16_ins op, string asmstr, InstrItinClass itin>: + FI16; + +// +// +// I8 instruction format +// + +class FI816_ins_base _func, string asmstr, + string asmstr2, InstrItinClass itin>: + FI816<_func, (outs), (ins simm16:$imm), !strconcat(asmstr, asmstr2), + [], itin>; + +class FI816_ins _func, string asmstr, + InstrItinClass itin>: + FI816_ins_base<_func, asmstr, "\t$imm # 16 bit inst", itin>; + +class FI816_SP_ins _func, string asmstr, + InstrItinClass itin>: + FI816_ins_base<_func, asmstr, "\t$$sp, $imm # 16 bit inst", itin>; + +// +// RI instruction format +// + + +class FRI16_ins_base op, string asmstr, string asmstr2, + InstrItinClass itin>: + FRI16; + +class FRI16_ins op, string asmstr, + InstrItinClass itin>: + FRI16_ins_base; + +class FRI16_TCP_ins _op, string asmstr, + InstrItinClass itin>: + FRI16<_op, (outs CPU16Regs:$rx), (ins pcrel16:$imm, i32imm:$size), + !strconcat(asmstr, "\t$rx, $imm\t# 16 bit inst"), [], itin>; + +class FRI16R_ins_base op, string asmstr, string asmstr2, + InstrItinClass itin>: + FRI16; + +class FRI16R_ins op, string asmstr, + InstrItinClass itin>: + FRI16R_ins_base; + +class F2RI16_ins _op, string asmstr, + InstrItinClass itin>: + FRI16<_op, (outs CPU16Regs:$rx), (ins CPU16Regs:$rx_, simm16:$imm), + !strconcat(asmstr, "\t$rx, $imm\t# 16 bit inst"), [], itin> { + let Constraints = "$rx_ = $rx"; +} + +class FRI16_B_ins _op, string asmstr, + InstrItinClass itin>: + FRI16<_op, (outs), (ins CPU16Regs:$rx, brtarget:$imm), + !strconcat(asmstr, "\t$rx, $imm # 16 bit inst"), [], itin>; +// +// Compare a register and immediate and place result in CC +// Implicit use of T8 +// +// EXT-CCRR Instruction format +// +class FEXT_CCRXI16_ins: + MaxisPseudo16<(outs CPU16Regs:$cc), (ins CPU16Regs:$rx, simm16:$imm), + !strconcat(asmstr, "\t$rx, $imm\n\tmove\t$cc, $$t8"), []> { + let isCodeGenOnly=1; + let usesCustomInserter = 1; +} + +// JAL and JALX instruction format +// +class FJAL16_ins _X, string asmstr, + InstrItinClass itin>: + FJAL16<_X, (outs), (ins uimm26:$imm), + !strconcat(asmstr, "\t$imm\n\tnop"),[], + itin> { + let isCodeGenOnly=1; + let Size=6; +} + +class FJALB16_ins _X, string asmstr, + InstrItinClass itin>: + FJAL16<_X, (outs), (ins uimm26:$imm), + !strconcat(asmstr, "\t$imm\t# branch\n\tnop"),[], + itin> { + let isCodeGenOnly=1; + let Size=6; +} + +// +// EXT-I instruction format +// +class FEXT_I16_ins eop, string asmstr, InstrItinClass itin> : + FEXT_I16; + +// +// EXT-I8 instruction format +// + +class FEXT_I816_ins_base _func, string asmstr, + string asmstr2, InstrItinClass itin>: + FEXT_I816<_func, (outs), (ins simm16:$imm), !strconcat(asmstr, asmstr2), + [], itin>; + +class FEXT_I816_ins _func, string asmstr, + InstrItinClass itin>: + FEXT_I816_ins_base<_func, asmstr, "\t$imm", itin>; + +class FEXT_I816_SP_ins _func, string asmstr, + InstrItinClass itin>: + FEXT_I816_ins_base<_func, asmstr, "\t$$sp, $imm", itin>; + +// +// Assembler formats in alphabetical order. +// Natural and pseudos are mixed together. +// +// Compare two registers and place result in CC +// Implicit use of T8 +// +// CC-RR Instruction format +// +class FCCRR16_ins : + MaxisPseudo16<(outs CPU16Regs:$cc), (ins CPU16Regs:$rx, CPU16Regs:$ry), + !strconcat(asmstr, "\t$rx, $ry\n\tmove\t$cc, $$t8"), []> { + let isCodeGenOnly=1; + let usesCustomInserter = 1; +} + +// +// EXT-RI instruction format +// + +class FEXT_RI16_ins_base _op, string asmstr, string asmstr2, + InstrItinClass itin>: + FEXT_RI16<_op, (outs CPU16Regs:$rx), (ins simm16:$imm), + !strconcat(asmstr, asmstr2), [], itin>; + +class FEXT_RI16_ins _op, string asmstr, + InstrItinClass itin>: + FEXT_RI16_ins_base<_op, asmstr, "\t$rx, $imm", itin>; + +class FEXT_RI16R_ins_base _op, string asmstr, string asmstr2, + InstrItinClass itin>: + FEXT_RI16<_op, (outs ), (ins CPU16Regs:$rx, simm16:$imm), + !strconcat(asmstr, asmstr2), [], itin>; + +class FEXT_RI16R_ins _op, string asmstr, + InstrItinClass itin>: + FEXT_RI16R_ins_base<_op, asmstr, "\t$rx, $imm", itin>; + +class FEXT_RI16_PC_ins _op, string asmstr, InstrItinClass itin>: + FEXT_RI16_ins_base<_op, asmstr, "\t$rx, $$pc, $imm", itin>; + +class FEXT_RI16_B_ins _op, string asmstr, + InstrItinClass itin>: + FEXT_RI16<_op, (outs), (ins CPU16Regs:$rx, brtarget:$imm), + !strconcat(asmstr, "\t$rx, $imm"), [], itin>; + +class FEXT_RI16_TCP_ins _op, string asmstr, + InstrItinClass itin>: + FEXT_RI16<_op, (outs CPU16Regs:$rx), (ins pcrel16:$imm, i32imm:$size), + !strconcat(asmstr, "\t$rx, $imm"), [], itin>; + +class FEXT_2RI16_ins _op, string asmstr, + InstrItinClass itin>: + FEXT_RI16<_op, (outs CPU16Regs:$rx), (ins CPU16Regs:$rx_, simm16:$imm), + !strconcat(asmstr, "\t$rx, $imm"), [], itin> { + let Constraints = "$rx_ = $rx"; +} + +// +// EXT-RRI instruction format +// + +class FEXT_RRI16_mem_ins op, string asmstr, Operand MemOpnd, + InstrItinClass itin>: + FEXT_RRI16; + +class FEXT_RRI16_mem2_ins op, string asmstr, Operand MemOpnd, + InstrItinClass itin>: + FEXT_RRI16; + +// +// +// EXT-RRI-A instruction format +// + +class FEXT_RRI_A16_mem_ins op, string asmstr, Operand MemOpnd, + InstrItinClass itin>: + FEXT_RRI_A16; + +// +// EXT-SHIFT instruction format +// +class FEXT_SHIFT16_ins _f, string asmstr, InstrItinClass itin>: + FEXT_SHIFT16<_f, (outs CPU16Regs:$rx), (ins CPU16Regs:$ry, uimm5:$sa), + !strconcat(asmstr, "\t$rx, $ry, $sa"), [], itin>; + +// +// EXT-T8I8 +// +class FEXT_T8I816_ins: + MaxisPseudo16<(outs), + (ins CPU16Regs:$rx, CPU16Regs:$ry, brtarget:$imm), + !strconcat(asmstr2, !strconcat("\t$rx, $ry\n\t", + !strconcat(asmstr, "\t$imm"))),[]> { + let isCodeGenOnly=1; + let usesCustomInserter = 1; +} + +// +// EXT-T8I8I +// +class FEXT_T8I8I16_ins: + MaxisPseudo16<(outs), + (ins CPU16Regs:$rx, simm16:$imm, brtarget:$targ), + !strconcat(asmstr2, !strconcat("\t$rx, $imm\n\t", + !strconcat(asmstr, "\t$targ"))), []> { + let isCodeGenOnly=1; + let usesCustomInserter = 1; +} +// + + +// +// I8_MOVR32 instruction format (used only by the MOVR32 instructio +// +class FI8_MOVR3216_ins: + FI8_MOVR3216<(outs CPU16Regs:$rz), (ins GPR32:$r32), + !strconcat(asmstr, "\t$rz, $r32"), [], itin>; + +// +// I8_MOV32R instruction format (used only by MOV32R instruction) +// + +class FI8_MOV32R16_ins: + FI8_MOV32R16<(outs GPR32:$r32), (ins CPU16Regs:$rz), + !strconcat(asmstr, "\t$r32, $rz"), [], itin>; + +// +// This are pseudo formats for multiply +// This first one can be changed to non-pseudo now. +// +// MULT +// +class FMULT16_ins : + MaxisPseudo16<(outs), (ins CPU16Regs:$rx, CPU16Regs:$ry), + !strconcat(asmstr, "\t$rx, $ry"), []>; + +// +// MULT-LO +// +class FMULT16_LO_ins : + MaxisPseudo16<(outs CPU16Regs:$rz), (ins CPU16Regs:$rx, CPU16Regs:$ry), + !strconcat(asmstr, "\t$rx, $ry\n\tmflo\t$rz"), []> { + let isCodeGenOnly=1; +} + +// +// RR-type instruction format +// + +class FRR16_ins f, string asmstr, InstrItinClass itin> : + FRR16 { +} + +class FRRBreakNull16_ins : + FRRBreak16<(outs), (ins), asmstr, [], itin> { + let Code=0; +} + +class FRR16R_ins f, string asmstr, InstrItinClass itin> : + FRR16 { +} + +class FRRTR16_ins : + MaxisPseudo16<(outs CPU16Regs:$rz), (ins CPU16Regs:$rx, CPU16Regs:$ry), + !strconcat(asmstr, "\t$rx, $ry\n\tmove\t$rz, $$t8"), []> ; + +// +// maybe refactor but need a $zero as a dummy first parameter +// +class FRR16_div_ins f, string asmstr, InstrItinClass itin> : + FRR16 ; + +class FUnaryRR16_ins f, string asmstr, InstrItinClass itin> : + FRR16 ; + + +class FRR16_M_ins f, string asmstr, + InstrItinClass itin> : + FRR16; + +class FRxRxRy16_ins f, string asmstr, + InstrItinClass itin> : + FRR16 { + let Constraints = "$rx = $rz"; +} + +let rx=0 in +class FRR16_JALRC_RA_only_ins nd_, bits<1> l_, + string asmstr, InstrItinClass itin>: + FRR16_JALRC ; + + +class FRR16_JALRC_ins nd, bits<1> l, bits<1> ra, + string asmstr, InstrItinClass itin>: + FRR16_JALRC ; + +class FRR_SF16_ins + _funct, bits<3> _subfunc, + string asmstr, InstrItinClass itin>: + FRR_SF16<_funct, _subfunc, (outs CPU16Regs:$rx), (ins CPU16Regs:$rx_), + !strconcat(asmstr, "\t $rx"), + [], itin> { + let Constraints = "$rx_ = $rx"; + } +// +// RRR-type instruction format +// + +class FRRR16_ins _f, string asmstr, InstrItinClass itin> : + FRRR16<_f, (outs CPU16Regs:$rz), (ins CPU16Regs:$rx, CPU16Regs:$ry), + !strconcat(asmstr, "\t$rz, $rx, $ry"), [], itin>; + +// +// These Sel patterns support the generation of conditional move +// pseudo instructions. +// +// The nomenclature uses the components making up the pseudo and may +// be a bit counter intuitive when compared with the end result we seek. +// For example using a bqez in the example directly below results in the +// conditional move being done if the tested register is not zero. +// I considered in easier to check by keeping the pseudo consistent with +// it's components but it could have been done differently. +// +// The simplest case is when can test and operand directly and do the +// conditional move based on a simple maxis16 conditional +// branch instruction. +// for example: +// if $op == beqz or bnez: +// +// $op1 $rt, .+4 +// move $rd, $rs +// +// if $op == beqz, then if $rt != 0, then the conditional assignment +// $rd = $rs is done. + +// if $op == bnez, then if $rt == 0, then the conditional assignment +// $rd = $rs is done. +// +// So this pseudo class only has one operand, i.e. op +// +class Sel: + MaxisPseudo16<(outs CPU16Regs:$rd_), (ins CPU16Regs:$rd, CPU16Regs:$rs, + CPU16Regs:$rt), + !strconcat(op, "\t$rt, .+4\n\t\n\tmove $rd, $rs"), []> { + //let isCodeGenOnly=1; + let Constraints = "$rd = $rd_"; + let usesCustomInserter = 1; +} + +// +// The next two instruction classes allow for an operand which tests +// two operands and returns a value in register T8 and +//then does a conditional branch based on the value of T8 +// + +// op2 can be cmpi or slti/sltiu +// op1 can bteqz or btnez +// the operands for op2 are a register and a signed constant +// +// $op2 $t, $imm ;test register t and branch conditionally +// $op1 .+4 ;op1 is a conditional branch +// move $rd, $rs +// +// +class SeliT: + MaxisPseudo16<(outs CPU16Regs:$rd_), (ins CPU16Regs:$rd, CPU16Regs:$rs, + CPU16Regs:$rl, simm16:$imm), + !strconcat(op2, + !strconcat("\t$rl, $imm\n\t", + !strconcat(op1, "\t.+4\n\tmove $rd, $rs"))), []> { + let isCodeGenOnly=1; + let Constraints = "$rd = $rd_"; + let usesCustomInserter = 1; +} + +// +// op2 can be cmp or slt/sltu +// op1 can be bteqz or btnez +// the operands for op2 are two registers +// op1 is a conditional branch +// +// +// $op2 $rl, $rr ;test registers rl,rr +// $op1 .+4 ;op2 is a conditional branch +// move $rd, $rs +// +// +class SelT: + MaxisPseudo16<(outs CPU16Regs:$rd_), + (ins CPU16Regs:$rd, CPU16Regs:$rs, + CPU16Regs:$rl, CPU16Regs:$rr), + !strconcat(op2, + !strconcat("\t$rl, $rr\n\t", + !strconcat(op1, "\t.+4\n\tmove $rd, $rs"))), []> { + let isCodeGenOnly=1; + let Constraints = "$rd = $rd_"; + let usesCustomInserter = 1; +} + +// +// 32 bit constant +// +def Constant32: + MaxisPseudo16<(outs), (ins simm32:$imm), "\t.word $imm", []>; + +def LwConstant32: + MaxisPseudo16<(outs CPU16Regs:$rx), (ins simm32:$imm, simm32:$constid), + "lw\t$rx, 1f\n\tb\t2f\n\t.align\t2\n1: \t.word\t$imm\n2:", []>; + + +// +// Some general instruction class info +// +// + +class ArithLogic16Defs { + bits<5> shamt = 0; + bit isCommutable = isCom; + bit isReMaterializable = 1; + bit hasSideEffects = 0; +} + +class branch16 { + bit isBranch = 1; + bit isTerminator = 1; + bit isBarrier = 1; +} + +class cbranch16 { + bit isBranch = 1; + bit isTerminator = 1; +} + +class MayLoad { + bit mayLoad = 1; +} + +class MayStore { + bit mayStore = 1; +} +// + + +// Format: ADDIU rx, immediate MAXIS16e +// Purpose: Add Immediate Unsigned Word (2-Operand, Extended) +// To add a constant to a 32-bit integer. +// +def AddiuRxImmX16: FEXT_RI16_ins<0b01001, "addiu", IIM16Alu>; + +def AddiuRxRxImm16: F2RI16_ins<0b01001, "addiu", IIM16Alu>, + ArithLogic16Defs<0> { + let AddedComplexity = 5; +} +def AddiuRxRxImmX16: FEXT_2RI16_ins<0b01001, "addiu", IIM16Alu>, + ArithLogic16Defs<0> { + let isCodeGenOnly = 1; +} + +def AddiuRxRyOffMemX16: + FEXT_RRI_A16_mem_ins<0, "addiu", mem16_ea, IIM16Alu>; + +// + +// Format: ADDIU rx, pc, immediate MAXIS16e +// Purpose: Add Immediate Unsigned Word (3-Operand, PC-Relative, Extended) +// To add a constant to the program counter. +// +def AddiuRxPcImmX16: FEXT_RI16_PC_ins<0b00001, "addiu", IIM16Alu>; + +// +// Format: ADDIU sp, immediate MAXIS16e +// Purpose: Add Immediate Unsigned Word (2-Operand, SP-Relative, Extended) +// To add a constant to the stack pointer. +// +def AddiuSpImm16 + : FI816_SP_ins<0b011, "addiu", IIM16Alu> { + let Defs = [SP]; + let Uses = [SP]; + let AddedComplexity = 5; +} + +def AddiuSpImmX16 + : FEXT_I816_SP_ins<0b011, "addiu", IIM16Alu> { + let Defs = [SP]; + let Uses = [SP]; +} + +// +// Format: ADDU rz, rx, ry MAXIS16e +// Purpose: Add Unsigned Word (3-Operand) +// To add 32-bit integers. +// + +def AdduRxRyRz16: FRRR16_ins<01, "addu", IIM16Alu>, ArithLogic16Defs<1>; + +// +// Format: AND rx, ry MAXIS16e +// Purpose: AND +// To do a bitwise logical AND. + +def AndRxRxRy16: FRxRxRy16_ins<0b01100, "and", IIM16Alu>, ArithLogic16Defs<1>; + + +// +// Format: BEQZ rx, offset MAXIS16e +// Purpose: Branch on Equal to Zero +// To test a GPR then do a PC-relative conditional branch. +// +def BeqzRxImm16: FRI16_B_ins<0b00100, "beqz", IIM16Alu>, cbranch16; + + +// +// Format: BEQZ rx, offset MAXIS16e +// Purpose: Branch on Equal to Zero (Extended) +// To test a GPR then do a PC-relative conditional branch. +// +def BeqzRxImmX16: FEXT_RI16_B_ins<0b00100, "beqz", IIM16Alu>, cbranch16; + +// +// Format: B offset MAXIS16e +// Purpose: Unconditional Branch (Extended) +// To do an unconditional PC-relative branch. +// + +def Bimm16: FI16_ins<0b00010, "b", IIM16Alu>, branch16; + +// Format: B offset MAXIS16e +// Purpose: Unconditional Branch +// To do an unconditional PC-relative branch. +// +def BimmX16: FEXT_I16_ins<0b00010, "b", IIM16Alu>, branch16; + +// +// Format: BNEZ rx, offset MAXIS16e +// Purpose: Branch on Not Equal to Zero +// To test a GPR then do a PC-relative conditional branch. +// +def BnezRxImm16: FRI16_B_ins<0b00101, "bnez", IIM16Alu>, cbranch16; + +// +// Format: BNEZ rx, offset MAXIS16e +// Purpose: Branch on Not Equal to Zero (Extended) +// To test a GPR then do a PC-relative conditional branch. +// +def BnezRxImmX16: FEXT_RI16_B_ins<0b00101, "bnez", IIM16Alu>, cbranch16; + + +// +//Format: BREAK immediate +// Purpose: Breakpoint +// To cause a Breakpoint exception. + +def Break16: FRRBreakNull16_ins<"break 0", IIM16Alu>; +// +// Format: BTEQZ offset MAXIS16e +// Purpose: Branch on T Equal to Zero (Extended) +// To test special register T then do a PC-relative conditional branch. +// +def Bteqz16: FI816_ins<0b000, "bteqz", IIM16Alu>, cbranch16 { + let Uses = [T8]; +} + +def BteqzX16: FEXT_I816_ins<0b000, "bteqz", IIM16Alu>, cbranch16 { + let Uses = [T8]; +} + +def BteqzT8CmpX16: FEXT_T8I816_ins<"bteqz", "cmp">, cbranch16; + +def BteqzT8CmpiX16: FEXT_T8I8I16_ins<"bteqz", "cmpi">, + cbranch16; + +def BteqzT8SltX16: FEXT_T8I816_ins<"bteqz", "slt">, cbranch16; + +def BteqzT8SltuX16: FEXT_T8I816_ins<"bteqz", "sltu">, cbranch16; + +def BteqzT8SltiX16: FEXT_T8I8I16_ins<"bteqz", "slti">, cbranch16; + +def BteqzT8SltiuX16: FEXT_T8I8I16_ins<"bteqz", "sltiu">, + cbranch16; + +// +// Format: BTNEZ offset MAXIS16e +// Purpose: Branch on T Not Equal to Zero (Extended) +// To test special register T then do a PC-relative conditional branch. +// + +def Btnez16: FI816_ins<0b001, "btnez", IIM16Alu>, cbranch16 { + let Uses = [T8]; +} + +def BtnezX16: FEXT_I816_ins<0b001, "btnez", IIM16Alu> ,cbranch16 { + let Uses = [T8]; +} + +def BtnezT8CmpX16: FEXT_T8I816_ins<"btnez", "cmp">, cbranch16; + +def BtnezT8CmpiX16: FEXT_T8I8I16_ins<"btnez", "cmpi">, cbranch16; + +def BtnezT8SltX16: FEXT_T8I816_ins<"btnez", "slt">, cbranch16; + +def BtnezT8SltuX16: FEXT_T8I816_ins<"btnez", "sltu">, cbranch16; + +def BtnezT8SltiX16: FEXT_T8I8I16_ins<"btnez", "slti">, cbranch16; + +def BtnezT8SltiuX16: FEXT_T8I8I16_ins<"btnez", "sltiu">, + cbranch16; + +// +// Format: CMP rx, ry MAXIS16e +// Purpose: Compare +// To compare the contents of two GPRs. +// +def CmpRxRy16: FRR16R_ins<0b01010, "cmp", IIM16Alu> { + let Defs = [T8]; +} + +// +// Format: CMPI rx, immediate MAXIS16e +// Purpose: Compare Immediate +// To compare a constant with the contents of a GPR. +// +def CmpiRxImm16: FRI16R_ins<0b01110, "cmpi", IIM16Alu> { + let Defs = [T8]; +} + +// +// Format: CMPI rx, immediate MAXIS16e +// Purpose: Compare Immediate (Extended) +// To compare a constant with the contents of a GPR. +// +def CmpiRxImmX16: FEXT_RI16R_ins<0b01110, "cmpi", IIM16Alu> { + let Defs = [T8]; +} + + +// +// Format: DIV rx, ry MAXIS16e +// Purpose: Divide Word +// To divide 32-bit signed integers. +// +def DivRxRy16: FRR16_div_ins<0b11010, "div", IIM16Alu> { + let Defs = [HI0, LO0]; +} + +// +// Format: DIVU rx, ry MAXIS16e +// Purpose: Divide Unsigned Word +// To divide 32-bit unsigned integers. +// +def DivuRxRy16: FRR16_div_ins<0b11011, "divu", IIM16Alu> { + let Defs = [HI0, LO0]; +} +// +// Format: JAL target MAXIS16e +// Purpose: Jump and Link +// To execute a procedure call within the current 256 MB-aligned +// region and preserve the current ISA. +// + +def Jal16 : FJAL16_ins<0b0, "jal", IIM16Alu> { + let hasDelaySlot = 0; // not true, but we add the nop for now + let isCall=1; + let Defs = [RA]; +} + +def JalB16 : FJALB16_ins<0b0, "jal", IIM16Alu>, branch16 { + let hasDelaySlot = 0; // not true, but we add the nop for now + let isBranch=1; + let Defs = [RA]; +} + +// +// Format: JR ra MAXIS16e +// Purpose: Jump Register Through Register ra +// To execute a branch to the instruction address in the return +// address register. +// + +def JrRa16: FRR16_JALRC_RA_only_ins<0, 0, "jr", IIM16Alu> { + let isBranch = 1; + let isIndirectBranch = 1; + let hasDelaySlot = 1; + let isTerminator=1; + let isBarrier=1; + let isReturn=1; +} + +def JrcRa16: FRR16_JALRC_RA_only_ins<1, 1, "jrc", IIM16Alu> { + let isBranch = 1; + let isIndirectBranch = 1; + let isTerminator=1; + let isBarrier=1; + let isReturn=1; +} + +def JrcRx16: FRR16_JALRC_ins<1, 1, 0, "jrc", IIM16Alu> { + let isBranch = 1; + let isIndirectBranch = 1; + let isTerminator=1; + let isBarrier=1; +} +// +// Format: LB ry, offset(rx) MAXIS16e +// Purpose: Load Byte (Extended) +// To load a byte from memory as a signed value. +// +def LbRxRyOffMemX16: FEXT_RRI16_mem_ins<0b10011, "lb", mem16, II_LB>, MayLoad{ + let isCodeGenOnly = 1; +} + +// +// Format: LBU ry, offset(rx) MAXIS16e +// Purpose: Load Byte Unsigned (Extended) +// To load a byte from memory as a unsigned value. +// +def LbuRxRyOffMemX16: + FEXT_RRI16_mem_ins<0b10100, "lbu", mem16, II_LBU>, MayLoad { + let isCodeGenOnly = 1; +} + +// +// Format: LH ry, offset(rx) MAXIS16e +// Purpose: Load Halfword signed (Extended) +// To load a halfword from memory as a signed value. +// +def LhRxRyOffMemX16: FEXT_RRI16_mem_ins<0b10100, "lh", mem16, II_LH>, MayLoad{ + let isCodeGenOnly = 1; +} + +// +// Format: LHU ry, offset(rx) MAXIS16e +// Purpose: Load Halfword unsigned (Extended) +// To load a halfword from memory as an unsigned value. +// +def LhuRxRyOffMemX16: + FEXT_RRI16_mem_ins<0b10100, "lhu", mem16, II_LHU>, MayLoad { + let isCodeGenOnly = 1; +} + +// +// Format: LI rx, immediate MAXIS16e +// Purpose: Load Immediate +// To load a constant into a GPR. +// +def LiRxImm16: FRI16_ins<0b01101, "li", IIM16Alu>; + +// +// Format: LI rx, immediate MAXIS16e +// Purpose: Load Immediate (Extended) +// To load a constant into a GPR. +// +def LiRxImmX16: FEXT_RI16_ins<0b01101, "li", IIM16Alu>; + +def LiRxImmAlignX16: FEXT_RI16_ins<0b01101, ".align 2\n\tli", IIM16Alu> { + let isCodeGenOnly = 1; +} + +// +// Format: LW ry, offset(rx) MAXIS16e +// Purpose: Load Word (Extended) +// To load a word from memory as a signed value. +// +def LwRxRyOffMemX16: FEXT_RRI16_mem_ins<0b10011, "lw", mem16, II_LW>, MayLoad{ + let isCodeGenOnly = 1; +} + +// Format: LW rx, offset(sp) MAXIS16e +// Purpose: Load Word (SP-Relative, Extended) +// To load an SP-relative word from memory as a signed value. +// +def LwRxSpImmX16: FEXT_RRI16_mem_ins<0b10010, "lw", mem16sp, II_LW>, MayLoad; + +def LwRxPcTcp16: FRI16_TCP_ins<0b10110, "lw", II_LW>, MayLoad; + +def LwRxPcTcpX16: FEXT_RI16_TCP_ins<0b10110, "lw", II_LW>, MayLoad; +// +// Format: MOVE r32, rz MAXIS16e +// Purpose: Move +// To move the contents of a GPR to a GPR. +// +def Move32R16: FI8_MOV32R16_ins<"move", IIM16Alu>; + +// +// Format: MOVE ry, r32 MAXIS16e +//Purpose: Move +// To move the contents of a GPR to a GPR. +// +def MoveR3216: FI8_MOVR3216_ins<"move", IIM16Alu>; + +// +// Format: MFHI rx MAXIS16e +// Purpose: Move From HI Register +// To copy the special purpose HI register to a GPR. +// +def Mfhi16: FRR16_M_ins<0b10000, "mfhi", IIM16Alu> { + let Uses = [HI0]; + let hasSideEffects = 0; +} + +// +// Format: MFLO rx MAXIS16e +// Purpose: Move From LO Register +// To copy the special purpose LO register to a GPR. +// +def Mflo16: FRR16_M_ins<0b10010, "mflo", IIM16Alu> { + let Uses = [LO0]; + let hasSideEffects = 0; +} + +// +// Pseudo Instruction for mult +// +def MultRxRy16: FMULT16_ins<"mult", IIM16Alu> { + let isCommutable = 1; + let hasSideEffects = 0; + let Defs = [HI0, LO0]; +} + +def MultuRxRy16: FMULT16_ins<"multu", IIM16Alu> { + let isCommutable = 1; + let hasSideEffects = 0; + let Defs = [HI0, LO0]; +} + +// +// Format: MULT rx, ry MAXIS16e +// Purpose: Multiply Word +// To multiply 32-bit signed integers. +// +def MultRxRyRz16: FMULT16_LO_ins<"mult", IIM16Alu> { + let isCommutable = 1; + let hasSideEffects = 0; + let Defs = [HI0, LO0]; +} + +// +// Format: MULTU rx, ry MAXIS16e +// Purpose: Multiply Unsigned Word +// To multiply 32-bit unsigned integers. +// +def MultuRxRyRz16: FMULT16_LO_ins<"multu", IIM16Alu> { + let isCommutable = 1; + let hasSideEffects = 0; + let Defs = [HI0, LO0]; +} + +// +// Format: NEG rx, ry MAXIS16e +// Purpose: Negate +// To negate an integer value. +// +def NegRxRy16: FUnaryRR16_ins<0b11101, "neg", IIM16Alu>; + +// +// Format: NOT rx, ry MAXIS16e +// Purpose: Not +// To complement an integer value +// +def NotRxRy16: FUnaryRR16_ins<0b01111, "not", IIM16Alu>; + +// +// Format: OR rx, ry MAXIS16e +// Purpose: Or +// To do a bitwise logical OR. +// +def OrRxRxRy16: FRxRxRy16_ins<0b01101, "or", IIM16Alu>, ArithLogic16Defs<1>; + +// +// Format: RESTORE {ra,}{s0/s1/s0-1,}{framesize} +// (All args are optional) MAXIS16e +// Purpose: Restore Registers and Deallocate Stack Frame +// To deallocate a stack frame before exit from a subroutine, +// restoring return address and static registers, and adjusting +// stack +// + +def Restore16: + FI8_SVRS16<0b1, (outs), (ins variable_ops), + "", [], II_RESTORE >, MayLoad { + let isCodeGenOnly = 1; + let Defs = [SP]; + let Uses = [SP]; +} + + +def RestoreX16: + FI8_SVRS16<0b1, (outs), (ins variable_ops), + "", [], II_RESTORE >, MayLoad { + let isCodeGenOnly = 1; + let Defs = [SP]; + let Uses = [SP]; +} + +// +// Format: SAVE {ra,}{s0/s1/s0-1,}{framesize} (All arguments are optional) +// MAXIS16e +// Purpose: Save Registers and Set Up Stack Frame +// To set up a stack frame on entry to a subroutine, +// saving return address and static registers, and adjusting stack +// +def Save16: + FI8_SVRS16<0b1, (outs), (ins variable_ops), + "", [], II_SAVE >, MayStore { + let isCodeGenOnly = 1; + let Uses = [SP]; + let Defs = [SP]; +} + +def SaveX16: + FI8_SVRS16<0b1, (outs), (ins variable_ops), + "", [], II_SAVE >, MayStore { + let isCodeGenOnly = 1; + let Uses = [SP]; + let Defs = [SP]; +} +// +// Format: SB ry, offset(rx) MAXIS16e +// Purpose: Store Byte (Extended) +// To store a byte to memory. +// +def SbRxRyOffMemX16: + FEXT_RRI16_mem2_ins<0b11000, "sb", mem16, II_SB>, MayStore; + +// +// Format: SEB rx MAXIS16e +// Purpose: Sign-Extend Byte +// Sign-extend least significant byte in register rx. +// +def SebRx16 + : FRR_SF16_ins<0b10001, 0b100, "seb", IIM16Alu>; + +// +// Format: SEH rx MAXIS16e +// Purpose: Sign-Extend Halfword +// Sign-extend least significant word in register rx. +// +def SehRx16 + : FRR_SF16_ins<0b10001, 0b101, "seh", IIM16Alu>; + +// +// The Sel(T) instructions are pseudos +// T means that they use T8 implicitly. +// +// +// Format: SelBeqZ rd, rs, rt +// Purpose: if rt==0, do nothing +// else rs = rt +// +def SelBeqZ: Sel<"beqz">; + +// +// Format: SelTBteqZCmp rd, rs, rl, rr +// Purpose: b = Cmp rl, rr. +// If b==0 then do nothing. +// if b!=0 then rd = rs +// +def SelTBteqZCmp: SelT<"bteqz", "cmp">; + +// +// Format: SelTBteqZCmpi rd, rs, rl, rr +// Purpose: b = Cmpi rl, imm. +// If b==0 then do nothing. +// if b!=0 then rd = rs +// +def SelTBteqZCmpi: SeliT<"bteqz", "cmpi">; + +// +// Format: SelTBteqZSlt rd, rs, rl, rr +// Purpose: b = Slt rl, rr. +// If b==0 then do nothing. +// if b!=0 then rd = rs +// +def SelTBteqZSlt: SelT<"bteqz", "slt">; + +// +// Format: SelTBteqZSlti rd, rs, rl, rr +// Purpose: b = Slti rl, imm. +// If b==0 then do nothing. +// if b!=0 then rd = rs +// +def SelTBteqZSlti: SeliT<"bteqz", "slti">; + +// +// Format: SelTBteqZSltu rd, rs, rl, rr +// Purpose: b = Sltu rl, rr. +// If b==0 then do nothing. +// if b!=0 then rd = rs +// +def SelTBteqZSltu: SelT<"bteqz", "sltu">; + +// +// Format: SelTBteqZSltiu rd, rs, rl, rr +// Purpose: b = Sltiu rl, imm. +// If b==0 then do nothing. +// if b!=0 then rd = rs +// +def SelTBteqZSltiu: SeliT<"bteqz", "sltiu">; + +// +// Format: SelBnez rd, rs, rt +// Purpose: if rt!=0, do nothing +// else rs = rt +// +def SelBneZ: Sel<"bnez">; + +// +// Format: SelTBtneZCmp rd, rs, rl, rr +// Purpose: b = Cmp rl, rr. +// If b!=0 then do nothing. +// if b0=0 then rd = rs +// +def SelTBtneZCmp: SelT<"btnez", "cmp">; + +// +// Format: SelTBtnezCmpi rd, rs, rl, rr +// Purpose: b = Cmpi rl, imm. +// If b!=0 then do nothing. +// if b==0 then rd = rs +// +def SelTBtneZCmpi: SeliT<"btnez", "cmpi">; + +// +// Format: SelTBtneZSlt rd, rs, rl, rr +// Purpose: b = Slt rl, rr. +// If b!=0 then do nothing. +// if b==0 then rd = rs +// +def SelTBtneZSlt: SelT<"btnez", "slt">; + +// +// Format: SelTBtneZSlti rd, rs, rl, rr +// Purpose: b = Slti rl, imm. +// If b!=0 then do nothing. +// if b==0 then rd = rs +// +def SelTBtneZSlti: SeliT<"btnez", "slti">; + +// +// Format: SelTBtneZSltu rd, rs, rl, rr +// Purpose: b = Sltu rl, rr. +// If b!=0 then do nothing. +// if b==0 then rd = rs +// +def SelTBtneZSltu: SelT<"btnez", "sltu">; + +// +// Format: SelTBtneZSltiu rd, rs, rl, rr +// Purpose: b = Slti rl, imm. +// If b!=0 then do nothing. +// if b==0 then rd = rs +// +def SelTBtneZSltiu: SeliT<"btnez", "sltiu">; +// +// +// Format: SH ry, offset(rx) MAXIS16e +// Purpose: Store Halfword (Extended) +// To store a halfword to memory. +// +def ShRxRyOffMemX16: + FEXT_RRI16_mem2_ins<0b11001, "sh", mem16, II_SH>, MayStore; + +// +// Format: SLL rx, ry, sa MAXIS16e +// Purpose: Shift Word Left Logical (Extended) +// To execute a left-shift of a word by a fixed number of bits-0 to 31 bits. +// +def SllX16: FEXT_SHIFT16_ins<0b00, "sll", IIM16Alu>; + +// +// Format: SLLV ry, rx MAXIS16e +// Purpose: Shift Word Left Logical Variable +// To execute a left-shift of a word by a variable number of bits. +// +def SllvRxRy16 : FRxRxRy16_ins<0b00100, "sllv", IIM16Alu>; + +// Format: SLTI rx, immediate MAXIS16e +// Purpose: Set on Less Than Immediate +// To record the result of a less-than comparison with a constant. +// +// +def SltiRxImm16: FRI16R_ins<0b01010, "slti", IIM16Alu> { + let Defs = [T8]; +} + +// +// Format: SLTI rx, immediate MAXIS16e +// Purpose: Set on Less Than Immediate (Extended) +// To record the result of a less-than comparison with a constant. +// +// +def SltiRxImmX16: FEXT_RI16R_ins<0b01010, "slti", IIM16Alu> { + let Defs = [T8]; +} + +def SltiCCRxImmX16: FEXT_CCRXI16_ins<"slti">; + +// Format: SLTIU rx, immediate MAXIS16e +// Purpose: Set on Less Than Immediate Unsigned +// To record the result of a less-than comparison with a constant. +// +// +def SltiuRxImm16: FRI16R_ins<0b01011, "sltiu", IIM16Alu> { + let Defs = [T8]; +} + +// +// Format: SLTI rx, immediate MAXIS16e +// Purpose: Set on Less Than Immediate Unsigned (Extended) +// To record the result of a less-than comparison with a constant. +// +// +def SltiuRxImmX16: FEXT_RI16R_ins<0b01011, "sltiu", IIM16Alu> { + let Defs = [T8]; +} +// +// Format: SLTIU rx, immediate MAXIS16e +// Purpose: Set on Less Than Immediate Unsigned (Extended) +// To record the result of a less-than comparison with a constant. +// +def SltiuCCRxImmX16: FEXT_CCRXI16_ins<"sltiu">; + +// +// Format: SLT rx, ry MAXIS16e +// Purpose: Set on Less Than +// To record the result of a less-than comparison. +// +def SltRxRy16: FRR16R_ins<0b00010, "slt", IIM16Alu>{ + let Defs = [T8]; +} + +def SltCCRxRy16: FCCRR16_ins<"slt">; + +// Format: SLTU rx, ry MAXIS16e +// Purpose: Set on Less Than Unsigned +// To record the result of an unsigned less-than comparison. +// +def SltuRxRy16: FRR16R_ins<0b00011, "sltu", IIM16Alu>{ + let Defs = [T8]; +} + +def SltuRxRyRz16: FRRTR16_ins<"sltu"> { + let isCodeGenOnly=1; + let Defs = [T8]; +} + + +def SltuCCRxRy16: FCCRR16_ins<"sltu">; +// +// Format: SRAV ry, rx MAXIS16e +// Purpose: Shift Word Right Arithmetic Variable +// To execute an arithmetic right-shift of a word by a variable +// number of bits. +// +def SravRxRy16: FRxRxRy16_ins<0b00111, "srav", IIM16Alu>; + + +// +// Format: SRA rx, ry, sa MAXIS16e +// Purpose: Shift Word Right Arithmetic (Extended) +// To execute an arithmetic right-shift of a word by a fixed +// number of bits-1 to 8 bits. +// +def SraX16: FEXT_SHIFT16_ins<0b11, "sra", IIM16Alu>; + + +// +// Format: SRLV ry, rx MAXIS16e +// Purpose: Shift Word Right Logical Variable +// To execute a logical right-shift of a word by a variable +// number of bits. +// +def SrlvRxRy16: FRxRxRy16_ins<0b00110, "srlv", IIM16Alu>; + + +// +// Format: SRL rx, ry, sa MAXIS16e +// Purpose: Shift Word Right Logical (Extended) +// To execute a logical right-shift of a word by a fixed +// number of bits-1 to 31 bits. +// +def SrlX16: FEXT_SHIFT16_ins<0b10, "srl", IIM16Alu>; + +// +// Format: SUBU rz, rx, ry MAXIS16e +// Purpose: Subtract Unsigned Word +// To subtract 32-bit integers +// +def SubuRxRyRz16: FRRR16_ins<0b11, "subu", IIM16Alu>, ArithLogic16Defs<0>; + +// +// Format: SW ry, offset(rx) MAXIS16e +// Purpose: Store Word (Extended) +// To store a word to memory. +// +def SwRxRyOffMemX16: FEXT_RRI16_mem2_ins<0b11011, "sw", mem16, II_SW>, MayStore; + +// +// Format: SW rx, offset(sp) MAXIS16e +// Purpose: Store Word rx (SP-Relative) +// To store an SP-relative word to memory. +// +def SwRxSpImmX16: FEXT_RRI16_mem2_ins<0b11010, "sw", mem16sp, II_SW>, MayStore; + +// +// +// Format: XOR rx, ry MAXIS16e +// Purpose: Xor +// To do a bitwise logical XOR. +// +def XorRxRxRy16: FRxRxRy16_ins<0b01110, "xor", IIM16Alu>, ArithLogic16Defs<1>; + +class Maxis16Pat : Pat { + let Predicates = [InMaxis16Mode]; +} + +// Unary Arith/Logic +// +class ArithLogicU_pat : + Maxis16Pat<(OpNode CPU16Regs:$r), + (I CPU16Regs:$r)>; + +def: ArithLogicU_pat; +def: ArithLogicU_pat; + +class ArithLogic16_pat : + Maxis16Pat<(OpNode CPU16Regs:$l, CPU16Regs:$r), + (I CPU16Regs:$l, CPU16Regs:$r)>; + +def: ArithLogic16_pat; +def: ArithLogic16_pat; +def: ArithLogic16_pat; +def: ArithLogic16_pat; +def: ArithLogic16_pat; +def: ArithLogic16_pat; + +// Arithmetic and logical instructions with 2 register operands. + +class ArithLogicI16_pat : + Maxis16Pat<(OpNode CPU16Regs:$in, imm_type:$imm), + (I CPU16Regs:$in, imm_type:$imm)>; + +def: ArithLogicI16_pat; +def: ArithLogicI16_pat; +def: ArithLogicI16_pat; +def: ArithLogicI16_pat; +def: ArithLogicI16_pat; + +class shift_rotate_reg16_pat : + Maxis16Pat<(OpNode CPU16Regs:$r, CPU16Regs:$ra), + (I CPU16Regs:$r, CPU16Regs:$ra)>; + +def: shift_rotate_reg16_pat; +def: shift_rotate_reg16_pat; +def: shift_rotate_reg16_pat; + +class LoadM16_pat : + Maxis16Pat<(OpNode Addr:$addr), (I Addr:$addr)>; + +def: LoadM16_pat; +def: LoadM16_pat; +def: LoadM16_pat; +def: LoadM16_pat; +def: LoadM16_pat; + +class StoreM16_pat : + Maxis16Pat<(OpNode CPU16Regs:$r, Addr:$addr), (I CPU16Regs:$r, Addr:$addr)>; + +def: StoreM16_pat; +def: StoreM16_pat; +def: StoreM16_pat; + +// Unconditional branch +class UncondBranch16_pat: + Maxis16Pat<(OpNode bb:$imm16), (I bb:$imm16)> { + let Predicates = [InMaxis16Mode]; + } + +def : Maxis16Pat<(MaxisJmpLink (i32 tglobaladdr:$dst)), + (Jal16 tglobaladdr:$dst)>; + +def : Maxis16Pat<(MaxisJmpLink (i32 texternalsym:$dst)), + (Jal16 texternalsym:$dst)>; + +// Indirect branch +def: Maxis16Pat<(brind CPU16Regs:$rs), (JrcRx16 CPU16Regs:$rs)> { + // Ensure that the addition of MAXIS32r6/MAXIS64r6 support does not change + // MAXIS16's behaviour. + let AddedComplexity = 1; +} + +// Jump and Link (Call) +let isCall=1, hasDelaySlot=0 in +def JumpLinkReg16: + FRR16_JALRC<0, 0, 0, (outs), (ins CPU16Regs:$rs), + "jalrc\t$rs", [(MaxisJmpLink CPU16Regs:$rs)], II_JALRC> { + let Defs = [RA]; +} + +// Maxis16 pseudos +let isReturn=1, isTerminator=1, hasDelaySlot=1, isBarrier=1, hasCtrlDep=1, + hasExtraSrcRegAllocReq = 1 in +def RetRA16 : MaxisPseudo16<(outs), (ins), "", [(MaxisRet)]>; + + +// setcc patterns + +class SetCC_R16: + Maxis16Pat<(cond_op CPU16Regs:$rx, CPU16Regs:$ry), + (I CPU16Regs:$rx, CPU16Regs:$ry)>; + +class SetCC_I16: + Maxis16Pat<(cond_op CPU16Regs:$rx, imm_type:$imm16), + (I CPU16Regs:$rx, imm_type:$imm16)>; + + +def: Maxis16Pat<(i32 addr16sp:$addr), (AddiuRxRyOffMemX16 addr16sp:$addr)>; + + +// Large (>16 bit) immediate loads +def : Maxis16Pat<(i32 imm:$imm), (LwConstant32 imm:$imm, -1)>; + +// Carry MaxisPatterns +def : Maxis16Pat<(subc CPU16Regs:$lhs, CPU16Regs:$rhs), + (SubuRxRyRz16 CPU16Regs:$lhs, CPU16Regs:$rhs)>; +def : Maxis16Pat<(addc CPU16Regs:$lhs, CPU16Regs:$rhs), + (AdduRxRyRz16 CPU16Regs:$lhs, CPU16Regs:$rhs)>; +def : Maxis16Pat<(addc CPU16Regs:$src, immSExt16:$imm), + (AddiuRxRxImmX16 CPU16Regs:$src, imm:$imm)>; + +// +// Some branch conditional patterns are not generated by llvm at this time. +// Some are for seemingly arbitrary reasons not used: i.e. with signed number +// comparison they are used and for unsigned a different pattern is used. +// I am pushing upstream from the full maxis16 port and it seemed that I needed +// these earlier and the maxis32 port has these but now I cannot create test +// cases that use these patterns. While I sort this all out I will leave these +// extra patterns commented out and if I can be sure they are really not used, +// I will delete the code. I don't want to check the code in uncommented without +// a valid test case. In some cases, the compiler is generating patterns with +// setcc instead and earlier I had implemented setcc first so may have masked +// the problem. The setcc variants are suboptimal for maxis16 so I may wantto +// figure out how to enable the brcond patterns or else possibly new +// combinations of of brcond and setcc. +// +// +// bcond-seteq +// +def: Maxis16Pat + <(brcond (i32 (seteq CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), + (BteqzT8CmpX16 CPU16Regs:$rx, CPU16Regs:$ry, bb:$imm16) + >; + + +def: Maxis16Pat + <(brcond (i32 (seteq CPU16Regs:$rx, immZExt16:$imm)), bb:$targ16), + (BteqzT8CmpiX16 CPU16Regs:$rx, immSExt16:$imm, bb:$targ16) + >; + +def: Maxis16Pat + <(brcond (i32 (seteq CPU16Regs:$rx, 0)), bb:$targ16), + (BeqzRxImm16 CPU16Regs:$rx, bb:$targ16) + >; + +// +// bcond-setgt (do we need to have this pair of setlt, setgt??) +// +def: Maxis16Pat + <(brcond (i32 (setgt CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), + (BtnezT8SltX16 CPU16Regs:$ry, CPU16Regs:$rx, bb:$imm16) + >; + +// +// bcond-setge +// +def: Maxis16Pat + <(brcond (i32 (setge CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), + (BteqzT8SltX16 CPU16Regs:$rx, CPU16Regs:$ry, bb:$imm16) + >; + +// +// never called because compiler transforms a >= k to a > (k-1) +def: Maxis16Pat + <(brcond (i32 (setge CPU16Regs:$rx, immSExt16:$imm)), bb:$imm16), + (BteqzT8SltiX16 CPU16Regs:$rx, immSExt16:$imm, bb:$imm16) + >; + +// +// bcond-setlt +// +def: Maxis16Pat + <(brcond (i32 (setlt CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), + (BtnezT8SltX16 CPU16Regs:$rx, CPU16Regs:$ry, bb:$imm16) + >; + +def: Maxis16Pat + <(brcond (i32 (setlt CPU16Regs:$rx, immSExt16:$imm)), bb:$imm16), + (BtnezT8SltiX16 CPU16Regs:$rx, immSExt16:$imm, bb:$imm16) + >; + +// +// bcond-setle +// +def: Maxis16Pat + <(brcond (i32 (setle CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), + (BteqzT8SltX16 CPU16Regs:$ry, CPU16Regs:$rx, bb:$imm16) + >; + +// +// bcond-setne +// +def: Maxis16Pat + <(brcond (i32 (setne CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), + (BtnezT8CmpX16 CPU16Regs:$rx, CPU16Regs:$ry, bb:$imm16) + >; + +def: Maxis16Pat + <(brcond (i32 (setne CPU16Regs:$rx, immZExt16:$imm)), bb:$targ16), + (BtnezT8CmpiX16 CPU16Regs:$rx, immSExt16:$imm, bb:$targ16) + >; + +def: Maxis16Pat + <(brcond (i32 (setne CPU16Regs:$rx, 0)), bb:$targ16), + (BnezRxImm16 CPU16Regs:$rx, bb:$targ16) + >; + +// +// This needs to be there but I forget which code will generate it +// +def: Maxis16Pat + <(brcond CPU16Regs:$rx, bb:$targ16), + (BnezRxImm16 CPU16Regs:$rx, bb:$targ16) + >; + +// + +// +// bcond-setugt +// +//def: Maxis16Pat +// <(brcond (i32 (setugt CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), +// (BtnezT8SltuX16 CPU16Regs:$ry, CPU16Regs:$rx, bb:$imm16) +// >; + +// +// bcond-setuge +// +//def: Maxis16Pat +// <(brcond (i32 (setuge CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), +// (BteqzT8SltuX16 CPU16Regs:$rx, CPU16Regs:$ry, bb:$imm16) +// >; + + +// +// bcond-setult +// +//def: Maxis16Pat +// <(brcond (i32 (setult CPU16Regs:$rx, CPU16Regs:$ry)), bb:$imm16), +// (BtnezT8SltuX16 CPU16Regs:$rx, CPU16Regs:$ry, bb:$imm16) +// >; + +def: UncondBranch16_pat; + +// Small immediates +def: Maxis16Pat<(i32 immSExt16:$in), + (AddiuRxRxImmX16 (MoveR3216 ZERO), immSExt16:$in)>; + +def: Maxis16Pat<(i32 immZExt16:$in), (LiRxImmX16 immZExt16:$in)>; + +// +// MaxisDivRem +// +def: Maxis16Pat + <(MaxisDivRem16 CPU16Regs:$rx, CPU16Regs:$ry), + (DivRxRy16 CPU16Regs:$rx, CPU16Regs:$ry)>; + +// +// MaxisDivRemU +// +def: Maxis16Pat + <(MaxisDivRemU16 CPU16Regs:$rx, CPU16Regs:$ry), + (DivuRxRy16 CPU16Regs:$rx, CPU16Regs:$ry)>; + +// signed a,b +// x = (a>=b)?x:y +// +// if !(a < b) x = y +// +def : Maxis16Pat<(select (i32 (setge CPU16Regs:$a, CPU16Regs:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBteqZSlt CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$a, CPU16Regs:$b)>; + +// signed a,b +// x = (a>b)?x:y +// +// if (b < a) x = y +// +def : Maxis16Pat<(select (i32 (setgt CPU16Regs:$a, CPU16Regs:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBtneZSlt CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$b, CPU16Regs:$a)>; + +// unsigned a,b +// x = (a>=b)?x:y +// +// if !(a < b) x = y; +// +def : Maxis16Pat< + (select (i32 (setuge CPU16Regs:$a, CPU16Regs:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBteqZSltu CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$a, CPU16Regs:$b)>; + +// unsigned a,b +// x = (a>b)?x:y +// +// if (b < a) x = y +// +def : Maxis16Pat<(select (i32 (setugt CPU16Regs:$a, CPU16Regs:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBtneZSltu CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$b, CPU16Regs:$a)>; + +// signed +// x = (a >= k)?x:y +// due to an llvm optimization, i don't think that this will ever +// be used. This is transformed into x = (a > k-1)?x:y +// +// + +//def : Maxis16Pat< +// (select (i32 (setge CPU16Regs:$lhs, immSExt16:$rhs)), +// CPU16Regs:$T, CPU16Regs:$F), +// (SelTBteqZSlti CPU16Regs:$T, CPU16Regs:$F, +// CPU16Regs:$lhs, immSExt16:$rhs)>; + +//def : Maxis16Pat< +// (select (i32 (setuge CPU16Regs:$lhs, immSExt16:$rhs)), +// CPU16Regs:$T, CPU16Regs:$F), +// (SelTBteqZSltiu CPU16Regs:$T, CPU16Regs:$F, +// CPU16Regs:$lhs, immSExt16:$rhs)>; + +// signed +// x = (a < k)?x:y +// +// if !(a < k) x = y; +// +def : Maxis16Pat< + (select (i32 (setlt CPU16Regs:$a, immSExt16:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBtneZSlti CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$a, immSExt16:$b)>; + + +// +// +// signed +// x = (a <= b)? x : y +// +// if (b < a) x = y +// +def : Maxis16Pat<(select (i32 (setle CPU16Regs:$a, CPU16Regs:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBteqZSlt CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$b, CPU16Regs:$a)>; + +// +// unnsigned +// x = (a <= b)? x : y +// +// if (b < a) x = y +// +def : Maxis16Pat<(select (i32 (setule CPU16Regs:$a, CPU16Regs:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBteqZSltu CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$b, CPU16Regs:$a)>; + +// +// signed/unsigned +// x = (a == b)? x : y +// +// if (a != b) x = y +// +def : Maxis16Pat<(select (i32 (seteq CPU16Regs:$a, CPU16Regs:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBteqZCmp CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$b, CPU16Regs:$a)>; + +// +// signed/unsigned +// x = (a == 0)? x : y +// +// if (a != 0) x = y +// +def : Maxis16Pat<(select (i32 (seteq CPU16Regs:$a, 0)), + CPU16Regs:$x, CPU16Regs:$y), + (SelBeqZ CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$a)>; + + +// +// signed/unsigned +// x = (a == k)? x : y +// +// if (a != k) x = y +// +def : Maxis16Pat<(select (i32 (seteq CPU16Regs:$a, immZExt16:$k)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBteqZCmpi CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$a, immZExt16:$k)>; + + +// +// signed/unsigned +// x = (a != b)? x : y +// +// if (a == b) x = y +// +// +def : Maxis16Pat<(select (i32 (setne CPU16Regs:$a, CPU16Regs:$b)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBtneZCmp CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$b, CPU16Regs:$a)>; + +// +// signed/unsigned +// x = (a != 0)? x : y +// +// if (a == 0) x = y +// +def : Maxis16Pat<(select (i32 (setne CPU16Regs:$a, 0)), + CPU16Regs:$x, CPU16Regs:$y), + (SelBneZ CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$a)>; + +// signed/unsigned +// x = (a)? x : y +// +// if (!a) x = y +// +def : Maxis16Pat<(select CPU16Regs:$a, + CPU16Regs:$x, CPU16Regs:$y), + (SelBneZ CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$a)>; + + +// +// signed/unsigned +// x = (a != k)? x : y +// +// if (a == k) x = y +// +def : Maxis16Pat<(select (i32 (setne CPU16Regs:$a, immZExt16:$k)), + CPU16Regs:$x, CPU16Regs:$y), + (SelTBtneZCmpi CPU16Regs:$x, CPU16Regs:$y, + CPU16Regs:$a, immZExt16:$k)>; + +// +// When writing C code to test setxx these patterns, +// some will be transformed into +// other things. So we test using C code but using -O3 and -O0 +// +// seteq +// +def : Maxis16Pat + <(seteq CPU16Regs:$lhs,CPU16Regs:$rhs), + (SltiuCCRxImmX16 (XorRxRxRy16 CPU16Regs:$lhs, CPU16Regs:$rhs), 1)>; + +def : Maxis16Pat + <(seteq CPU16Regs:$lhs, 0), + (SltiuCCRxImmX16 CPU16Regs:$lhs, 1)>; + + +// +// setge +// + +def: Maxis16Pat + <(setge CPU16Regs:$lhs, CPU16Regs:$rhs), + (XorRxRxRy16 (SltCCRxRy16 CPU16Regs:$lhs, CPU16Regs:$rhs), + (LiRxImmX16 1))>; + +// +// For constants, llvm transforms this to: +// x > (k - 1) and then reverses the operands to use setlt. So this pattern +// is not used now by the compiler. (Presumably checking that k-1 does not +// overflow). The compiler never uses this at the current time, due to +// other optimizations. +// +//def: Maxis16Pat +// <(setge CPU16Regs:$lhs, immSExt16:$rhs), +// (XorRxRxRy16 (SltiCCRxImmX16 CPU16Regs:$lhs, immSExt16:$rhs), +// (LiRxImmX16 1))>; + +// This catches the x >= -32768 case by transforming it to x > -32769 +// +def: Maxis16Pat + <(setgt CPU16Regs:$lhs, -32769), + (XorRxRxRy16 (SltiCCRxImmX16 CPU16Regs:$lhs, -32768), + (LiRxImmX16 1))>; + +// +// setgt +// +// + +def: Maxis16Pat + <(setgt CPU16Regs:$lhs, CPU16Regs:$rhs), + (SltCCRxRy16 CPU16Regs:$rhs, CPU16Regs:$lhs)>; + +// +// setle +// +def: Maxis16Pat + <(setle CPU16Regs:$lhs, CPU16Regs:$rhs), + (XorRxRxRy16 (SltCCRxRy16 CPU16Regs:$rhs, CPU16Regs:$lhs), (LiRxImm16 1))>; + +// +// setlt +// +def: SetCC_R16; + +def: SetCC_I16; + +// +// setne +// +def : Maxis16Pat + <(setne CPU16Regs:$lhs,CPU16Regs:$rhs), + (SltuCCRxRy16 (LiRxImmX16 0), + (XorRxRxRy16 CPU16Regs:$lhs, CPU16Regs:$rhs))>; + + +// +// setuge +// +def: Maxis16Pat + <(setuge CPU16Regs:$lhs, CPU16Regs:$rhs), + (XorRxRxRy16 (SltuCCRxRy16 CPU16Regs:$lhs, CPU16Regs:$rhs), + (LiRxImmX16 1))>; + +// this pattern will never be used because the compiler will transform +// x >= k to x > (k - 1) and then use SLT +// +//def: Maxis16Pat +// <(setuge CPU16Regs:$lhs, immZExt16:$rhs), +// (XorRxRxRy16 (SltiuCCRxImmX16 CPU16Regs:$lhs, immZExt16:$rhs), +// (LiRxImmX16 1))>; + +// +// setugt +// +def: Maxis16Pat + <(setugt CPU16Regs:$lhs, CPU16Regs:$rhs), + (SltuCCRxRy16 CPU16Regs:$rhs, CPU16Regs:$lhs)>; + +// +// setule +// +def: Maxis16Pat + <(setule CPU16Regs:$lhs, CPU16Regs:$rhs), + (XorRxRxRy16 (SltuCCRxRy16 CPU16Regs:$rhs, CPU16Regs:$lhs), (LiRxImmX16 1))>; + +// +// setult +// +def: SetCC_R16; + +def: SetCC_I16; + +def: Maxis16Pat<(add CPU16Regs:$hi, (MaxisLo tglobaladdr:$lo)), + (AddiuRxRxImmX16 CPU16Regs:$hi, tglobaladdr:$lo)>; + +// hi/lo relocs +def : Maxis16Pat<(MaxisHi tblockaddress:$in), + (SllX16 (LiRxImmX16 tblockaddress:$in), 16)>; +def : Maxis16Pat<(MaxisHi tglobaladdr:$in), + (SllX16 (LiRxImmX16 tglobaladdr:$in), 16)>; +def : Maxis16Pat<(MaxisHi tjumptable:$in), + (SllX16 (LiRxImmX16 tjumptable:$in), 16)>; +def : Maxis16Pat<(MaxisHi tglobaltlsaddr:$in), + (SllX16 (LiRxImmX16 tglobaltlsaddr:$in), 16)>; + +def : Maxis16Pat<(MaxisLo tblockaddress:$in), (LiRxImmX16 tblockaddress:$in)>; + +// wrapper_pic +class Wrapper16Pat: + Maxis16Pat<(MaxisWrapper RC:$gp, node:$in), + (ADDiuOp RC:$gp, node:$in)>; + + +def : Wrapper16Pat; +def : Wrapper16Pat; + +def : Maxis16Pat<(i32 (extloadi8 addr16:$src)), + (LbuRxRyOffMemX16 addr16:$src)>; +def : Maxis16Pat<(i32 (extloadi16 addr16:$src)), + (LhuRxRyOffMemX16 addr16:$src)>; + +def: Maxis16Pat<(trap), (Break16)>; + +def : Maxis16Pat<(sext_inreg CPU16Regs:$val, i8), + (SebRx16 CPU16Regs:$val)>; + +def : Maxis16Pat<(sext_inreg CPU16Regs:$val, i16), + (SehRx16 CPU16Regs:$val)>; + +def GotPrologue16: + MaxisPseudo16< + (outs CPU16Regs:$rh, CPU16Regs:$rl), + (ins simm16:$immHi, simm16:$immLo), + "li\t$rh, $immHi\n\taddiu\t$rl, $$pc, $immLo\n ",[]> ; + +// An operand for the CONSTPOOL_ENTRY pseudo-instruction. +def cpinst_operand : Operand { + // let PrintMethod = "printCPInstOperand"; +} + +// CONSTPOOL_ENTRY - This instruction represents a floating constant pool in +// the function. The first operand is the ID# for this instruction, the second +// is the index into the MachineConstantPool that this is, the third is the +// size in bytes of this constant pool entry. +// +let hasSideEffects = 0, isNotDuplicable = 1 in +def CONSTPOOL_ENTRY : +MaxisPseudo16<(outs), (ins cpinst_operand:$instid, cpinst_operand:$cpidx, + i32imm:$size), "foo", []>; + diff --git a/lib/Target/Maxis/Maxis16RegisterInfo.cpp b/lib/Target/Maxis/Maxis16RegisterInfo.cpp new file mode 100644 index 00000000..3f891807 --- /dev/null +++ b/lib/Target/Maxis/Maxis16RegisterInfo.cpp @@ -0,0 +1,148 @@ +//===-- Maxis16RegisterInfo.cpp - MAXIS16 Register Information --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the MAXIS16 implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "Maxis16RegisterInfo.h" +#include "Maxis.h" +#include "Maxis16InstrInfo.h" +#include "MaxisInstrInfo.h" +#include "MaxisMachineFunction.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Function.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" +#include "llvm/Target/TargetOptions.h" + +using namespace llvm; + +#define DEBUG_TYPE "maxis16-registerinfo" + +Maxis16RegisterInfo::Maxis16RegisterInfo() : MaxisRegisterInfo() {} + +bool Maxis16RegisterInfo::requiresRegisterScavenging + (const MachineFunction &MF) const { + return false; +} +bool Maxis16RegisterInfo::requiresFrameIndexScavenging + (const MachineFunction &MF) const { + return false; +} + +bool Maxis16RegisterInfo::useFPForScavengingIndex + (const MachineFunction &MF) const { + return false; +} + +bool Maxis16RegisterInfo::saveScavengerRegister + (MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + MachineBasicBlock::iterator &UseMI, + const TargetRegisterClass *RC, + unsigned Reg) const { + DebugLoc DL; + const TargetInstrInfo &TII = *MBB.getParent()->getSubtarget().getInstrInfo(); + TII.copyPhysReg(MBB, I, DL, Maxis::T0, Reg, true); + TII.copyPhysReg(MBB, UseMI, DL, Reg, Maxis::T0, true); + return true; +} + +const TargetRegisterClass * +Maxis16RegisterInfo::intRegClass(unsigned Size) const { + assert(Size == 4); + return &Maxis::CPU16RegsRegClass; +} + +void Maxis16RegisterInfo::eliminateFI(MachineBasicBlock::iterator II, + unsigned OpNo, int FrameIndex, + uint64_t StackSize, + int64_t SPOffset) const { + MachineInstr &MI = *II; + MachineFunction &MF = *MI.getParent()->getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + + const std::vector &CSI = MFI.getCalleeSavedInfo(); + int MinCSFI = 0; + int MaxCSFI = -1; + + if (CSI.size()) { + MinCSFI = CSI[0].getFrameIdx(); + MaxCSFI = CSI[CSI.size() - 1].getFrameIdx(); + } + + // The following stack frame objects are always + // referenced relative to $sp: + // 1. Outgoing arguments. + // 2. Pointer to dynamically allocated stack space. + // 3. Locations for callee-saved registers. + // Everything else is referenced relative to whatever register + // getFrameRegister() returns. + unsigned FrameReg; + + if (FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI) + FrameReg = Maxis::SP; + else { + const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering(); + if (TFI->hasFP(MF)) { + FrameReg = Maxis::S0; + } + else { + if ((MI.getNumOperands()> OpNo+2) && MI.getOperand(OpNo+2).isReg()) + FrameReg = MI.getOperand(OpNo+2).getReg(); + else + FrameReg = Maxis::SP; + } + } + // Calculate final offset. + // - There is no need to change the offset if the frame object + // is one of the + // following: an outgoing argument, pointer to a dynamically allocated + // stack space or a $gp restore location, + // - If the frame object is any of the following, + // its offset must be adjusted + // by adding the size of the stack: + // incoming argument, callee-saved register location or local variable. + int64_t Offset; + bool IsKill = false; + Offset = SPOffset + (int64_t)StackSize; + Offset += MI.getOperand(OpNo + 1).getImm(); + + + DEBUG(errs() << "Offset : " << Offset << "\n" << "<--------->\n"); + + if (!MI.isDebugValue() && + !Maxis16InstrInfo::validImmediate(MI.getOpcode(), FrameReg, Offset)) { + MachineBasicBlock &MBB = *MI.getParent(); + DebugLoc DL = II->getDebugLoc(); + unsigned NewImm; + const Maxis16InstrInfo &TII = + *static_cast(MF.getSubtarget().getInstrInfo()); + FrameReg = TII.loadImmediate(FrameReg, Offset, MBB, II, DL, NewImm); + Offset = SignExtend64<16>(NewImm); + IsKill = true; + } + MI.getOperand(OpNo).ChangeToRegister(FrameReg, false, false, IsKill); + MI.getOperand(OpNo + 1).ChangeToImmediate(Offset); + + +} diff --git a/lib/Target/Maxis/Maxis16RegisterInfo.h b/lib/Target/Maxis/Maxis16RegisterInfo.h new file mode 100644 index 00000000..dfdbd07d --- /dev/null +++ b/lib/Target/Maxis/Maxis16RegisterInfo.h @@ -0,0 +1,48 @@ +//===-- Maxis16RegisterInfo.h - Maxis16 Register Information ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis16 implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXIS16REGISTERINFO_H +#define LLVM_LIB_TARGET_MAXIS_MAXIS16REGISTERINFO_H + +#include "MaxisRegisterInfo.h" + +namespace llvm { +class Maxis16InstrInfo; + +class Maxis16RegisterInfo : public MaxisRegisterInfo { +public: + Maxis16RegisterInfo(); + + bool requiresRegisterScavenging(const MachineFunction &MF) const override; + + bool requiresFrameIndexScavenging(const MachineFunction &MF) const override; + + bool useFPForScavengingIndex(const MachineFunction &MF) const override; + + bool saveScavengerRegister(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + MachineBasicBlock::iterator &UseMI, + const TargetRegisterClass *RC, + unsigned Reg) const override; + + const TargetRegisterClass *intRegClass(unsigned Size) const override; + +private: + void eliminateFI(MachineBasicBlock::iterator II, unsigned OpNo, + int FrameIndex, uint64_t StackSize, + int64_t SPOffset) const override; +}; + +} // end namespace llvm + +#endif diff --git a/lib/Target/Maxis/Maxis32r6InstrFormats.td b/lib/Target/Maxis/Maxis32r6InstrFormats.td new file mode 100644 index 00000000..b4a43b35 --- /dev/null +++ b/lib/Target/Maxis/Maxis32r6InstrFormats.td @@ -0,0 +1,578 @@ +//=- Maxis32r6InstrFormats.td - Maxis32r6 Instruction Formats -*- tablegen -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis32r6 instruction formats. +// +//===----------------------------------------------------------------------===// + +class R6MMR6Rel; + +def MaxisR62MicroMaxisR6 : InstrMapping { + let FilterClass = "R6MMR6Rel"; + // Instructions with the same BaseOpcode and isNVStore values form a row. + let RowFields = ["BaseOpcode"]; + // Instructions with the same predicate sense form a column. + let ColFields = ["Arch"]; + // The key column is the unpredicated instructions. + let KeyCol = ["maxisr6"]; + // Value columns are PredSense=true and PredSense=false + let ValueCols = [["maxisr6"], ["micromaxisr6"]]; +} + +class MaxisR6Arch { + string Arch = "maxisr6"; + string BaseOpcode = opstr; +} + +class MaxisR6Inst : MaxisInst<(outs), (ins), "", [], NoItinerary, FrmOther>, + PredicateControl { + let DecoderNamespace = "Maxis32r6_64r6"; + let EncodingPredicates = [HasStdEnc]; +} + +//===----------------------------------------------------------------------===// +// +// Field Values +// +//===----------------------------------------------------------------------===// + +class OPGROUP Val> { + bits<6> Value = Val; +} +def OPGROUP_COP0 : OPGROUP<0b010000>; +def OPGROUP_COP1 : OPGROUP<0b010001>; +def OPGROUP_COP2 : OPGROUP<0b010010>; +def OPGROUP_ADDI : OPGROUP<0b001000>; +def OPGROUP_AUI : OPGROUP<0b001111>; +def OPGROUP_BLEZ : OPGROUP<0b000110>; +def OPGROUP_BGTZ : OPGROUP<0b000111>; +def OPGROUP_BLEZL : OPGROUP<0b010110>; +def OPGROUP_BGTZL : OPGROUP<0b010111>; +def OPGROUP_DADDI : OPGROUP<0b011000>; +def OPGROUP_DAUI : OPGROUP<0b011101>; +def OPGROUP_PCREL : OPGROUP<0b111011>; +def OPGROUP_REGIMM : OPGROUP<0b000001>; +def OPGROUP_SPECIAL : OPGROUP<0b000000>; +// The spec occasionally names this value LL, LLD, SC, or SCD. +def OPGROUP_SPECIAL3 : OPGROUP<0b011111>; +// The spec names this constant LWC2, LDC2, SWC2, and SDC2 in different places. +def OPGROUP_COP2LDST : OPGROUP<0b010010>; + +class OPCODE2 Val> { + bits<2> Value = Val; +} +def OPCODE2_ADDIUPC : OPCODE2<0b00>; +def OPCODE2_LWPC : OPCODE2<0b01>; +def OPCODE2_LWUPC : OPCODE2<0b10>; + +class OPCODE3 Val> { + bits<3> Value = Val; +} +def OPCODE3_LDPC : OPCODE3<0b110>; + +class OPCODE5 Val> { + bits<5> Value = Val; +} +def OPCODE5_ALUIPC : OPCODE5<0b11111>; +def OPCODE5_AUIPC : OPCODE5<0b11110>; +def OPCODE5_DAHI : OPCODE5<0b00110>; +def OPCODE5_DATI : OPCODE5<0b11110>; +def OPCODE5_BC1EQZ : OPCODE5<0b01001>; +def OPCODE5_BC1NEZ : OPCODE5<0b01101>; +def OPCODE5_BC2EQZ : OPCODE5<0b01001>; +def OPCODE5_BC2NEZ : OPCODE5<0b01101>; +def OPCODE5_BGEZAL : OPCODE5<0b10001>; +// The next four constants are unnamed in the spec. These names are taken from +// the OPGROUP names they are used with. +def OPCODE5_LDC2 : OPCODE5<0b01110>; +def OPCODE5_LWC2 : OPCODE5<0b01010>; +def OPCODE5_SDC2 : OPCODE5<0b01111>; +def OPCODE5_SWC2 : OPCODE5<0b01011>; + +class OPCODE6 Val> { + bits<6> Value = Val; +} +def OPCODE6_ALIGN : OPCODE6<0b100000>; +def OPCODE6_DALIGN : OPCODE6<0b100100>; +def OPCODE6_BITSWAP : OPCODE6<0b100000>; +def OPCODE6_DBITSWAP : OPCODE6<0b100100>; +def OPCODE6_JALR : OPCODE6<0b001001>; +def OPCODE6_CACHE : OPCODE6<0b100101>; +def OPCODE6_PREF : OPCODE6<0b110101>; +// The next four constants are unnamed in the spec. These names are taken from +// the OPGROUP names they are used with. +def OPCODE6_LL : OPCODE6<0b110110>; +def OPCODE6_LLD : OPCODE6<0b110111>; +def OPCODE6_SC : OPCODE6<0b100110>; +def OPCODE6_SCD : OPCODE6<0b100111>; +def OPCODE6_CLO : OPCODE6<0b010001>; +def OPCODE6_CLZ : OPCODE6<0b010000>; +def OPCODE6_DCLO : OPCODE6<0b010011>; +def OPCODE6_DCLZ : OPCODE6<0b010010>; +def OPCODE6_LSA : OPCODE6<0b000101>; +def OPCODE6_DLSA : OPCODE6<0b010101>; +def OPCODE6_SDBBP : OPCODE6<0b001110>; + +class FIELD_FMT Val> { + bits<5> Value = Val; +} +def FIELD_FMT_S : FIELD_FMT<0b10000>; +def FIELD_FMT_D : FIELD_FMT<0b10001>; + +class FIELD_CMP_COND Val> { + bits<5> Value = Val; +} +// Note: The CMP_COND_FMT names differ from the C_COND_FMT names. +def FIELD_CMP_COND_AF : FIELD_CMP_COND<0b00000>; +def FIELD_CMP_COND_UN : FIELD_CMP_COND<0b00001>; +def FIELD_CMP_COND_EQ : FIELD_CMP_COND<0b00010>; +def FIELD_CMP_COND_UEQ : FIELD_CMP_COND<0b00011>; +def FIELD_CMP_COND_LT : FIELD_CMP_COND<0b00100>; +def FIELD_CMP_COND_ULT : FIELD_CMP_COND<0b00101>; +def FIELD_CMP_COND_LE : FIELD_CMP_COND<0b00110>; +def FIELD_CMP_COND_ULE : FIELD_CMP_COND<0b00111>; +def FIELD_CMP_COND_SAF : FIELD_CMP_COND<0b01000>; +def FIELD_CMP_COND_SUN : FIELD_CMP_COND<0b01001>; +def FIELD_CMP_COND_SEQ : FIELD_CMP_COND<0b01010>; +def FIELD_CMP_COND_SUEQ : FIELD_CMP_COND<0b01011>; +def FIELD_CMP_COND_SLT : FIELD_CMP_COND<0b01100>; +def FIELD_CMP_COND_SULT : FIELD_CMP_COND<0b01101>; +def FIELD_CMP_COND_SLE : FIELD_CMP_COND<0b01110>; +def FIELD_CMP_COND_SULE : FIELD_CMP_COND<0b01111>; + +class FIELD_CMP_FORMAT Val> { + bits<5> Value = Val; +} +def FIELD_CMP_FORMAT_S : FIELD_CMP_FORMAT<0b10100>; +def FIELD_CMP_FORMAT_D : FIELD_CMP_FORMAT<0b10101>; + +//===----------------------------------------------------------------------===// +// +// Disambiguators +// +//===----------------------------------------------------------------------===// +// +// Some encodings are ambiguous except by comparing field values. + +class DecodeDisambiguates { + string DecoderMethod = !strconcat("Decode", Name); +} + +class DecodeDisambiguatedBy : DecodeDisambiguates { + string DecoderNamespace = "Maxis32r6_64r6_Ambiguous"; +} + +//===----------------------------------------------------------------------===// +// +// Encoding Formats +// +//===----------------------------------------------------------------------===// + +class AUI_FM : MaxisR6Inst { + bits<5> rs; + bits<5> rt; + bits<16> imm; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_AUI.Value; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-0} = imm; +} + +class DAUI_FM : AUI_FM { + let Inst{31-26} = OPGROUP_DAUI.Value; +} + +class BAL_FM : MaxisR6Inst { + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_REGIMM.Value; + let Inst{25-21} = 0b00000; + let Inst{20-16} = OPCODE5_BGEZAL.Value; + let Inst{15-0} = offset; +} + +class COP0_EVP_DVP_FM sc> : MaxisR6Inst { + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_COP0.Value; + let Inst{25-21} = 0b01011; + let Inst{20-16} = rt; + let Inst{15-11} = 0b00000; + let Inst{10-6} = 0b00000; + let Inst{5} = sc; + let Inst{4-3} = 0b00; + let Inst{2-0} = 0b100; +} + +class COP1_2R_FM funct, FIELD_FMT Format> : MaxisR6Inst { + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_COP1.Value; + let Inst{25-21} = Format.Value; + let Inst{20-16} = 0b00000; + let Inst{15-11} = fs; + let Inst{10-6} = fd; + let Inst{5-0} = funct; +} + +class COP1_3R_FM funct, FIELD_FMT Format> : MaxisR6Inst { + bits<5> ft; + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_COP1.Value; + let Inst{25-21} = Format.Value; + let Inst{20-16} = ft; + let Inst{15-11} = fs; + let Inst{10-6} = fd; + let Inst{5-0} = funct; +} + +class COP1_BCCZ_FM : MaxisR6Inst { + bits<5> ft; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_COP1.Value; + let Inst{25-21} = Operation.Value; + let Inst{20-16} = ft; + let Inst{15-0} = offset; +} + +class COP2_BCCZ_FM : MaxisR6Inst { + bits<5> ct; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_COP2.Value; + let Inst{25-21} = Operation.Value; + let Inst{20-16} = ct; + let Inst{15-0} = offset; +} + +class PCREL16_FM : MaxisR6Inst { + bits<5> rs; + bits<16> imm; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_PCREL.Value; + let Inst{25-21} = rs; + let Inst{20-16} = Operation.Value; + let Inst{15-0} = imm; +} + +class PCREL19_FM : MaxisR6Inst { + bits<5> rs; + bits<19> imm; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_PCREL.Value; + let Inst{25-21} = rs; + let Inst{20-19} = Operation.Value; + let Inst{18-0} = imm; +} + +class PCREL18_FM : MaxisR6Inst { + bits<5> rs; + bits<18> imm; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_PCREL.Value; + let Inst{25-21} = rs; + let Inst{20-18} = Operation.Value; + let Inst{17-0} = imm; +} + +class SPECIAL3_2R_FM : MaxisR6Inst { + bits<5> rd; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL3.Value; + let Inst{25-21} = 0b00000; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = 0b00000; + let Inst{5-0} = Operation.Value; +} + +class SPECIAL3_MEM_FM : MaxisR6Inst { + bits<21> addr; + bits<5> hint; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL3.Value; + let Inst{25-21} = base; + let Inst{20-16} = hint; + let Inst{15-7} = offset; + let Inst{6} = 0; + let Inst{5-0} = Operation.Value; +} + +class SPECIAL_2R_FM : MaxisR6Inst { + bits<5> rd; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL.Value; + let Inst{25-21} = rs; + let Inst{20-16} = 0b00000; + let Inst{15-11} = rd; + let Inst{10-6} = 0b00001; + let Inst{5-0} = Operation.Value; +} + +class SPECIAL_3R_FM mulop, bits<6> funct> : MaxisR6Inst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL.Value; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = mulop; + let Inst{5-0} = funct; +} + +class SPECIAL_SDBBP_FM : MaxisR6Inst { + bits<20> code_; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL.Value; + let Inst{25-6} = code_; + let Inst{5-0} = OPCODE6_SDBBP.Value; +} + +// This class is ambiguous with other branches: +// BEQC/BNEC require that rs < rt && rs != 0 +class CMP_BRANCH_2R_OFF16_FM : MaxisR6Inst { + bits<5> rs; + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = funct.Value; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-0} = offset; +} + +// This class is ambiguous with other branches: +// BLEZC/BGEZC/BEQZALC/BNEZALC/BGTZALC require that rs == 0 && rt != 0 +// The '1R_RT' in the name means 1 register in the rt field. +class CMP_BRANCH_1R_RT_OFF16_FM : MaxisR6Inst { + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = funct.Value; + let Inst{25-21} = 0b00000; + let Inst{20-16} = rt; + let Inst{15-0} = offset; +} + +// This class is ambiguous with other branches: +// BLTZC/BGTZC/BLTZALC/BGEZALC require that rs == rt && rt != 0 +// The '1R_BOTH' in the name means 1 register in both the rs and rt fields. +class CMP_BRANCH_1R_BOTH_OFF16_FM : MaxisR6Inst { + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = funct.Value; + let Inst{25-21} = rt; + let Inst{20-16} = rt; + let Inst{15-0} = offset; +} + +class CMP_BRANCH_OFF21_FM funct> : MaxisR6Inst { + bits<5> rs; // rs != 0 + bits<21> offset; + + bits<32> Inst; + + let Inst{31-26} = funct; + let Inst{25-21} = rs; + let Inst{20-0} = offset; +} + +class JMP_IDX_COMPACT_FM funct> : MaxisR6Inst { + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = funct; + let Inst{25-21} = 0b00000; + let Inst{20-16} = rt; + let Inst{15-0} = offset; +} + +class BRANCH_OFF26_FM funct> : MaxisR6Inst { + bits<32> Inst; + bits<26> offset; + + let Inst{31-26} = funct; + let Inst{25-0} = offset; +} + +class SPECIAL3_ALIGN_FM : MaxisR6Inst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + bits<2> bp; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL3.Value; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-8} = 0b010; + let Inst{7-6} = bp; + let Inst{5-0} = Operation.Value; +} + +class SPECIAL3_DALIGN_FM : MaxisR6Inst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + bits<3> bp; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL3.Value; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-9} = 0b01; + let Inst{8-6} = bp; + let Inst{5-0} = Operation.Value; +} + +class SPECIAL3_LL_SC_FM : MaxisR6Inst { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL3.Value; + let Inst{25-21} = base; + let Inst{20-16} = rt; + let Inst{15-7} = offset; + let Inst{5-0} = Operation.Value; + + string DecoderMethod = "DecodeSpecial3LlSc"; +} + +class SPECIAL_LSA_FM : MaxisR6Inst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + bits<2> imm2; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL.Value; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-8} = 0b000; + let Inst{7-6} = imm2; + let Inst{5-0} = Operation.Value; +} + +class REGIMM_FM : MaxisR6Inst { + bits<5> rs; + bits<16> imm; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_REGIMM.Value; + let Inst{25-21} = rs; + let Inst{20-16} = Operation.Value; + let Inst{15-0} = imm; +} + +class COP1_CMP_CONDN_FM : MaxisR6Inst { + bits<5> fd; + bits<5> fs; + bits<5> ft; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_COP1.Value; + let Inst{25-21} = Format.Value; + let Inst{20-16} = ft; + let Inst{15-11} = fs; + let Inst{10-6} = fd; + let Inst{5} = 0; + let Inst{4-0} = Cond.Value; +} + +class JR_HB_R6_FM : MaxisR6Inst { + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL.Value; + let Inst{25-21} = rs; + let Inst{20-16} = 0; + let Inst{15-11} = 0; + let Inst{10} = 1; + let Inst{9-6} = 0; + let Inst{5-0} = Operation.Value; +} + +class COP2LDST_FM : MaxisR6Inst { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<11> offset = addr{10-0}; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_COP2LDST.Value; + let Inst{25-21} = Operation.Value; + let Inst{20-16} = rt; + let Inst{15-11} = base; + let Inst{10-0} = offset; +} diff --git a/lib/Target/Maxis/Maxis32r6InstrInfo.td b/lib/Target/Maxis/Maxis32r6InstrInfo.td new file mode 100644 index 00000000..e80e71c4 --- /dev/null +++ b/lib/Target/Maxis/Maxis32r6InstrInfo.td @@ -0,0 +1,1038 @@ +//=- Maxis32r6InstrInfo.td - Maxis32r6 Instruction Information -*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis32r6 instructions. +// +//===----------------------------------------------------------------------===// + +include "Maxis32r6InstrFormats.td" + +//===----------------------------------------------------------------------===// +// +// Maxis profiles and nodes +// +//===----------------------------------------------------------------------===// + +def SDT_MaxisFSelect : SDTypeProfile<1, 3, [SDTCisFP<1>, + SDTCisSameAs<0,2>, + SDTCisSameAs<2,3>]>; + +def MaxisFSelect : SDNode<"MaxisISD::FSELECT", SDT_MaxisFSelect>; + +//===----------------------------------------------------------------------===// +// +// Maxis Operands +// +//===----------------------------------------------------------------------===// + +// Notes about removals/changes from MAXIS32r6: +// Reencoded: jr -> jalr +// Reencoded: jr.hb -> jalr.hb + +def brtarget21 : Operand { + let EncoderMethod = "getBranchTarget21OpValue"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTarget21"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def brtarget26 : Operand { + let EncoderMethod = "getBranchTarget26OpValue"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTarget26"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def jmpoffset16 : Operand { + let EncoderMethod = "getJumpOffset16OpValue"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def calloffset16 : Operand { + let EncoderMethod = "getJumpOffset16OpValue"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +//===----------------------------------------------------------------------===// +// +// Instruction Encodings +// +//===----------------------------------------------------------------------===// + +class ADDIUPC_ENC : PCREL19_FM; +class ALIGN_ENC : SPECIAL3_ALIGN_FM; +class ALUIPC_ENC : PCREL16_FM; +class AUI_ENC : AUI_FM; +class AUIPC_ENC : PCREL16_FM; + +class BAL_ENC : BAL_FM; +class BALC_ENC : BRANCH_OFF26_FM<0b111010>; +class BC_ENC : BRANCH_OFF26_FM<0b110010>; +class BEQC_ENC : CMP_BRANCH_2R_OFF16_FM, + DecodeDisambiguates<"AddiGroupBranch">; +class BEQZALC_ENC : CMP_BRANCH_1R_RT_OFF16_FM, + DecodeDisambiguatedBy<"DaddiGroupBranch">; +class BNEC_ENC : CMP_BRANCH_2R_OFF16_FM, + DecodeDisambiguates<"DaddiGroupBranch">; +class BNEZALC_ENC : CMP_BRANCH_1R_RT_OFF16_FM, + DecodeDisambiguatedBy<"DaddiGroupBranch">; + +class BLTZC_ENC : CMP_BRANCH_1R_BOTH_OFF16_FM, + DecodeDisambiguates<"BgtzlGroupBranch">; +class BGEC_ENC : CMP_BRANCH_2R_OFF16_FM, + DecodeDisambiguatedBy<"BlezlGroupBranch">; +class BGEUC_ENC : CMP_BRANCH_2R_OFF16_FM, + DecodeDisambiguatedBy<"BlezGroupBranch">; +class BGEZC_ENC : CMP_BRANCH_1R_BOTH_OFF16_FM, + DecodeDisambiguates<"BlezlGroupBranch">; +class BGTZALC_ENC : CMP_BRANCH_1R_RT_OFF16_FM, + DecodeDisambiguatedBy<"BgtzGroupBranch">; + +class BLTC_ENC : CMP_BRANCH_2R_OFF16_FM, + DecodeDisambiguatedBy<"BgtzlGroupBranch">; +class BLTUC_ENC : CMP_BRANCH_2R_OFF16_FM, + DecodeDisambiguatedBy<"BgtzGroupBranch">; + +class BLEZC_ENC : CMP_BRANCH_1R_RT_OFF16_FM, + DecodeDisambiguatedBy<"BlezlGroupBranch">; +class BLTZALC_ENC : CMP_BRANCH_1R_BOTH_OFF16_FM, + DecodeDisambiguates<"BgtzGroupBranch">; +class BGTZC_ENC : CMP_BRANCH_1R_RT_OFF16_FM, + DecodeDisambiguatedBy<"BgtzlGroupBranch">; + +class BEQZC_ENC : CMP_BRANCH_OFF21_FM<0b110110>; +class BGEZALC_ENC : CMP_BRANCH_1R_BOTH_OFF16_FM, + DecodeDisambiguates<"BlezGroupBranch">; +class BNEZC_ENC : CMP_BRANCH_OFF21_FM<0b111110>; + +class BC1EQZ_ENC : COP1_BCCZ_FM; +class BC1NEZ_ENC : COP1_BCCZ_FM; +class BC2EQZ_ENC : COP2_BCCZ_FM; +class BC2NEZ_ENC : COP2_BCCZ_FM; + +class DVP_ENC : COP0_EVP_DVP_FM<0b1>; +class EVP_ENC : COP0_EVP_DVP_FM<0b0>; + +class JIALC_ENC : JMP_IDX_COMPACT_FM<0b111110>; +class JIC_ENC : JMP_IDX_COMPACT_FM<0b110110>; +class JR_HB_R6_ENC : JR_HB_R6_FM; +class BITSWAP_ENC : SPECIAL3_2R_FM; +class BLEZALC_ENC : CMP_BRANCH_1R_RT_OFF16_FM, + DecodeDisambiguatedBy<"BlezGroupBranch">; +class BNVC_ENC : CMP_BRANCH_2R_OFF16_FM, + DecodeDisambiguatedBy<"DaddiGroupBranch">; +class BOVC_ENC : CMP_BRANCH_2R_OFF16_FM, + DecodeDisambiguatedBy<"AddiGroupBranch">; +class DIV_ENC : SPECIAL_3R_FM<0b00010, 0b011010>; +class DIVU_ENC : SPECIAL_3R_FM<0b00010, 0b011011>; +class MOD_ENC : SPECIAL_3R_FM<0b00011, 0b011010>; +class MODU_ENC : SPECIAL_3R_FM<0b00011, 0b011011>; +class MUH_ENC : SPECIAL_3R_FM<0b00011, 0b011000>; +class MUHU_ENC : SPECIAL_3R_FM<0b00011, 0b011001>; +class MUL_R6_ENC : SPECIAL_3R_FM<0b00010, 0b011000>; +class MULU_ENC : SPECIAL_3R_FM<0b00010, 0b011001>; + +class MADDF_S_ENC : COP1_3R_FM<0b011000, FIELD_FMT_S>; +class MADDF_D_ENC : COP1_3R_FM<0b011000, FIELD_FMT_D>; +class MSUBF_S_ENC : COP1_3R_FM<0b011001, FIELD_FMT_S>; +class MSUBF_D_ENC : COP1_3R_FM<0b011001, FIELD_FMT_D>; + +class SEL_D_ENC : COP1_3R_FM<0b010000, FIELD_FMT_D>; +class SEL_S_ENC : COP1_3R_FM<0b010000, FIELD_FMT_S>; + +class SELEQZ_ENC : SPECIAL_3R_FM<0b00000, 0b110101>; +class SELNEZ_ENC : SPECIAL_3R_FM<0b00000, 0b110111>; + +class LWPC_ENC : PCREL19_FM; +class LWUPC_ENC : PCREL19_FM; + +class MAX_S_ENC : COP1_3R_FM<0b011101, FIELD_FMT_S>; +class MAX_D_ENC : COP1_3R_FM<0b011101, FIELD_FMT_D>; +class MIN_S_ENC : COP1_3R_FM<0b011100, FIELD_FMT_S>; +class MIN_D_ENC : COP1_3R_FM<0b011100, FIELD_FMT_D>; + +class MAXA_S_ENC : COP1_3R_FM<0b011111, FIELD_FMT_S>; +class MAXA_D_ENC : COP1_3R_FM<0b011111, FIELD_FMT_D>; +class MINA_S_ENC : COP1_3R_FM<0b011110, FIELD_FMT_S>; +class MINA_D_ENC : COP1_3R_FM<0b011110, FIELD_FMT_D>; + +class SELEQZ_S_ENC : COP1_3R_FM<0b010100, FIELD_FMT_S>; +class SELEQZ_D_ENC : COP1_3R_FM<0b010100, FIELD_FMT_D>; +class SELNEZ_S_ENC : COP1_3R_FM<0b010111, FIELD_FMT_S>; +class SELNEZ_D_ENC : COP1_3R_FM<0b010111, FIELD_FMT_D>; + +class RINT_S_ENC : COP1_2R_FM<0b011010, FIELD_FMT_S>; +class RINT_D_ENC : COP1_2R_FM<0b011010, FIELD_FMT_D>; +class CLASS_S_ENC : COP1_2R_FM<0b011011, FIELD_FMT_S>; +class CLASS_D_ENC : COP1_2R_FM<0b011011, FIELD_FMT_D>; + +class CACHE_ENC : SPECIAL3_MEM_FM; +class PREF_ENC : SPECIAL3_MEM_FM; + +class LDC2_R6_ENC : COP2LDST_FM; +class LWC2_R6_ENC : COP2LDST_FM; +class SDC2_R6_ENC : COP2LDST_FM; +class SWC2_R6_ENC : COP2LDST_FM; + +class LSA_R6_ENC : SPECIAL_LSA_FM; + +class LL_R6_ENC : SPECIAL3_LL_SC_FM; +class SC_R6_ENC : SPECIAL3_LL_SC_FM; + +class CLO_R6_ENC : SPECIAL_2R_FM; +class CLZ_R6_ENC : SPECIAL_2R_FM; + +class SDBBP_R6_ENC : SPECIAL_SDBBP_FM; + +//===----------------------------------------------------------------------===// +// +// Instruction Multiclasses +// +//===----------------------------------------------------------------------===// + +class CMP_CONDN_DESC_BASE { + dag OutOperandList = (outs FGRCCOpnd:$fd); + dag InOperandList = (ins FGROpnd:$fs, FGROpnd:$ft); + string AsmString = !strconcat("cmp.", CondStr, ".", Typestr, "\t$fd, $fs, $ft"); + list Pattern = [(set FGRCCOpnd:$fd, (Op FGROpnd:$fs, FGROpnd:$ft))]; + bit isCTI = 1; + InstrItinClass Itinerary = Itin; +} + +multiclass CMP_CC_M { + let AdditionalPredicates = [NotInMicroMaxis] in { + def CMP_F_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"af", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_UN_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"un", Typestr, FGROpnd, Itin, setuo>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_EQ_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"eq", Typestr, FGROpnd, Itin, + setoeq>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_UEQ_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"ueq", Typestr, FGROpnd, Itin, + setueq>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_LT_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"lt", Typestr, FGROpnd, Itin, + setolt>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_ULT_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"ult", Typestr, FGROpnd, Itin, + setult>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_LE_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"le", Typestr, FGROpnd, Itin, + setole>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_ULE_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"ule", Typestr, FGROpnd, Itin, + setule>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_SAF_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"saf", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_SUN_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"sun", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_SEQ_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"seq", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_SUEQ_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"sueq", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_SLT_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"slt", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_SULT_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"sult", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_SLE_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"sle", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + def CMP_SULE_#NAME : R6MMR6Rel, COP1_CMP_CONDN_FM, + CMP_CONDN_DESC_BASE<"sule", Typestr, FGROpnd, Itin>, + MaxisR6Arch, + ISA_MAXIS32R6, HARDFLOAT; + } +} + +//===----------------------------------------------------------------------===// +// +// Instruction Descriptions +// +//===----------------------------------------------------------------------===// + +class PCREL_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rs); + dag InOperandList = (ins ImmOpnd:$imm); + string AsmString = !strconcat(instr_asm, "\t$rs, $imm"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class ADDIUPC_DESC : PCREL_DESC_BASE<"addiupc", GPR32Opnd, simm19_lsl2, + II_ADDIUPC>; +class LWPC_DESC: PCREL_DESC_BASE<"lwpc", GPR32Opnd, simm19_lsl2, II_LWPC>; +class LWUPC_DESC: PCREL_DESC_BASE<"lwupc", GPR32Opnd, simm19_lsl2, II_LWUPC>; + +class ALIGN_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt, ImmOpnd:$bp); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt, $bp"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class ALIGN_DESC : ALIGN_DESC_BASE<"align", GPR32Opnd, uimm2, II_ALIGN>; + +class ALUIPC_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rs); + dag InOperandList = (ins simm16:$imm); + string AsmString = !strconcat(instr_asm, "\t$rs, $imm"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class ALUIPC_DESC : ALUIPC_DESC_BASE<"aluipc", GPR32Opnd, II_ALUIPC>; +class AUIPC_DESC : ALUIPC_DESC_BASE<"auipc", GPR32Opnd, II_AUIPC>; + +class AUI_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins GPROpnd:$rs, uimm16:$imm); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs, $imm"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class AUI_DESC : AUI_DESC_BASE<"aui", GPR32Opnd, II_AUI>; + +class BRANCH_DESC_BASE { + bit isBranch = 1; + bit isTerminator = 1; + bit hasDelaySlot = 0; + bit isCTI = 1; +} + +class BC_DESC_BASE : BRANCH_DESC_BASE, + MaxisR6Arch { + dag InOperandList = (ins opnd:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(instr_asm, "\t$offset"); + bit isBarrier = 1; + InstrItinClass Itinerary = II_BC; + bit isCTI = 1; +} + +class CMP_BC_DESC_BASE : BRANCH_DESC_BASE, + MaxisR6Arch { + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt, opnd:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(instr_asm, "\t$rs, $rt, $offset"); + list Defs = [AT]; + InstrItinClass Itinerary = II_BCCC; + bit hasForbiddenSlot = 1; + bit isCTI = 1; +} + +class CMP_CBR_EQNE_Z_DESC_BASE + : BRANCH_DESC_BASE, MaxisR6Arch { + dag InOperandList = (ins GPROpnd:$rs, opnd:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(instr_asm, "\t$rs, $offset"); + list Defs = [AT]; + InstrItinClass Itinerary = II_BCCZC; + bit hasForbiddenSlot = 1; + bit isCTI = 1; +} + +class CMP_CBR_RT_Z_DESC_BASE + : BRANCH_DESC_BASE, MaxisR6Arch { + dag InOperandList = (ins GPROpnd:$rt, opnd:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(instr_asm, "\t$rt, $offset"); + list Defs = [AT]; + InstrItinClass Itinerary = II_BCCZC; + bit hasForbiddenSlot = 1; + bit isCTI = 1; +} + +class BAL_DESC : BC_DESC_BASE<"bal", brtarget> { + bit isCall = 1; + bit hasDelaySlot = 1; + list Defs = [RA]; + bit isCTI = 1; +} + +class BALC_DESC : BC_DESC_BASE<"balc", brtarget26> { + bit isCall = 1; + list Defs = [RA]; + InstrItinClass Itinerary = II_BALC; + bit isCTI = 1; +} + +class BC_DESC : BC_DESC_BASE<"bc", brtarget26>; +class BGEC_DESC : CMP_BC_DESC_BASE<"bgec", brtarget, GPR32Opnd>; +class BGEUC_DESC : CMP_BC_DESC_BASE<"bgeuc", brtarget, GPR32Opnd>; +class BEQC_DESC : CMP_BC_DESC_BASE<"beqc", brtarget, GPR32Opnd>; +class BNEC_DESC : CMP_BC_DESC_BASE<"bnec", brtarget, GPR32Opnd>; + +class BLTC_DESC : CMP_BC_DESC_BASE<"bltc", brtarget, GPR32Opnd>; +class BLTUC_DESC : CMP_BC_DESC_BASE<"bltuc", brtarget, GPR32Opnd>; + +class BLTZC_DESC : CMP_CBR_RT_Z_DESC_BASE<"bltzc", brtarget, GPR32Opnd>; +class BGEZC_DESC : CMP_CBR_RT_Z_DESC_BASE<"bgezc", brtarget, GPR32Opnd>; + +class BLEZC_DESC : CMP_CBR_RT_Z_DESC_BASE<"blezc", brtarget, GPR32Opnd>; +class BGTZC_DESC : CMP_CBR_RT_Z_DESC_BASE<"bgtzc", brtarget, GPR32Opnd>; + +class BEQZC_DESC : CMP_CBR_EQNE_Z_DESC_BASE<"beqzc", brtarget21, GPR32Opnd>; +class BNEZC_DESC : CMP_CBR_EQNE_Z_DESC_BASE<"bnezc", brtarget21, GPR32Opnd>; + +class COP1_BCCZ_DESC_BASE : BRANCH_DESC_BASE { + dag InOperandList = (ins FGR64Opnd:$ft, brtarget:$offset); + dag OutOperandList = (outs); + string AsmString = instr_asm; + bit hasDelaySlot = 1; + InstrItinClass Itinerary = II_BC1CCZ; +} + +class BC1EQZ_DESC : COP1_BCCZ_DESC_BASE<"bc1eqz $ft, $offset">; +class BC1NEZ_DESC : COP1_BCCZ_DESC_BASE<"bc1nez $ft, $offset">; + +class COP2_BCCZ_DESC_BASE : BRANCH_DESC_BASE { + dag InOperandList = (ins COP2Opnd:$ct, brtarget:$offset); + dag OutOperandList = (outs); + string AsmString = instr_asm; + bit hasDelaySlot = 1; + bit isCTI = 1; + InstrItinClass Itinerary = II_BC2CCZ; +} + +class BC2EQZ_DESC : COP2_BCCZ_DESC_BASE<"bc2eqz $ct, $offset">; +class BC2NEZ_DESC : COP2_BCCZ_DESC_BASE<"bc2nez $ct, $offset">; + +class BOVC_DESC : CMP_BC_DESC_BASE<"bovc", brtarget, GPR32Opnd>; +class BNVC_DESC : CMP_BC_DESC_BASE<"bnvc", brtarget, GPR32Opnd>; + +class JMP_IDX_COMPACT_DESC_BASE + : MaxisR6Arch { + dag InOperandList = (ins GPROpnd:$rt, opnd:$offset); + string AsmString = !strconcat(opstr, "\t$rt, $offset"); + list Pattern = []; + bit hasDelaySlot = 0; + InstrItinClass Itinerary = itin; + bit isCTI = 1; + bit isBranch = 1; + bit isIndirectBranch = 1; +} + +class JIALC_DESC : JMP_IDX_COMPACT_DESC_BASE<"jialc", calloffset16, + GPR32Opnd, II_JIALC> { + bit isCall = 1; + list Defs = [RA]; +} + +class JIC_DESC : JMP_IDX_COMPACT_DESC_BASE<"jic", jmpoffset16, + GPR32Opnd, II_JIALC> { + bit isBarrier = 1; + bit isTerminator = 1; + list Defs = [AT]; +} + +class JR_HB_R6_DESC : JR_HB_DESC_BASE<"jr.hb", GPR32Opnd> { + bit isBranch = 1; + bit isIndirectBranch = 1; + bit hasDelaySlot = 1; + bit isTerminator=1; + bit isBarrier=1; + bit isCTI = 1; + InstrItinClass Itinerary = II_JR_HB; +} + +class BITSWAP_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rt"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class BITSWAP_DESC : BITSWAP_DESC_BASE<"bitswap", GPR32Opnd, II_BITSWAP>; + +class DIVMOD_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt"); + list Pattern = [(set GPROpnd:$rd, (Op GPROpnd:$rs, GPROpnd:$rt))]; + InstrItinClass Itinerary = itin; + // This instruction doesn't trap division by zero itself. We must insert + // teq instructions as well. + bit usesCustomInserter = 1; +} + +class DVPEVP_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins); + string AsmString = !strconcat(instr_asm, "\t$rt"); + list Pattern = []; + InstrItinClass Itinerary = Itin; + bit hasUnModeledSideEffects = 1; +} + +class DVP_DESC : DVPEVP_DESC_BASE<"dvp", II_DVP>; +class EVP_DESC : DVPEVP_DESC_BASE<"evp", II_EVP>; + +class DIV_DESC : DIVMOD_DESC_BASE<"div", GPR32Opnd, II_DIV, sdiv>; +class DIVU_DESC : DIVMOD_DESC_BASE<"divu", GPR32Opnd, II_DIVU, udiv>; +class MOD_DESC : DIVMOD_DESC_BASE<"mod", GPR32Opnd, II_MOD, srem>; +class MODU_DESC : DIVMOD_DESC_BASE<"modu", GPR32Opnd, II_MODU, urem>; + +class BEQZALC_DESC : CMP_CBR_RT_Z_DESC_BASE<"beqzalc", brtarget, GPR32Opnd> { + list Defs = [RA]; +} + +class BGEZALC_DESC : CMP_CBR_RT_Z_DESC_BASE<"bgezalc", brtarget, GPR32Opnd> { + list Defs = [RA]; +} + +class BGTZALC_DESC : CMP_CBR_RT_Z_DESC_BASE<"bgtzalc", brtarget, GPR32Opnd> { + list Defs = [RA]; +} + +class BLEZALC_DESC : CMP_CBR_RT_Z_DESC_BASE<"blezalc", brtarget, GPR32Opnd> { + list Defs = [RA]; +} + +class BLTZALC_DESC : CMP_CBR_RT_Z_DESC_BASE<"bltzalc", brtarget, GPR32Opnd> { + list Defs = [RA]; +} + +class BNEZALC_DESC : CMP_CBR_RT_Z_DESC_BASE<"bnezalc", brtarget, GPR32Opnd> { + list Defs = [RA]; +} + +class MUL_R6_DESC_BASE : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt"); + list Pattern = [(set GPROpnd:$rd, (Op GPROpnd:$rs, GPROpnd:$rt))]; + InstrItinClass Itinerary = itin; +} + +class MUH_DESC : MUL_R6_DESC_BASE<"muh", GPR32Opnd, II_MUH, mulhs>; +class MUHU_DESC : MUL_R6_DESC_BASE<"muhu", GPR32Opnd, II_MUHU, mulhu>; +class MUL_R6_DESC : MUL_R6_DESC_BASE<"mul", GPR32Opnd, II_MUL, mul>; +class MULU_DESC : MUL_R6_DESC_BASE<"mulu", GPR32Opnd, II_MULU>; + +class COP1_SEL_DESC_BASE { + dag OutOperandList = (outs FGROpnd:$fd); + dag InOperandList = (ins FGRCCOpnd:$fd_in, FGROpnd:$fs, FGROpnd:$ft); + string AsmString = !strconcat(instr_asm, "\t$fd, $fs, $ft"); + list Pattern = [(set FGROpnd:$fd, (select FGRCCOpnd:$fd_in, + FGROpnd:$ft, + FGROpnd:$fs))]; + string Constraints = "$fd_in = $fd"; + InstrItinClass Itinerary = itin; +} + +class COP1_SEL_D_DESC_BASE { + dag OutOperandList = (outs FGROpnd:$fd); + dag InOperandList = (ins FGROpnd:$fd_in, FGROpnd:$fs, FGROpnd:$ft); + string AsmString = !strconcat(instr_asm, "\t$fd, $fs, $ft"); + list Pattern = [(set FGROpnd:$fd, (MaxisFSelect FGROpnd:$fd_in, + FGROpnd:$ft, + FGROpnd:$fs))]; + string Constraints = "$fd_in = $fd"; + InstrItinClass Itinerary = itin; +} + +class SEL_D_DESC : COP1_SEL_D_DESC_BASE<"sel.d", FGR64Opnd, II_SEL_D>, + MaxisR6Arch<"sel.d">; +class SEL_S_DESC : COP1_SEL_DESC_BASE<"sel.s", FGR32Opnd, II_SEL_S>, + MaxisR6Arch<"sel.s">; + +class SELEQNE_Z_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt"); + list Pattern = []; + InstrItinClass Itinerary = II_SELCCZ; +} + +class SELEQZ_DESC : SELEQNE_Z_DESC_BASE<"seleqz", GPR32Opnd>; +class SELNEZ_DESC : SELEQNE_Z_DESC_BASE<"selnez", GPR32Opnd>; + +class COP1_4R_DESC_BASE { + dag OutOperandList = (outs FGROpnd:$fd); + dag InOperandList = (ins FGROpnd:$fd_in, FGROpnd:$fs, FGROpnd:$ft); + string AsmString = !strconcat(instr_asm, "\t$fd, $fs, $ft"); + list Pattern = []; + string Constraints = "$fd_in = $fd"; + InstrItinClass Itinerary = itin; +} + +class MADDF_S_DESC : COP1_4R_DESC_BASE<"maddf.s", FGR32Opnd, II_MADDF_S>; +class MADDF_D_DESC : COP1_4R_DESC_BASE<"maddf.d", FGR64Opnd, II_MADDF_D>; +class MSUBF_S_DESC : COP1_4R_DESC_BASE<"msubf.s", FGR32Opnd, II_MSUBF_S>; +class MSUBF_D_DESC : COP1_4R_DESC_BASE<"msubf.d", FGR64Opnd, II_MSUBF_D>; + +class MAX_MIN_DESC_BASE { + dag OutOperandList = (outs FGROpnd:$fd); + dag InOperandList = (ins FGROpnd:$fs, FGROpnd:$ft); + string AsmString = !strconcat(instr_asm, "\t$fd, $fs, $ft"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class MAX_S_DESC : MAX_MIN_DESC_BASE<"max.s", FGR32Opnd, II_MAX_S>; +class MAX_D_DESC : MAX_MIN_DESC_BASE<"max.d", FGR64Opnd, II_MAX_D>; +class MIN_S_DESC : MAX_MIN_DESC_BASE<"min.s", FGR32Opnd, II_MIN_S>; +class MIN_D_DESC : MAX_MIN_DESC_BASE<"min.d", FGR64Opnd, II_MIN_D>; + +class MAXA_S_DESC : MAX_MIN_DESC_BASE<"maxa.s", FGR32Opnd, II_MAX_S>; +class MAXA_D_DESC : MAX_MIN_DESC_BASE<"maxa.d", FGR64Opnd, II_MAX_D>; +class MINA_S_DESC : MAX_MIN_DESC_BASE<"mina.s", FGR32Opnd, II_MIN_D>; +class MINA_D_DESC : MAX_MIN_DESC_BASE<"mina.d", FGR64Opnd, II_MIN_S>; + +class SELEQNEZ_DESC_BASE { + dag OutOperandList = (outs FGROpnd:$fd); + dag InOperandList = (ins FGROpnd:$fs, FGROpnd:$ft); + string AsmString = !strconcat(instr_asm, "\t$fd, $fs, $ft"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class SELEQZ_S_DESC : SELEQNEZ_DESC_BASE<"seleqz.s", FGR32Opnd, II_SELCCZ_S>, + MaxisR6Arch<"seleqz.s">; +class SELEQZ_D_DESC : SELEQNEZ_DESC_BASE<"seleqz.d", FGR64Opnd, II_SELCCZ_D>, + MaxisR6Arch<"seleqz.d">; +class SELNEZ_S_DESC : SELEQNEZ_DESC_BASE<"selnez.s", FGR32Opnd, II_SELCCZ_S>, + MaxisR6Arch<"selnez.s">; +class SELNEZ_D_DESC : SELEQNEZ_DESC_BASE<"selnez.d", FGR64Opnd, II_SELCCZ_D>, + MaxisR6Arch<"selnez.d">; + +class CLASS_RINT_DESC_BASE { + dag OutOperandList = (outs FGROpnd:$fd); + dag InOperandList = (ins FGROpnd:$fs); + string AsmString = !strconcat(instr_asm, "\t$fd, $fs"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class RINT_S_DESC : CLASS_RINT_DESC_BASE<"rint.s", FGR32Opnd, II_RINT_S>; +class RINT_D_DESC : CLASS_RINT_DESC_BASE<"rint.d", FGR64Opnd, II_RINT_D>; +class CLASS_S_DESC : CLASS_RINT_DESC_BASE<"class.s", FGR32Opnd, II_CLASS_S>; +class CLASS_D_DESC : CLASS_RINT_DESC_BASE<"class.d", FGR64Opnd, II_CLASS_D>; + +class CACHE_HINT_DESC + : MaxisR6Arch { + dag OutOperandList = (outs); + dag InOperandList = (ins MemOpnd:$addr, uimm5:$hint); + string AsmString = !strconcat(instr_asm, "\t$hint, $addr"); + list Pattern = []; + string DecoderMethod = "DecodeCacheeOp_CacheOpR6"; + InstrItinClass Itinerary = itin; +} + +class CACHE_DESC : CACHE_HINT_DESC<"cache", mem_simm9, GPR32Opnd, II_CACHE>; +class PREF_DESC : CACHE_HINT_DESC<"pref", mem_simm9, GPR32Opnd, II_PREF>; + +class COP2LD_DESC_BASE { + dag OutOperandList = (outs COPOpnd:$rt); + dag InOperandList = (ins mem_simm11:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + bit mayLoad = 1; + string DecoderMethod = "DecodeFMemCop2R6"; + InstrItinClass Itinerary = itin; +} + +class LDC2_R6_DESC : COP2LD_DESC_BASE<"ldc2", COP2Opnd, II_LDC2>; +class LWC2_R6_DESC : COP2LD_DESC_BASE<"lwc2", COP2Opnd, II_LWC2>; + +class COP2ST_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins COPOpnd:$rt, mem_simm11:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + bit mayStore = 1; + string DecoderMethod = "DecodeFMemCop2R6"; + InstrItinClass Itinerary = itin; +} + +class SDC2_R6_DESC : COP2ST_DESC_BASE<"sdc2", COP2Opnd, II_SDC2>; +class SWC2_R6_DESC : COP2ST_DESC_BASE<"swc2", COP2Opnd, II_SWC2>; + +class LSA_R6_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt, ImmOpnd:$imm2); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt, $imm2"); + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class LSA_R6_DESC : LSA_R6_DESC_BASE<"lsa", GPR32Opnd, uimm2_plus1, II_LSA>; + +class LL_R6_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins MemOpnd:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + bit mayLoad = 1; + InstrItinClass Itinerary = itin; +} + +class LL_R6_DESC : LL_R6_DESC_BASE<"ll", GPR32Opnd, mem_simm9, II_LL>; + +class SC_R6_DESC_BASE { + dag OutOperandList = (outs GPROpnd:$dst); + dag InOperandList = (ins GPROpnd:$rt, mem_simm9:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + bit mayStore = 1; + string Constraints = "$rt = $dst"; + InstrItinClass Itinerary = itin; +} + +class SC_R6_DESC : SC_R6_DESC_BASE<"sc", GPR32Opnd, II_SC>; + +class CLO_CLZ_R6_DESC_BASE + : MaxisR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs"); + InstrItinClass Itinerary = itin; +} + +class CLO_R6_DESC_BASE : + CLO_CLZ_R6_DESC_BASE { + list Pattern = [(set GPROpnd:$rd, (ctlz (not GPROpnd:$rs)))]; +} + +class CLZ_R6_DESC_BASE : + CLO_CLZ_R6_DESC_BASE { + list Pattern = [(set GPROpnd:$rd, (ctlz GPROpnd:$rs))]; +} + +class CLO_R6_DESC : CLO_R6_DESC_BASE<"clo", GPR32Opnd, II_CLO>; +class CLZ_R6_DESC : CLZ_R6_DESC_BASE<"clz", GPR32Opnd, II_CLZ>; + +class SDBBP_R6_DESC { + dag OutOperandList = (outs); + dag InOperandList = (ins uimm20:$code_); + string AsmString = "sdbbp\t$code_"; + list Pattern = []; + bit isCTI = 1; + InstrItinClass Itinerary = II_SDBBP; +} + +//===----------------------------------------------------------------------===// +// +// Instruction Definitions +// +//===----------------------------------------------------------------------===// + +def ADDIUPC : R6MMR6Rel, ADDIUPC_ENC, ADDIUPC_DESC, ISA_MAXIS32R6; +def ALIGN : R6MMR6Rel, ALIGN_ENC, ALIGN_DESC, ISA_MAXIS32R6; +def ALUIPC : R6MMR6Rel, ALUIPC_ENC, ALUIPC_DESC, ISA_MAXIS32R6; +def AUI : R6MMR6Rel, AUI_ENC, AUI_DESC, ISA_MAXIS32R6; +def AUIPC : R6MMR6Rel, AUIPC_ENC, AUIPC_DESC, ISA_MAXIS32R6; +def BAL : BAL_ENC, BAL_DESC, ISA_MAXIS32R6; +def BALC : R6MMR6Rel, BALC_ENC, BALC_DESC, ISA_MAXIS32R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def BC1EQZ : BC1EQZ_ENC, BC1EQZ_DESC, ISA_MAXIS32R6, HARDFLOAT; + def BC1NEZ : BC1NEZ_ENC, BC1NEZ_DESC, ISA_MAXIS32R6, HARDFLOAT; + def BC2EQZ : BC2EQZ_ENC, BC2EQZ_DESC, ISA_MAXIS32R6; + def BC2NEZ : BC2NEZ_ENC, BC2NEZ_DESC, ISA_MAXIS32R6; + def BC : R6MMR6Rel, BC_ENC, BC_DESC, ISA_MAXIS32R6; + def BEQC : R6MMR6Rel, BEQC_ENC, BEQC_DESC, ISA_MAXIS32R6; + def BEQZALC : R6MMR6Rel, BEQZALC_ENC, BEQZALC_DESC, ISA_MAXIS32R6; + def BEQZC : R6MMR6Rel, BEQZC_ENC, BEQZC_DESC, ISA_MAXIS32R6; + def BGEC : R6MMR6Rel, BGEC_ENC, BGEC_DESC, ISA_MAXIS32R6; + def BGEUC : R6MMR6Rel, BGEUC_ENC, BGEUC_DESC, ISA_MAXIS32R6; + def BGEZALC : R6MMR6Rel, BGEZALC_ENC, BGEZALC_DESC, ISA_MAXIS32R6; + def BGEZC : R6MMR6Rel, BGEZC_ENC, BGEZC_DESC, ISA_MAXIS32R6; + def BGTZALC : R6MMR6Rel, BGTZALC_ENC, BGTZALC_DESC, ISA_MAXIS32R6; + def BGTZC : R6MMR6Rel, BGTZC_ENC, BGTZC_DESC, ISA_MAXIS32R6; +} +def BITSWAP : R6MMR6Rel, BITSWAP_ENC, BITSWAP_DESC, ISA_MAXIS32R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def BLEZALC : R6MMR6Rel, BLEZALC_ENC, BLEZALC_DESC, ISA_MAXIS32R6; + def BLEZC : R6MMR6Rel, BLEZC_ENC, BLEZC_DESC, ISA_MAXIS32R6; + def BLTC : R6MMR6Rel, BLTC_ENC, BLTC_DESC, ISA_MAXIS32R6; + def BLTUC : R6MMR6Rel, BLTUC_ENC, BLTUC_DESC, ISA_MAXIS32R6; + def BLTZALC : R6MMR6Rel, BLTZALC_ENC, BLTZALC_DESC, ISA_MAXIS32R6; + def BLTZC : R6MMR6Rel, BLTZC_ENC, BLTZC_DESC, ISA_MAXIS32R6; + def BNEC : R6MMR6Rel, BNEC_ENC, BNEC_DESC, ISA_MAXIS32R6; + def BNEZALC : R6MMR6Rel, BNEZALC_ENC, BNEZALC_DESC, ISA_MAXIS32R6; + def BNEZC : R6MMR6Rel, BNEZC_ENC, BNEZC_DESC, ISA_MAXIS32R6; + def BNVC : R6MMR6Rel, BNVC_ENC, BNVC_DESC, ISA_MAXIS32R6; + def BOVC : R6MMR6Rel, BOVC_ENC, BOVC_DESC, ISA_MAXIS32R6; +} +def CACHE_R6 : R6MMR6Rel, CACHE_ENC, CACHE_DESC, ISA_MAXIS32R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def CLASS_D : CLASS_D_ENC, CLASS_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + def CLASS_S : CLASS_S_ENC, CLASS_S_DESC, ISA_MAXIS32R6, HARDFLOAT; +} +def CLO_R6 : R6MMR6Rel, CLO_R6_ENC, CLO_R6_DESC, ISA_MAXIS32R6; +def CLZ_R6 : R6MMR6Rel, CLZ_R6_ENC, CLZ_R6_DESC, ISA_MAXIS32R6; +defm S : CMP_CC_M; +defm D : CMP_CC_M; +let AdditionalPredicates = [NotInMicroMaxis] in { + def DIV : R6MMR6Rel, DIV_ENC, DIV_DESC, ISA_MAXIS32R6; + def DIVU : R6MMR6Rel, DIVU_ENC, DIVU_DESC, ISA_MAXIS32R6; +} + +def DVP : R6MMR6Rel, DVP_ENC, DVP_DESC, ISA_MAXIS32R6; +def EVP : R6MMR6Rel, EVP_ENC, EVP_DESC, ISA_MAXIS32R6; + +def JIALC : R6MMR6Rel, JIALC_ENC, JIALC_DESC, ISA_MAXIS32R6; +def JIC : R6MMR6Rel, JIC_ENC, JIC_DESC, ISA_MAXIS32R6; +def JR_HB_R6 : JR_HB_R6_ENC, JR_HB_R6_DESC, ISA_MAXIS32R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def LDC2_R6 : LDC2_R6_ENC, LDC2_R6_DESC, ISA_MAXIS32R6; + def LL_R6 : LL_R6_ENC, LL_R6_DESC, PTR_32, ISA_MAXIS32R6; +} +def LSA_R6 : R6MMR6Rel, LSA_R6_ENC, LSA_R6_DESC, ISA_MAXIS32R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def LWC2_R6 : LWC2_R6_ENC, LWC2_R6_DESC, ISA_MAXIS32R6; +} +def LWPC : R6MMR6Rel, LWPC_ENC, LWPC_DESC, ISA_MAXIS32R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def LWUPC : R6MMR6Rel, LWUPC_ENC, LWUPC_DESC, ISA_MAXIS32R6; + def MADDF_S : MADDF_S_ENC, MADDF_S_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MADDF_D : MADDF_D_ENC, MADDF_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MAXA_D : MAXA_D_ENC, MAXA_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MAXA_S : MAXA_S_ENC, MAXA_S_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MAX_D : MAX_D_ENC, MAX_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MAX_S : MAX_S_ENC, MAX_S_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MINA_D : MINA_D_ENC, MINA_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MINA_S : MINA_S_ENC, MINA_S_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MIN_D : MIN_D_ENC, MIN_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MIN_S : MIN_S_ENC, MIN_S_DESC, ISA_MAXIS32R6, HARDFLOAT; + + def MOD : R6MMR6Rel, MOD_ENC, MOD_DESC, ISA_MAXIS32R6; + def MODU : R6MMR6Rel, MODU_ENC, MODU_DESC, ISA_MAXIS32R6; + + def MSUBF_S : MSUBF_S_ENC, MSUBF_S_DESC, ISA_MAXIS32R6, HARDFLOAT; + def MSUBF_D : MSUBF_D_ENC, MSUBF_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + + def MUH : R6MMR6Rel, MUH_ENC, MUH_DESC, ISA_MAXIS32R6; + def MUHU : R6MMR6Rel, MUHU_ENC, MUHU_DESC, ISA_MAXIS32R6; + def MUL_R6 : R6MMR6Rel, MUL_R6_ENC, MUL_R6_DESC, ISA_MAXIS32R6; + def MULU : R6MMR6Rel, MULU_ENC, MULU_DESC, ISA_MAXIS32R6; +} +def NAL; // BAL with rd=0 +def PREF_R6 : R6MMR6Rel, PREF_ENC, PREF_DESC, ISA_MAXIS32R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def RINT_D : RINT_D_ENC, RINT_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + def RINT_S : RINT_S_ENC, RINT_S_DESC, ISA_MAXIS32R6, HARDFLOAT; + def SC_R6 : SC_R6_ENC, SC_R6_DESC, PTR_32, ISA_MAXIS32R6; + def SDBBP_R6 : SDBBP_R6_ENC, SDBBP_R6_DESC, ISA_MAXIS32R6; + def SELEQZ : R6MMR6Rel, SELEQZ_ENC, SELEQZ_DESC, ISA_MAXIS32R6, GPR_32; + def SELNEZ : R6MMR6Rel, SELNEZ_ENC, SELNEZ_DESC, ISA_MAXIS32R6, GPR_32; + def SELEQZ_D : R6MMR6Rel, SELEQZ_D_ENC, SELEQZ_D_DESC, ISA_MAXIS32R6, + HARDFLOAT; + def SELEQZ_S : R6MMR6Rel, SELEQZ_S_ENC, SELEQZ_S_DESC, ISA_MAXIS32R6, + HARDFLOAT; + def SELNEZ_D : R6MMR6Rel, SELNEZ_D_ENC, SELNEZ_D_DESC, ISA_MAXIS32R6, + HARDFLOAT; + def SELNEZ_S : R6MMR6Rel, SELNEZ_S_ENC, SELNEZ_S_DESC, ISA_MAXIS32R6, + HARDFLOAT; + def SEL_D : R6MMR6Rel, SEL_D_ENC, SEL_D_DESC, ISA_MAXIS32R6, HARDFLOAT; + def SEL_S : R6MMR6Rel, SEL_S_ENC, SEL_S_DESC, ISA_MAXIS32R6, HARDFLOAT; + def SDC2_R6 : SDC2_R6_ENC, SDC2_R6_DESC, ISA_MAXIS32R6; + def SWC2_R6 : SWC2_R6_ENC, SWC2_R6_DESC, ISA_MAXIS32R6; +} + +//===----------------------------------------------------------------------===// +// +// Instruction Aliases +// +//===----------------------------------------------------------------------===// + +def : MaxisInstAlias<"dvp", (DVP ZERO), 0>, ISA_MAXIS32R6; +def : MaxisInstAlias<"evp", (EVP ZERO), 0>, ISA_MAXIS32R6; + +let AdditionalPredicates = [NotInMicroMaxis] in { +def : MaxisInstAlias<"sdbbp", (SDBBP_R6 0)>, ISA_MAXIS32R6; +def : MaxisInstAlias<"jr $rs", (JALR ZERO, GPR32Opnd:$rs), 1>, ISA_MAXIS32R6, GPR_32; +} + +def : MaxisInstAlias<"jrc $rs", (JIC GPR32Opnd:$rs, 0), 1>, ISA_MAXIS32R6, GPR_32; + +let AdditionalPredicates = [NotInMicroMaxis] in { +def : MaxisInstAlias<"jalrc $rs", (JIALC GPR32Opnd:$rs, 0), 1>, ISA_MAXIS32R6, GPR_32; +} + +def : MaxisInstAlias<"div $rs, $rt", (DIV GPR32Opnd:$rs, GPR32Opnd:$rs, + GPR32Opnd:$rt)>, ISA_MAXIS32R6; +def : MaxisInstAlias<"divu $rs, $rt", (DIVU GPR32Opnd:$rs, GPR32Opnd:$rs, + GPR32Opnd:$rt)>, ISA_MAXIS32R6; + +def : MaxisInstAlias<"lapc $rd, $imm", + (ADDIUPC GPR32Opnd:$rd, simm19_lsl2:$imm)>, ISA_MAXIS32R6; + +//===----------------------------------------------------------------------===// +// +// Patterns and Pseudo Instructions +// +//===----------------------------------------------------------------------===// + +// comparisons supported via another comparison +multiclass Cmp_Pats { +def : MaxisPat<(setone VT:$lhs, VT:$rhs), + (NOROp (!cast("CMP_UEQ_"#NAME) VT:$lhs, VT:$rhs), ZEROReg)>; +def : MaxisPat<(seto VT:$lhs, VT:$rhs), + (NOROp (!cast("CMP_UN_"#NAME) VT:$lhs, VT:$rhs), ZEROReg)>; +def : MaxisPat<(setune VT:$lhs, VT:$rhs), + (NOROp (!cast("CMP_EQ_"#NAME) VT:$lhs, VT:$rhs), ZEROReg)>; +def : MaxisPat<(seteq VT:$lhs, VT:$rhs), + (!cast("CMP_EQ_"#NAME) VT:$lhs, VT:$rhs)>; +def : MaxisPat<(setgt VT:$lhs, VT:$rhs), + (!cast("CMP_LE_"#NAME) VT:$rhs, VT:$lhs)>; +def : MaxisPat<(setge VT:$lhs, VT:$rhs), + (!cast("CMP_LT_"#NAME) VT:$rhs, VT:$lhs)>; +def : MaxisPat<(setlt VT:$lhs, VT:$rhs), + (!cast("CMP_LT_"#NAME) VT:$lhs, VT:$rhs)>; +def : MaxisPat<(setle VT:$lhs, VT:$rhs), + (!cast("CMP_LE_"#NAME) VT:$lhs, VT:$rhs)>; +def : MaxisPat<(setne VT:$lhs, VT:$rhs), + (NOROp (!cast("CMP_EQ_"#NAME) VT:$lhs, VT:$rhs), ZEROReg)>; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + defm S : Cmp_Pats, ISA_MAXIS32R6; + defm D : Cmp_Pats, ISA_MAXIS32R6; +} + +// i32 selects +multiclass SelectInt_Pats { +// reg, immz +def : MaxisPat<(select (Opg (seteq RC:$cond, immz)), RC:$t, RC:$f), + (OROp (SELEQZOp RC:$t, RC:$cond), (SELNEZOp RC:$f, RC:$cond))>; +def : MaxisPat<(select (Opg (setne RC:$cond, immz)), RC:$t, RC:$f), + (OROp (SELNEZOp RC:$t, RC:$cond), (SELEQZOp RC:$f, RC:$cond))>; + +// reg, immZExt16[_64] +def : MaxisPat<(select (Opg (seteq RC:$cond, imm_type:$imm)), RC:$t, RC:$f), + (OROp (SELEQZOp RC:$t, (XORiOp RC:$cond, imm_type:$imm)), + (SELNEZOp RC:$f, (XORiOp RC:$cond, imm_type:$imm)))>; +def : MaxisPat<(select (Opg (setne RC:$cond, imm_type:$imm)), RC:$t, RC:$f), + (OROp (SELNEZOp RC:$t, (XORiOp RC:$cond, imm_type:$imm)), + (SELEQZOp RC:$f, (XORiOp RC:$cond, imm_type:$imm)))>; + +// reg, immSExt16Plus1 +def : MaxisPat<(select (Opg (setgt RC:$cond, immSExt16Plus1:$imm)), RC:$t, RC:$f), + (OROp (SELEQZOp RC:$t, (SLTiOp RC:$cond, (Plus1 imm:$imm))), + (SELNEZOp RC:$f, (SLTiOp RC:$cond, (Plus1 imm:$imm))))>; +def : MaxisPat<(select (Opg (setugt RC:$cond, immSExt16Plus1:$imm)), RC:$t, RC:$f), + (OROp (SELEQZOp RC:$t, (SLTiuOp RC:$cond, (Plus1 imm:$imm))), + (SELNEZOp RC:$f, (SLTiuOp RC:$cond, (Plus1 imm:$imm))))>; + +def : MaxisPat<(select (Opg (seteq RC:$cond, immz)), RC:$t, immz), + (SELEQZOp RC:$t, RC:$cond)>; +def : MaxisPat<(select (Opg (setne RC:$cond, immz)), RC:$t, immz), + (SELNEZOp RC:$t, RC:$cond)>; +def : MaxisPat<(select (Opg (seteq RC:$cond, immz)), immz, RC:$f), + (SELNEZOp RC:$f, RC:$cond)>; +def : MaxisPat<(select (Opg (setne RC:$cond, immz)), immz, RC:$f), + (SELEQZOp RC:$f, RC:$cond)>; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { +defm : SelectInt_Pats, ISA_MAXIS32R6; + +def : MaxisPat<(select i32:$cond, i32:$t, i32:$f), + (OR (SELNEZ i32:$t, i32:$cond), + (SELEQZ i32:$f, i32:$cond))>, + ISA_MAXIS32R6; +def : MaxisPat<(select i32:$cond, i32:$t, immz), + (SELNEZ i32:$t, i32:$cond)>, + ISA_MAXIS32R6; +def : MaxisPat<(select i32:$cond, immz, i32:$f), + (SELEQZ i32:$f, i32:$cond)>, + ISA_MAXIS32R6; +} diff --git a/lib/Target/Maxis/Maxis64InstrInfo.td b/lib/Target/Maxis/Maxis64InstrInfo.td new file mode 100644 index 00000000..bf8e7359 --- /dev/null +++ b/lib/Target/Maxis/Maxis64InstrInfo.td @@ -0,0 +1,983 @@ +//===- Maxis64InstrInfo.td - Maxis64 Instruction Information -*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis64 instructions. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Maxis Operand, Complex Patterns and Transformations Definitions. +//===----------------------------------------------------------------------===// + +// shamt must fit in 6 bits. +def immZExt6 : ImmLeaf; + +// Node immediate fits as 10-bit sign extended on target immediate. +// e.g. seqi, snei +def immSExt10_64 : PatLeaf<(i64 imm), + [{ return isInt<10>(N->getSExtValue()); }]>; + +def immZExt16_64 : PatLeaf<(i64 imm), + [{ return isUInt<16>(N->getZExtValue()); }]>; + +def immZExt5_64 : ImmLeaf; + +// Transformation function: get log2 of low 32 bits of immediate +def Log2LO : SDNodeXFormgetZExtValue())); +}]>; + +// Transformation function: get log2 of high 32 bits of immediate +def Log2HI : SDNodeXFormgetZExtValue() >> 32))); +}]>; + +// Predicate: True if immediate is a power of 2 and fits 32 bits +def PowerOf2LO : PatLeaf<(imm), [{ + if (N->getValueType(0) == MVT::i64) { + uint64_t Imm = N->getZExtValue(); + return isPowerOf2_64(Imm) && (Imm & 0xffffffff) == Imm; + } + else + return false; +}]>; + +// Predicate: True if immediate is a power of 2 and exceeds 32 bits +def PowerOf2HI : PatLeaf<(imm), [{ + if (N->getValueType(0) == MVT::i64) { + uint64_t Imm = N->getZExtValue(); + return isPowerOf2_64(Imm) && (Imm & 0xffffffff00000000) == Imm; + } + else + return false; +}]>; + +def PowerOf2LO_i32 : PatLeaf<(imm), [{ + if (N->getValueType(0) == MVT::i32) { + uint64_t Imm = N->getZExtValue(); + return isPowerOf2_32(Imm) && isUInt<32>(Imm); + } + else + return false; +}]>; + +def assertzext_lt_i32 : PatFrag<(ops node:$src), (assertzext node:$src), [{ + return cast(N->getOperand(1))->getVT().bitsLT(MVT::i32); +}]>; + +//===----------------------------------------------------------------------===// +// Instructions specific format +//===----------------------------------------------------------------------===// +let usesCustomInserter = 1 in { + def ATOMIC_LOAD_ADD_I64 : Atomic2Ops; + def ATOMIC_LOAD_SUB_I64 : Atomic2Ops; + def ATOMIC_LOAD_AND_I64 : Atomic2Ops; + def ATOMIC_LOAD_OR_I64 : Atomic2Ops; + def ATOMIC_LOAD_XOR_I64 : Atomic2Ops; + def ATOMIC_LOAD_NAND_I64 : Atomic2Ops; + def ATOMIC_SWAP_I64 : Atomic2Ops; + def ATOMIC_CMP_SWAP_I64 : AtomicCmpSwap; +} + +/// Pseudo instructions for loading and storing accumulator registers. +let isPseudo = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { + def LOAD_ACC128 : Load<"", ACC128>; + def STORE_ACC128 : Store<"", ACC128>; +} + +//===----------------------------------------------------------------------===// +// Instruction definition +//===----------------------------------------------------------------------===// +let DecoderNamespace = "Maxis64" in { +/// Arithmetic Instructions (ALU Immediate) +def DADDi : ArithLogicI<"daddi", simm16_64, GPR64Opnd, II_DADDI>, + ADDI_FM<0x18>, ISA_MAXIS3_NOT_32R6_64R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def DADDiu : ArithLogicI<"daddiu", simm16_64, GPR64Opnd, II_DADDIU, + immSExt16, add>, + ADDI_FM<0x19>, IsAsCheapAsAMove, ISA_MAXIS3; +} + +let isCodeGenOnly = 1 in { +def SLTi64 : SetCC_I<"slti", setlt, simm16_64, immSExt16, GPR64Opnd>, + SLTI_FM<0xa>; +def SLTiu64 : SetCC_I<"sltiu", setult, simm16_64, immSExt16, GPR64Opnd>, + SLTI_FM<0xb>; +def ANDi64 : ArithLogicI<"andi", uimm16_64, GPR64Opnd, II_AND, immZExt16, and>, + ADDI_FM<0xc>; +def ORi64 : ArithLogicI<"ori", uimm16_64, GPR64Opnd, II_OR, immZExt16, or>, + ADDI_FM<0xd>; +def XORi64 : ArithLogicI<"xori", uimm16_64, GPR64Opnd, II_XOR, immZExt16, xor>, + ADDI_FM<0xe>; +def LUi64 : LoadUpper<"lui", GPR64Opnd, uimm16_64_relaxed>, LUI_FM; +} + +/// Arithmetic Instructions (3-Operand, R-Type) +let AdditionalPredicates = [NotInMicroMaxis] in { + def DADD : ArithLogicR<"dadd", GPR64Opnd, 1, II_DADD>, ADD_FM<0, 0x2c>, + ISA_MAXIS3; + def DADDu : ArithLogicR<"daddu", GPR64Opnd, 1, II_DADDU, add>, + ADD_FM<0, 0x2d>, ISA_MAXIS3; + def DSUBu : ArithLogicR<"dsubu", GPR64Opnd, 0, II_DSUBU, sub>, + ADD_FM<0, 0x2f>, ISA_MAXIS3; + def DSUB : ArithLogicR<"dsub", GPR64Opnd, 0, II_DSUB>, ADD_FM<0, 0x2e>, + ISA_MAXIS3; +} + +let isCodeGenOnly = 1 in { +def SLT64 : SetCC_R<"slt", setlt, GPR64Opnd>, ADD_FM<0, 0x2a>; +def SLTu64 : SetCC_R<"sltu", setult, GPR64Opnd>, ADD_FM<0, 0x2b>; +def AND64 : ArithLogicR<"and", GPR64Opnd, 1, II_AND, and>, ADD_FM<0, 0x24>; +def OR64 : ArithLogicR<"or", GPR64Opnd, 1, II_OR, or>, ADD_FM<0, 0x25>; +def XOR64 : ArithLogicR<"xor", GPR64Opnd, 1, II_XOR, xor>, ADD_FM<0, 0x26>; +def NOR64 : LogicNOR<"nor", GPR64Opnd>, ADD_FM<0, 0x27>; +} + +/// Shift Instructions +let AdditionalPredicates = [NotInMicroMaxis] in { + def DSLL : shift_rotate_imm<"dsll", uimm6, GPR64Opnd, II_DSLL, shl, + immZExt6>, + SRA_FM<0x38, 0>, ISA_MAXIS3; + def DSRL : shift_rotate_imm<"dsrl", uimm6, GPR64Opnd, II_DSRL, srl, + immZExt6>, + SRA_FM<0x3a, 0>, ISA_MAXIS3; + def DSRA : shift_rotate_imm<"dsra", uimm6, GPR64Opnd, II_DSRA, sra, + immZExt6>, + SRA_FM<0x3b, 0>, ISA_MAXIS3; + def DSLLV : shift_rotate_reg<"dsllv", GPR64Opnd, II_DSLLV, shl>, + SRLV_FM<0x14, 0>, ISA_MAXIS3; + def DSRAV : shift_rotate_reg<"dsrav", GPR64Opnd, II_DSRAV, sra>, + SRLV_FM<0x17, 0>, ISA_MAXIS3; + def DSRLV : shift_rotate_reg<"dsrlv", GPR64Opnd, II_DSRLV, srl>, + SRLV_FM<0x16, 0>, ISA_MAXIS3; + def DSLL32 : shift_rotate_imm<"dsll32", uimm5, GPR64Opnd, II_DSLL32>, + SRA_FM<0x3c, 0>, ISA_MAXIS3; + def DSRL32 : shift_rotate_imm<"dsrl32", uimm5, GPR64Opnd, II_DSRL32>, + SRA_FM<0x3e, 0>, ISA_MAXIS3; + def DSRA32 : shift_rotate_imm<"dsra32", uimm5, GPR64Opnd, II_DSRA32>, + SRA_FM<0x3f, 0>, ISA_MAXIS3; + +// Rotate Instructions + def DROTR : shift_rotate_imm<"drotr", uimm6, GPR64Opnd, II_DROTR, rotr, + immZExt6>, + SRA_FM<0x3a, 1>, ISA_MAXIS64R2; + def DROTRV : shift_rotate_reg<"drotrv", GPR64Opnd, II_DROTRV, rotr>, + SRLV_FM<0x16, 1>, ISA_MAXIS64R2; + def DROTR32 : shift_rotate_imm<"drotr32", uimm5, GPR64Opnd, II_DROTR32>, + SRA_FM<0x3e, 1>, ISA_MAXIS64R2; +} + +/// Load and Store Instructions +/// aligned +let isCodeGenOnly = 1 in { +def LB64 : Load<"lb", GPR64Opnd, sextloadi8, II_LB>, LW_FM<0x20>; +def LBu64 : Load<"lbu", GPR64Opnd, zextloadi8, II_LBU>, LW_FM<0x24>; +def LH64 : Load<"lh", GPR64Opnd, sextloadi16, II_LH>, LW_FM<0x21>; +def LHu64 : Load<"lhu", GPR64Opnd, zextloadi16, II_LHU>, LW_FM<0x25>; +def LW64 : Load<"lw", GPR64Opnd, sextloadi32, II_LW>, LW_FM<0x23>; +def SB64 : Store<"sb", GPR64Opnd, truncstorei8, II_SB>, LW_FM<0x28>; +def SH64 : Store<"sh", GPR64Opnd, truncstorei16, II_SH>, LW_FM<0x29>; +def SW64 : Store<"sw", GPR64Opnd, truncstorei32, II_SW>, LW_FM<0x2b>; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def LWu : MMRel, Load<"lwu", GPR64Opnd, zextloadi32, II_LWU>, + LW_FM<0x27>, ISA_MAXIS3; + def LD : LoadMemory<"ld", GPR64Opnd, mem_simm16, load, II_LD>, + LW_FM<0x37>, ISA_MAXIS3; + def SD : StoreMemory<"sd", GPR64Opnd, mem_simm16, store, II_SD>, + LW_FM<0x3f>, ISA_MAXIS3; +} + + + +/// load/store left/right +let isCodeGenOnly = 1 in { +def LWL64 : LoadLeftRight<"lwl", MaxisLWL, GPR64Opnd, II_LWL>, LW_FM<0x22>; +def LWR64 : LoadLeftRight<"lwr", MaxisLWR, GPR64Opnd, II_LWR>, LW_FM<0x26>; +def SWL64 : StoreLeftRight<"swl", MaxisSWL, GPR64Opnd, II_SWL>, LW_FM<0x2a>; +def SWR64 : StoreLeftRight<"swr", MaxisSWR, GPR64Opnd, II_SWR>, LW_FM<0x2e>; +} + +def LDL : LoadLeftRight<"ldl", MaxisLDL, GPR64Opnd, II_LDL>, LW_FM<0x1a>, + ISA_MAXIS3_NOT_32R6_64R6; +def LDR : LoadLeftRight<"ldr", MaxisLDR, GPR64Opnd, II_LDR>, LW_FM<0x1b>, + ISA_MAXIS3_NOT_32R6_64R6; +def SDL : StoreLeftRight<"sdl", MaxisSDL, GPR64Opnd, II_SDL>, LW_FM<0x2c>, + ISA_MAXIS3_NOT_32R6_64R6; +def SDR : StoreLeftRight<"sdr", MaxisSDR, GPR64Opnd, II_SDR>, LW_FM<0x2d>, + ISA_MAXIS3_NOT_32R6_64R6; + +/// Load-linked, Store-conditional +let AdditionalPredicates = [NotInMicroMaxis] in { + def LLD : LLBase<"lld", GPR64Opnd, mem_simm16>, LW_FM<0x34>, + ISA_MAXIS3_NOT_32R6_64R6; +} +def SCD : SCBase<"scd", GPR64Opnd>, LW_FM<0x3c>, ISA_MAXIS3_NOT_32R6_64R6; + +let AdditionalPredicates = [NotInMicroMaxis], + DecoderNamespace = "Maxis32_64_PTR64" in { +def LL64 : LLBase<"ll", GPR32Opnd>, LW_FM<0x30>, PTR_64, + ISA_MAXIS2_NOT_32R6_64R6; +def SC64 : SCBase<"sc", GPR32Opnd>, LW_FM<0x38>, PTR_64, + ISA_MAXIS2_NOT_32R6_64R6; +def JR64 : IndirectBranch<"jr", GPR64Opnd>, MTLO_FM<8>, PTR_64; +} + +def JALR64 : JumpLinkReg<"jalr", GPR64Opnd>, JALR_FM; + +/// Jump and Branch Instructions +let isCodeGenOnly = 1 in { + def BEQ64 : CBranch<"beq", brtarget, seteq, GPR64Opnd>, BEQ_FM<4>; + def BNE64 : CBranch<"bne", brtarget, setne, GPR64Opnd>, BEQ_FM<5>; + def BGEZ64 : CBranchZero<"bgez", brtarget, setge, GPR64Opnd>, BGEZ_FM<1, 1>; + def BGTZ64 : CBranchZero<"bgtz", brtarget, setgt, GPR64Opnd>, BGEZ_FM<7, 0>; + def BLEZ64 : CBranchZero<"blez", brtarget, setle, GPR64Opnd>, BGEZ_FM<6, 0>; + def BLTZ64 : CBranchZero<"bltz", brtarget, setlt, GPR64Opnd>, BGEZ_FM<1, 0>; + def JALR64Pseudo : JumpLinkRegPseudo; +} + +def TAILCALLREG64 : TailCallReg; + +def PseudoReturn64 : PseudoReturnBase; +def PseudoIndirectBranch64 : PseudoIndirectBranchBase; + +/// Multiply and Divide Instructions. +let AdditionalPredicates = [NotInMicroMaxis] in { + def DMULT : Mult<"dmult", II_DMULT, GPR64Opnd, [HI0_64, LO0_64]>, + MULT_FM<0, 0x1c>, ISA_MAXIS3_NOT_32R6_64R6; + def DMULTu : Mult<"dmultu", II_DMULTU, GPR64Opnd, [HI0_64, LO0_64]>, + MULT_FM<0, 0x1d>, ISA_MAXIS3_NOT_32R6_64R6; +} +def PseudoDMULT : MultDivPseudo, ISA_MAXIS3_NOT_32R6_64R6; +def PseudoDMULTu : MultDivPseudo, ISA_MAXIS3_NOT_32R6_64R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def DSDIV : Div<"ddiv", II_DDIV, GPR64Opnd, [HI0_64, LO0_64]>, + MULT_FM<0, 0x1e>, ISA_MAXIS3_NOT_32R6_64R6; + def DUDIV : Div<"ddivu", II_DDIVU, GPR64Opnd, [HI0_64, LO0_64]>, + MULT_FM<0, 0x1f>, ISA_MAXIS3_NOT_32R6_64R6; +} +def PseudoDSDIV : MultDivPseudo, ISA_MAXIS3_NOT_32R6_64R6; +def PseudoDUDIV : MultDivPseudo, ISA_MAXIS3_NOT_32R6_64R6; + +let isCodeGenOnly = 1 in { +def MTHI64 : MoveToLOHI<"mthi", GPR64Opnd, [HI0_64]>, MTLO_FM<0x11>, + ISA_MAXIS3_NOT_32R6_64R6; +def MTLO64 : MoveToLOHI<"mtlo", GPR64Opnd, [LO0_64]>, MTLO_FM<0x13>, + ISA_MAXIS3_NOT_32R6_64R6; +def MFHI64 : MoveFromLOHI<"mfhi", GPR64Opnd, AC0_64>, MFLO_FM<0x10>, + ISA_MAXIS3_NOT_32R6_64R6; +def MFLO64 : MoveFromLOHI<"mflo", GPR64Opnd, AC0_64>, MFLO_FM<0x12>, + ISA_MAXIS3_NOT_32R6_64R6; +def PseudoMFHI64 : PseudoMFLOHI, + ISA_MAXIS3_NOT_32R6_64R6; +def PseudoMFLO64 : PseudoMFLOHI, + ISA_MAXIS3_NOT_32R6_64R6; +def PseudoMTLOHI64 : PseudoMTLOHI, ISA_MAXIS3_NOT_32R6_64R6; + +/// Sign Ext In Register Instructions. +def SEB64 : SignExtInReg<"seb", i8, GPR64Opnd, II_SEB>, SEB_FM<0x10, 0x20>, + ISA_MAXIS32R2; +def SEH64 : SignExtInReg<"seh", i16, GPR64Opnd, II_SEH>, SEB_FM<0x18, 0x20>, + ISA_MAXIS32R2; +} + +/// Count Leading +let AdditionalPredicates = [NotInMicroMaxis] in { + def DCLZ : CountLeading0<"dclz", GPR64Opnd, II_DCLZ>, CLO_FM<0x24>, + ISA_MAXIS64_NOT_64R6; + def DCLO : CountLeading1<"dclo", GPR64Opnd, II_DCLO>, CLO_FM<0x25>, + ISA_MAXIS64_NOT_64R6; + +/// Double Word Swap Bytes/HalfWords + def DSBH : SubwordSwap<"dsbh", GPR64Opnd, II_DSBH>, SEB_FM<2, 0x24>, + ISA_MAXIS64R2; + def DSHD : SubwordSwap<"dshd", GPR64Opnd, II_DSHD>, SEB_FM<5, 0x24>, + ISA_MAXIS64R2; +} + +def LEA_ADDiu64 : EffectiveAddress<"daddiu", GPR64Opnd>, LW_FM<0x19>; + +let isCodeGenOnly = 1 in +def RDHWR64 : ReadHardware, RDHWR_FM; + +let AdditionalPredicates = [NotInMicroMaxis] in { + // The 'pos + size' constraints for code generation are enforced by the + // code that lowers into MaxisISD::Ext. + // For assembly parsing, we alias dextu and dextm to dext, and match by + // operand were possible then check the 'pos + size' in MaxisAsmParser. + // We override the generated decoder to enforce that dext always comes out + // for dextm and dextu like binutils. + let DecoderMethod = "DecodeDEXT" in { + def DEXT : ExtBase<"dext", GPR64Opnd, uimm5_report_uimm6, + uimm5_plus1_report_uimm6, immZExt5, immZExt5Plus1, + MaxisExt>, EXT_FM<3>, ISA_MAXIS64R2; + def DEXTM : ExtBase<"dextm", GPR64Opnd, uimm5, uimm5_plus33, immZExt5, + immZExt5Plus33, MaxisExt>, EXT_FM<1>, ISA_MAXIS64R2; + def DEXTU : ExtBase<"dextu", GPR64Opnd, uimm5_plus32, uimm5_plus1, + immZExt5Plus32, immZExt5Plus1, MaxisExt>, EXT_FM<2>, + ISA_MAXIS64R2; + } + // The 'pos + size' constraints for code generation are enforced by the + // code that lowers into MaxisISD::Ins. + // For assembly parsing, we alias dinsu and dinsm to dins, and match by + // operand were possible then check the 'pos + size' in MaxisAsmParser. + // We override the generated decoder to enforce that dins always comes out + // for dinsm and dinsu like binutils. + let DecoderMethod = "DecodeDINS" in { + def DINS : InsBase<"dins", GPR64Opnd, uimm6, uimm5_inssize_plus1, + immZExt5, immZExt5Plus1>, EXT_FM<7>, + ISA_MAXIS64R2; + def DINSU : InsBase<"dinsu", GPR64Opnd, uimm5_plus32, uimm5_inssize_plus1, + immZExt5Plus32, immZExt5Plus1>, + EXT_FM<6>, ISA_MAXIS64R2; + def DINSM : InsBase<"dinsm", GPR64Opnd, uimm5, uimm_range_2_64, + immZExt5, immZExtRange2To64>, + EXT_FM<5>, ISA_MAXIS64R2; + } +} + +let isCodeGenOnly = 1, AdditionalPredicates = [NotInMicroMaxis] in { + def DEXT64_32 : InstSE<(outs GPR64Opnd:$rt), + (ins GPR32Opnd:$rs, uimm5_report_uimm6:$pos, + uimm5_plus1:$size), + "dext $rt, $rs, $pos, $size", [], II_EXT, FrmR, "dext">, + EXT_FM<3>, ISA_MAXIS64R2; +} + +let isCodeGenOnly = 1, rs = 0, shamt = 0 in { + def DSLL64_32 : FR<0x00, 0x3c, (outs GPR64:$rd), (ins GPR32:$rt), + "dsll\t$rd, $rt, 32", [], II_DSLL>; + def SLL64_32 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR32:$rt), + "sll\t$rd, $rt, 0", [], II_SLL>; + def SLL64_64 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR64:$rt), + "sll\t$rd, $rt, 0", [], II_SLL>; +} + +// We need the following pseudo instruction to avoid offset calculation for +// long branches. See the comment in file MaxisLongBranch.cpp for detailed +// explanation. + +// Expands to: daddiu $dst, $src, %PART($tgt - $baltgt) +// where %PART may be %hi or %lo, depending on the relocation kind +// that $tgt is annotated with. +def LONG_BRANCH_DADDiu : PseudoSE<(outs GPR64Opnd:$dst), + (ins GPR64Opnd:$src, brtarget:$tgt, brtarget:$baltgt), []>; + +// Cavium Octeon cnMAXIS instructions +let DecoderNamespace = "CnMaxis", + // FIXME: The lack of HasStdEnc is probably a bug + EncodingPredicates = [] in { + +class Count1s: + InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), + [(set RO:$rd, (ctpop RO:$rs))], II_POP, FrmR, opstr> { + let TwoOperandAliasConstraint = "$rd = $rs"; +} + +class ExtsCins: + InstSE<(outs RO:$rt), (ins RO:$rs, uimm5:$pos, uimm5:$lenm1), + !strconcat(opstr, "\t$rt, $rs, $pos, $lenm1"), + [(set RO:$rt, (Op RO:$rs, PosImm:$pos, imm:$lenm1))], + itin, FrmR, opstr> { + let TwoOperandAliasConstraint = "$rt = $rs"; +} + +class SetCC64_R : + InstSE<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), + !strconcat(opstr, "\t$rd, $rs, $rt"), + [(set GPR64Opnd:$rd, (zext (cond_op GPR64Opnd:$rs, + GPR64Opnd:$rt)))], + II_SEQ_SNE, FrmR, opstr> { + let TwoOperandAliasConstraint = "$rd = $rs"; +} + +class SetCC64_I: + InstSE<(outs GPR64Opnd:$rt), (ins GPR64Opnd:$rs, simm10_64:$imm10), + !strconcat(opstr, "\t$rt, $rs, $imm10"), + [(set GPR64Opnd:$rt, (zext (cond_op GPR64Opnd:$rs, + immSExt10_64:$imm10)))], + II_SEQI_SNEI, FrmI, opstr> { + let TwoOperandAliasConstraint = "$rt = $rs"; +} + +class CBranchBitNum shift = 1> : + InstSE<(outs), (ins RO:$rs, ImmOp:$p, opnd:$offset), + !strconcat(opstr, "\t$rs, $p, $offset"), + [(brcond (i32 (cond_op (and RO:$rs, (shl shift, immZExt5_64:$p)), 0)), + bb:$offset)], II_BBIT, FrmI, opstr> { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 1; + let Defs = [AT]; +} + +class MFC2OP : + InstSE<(outs RO:$rt, uimm16:$imm16), (ins), + !strconcat(asmstr, "\t$rt, $imm16"), [], itin, FrmFR>; + +// Unsigned Byte Add +def BADDu : ArithLogicR<"baddu", GPR64Opnd, 1, II_BADDU>, + ADD_FM<0x1c, 0x28>, ASE_CNMAXIS { + let Pattern = [(set GPR64Opnd:$rd, + (and (add GPR64Opnd:$rs, GPR64Opnd:$rt), 255))]; +} + +// Branch on Bit Clear /+32 +def BBIT0 : CBranchBitNum<"bbit0", brtarget, seteq, GPR64Opnd, + uimm5_64_report_uimm6>, BBIT_FM<0x32>, ASE_CNMAXIS; +def BBIT032: CBranchBitNum<"bbit032", brtarget, seteq, GPR64Opnd, uimm5_64, + 0x100000000>, BBIT_FM<0x36>, ASE_CNMAXIS; + +// Branch on Bit Set /+32 +def BBIT1 : CBranchBitNum<"bbit1", brtarget, setne, GPR64Opnd, + uimm5_64_report_uimm6>, BBIT_FM<0x3a>, ASE_CNMAXIS; +def BBIT132: CBranchBitNum<"bbit132", brtarget, setne, GPR64Opnd, uimm5_64, + 0x100000000>, BBIT_FM<0x3e>, ASE_CNMAXIS; + +// Multiply Doubleword to GPR +def DMUL : ArithLogicR<"dmul", GPR64Opnd, 1, II_DMUL, mul>, + ADD_FM<0x1c, 0x03>, ASE_CNMAXIS { + let Defs = [HI0, LO0, P0, P1, P2]; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + // Extract a signed bit field /+32 + def EXTS : ExtsCins<"exts", II_EXT, GPR64Opnd, immZExt5>, EXTS_FM<0x3a>, + ASE_MAXIS64_CNMAXIS; + def EXTS32: ExtsCins<"exts32", II_EXT, GPR64Opnd, immZExt5Plus32>, + EXTS_FM<0x3b>, ASE_MAXIS64_CNMAXIS; + + // Clear and insert a bit field /+32 + def CINS : ExtsCins<"cins", II_INS, GPR64Opnd, immZExt5, MaxisCIns>, + EXTS_FM<0x32>, ASE_MAXIS64_CNMAXIS; + def CINS32: ExtsCins<"cins32", II_INS, GPR64Opnd, immZExt5Plus32, MaxisCIns>, + EXTS_FM<0x33>, ASE_MAXIS64_CNMAXIS; + let isCodeGenOnly = 1 in { + def CINS_i32 : ExtsCins<"cins", II_INS, GPR32Opnd, immZExt5, MaxisCIns>, + EXTS_FM<0x32>, ASE_MAXIS64_CNMAXIS; + def CINS64_32 :InstSE<(outs GPR64Opnd:$rt), + (ins GPR32Opnd:$rs, uimm5:$pos, uimm5:$lenm1), + "cins\t$rt, $rs, $pos, $lenm1", [], II_INS, FrmR, + "cins">, + EXTS_FM<0x32>, ASE_MAXIS64_CNMAXIS; + } +} + +// Move to multiplier/product register +def MTM0 : MoveToLOHI<"mtm0", GPR64Opnd, [MPL0, P0, P1, P2]>, MTMR_FM<0x08>, + ASE_CNMAXIS; +def MTM1 : MoveToLOHI<"mtm1", GPR64Opnd, [MPL1, P0, P1, P2]>, MTMR_FM<0x0c>, + ASE_CNMAXIS; +def MTM2 : MoveToLOHI<"mtm2", GPR64Opnd, [MPL2, P0, P1, P2]>, MTMR_FM<0x0d>, + ASE_CNMAXIS; +def MTP0 : MoveToLOHI<"mtp0", GPR64Opnd, [P0]>, MTMR_FM<0x09>, ASE_CNMAXIS; +def MTP1 : MoveToLOHI<"mtp1", GPR64Opnd, [P1]>, MTMR_FM<0x0a>, ASE_CNMAXIS; +def MTP2 : MoveToLOHI<"mtp2", GPR64Opnd, [P2]>, MTMR_FM<0x0b>, ASE_CNMAXIS; + +// Count Ones in a Word/Doubleword +def POP : Count1s<"pop", GPR32Opnd>, POP_FM<0x2c>, ASE_CNMAXIS; +def DPOP : Count1s<"dpop", GPR64Opnd>, POP_FM<0x2d>, ASE_CNMAXIS; + +// Set on equal/not equal +def SEQ : SetCC64_R<"seq", seteq>, SEQ_FM<0x2a>, ASE_CNMAXIS; +def SEQi : SetCC64_I<"seqi", seteq>, SEQI_FM<0x2e>, ASE_CNMAXIS; +def SNE : SetCC64_R<"sne", setne>, SEQ_FM<0x2b>, ASE_CNMAXIS; +def SNEi : SetCC64_I<"snei", setne>, SEQI_FM<0x2f>, ASE_CNMAXIS; + +// 192-bit x 64-bit Unsigned Multiply and Add +def V3MULU: ArithLogicR<"v3mulu", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x11>, + ASE_CNMAXIS { + let Defs = [P0, P1, P2]; +} + +// 64-bit Unsigned Multiply and Add Move +def VMM0 : ArithLogicR<"vmm0", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x10>, + ASE_CNMAXIS { + let Defs = [MPL0, P0, P1, P2]; +} + +// 64-bit Unsigned Multiply and Add +def VMULU : ArithLogicR<"vmulu", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x0f>, + ASE_CNMAXIS { + let Defs = [MPL1, MPL2, P0, P1, P2]; +} + +// Move between CPU and coprocessor registers +def DMFC2_OCTEON : MFC2OP<"dmfc2", GPR64Opnd, II_DMFC2>, MFC2OP_FM<0x12, 1>, + ASE_CNMAXIS; +def DMTC2_OCTEON : MFC2OP<"dmtc2", GPR64Opnd, II_DMTC2>, MFC2OP_FM<0x12, 5>, + ASE_CNMAXIS; +} + +} + +/// Move between CPU and coprocessor registers +let DecoderNamespace = "Maxis64", Predicates = [HasMaxis64] in { +def DMFC0 : MFC3OP<"dmfc0", GPR64Opnd, COP0Opnd, II_DMFC0>, MFC3OP_FM<0x10, 1>, + ISA_MAXIS3; +def DMTC0 : MTC3OP<"dmtc0", COP0Opnd, GPR64Opnd, II_DMTC0>, MFC3OP_FM<0x10, 5>, + ISA_MAXIS3; +def DMFC2 : MFC3OP<"dmfc2", GPR64Opnd, COP2Opnd, II_DMFC2>, MFC3OP_FM<0x12, 1>, + ISA_MAXIS3; +def DMTC2 : MTC3OP<"dmtc2", COP2Opnd, GPR64Opnd, II_DMTC2>, MFC3OP_FM<0x12, 5>, + ISA_MAXIS3; +} + +//===----------------------------------------------------------------------===// +// Arbitrary patterns that map to one or more instructions +//===----------------------------------------------------------------------===// + +// Materialize i64 constants. +defm : MaterializeImms; + +def : MaxisPat<(i64 immZExt32Low16Zero:$imm), + (DSLL (ORi64 ZERO_64, (HI16 imm:$imm)), 16)>; + +def : MaxisPat<(i64 immZExt32:$imm), + (ORi64 (DSLL (ORi64 ZERO_64, (HI16 imm:$imm)), 16), + (LO16 imm:$imm))>; + +// extended loads +def : MaxisPat<(i64 (extloadi1 addr:$src)), (LB64 addr:$src)>; +def : MaxisPat<(i64 (extloadi8 addr:$src)), (LB64 addr:$src)>; +def : MaxisPat<(i64 (extloadi16 addr:$src)), (LH64 addr:$src)>; +def : MaxisPat<(i64 (extloadi32 addr:$src)), (LW64 addr:$src)>; + +// hi/lo relocs +let AdditionalPredicates = [NotInMicroMaxis] in +defm : MaxisHiLoRelocs, SYM_32; + +def : MaxisPat<(MaxisGotHi tglobaladdr:$in), (LUi64 tglobaladdr:$in)>; +def : MaxisPat<(MaxisGotHi texternalsym:$in), (LUi64 texternalsym:$in)>; + +// highest/higher/hi/lo relocs +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisPat<(MaxisJmpLink (i64 texternalsym:$dst)), + (JAL texternalsym:$dst)>, SYM_64; + def : MaxisPat<(MaxisHighest (i64 tglobaladdr:$in)), + (LUi64 tglobaladdr:$in)>, SYM_64; + def : MaxisPat<(MaxisHighest (i64 tblockaddress:$in)), + (LUi64 tblockaddress:$in)>, SYM_64; + def : MaxisPat<(MaxisHighest (i64 tjumptable:$in)), + (LUi64 tjumptable:$in)>, SYM_64; + def : MaxisPat<(MaxisHighest (i64 tconstpool:$in)), + (LUi64 tconstpool:$in)>, SYM_64; + def : MaxisPat<(MaxisHighest (i64 tglobaltlsaddr:$in)), + (LUi64 tglobaltlsaddr:$in)>, SYM_64; + def : MaxisPat<(MaxisHighest (i64 texternalsym:$in)), + (LUi64 texternalsym:$in)>, SYM_64; + + def : MaxisPat<(MaxisHigher (i64 tglobaladdr:$in)), + (DADDiu ZERO_64, tglobaladdr:$in)>, SYM_64; + def : MaxisPat<(MaxisHigher (i64 tblockaddress:$in)), + (DADDiu ZERO_64, tblockaddress:$in)>, SYM_64; + def : MaxisPat<(MaxisHigher (i64 tjumptable:$in)), + (DADDiu ZERO_64, tjumptable:$in)>, SYM_64; + def : MaxisPat<(MaxisHigher (i64 tconstpool:$in)), + (DADDiu ZERO_64, tconstpool:$in)>, SYM_64; + def : MaxisPat<(MaxisHigher (i64 tglobaltlsaddr:$in)), + (DADDiu ZERO_64, tglobaltlsaddr:$in)>, SYM_64; + def : MaxisPat<(MaxisHigher (i64 texternalsym:$in)), + (DADDiu ZERO_64, texternalsym:$in)>, SYM_64; + + def : MaxisPat<(add GPR64:$hi, (MaxisHigher (i64 tglobaladdr:$lo))), + (DADDiu GPR64:$hi, tglobaladdr:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisHigher (i64 tblockaddress:$lo))), + (DADDiu GPR64:$hi, tblockaddress:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisHigher (i64 tjumptable:$lo))), + (DADDiu GPR64:$hi, tjumptable:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisHigher (i64 tconstpool:$lo))), + (DADDiu GPR64:$hi, tconstpool:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisHigher (i64 tglobaltlsaddr:$lo))), + (DADDiu GPR64:$hi, tglobaltlsaddr:$lo)>, SYM_64; + + def : MaxisPat<(add GPR64:$hi, (MaxisHi (i64 tglobaladdr:$lo))), + (DADDiu GPR64:$hi, tglobaladdr:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisHi (i64 tblockaddress:$lo))), + (DADDiu GPR64:$hi, tblockaddress:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisHi (i64 tjumptable:$lo))), + (DADDiu GPR64:$hi, tjumptable:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisHi (i64 tconstpool:$lo))), + (DADDiu GPR64:$hi, tconstpool:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisHi (i64 tglobaltlsaddr:$lo))), + (DADDiu GPR64:$hi, tglobaltlsaddr:$lo)>, SYM_64; + + def : MaxisPat<(add GPR64:$hi, (MaxisLo (i64 tglobaladdr:$lo))), + (DADDiu GPR64:$hi, tglobaladdr:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisLo (i64 tblockaddress:$lo))), + (DADDiu GPR64:$hi, tblockaddress:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisLo (i64 tjumptable:$lo))), + (DADDiu GPR64:$hi, tjumptable:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisLo (i64 tconstpool:$lo))), + (DADDiu GPR64:$hi, tconstpool:$lo)>, SYM_64; + def : MaxisPat<(add GPR64:$hi, (MaxisLo (i64 tglobaltlsaddr:$lo))), + (DADDiu GPR64:$hi, tglobaltlsaddr:$lo)>, SYM_64; +} + +// gp_rel relocs +def : MaxisPat<(add GPR64:$gp, (MaxisGPRel tglobaladdr:$in)), + (DADDiu GPR64:$gp, tglobaladdr:$in)>, ABI_N64; +def : MaxisPat<(add GPR64:$gp, (MaxisGPRel tconstpool:$in)), + (DADDiu GPR64:$gp, tconstpool:$in)>, ABI_N64; + +def : WrapperPat; +def : WrapperPat; +def : WrapperPat; +def : WrapperPat; +def : WrapperPat; +def : WrapperPat; + + +defm : BrcondPats; +def : MaxisPat<(brcond (i32 (setlt i64:$lhs, 1)), bb:$dst), + (BLEZ64 i64:$lhs, bb:$dst)>; +def : MaxisPat<(brcond (i32 (setgt i64:$lhs, -1)), bb:$dst), + (BGEZ64 i64:$lhs, bb:$dst)>; + +// setcc patterns +let AdditionalPredicates = [NotInMicroMaxis] in { + defm : SeteqPats; + defm : SetlePats; + defm : SetgtPats; + defm : SetgePats; + defm : SetgeImmPats; +} +// truncate +def : MaxisPat<(trunc (assertsext GPR64:$src)), + (EXTRACT_SUBREG GPR64:$src, sub_32)>; +// The forward compatibility strategy employed by MAXIS requires us to treat +// values as being sign extended to an infinite number of bits. This allows +// existing software to run without modification on any future MAXIS +// implementation (e.g. 128-bit, or 1024-bit). Being compatible with this +// strategy requires that truncation acts as a sign-extension for values being +// fed into instructions operating on 32-bit values. Such instructions have +// undefined results if this is not true. +// For our case, this means that we can't issue an extract_subreg for nodes +// such as (trunc:i32 (assertzext:i64 X, i32)), because the sign-bit of the +// lower subreg would not be replicated into the upper half. +def : MaxisPat<(trunc (assertzext_lt_i32 GPR64:$src)), + (EXTRACT_SUBREG GPR64:$src, sub_32)>; +def : MaxisPat<(i32 (trunc GPR64:$src)), + (SLL (EXTRACT_SUBREG GPR64:$src, sub_32), 0)>; + +// variable shift instructions patterns +def : MaxisPat<(shl GPR64:$rt, (i32 (trunc GPR64:$rs))), + (DSLLV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>; +def : MaxisPat<(srl GPR64:$rt, (i32 (trunc GPR64:$rs))), + (DSRLV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>; +def : MaxisPat<(sra GPR64:$rt, (i32 (trunc GPR64:$rs))), + (DSRAV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisPat<(rotr GPR64:$rt, (i32 (trunc GPR64:$rs))), + (DROTRV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>; +} + +// 32-to-64-bit extension +def : MaxisPat<(i64 (anyext GPR32:$src)), + (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$src, sub_32)>; +def : MaxisPat<(i64 (zext GPR32:$src)), (DSRL (DSLL64_32 GPR32:$src), 32)>; +def : MaxisPat<(i64 (sext GPR32:$src)), (SLL64_32 GPR32:$src)>; + +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisPat<(i64 (zext GPR32:$src)), (DEXT64_32 GPR32:$src, 0, 32)>, + ISA_MAXIS64R2; + def : MaxisPat<(i64 (zext (i32 (shl GPR32:$rt, immZExt5:$imm)))), + (CINS64_32 GPR32:$rt, imm:$imm, (immZExt5To31 imm:$imm))>, + ASE_MAXIS64_CNMAXIS; +} + +// Sign extend in register +def : MaxisPat<(i64 (sext_inreg GPR64:$src, i32)), + (SLL64_64 GPR64:$src)>; + +// bswap MaxisPattern +def : MaxisPat<(bswap GPR64:$rt), (DSHD (DSBH GPR64:$rt))>; + +// Carry pattern +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisPat<(subc GPR64:$lhs, GPR64:$rhs), + (DSUBu GPR64:$lhs, GPR64:$rhs)>; + def : MaxisPat<(addc GPR64:$lhs, GPR64:$rhs), + (DADDu GPR64:$lhs, GPR64:$rhs)>, ASE_NOT_DSP; + def : MaxisPat<(addc GPR64:$lhs, immSExt16:$imm), + (DADDiu GPR64:$lhs, imm:$imm)>, ASE_NOT_DSP; +} + +// Octeon bbit0/bbit1 MaxisPattern +def : MaxisPat<(brcond (i32 (seteq (and i64:$lhs, PowerOf2LO:$mask), 0)), bb:$dst), + (BBIT0 i64:$lhs, (Log2LO PowerOf2LO:$mask), bb:$dst)>, ASE_MAXIS64_CNMAXIS; +def : MaxisPat<(brcond (i32 (seteq (and i64:$lhs, PowerOf2HI:$mask), 0)), bb:$dst), + (BBIT032 i64:$lhs, (Log2HI PowerOf2HI:$mask), bb:$dst)>, ASE_MAXIS64_CNMAXIS; +def : MaxisPat<(brcond (i32 (setne (and i64:$lhs, PowerOf2LO:$mask), 0)), bb:$dst), + (BBIT1 i64:$lhs, (Log2LO PowerOf2LO:$mask), bb:$dst)>, ASE_MAXIS64_CNMAXIS; +def : MaxisPat<(brcond (i32 (setne (and i64:$lhs, PowerOf2HI:$mask), 0)), bb:$dst), + (BBIT132 i64:$lhs, (Log2HI PowerOf2HI:$mask), bb:$dst)>, ASE_MAXIS64_CNMAXIS; +def : MaxisPat<(brcond (i32 (seteq (and i32:$lhs, PowerOf2LO_i32:$mask), 0)), bb:$dst), + (BBIT0 (INSERT_SUBREG (i64 (IMPLICIT_DEF)), i32:$lhs, sub_32), + (Log2LO PowerOf2LO_i32:$mask), bb:$dst)>, ASE_MAXIS64_CNMAXIS; +def : MaxisPat<(brcond (i32 (setne (and i32:$lhs, PowerOf2LO_i32:$mask), 0)), bb:$dst), + (BBIT1 (INSERT_SUBREG (i64 (IMPLICIT_DEF)), i32:$lhs, sub_32), + (Log2LO PowerOf2LO_i32:$mask), bb:$dst)>, ASE_MAXIS64_CNMAXIS; + +// Atomic load patterns. +def : MaxisPat<(atomic_load_8 addr:$a), (LB64 addr:$a)>; +def : MaxisPat<(atomic_load_16 addr:$a), (LH64 addr:$a)>; +def : MaxisPat<(atomic_load_32 addr:$a), (LW64 addr:$a)>; +def : MaxisPat<(atomic_load_64 addr:$a), (LD addr:$a)>; + +// Atomic store patterns. +def : MaxisPat<(atomic_store_8 addr:$a, GPR64:$v), (SB64 GPR64:$v, addr:$a)>; +def : MaxisPat<(atomic_store_16 addr:$a, GPR64:$v), (SH64 GPR64:$v, addr:$a)>; +def : MaxisPat<(atomic_store_32 addr:$a, GPR64:$v), (SW64 GPR64:$v, addr:$a)>; +def : MaxisPat<(atomic_store_64 addr:$a, GPR64:$v), (SD GPR64:$v, addr:$a)>; + +//===----------------------------------------------------------------------===// +// Instruction aliases +//===----------------------------------------------------------------------===// +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"move $dst, $src", + (OR64 GPR64Opnd:$dst, GPR64Opnd:$src, ZERO_64), 1>, + GPR_64; + def : MaxisInstAlias<"move $dst, $src", + (DADDu GPR64Opnd:$dst, GPR64Opnd:$src, ZERO_64), 1>, + GPR_64; + def : MaxisInstAlias<"dadd $rs, $rt, $imm", + (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, simm16_64:$imm), + 0>, ISA_MAXIS3_NOT_32R6_64R6; + def : MaxisInstAlias<"dadd $rs, $imm", + (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, simm16_64:$imm), + 0>, ISA_MAXIS3_NOT_32R6_64R6; + def : MaxisInstAlias<"daddu $rs, $rt, $imm", + (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rt, simm16_64:$imm), + 0>, ISA_MAXIS3; + def : MaxisInstAlias<"daddu $rs, $imm", + (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rs, simm16_64:$imm), + 0>, ISA_MAXIS3; + + defm : OneOrTwoOperandMacroImmediateAlias<"and", ANDi64, GPR64Opnd, imm64>, + GPR_64; + + defm : OneOrTwoOperandMacroImmediateAlias<"or", ORi64, GPR64Opnd, imm64>, + GPR_64; + + defm : OneOrTwoOperandMacroImmediateAlias<"xor", XORi64, GPR64Opnd, imm64>, + GPR_64; +} +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"dneg $rt, $rs", + (DSUB GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rs), 1>, + ISA_MAXIS3; + def : MaxisInstAlias<"dneg $rt", + (DSUB GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rt), 1>, + ISA_MAXIS3; + def : MaxisInstAlias<"dnegu $rt, $rs", + (DSUBu GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rs), 1>, + ISA_MAXIS3; + def : MaxisInstAlias<"dnegu $rt", + (DSUBu GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rt), 1>, + ISA_MAXIS3; +} +def : MaxisInstAlias<"dsubi $rs, $rt, $imm", + (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, + InvertedImOperand64:$imm), + 0>, ISA_MAXIS3_NOT_32R6_64R6; +def : MaxisInstAlias<"dsubi $rs, $imm", + (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, + InvertedImOperand64:$imm), + 0>, ISA_MAXIS3_NOT_32R6_64R6; +def : MaxisInstAlias<"dsub $rs, $rt, $imm", + (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, + InvertedImOperand64:$imm), + 0>, ISA_MAXIS3_NOT_32R6_64R6; +def : MaxisInstAlias<"dsub $rs, $imm", + (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, + InvertedImOperand64:$imm), + 0>, ISA_MAXIS3_NOT_32R6_64R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"dsubu $rt, $rs, $imm", + (DADDiu GPR64Opnd:$rt, GPR64Opnd:$rs, + InvertedImOperand64:$imm), 0>, ISA_MAXIS3; + def : MaxisInstAlias<"dsubu $rs, $imm", + (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rs, + InvertedImOperand64:$imm), 0>, ISA_MAXIS3; +} +def : MaxisInstAlias<"dsra $rd, $rt, $rs", + (DSRAV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, + ISA_MAXIS3; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"dsll $rd, $rt, $rs", + (DSLLV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, + ISA_MAXIS3; + def : MaxisInstAlias<"dsrl $rd, $rt, $rs", + (DSRLV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, + ISA_MAXIS3; + def : MaxisInstAlias<"dsrl $rd, $rt", + (DSRLV GPR64Opnd:$rd, GPR64Opnd:$rd, GPR32Opnd:$rt), 0>, + ISA_MAXIS3; + def : MaxisInstAlias<"dsll $rd, $rt", + (DSLLV GPR64Opnd:$rd, GPR64Opnd:$rd, GPR32Opnd:$rt), 0>, + ISA_MAXIS3; + def : MaxisInstAlias<"dins $rt, $rs, $pos, $size", + (DINSM GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5:$pos, + uimm_range_2_64:$size), 0>, ISA_MAXIS64R2; + def : MaxisInstAlias<"dins $rt, $rs, $pos, $size", + (DINSU GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32:$pos, + uimm5_plus1:$size), 0>, ISA_MAXIS64R2; + def : MaxisInstAlias<"dext $rt, $rs, $pos, $size", + (DEXTM GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5:$pos, + uimm5_plus33:$size), 0>, ISA_MAXIS64R2; + def : MaxisInstAlias<"dext $rt, $rs, $pos, $size", + (DEXTU GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32:$pos, + uimm5_plus1:$size), 0>, ISA_MAXIS64R2; + +// Two operand (implicit 0 selector) versions: + def : MaxisInstAlias<"dmtc0 $rt, $rd", + (DMTC0 COP0Opnd:$rd, GPR64Opnd:$rt, 0), 0>; + def : MaxisInstAlias<"dmfc0 $rt, $rd", + (DMFC0 GPR64Opnd:$rt, COP0Opnd:$rd, 0), 0>; +} +def : MaxisInstAlias<"dmfc2 $rt, $rd", (DMFC2 GPR64Opnd:$rt, COP2Opnd:$rd, 0), 0>; +def : MaxisInstAlias<"dmtc2 $rt, $rd", (DMTC2 COP2Opnd:$rd, GPR64Opnd:$rt, 0), 0>; + +def : MaxisInstAlias<"synciobdma", (SYNC 0x2), 0>, ASE_MAXIS64_CNMAXIS; +def : MaxisInstAlias<"syncs", (SYNC 0x6), 0>, ASE_MAXIS64_CNMAXIS; +def : MaxisInstAlias<"syncw", (SYNC 0x4), 0>, ASE_MAXIS64_CNMAXIS; +def : MaxisInstAlias<"syncws", (SYNC 0x5), 0>, ASE_MAXIS64_CNMAXIS; + +// cnMAXIS Aliases. + +// bbit* with $p 32-63 converted to bbit*32 with $p 0-31 +def : MaxisInstAlias<"bbit0 $rs, $p, $offset", + (BBIT032 GPR64Opnd:$rs, uimm5_plus32_normalize_64:$p, + brtarget:$offset), 0>, + ASE_CNMAXIS; +def : MaxisInstAlias<"bbit1 $rs, $p, $offset", + (BBIT132 GPR64Opnd:$rs, uimm5_plus32_normalize_64:$p, + brtarget:$offset), 0>, + ASE_CNMAXIS; + +// exts with $pos 32-63 in converted to exts32 with $pos 0-31 +def : MaxisInstAlias<"exts $rt, $rs, $pos, $lenm1", + (EXTS32 GPR64Opnd:$rt, GPR64Opnd:$rs, + uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, + ASE_MAXIS64_CNMAXIS; +def : MaxisInstAlias<"exts $rt, $pos, $lenm1", + (EXTS32 GPR64Opnd:$rt, GPR64Opnd:$rt, + uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, + ASE_MAXIS64_CNMAXIS; + +// cins with $pos 32-63 in converted to cins32 with $pos 0-31 +def : MaxisInstAlias<"cins $rt, $rs, $pos, $lenm1", + (CINS32 GPR64Opnd:$rt, GPR64Opnd:$rs, + uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, + ASE_MAXIS64_CNMAXIS; +def : MaxisInstAlias<"cins $rt, $pos, $lenm1", + (CINS32 GPR64Opnd:$rt, GPR64Opnd:$rt, + uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, + ASE_MAXIS64_CNMAXIS; + +//===----------------------------------------------------------------------===// +// Assembler Pseudo Instructions +//===----------------------------------------------------------------------===// + +class LoadImmediate64 : + MaxisAsmPseudoInst<(outs RO:$rt), (ins Od:$imm64), + !strconcat(instr_asm, "\t$rt, $imm64")> ; +def LoadImm64 : LoadImmediate64<"dli", imm64, GPR64Opnd>; + +def LoadAddrReg64 : MaxisAsmPseudoInst<(outs GPR64Opnd:$rt), (ins mem:$addr), + "dla\t$rt, $addr">; +def LoadAddrImm64 : MaxisAsmPseudoInst<(outs GPR64Opnd:$rt), (ins imm64:$imm64), + "dla\t$rt, $imm64">; + +def DMULImmMacro : MaxisAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, + simm32_relaxed:$imm), + "dmul\t$rs, $rt, $imm">, + ISA_MAXIS3_NOT_32R6_64R6; +def DMULOMacro : MaxisAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, + GPR64Opnd:$rd), + "dmulo\t$rs, $rt, $rd">, + ISA_MAXIS3_NOT_32R6_64R6; +def DMULOUMacro : MaxisAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, + GPR64Opnd:$rd), + "dmulou\t$rs, $rt, $rd">, + ISA_MAXIS3_NOT_32R6_64R6; + +def DMULMacro : MaxisAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, + GPR64Opnd:$rd), + "dmul\t$rs, $rt, $rd"> { + let InsnPredicates = [HasMaxis3, NotMaxis64r6, NotCnMaxis]; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def DSDivMacro : MaxisAsmPseudoInst<(outs GPR64Opnd:$rd), + (ins GPR64Opnd:$rs, GPR64Opnd:$rt), + "ddiv\t$rd, $rs, $rt">, + ISA_MAXIS3_NOT_32R6_64R6; + def DSDivIMacro : MaxisAsmPseudoInst<(outs GPR64Opnd:$rd), + (ins GPR64Opnd:$rs, imm64:$imm), + "ddiv\t$rd, $rs, $imm">, + ISA_MAXIS3_NOT_32R6_64R6; + def DUDivMacro : MaxisAsmPseudoInst<(outs GPR64Opnd:$rd), + (ins GPR64Opnd:$rs, GPR64Opnd:$rt), + "ddivu\t$rd, $rs, $rt">, + ISA_MAXIS3_NOT_32R6_64R6; + def DUDivIMacro : MaxisAsmPseudoInst<(outs GPR64Opnd:$rd), + (ins GPR64Opnd:$rs, imm64:$imm), + "ddivu\t$rd, $rs, $imm">, + ISA_MAXIS3_NOT_32R6_64R6; + + // GAS expands 'div' and 'ddiv' differently when the destination + // register is $zero and the instruction is in the two operand + // form. 'ddiv' gets expanded, while 'div' is not expanded. + + def : MaxisInstAlias<"ddiv $rs, $rt", (DSDivMacro GPR64Opnd:$rs, + GPR64Opnd:$rs, + GPR64Opnd:$rt), 0>, + ISA_MAXIS3_NOT_32R6_64R6; + def : MaxisInstAlias<"ddiv $rd, $imm", (DSDivIMacro GPR64Opnd:$rd, + GPR64Opnd:$rd, + imm64:$imm), 0>, + ISA_MAXIS3_NOT_32R6_64R6; + + // GAS expands 'divu' and 'ddivu' differently when the destination + // register is $zero and the instruction is in the two operand + // form. 'ddivu' gets expanded, while 'divu' is not expanded. + + def : MaxisInstAlias<"ddivu $rt, $rs", (DUDivMacro GPR64Opnd:$rt, + GPR64Opnd:$rt, + GPR64Opnd:$rs), 0>, + ISA_MAXIS3_NOT_32R6_64R6; + def : MaxisInstAlias<"ddivu $rd, $imm", (DUDivIMacro GPR64Opnd:$rd, + GPR64Opnd:$rd, + imm64:$imm), 0>, + ISA_MAXIS3_NOT_32R6_64R6; +} + +def NORImm64 : NORIMM_DESC_BASE, GPR_64; +def : MaxisInstAlias<"nor\t$rs, $imm", (NORImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, + imm64:$imm)>, GPR_64; +def SLTImm64 : MaxisAsmPseudoInst<(outs GPR64Opnd:$rs), + (ins GPR64Opnd:$rt, imm64:$imm), + "slt\t$rs, $rt, $imm">, GPR_64; +def : MaxisInstAlias<"slt\t$rs, $imm", (SLTImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, + imm64:$imm)>, GPR_64; +def SLTUImm64 : MaxisAsmPseudoInst<(outs GPR64Opnd:$rs), + (ins GPR64Opnd:$rt, imm64:$imm), + "sltu\t$rs, $rt, $imm">, GPR_64; +def : MaxisInstAlias<"sltu\t$rs, $imm", (SLTUImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, + imm64:$imm)>, GPR_64; diff --git a/lib/Target/Maxis/Maxis64r6InstrInfo.td b/lib/Target/Maxis/Maxis64r6InstrInfo.td new file mode 100644 index 00000000..18f775bd --- /dev/null +++ b/lib/Target/Maxis/Maxis64r6InstrInfo.td @@ -0,0 +1,279 @@ +//=- Maxis64r6InstrInfo.td - Maxis64r6 Instruction Information -*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis64r6 instructions. +// +//===----------------------------------------------------------------------===// + +// Notes about removals/changes from MAXIS32r6: +// Reencoded: dclo, dclz + +//===----------------------------------------------------------------------===// +// +// Instruction Encodings +// +//===----------------------------------------------------------------------===// + +class DALIGN_ENC : SPECIAL3_DALIGN_FM; +class DAUI_ENC : DAUI_FM; +class DAHI_ENC : REGIMM_FM; +class DATI_ENC : REGIMM_FM; +class DBITSWAP_ENC : SPECIAL3_2R_FM; +class DCLO_R6_ENC : SPECIAL_2R_FM; +class DCLZ_R6_ENC : SPECIAL_2R_FM; +class DDIV_ENC : SPECIAL_3R_FM<0b00010, 0b011110>; +class DDIVU_ENC : SPECIAL_3R_FM<0b00010, 0b011111>; +class DLSA_R6_ENC : SPECIAL_LSA_FM; +class DMOD_ENC : SPECIAL_3R_FM<0b00011, 0b011110>; +class DMODU_ENC : SPECIAL_3R_FM<0b00011, 0b011111>; +class DMUH_ENC : SPECIAL_3R_FM<0b00011, 0b011100>; +class DMUHU_ENC : SPECIAL_3R_FM<0b00011, 0b011101>; +class DMUL_R6_ENC : SPECIAL_3R_FM<0b00010, 0b011100>; +class DMULU_ENC : SPECIAL_3R_FM<0b00010, 0b011101>; +class LDPC_ENC : PCREL18_FM; +class LLD_R6_ENC : SPECIAL3_LL_SC_FM; +class SCD_R6_ENC : SPECIAL3_LL_SC_FM; + +//===----------------------------------------------------------------------===// +// +// Instruction Descriptions +// +//===----------------------------------------------------------------------===// + +class AHI_ATI_DESC_BASE { + dag OutOperandList = (outs GPROpnd:$rs); + dag InOperandList = (ins GPROpnd:$rt, uimm16_altrelaxed:$imm); + string AsmString = !strconcat(instr_asm, "\t$rs, $rt, $imm"); + string Constraints = "$rs = $rt"; + InstrItinClass Itinerary = itin; +} + +class DALIGN_DESC : ALIGN_DESC_BASE<"dalign", GPR64Opnd, uimm3, II_DALIGN>; +class DAHI_DESC : AHI_ATI_DESC_BASE<"dahi", GPR64Opnd, II_DAHI>; +class DATI_DESC : AHI_ATI_DESC_BASE<"dati", GPR64Opnd, II_DATI>; +class DAUI_DESC : AUI_DESC_BASE<"daui", GPR64Opnd, II_DAUI>; +class DBITSWAP_DESC : BITSWAP_DESC_BASE<"dbitswap", GPR64Opnd, II_DBITSWAP>; +class DCLO_R6_DESC : CLO_R6_DESC_BASE<"dclo", GPR64Opnd, II_DCLO>; +class DCLZ_R6_DESC : CLZ_R6_DESC_BASE<"dclz", GPR64Opnd, II_DCLZ>; +class DDIV_DESC : DIVMOD_DESC_BASE<"ddiv", GPR64Opnd, II_DDIV, sdiv>; +class DDIVU_DESC : DIVMOD_DESC_BASE<"ddivu", GPR64Opnd, II_DDIVU, udiv>; +class DLSA_R6_DESC : LSA_R6_DESC_BASE<"dlsa", GPR64Opnd, uimm2_plus1, II_DLSA>; +class DMOD_DESC : DIVMOD_DESC_BASE<"dmod", GPR64Opnd, II_DMOD, srem>; +class DMODU_DESC : DIVMOD_DESC_BASE<"dmodu", GPR64Opnd, II_DMODU, urem>; +class DMUH_DESC : MUL_R6_DESC_BASE<"dmuh", GPR64Opnd, II_DMUH, mulhs>; +class DMUHU_DESC : MUL_R6_DESC_BASE<"dmuhu", GPR64Opnd, II_DMUHU, mulhu>; +class DMUL_R6_DESC : MUL_R6_DESC_BASE<"dmul", GPR64Opnd, II_DMUL, mul>; +class DMULU_DESC : MUL_R6_DESC_BASE<"dmulu", GPR64Opnd, II_DMUL>; +class LDPC_DESC : PCREL_DESC_BASE<"ldpc", GPR64Opnd, simm18_lsl3, II_LDPC>; +class LLD_R6_DESC : LL_R6_DESC_BASE<"lld", GPR64Opnd, mem_simm16, II_LLD>; +class SCD_R6_DESC : SC_R6_DESC_BASE<"scd", GPR64Opnd, II_SCD>; +class SELEQZ64_DESC : SELEQNE_Z_DESC_BASE<"seleqz", GPR64Opnd>; +class SELNEZ64_DESC : SELEQNE_Z_DESC_BASE<"selnez", GPR64Opnd>; + +class BGEC64_DESC : CMP_BC_DESC_BASE<"bgec", brtarget, GPR64Opnd>; +class BGEUC64_DESC : CMP_BC_DESC_BASE<"bgeuc", brtarget, GPR64Opnd>; +class BEQC64_DESC : CMP_BC_DESC_BASE<"beqc", brtarget, GPR64Opnd>; +class BNEC64_DESC : CMP_BC_DESC_BASE<"bnec", brtarget, GPR64Opnd>; +class BLTC64_DESC : CMP_BC_DESC_BASE<"bltc", brtarget, GPR64Opnd>; +class BLTUC64_DESC : CMP_BC_DESC_BASE<"bltuc", brtarget, GPR64Opnd>; +class BLTZC64_DESC : CMP_CBR_RT_Z_DESC_BASE<"bltzc", brtarget, GPR64Opnd>; +class BGEZC64_DESC : CMP_CBR_RT_Z_DESC_BASE<"bgezc", brtarget, GPR64Opnd>; +class BLEZC64_DESC : CMP_CBR_RT_Z_DESC_BASE<"blezc", brtarget, GPR64Opnd>; +class BGTZC64_DESC : CMP_CBR_RT_Z_DESC_BASE<"bgtzc", brtarget, GPR64Opnd>; +class BEQZC64_DESC : CMP_CBR_EQNE_Z_DESC_BASE<"beqzc", brtarget21, GPR64Opnd>; +class BNEZC64_DESC : CMP_CBR_EQNE_Z_DESC_BASE<"bnezc", brtarget21, GPR64Opnd>; + +class JIALC64_DESC : JMP_IDX_COMPACT_DESC_BASE<"jialc", calloffset16, + GPR64Opnd, II_JIALC> { + bit isCall = 1; + list Defs = [RA]; +} + +class JIC64_DESC : JMP_IDX_COMPACT_DESC_BASE<"jic", jmpoffset16, GPR64Opnd, + II_JIC> { + bit isBarrier = 1; + bit isTerminator = 1; + list Defs = [AT]; +} + +class LL64_R6_DESC : LL_R6_DESC_BASE<"ll", GPR32Opnd, mem_simm9, II_LL>; +class SC64_R6_DESC : SC_R6_DESC_BASE<"sc", GPR32Opnd, II_SC>; +//===----------------------------------------------------------------------===// +// +// Instruction Definitions +// +//===----------------------------------------------------------------------===// + +let AdditionalPredicates = [NotInMicroMaxis] in { + let DecoderMethod = "DecodeDAHIDATI" in { + def DATI : DATI_ENC, DATI_DESC, ISA_MAXIS64R6; + def DAHI : DAHI_ENC, DAHI_DESC, ISA_MAXIS64R6; + } + def DAUI : DAUI_ENC, DAUI_DESC, ISA_MAXIS64R6; + def DALIGN : DALIGN_ENC, DALIGN_DESC, ISA_MAXIS64R6; + def DBITSWAP : DBITSWAP_ENC, DBITSWAP_DESC, ISA_MAXIS64R6; + def DCLO_R6 : DCLO_R6_ENC, DCLO_R6_DESC, ISA_MAXIS64R6; + def DCLZ_R6 : DCLZ_R6_ENC, DCLZ_R6_DESC, ISA_MAXIS64R6; + def DDIV : DDIV_ENC, DDIV_DESC, ISA_MAXIS64R6; + def DDIVU : DDIVU_ENC, DDIVU_DESC, ISA_MAXIS64R6; + def DMOD : DMOD_ENC, DMOD_DESC, ISA_MAXIS64R6; + def DMODU : DMODU_ENC, DMODU_DESC, ISA_MAXIS64R6; + def DLSA_R6 : DLSA_R6_ENC, DLSA_R6_DESC, ISA_MAXIS64R6; + def DMUH: DMUH_ENC, DMUH_DESC, ISA_MAXIS64R6; + def DMUHU: DMUHU_ENC, DMUHU_DESC, ISA_MAXIS64R6; + def DMUL_R6: DMUL_R6_ENC, DMUL_R6_DESC, ISA_MAXIS64R6; + def DMULU: DMULU_ENC, DMULU_DESC, ISA_MAXIS64R6; + def LLD_R6 : LLD_R6_ENC, LLD_R6_DESC, ISA_MAXIS64R6; +} +def LDPC: LDPC_ENC, LDPC_DESC, ISA_MAXIS64R6; +def SCD_R6 : SCD_R6_ENC, SCD_R6_DESC, ISA_MAXIS32R6; +let DecoderNamespace = "Maxis32r6_64r6_GP64" in { + def SELEQZ64 : SELEQZ_ENC, SELEQZ64_DESC, ISA_MAXIS32R6, GPR_64; + def SELNEZ64 : SELNEZ_ENC, SELNEZ64_DESC, ISA_MAXIS32R6, GPR_64; +} +let AdditionalPredicates = [NotInMicroMaxis], + DecoderNamespace = "Maxis32r6_64r6_PTR64" in { + def LL64_R6 : LL_R6_ENC, LL64_R6_DESC, PTR_64, ISA_MAXIS64R6; + def SC64_R6 : SC_R6_ENC, SC64_R6_DESC, PTR_64, ISA_MAXIS64R6; +} + +let DecoderNamespace = "Maxis32r6_64r6_GP64" in { +// Jump and Branch Instructions +def JIALC64 : JIALC_ENC, JIALC64_DESC, ISA_MAXIS64R6, GPR_64; +def JIC64 : JIC_ENC, JIC64_DESC, ISA_MAXIS64R6, GPR_64; + +def BEQC64 : BEQC_ENC, BEQC64_DESC, ISA_MAXIS64R6, GPR_64; +def BEQZC64 : BEQZC_ENC, BEQZC64_DESC, ISA_MAXIS64R6, GPR_64; +def BGEC64 : BGEC_ENC, BGEC64_DESC, ISA_MAXIS64R6, GPR_64; +def BGEUC64 : BGEUC_ENC, BGEUC64_DESC, ISA_MAXIS64R6, GPR_64; +def BGTZC64 : BGTZC_ENC, BGTZC64_DESC, ISA_MAXIS64R6, GPR_64; +def BLEZC64 : BLEZC_ENC, BLEZC64_DESC, ISA_MAXIS64R6, GPR_64; +def BLTC64 : BLTC_ENC, BLTC64_DESC, ISA_MAXIS64R6, GPR_64; +def BLTUC64 : BLTUC_ENC, BLTUC64_DESC, ISA_MAXIS64R6, GPR_64; +def BNEC64 : BNEC_ENC, BNEC64_DESC, ISA_MAXIS64R6, GPR_64; +def BNEZC64 : BNEZC_ENC, BNEZC64_DESC, ISA_MAXIS64R6, GPR_64; +} +let DecoderNamespace = "Maxis32r6_64r6_BranchZero" in { +def BLTZC64 : BLTZC_ENC, BLTZC64_DESC, ISA_MAXIS64R6, GPR_64; +def BGEZC64 : BGEZC_ENC, BGEZC64_DESC, ISA_MAXIS64R6, GPR_64; +} + +//===----------------------------------------------------------------------===// +// +// Instruction Aliases +// +//===----------------------------------------------------------------------===// + +def : MaxisInstAlias<"jr $rs", (JALR64 ZERO_64, GPR64Opnd:$rs), 1>, ISA_MAXIS64R6; + +def : MaxisInstAlias<"jrc $rs", (JIC64 GPR64Opnd:$rs, 0), 1>, ISA_MAXIS64R6; + +def : MaxisInstAlias<"jalrc $rs", (JIALC64 GPR64Opnd:$rs, 0), 1>, ISA_MAXIS64R6; +//===----------------------------------------------------------------------===// +// +// Patterns and Pseudo Instructions +// +//===----------------------------------------------------------------------===// + +// i64 selects +def : MaxisPat<(select i64:$cond, i64:$t, i64:$f), + (OR64 (SELNEZ64 i64:$t, i64:$cond), + (SELEQZ64 i64:$f, i64:$cond))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (seteq i64:$cond, immz)), i64:$t, i64:$f), + (OR64 (SELEQZ64 i64:$t, i64:$cond), + (SELNEZ64 i64:$f, i64:$cond))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (setne i64:$cond, immz)), i64:$t, i64:$f), + (OR64 (SELNEZ64 i64:$t, i64:$cond), + (SELEQZ64 i64:$f, i64:$cond))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (seteq i64:$cond, immZExt16_64:$imm)), i64:$t, i64:$f), + (OR64 (SELEQZ64 i64:$t, (XORi64 i64:$cond, immZExt16_64:$imm)), + (SELNEZ64 i64:$f, (XORi64 i64:$cond, immZExt16_64:$imm)))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (setne i64:$cond, immZExt16_64:$imm)), i64:$t, i64:$f), + (OR64 (SELNEZ64 i64:$t, (XORi64 i64:$cond, immZExt16_64:$imm)), + (SELEQZ64 i64:$f, (XORi64 i64:$cond, immZExt16_64:$imm)))>, + ISA_MAXIS64R6; +def : MaxisPat< + (select (i32 (setgt i64:$cond, immSExt16Plus1:$imm)), i64:$t, i64:$f), + (OR64 (SELEQZ64 i64:$t, + (SUBREG_TO_REG (i64 0), (SLTi64 i64:$cond, (Plus1 imm:$imm)), + sub_32)), + (SELNEZ64 i64:$f, + (SUBREG_TO_REG (i64 0), (SLTi64 i64:$cond, (Plus1 imm:$imm)), + sub_32)))>, + ISA_MAXIS64R6; +def : MaxisPat< + (select (i32 (setugt i64:$cond, immSExt16Plus1:$imm)), i64:$t, i64:$f), + (OR64 (SELEQZ64 i64:$t, + (SUBREG_TO_REG (i64 0), (SLTiu64 i64:$cond, (Plus1 imm:$imm)), + sub_32)), + (SELNEZ64 i64:$f, + (SUBREG_TO_REG (i64 0), (SLTiu64 i64:$cond, (Plus1 imm:$imm)), + sub_32)))>, + ISA_MAXIS64R6; + +def : MaxisPat<(select (i32 (setne i64:$cond, immz)), i64:$t, immz), + (SELNEZ64 i64:$t, i64:$cond)>, ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (seteq i64:$cond, immz)), i64:$t, immz), + (SELEQZ64 i64:$t, i64:$cond)>, ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (setne i64:$cond, immz)), immz, i64:$f), + (SELEQZ64 i64:$f, i64:$cond)>, ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (seteq i64:$cond, immz)), immz, i64:$f), + (SELNEZ64 i64:$f, i64:$cond)>, ISA_MAXIS64R6; + +// i64 selects from an i32 comparison +// One complicating factor here is that bits 32-63 of an i32 are undefined. +// FIXME: Ideally, setcc would always produce an i64 on MAXIS64 targets. +// This would allow us to remove the sign-extensions here. +def : MaxisPat<(select i32:$cond, i64:$t, i64:$f), + (OR64 (SELNEZ64 i64:$t, (SLL64_32 i32:$cond)), + (SELEQZ64 i64:$f, (SLL64_32 i32:$cond)))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (seteq i32:$cond, immz)), i64:$t, i64:$f), + (OR64 (SELEQZ64 i64:$t, (SLL64_32 i32:$cond)), + (SELNEZ64 i64:$f, (SLL64_32 i32:$cond)))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (setne i32:$cond, immz)), i64:$t, i64:$f), + (OR64 (SELNEZ64 i64:$t, (SLL64_32 i32:$cond)), + (SELEQZ64 i64:$f, (SLL64_32 i32:$cond)))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (seteq i32:$cond, immZExt16:$imm)), i64:$t, i64:$f), + (OR64 (SELEQZ64 i64:$t, (SLL64_32 (XORi i32:$cond, + immZExt16:$imm))), + (SELNEZ64 i64:$f, (SLL64_32 (XORi i32:$cond, + immZExt16:$imm))))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (setne i32:$cond, immZExt16:$imm)), i64:$t, i64:$f), + (OR64 (SELNEZ64 i64:$t, (SLL64_32 (XORi i32:$cond, + immZExt16:$imm))), + (SELEQZ64 i64:$f, (SLL64_32 (XORi i32:$cond, + immZExt16:$imm))))>, + ISA_MAXIS64R6; + +def : MaxisPat<(select i32:$cond, i64:$t, immz), + (SELNEZ64 i64:$t, (SLL64_32 i32:$cond))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (setne i32:$cond, immz)), i64:$t, immz), + (SELNEZ64 i64:$t, (SLL64_32 i32:$cond))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (seteq i32:$cond, immz)), i64:$t, immz), + (SELEQZ64 i64:$t, (SLL64_32 i32:$cond))>, + ISA_MAXIS64R6; +def : MaxisPat<(select i32:$cond, immz, i64:$f), + (SELEQZ64 i64:$f, (SLL64_32 i32:$cond))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (setne i32:$cond, immz)), immz, i64:$f), + (SELEQZ64 i64:$f, (SLL64_32 i32:$cond))>, + ISA_MAXIS64R6; +def : MaxisPat<(select (i32 (seteq i32:$cond, immz)), immz, i64:$f), + (SELNEZ64 i64:$f, (SLL64_32 i32:$cond))>, + ISA_MAXIS64R6; diff --git a/lib/Target/Maxis/MaxisAnalyzeImmediate.cpp b/lib/Target/Maxis/MaxisAnalyzeImmediate.cpp new file mode 100644 index 00000000..89d0548a --- /dev/null +++ b/lib/Target/Maxis/MaxisAnalyzeImmediate.cpp @@ -0,0 +1,158 @@ +//===- MaxisAnalyzeImmediate.cpp - Analyze Immediates ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisAnalyzeImmediate.h" +#include "Maxis.h" +#include "llvm/Support/MathExtras.h" +#include +#include +#include + +using namespace llvm; + +MaxisAnalyzeImmediate::Inst::Inst(unsigned O, unsigned I) : Opc(O), ImmOpnd(I) {} + +// Add I to the instruction sequences. +void MaxisAnalyzeImmediate::AddInstr(InstSeqLs &SeqLs, const Inst &I) { + // Add an instruction seqeunce consisting of just I. + if (SeqLs.empty()) { + SeqLs.push_back(InstSeq(1, I)); + return; + } + + for (InstSeqLs::iterator Iter = SeqLs.begin(); Iter != SeqLs.end(); ++Iter) + Iter->push_back(I); +} + +void MaxisAnalyzeImmediate::GetInstSeqLsADDiu(uint64_t Imm, unsigned RemSize, + InstSeqLs &SeqLs) { + GetInstSeqLs((Imm + 0x8000ULL) & 0xffffffffffff0000ULL, RemSize, SeqLs); + AddInstr(SeqLs, Inst(ADDiu, Imm & 0xffffULL)); +} + +void MaxisAnalyzeImmediate::GetInstSeqLsORi(uint64_t Imm, unsigned RemSize, + InstSeqLs &SeqLs) { + GetInstSeqLs(Imm & 0xffffffffffff0000ULL, RemSize, SeqLs); + AddInstr(SeqLs, Inst(ORi, Imm & 0xffffULL)); +} + +void MaxisAnalyzeImmediate::GetInstSeqLsSLL(uint64_t Imm, unsigned RemSize, + InstSeqLs &SeqLs) { + unsigned Shamt = countTrailingZeros(Imm); + GetInstSeqLs(Imm >> Shamt, RemSize - Shamt, SeqLs); + AddInstr(SeqLs, Inst(SLL, Shamt)); +} + +void MaxisAnalyzeImmediate::GetInstSeqLs(uint64_t Imm, unsigned RemSize, + InstSeqLs &SeqLs) { + uint64_t MaskedImm = Imm & (0xffffffffffffffffULL >> (64 - Size)); + + // Do nothing if Imm is 0. + if (!MaskedImm) + return; + + // A single ADDiu will do if RemSize <= 16. + if (RemSize <= 16) { + AddInstr(SeqLs, Inst(ADDiu, MaskedImm)); + return; + } + + // Shift if the lower 16-bit is cleared. + if (!(Imm & 0xffff)) { + GetInstSeqLsSLL(Imm, RemSize, SeqLs); + return; + } + + GetInstSeqLsADDiu(Imm, RemSize, SeqLs); + + // If bit 15 is cleared, it doesn't make a difference whether the last + // instruction is an ADDiu or ORi. In that case, do not call GetInstSeqLsORi. + if (Imm & 0x8000) { + InstSeqLs SeqLsORi; + GetInstSeqLsORi(Imm, RemSize, SeqLsORi); + SeqLs.append(std::make_move_iterator(SeqLsORi.begin()), + std::make_move_iterator(SeqLsORi.end())); + } +} + +// Replace a ADDiu & SLL pair with a LUi. +// e.g. the following two instructions +// ADDiu 0x0111 +// SLL 18 +// are replaced with +// LUi 0x444 +void MaxisAnalyzeImmediate::ReplaceADDiuSLLWithLUi(InstSeq &Seq) { + // Check if the first two instructions are ADDiu and SLL and the shift amount + // is at least 16. + if ((Seq.size() < 2) || (Seq[0].Opc != ADDiu) || + (Seq[1].Opc != SLL) || (Seq[1].ImmOpnd < 16)) + return; + + // Sign-extend and shift operand of ADDiu and see if it still fits in 16-bit. + int64_t Imm = SignExtend64<16>(Seq[0].ImmOpnd); + int64_t ShiftedImm = (uint64_t)Imm << (Seq[1].ImmOpnd - 16); + + if (!isInt<16>(ShiftedImm)) + return; + + // Replace the first instruction and erase the second. + Seq[0].Opc = LUi; + Seq[0].ImmOpnd = (unsigned)(ShiftedImm & 0xffff); + Seq.erase(Seq.begin() + 1); +} + +void MaxisAnalyzeImmediate::GetShortestSeq(InstSeqLs &SeqLs, InstSeq &Insts) { + InstSeqLs::iterator ShortestSeq = SeqLs.end(); + // The length of an instruction sequence is at most 7. + unsigned ShortestLength = 8; + + for (InstSeqLs::iterator S = SeqLs.begin(); S != SeqLs.end(); ++S) { + ReplaceADDiuSLLWithLUi(*S); + assert(S->size() <= 7); + + if (S->size() < ShortestLength) { + ShortestSeq = S; + ShortestLength = S->size(); + } + } + + Insts.clear(); + Insts.append(ShortestSeq->begin(), ShortestSeq->end()); +} + +const MaxisAnalyzeImmediate::InstSeq +&MaxisAnalyzeImmediate::Analyze(uint64_t Imm, unsigned Size, + bool LastInstrIsADDiu) { + this->Size = Size; + + if (Size == 32) { + ADDiu = Maxis::ADDiu; + ORi = Maxis::ORi; + SLL = Maxis::SLL; + LUi = Maxis::LUi; + } else { + ADDiu = Maxis::DADDiu; + ORi = Maxis::ORi64; + SLL = Maxis::DSLL; + LUi = Maxis::LUi64; + } + + InstSeqLs SeqLs; + + // Get the list of instruction sequences. + if (LastInstrIsADDiu | !Imm) + GetInstSeqLsADDiu(Imm, Size, SeqLs); + else + GetInstSeqLs(Imm, Size, SeqLs); + + // Set Insts to the shortest instruction sequence. + GetShortestSeq(SeqLs, Insts); + + return Insts; +} diff --git a/lib/Target/Maxis/MaxisAnalyzeImmediate.h b/lib/Target/Maxis/MaxisAnalyzeImmediate.h new file mode 100644 index 00000000..68fe1c42 --- /dev/null +++ b/lib/Target/Maxis/MaxisAnalyzeImmediate.h @@ -0,0 +1,67 @@ +//===- MaxisAnalyzeImmediate.h - Analyze Immediates -------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISANALYZEIMMEDIATE_H +#define LLVM_LIB_TARGET_MAXIS_MAXISANALYZEIMMEDIATE_H + +#include "llvm/ADT/SmallVector.h" +#include + +namespace llvm { + + class MaxisAnalyzeImmediate { + public: + struct Inst { + unsigned Opc, ImmOpnd; + + Inst(unsigned Opc, unsigned ImmOpnd); + }; + using InstSeq = SmallVector; + + /// Analyze - Get an instruction sequence to load immediate Imm. The last + /// instruction in the sequence must be an ADDiu if LastInstrIsADDiu is + /// true; + const InstSeq &Analyze(uint64_t Imm, unsigned Size, bool LastInstrIsADDiu); + + private: + using InstSeqLs = SmallVector; + + /// AddInstr - Add I to all instruction sequences in SeqLs. + void AddInstr(InstSeqLs &SeqLs, const Inst &I); + + /// GetInstSeqLsADDiu - Get instruction sequences which end with an ADDiu to + /// load immediate Imm + void GetInstSeqLsADDiu(uint64_t Imm, unsigned RemSize, InstSeqLs &SeqLs); + + /// GetInstSeqLsORi - Get instrutcion sequences which end with an ORi to + /// load immediate Imm + void GetInstSeqLsORi(uint64_t Imm, unsigned RemSize, InstSeqLs &SeqLs); + + /// GetInstSeqLsSLL - Get instruction sequences which end with a SLL to + /// load immediate Imm + void GetInstSeqLsSLL(uint64_t Imm, unsigned RemSize, InstSeqLs &SeqLs); + + /// GetInstSeqLs - Get instruction sequences to load immediate Imm. + void GetInstSeqLs(uint64_t Imm, unsigned RemSize, InstSeqLs &SeqLs); + + /// ReplaceADDiuSLLWithLUi - Replace an ADDiu & SLL pair with a LUi. + void ReplaceADDiuSLLWithLUi(InstSeq &Seq); + + /// GetShortestSeq - Find the shortest instruction sequence in SeqLs and + /// return it in Insts. + void GetShortestSeq(InstSeqLs &SeqLs, InstSeq &Insts); + + unsigned Size; + unsigned ADDiu, ORi, SLL, LUi; + InstSeq Insts; + }; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISANALYZEIMMEDIATE_H diff --git a/lib/Target/Maxis/MaxisAsmPrinter.cpp b/lib/Target/Maxis/MaxisAsmPrinter.cpp new file mode 100644 index 00000000..5a7639f8 --- /dev/null +++ b/lib/Target/Maxis/MaxisAsmPrinter.cpp @@ -0,0 +1,1234 @@ +//===- MaxisAsmPrinter.cpp - Maxis LLVM Assembly Printer --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a printer that converts from our internal representation +// of machine-dependent LLVM code to GAS-format MAXIS assembly language. +// +//===----------------------------------------------------------------------===// + +#include "MaxisAsmPrinter.h" +#include "InstPrinter/MaxisInstPrinter.h" +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MCTargetDesc/MaxisMCNaCl.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "Maxis.h" +#include "MaxisMCInstLower.h" +#include "MaxisMachineFunction.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.h" +#include "MaxisTargetStreamer.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/Instructions.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCSectionELF.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-asm-printer" + +MaxisTargetStreamer &MaxisAsmPrinter::getTargetStreamer() const { + return static_cast(*OutStreamer->getTargetStreamer()); +} + +bool MaxisAsmPrinter::runOnMachineFunction(MachineFunction &MF) { + Subtarget = &MF.getSubtarget(); + + MaxisFI = MF.getInfo(); + if (Subtarget->inMaxis16Mode()) + for (std::map< + const char *, + const Maxis16HardFloatInfo::FuncSignature *>::const_iterator + it = MaxisFI->StubsNeeded.begin(); + it != MaxisFI->StubsNeeded.end(); ++it) { + const char *Symbol = it->first; + const Maxis16HardFloatInfo::FuncSignature *Signature = it->second; + if (StubsNeeded.find(Symbol) == StubsNeeded.end()) + StubsNeeded[Symbol] = Signature; + } + MCP = MF.getConstantPool(); + + // In NaCl, all indirect jump targets must be aligned to bundle size. + if (Subtarget->isTargetNaCl()) + NaClAlignIndirectJumpTargets(MF); + + AsmPrinter::runOnMachineFunction(MF); + + emitXRayTable(); + + return true; +} + +bool MaxisAsmPrinter::lowerOperand(const MachineOperand &MO, MCOperand &MCOp) { + MCOp = MCInstLowering.LowerOperand(MO); + return MCOp.isValid(); +} + +#include "MaxisGenMCPseudoLowering.inc" + +// Lower PseudoReturn/PseudoIndirectBranch/PseudoIndirectBranch64 to JR, JR_MM, +// JALR, or JALR64 as appropriate for the target. +void MaxisAsmPrinter::emitPseudoIndirectBranch(MCStreamer &OutStreamer, + const MachineInstr *MI) { + bool HasLinkReg = false; + bool InMicroMaxisMode = Subtarget->inMicroMaxisMode(); + MCInst TmpInst0; + + if (Subtarget->hasMaxis64r6()) { + // MAXIS64r6 should use (JALR64 ZERO_64, $rs) + TmpInst0.setOpcode(Maxis::JALR64); + HasLinkReg = true; + } else if (Subtarget->hasMaxis32r6()) { + // MAXIS32r6 should use (JALR ZERO, $rs) + if (InMicroMaxisMode) + TmpInst0.setOpcode(Maxis::JRC16_MMR6); + else { + TmpInst0.setOpcode(Maxis::JALR); + HasLinkReg = true; + } + } else if (Subtarget->inMicroMaxisMode()) + // microMAXIS should use (JR_MM $rs) + TmpInst0.setOpcode(Maxis::JR_MM); + else { + // Everything else should use (JR $rs) + TmpInst0.setOpcode(Maxis::JR); + } + + MCOperand MCOp; + + if (HasLinkReg) { + unsigned ZeroReg = Subtarget->isGP64bit() ? Maxis::ZERO_64 : Maxis::ZERO; + TmpInst0.addOperand(MCOperand::createReg(ZeroReg)); + } + + lowerOperand(MI->getOperand(0), MCOp); + TmpInst0.addOperand(MCOp); + + EmitToStreamer(OutStreamer, TmpInst0); +} + +void MaxisAsmPrinter::EmitInstruction(const MachineInstr *MI) { + MaxisTargetStreamer &TS = getTargetStreamer(); + unsigned Opc = MI->getOpcode(); + TS.forbidModuleDirective(); + + if (MI->isDebugValue()) { + SmallString<128> Str; + raw_svector_ostream OS(Str); + + PrintDebugValueComment(MI, OS); + return; + } + + // If we just ended a constant pool, mark it as such. + if (InConstantPool && Opc != Maxis::CONSTPOOL_ENTRY) { + OutStreamer->EmitDataRegion(MCDR_DataRegionEnd); + InConstantPool = false; + } + if (Opc == Maxis::CONSTPOOL_ENTRY) { + // CONSTPOOL_ENTRY - This instruction represents a floating + // constant pool in the function. The first operand is the ID# + // for this instruction, the second is the index into the + // MachineConstantPool that this is, the third is the size in + // bytes of this constant pool entry. + // The required alignment is specified on the basic block holding this MI. + // + unsigned LabelId = (unsigned)MI->getOperand(0).getImm(); + unsigned CPIdx = (unsigned)MI->getOperand(1).getIndex(); + + // If this is the first entry of the pool, mark it. + if (!InConstantPool) { + OutStreamer->EmitDataRegion(MCDR_DataRegion); + InConstantPool = true; + } + + OutStreamer->EmitLabel(GetCPISymbol(LabelId)); + + const MachineConstantPoolEntry &MCPE = MCP->getConstants()[CPIdx]; + if (MCPE.isMachineConstantPoolEntry()) + EmitMachineConstantPoolValue(MCPE.Val.MachineCPVal); + else + EmitGlobalConstant(MF->getDataLayout(), MCPE.Val.ConstVal); + return; + } + + switch (Opc) { + case Maxis::PATCHABLE_FUNCTION_ENTER: + LowerPATCHABLE_FUNCTION_ENTER(*MI); + return; + case Maxis::PATCHABLE_FUNCTION_EXIT: + LowerPATCHABLE_FUNCTION_EXIT(*MI); + return; + case Maxis::PATCHABLE_TAIL_CALL: + LowerPATCHABLE_TAIL_CALL(*MI); + return; + } + + MachineBasicBlock::const_instr_iterator I = MI->getIterator(); + MachineBasicBlock::const_instr_iterator E = MI->getParent()->instr_end(); + + do { + // Do any auto-generated pseudo lowerings. + if (emitPseudoExpansionLowering(*OutStreamer, &*I)) + continue; + + if (I->getOpcode() == Maxis::PseudoReturn || + I->getOpcode() == Maxis::PseudoReturn64 || + I->getOpcode() == Maxis::PseudoIndirectBranch || + I->getOpcode() == Maxis::PseudoIndirectBranch64 || + I->getOpcode() == Maxis::TAILCALLREG || + I->getOpcode() == Maxis::TAILCALLREG64) { + emitPseudoIndirectBranch(*OutStreamer, &*I); + continue; + } + + // The inMaxis16Mode() test is not permanent. + // Some instructions are marked as pseudo right now which + // would make the test fail for the wrong reason but + // that will be fixed soon. We need this here because we are + // removing another test for this situation downstream in the + // callchain. + // + if (I->isPseudo() && !Subtarget->inMaxis16Mode() + && !isLongBranchPseudo(I->getOpcode())) + llvm_unreachable("Pseudo opcode found in EmitInstruction()"); + + MCInst TmpInst0; + MCInstLowering.Lower(&*I, TmpInst0); + EmitToStreamer(*OutStreamer, TmpInst0); + } while ((++I != E) && I->isInsideBundle()); // Delay slot check +} + +//===----------------------------------------------------------------------===// +// +// Maxis Asm Directives +// +// -- Frame directive "frame Stackpointer, Stacksize, RARegister" +// Describe the stack frame. +// +// -- Mask directives "(f)mask bitmask, offset" +// Tells the assembler which registers are saved and where. +// bitmask - contain a little endian bitset indicating which registers are +// saved on function prologue (e.g. with a 0x80000000 mask, the +// assembler knows the register 31 (RA) is saved at prologue. +// offset - the position before stack pointer subtraction indicating where +// the first saved register on prologue is located. (e.g. with a +// +// Consider the following function prologue: +// +// .frame $fp,48,$ra +// .mask 0xc0000000,-8 +// addiu $sp, $sp, -48 +// sw $ra, 40($sp) +// sw $fp, 36($sp) +// +// With a 0xc0000000 mask, the assembler knows the register 31 (RA) and +// 30 (FP) are saved at prologue. As the save order on prologue is from +// left to right, RA is saved first. A -8 offset means that after the +// stack pointer subtration, the first register in the mask (RA) will be +// saved at address 48-8=40. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Mask directives +//===----------------------------------------------------------------------===// + +// Create a bitmask with all callee saved registers for CPU or Floating Point +// registers. For CPU registers consider RA, GP and FP for saving if necessary. +void MaxisAsmPrinter::printSavedRegsBitmask() { + // CPU and FPU Saved Registers Bitmasks + unsigned CPUBitmask = 0, FPUBitmask = 0; + int CPUTopSavedRegOff, FPUTopSavedRegOff; + + // Set the CPU and FPU Bitmasks + const MachineFrameInfo &MFI = MF->getFrameInfo(); + const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo(); + const std::vector &CSI = MFI.getCalleeSavedInfo(); + // size of stack area to which FP callee-saved regs are saved. + unsigned CPURegSize = TRI->getRegSizeInBits(Maxis::GPR32RegClass) / 8; + unsigned FGR32RegSize = TRI->getRegSizeInBits(Maxis::FGR32RegClass) / 8; + unsigned AFGR64RegSize = TRI->getRegSizeInBits(Maxis::AFGR64RegClass) / 8; + bool HasAFGR64Reg = false; + unsigned CSFPRegsSize = 0; + + for (const auto &I : CSI) { + unsigned Reg = I.getReg(); + unsigned RegNum = TRI->getEncodingValue(Reg); + + // If it's a floating point register, set the FPU Bitmask. + // If it's a general purpose register, set the CPU Bitmask. + if (Maxis::FGR32RegClass.contains(Reg)) { + FPUBitmask |= (1 << RegNum); + CSFPRegsSize += FGR32RegSize; + } else if (Maxis::AFGR64RegClass.contains(Reg)) { + FPUBitmask |= (3 << RegNum); + CSFPRegsSize += AFGR64RegSize; + HasAFGR64Reg = true; + } else if (Maxis::GPR32RegClass.contains(Reg)) + CPUBitmask |= (1 << RegNum); + } + + // FP Regs are saved right below where the virtual frame pointer points to. + FPUTopSavedRegOff = FPUBitmask ? + (HasAFGR64Reg ? -AFGR64RegSize : -FGR32RegSize) : 0; + + // CPU Regs are saved below FP Regs. + CPUTopSavedRegOff = CPUBitmask ? -CSFPRegsSize - CPURegSize : 0; + + MaxisTargetStreamer &TS = getTargetStreamer(); + // Print CPUBitmask + TS.emitMask(CPUBitmask, CPUTopSavedRegOff); + + // Print FPUBitmask + TS.emitFMask(FPUBitmask, FPUTopSavedRegOff); +} + +//===----------------------------------------------------------------------===// +// Frame and Set directives +//===----------------------------------------------------------------------===// + +/// Frame Directive +void MaxisAsmPrinter::emitFrameDirective() { + const TargetRegisterInfo &RI = *MF->getSubtarget().getRegisterInfo(); + + unsigned stackReg = RI.getFrameRegister(*MF); + unsigned returnReg = RI.getRARegister(); + unsigned stackSize = MF->getFrameInfo().getStackSize(); + + getTargetStreamer().emitFrame(stackReg, stackSize, returnReg); +} + +/// Emit Set directives. +const char *MaxisAsmPrinter::getCurrentABIString() const { + switch (static_cast(TM).getABI().GetEnumValue()) { + case MaxisABIInfo::ABI::O32: return "abi32"; + case MaxisABIInfo::ABI::N32: return "abiN32"; + case MaxisABIInfo::ABI::N64: return "abi64"; + default: llvm_unreachable("Unknown Maxis ABI"); + } +} + +void MaxisAsmPrinter::EmitFunctionEntryLabel() { + MaxisTargetStreamer &TS = getTargetStreamer(); + + // NaCl sandboxing requires that indirect call instructions are masked. + // This means that function entry points should be bundle-aligned. + if (Subtarget->isTargetNaCl()) + EmitAlignment(std::max(MF->getAlignment(), MAXIS_NACL_BUNDLE_ALIGN)); + + if (Subtarget->inMicroMaxisMode()) { + TS.emitDirectiveSetMicroMaxis(); + TS.setUsesMicroMaxis(); + TS.updateABIInfo(*Subtarget); + } else + TS.emitDirectiveSetNoMicroMaxis(); + + if (Subtarget->inMaxis16Mode()) + TS.emitDirectiveSetMaxis16(); + else + TS.emitDirectiveSetNoMaxis16(); + + TS.emitDirectiveEnt(*CurrentFnSym); + OutStreamer->EmitLabel(CurrentFnSym); +} + +/// EmitFunctionBodyStart - Targets can override this to emit stuff before +/// the first basic block in the function. +void MaxisAsmPrinter::EmitFunctionBodyStart() { + MaxisTargetStreamer &TS = getTargetStreamer(); + + MCInstLowering.Initialize(&MF->getContext()); + + bool IsNakedFunction = MF->getFunction().hasFnAttribute(Attribute::Naked); + if (!IsNakedFunction) + emitFrameDirective(); + + if (!IsNakedFunction) + printSavedRegsBitmask(); + + if (!Subtarget->inMaxis16Mode()) { + TS.emitDirectiveSetNoReorder(); + TS.emitDirectiveSetNoMacro(); + TS.emitDirectiveSetNoAt(); + } +} + +/// EmitFunctionBodyEnd - Targets can override this to emit stuff after +/// the last basic block in the function. +void MaxisAsmPrinter::EmitFunctionBodyEnd() { + MaxisTargetStreamer &TS = getTargetStreamer(); + + // There are instruction for this macros, but they must + // always be at the function end, and we can't emit and + // break with BB logic. + if (!Subtarget->inMaxis16Mode()) { + TS.emitDirectiveSetAt(); + TS.emitDirectiveSetMacro(); + TS.emitDirectiveSetReorder(); + } + TS.emitDirectiveEnd(CurrentFnSym->getName()); + // Make sure to terminate any constant pools that were at the end + // of the function. + if (!InConstantPool) + return; + InConstantPool = false; + OutStreamer->EmitDataRegion(MCDR_DataRegionEnd); +} + +void MaxisAsmPrinter::EmitBasicBlockEnd(const MachineBasicBlock &MBB) { + AsmPrinter::EmitBasicBlockEnd(MBB); + MaxisTargetStreamer &TS = getTargetStreamer(); + if (MBB.empty()) + TS.emitDirectiveInsn(); +} + +/// isBlockOnlyReachableByFallthough - Return true if the basic block has +/// exactly one predecessor and the control transfer mechanism between +/// the predecessor and this block is a fall-through. +bool MaxisAsmPrinter::isBlockOnlyReachableByFallthrough(const MachineBasicBlock* + MBB) const { + // The predecessor has to be immediately before this block. + const MachineBasicBlock *Pred = *MBB->pred_begin(); + + // If the predecessor is a switch statement, assume a jump table + // implementation, so it is not a fall through. + if (const BasicBlock *bb = Pred->getBasicBlock()) + if (isa(bb->getTerminator())) + return false; + + // If this is a landing pad, it isn't a fall through. If it has no preds, + // then nothing falls through to it. + if (MBB->isEHPad() || MBB->pred_empty()) + return false; + + // If there isn't exactly one predecessor, it can't be a fall through. + MachineBasicBlock::const_pred_iterator PI = MBB->pred_begin(), PI2 = PI; + ++PI2; + + if (PI2 != MBB->pred_end()) + return false; + + // The predecessor has to be immediately before this block. + if (!Pred->isLayoutSuccessor(MBB)) + return false; + + // If the block is completely empty, then it definitely does fall through. + if (Pred->empty()) + return true; + + // Otherwise, check the last instruction. + // Check if the last terminator is an unconditional branch. + MachineBasicBlock::const_iterator I = Pred->end(); + while (I != Pred->begin() && !(--I)->isTerminator()) ; + + return !I->isBarrier(); +} + +// Print out an operand for an inline asm expression. +bool MaxisAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O) { + // Does this asm operand have a single letter operand modifier? + if (ExtraCode && ExtraCode[0]) { + if (ExtraCode[1] != 0) return true; // Unknown modifier. + + const MachineOperand &MO = MI->getOperand(OpNum); + switch (ExtraCode[0]) { + default: + // See if this is a generic print operand + return AsmPrinter::PrintAsmOperand(MI,OpNum,AsmVariant,ExtraCode,O); + case 'X': // hex const int + if ((MO.getType()) != MachineOperand::MO_Immediate) + return true; + O << "0x" << Twine::utohexstr(MO.getImm()); + return false; + case 'x': // hex const int (low 16 bits) + if ((MO.getType()) != MachineOperand::MO_Immediate) + return true; + O << "0x" << Twine::utohexstr(MO.getImm() & 0xffff); + return false; + case 'd': // decimal const int + if ((MO.getType()) != MachineOperand::MO_Immediate) + return true; + O << MO.getImm(); + return false; + case 'm': // decimal const int minus 1 + if ((MO.getType()) != MachineOperand::MO_Immediate) + return true; + O << MO.getImm() - 1; + return false; + case 'z': + // $0 if zero, regular printing otherwise + if (MO.getType() == MachineOperand::MO_Immediate && MO.getImm() == 0) { + O << "$0"; + return false; + } + // If not, call printOperand as normal. + break; + case 'D': // Second part of a double word register operand + case 'L': // Low order register of a double word register operand + case 'M': // High order register of a double word register operand + { + if (OpNum == 0) + return true; + const MachineOperand &FlagsOP = MI->getOperand(OpNum - 1); + if (!FlagsOP.isImm()) + return true; + unsigned Flags = FlagsOP.getImm(); + unsigned NumVals = InlineAsm::getNumOperandRegisters(Flags); + // Number of registers represented by this operand. We are looking + // for 2 for 32 bit mode and 1 for 64 bit mode. + if (NumVals != 2) { + if (Subtarget->isGP64bit() && NumVals == 1 && MO.isReg()) { + unsigned Reg = MO.getReg(); + O << '$' << MaxisInstPrinter::getRegisterName(Reg); + return false; + } + return true; + } + + unsigned RegOp = OpNum; + if (!Subtarget->isGP64bit()){ + // Endianness reverses which register holds the high or low value + // between M and L. + switch(ExtraCode[0]) { + case 'M': + RegOp = (Subtarget->isLittle()) ? OpNum + 1 : OpNum; + break; + case 'L': + RegOp = (Subtarget->isLittle()) ? OpNum : OpNum + 1; + break; + case 'D': // Always the second part + RegOp = OpNum + 1; + } + if (RegOp >= MI->getNumOperands()) + return true; + const MachineOperand &MO = MI->getOperand(RegOp); + if (!MO.isReg()) + return true; + unsigned Reg = MO.getReg(); + O << '$' << MaxisInstPrinter::getRegisterName(Reg); + return false; + } + } + case 'w': + // Print MSA registers for the 'f' constraint + // In LLVM, the 'w' modifier doesn't need to do anything. + // We can just call printOperand as normal. + break; + } + } + + printOperand(MI, OpNum, O); + return false; +} + +bool MaxisAsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI, + unsigned OpNum, unsigned AsmVariant, + const char *ExtraCode, + raw_ostream &O) { + assert(OpNum + 1 < MI->getNumOperands() && "Insufficient operands"); + const MachineOperand &BaseMO = MI->getOperand(OpNum); + const MachineOperand &OffsetMO = MI->getOperand(OpNum + 1); + assert(BaseMO.isReg() && "Unexpected base pointer for inline asm memory operand."); + assert(OffsetMO.isImm() && "Unexpected offset for inline asm memory operand."); + int Offset = OffsetMO.getImm(); + + // Currently we are expecting either no ExtraCode or 'D' + if (ExtraCode) { + if (ExtraCode[0] == 'D') + Offset += 4; + else + return true; // Unknown modifier. + // FIXME: M = high order bits + // FIXME: L = low order bits + } + + O << Offset << "($" << MaxisInstPrinter::getRegisterName(BaseMO.getReg()) << ")"; + + return false; +} + +void MaxisAsmPrinter::printOperand(const MachineInstr *MI, int opNum, + raw_ostream &O) { + const MachineOperand &MO = MI->getOperand(opNum); + bool closeP = false; + + if (MO.getTargetFlags()) + closeP = true; + + switch(MO.getTargetFlags()) { + case MaxisII::MO_GPREL: O << "%gp_rel("; break; + case MaxisII::MO_GOT_CALL: O << "%call16("; break; + case MaxisII::MO_GOT: O << "%got("; break; + case MaxisII::MO_ABS_HI: O << "%hi("; break; + case MaxisII::MO_ABS_LO: O << "%lo("; break; + case MaxisII::MO_HIGHER: O << "%higher("; break; + case MaxisII::MO_HIGHEST: O << "%highest(("; break; + case MaxisII::MO_TLSGD: O << "%tlsgd("; break; + case MaxisII::MO_GOTTPREL: O << "%gottprel("; break; + case MaxisII::MO_TPREL_HI: O << "%tprel_hi("; break; + case MaxisII::MO_TPREL_LO: O << "%tprel_lo("; break; + case MaxisII::MO_GPOFF_HI: O << "%hi(%neg(%gp_rel("; break; + case MaxisII::MO_GPOFF_LO: O << "%lo(%neg(%gp_rel("; break; + case MaxisII::MO_GOT_DISP: O << "%got_disp("; break; + case MaxisII::MO_GOT_PAGE: O << "%got_page("; break; + case MaxisII::MO_GOT_OFST: O << "%got_ofst("; break; + } + + switch (MO.getType()) { + case MachineOperand::MO_Register: + O << '$' + << StringRef(MaxisInstPrinter::getRegisterName(MO.getReg())).lower(); + break; + + case MachineOperand::MO_Immediate: + O << MO.getImm(); + break; + + case MachineOperand::MO_MachineBasicBlock: + MO.getMBB()->getSymbol()->print(O, MAI); + return; + + case MachineOperand::MO_GlobalAddress: + getSymbol(MO.getGlobal())->print(O, MAI); + break; + + case MachineOperand::MO_BlockAddress: { + MCSymbol *BA = GetBlockAddressSymbol(MO.getBlockAddress()); + O << BA->getName(); + break; + } + + case MachineOperand::MO_ConstantPoolIndex: + O << getDataLayout().getPrivateGlobalPrefix() << "CPI" + << getFunctionNumber() << "_" << MO.getIndex(); + if (MO.getOffset()) + O << "+" << MO.getOffset(); + break; + + default: + llvm_unreachable(""); + } + + if (closeP) O << ")"; +} + +void MaxisAsmPrinter:: +printMemOperand(const MachineInstr *MI, int opNum, raw_ostream &O) { + // Load/Store memory operands -- imm($reg) + // If PIC target the target is loaded as the + // pattern lw $25,%call16($28) + + // opNum can be invalid if instruction has reglist as operand. + // MemOperand is always last operand of instruction (base + offset). + switch (MI->getOpcode()) { + default: + break; + case Maxis::SWM32_MM: + case Maxis::LWM32_MM: + opNum = MI->getNumOperands() - 2; + break; + } + + printOperand(MI, opNum+1, O); + O << "("; + printOperand(MI, opNum, O); + O << ")"; +} + +void MaxisAsmPrinter:: +printMemOperandEA(const MachineInstr *MI, int opNum, raw_ostream &O) { + // when using stack locations for not load/store instructions + // print the same way as all normal 3 operand instructions. + printOperand(MI, opNum, O); + O << ", "; + printOperand(MI, opNum+1, O); +} + +void MaxisAsmPrinter:: +printFCCOperand(const MachineInstr *MI, int opNum, raw_ostream &O, + const char *Modifier) { + const MachineOperand &MO = MI->getOperand(opNum); + O << Maxis::MaxisFCCToString((Maxis::CondCode)MO.getImm()); +} + +void MaxisAsmPrinter:: +printRegisterList(const MachineInstr *MI, int opNum, raw_ostream &O) { + for (int i = opNum, e = MI->getNumOperands(); i != e; ++i) { + if (i != opNum) O << ", "; + printOperand(MI, i, O); + } +} + +void MaxisAsmPrinter::EmitStartOfAsmFile(Module &M) { + MaxisTargetStreamer &TS = getTargetStreamer(); + + // MaxisTargetStreamer has an initialization order problem when emitting an + // object file directly (see MaxisTargetELFStreamer for full details). Work + // around it by re-initializing the PIC state here. + TS.setPic(OutContext.getObjectFileInfo()->isPositionIndependent()); + + // Compute MAXIS architecture attributes based on the default subtarget + // that we'd have constructed. Module level directives aren't LTO + // clean anyhow. + // FIXME: For ifunc related functions we could iterate over and look + // for a feature string that doesn't match the default one. + const Triple &TT = TM.getTargetTriple(); + StringRef CPU = MAXIS_MC::selectMaxisCPU(TT, TM.getTargetCPU()); + StringRef FS = TM.getTargetFeatureString(); + const MaxisTargetMachine &MTM = static_cast(TM); + const MaxisSubtarget STI(TT, CPU, FS, MTM.isLittleEndian(), MTM, 0); + + bool IsABICalls = STI.isABICalls(); + const MaxisABIInfo &ABI = MTM.getABI(); + if (IsABICalls) { + TS.emitDirectiveAbiCalls(); + // FIXME: This condition should be a lot more complicated that it is here. + // Ideally it should test for properties of the ABI and not the ABI + // itself. + // For the moment, I'm only correcting enough to make MAXIS-IV work. + if (!isPositionIndependent() && STI.hasSym32()) + TS.emitDirectiveOptionPic0(); + } + + // Tell the assembler which ABI we are using + std::string SectionName = std::string(".mdebug.") + getCurrentABIString(); + OutStreamer->SwitchSection( + OutContext.getELFSection(SectionName, ELF::SHT_PROGBITS, 0)); + + // NaN: At the moment we only support: + // 1. .nan legacy (default) + // 2. .nan 2008 + STI.isNaN2008() ? TS.emitDirectiveNaN2008() + : TS.emitDirectiveNaNLegacy(); + + // TODO: handle O64 ABI + + TS.updateABIInfo(STI); + + // We should always emit a '.module fp=...' but binutils 2.24 does not accept + // it. We therefore emit it when it contradicts the ABI defaults (-mfpxx or + // -mfp64) and omit it otherwise. + if (ABI.IsO32() && (STI.isABI_FPXX() || STI.isFP64bit())) + TS.emitDirectiveModuleFP(); + + // We should always emit a '.module [no]oddspreg' but binutils 2.24 does not + // accept it. We therefore emit it when it contradicts the default or an + // option has changed the default (i.e. FPXX) and omit it otherwise. + if (ABI.IsO32() && (!STI.useOddSPReg() || STI.isABI_FPXX())) + TS.emitDirectiveModuleOddSPReg(); +} + +void MaxisAsmPrinter::emitInlineAsmStart() const { + MaxisTargetStreamer &TS = getTargetStreamer(); + + // GCC's choice of assembler options for inline assembly code ('at', 'macro' + // and 'reorder') is different from LLVM's choice for generated code ('noat', + // 'nomacro' and 'noreorder'). + // In order to maintain compatibility with inline assembly code which depends + // on GCC's assembler options being used, we have to switch to those options + // for the duration of the inline assembly block and then switch back. + TS.emitDirectiveSetPush(); + TS.emitDirectiveSetAt(); + TS.emitDirectiveSetMacro(); + TS.emitDirectiveSetReorder(); + OutStreamer->AddBlankLine(); +} + +void MaxisAsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo, + const MCSubtargetInfo *EndInfo) const { + OutStreamer->AddBlankLine(); + getTargetStreamer().emitDirectiveSetPop(); +} + +void MaxisAsmPrinter::EmitJal(const MCSubtargetInfo &STI, MCSymbol *Symbol) { + MCInst I; + I.setOpcode(Maxis::JAL); + I.addOperand( + MCOperand::createExpr(MCSymbolRefExpr::create(Symbol, OutContext))); + OutStreamer->EmitInstruction(I, STI); +} + +void MaxisAsmPrinter::EmitInstrReg(const MCSubtargetInfo &STI, unsigned Opcode, + unsigned Reg) { + MCInst I; + I.setOpcode(Opcode); + I.addOperand(MCOperand::createReg(Reg)); + OutStreamer->EmitInstruction(I, STI); +} + +void MaxisAsmPrinter::EmitInstrRegReg(const MCSubtargetInfo &STI, + unsigned Opcode, unsigned Reg1, + unsigned Reg2) { + MCInst I; + // + // Because of the current td files for Maxis32, the operands for MTC1 + // appear backwards from their normal assembly order. It's not a trivial + // change to fix this in the td file so we adjust for it here. + // + if (Opcode == Maxis::MTC1) { + unsigned Temp = Reg1; + Reg1 = Reg2; + Reg2 = Temp; + } + I.setOpcode(Opcode); + I.addOperand(MCOperand::createReg(Reg1)); + I.addOperand(MCOperand::createReg(Reg2)); + OutStreamer->EmitInstruction(I, STI); +} + +void MaxisAsmPrinter::EmitInstrRegRegReg(const MCSubtargetInfo &STI, + unsigned Opcode, unsigned Reg1, + unsigned Reg2, unsigned Reg3) { + MCInst I; + I.setOpcode(Opcode); + I.addOperand(MCOperand::createReg(Reg1)); + I.addOperand(MCOperand::createReg(Reg2)); + I.addOperand(MCOperand::createReg(Reg3)); + OutStreamer->EmitInstruction(I, STI); +} + +void MaxisAsmPrinter::EmitMovFPIntPair(const MCSubtargetInfo &STI, + unsigned MovOpc, unsigned Reg1, + unsigned Reg2, unsigned FPReg1, + unsigned FPReg2, bool LE) { + if (!LE) { + unsigned temp = Reg1; + Reg1 = Reg2; + Reg2 = temp; + } + EmitInstrRegReg(STI, MovOpc, Reg1, FPReg1); + EmitInstrRegReg(STI, MovOpc, Reg2, FPReg2); +} + +void MaxisAsmPrinter::EmitSwapFPIntParams(const MCSubtargetInfo &STI, + Maxis16HardFloatInfo::FPParamVariant PV, + bool LE, bool ToFP) { + using namespace Maxis16HardFloatInfo; + + unsigned MovOpc = ToFP ? Maxis::MTC1 : Maxis::MFC1; + switch (PV) { + case FSig: + EmitInstrRegReg(STI, MovOpc, Maxis::A0, Maxis::F12); + break; + case FFSig: + EmitMovFPIntPair(STI, MovOpc, Maxis::A0, Maxis::A1, Maxis::F12, Maxis::F14, LE); + break; + case FDSig: + EmitInstrRegReg(STI, MovOpc, Maxis::A0, Maxis::F12); + EmitMovFPIntPair(STI, MovOpc, Maxis::A2, Maxis::A3, Maxis::F14, Maxis::F15, LE); + break; + case DSig: + EmitMovFPIntPair(STI, MovOpc, Maxis::A0, Maxis::A1, Maxis::F12, Maxis::F13, LE); + break; + case DDSig: + EmitMovFPIntPair(STI, MovOpc, Maxis::A0, Maxis::A1, Maxis::F12, Maxis::F13, LE); + EmitMovFPIntPair(STI, MovOpc, Maxis::A2, Maxis::A3, Maxis::F14, Maxis::F15, LE); + break; + case DFSig: + EmitMovFPIntPair(STI, MovOpc, Maxis::A0, Maxis::A1, Maxis::F12, Maxis::F13, LE); + EmitInstrRegReg(STI, MovOpc, Maxis::A2, Maxis::F14); + break; + case NoSig: + return; + } +} + +void MaxisAsmPrinter::EmitSwapFPIntRetval( + const MCSubtargetInfo &STI, Maxis16HardFloatInfo::FPReturnVariant RV, + bool LE) { + using namespace Maxis16HardFloatInfo; + + unsigned MovOpc = Maxis::MFC1; + switch (RV) { + case FRet: + EmitInstrRegReg(STI, MovOpc, Maxis::V0, Maxis::F0); + break; + case DRet: + EmitMovFPIntPair(STI, MovOpc, Maxis::V0, Maxis::V1, Maxis::F0, Maxis::F1, LE); + break; + case CFRet: + EmitMovFPIntPair(STI, MovOpc, Maxis::V0, Maxis::V1, Maxis::F0, Maxis::F1, LE); + break; + case CDRet: + EmitMovFPIntPair(STI, MovOpc, Maxis::V0, Maxis::V1, Maxis::F0, Maxis::F1, LE); + EmitMovFPIntPair(STI, MovOpc, Maxis::A0, Maxis::A1, Maxis::F2, Maxis::F3, LE); + break; + case NoFPRet: + break; + } +} + +void MaxisAsmPrinter::EmitFPCallStub( + const char *Symbol, const Maxis16HardFloatInfo::FuncSignature *Signature) { + using namespace Maxis16HardFloatInfo; + + MCSymbol *MSymbol = OutContext.getOrCreateSymbol(StringRef(Symbol)); + bool LE = getDataLayout().isLittleEndian(); + // Construct a local MCSubtargetInfo here. + // This is because the MachineFunction won't exist (but have not yet been + // freed) and since we're at the global level we can use the default + // constructed subtarget. + std::unique_ptr STI(TM.getTarget().createMCSubtargetInfo( + TM.getTargetTriple().str(), TM.getTargetCPU(), + TM.getTargetFeatureString())); + + // + // .global xxxx + // + OutStreamer->EmitSymbolAttribute(MSymbol, MCSA_Global); + const char *RetType; + // + // make the comment field identifying the return and parameter + // types of the floating point stub + // # Stub function to call rettype xxxx (params) + // + switch (Signature->RetSig) { + case FRet: + RetType = "float"; + break; + case DRet: + RetType = "double"; + break; + case CFRet: + RetType = "complex"; + break; + case CDRet: + RetType = "double complex"; + break; + case NoFPRet: + RetType = ""; + break; + } + const char *Parms; + switch (Signature->ParamSig) { + case FSig: + Parms = "float"; + break; + case FFSig: + Parms = "float, float"; + break; + case FDSig: + Parms = "float, double"; + break; + case DSig: + Parms = "double"; + break; + case DDSig: + Parms = "double, double"; + break; + case DFSig: + Parms = "double, float"; + break; + case NoSig: + Parms = ""; + break; + } + OutStreamer->AddComment("\t# Stub function to call " + Twine(RetType) + " " + + Twine(Symbol) + " (" + Twine(Parms) + ")"); + // + // probably not necessary but we save and restore the current section state + // + OutStreamer->PushSection(); + // + // .section maxis16.call.fpxxxx,"ax",@progbits + // + MCSectionELF *M = OutContext.getELFSection( + ".maxis16.call.fp." + std::string(Symbol), ELF::SHT_PROGBITS, + ELF::SHF_ALLOC | ELF::SHF_EXECINSTR); + OutStreamer->SwitchSection(M, nullptr); + // + // .align 2 + // + OutStreamer->EmitValueToAlignment(4); + MaxisTargetStreamer &TS = getTargetStreamer(); + // + // .set nomaxis16 + // .set nomicromaxis + // + TS.emitDirectiveSetNoMaxis16(); + TS.emitDirectiveSetNoMicroMaxis(); + // + // .ent __call_stub_fp_xxxx + // .type __call_stub_fp_xxxx,@function + // __call_stub_fp_xxxx: + // + std::string x = "__call_stub_fp_" + std::string(Symbol); + MCSymbolELF *Stub = + cast(OutContext.getOrCreateSymbol(StringRef(x))); + TS.emitDirectiveEnt(*Stub); + MCSymbol *MType = + OutContext.getOrCreateSymbol("__call_stub_fp_" + Twine(Symbol)); + OutStreamer->EmitSymbolAttribute(MType, MCSA_ELF_TypeFunction); + OutStreamer->EmitLabel(Stub); + + // Only handle non-pic for now. + assert(!isPositionIndependent() && + "should not be here if we are compiling pic"); + TS.emitDirectiveSetReorder(); + // + // We need to add a MaxisMCExpr class to MCTargetDesc to fully implement + // stubs without raw text but this current patch is for compiler generated + // functions and they all return some value. + // The calling sequence for non pic is different in that case and we need + // to implement %lo and %hi in order to handle the case of no return value + // See the corresponding method in Maxis16HardFloat for details. + // + // mov the return address to S2. + // we have no stack space to store it and we are about to make another call. + // We need to make sure that the enclosing function knows to save S2 + // This should have already been handled. + // + // Mov $18, $31 + + EmitInstrRegRegReg(*STI, Maxis::OR, Maxis::S2, Maxis::RA, Maxis::ZERO); + + EmitSwapFPIntParams(*STI, Signature->ParamSig, LE, true); + + // Jal xxxx + // + EmitJal(*STI, MSymbol); + + // fix return values + EmitSwapFPIntRetval(*STI, Signature->RetSig, LE); + // + // do the return + // if (Signature->RetSig == NoFPRet) + // llvm_unreachable("should not be any stubs here with no return value"); + // else + EmitInstrReg(*STI, Maxis::JR, Maxis::S2); + + MCSymbol *Tmp = OutContext.createTempSymbol(); + OutStreamer->EmitLabel(Tmp); + const MCSymbolRefExpr *E = MCSymbolRefExpr::create(Stub, OutContext); + const MCSymbolRefExpr *T = MCSymbolRefExpr::create(Tmp, OutContext); + const MCExpr *T_min_E = MCBinaryExpr::createSub(T, E, OutContext); + OutStreamer->emitELFSize(Stub, T_min_E); + TS.emitDirectiveEnd(x); + OutStreamer->PopSection(); +} + +void MaxisAsmPrinter::EmitEndOfAsmFile(Module &M) { + // Emit needed stubs + // + for (std::map< + const char *, + const Maxis16HardFloatInfo::FuncSignature *>::const_iterator + it = StubsNeeded.begin(); + it != StubsNeeded.end(); ++it) { + const char *Symbol = it->first; + const Maxis16HardFloatInfo::FuncSignature *Signature = it->second; + EmitFPCallStub(Symbol, Signature); + } + // return to the text section + OutStreamer->SwitchSection(OutContext.getObjectFileInfo()->getTextSection()); +} + +void MaxisAsmPrinter::EmitSled(const MachineInstr &MI, SledKind Kind) { + const uint8_t NoopsInSledCount = Subtarget->isGP64bit() ? 15 : 11; + // For maxis32 we want to emit the following pattern: + // + // .Lxray_sled_N: + // ALIGN + // B .tmpN + // 11 NOP instructions (44 bytes) + // ADDIU T9, T9, 52 + // .tmpN + // + // We need the 44 bytes (11 instructions) because at runtime, we'd + // be patching over the full 48 bytes (12 instructions) with the following + // pattern: + // + // ADDIU SP, SP, -8 + // NOP + // SW RA, 4(SP) + // SW T9, 0(SP) + // LUI T9, %hi(__xray_FunctionEntry/Exit) + // ORI T9, T9, %lo(__xray_FunctionEntry/Exit) + // LUI T0, %hi(function_id) + // JALR T9 + // ORI T0, T0, %lo(function_id) + // LW T9, 0(SP) + // LW RA, 4(SP) + // ADDIU SP, SP, 8 + // + // We add 52 bytes to t9 because we want to adjust the function pointer to + // the actual start of function i.e. the address just after the noop sled. + // We do this because gp displacement relocation is emitted at the start of + // of the function i.e after the nop sled and to correctly calculate the + // global offset table address, t9 must hold the address of the instruction + // containing the gp displacement relocation. + // FIXME: Is this correct for the static relocation model? + // + // For maxis64 we want to emit the following pattern: + // + // .Lxray_sled_N: + // ALIGN + // B .tmpN + // 15 NOP instructions (60 bytes) + // .tmpN + // + // We need the 60 bytes (15 instructions) because at runtime, we'd + // be patching over the full 64 bytes (16 instructions) with the following + // pattern: + // + // DADDIU SP, SP, -16 + // NOP + // SD RA, 8(SP) + // SD T9, 0(SP) + // LUI T9, %highest(__xray_FunctionEntry/Exit) + // ORI T9, T9, %higher(__xray_FunctionEntry/Exit) + // DSLL T9, T9, 16 + // ORI T9, T9, %hi(__xray_FunctionEntry/Exit) + // DSLL T9, T9, 16 + // ORI T9, T9, %lo(__xray_FunctionEntry/Exit) + // LUI T0, %hi(function_id) + // JALR T9 + // ADDIU T0, T0, %lo(function_id) + // LD T9, 0(SP) + // LD RA, 8(SP) + // DADDIU SP, SP, 16 + // + OutStreamer->EmitCodeAlignment(4); + auto CurSled = OutContext.createTempSymbol("xray_sled_", true); + OutStreamer->EmitLabel(CurSled); + auto Target = OutContext.createTempSymbol(); + + // Emit "B .tmpN" instruction, which jumps over the nop sled to the actual + // start of function + const MCExpr *TargetExpr = MCSymbolRefExpr::create( + Target, MCSymbolRefExpr::VariantKind::VK_None, OutContext); + EmitToStreamer(*OutStreamer, MCInstBuilder(Maxis::BEQ) + .addReg(Maxis::ZERO) + .addReg(Maxis::ZERO) + .addExpr(TargetExpr)); + + for (int8_t I = 0; I < NoopsInSledCount; I++) + EmitToStreamer(*OutStreamer, MCInstBuilder(Maxis::SLL) + .addReg(Maxis::ZERO) + .addReg(Maxis::ZERO) + .addImm(0)); + + OutStreamer->EmitLabel(Target); + + if (!Subtarget->isGP64bit()) { + EmitToStreamer(*OutStreamer, + MCInstBuilder(Maxis::ADDiu) + .addReg(Maxis::T9) + .addReg(Maxis::T9) + .addImm(0x34)); + } + + recordSled(CurSled, MI, Kind); +} + +void MaxisAsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI) { + EmitSled(MI, SledKind::FUNCTION_ENTER); +} + +void MaxisAsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) { + EmitSled(MI, SledKind::FUNCTION_EXIT); +} + +void MaxisAsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) { + EmitSled(MI, SledKind::TAIL_CALL); +} + +void MaxisAsmPrinter::PrintDebugValueComment(const MachineInstr *MI, + raw_ostream &OS) { + // TODO: implement +} + +// Emit .dtprelword or .dtpreldword directive +// and value for debug thread local expression. +void MaxisAsmPrinter::EmitDebugThreadLocal(const MCExpr *Value, + unsigned Size) const { + switch (Size) { + case 4: + OutStreamer->EmitDTPRel32Value(Value); + break; + case 8: + OutStreamer->EmitDTPRel64Value(Value); + break; + default: + llvm_unreachable("Unexpected size of expression value."); + } +} + +// Align all targets of indirect branches on bundle size. Used only if target +// is NaCl. +void MaxisAsmPrinter::NaClAlignIndirectJumpTargets(MachineFunction &MF) { + // Align all blocks that are jumped to through jump table. + if (MachineJumpTableInfo *JtInfo = MF.getJumpTableInfo()) { + const std::vector &JT = JtInfo->getJumpTables(); + for (unsigned I = 0; I < JT.size(); ++I) { + const std::vector &MBBs = JT[I].MBBs; + + for (unsigned J = 0; J < MBBs.size(); ++J) + MBBs[J]->setAlignment(MAXIS_NACL_BUNDLE_ALIGN); + } + } + + // If basic block address is taken, block can be target of indirect branch. + for (auto &MBB : MF) { + if (MBB.hasAddressTaken()) + MBB.setAlignment(MAXIS_NACL_BUNDLE_ALIGN); + } +} + +bool MaxisAsmPrinter::isLongBranchPseudo(int Opcode) const { + return (Opcode == Maxis::LONG_BRANCH_LUi + || Opcode == Maxis::LONG_BRANCH_ADDiu + || Opcode == Maxis::LONG_BRANCH_DADDiu); +} + +// Force static initialization. +extern "C" void LLVMInitializeMaxisAsmPrinter() { + RegisterAsmPrinter X(getTheMaxisTarget()); + RegisterAsmPrinter Y(getTheMaxiselTarget()); + RegisterAsmPrinter A(getTheMaxis64Target()); + RegisterAsmPrinter B(getTheMaxis64elTarget()); +} diff --git a/lib/Target/Maxis/MaxisAsmPrinter.h b/lib/Target/Maxis/MaxisAsmPrinter.h new file mode 100644 index 00000000..32a5600a --- /dev/null +++ b/lib/Target/Maxis/MaxisAsmPrinter.h @@ -0,0 +1,168 @@ +//===- MaxisAsmPrinter.h - Maxis 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. +// +//===----------------------------------------------------------------------===// +// +// Maxis Assembly printer class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISASMPRINTER_H +#define LLVM_LIB_TARGET_MAXIS_MAXISASMPRINTER_H + +#include "Maxis16HardFloatInfo.h" +#include "MaxisMCInstLower.h" +#include "MaxisSubtarget.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/Compiler.h" +#include +#include +#include + +namespace llvm { + +class MCOperand; +class MCSubtargetInfo; +class MCSymbol; +class MachineBasicBlock; +class MachineConstantPool; +class MachineFunction; +class MachineInstr; +class MachineOperand; +class MaxisFunctionInfo; +class MaxisTargetStreamer; +class Module; +class raw_ostream; +class TargetMachine; + +class LLVM_LIBRARY_VISIBILITY MaxisAsmPrinter : public AsmPrinter { + MaxisTargetStreamer &getTargetStreamer() const; + + void EmitInstrWithMacroNoAT(const MachineInstr *MI); + + //===------------------------------------------------------------------===// + // XRay implementation + //===------------------------------------------------------------------===// + +public: + // XRay-specific lowering for Maxis. + void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI); + void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI); + void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI); + +private: + /// MCP - Keep a pointer to constantpool entries of the current + /// MachineFunction. + const MachineConstantPool *MCP = nullptr; + + /// InConstantPool - Maintain state when emitting a sequence of constant + /// pool entries so we can properly mark them as data regions. + bool InConstantPool = false; + + std::map + StubsNeeded; + + void EmitSled(const MachineInstr &MI, SledKind Kind); + + // tblgen'erated function. + bool emitPseudoExpansionLowering(MCStreamer &OutStreamer, + const MachineInstr *MI); + + // Emit PseudoReturn, PseudoReturn64, PseudoIndirectBranch, + // and PseudoIndirectBranch64 as a JR, JR_MM, JALR, or JALR64 as appropriate + // for the target. + void emitPseudoIndirectBranch(MCStreamer &OutStreamer, + const MachineInstr *MI); + + // lowerOperand - Convert a MachineOperand into the equivalent MCOperand. + bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp); + + void emitInlineAsmStart() const override; + + void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo, + const MCSubtargetInfo *EndInfo) const override; + + void EmitJal(const MCSubtargetInfo &STI, MCSymbol *Symbol); + + void EmitInstrReg(const MCSubtargetInfo &STI, unsigned Opcode, unsigned Reg); + + void EmitInstrRegReg(const MCSubtargetInfo &STI, unsigned Opcode, + unsigned Reg1, unsigned Reg2); + + void EmitInstrRegRegReg(const MCSubtargetInfo &STI, unsigned Opcode, + unsigned Reg1, unsigned Reg2, unsigned Reg3); + + void EmitMovFPIntPair(const MCSubtargetInfo &STI, unsigned MovOpc, + unsigned Reg1, unsigned Reg2, unsigned FPReg1, + unsigned FPReg2, bool LE); + + void EmitSwapFPIntParams(const MCSubtargetInfo &STI, + Maxis16HardFloatInfo::FPParamVariant, bool LE, + bool ToFP); + + void EmitSwapFPIntRetval(const MCSubtargetInfo &STI, + Maxis16HardFloatInfo::FPReturnVariant, bool LE); + + void EmitFPCallStub(const char *, const Maxis16HardFloatInfo::FuncSignature *); + + void NaClAlignIndirectJumpTargets(MachineFunction &MF); + + bool isLongBranchPseudo(int Opcode) const; + +public: + const MaxisSubtarget *Subtarget; + const MaxisFunctionInfo *MaxisFI; + MaxisMCInstLower MCInstLowering; + + explicit MaxisAsmPrinter(TargetMachine &TM, + std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)), MCInstLowering(*this) {} + + StringRef getPassName() const override { return "Maxis Assembly Printer"; } + + bool runOnMachineFunction(MachineFunction &MF) override; + + void EmitConstantPool() override { + bool UsingConstantPools = + (Subtarget->inMaxis16Mode() && Subtarget->useConstantIslands()); + if (!UsingConstantPools) + AsmPrinter::EmitConstantPool(); + // we emit constant pools customly! + } + + void EmitInstruction(const MachineInstr *MI) override; + void printSavedRegsBitmask(); + void emitFrameDirective(); + const char *getCurrentABIString() const; + void EmitFunctionEntryLabel() override; + void EmitFunctionBodyStart() override; + void EmitFunctionBodyEnd() override; + void EmitBasicBlockEnd(const MachineBasicBlock &MBB) override; + bool isBlockOnlyReachableByFallthrough( + const MachineBasicBlock* MBB) const override; + bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O) override; + bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum, + unsigned AsmVariant, const char *ExtraCode, + raw_ostream &O) override; + void printOperand(const MachineInstr *MI, int opNum, raw_ostream &O); + void printMemOperand(const MachineInstr *MI, int opNum, raw_ostream &O); + void printMemOperandEA(const MachineInstr *MI, int opNum, raw_ostream &O); + void printFCCOperand(const MachineInstr *MI, int opNum, raw_ostream &O, + const char *Modifier = nullptr); + void printRegisterList(const MachineInstr *MI, int opNum, raw_ostream &O); + void EmitStartOfAsmFile(Module &M) override; + void EmitEndOfAsmFile(Module &M) override; + void PrintDebugValueComment(const MachineInstr *MI, raw_ostream &OS); + void EmitDebugThreadLocal(const MCExpr *Value, unsigned Size) const override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISASMPRINTER_H diff --git a/lib/Target/Maxis/MaxisCCState.cpp b/lib/Target/Maxis/MaxisCCState.cpp new file mode 100644 index 00000000..75b64ecf --- /dev/null +++ b/lib/Target/Maxis/MaxisCCState.cpp @@ -0,0 +1,176 @@ +//===---- MaxisCCState.cpp - CCState with Maxis specific extensions ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisCCState.h" +#include "MaxisSubtarget.h" +#include "llvm/IR/Module.h" + +using namespace llvm; + +/// This function returns true if CallSym is a long double emulation routine. +static bool isF128SoftLibCall(const char *CallSym) { + const char *const LibCalls[] = { + "__addtf3", "__divtf3", "__eqtf2", "__extenddftf2", + "__extendsftf2", "__fixtfdi", "__fixtfsi", "__fixtfti", + "__fixunstfdi", "__fixunstfsi", "__fixunstfti", "__floatditf", + "__floatsitf", "__floattitf", "__floatunditf", "__floatunsitf", + "__floatuntitf", "__getf2", "__gttf2", "__letf2", + "__lttf2", "__multf3", "__netf2", "__powitf2", + "__subtf3", "__trunctfdf2", "__trunctfsf2", "__unordtf2", + "ceill", "copysignl", "cosl", "exp2l", + "expl", "floorl", "fmal", "fmodl", + "log10l", "log2l", "logl", "nearbyintl", + "powl", "rintl", "roundl", "sinl", + "sqrtl", "truncl"}; + + // Check that LibCalls is sorted alphabetically. + auto Comp = [](const char *S1, const char *S2) { return strcmp(S1, S2) < 0; }; + assert(std::is_sorted(std::begin(LibCalls), std::end(LibCalls), Comp)); + return std::binary_search(std::begin(LibCalls), std::end(LibCalls), + CallSym, Comp); +} + +/// This function returns true if Ty is fp128, {f128} or i128 which was +/// originally a fp128. +static bool originalTypeIsF128(const Type *Ty, const char *Func) { + if (Ty->isFP128Ty()) + return true; + + if (Ty->isStructTy() && Ty->getStructNumElements() == 1 && + Ty->getStructElementType(0)->isFP128Ty()) + return true; + + // If the Ty is i128 and the function being called is a long double emulation + // routine, then the original type is f128. + return (Func && Ty->isIntegerTy(128) && isF128SoftLibCall(Func)); +} + +/// Return true if the original type was vXfXX. +static bool originalEVTTypeIsVectorFloat(EVT Ty) { + if (Ty.isVector() && Ty.getVectorElementType().isFloatingPoint()) + return true; + + return false; +} + +/// Return true if the original type was vXfXX / vXfXX. +static bool originalTypeIsVectorFloat(const Type * Ty) { + if (Ty->isVectorTy() && Ty->isFPOrFPVectorTy()) + return true; + + return false; +} + +MaxisCCState::SpecialCallingConvType +MaxisCCState::getSpecialCallingConvForCallee(const SDNode *Callee, + const MaxisSubtarget &Subtarget) { + MaxisCCState::SpecialCallingConvType SpecialCallingConv = NoSpecialCallingConv; + if (Subtarget.inMaxis16HardFloat()) { + if (const GlobalAddressSDNode *G = + dyn_cast(Callee)) { + llvm::StringRef Sym = G->getGlobal()->getName(); + Function *F = G->getGlobal()->getParent()->getFunction(Sym); + if (F && F->hasFnAttribute("__Maxis16RetHelper")) { + SpecialCallingConv = Maxis16RetHelperConv; + } + } + } + return SpecialCallingConv; +} + +void MaxisCCState::PreAnalyzeCallResultForF128( + const SmallVectorImpl &Ins, + const Type *RetTy, const char *Call) { + for (unsigned i = 0; i < Ins.size(); ++i) { + OriginalArgWasF128.push_back( + originalTypeIsF128(RetTy, Call)); + OriginalArgWasFloat.push_back(RetTy->isFloatingPointTy()); + } +} + +/// Identify lowered values that originated from f128 or float arguments and +/// record this for use by RetCC_MaxisN. +void MaxisCCState::PreAnalyzeReturnForF128( + const SmallVectorImpl &Outs) { + const MachineFunction &MF = getMachineFunction(); + for (unsigned i = 0; i < Outs.size(); ++i) { + OriginalArgWasF128.push_back( + originalTypeIsF128(MF.getFunction().getReturnType(), nullptr)); + OriginalArgWasFloat.push_back( + MF.getFunction().getReturnType()->isFloatingPointTy()); + } +} + +/// Identify lower values that originated from vXfXX and record +/// this. +void MaxisCCState::PreAnalyzeCallResultForVectorFloat( + const SmallVectorImpl &Ins, const Type *RetTy) { + for (unsigned i = 0; i < Ins.size(); ++i) { + OriginalRetWasFloatVector.push_back(originalTypeIsVectorFloat(RetTy)); + } +} + +/// Identify lowered values that originated from vXfXX arguments and record +/// this. +void MaxisCCState::PreAnalyzeReturnForVectorFloat( + const SmallVectorImpl &Outs) { + for (unsigned i = 0; i < Outs.size(); ++i) { + ISD::OutputArg Out = Outs[i]; + OriginalRetWasFloatVector.push_back( + originalEVTTypeIsVectorFloat(Out.ArgVT)); + } +} + +/// Identify lowered values that originated from f128, float and sret to vXfXX +/// arguments and record this. +void MaxisCCState::PreAnalyzeCallOperands( + const SmallVectorImpl &Outs, + std::vector &FuncArgs, + const char *Func) { + for (unsigned i = 0; i < Outs.size(); ++i) { + TargetLowering::ArgListEntry FuncArg = FuncArgs[Outs[i].OrigArgIndex]; + + OriginalArgWasF128.push_back(originalTypeIsF128(FuncArg.Ty, Func)); + OriginalArgWasFloat.push_back(FuncArg.Ty->isFloatingPointTy()); + OriginalArgWasFloatVector.push_back(FuncArg.Ty->isVectorTy()); + CallOperandIsFixed.push_back(Outs[i].IsFixed); + } +} + +/// Identify lowered values that originated from f128, float and vXfXX arguments +/// and record this. +void MaxisCCState::PreAnalyzeFormalArgumentsForF128( + const SmallVectorImpl &Ins) { + const MachineFunction &MF = getMachineFunction(); + for (unsigned i = 0; i < Ins.size(); ++i) { + Function::const_arg_iterator FuncArg = MF.getFunction().arg_begin(); + + // SRet arguments cannot originate from f128 or {f128} returns so we just + // push false. We have to handle this specially since SRet arguments + // aren't mapped to an original argument. + if (Ins[i].Flags.isSRet()) { + OriginalArgWasF128.push_back(false); + OriginalArgWasFloat.push_back(false); + OriginalArgWasFloatVector.push_back(false); + continue; + } + + assert(Ins[i].getOrigArgIndex() < MF.getFunction().arg_size()); + std::advance(FuncArg, Ins[i].getOrigArgIndex()); + + OriginalArgWasF128.push_back( + originalTypeIsF128(FuncArg->getType(), nullptr)); + OriginalArgWasFloat.push_back(FuncArg->getType()->isFloatingPointTy()); + + // The MAXIS vector ABI exhibits a corner case of sorts or quirk; if the + // first argument is actually an SRet pointer to a vector, then the next + // argument slot is $a2. + OriginalArgWasFloatVector.push_back(FuncArg->getType()->isVectorTy()); + } +} diff --git a/lib/Target/Maxis/MaxisCCState.h b/lib/Target/Maxis/MaxisCCState.h new file mode 100644 index 00000000..b042d172 --- /dev/null +++ b/lib/Target/Maxis/MaxisCCState.h @@ -0,0 +1,167 @@ +//===---- MaxisCCState.h - CCState with Maxis specific extensions -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef MAXISCCSTATE_H +#define MAXISCCSTATE_H + +#include "MaxisISelLowering.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/CallingConvLower.h" + +namespace llvm { +class SDNode; +class MaxisSubtarget; + +class MaxisCCState : public CCState { +public: + enum SpecialCallingConvType { Maxis16RetHelperConv, NoSpecialCallingConv }; + + /// Determine the SpecialCallingConvType for the given callee + static SpecialCallingConvType + getSpecialCallingConvForCallee(const SDNode *Callee, + const MaxisSubtarget &Subtarget); + +private: + /// Identify lowered values that originated from f128 arguments and record + /// this for use by RetCC_MaxisN. + void PreAnalyzeCallResultForF128(const SmallVectorImpl &Ins, + const Type *RetTy, const char * Func); + + /// Identify lowered values that originated from f128 arguments and record + /// this for use by RetCC_MaxisN. + void PreAnalyzeReturnForF128(const SmallVectorImpl &Outs); + + /// Identify lowered values that originated from f128 arguments and record + /// this. + void + PreAnalyzeCallOperands(const SmallVectorImpl &Outs, + std::vector &FuncArgs, + const char *Func); + + /// Identify lowered values that originated from f128 arguments and record + /// this for use by RetCC_MaxisN. + void + PreAnalyzeFormalArgumentsForF128(const SmallVectorImpl &Ins); + + void + PreAnalyzeCallResultForVectorFloat(const SmallVectorImpl &Ins, + const Type *RetTy); + + void PreAnalyzeFormalArgumentsForVectorFloat( + const SmallVectorImpl &Ins); + + void + PreAnalyzeReturnForVectorFloat(const SmallVectorImpl &Outs); + + /// Records whether the value has been lowered from an f128. + SmallVector OriginalArgWasF128; + + /// Records whether the value has been lowered from float. + SmallVector OriginalArgWasFloat; + + /// Records whether the value has been lowered from a floating point vector. + SmallVector OriginalArgWasFloatVector; + + /// Records whether the return value has been lowered from a floating point + /// vector. + SmallVector OriginalRetWasFloatVector; + + /// Records whether the value was a fixed argument. + /// See ISD::OutputArg::IsFixed, + SmallVector CallOperandIsFixed; + + // Used to handle MAXIS16-specific calling convention tweaks. + // FIXME: This should probably be a fully fledged calling convention. + SpecialCallingConvType SpecialCallingConv; + +public: + MaxisCCState(CallingConv::ID CC, bool isVarArg, MachineFunction &MF, + SmallVectorImpl &locs, LLVMContext &C, + SpecialCallingConvType SpecialCC = NoSpecialCallingConv) + : CCState(CC, isVarArg, MF, locs, C), SpecialCallingConv(SpecialCC) {} + + void + AnalyzeCallOperands(const SmallVectorImpl &Outs, + CCAssignFn Fn, + std::vector &FuncArgs, + const char *Func) { + PreAnalyzeCallOperands(Outs, FuncArgs, Func); + CCState::AnalyzeCallOperands(Outs, Fn); + OriginalArgWasF128.clear(); + OriginalArgWasFloat.clear(); + OriginalArgWasFloatVector.clear(); + CallOperandIsFixed.clear(); + } + + // The AnalyzeCallOperands in the base class is not usable since we must + // provide a means of accessing ArgListEntry::IsFixed. Delete them from this + // class. This doesn't stop them being used via the base class though. + void AnalyzeCallOperands(const SmallVectorImpl &Outs, + CCAssignFn Fn) = delete; + void AnalyzeCallOperands(const SmallVectorImpl &Outs, + SmallVectorImpl &Flags, + CCAssignFn Fn) = delete; + + void AnalyzeFormalArguments(const SmallVectorImpl &Ins, + CCAssignFn Fn) { + PreAnalyzeFormalArgumentsForF128(Ins); + CCState::AnalyzeFormalArguments(Ins, Fn); + OriginalArgWasFloat.clear(); + OriginalArgWasF128.clear(); + OriginalArgWasFloatVector.clear(); + } + + void AnalyzeCallResult(const SmallVectorImpl &Ins, + CCAssignFn Fn, const Type *RetTy, + const char *Func) { + PreAnalyzeCallResultForF128(Ins, RetTy, Func); + PreAnalyzeCallResultForVectorFloat(Ins, RetTy); + CCState::AnalyzeCallResult(Ins, Fn); + OriginalArgWasFloat.clear(); + OriginalArgWasF128.clear(); + OriginalArgWasFloatVector.clear(); + } + + void AnalyzeReturn(const SmallVectorImpl &Outs, + CCAssignFn Fn) { + PreAnalyzeReturnForF128(Outs); + PreAnalyzeReturnForVectorFloat(Outs); + CCState::AnalyzeReturn(Outs, Fn); + OriginalArgWasFloat.clear(); + OriginalArgWasF128.clear(); + OriginalArgWasFloatVector.clear(); + } + + bool CheckReturn(const SmallVectorImpl &ArgsFlags, + CCAssignFn Fn) { + PreAnalyzeReturnForF128(ArgsFlags); + PreAnalyzeReturnForVectorFloat(ArgsFlags); + bool Return = CCState::CheckReturn(ArgsFlags, Fn); + OriginalArgWasFloat.clear(); + OriginalArgWasF128.clear(); + OriginalArgWasFloatVector.clear(); + return Return; + } + + bool WasOriginalArgF128(unsigned ValNo) { return OriginalArgWasF128[ValNo]; } + bool WasOriginalArgFloat(unsigned ValNo) { + return OriginalArgWasFloat[ValNo]; + } + bool WasOriginalArgVectorFloat(unsigned ValNo) const { + return OriginalArgWasFloatVector[ValNo]; + } + bool WasOriginalRetVectorFloat(unsigned ValNo) const { + return OriginalRetWasFloatVector[ValNo]; + } + bool IsCallOperandFixed(unsigned ValNo) { return CallOperandIsFixed[ValNo]; } + SpecialCallingConvType getSpecialCallingConv() { return SpecialCallingConv; } +}; +} + +#endif diff --git a/lib/Target/Maxis/MaxisCallingConv.td b/lib/Target/Maxis/MaxisCallingConv.td new file mode 100644 index 00000000..75f63061 --- /dev/null +++ b/lib/Target/Maxis/MaxisCallingConv.td @@ -0,0 +1,412 @@ +//===-- MaxisCallingConv.td - Calling Conventions for Maxis --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This describes the calling conventions for Maxis architecture. +//===----------------------------------------------------------------------===// + +/// CCIfSubtarget - Match if the current subtarget has a feature F. +class CCIfSubtarget + : CCIf" + "(State.getMachineFunction().getSubtarget()).", + F), + A>; + +// The inverse of CCIfSubtarget +class CCIfSubtargetNot : CCIfSubtarget; + +/// Match if the original argument (before lowering) was a float. +/// For example, this is true for i32's that were lowered from soft-float. +class CCIfOrigArgWasNotFloat + : CCIf<"!static_cast(&State)->WasOriginalArgFloat(ValNo)", + A>; + +/// Match if the original argument (before lowering) was a 128-bit float (i.e. +/// long double). +class CCIfOrigArgWasF128 + : CCIf<"static_cast(&State)->WasOriginalArgF128(ValNo)", A>; + +/// Match if this specific argument is a vararg. +/// This is slightly different fro CCIfIsVarArg which matches if any argument is +/// a vararg. +class CCIfArgIsVarArg + : CCIf<"!static_cast(&State)->IsCallOperandFixed(ValNo)", A>; + +/// Match if the return was a floating point vector. +class CCIfOrigArgWasNotVectorFloat + : CCIf<"!static_cast(&State)" + "->WasOriginalRetVectorFloat(ValNo)", A>; + +/// Match if the special calling conv is the specified value. +class CCIfSpecialCallingConv + : CCIf<"static_cast(&State)->getSpecialCallingConv() == " + "MaxisCCState::" # CC, A>; + +// For soft-float, f128 values are returned in A0_64 rather than V1_64. +def RetCC_F128SoftFloat : CallingConv<[ + CCAssignToReg<[V0_64, A0_64]> +]>; + +// For hard-float, f128 values are returned as a pair of f64's rather than a +// pair of i64's. +def RetCC_F128HardFloat : CallingConv<[ + CCBitConvertToType, + + // Contrary to the ABI documentation, a struct containing a long double is + // returned in $f0, and $f1 instead of the usual $f0, and $f2. This is to + // match the de facto ABI as implemented by GCC. + CCIfInReg>, + + CCAssignToReg<[D0_64, D2_64]> +]>; + +// Handle F128 specially since we can't identify the original type during the +// tablegen-erated code. +def RetCC_F128 : CallingConv<[ + CCIfSubtarget<"useSoftFloat()", + CCIfType<[i64], CCDelegateTo>>, + CCIfSubtargetNot<"useSoftFloat()", + CCIfType<[i64], CCDelegateTo>> +]>; + +//===----------------------------------------------------------------------===// +// Maxis O32 Calling Convention +//===----------------------------------------------------------------------===// + +def CC_MaxisO32 : CallingConv<[ + // Promote i8/i16 arguments to i32. + CCIfType<[i1, i8, i16], CCPromoteToType>, + + // Integer values get stored in stack slots that are 4 bytes in + // size and 4-byte aligned. + CCIfType<[i32, f32], CCAssignToStack<4, 4>>, + + // Integer values get stored in stack slots that are 8 bytes in + // size and 8-byte aligned. + CCIfType<[f64], CCAssignToStack<8, 8>> +]>; + +// Only the return rules are defined here for O32. The rules for argument +// passing are defined in MaxisISelLowering.cpp. +def RetCC_MaxisO32 : CallingConv<[ + // Promote i1/i8/i16 return values to i32. + CCIfType<[i1, i8, i16], CCPromoteToType>, + + // i32 are returned in registers V0, V1, A0, A1, unless the original return + // type was a vector of floats. + CCIfOrigArgWasNotVectorFloat>>, + + // f32 are returned in registers F0, F2 + CCIfType<[f32], CCAssignToReg<[F0, F2]>>, + + // f64 arguments are returned in D0_64 and D2_64 in FP64bit mode or + // in D0 and D1 in FP32bit mode. + CCIfType<[f64], CCIfSubtarget<"isFP64bit()", CCAssignToReg<[D0_64, D2_64]>>>, + CCIfType<[f64], CCIfSubtargetNot<"isFP64bit()", CCAssignToReg<[D0, D1]>>> +]>; + +def CC_MaxisO32_FP32 : CustomCallingConv; +def CC_MaxisO32_FP64 : CustomCallingConv; + +def CC_MaxisO32_FP : CallingConv<[ + CCIfSubtargetNot<"isFP64bit()", CCDelegateTo>, + CCIfSubtarget<"isFP64bit()", CCDelegateTo> +]>; + +//===----------------------------------------------------------------------===// +// Maxis N32/64 Calling Convention +//===----------------------------------------------------------------------===// + +def CC_MaxisN_SoftFloat : CallingConv<[ + CCAssignToRegWithShadow<[A0, A1, A2, A3, + T0, T1, T2, T3], + [D12_64, D13_64, D14_64, D15_64, + D16_64, D17_64, D18_64, D19_64]>, + CCAssignToStack<4, 8> +]>; + +def CC_MaxisN : CallingConv<[ + CCIfType<[i8, i16, i32, i64], + CCIfSubtargetNot<"isLittle()", + CCIfInReg>>>, + + // All integers (except soft-float integers) are promoted to 64-bit. + CCIfType<[i8, i16, i32], CCIfOrigArgWasNotFloat>>, + + // The only i32's we have left are soft-float arguments. + CCIfSubtarget<"useSoftFloat()", CCIfType<[i32], CCDelegateTo>>, + + // Integer arguments are passed in integer registers. + CCIfType<[i64], CCAssignToRegWithShadow<[A0_64, A1_64, A2_64, A3_64, + T0_64, T1_64, T2_64, T3_64], + [D12_64, D13_64, D14_64, D15_64, + D16_64, D17_64, D18_64, D19_64]>>, + + // f32 arguments are passed in single precision FP registers. + CCIfType<[f32], CCAssignToRegWithShadow<[F12, F13, F14, F15, + F16, F17, F18, F19], + [A0_64, A1_64, A2_64, A3_64, + T0_64, T1_64, T2_64, T3_64]>>, + + // f64 arguments are passed in double precision FP registers. + CCIfType<[f64], CCAssignToRegWithShadow<[D12_64, D13_64, D14_64, D15_64, + D16_64, D17_64, D18_64, D19_64], + [A0_64, A1_64, A2_64, A3_64, + T0_64, T1_64, T2_64, T3_64]>>, + + // All stack parameter slots become 64-bit doublewords and are 8-byte aligned. + CCIfType<[f32], CCAssignToStack<4, 8>>, + CCIfType<[i64, f64], CCAssignToStack<8, 8>> +]>; + +// N32/64 variable arguments. +// All arguments are passed in integer registers. +def CC_MaxisN_VarArg : CallingConv<[ + CCIfType<[i8, i16, i32, i64], + CCIfSubtargetNot<"isLittle()", + CCIfInReg>>>, + + // All integers are promoted to 64-bit. + CCIfType<[i8, i16, i32], CCPromoteToType>, + + CCIfType<[f32], CCAssignToReg<[A0, A1, A2, A3, T0, T1, T2, T3]>>, + + CCIfType<[i64, f64], CCAssignToReg<[A0_64, A1_64, A2_64, A3_64, + T0_64, T1_64, T2_64, T3_64]>>, + + // All stack parameter slots become 64-bit doublewords and are 8-byte aligned. + CCIfType<[f32], CCAssignToStack<4, 8>>, + CCIfType<[i64, f64], CCAssignToStack<8, 8>> +]>; + +def RetCC_MaxisN : CallingConv<[ + // f128 needs to be handled similarly to f32 and f64. However, f128 is not + // legal and is lowered to i128 which is further lowered to a pair of i64's. + // This presents us with a problem for the calling convention since hard-float + // still needs to pass them in FPU registers, and soft-float needs to use $v0, + // and $a0 instead of the usual $v0, and $v1. We therefore resort to a + // pre-analyze (see PreAnalyzeReturnForF128()) step to pass information on + // whether the result was originally an f128 into the tablegen-erated code. + // + // f128 should only occur for the N64 ABI where long double is 128-bit. On + // N32, long double is equivalent to double. + CCIfType<[i64], CCIfOrigArgWasF128>>, + + // Aggregate returns are positioned at the lowest address in the slot for + // both little and big-endian targets. When passing in registers, this + // requires that big-endian targets shift the value into the upper bits. + CCIfSubtarget<"isLittle()", + CCIfType<[i8, i16, i32, i64], CCIfInReg>>>, + CCIfSubtargetNot<"isLittle()", + CCIfType<[i8, i16, i32, i64], + CCIfInReg>>>, + + // i64 are returned in registers V0_64, V1_64 + CCIfType<[i64], CCAssignToReg<[V0_64, V1_64]>>, + + // f32 are returned in registers F0, F2 + CCIfType<[f32], CCAssignToReg<[F0, F2]>>, + + // f64 are returned in registers D0, D2 + CCIfType<[f64], CCAssignToReg<[D0_64, D2_64]>> +]>; + +//===----------------------------------------------------------------------===// +// Maxis FastCC Calling Convention +//===----------------------------------------------------------------------===// +def CC_MaxisO32_FastCC : CallingConv<[ + // f64 arguments are passed in double-precision floating pointer registers. + CCIfType<[f64], CCIfSubtargetNot<"isFP64bit()", + CCAssignToReg<[D0, D1, D2, D3, D4, D5, D6, + D7, D8, D9]>>>, + CCIfType<[f64], CCIfSubtarget<"isFP64bit()", CCIfSubtarget<"useOddSPReg()", + CCAssignToReg<[D0_64, D1_64, D2_64, D3_64, + D4_64, D5_64, D6_64, D7_64, + D8_64, D9_64, D10_64, D11_64, + D12_64, D13_64, D14_64, D15_64, + D16_64, D17_64, D18_64, + D19_64]>>>>, + CCIfType<[f64], CCIfSubtarget<"isFP64bit()", CCIfSubtarget<"noOddSPReg()", + CCAssignToReg<[D0_64, D2_64, D4_64, D6_64, + D8_64, D10_64, D12_64, D14_64, + D16_64, D18_64]>>>>, + + // Stack parameter slots for f64 are 64-bit doublewords and 8-byte aligned. + CCIfType<[f64], CCAssignToStack<8, 8>> +]>; + +def CC_MaxisN_FastCC : CallingConv<[ + // Integer arguments are passed in integer registers. + CCIfType<[i64], CCAssignToReg<[A0_64, A1_64, A2_64, A3_64, T0_64, T1_64, + T2_64, T3_64, T4_64, T5_64, T6_64, T7_64, + T8_64, V1_64]>>, + + // f64 arguments are passed in double-precision floating pointer registers. + CCIfType<[f64], CCAssignToReg<[D0_64, D1_64, D2_64, D3_64, D4_64, D5_64, + D6_64, D7_64, D8_64, D9_64, D10_64, D11_64, + D12_64, D13_64, D14_64, D15_64, D16_64, D17_64, + D18_64, D19_64]>>, + + // Stack parameter slots for i64 and f64 are 64-bit doublewords and + // 8-byte aligned. + CCIfType<[i64, f64], CCAssignToStack<8, 8>> +]>; + +def CC_Maxis_FastCC : CallingConv<[ + // Handles byval parameters. + CCIfByVal>, + + // Promote i8/i16 arguments to i32. + CCIfType<[i8, i16], CCPromoteToType>, + + // Integer arguments are passed in integer registers. All scratch registers, + // except for AT, V0 and T9, are available to be used as argument registers. + CCIfType<[i32], CCIfSubtargetNot<"isTargetNaCl()", + CCAssignToReg<[A0, A1, A2, A3, T0, T1, T2, T3, T4, T5, T6, T7, T8, V1]>>>, + + // In NaCl, T6, T7 and T8 are reserved and not available as argument + // registers for fastcc. T6 contains the mask for sandboxing control flow + // (indirect jumps and calls). T7 contains the mask for sandboxing memory + // accesses (loads and stores). T8 contains the thread pointer. + CCIfType<[i32], CCIfSubtarget<"isTargetNaCl()", + CCAssignToReg<[A0, A1, A2, A3, T0, T1, T2, T3, T4, T5, V1]>>>, + + // f32 arguments are passed in single-precision floating pointer registers. + CCIfType<[f32], CCIfSubtarget<"useOddSPReg()", + CCAssignToReg<[F0, F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12, F13, + F14, F15, F16, F17, F18, F19]>>>, + + // Don't use odd numbered single-precision registers for -mno-odd-spreg. + CCIfType<[f32], CCIfSubtarget<"noOddSPReg()", + CCAssignToReg<[F0, F2, F4, F6, F8, F10, F12, F14, F16, F18]>>>, + + // Stack parameter slots for i32 and f32 are 32-bit words and 4-byte aligned. + CCIfType<[i32, f32], CCAssignToStack<4, 4>>, + + CCIfSubtarget<"isABI_O32()", CCDelegateTo>, + CCDelegateTo +]>; + +//===----------------------------------------------------------------------===// +// Maxis Calling Convention Dispatch +//===----------------------------------------------------------------------===// + +def RetCC_Maxis : CallingConv<[ + CCIfSubtarget<"isABI_N32()", CCDelegateTo>, + CCIfSubtarget<"isABI_N64()", CCDelegateTo>, + CCDelegateTo +]>; + +def CC_Maxis_ByVal : CallingConv<[ + CCIfSubtarget<"isABI_O32()", CCIfByVal>>, + CCIfByVal> +]>; + +def CC_Maxis16RetHelper : CallingConv<[ + CCIfByVal>, + + // Integer arguments are passed in integer registers. + CCIfType<[i32], CCAssignToReg<[V0, V1, A0, A1]>> +]>; + +def CC_Maxis_FixedArg : CallingConv<[ + // Maxis16 needs special handling on some functions. + CCIf<"State.getCallingConv() != CallingConv::Fast", + CCIfSpecialCallingConv<"Maxis16RetHelperConv", + CCDelegateTo>>, + + CCIfByVal>, + + // f128 needs to be handled similarly to f32 and f64 on hard-float. However, + // f128 is not legal and is lowered to i128 which is further lowered to a pair + // of i64's. + // This presents us with a problem for the calling convention since hard-float + // still needs to pass them in FPU registers. We therefore resort to a + // pre-analyze (see PreAnalyzeFormalArgsForF128()) step to pass information on + // whether the argument was originally an f128 into the tablegen-erated code. + // + // f128 should only occur for the N64 ABI where long double is 128-bit. On + // N32, long double is equivalent to double. + CCIfType<[i64], + CCIfSubtargetNot<"useSoftFloat()", + CCIfOrigArgWasF128>>>, + + CCIfCC<"CallingConv::Fast", CCDelegateTo>, + + CCIfSubtarget<"isABI_O32()", CCDelegateTo>, + CCDelegateTo +]>; + +def CC_Maxis_VarArg : CallingConv<[ + CCIfByVal>, + + CCIfSubtarget<"isABI_O32()", CCDelegateTo>, + CCDelegateTo +]>; + +def CC_Maxis : CallingConv<[ + CCIfVarArg>>, + CCDelegateTo +]>; + +//===----------------------------------------------------------------------===// +// Callee-saved register lists. +//===----------------------------------------------------------------------===// + +def CSR_SingleFloatOnly : CalleeSavedRegs<(add (sequence "F%u", 31, 20), RA, FP, + (sequence "S%u", 7, 0))>; + +def CSR_O32_FPXX : CalleeSavedRegs<(add (sequence "D%u", 15, 10), RA, FP, + (sequence "S%u", 7, 0))> { + let OtherPreserved = (add (decimate (sequence "F%u", 30, 20), 2)); +} + +def CSR_O32 : CalleeSavedRegs<(add (sequence "D%u", 15, 10), RA, FP, + (sequence "S%u", 7, 0))>; + +def CSR_O32_FP64 : + CalleeSavedRegs<(add (decimate (sequence "D%u_64", 30, 20), 2), RA, FP, + (sequence "S%u", 7, 0))>; + +def CSR_N32 : CalleeSavedRegs<(add D20_64, D22_64, D24_64, D26_64, D28_64, + D30_64, RA_64, FP_64, GP_64, + (sequence "S%u_64", 7, 0))>; + +def CSR_N64 : CalleeSavedRegs<(add (sequence "D%u_64", 31, 24), RA_64, FP_64, + GP_64, (sequence "S%u_64", 7, 0))>; + +def CSR_Maxis16RetHelper : + CalleeSavedRegs<(add V0, V1, FP, + (sequence "A%u", 3, 0), (sequence "S%u", 7, 0), + (sequence "D%u", 15, 10))>; + +def CSR_Interrupt_32R6 : CalleeSavedRegs<(add (sequence "A%u", 3, 0), + (sequence "S%u", 7, 0), + (sequence "V%u", 1, 0), + (sequence "T%u", 9, 0), + RA, FP, GP, AT)>; + +def CSR_Interrupt_32 : CalleeSavedRegs<(add (sequence "A%u", 3, 0), + (sequence "S%u", 7, 0), + (sequence "V%u", 1, 0), + (sequence "T%u", 9, 0), + RA, FP, GP, AT, LO0, HI0)>; + +def CSR_Interrupt_64R6 : CalleeSavedRegs<(add (sequence "A%u_64", 3, 0), + (sequence "V%u_64", 1, 0), + (sequence "S%u_64", 7, 0), + (sequence "T%u_64", 9, 0), + RA_64, FP_64, GP_64, AT_64)>; + +def CSR_Interrupt_64 : CalleeSavedRegs<(add (sequence "A%u_64", 3, 0), + (sequence "S%u_64", 7, 0), + (sequence "T%u_64", 9, 0), + (sequence "V%u_64", 1, 0), + RA_64, FP_64, GP_64, AT_64, + LO0_64, HI0_64)>; diff --git a/lib/Target/Maxis/MaxisCondMov.td b/lib/Target/Maxis/MaxisCondMov.td new file mode 100644 index 00000000..64d945d1 --- /dev/null +++ b/lib/Target/Maxis/MaxisCondMov.td @@ -0,0 +1,299 @@ +//===-- MaxisCondMov.td - Describe Maxis Conditional Moves --*- tablegen -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is the Conditional Moves implementation. +// +//===----------------------------------------------------------------------===// + +// Conditional moves: +// These instructions are expanded in +// MaxisISelLowering::EmitInstrWithCustomInserter if target does not have +// conditional move instructions. +// cond:int, data:int +class CMov_I_I_FT : + InstSE<(outs DRC:$rd), (ins DRC:$rs, CRC:$rt, DRC:$F), + !strconcat(opstr, "\t$rd, $rs, $rt"), [], Itin, FrmFR, opstr> { + let Constraints = "$F = $rd"; +} + +// cond:int, data:float +class CMov_I_F_FT : + InstSE<(outs DRC:$fd), (ins DRC:$fs, CRC:$rt, DRC:$F), + !strconcat(opstr, "\t$fd, $fs, $rt"), [], Itin, FrmFR, opstr>, + HARDFLOAT { + let Constraints = "$F = $fd"; +} + +// cond:float, data:int +class CMov_F_I_FT : + InstSE<(outs RC:$rd), (ins RC:$rs, FCCRegsOpnd:$fcc, RC:$F), + !strconcat(opstr, "\t$rd, $rs, $fcc"), + [(set RC:$rd, (OpNode RC:$rs, FCCRegsOpnd:$fcc, RC:$F))], + Itin, FrmFR, opstr>, HARDFLOAT { + let Constraints = "$F = $rd"; +} + +// cond:float, data:float +class CMov_F_F_FT : + InstSE<(outs RC:$fd), (ins RC:$fs, FCCRegsOpnd:$fcc, RC:$F), + !strconcat(opstr, "\t$fd, $fs, $fcc"), + [(set RC:$fd, (OpNode RC:$fs, FCCRegsOpnd:$fcc, RC:$F))], + Itin, FrmFR, opstr>, HARDFLOAT { + let Constraints = "$F = $fd"; +} + +// select patterns +multiclass MovzPats0 { + def : MaxisPat<(select (i32 (setge CRC:$lhs, CRC:$rhs)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (SLTOp CRC:$lhs, CRC:$rhs), DRC:$F)>; + def : MaxisPat<(select (i32 (setuge CRC:$lhs, CRC:$rhs)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (SLTuOp CRC:$lhs, CRC:$rhs), DRC:$F)>; + def : MaxisPat<(select (i32 (setge CRC:$lhs, immSExt16:$rhs)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (SLTiOp CRC:$lhs, immSExt16:$rhs), DRC:$F)>; + def : MaxisPat<(select (i32 (setuge CRC:$lh, immSExt16:$rh)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (SLTiuOp CRC:$lh, immSExt16:$rh), DRC:$F)>; + def : MaxisPat<(select (i32 (setle CRC:$lhs, CRC:$rhs)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (SLTOp CRC:$rhs, CRC:$lhs), DRC:$F)>; + def : MaxisPat<(select (i32 (setule CRC:$lhs, CRC:$rhs)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (SLTuOp CRC:$rhs, CRC:$lhs), DRC:$F)>; + def : MaxisPat<(select (i32 (setgt CRC:$lhs, immSExt16Plus1:$rhs)), + DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (SLTiOp CRC:$lhs, (Plus1 imm:$rhs)), DRC:$F)>; + def : MaxisPat<(select (i32 (setugt CRC:$lhs, immSExt16Plus1:$rhs)), + DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (SLTiuOp CRC:$lhs, (Plus1 imm:$rhs)), + DRC:$F)>; +} + +multiclass MovzPats1 { + def : MaxisPat<(select (i32 (seteq CRC:$lhs, CRC:$rhs)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (XOROp CRC:$lhs, CRC:$rhs), DRC:$F)>; + def : MaxisPat<(select (i32 (seteq CRC:$lhs, 0)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, CRC:$lhs, DRC:$F)>; +} + +multiclass MovzPats2 { + def : MaxisPat< + (select (i32 (seteq CRC:$lhs, immZExt16:$uimm16)), DRC:$T, DRC:$F), + (MOVZInst DRC:$T, (XORiOp CRC:$lhs, immZExt16:$uimm16), DRC:$F)>; +} + +multiclass MovnPats { + def : MaxisPat<(select (i32 (setne CRC:$lhs, CRC:$rhs)), DRC:$T, DRC:$F), + (MOVNInst DRC:$T, (XOROp CRC:$lhs, CRC:$rhs), DRC:$F)>; + def : MaxisPat<(select CRC:$cond, DRC:$T, DRC:$F), + (MOVNInst DRC:$T, CRC:$cond, DRC:$F)>; + def : MaxisPat<(select (i32 (setne CRC:$lhs, 0)),DRC:$T, DRC:$F), + (MOVNInst DRC:$T, CRC:$lhs, DRC:$F)>; +} + +// Instantiation of instructions. +def MOVZ_I_I : MMRel, CMov_I_I_FT<"movz", GPR32Opnd, GPR32Opnd, II_MOVZ>, + ADD_FM<0, 0xa>, INSN_MAXIS4_32_NOT_32R6_64R6; + +let isCodeGenOnly = 1 in { + def MOVZ_I_I64 : CMov_I_I_FT<"movz", GPR32Opnd, GPR64Opnd, II_MOVZ>, + ADD_FM<0, 0xa>, INSN_MAXIS4_32_NOT_32R6_64R6; + def MOVZ_I64_I : CMov_I_I_FT<"movz", GPR64Opnd, GPR32Opnd, II_MOVZ>, + ADD_FM<0, 0xa>, INSN_MAXIS4_32_NOT_32R6_64R6; + def MOVZ_I64_I64 : CMov_I_I_FT<"movz", GPR64Opnd, GPR64Opnd, II_MOVZ>, + ADD_FM<0, 0xa>, INSN_MAXIS4_32_NOT_32R6_64R6; +} + +def MOVN_I_I : MMRel, CMov_I_I_FT<"movn", GPR32Opnd, GPR32Opnd, II_MOVN>, + ADD_FM<0, 0xb>, INSN_MAXIS4_32_NOT_32R6_64R6; + +let isCodeGenOnly = 1 in { + def MOVN_I_I64 : CMov_I_I_FT<"movn", GPR32Opnd, GPR64Opnd, II_MOVN>, + ADD_FM<0, 0xb>, INSN_MAXIS4_32_NOT_32R6_64R6; + def MOVN_I64_I : CMov_I_I_FT<"movn", GPR64Opnd, GPR32Opnd, II_MOVN>, + ADD_FM<0, 0xb>, INSN_MAXIS4_32_NOT_32R6_64R6; + def MOVN_I64_I64 : CMov_I_I_FT<"movn", GPR64Opnd, GPR64Opnd, II_MOVN>, + ADD_FM<0, 0xb>, INSN_MAXIS4_32_NOT_32R6_64R6; +} + +def MOVZ_I_S : MMRel, CMov_I_F_FT<"movz.s", GPR32Opnd, FGR32Opnd, II_MOVZ_S>, + CMov_I_F_FM<18, 16>, INSN_MAXIS4_32_NOT_32R6_64R6; + +let isCodeGenOnly = 1 in +def MOVZ_I64_S : CMov_I_F_FT<"movz.s", GPR64Opnd, FGR32Opnd, II_MOVZ_S>, + CMov_I_F_FM<18, 16>, INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; + +def MOVN_I_S : MMRel, CMov_I_F_FT<"movn.s", GPR32Opnd, FGR32Opnd, II_MOVN_S>, + CMov_I_F_FM<19, 16>, INSN_MAXIS4_32_NOT_32R6_64R6; + +let isCodeGenOnly = 1 in +def MOVN_I64_S : CMov_I_F_FT<"movn.s", GPR64Opnd, FGR32Opnd, II_MOVN_S>, + CMov_I_F_FM<19, 16>, INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; + +def MOVZ_I_D32 : MMRel, CMov_I_F_FT<"movz.d", GPR32Opnd, AFGR64Opnd, + II_MOVZ_D>, CMov_I_F_FM<18, 17>, + INSN_MAXIS4_32_NOT_32R6_64R6, FGR_32; +def MOVN_I_D32 : MMRel, CMov_I_F_FT<"movn.d", GPR32Opnd, AFGR64Opnd, + II_MOVN_D>, CMov_I_F_FM<19, 17>, + INSN_MAXIS4_32_NOT_32R6_64R6, FGR_32; + +let DecoderNamespace = "MaxisFP64" in { + def MOVZ_I_D64 : CMov_I_F_FT<"movz.d", GPR32Opnd, FGR64Opnd, II_MOVZ_D>, + CMov_I_F_FM<18, 17>, INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; + def MOVN_I_D64 : CMov_I_F_FT<"movn.d", GPR32Opnd, FGR64Opnd, II_MOVN_D>, + CMov_I_F_FM<19, 17>, INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; + let isCodeGenOnly = 1 in { + def MOVZ_I64_D64 : CMov_I_F_FT<"movz.d", GPR64Opnd, FGR64Opnd, II_MOVZ_D>, + CMov_I_F_FM<18, 17>, INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; + def MOVN_I64_D64 : CMov_I_F_FT<"movn.d", GPR64Opnd, FGR64Opnd, II_MOVN_D>, + CMov_I_F_FM<19, 17>, INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; + } +} + +def MOVT_I : MMRel, CMov_F_I_FT<"movt", GPR32Opnd, II_MOVT, MaxisCMovFP_T>, + CMov_F_I_FM<1>, INSN_MAXIS4_32_NOT_32R6_64R6; + +let isCodeGenOnly = 1 in +def MOVT_I64 : CMov_F_I_FT<"movt", GPR64Opnd, II_MOVT, MaxisCMovFP_T>, + CMov_F_I_FM<1>, INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; + +def MOVF_I : MMRel, CMov_F_I_FT<"movf", GPR32Opnd, II_MOVF, MaxisCMovFP_F>, + CMov_F_I_FM<0>, INSN_MAXIS4_32_NOT_32R6_64R6; + +let isCodeGenOnly = 1 in +def MOVF_I64 : CMov_F_I_FT<"movf", GPR64Opnd, II_MOVF, MaxisCMovFP_F>, + CMov_F_I_FM<0>, INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; + +def MOVT_S : MMRel, CMov_F_F_FT<"movt.s", FGR32Opnd, II_MOVT_S, MaxisCMovFP_T>, + CMov_F_F_FM<16, 1>, INSN_MAXIS4_32_NOT_32R6_64R6; +def MOVF_S : MMRel, CMov_F_F_FT<"movf.s", FGR32Opnd, II_MOVF_S, MaxisCMovFP_F>, + CMov_F_F_FM<16, 0>, INSN_MAXIS4_32_NOT_32R6_64R6; + +def MOVT_D32 : MMRel, CMov_F_F_FT<"movt.d", AFGR64Opnd, II_MOVT_D, + MaxisCMovFP_T>, CMov_F_F_FM<17, 1>, + INSN_MAXIS4_32_NOT_32R6_64R6, FGR_32; +def MOVF_D32 : MMRel, CMov_F_F_FT<"movf.d", AFGR64Opnd, II_MOVF_D, + MaxisCMovFP_F>, CMov_F_F_FM<17, 0>, + INSN_MAXIS4_32_NOT_32R6_64R6, FGR_32; + +let DecoderNamespace = "MaxisFP64" in { + def MOVT_D64 : CMov_F_F_FT<"movt.d", FGR64Opnd, II_MOVT_D, MaxisCMovFP_T>, + CMov_F_F_FM<17, 1>, INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; + def MOVF_D64 : CMov_F_F_FT<"movf.d", FGR64Opnd, II_MOVF_D, MaxisCMovFP_F>, + CMov_F_F_FM<17, 0>, INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; +} + +// Instantiation of conditional move patterns. +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6; +defm : MovzPats1, INSN_MAXIS4_32_NOT_32R6_64R6; +defm : MovzPats2, INSN_MAXIS4_32_NOT_32R6_64R6; + +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats1, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats1, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats1, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats2, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats2, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats2, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; + +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6; + +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6, + GPR_64; +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6, + GPR_64; +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6, + GPR_64; + +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6; +defm : MovzPats1, INSN_MAXIS4_32_NOT_32R6_64R6; +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6; + +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6, GPR_64; +defm : MovzPats1, INSN_MAXIS4_32_NOT_32R6_64R6, + GPR_64; +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6, + GPR_64; + +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6, FGR_32; +defm : MovzPats1, INSN_MAXIS4_32_NOT_32R6_64R6, + FGR_32; +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6, + FGR_32; + +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; +defm : MovzPats0, + INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; +defm : MovzPats1, INSN_MAXIS4_32_NOT_32R6_64R6, + FGR_64; +defm : MovzPats1, + INSN_MAXIS4_32_NOT_32R6_64R6, FGR_64; +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6, + FGR_64; +defm : MovnPats, INSN_MAXIS4_32_NOT_32R6_64R6, + FGR_64; + +// For targets that don't have conditional-move instructions +// we have to match SELECT nodes with pseudo instructions. +let usesCustomInserter = 1 in { + class Select_Pseudo : + PseudoSE<(outs RC:$dst), (ins GPR32Opnd:$cond, RC:$T, RC:$F), + [(set RC:$dst, (select GPR32Opnd:$cond, RC:$T, RC:$F))]>, + ISA_MAXIS1_NOT_4_32; + + class SelectFP_Pseudo_T : + PseudoSE<(outs RC:$dst), (ins FCCRegsOpnd:$cond, RC:$T, RC:$F), + [(set RC:$dst, (MaxisCMovFP_T RC:$T, FCCRegsOpnd:$cond, RC:$F))]>, + ISA_MAXIS1_NOT_4_32; + + class SelectFP_Pseudo_F : + PseudoSE<(outs RC:$dst), (ins FCCRegsOpnd:$cond, RC:$T, RC:$F), + [(set RC:$dst, (MaxisCMovFP_F RC:$T, FCCRegsOpnd:$cond, RC:$F))]>, + ISA_MAXIS1_NOT_4_32; +} + +def PseudoSELECT_I : Select_Pseudo; +def PseudoSELECT_I64 : Select_Pseudo; +def PseudoSELECT_S : Select_Pseudo; +def PseudoSELECT_D32 : Select_Pseudo, FGR_32; +def PseudoSELECT_D64 : Select_Pseudo, FGR_64; + +def PseudoSELECTFP_T_I : SelectFP_Pseudo_T; +def PseudoSELECTFP_T_I64 : SelectFP_Pseudo_T; +def PseudoSELECTFP_T_S : SelectFP_Pseudo_T; +def PseudoSELECTFP_T_D32 : SelectFP_Pseudo_T, FGR_32; +def PseudoSELECTFP_T_D64 : SelectFP_Pseudo_T, FGR_64; + +def PseudoSELECTFP_F_I : SelectFP_Pseudo_F; +def PseudoSELECTFP_F_I64 : SelectFP_Pseudo_F; +def PseudoSELECTFP_F_S : SelectFP_Pseudo_F; +def PseudoSELECTFP_F_D32 : SelectFP_Pseudo_F, FGR_32; +def PseudoSELECTFP_F_D64 : SelectFP_Pseudo_F, FGR_64; diff --git a/lib/Target/Maxis/MaxisConstantIslandPass.cpp b/lib/Target/Maxis/MaxisConstantIslandPass.cpp new file mode 100644 index 00000000..d7f2f911 --- /dev/null +++ b/lib/Target/Maxis/MaxisConstantIslandPass.cpp @@ -0,0 +1,1687 @@ +//===- MaxisConstantIslandPass.cpp - Emit Pc Relative loads ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass is used to make Pc relative loads of constants. +// For now, only Maxis16 will use this. +// +// Loading constants inline is expensive on Maxis16 and it's in general better +// to place the constant nearby in code space and then it can be loaded with a +// simple 16 bit load instruction. +// +// The constants can be not just numbers but addresses of functions and labels. +// This can be particularly helpful in static relocation mode for embedded +// non-linux targets. +// +//===----------------------------------------------------------------------===// + +#include "Maxis.h" +#include "Maxis16InstrInfo.h" +#include "MaxisMachineFunction.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-constant-islands" + +STATISTIC(NumCPEs, "Number of constpool entries"); +STATISTIC(NumSplit, "Number of uncond branches inserted"); +STATISTIC(NumCBrFixed, "Number of cond branches fixed"); +STATISTIC(NumUBrFixed, "Number of uncond branches fixed"); + +// FIXME: This option should be removed once it has received sufficient testing. +static cl::opt +AlignConstantIslands("maxis-align-constant-islands", cl::Hidden, cl::init(true), + cl::desc("Align constant islands in code")); + +// Rather than do make check tests with huge amounts of code, we force +// the test to use this amount. +static cl::opt ConstantIslandsSmallOffset( + "maxis-constant-islands-small-offset", + cl::init(0), + cl::desc("Make small offsets be this amount for testing purposes"), + cl::Hidden); + +// For testing purposes we tell it to not use relaxed load forms so that it +// will split blocks. +static cl::opt NoLoadRelaxation( + "maxis-constant-islands-no-load-relaxation", + cl::init(false), + cl::desc("Don't relax loads to long loads - for testing purposes"), + cl::Hidden); + +static unsigned int branchTargetOperand(MachineInstr *MI) { + switch (MI->getOpcode()) { + case Maxis::Bimm16: + case Maxis::BimmX16: + case Maxis::Bteqz16: + case Maxis::BteqzX16: + case Maxis::Btnez16: + case Maxis::BtnezX16: + case Maxis::JalB16: + return 0; + case Maxis::BeqzRxImm16: + case Maxis::BeqzRxImmX16: + case Maxis::BnezRxImm16: + case Maxis::BnezRxImmX16: + return 1; + } + llvm_unreachable("Unknown branch type"); +} + +static unsigned int longformBranchOpcode(unsigned int Opcode) { + switch (Opcode) { + case Maxis::Bimm16: + case Maxis::BimmX16: + return Maxis::BimmX16; + case Maxis::Bteqz16: + case Maxis::BteqzX16: + return Maxis::BteqzX16; + case Maxis::Btnez16: + case Maxis::BtnezX16: + return Maxis::BtnezX16; + case Maxis::JalB16: + return Maxis::JalB16; + case Maxis::BeqzRxImm16: + case Maxis::BeqzRxImmX16: + return Maxis::BeqzRxImmX16; + case Maxis::BnezRxImm16: + case Maxis::BnezRxImmX16: + return Maxis::BnezRxImmX16; + } + llvm_unreachable("Unknown branch type"); +} + +// FIXME: need to go through this whole constant islands port and check the math +// for branch ranges and clean this up and make some functions to calculate things +// that are done many times identically. +// Need to refactor some of the code to call this routine. +static unsigned int branchMaxOffsets(unsigned int Opcode) { + unsigned Bits, Scale; + switch (Opcode) { + case Maxis::Bimm16: + Bits = 11; + Scale = 2; + break; + case Maxis::BimmX16: + Bits = 16; + Scale = 2; + break; + case Maxis::BeqzRxImm16: + Bits = 8; + Scale = 2; + break; + case Maxis::BeqzRxImmX16: + Bits = 16; + Scale = 2; + break; + case Maxis::BnezRxImm16: + Bits = 8; + Scale = 2; + break; + case Maxis::BnezRxImmX16: + Bits = 16; + Scale = 2; + break; + case Maxis::Bteqz16: + Bits = 8; + Scale = 2; + break; + case Maxis::BteqzX16: + Bits = 16; + Scale = 2; + break; + case Maxis::Btnez16: + Bits = 8; + Scale = 2; + break; + case Maxis::BtnezX16: + Bits = 16; + Scale = 2; + break; + default: + llvm_unreachable("Unknown branch type"); + } + unsigned MaxOffs = ((1 << (Bits-1))-1) * Scale; + return MaxOffs; +} + +namespace { + + using Iter = MachineBasicBlock::iterator; + using ReverseIter = MachineBasicBlock::reverse_iterator; + + /// MaxisConstantIslands - Due to limited PC-relative displacements, Maxis + /// requires constant pool entries to be scattered among the instructions + /// inside a function. To do this, it completely ignores the normal LLVM + /// constant pool; instead, it places constants wherever it feels like with + /// special instructions. + /// + /// The terminology used in this pass includes: + /// Islands - Clumps of constants placed in the function. + /// Water - Potential places where an island could be formed. + /// CPE - A constant pool entry that has been placed somewhere, which + /// tracks a list of users. + + class MaxisConstantIslands : public MachineFunctionPass { + /// BasicBlockInfo - Information about the offset and size of a single + /// basic block. + struct BasicBlockInfo { + /// Offset - Distance from the beginning of the function to the beginning + /// of this basic block. + /// + /// Offsets are computed assuming worst case padding before an aligned + /// block. This means that subtracting basic block offsets always gives a + /// conservative estimate of the real distance which may be smaller. + /// + /// Because worst case padding is used, the computed offset of an aligned + /// block may not actually be aligned. + unsigned Offset = 0; + + /// Size - Size of the basic block in bytes. If the block contains + /// inline assembly, this is a worst case estimate. + /// + /// The size does not include any alignment padding whether from the + /// beginning of the block, or from an aligned jump table at the end. + unsigned Size = 0; + + BasicBlockInfo() = default; + + // FIXME: ignore LogAlign for this patch + // + unsigned postOffset(unsigned LogAlign = 0) const { + unsigned PO = Offset + Size; + return PO; + } + }; + + std::vector BBInfo; + + /// WaterList - A sorted list of basic blocks where islands could be placed + /// (i.e. blocks that don't fall through to the following block, due + /// to a return, unreachable, or unconditional branch). + std::vector WaterList; + + /// NewWaterList - The subset of WaterList that was created since the + /// previous iteration by inserting unconditional branches. + SmallSet NewWaterList; + + using water_iterator = std::vector::iterator; + + /// CPUser - One user of a constant pool, keeping the machine instruction + /// pointer, the constant pool being referenced, and the max displacement + /// allowed from the instruction to the CP. The HighWaterMark records the + /// highest basic block where a new CPEntry can be placed. To ensure this + /// pass terminates, the CP entries are initially placed at the end of the + /// function and then move monotonically to lower addresses. The + /// exception to this rule is when the current CP entry for a particular + /// CPUser is out of range, but there is another CP entry for the same + /// constant value in range. We want to use the existing in-range CP + /// entry, but if it later moves out of range, the search for new water + /// should resume where it left off. The HighWaterMark is used to record + /// that point. + struct CPUser { + MachineInstr *MI; + MachineInstr *CPEMI; + MachineBasicBlock *HighWaterMark; + + private: + unsigned MaxDisp; + unsigned LongFormMaxDisp; // maxis16 has 16/32 bit instructions + // with different displacements + unsigned LongFormOpcode; + + public: + bool NegOk; + + CPUser(MachineInstr *mi, MachineInstr *cpemi, unsigned maxdisp, + bool neg, + unsigned longformmaxdisp, unsigned longformopcode) + : MI(mi), CPEMI(cpemi), MaxDisp(maxdisp), + LongFormMaxDisp(longformmaxdisp), LongFormOpcode(longformopcode), + NegOk(neg){ + HighWaterMark = CPEMI->getParent(); + } + + /// getMaxDisp - Returns the maximum displacement supported by MI. + unsigned getMaxDisp() const { + unsigned xMaxDisp = ConstantIslandsSmallOffset? + ConstantIslandsSmallOffset: MaxDisp; + return xMaxDisp; + } + + void setMaxDisp(unsigned val) { + MaxDisp = val; + } + + unsigned getLongFormMaxDisp() const { + return LongFormMaxDisp; + } + + unsigned getLongFormOpcode() const { + return LongFormOpcode; + } + }; + + /// CPUsers - Keep track of all of the machine instructions that use various + /// constant pools and their max displacement. + std::vector CPUsers; + + /// CPEntry - One per constant pool entry, keeping the machine instruction + /// pointer, the constpool index, and the number of CPUser's which + /// reference this entry. + struct CPEntry { + MachineInstr *CPEMI; + unsigned CPI; + unsigned RefCount; + + CPEntry(MachineInstr *cpemi, unsigned cpi, unsigned rc = 0) + : CPEMI(cpemi), CPI(cpi), RefCount(rc) {} + }; + + /// CPEntries - Keep track of all of the constant pool entry machine + /// instructions. For each original constpool index (i.e. those that + /// existed upon entry to this pass), it keeps a vector of entries. + /// Original elements are cloned as we go along; the clones are + /// put in the vector of the original element, but have distinct CPIs. + std::vector> CPEntries; + + /// ImmBranch - One per immediate branch, keeping the machine instruction + /// pointer, conditional or unconditional, the max displacement, + /// and (if isCond is true) the corresponding unconditional branch + /// opcode. + struct ImmBranch { + MachineInstr *MI; + unsigned MaxDisp : 31; + bool isCond : 1; + int UncondBr; + + ImmBranch(MachineInstr *mi, unsigned maxdisp, bool cond, int ubr) + : MI(mi), MaxDisp(maxdisp), isCond(cond), UncondBr(ubr) {} + }; + + /// ImmBranches - Keep track of all the immediate branch instructions. + /// + std::vector ImmBranches; + + /// HasFarJump - True if any far jump instruction has been emitted during + /// the branch fix up pass. + bool HasFarJump; + + const MaxisSubtarget *STI = nullptr; + const Maxis16InstrInfo *TII; + MaxisFunctionInfo *MFI; + MachineFunction *MF = nullptr; + MachineConstantPool *MCP = nullptr; + + unsigned PICLabelUId; + bool PrescannedForConstants = false; + + void initPICLabelUId(unsigned UId) { + PICLabelUId = UId; + } + + unsigned createPICLabelUId() { + return PICLabelUId++; + } + + public: + static char ID; + + MaxisConstantIslands() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "Maxis Constant Islands"; } + + bool runOnMachineFunction(MachineFunction &F) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + void doInitialPlacement(std::vector &CPEMIs); + CPEntry *findConstPoolEntry(unsigned CPI, const MachineInstr *CPEMI); + unsigned getCPELogAlign(const MachineInstr &CPEMI); + void initializeFunctionInfo(const std::vector &CPEMIs); + unsigned getOffsetOf(MachineInstr *MI) const; + unsigned getUserOffset(CPUser&) const; + void dumpBBs(); + + bool isOffsetInRange(unsigned UserOffset, unsigned TrialOffset, + unsigned Disp, bool NegativeOK); + bool isOffsetInRange(unsigned UserOffset, unsigned TrialOffset, + const CPUser &U); + + void computeBlockSize(MachineBasicBlock *MBB); + MachineBasicBlock *splitBlockBeforeInstr(MachineInstr &MI); + void updateForInsertedWaterBlock(MachineBasicBlock *NewBB); + void adjustBBOffsetsAfter(MachineBasicBlock *BB); + bool decrementCPEReferenceCount(unsigned CPI, MachineInstr* CPEMI); + int findInRangeCPEntry(CPUser& U, unsigned UserOffset); + int findLongFormInRangeCPEntry(CPUser& U, unsigned UserOffset); + bool findAvailableWater(CPUser&U, unsigned UserOffset, + water_iterator &WaterIter); + void createNewWater(unsigned CPUserIndex, unsigned UserOffset, + MachineBasicBlock *&NewMBB); + bool handleConstantPoolUser(unsigned CPUserIndex); + void removeDeadCPEMI(MachineInstr *CPEMI); + bool removeUnusedCPEntries(); + bool isCPEntryInRange(MachineInstr *MI, unsigned UserOffset, + MachineInstr *CPEMI, unsigned Disp, bool NegOk, + bool DoDump = false); + bool isWaterInRange(unsigned UserOffset, MachineBasicBlock *Water, + CPUser &U, unsigned &Growth); + bool isBBInRange(MachineInstr *MI, MachineBasicBlock *BB, unsigned Disp); + bool fixupImmediateBr(ImmBranch &Br); + bool fixupConditionalBr(ImmBranch &Br); + bool fixupUnconditionalBr(ImmBranch &Br); + + void prescanForConstants(); + }; + +} // end anonymous namespace + +char MaxisConstantIslands::ID = 0; + +bool MaxisConstantIslands::isOffsetInRange + (unsigned UserOffset, unsigned TrialOffset, + const CPUser &U) { + return isOffsetInRange(UserOffset, TrialOffset, + U.getMaxDisp(), U.NegOk); +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +/// print block size and offset information - debugging +LLVM_DUMP_METHOD void MaxisConstantIslands::dumpBBs() { + for (unsigned J = 0, E = BBInfo.size(); J !=E; ++J) { + const BasicBlockInfo &BBI = BBInfo[J]; + dbgs() << format("%08x %bb.%u\t", BBI.Offset, J) + << format(" size=%#x\n", BBInfo[J].Size); + } +} +#endif + +bool MaxisConstantIslands::runOnMachineFunction(MachineFunction &mf) { + // The intention is for this to be a maxis16 only pass for now + // FIXME: + MF = &mf; + MCP = mf.getConstantPool(); + STI = &static_cast(mf.getSubtarget()); + DEBUG(dbgs() << "constant island machine function " << "\n"); + if (!STI->inMaxis16Mode() || !MaxisSubtarget::useConstantIslands()) { + return false; + } + TII = (const Maxis16InstrInfo *)STI->getInstrInfo(); + MFI = MF->getInfo(); + DEBUG(dbgs() << "constant island processing " << "\n"); + // + // will need to make predermination if there is any constants we need to + // put in constant islands. TBD. + // + if (!PrescannedForConstants) prescanForConstants(); + + HasFarJump = false; + // This pass invalidates liveness information when it splits basic blocks. + MF->getRegInfo().invalidateLiveness(); + + // Renumber all of the machine basic blocks in the function, guaranteeing that + // the numbers agree with the position of the block in the function. + MF->RenumberBlocks(); + + bool MadeChange = false; + + // Perform the initial placement of the constant pool entries. To start with, + // we put them all at the end of the function. + std::vector CPEMIs; + if (!MCP->isEmpty()) + doInitialPlacement(CPEMIs); + + /// The next UID to take is the first unused one. + initPICLabelUId(CPEMIs.size()); + + // Do the initial scan of the function, building up information about the + // sizes of each block, the location of all the water, and finding all of the + // constant pool users. + initializeFunctionInfo(CPEMIs); + CPEMIs.clear(); + DEBUG(dumpBBs()); + + /// Remove dead constant pool entries. + MadeChange |= removeUnusedCPEntries(); + + // Iteratively place constant pool entries and fix up branches until there + // is no change. + unsigned NoCPIters = 0, NoBRIters = 0; + (void)NoBRIters; + while (true) { + DEBUG(dbgs() << "Beginning CP iteration #" << NoCPIters << '\n'); + bool CPChange = false; + for (unsigned i = 0, e = CPUsers.size(); i != e; ++i) + CPChange |= handleConstantPoolUser(i); + if (CPChange && ++NoCPIters > 30) + report_fatal_error("Constant Island pass failed to converge!"); + DEBUG(dumpBBs()); + + // Clear NewWaterList now. If we split a block for branches, it should + // appear as "new water" for the next iteration of constant pool placement. + NewWaterList.clear(); + + DEBUG(dbgs() << "Beginning BR iteration #" << NoBRIters << '\n'); + bool BRChange = false; + for (unsigned i = 0, e = ImmBranches.size(); i != e; ++i) + BRChange |= fixupImmediateBr(ImmBranches[i]); + if (BRChange && ++NoBRIters > 30) + report_fatal_error("Branch Fix Up pass failed to converge!"); + DEBUG(dumpBBs()); + if (!CPChange && !BRChange) + break; + MadeChange = true; + } + + DEBUG(dbgs() << '\n'; dumpBBs()); + + BBInfo.clear(); + WaterList.clear(); + CPUsers.clear(); + CPEntries.clear(); + ImmBranches.clear(); + return MadeChange; +} + +/// doInitialPlacement - Perform the initial placement of the constant pool +/// entries. To start with, we put them all at the end of the function. +void +MaxisConstantIslands::doInitialPlacement(std::vector &CPEMIs) { + // Create the basic block to hold the CPE's. + MachineBasicBlock *BB = MF->CreateMachineBasicBlock(); + MF->push_back(BB); + + // MachineConstantPool measures alignment in bytes. We measure in log2(bytes). + unsigned MaxAlign = Log2_32(MCP->getConstantPoolAlignment()); + + // Mark the basic block as required by the const-pool. + // If AlignConstantIslands isn't set, use 4-byte alignment for everything. + BB->setAlignment(AlignConstantIslands ? MaxAlign : 2); + + // The function needs to be as aligned as the basic blocks. The linker may + // move functions around based on their alignment. + MF->ensureAlignment(BB->getAlignment()); + + // Order the entries in BB by descending alignment. That ensures correct + // alignment of all entries as long as BB is sufficiently aligned. Keep + // track of the insertion point for each alignment. We are going to bucket + // sort the entries as they are created. + SmallVector InsPoint(MaxAlign + 1, BB->end()); + + // Add all of the constants from the constant pool to the end block, use an + // identity mapping of CPI's to CPE's. + const std::vector &CPs = MCP->getConstants(); + + const DataLayout &TD = MF->getDataLayout(); + for (unsigned i = 0, e = CPs.size(); i != e; ++i) { + unsigned Size = TD.getTypeAllocSize(CPs[i].getType()); + assert(Size >= 4 && "Too small constant pool entry"); + unsigned Align = CPs[i].getAlignment(); + assert(isPowerOf2_32(Align) && "Invalid alignment"); + // Verify that all constant pool entries are a multiple of their alignment. + // If not, we would have to pad them out so that instructions stay aligned. + assert((Size % Align) == 0 && "CP Entry not multiple of 4 bytes!"); + + // Insert CONSTPOOL_ENTRY before entries with a smaller alignment. + unsigned LogAlign = Log2_32(Align); + MachineBasicBlock::iterator InsAt = InsPoint[LogAlign]; + + MachineInstr *CPEMI = + BuildMI(*BB, InsAt, DebugLoc(), TII->get(Maxis::CONSTPOOL_ENTRY)) + .addImm(i).addConstantPoolIndex(i).addImm(Size); + + CPEMIs.push_back(CPEMI); + + // Ensure that future entries with higher alignment get inserted before + // CPEMI. This is bucket sort with iterators. + for (unsigned a = LogAlign + 1; a <= MaxAlign; ++a) + if (InsPoint[a] == InsAt) + InsPoint[a] = CPEMI; + // Add a new CPEntry, but no corresponding CPUser yet. + CPEntries.emplace_back(1, CPEntry(CPEMI, i)); + ++NumCPEs; + DEBUG(dbgs() << "Moved CPI#" << i << " to end of function, size = " + << Size << ", align = " << Align <<'\n'); + } + DEBUG(BB->dump()); +} + +/// BBHasFallthrough - Return true if the specified basic block can fallthrough +/// into the block immediately after it. +static bool BBHasFallthrough(MachineBasicBlock *MBB) { + // Get the next machine basic block in the function. + MachineFunction::iterator MBBI = MBB->getIterator(); + // Can't fall off end of function. + if (std::next(MBBI) == MBB->getParent()->end()) + return false; + + MachineBasicBlock *NextBB = &*std::next(MBBI); + for (MachineBasicBlock::succ_iterator I = MBB->succ_begin(), + E = MBB->succ_end(); I != E; ++I) + if (*I == NextBB) + return true; + + return false; +} + +/// findConstPoolEntry - Given the constpool index and CONSTPOOL_ENTRY MI, +/// look up the corresponding CPEntry. +MaxisConstantIslands::CPEntry +*MaxisConstantIslands::findConstPoolEntry(unsigned CPI, + const MachineInstr *CPEMI) { + std::vector &CPEs = CPEntries[CPI]; + // Number of entries per constpool index should be small, just do a + // linear search. + for (unsigned i = 0, e = CPEs.size(); i != e; ++i) { + if (CPEs[i].CPEMI == CPEMI) + return &CPEs[i]; + } + return nullptr; +} + +/// getCPELogAlign - Returns the required alignment of the constant pool entry +/// represented by CPEMI. Alignment is measured in log2(bytes) units. +unsigned MaxisConstantIslands::getCPELogAlign(const MachineInstr &CPEMI) { + assert(CPEMI.getOpcode() == Maxis::CONSTPOOL_ENTRY); + + // Everything is 4-byte aligned unless AlignConstantIslands is set. + if (!AlignConstantIslands) + return 2; + + unsigned CPI = CPEMI.getOperand(1).getIndex(); + assert(CPI < MCP->getConstants().size() && "Invalid constant pool index."); + unsigned Align = MCP->getConstants()[CPI].getAlignment(); + assert(isPowerOf2_32(Align) && "Invalid CPE alignment"); + return Log2_32(Align); +} + +/// initializeFunctionInfo - Do the initial scan of the function, building up +/// information about the sizes of each block, the location of all the water, +/// and finding all of the constant pool users. +void MaxisConstantIslands:: +initializeFunctionInfo(const std::vector &CPEMIs) { + BBInfo.clear(); + BBInfo.resize(MF->getNumBlockIDs()); + + // First thing, compute the size of all basic blocks, and see if the function + // has any inline assembly in it. If so, we have to be conservative about + // alignment assumptions, as we don't know for sure the size of any + // instructions in the inline assembly. + for (MachineFunction::iterator I = MF->begin(), E = MF->end(); I != E; ++I) + computeBlockSize(&*I); + + // Compute block offsets. + adjustBBOffsetsAfter(&MF->front()); + + // Now go back through the instructions and build up our data structures. + for (MachineBasicBlock &MBB : *MF) { + // If this block doesn't fall through into the next MBB, then this is + // 'water' that a constant pool island could be placed. + if (!BBHasFallthrough(&MBB)) + WaterList.push_back(&MBB); + for (MachineInstr &MI : MBB) { + if (MI.isDebugValue()) + continue; + + int Opc = MI.getOpcode(); + if (MI.isBranch()) { + bool isCond = false; + unsigned Bits = 0; + unsigned Scale = 1; + int UOpc = Opc; + switch (Opc) { + default: + continue; // Ignore other branches for now + case Maxis::Bimm16: + Bits = 11; + Scale = 2; + isCond = false; + break; + case Maxis::BimmX16: + Bits = 16; + Scale = 2; + isCond = false; + break; + case Maxis::BeqzRxImm16: + UOpc=Maxis::Bimm16; + Bits = 8; + Scale = 2; + isCond = true; + break; + case Maxis::BeqzRxImmX16: + UOpc=Maxis::Bimm16; + Bits = 16; + Scale = 2; + isCond = true; + break; + case Maxis::BnezRxImm16: + UOpc=Maxis::Bimm16; + Bits = 8; + Scale = 2; + isCond = true; + break; + case Maxis::BnezRxImmX16: + UOpc=Maxis::Bimm16; + Bits = 16; + Scale = 2; + isCond = true; + break; + case Maxis::Bteqz16: + UOpc=Maxis::Bimm16; + Bits = 8; + Scale = 2; + isCond = true; + break; + case Maxis::BteqzX16: + UOpc=Maxis::Bimm16; + Bits = 16; + Scale = 2; + isCond = true; + break; + case Maxis::Btnez16: + UOpc=Maxis::Bimm16; + Bits = 8; + Scale = 2; + isCond = true; + break; + case Maxis::BtnezX16: + UOpc=Maxis::Bimm16; + Bits = 16; + Scale = 2; + isCond = true; + break; + } + // Record this immediate branch. + unsigned MaxOffs = ((1 << (Bits-1))-1) * Scale; + ImmBranches.push_back(ImmBranch(&MI, MaxOffs, isCond, UOpc)); + } + + if (Opc == Maxis::CONSTPOOL_ENTRY) + continue; + + // Scan the instructions for constant pool operands. + for (unsigned op = 0, e = MI.getNumOperands(); op != e; ++op) + if (MI.getOperand(op).isCPI()) { + // We found one. The addressing mode tells us the max displacement + // from the PC that this instruction permits. + + // Basic size info comes from the TSFlags field. + unsigned Bits = 0; + unsigned Scale = 1; + bool NegOk = false; + unsigned LongFormBits = 0; + unsigned LongFormScale = 0; + unsigned LongFormOpcode = 0; + switch (Opc) { + default: + llvm_unreachable("Unknown addressing mode for CP reference!"); + case Maxis::LwRxPcTcp16: + Bits = 8; + Scale = 4; + LongFormOpcode = Maxis::LwRxPcTcpX16; + LongFormBits = 14; + LongFormScale = 1; + break; + case Maxis::LwRxPcTcpX16: + Bits = 14; + Scale = 1; + NegOk = true; + break; + } + // Remember that this is a user of a CP entry. + unsigned CPI = MI.getOperand(op).getIndex(); + MachineInstr *CPEMI = CPEMIs[CPI]; + unsigned MaxOffs = ((1 << Bits)-1) * Scale; + unsigned LongFormMaxOffs = ((1 << LongFormBits)-1) * LongFormScale; + CPUsers.push_back(CPUser(&MI, CPEMI, MaxOffs, NegOk, LongFormMaxOffs, + LongFormOpcode)); + + // Increment corresponding CPEntry reference count. + CPEntry *CPE = findConstPoolEntry(CPI, CPEMI); + assert(CPE && "Cannot find a corresponding CPEntry!"); + CPE->RefCount++; + + // Instructions can only use one CP entry, don't bother scanning the + // rest of the operands. + break; + } + } + } +} + +/// computeBlockSize - Compute the size and some alignment information for MBB. +/// This function updates BBInfo directly. +void MaxisConstantIslands::computeBlockSize(MachineBasicBlock *MBB) { + BasicBlockInfo &BBI = BBInfo[MBB->getNumber()]; + BBI.Size = 0; + + for (const MachineInstr &MI : *MBB) + BBI.Size += TII->getInstSizeInBytes(MI); +} + +/// getOffsetOf - Return the current offset of the specified machine instruction +/// from the start of the function. This offset changes as stuff is moved +/// around inside the function. +unsigned MaxisConstantIslands::getOffsetOf(MachineInstr *MI) const { + MachineBasicBlock *MBB = MI->getParent(); + + // The offset is composed of two things: the sum of the sizes of all MBB's + // before this instruction's block, and the offset from the start of the block + // it is in. + unsigned Offset = BBInfo[MBB->getNumber()].Offset; + + // Sum instructions before MI in MBB. + for (MachineBasicBlock::iterator I = MBB->begin(); &*I != MI; ++I) { + assert(I != MBB->end() && "Didn't find MI in its own basic block?"); + Offset += TII->getInstSizeInBytes(*I); + } + return Offset; +} + +/// CompareMBBNumbers - Little predicate function to sort the WaterList by MBB +/// ID. +static bool CompareMBBNumbers(const MachineBasicBlock *LHS, + const MachineBasicBlock *RHS) { + return LHS->getNumber() < RHS->getNumber(); +} + +/// updateForInsertedWaterBlock - When a block is newly inserted into the +/// machine function, it upsets all of the block numbers. Renumber the blocks +/// and update the arrays that parallel this numbering. +void MaxisConstantIslands::updateForInsertedWaterBlock + (MachineBasicBlock *NewBB) { + // Renumber the MBB's to keep them consecutive. + NewBB->getParent()->RenumberBlocks(NewBB); + + // Insert an entry into BBInfo to align it properly with the (newly + // renumbered) block numbers. + BBInfo.insert(BBInfo.begin() + NewBB->getNumber(), BasicBlockInfo()); + + // Next, update WaterList. Specifically, we need to add NewMBB as having + // available water after it. + water_iterator IP = + std::lower_bound(WaterList.begin(), WaterList.end(), NewBB, + CompareMBBNumbers); + WaterList.insert(IP, NewBB); +} + +unsigned MaxisConstantIslands::getUserOffset(CPUser &U) const { + return getOffsetOf(U.MI); +} + +/// Split the basic block containing MI into two blocks, which are joined by +/// an unconditional branch. Update data structures and renumber blocks to +/// account for this change and returns the newly created block. +MachineBasicBlock * +MaxisConstantIslands::splitBlockBeforeInstr(MachineInstr &MI) { + MachineBasicBlock *OrigBB = MI.getParent(); + + // Create a new MBB for the code after the OrigBB. + MachineBasicBlock *NewBB = + MF->CreateMachineBasicBlock(OrigBB->getBasicBlock()); + MachineFunction::iterator MBBI = ++OrigBB->getIterator(); + MF->insert(MBBI, NewBB); + + // Splice the instructions starting with MI over to NewBB. + NewBB->splice(NewBB->end(), OrigBB, MI, OrigBB->end()); + + // Add an unconditional branch from OrigBB to NewBB. + // Note the new unconditional branch is not being recorded. + // There doesn't seem to be meaningful DebugInfo available; this doesn't + // correspond to anything in the source. + BuildMI(OrigBB, DebugLoc(), TII->get(Maxis::Bimm16)).addMBB(NewBB); + ++NumSplit; + + // Update the CFG. All succs of OrigBB are now succs of NewBB. + NewBB->transferSuccessors(OrigBB); + + // OrigBB branches to NewBB. + OrigBB->addSuccessor(NewBB); + + // Update internal data structures to account for the newly inserted MBB. + // This is almost the same as updateForInsertedWaterBlock, except that + // the Water goes after OrigBB, not NewBB. + MF->RenumberBlocks(NewBB); + + // Insert an entry into BBInfo to align it properly with the (newly + // renumbered) block numbers. + BBInfo.insert(BBInfo.begin() + NewBB->getNumber(), BasicBlockInfo()); + + // Next, update WaterList. Specifically, we need to add OrigMBB as having + // available water after it (but not if it's already there, which happens + // when splitting before a conditional branch that is followed by an + // unconditional branch - in that case we want to insert NewBB). + water_iterator IP = + std::lower_bound(WaterList.begin(), WaterList.end(), OrigBB, + CompareMBBNumbers); + MachineBasicBlock* WaterBB = *IP; + if (WaterBB == OrigBB) + WaterList.insert(std::next(IP), NewBB); + else + WaterList.insert(IP, OrigBB); + NewWaterList.insert(OrigBB); + + // Figure out how large the OrigBB is. As the first half of the original + // block, it cannot contain a tablejump. The size includes + // the new jump we added. (It should be possible to do this without + // recounting everything, but it's very confusing, and this is rarely + // executed.) + computeBlockSize(OrigBB); + + // Figure out how large the NewMBB is. As the second half of the original + // block, it may contain a tablejump. + computeBlockSize(NewBB); + + // All BBOffsets following these blocks must be modified. + adjustBBOffsetsAfter(OrigBB); + + return NewBB; +} + +/// isOffsetInRange - Checks whether UserOffset (the location of a constant pool +/// reference) is within MaxDisp of TrialOffset (a proposed location of a +/// constant pool entry). +bool MaxisConstantIslands::isOffsetInRange(unsigned UserOffset, + unsigned TrialOffset, unsigned MaxDisp, + bool NegativeOK) { + if (UserOffset <= TrialOffset) { + // User before the Trial. + if (TrialOffset - UserOffset <= MaxDisp) + return true; + } else if (NegativeOK) { + if (UserOffset - TrialOffset <= MaxDisp) + return true; + } + return false; +} + +/// isWaterInRange - Returns true if a CPE placed after the specified +/// Water (a basic block) will be in range for the specific MI. +/// +/// Compute how much the function will grow by inserting a CPE after Water. +bool MaxisConstantIslands::isWaterInRange(unsigned UserOffset, + MachineBasicBlock* Water, CPUser &U, + unsigned &Growth) { + unsigned CPELogAlign = getCPELogAlign(*U.CPEMI); + unsigned CPEOffset = BBInfo[Water->getNumber()].postOffset(CPELogAlign); + unsigned NextBlockOffset, NextBlockAlignment; + MachineFunction::const_iterator NextBlock = ++Water->getIterator(); + if (NextBlock == MF->end()) { + NextBlockOffset = BBInfo[Water->getNumber()].postOffset(); + NextBlockAlignment = 0; + } else { + NextBlockOffset = BBInfo[NextBlock->getNumber()].Offset; + NextBlockAlignment = NextBlock->getAlignment(); + } + unsigned Size = U.CPEMI->getOperand(2).getImm(); + unsigned CPEEnd = CPEOffset + Size; + + // The CPE may be able to hide in the alignment padding before the next + // block. It may also cause more padding to be required if it is more aligned + // that the next block. + if (CPEEnd > NextBlockOffset) { + Growth = CPEEnd - NextBlockOffset; + // Compute the padding that would go at the end of the CPE to align the next + // block. + Growth += OffsetToAlignment(CPEEnd, 1ULL << NextBlockAlignment); + + // If the CPE is to be inserted before the instruction, that will raise + // the offset of the instruction. Also account for unknown alignment padding + // in blocks between CPE and the user. + if (CPEOffset < UserOffset) + UserOffset += Growth; + } else + // CPE fits in existing padding. + Growth = 0; + + return isOffsetInRange(UserOffset, CPEOffset, U); +} + +/// isCPEntryInRange - Returns true if the distance between specific MI and +/// specific ConstPool entry instruction can fit in MI's displacement field. +bool MaxisConstantIslands::isCPEntryInRange + (MachineInstr *MI, unsigned UserOffset, + MachineInstr *CPEMI, unsigned MaxDisp, + bool NegOk, bool DoDump) { + unsigned CPEOffset = getOffsetOf(CPEMI); + + if (DoDump) { + DEBUG({ + unsigned Block = MI->getParent()->getNumber(); + const BasicBlockInfo &BBI = BBInfo[Block]; + dbgs() << "User of CPE#" << CPEMI->getOperand(0).getImm() + << " max delta=" << MaxDisp + << format(" insn address=%#x", UserOffset) << " in " + << printMBBReference(*MI->getParent()) << ": " + << format("%#x-%x\t", BBI.Offset, BBI.postOffset()) << *MI + << format("CPE address=%#x offset=%+d: ", CPEOffset, + int(CPEOffset - UserOffset)); + }); + } + + return isOffsetInRange(UserOffset, CPEOffset, MaxDisp, NegOk); +} + +#ifndef NDEBUG +/// BBIsJumpedOver - Return true of the specified basic block's only predecessor +/// unconditionally branches to its only successor. +static bool BBIsJumpedOver(MachineBasicBlock *MBB) { + if (MBB->pred_size() != 1 || MBB->succ_size() != 1) + return false; + MachineBasicBlock *Succ = *MBB->succ_begin(); + MachineBasicBlock *Pred = *MBB->pred_begin(); + MachineInstr *PredMI = &Pred->back(); + if (PredMI->getOpcode() == Maxis::Bimm16) + return PredMI->getOperand(0).getMBB() == Succ; + return false; +} +#endif + +void MaxisConstantIslands::adjustBBOffsetsAfter(MachineBasicBlock *BB) { + unsigned BBNum = BB->getNumber(); + for(unsigned i = BBNum + 1, e = MF->getNumBlockIDs(); i < e; ++i) { + // Get the offset and known bits at the end of the layout predecessor. + // Include the alignment of the current block. + unsigned Offset = BBInfo[i - 1].Offset + BBInfo[i - 1].Size; + BBInfo[i].Offset = Offset; + } +} + +/// decrementCPEReferenceCount - find the constant pool entry with index CPI +/// and instruction CPEMI, and decrement its refcount. If the refcount +/// becomes 0 remove the entry and instruction. Returns true if we removed +/// the entry, false if we didn't. +bool MaxisConstantIslands::decrementCPEReferenceCount(unsigned CPI, + MachineInstr *CPEMI) { + // Find the old entry. Eliminate it if it is no longer used. + CPEntry *CPE = findConstPoolEntry(CPI, CPEMI); + assert(CPE && "Unexpected!"); + if (--CPE->RefCount == 0) { + removeDeadCPEMI(CPEMI); + CPE->CPEMI = nullptr; + --NumCPEs; + return true; + } + return false; +} + +/// LookForCPEntryInRange - see if the currently referenced CPE is in range; +/// if not, see if an in-range clone of the CPE is in range, and if so, +/// change the data structures so the user references the clone. Returns: +/// 0 = no existing entry found +/// 1 = entry found, and there were no code insertions or deletions +/// 2 = entry found, and there were code insertions or deletions +int MaxisConstantIslands::findInRangeCPEntry(CPUser& U, unsigned UserOffset) +{ + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + + // Check to see if the CPE is already in-range. + if (isCPEntryInRange(UserMI, UserOffset, CPEMI, U.getMaxDisp(), U.NegOk, + true)) { + DEBUG(dbgs() << "In range\n"); + return 1; + } + + // No. Look for previously created clones of the CPE that are in range. + unsigned CPI = CPEMI->getOperand(1).getIndex(); + std::vector &CPEs = CPEntries[CPI]; + for (unsigned i = 0, e = CPEs.size(); i != e; ++i) { + // We already tried this one + if (CPEs[i].CPEMI == CPEMI) + continue; + // Removing CPEs can leave empty entries, skip + if (CPEs[i].CPEMI == nullptr) + continue; + if (isCPEntryInRange(UserMI, UserOffset, CPEs[i].CPEMI, U.getMaxDisp(), + U.NegOk)) { + DEBUG(dbgs() << "Replacing CPE#" << CPI << " with CPE#" + << CPEs[i].CPI << "\n"); + // Point the CPUser node to the replacement + U.CPEMI = CPEs[i].CPEMI; + // Change the CPI in the instruction operand to refer to the clone. + for (unsigned j = 0, e = UserMI->getNumOperands(); j != e; ++j) + if (UserMI->getOperand(j).isCPI()) { + UserMI->getOperand(j).setIndex(CPEs[i].CPI); + break; + } + // Adjust the refcount of the clone... + CPEs[i].RefCount++; + // ...and the original. If we didn't remove the old entry, none of the + // addresses changed, so we don't need another pass. + return decrementCPEReferenceCount(CPI, CPEMI) ? 2 : 1; + } + } + return 0; +} + +/// LookForCPEntryInRange - see if the currently referenced CPE is in range; +/// This version checks if the longer form of the instruction can be used to +/// to satisfy things. +/// if not, see if an in-range clone of the CPE is in range, and if so, +/// change the data structures so the user references the clone. Returns: +/// 0 = no existing entry found +/// 1 = entry found, and there were no code insertions or deletions +/// 2 = entry found, and there were code insertions or deletions +int MaxisConstantIslands::findLongFormInRangeCPEntry + (CPUser& U, unsigned UserOffset) +{ + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + + // Check to see if the CPE is already in-range. + if (isCPEntryInRange(UserMI, UserOffset, CPEMI, + U.getLongFormMaxDisp(), U.NegOk, + true)) { + DEBUG(dbgs() << "In range\n"); + UserMI->setDesc(TII->get(U.getLongFormOpcode())); + U.setMaxDisp(U.getLongFormMaxDisp()); + return 2; // instruction is longer length now + } + + // No. Look for previously created clones of the CPE that are in range. + unsigned CPI = CPEMI->getOperand(1).getIndex(); + std::vector &CPEs = CPEntries[CPI]; + for (unsigned i = 0, e = CPEs.size(); i != e; ++i) { + // We already tried this one + if (CPEs[i].CPEMI == CPEMI) + continue; + // Removing CPEs can leave empty entries, skip + if (CPEs[i].CPEMI == nullptr) + continue; + if (isCPEntryInRange(UserMI, UserOffset, CPEs[i].CPEMI, + U.getLongFormMaxDisp(), U.NegOk)) { + DEBUG(dbgs() << "Replacing CPE#" << CPI << " with CPE#" + << CPEs[i].CPI << "\n"); + // Point the CPUser node to the replacement + U.CPEMI = CPEs[i].CPEMI; + // Change the CPI in the instruction operand to refer to the clone. + for (unsigned j = 0, e = UserMI->getNumOperands(); j != e; ++j) + if (UserMI->getOperand(j).isCPI()) { + UserMI->getOperand(j).setIndex(CPEs[i].CPI); + break; + } + // Adjust the refcount of the clone... + CPEs[i].RefCount++; + // ...and the original. If we didn't remove the old entry, none of the + // addresses changed, so we don't need another pass. + return decrementCPEReferenceCount(CPI, CPEMI) ? 2 : 1; + } + } + return 0; +} + +/// getUnconditionalBrDisp - Returns the maximum displacement that can fit in +/// the specific unconditional branch instruction. +static inline unsigned getUnconditionalBrDisp(int Opc) { + switch (Opc) { + case Maxis::Bimm16: + return ((1<<10)-1)*2; + case Maxis::BimmX16: + return ((1<<16)-1)*2; + default: + break; + } + return ((1<<16)-1)*2; +} + +/// findAvailableWater - Look for an existing entry in the WaterList in which +/// we can place the CPE referenced from U so it's within range of U's MI. +/// Returns true if found, false if not. If it returns true, WaterIter +/// is set to the WaterList entry. +/// To ensure that this pass +/// terminates, the CPE location for a particular CPUser is only allowed to +/// move to a lower address, so search backward from the end of the list and +/// prefer the first water that is in range. +bool MaxisConstantIslands::findAvailableWater(CPUser &U, unsigned UserOffset, + water_iterator &WaterIter) { + if (WaterList.empty()) + return false; + + unsigned BestGrowth = ~0u; + for (water_iterator IP = std::prev(WaterList.end()), B = WaterList.begin();; + --IP) { + MachineBasicBlock* WaterBB = *IP; + // Check if water is in range and is either at a lower address than the + // current "high water mark" or a new water block that was created since + // the previous iteration by inserting an unconditional branch. In the + // latter case, we want to allow resetting the high water mark back to + // this new water since we haven't seen it before. Inserting branches + // should be relatively uncommon and when it does happen, we want to be + // sure to take advantage of it for all the CPEs near that block, so that + // we don't insert more branches than necessary. + unsigned Growth; + if (isWaterInRange(UserOffset, WaterBB, U, Growth) && + (WaterBB->getNumber() < U.HighWaterMark->getNumber() || + NewWaterList.count(WaterBB)) && Growth < BestGrowth) { + // This is the least amount of required padding seen so far. + BestGrowth = Growth; + WaterIter = IP; + DEBUG(dbgs() << "Found water after " << printMBBReference(*WaterBB) + << " Growth=" << Growth << '\n'); + + // Keep looking unless it is perfect. + if (BestGrowth == 0) + return true; + } + if (IP == B) + break; + } + return BestGrowth != ~0u; +} + +/// createNewWater - No existing WaterList entry will work for +/// CPUsers[CPUserIndex], so create a place to put the CPE. The end of the +/// block is used if in range, and the conditional branch munged so control +/// flow is correct. Otherwise the block is split to create a hole with an +/// unconditional branch around it. In either case NewMBB is set to a +/// block following which the new island can be inserted (the WaterList +/// is not adjusted). +void MaxisConstantIslands::createNewWater(unsigned CPUserIndex, + unsigned UserOffset, + MachineBasicBlock *&NewMBB) { + CPUser &U = CPUsers[CPUserIndex]; + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + unsigned CPELogAlign = getCPELogAlign(*CPEMI); + MachineBasicBlock *UserMBB = UserMI->getParent(); + const BasicBlockInfo &UserBBI = BBInfo[UserMBB->getNumber()]; + + // If the block does not end in an unconditional branch already, and if the + // end of the block is within range, make new water there. + if (BBHasFallthrough(UserMBB)) { + // Size of branch to insert. + unsigned Delta = 2; + // Compute the offset where the CPE will begin. + unsigned CPEOffset = UserBBI.postOffset(CPELogAlign) + Delta; + + if (isOffsetInRange(UserOffset, CPEOffset, U)) { + DEBUG(dbgs() << "Split at end of " << printMBBReference(*UserMBB) + << format(", expected CPE offset %#x\n", CPEOffset)); + NewMBB = &*++UserMBB->getIterator(); + // Add an unconditional branch from UserMBB to fallthrough block. Record + // it for branch lengthening; this new branch will not get out of range, + // but if the preceding conditional branch is out of range, the targets + // will be exchanged, and the altered branch may be out of range, so the + // machinery has to know about it. + int UncondBr = Maxis::Bimm16; + BuildMI(UserMBB, DebugLoc(), TII->get(UncondBr)).addMBB(NewMBB); + unsigned MaxDisp = getUnconditionalBrDisp(UncondBr); + ImmBranches.push_back(ImmBranch(&UserMBB->back(), + MaxDisp, false, UncondBr)); + BBInfo[UserMBB->getNumber()].Size += Delta; + adjustBBOffsetsAfter(UserMBB); + return; + } + } + + // What a big block. Find a place within the block to split it. + + // Try to split the block so it's fully aligned. Compute the latest split + // point where we can add a 4-byte branch instruction, and then align to + // LogAlign which is the largest possible alignment in the function. + unsigned LogAlign = MF->getAlignment(); + assert(LogAlign >= CPELogAlign && "Over-aligned constant pool entry"); + unsigned BaseInsertOffset = UserOffset + U.getMaxDisp(); + DEBUG(dbgs() << format("Split in middle of big block before %#x", + BaseInsertOffset)); + + // The 4 in the following is for the unconditional branch we'll be inserting + // Alignment of the island is handled + // inside isOffsetInRange. + BaseInsertOffset -= 4; + + DEBUG(dbgs() << format(", adjusted to %#x", BaseInsertOffset) + << " la=" << LogAlign << '\n'); + + // This could point off the end of the block if we've already got constant + // pool entries following this block; only the last one is in the water list. + // Back past any possible branches (allow for a conditional and a maximally + // long unconditional). + if (BaseInsertOffset + 8 >= UserBBI.postOffset()) { + BaseInsertOffset = UserBBI.postOffset() - 8; + DEBUG(dbgs() << format("Move inside block: %#x\n", BaseInsertOffset)); + } + unsigned EndInsertOffset = BaseInsertOffset + 4 + + CPEMI->getOperand(2).getImm(); + MachineBasicBlock::iterator MI = UserMI; + ++MI; + unsigned CPUIndex = CPUserIndex+1; + unsigned NumCPUsers = CPUsers.size(); + //MachineInstr *LastIT = 0; + for (unsigned Offset = UserOffset + TII->getInstSizeInBytes(*UserMI); + Offset < BaseInsertOffset; + Offset += TII->getInstSizeInBytes(*MI), MI = std::next(MI)) { + assert(MI != UserMBB->end() && "Fell off end of block"); + if (CPUIndex < NumCPUsers && CPUsers[CPUIndex].MI == MI) { + CPUser &U = CPUsers[CPUIndex]; + if (!isOffsetInRange(Offset, EndInsertOffset, U)) { + // Shift intertion point by one unit of alignment so it is within reach. + BaseInsertOffset -= 1u << LogAlign; + EndInsertOffset -= 1u << LogAlign; + } + // This is overly conservative, as we don't account for CPEMIs being + // reused within the block, but it doesn't matter much. Also assume CPEs + // are added in order with alignment padding. We may eventually be able + // to pack the aligned CPEs better. + EndInsertOffset += U.CPEMI->getOperand(2).getImm(); + CPUIndex++; + } + } + + NewMBB = splitBlockBeforeInstr(*--MI); +} + +/// handleConstantPoolUser - Analyze the specified user, checking to see if it +/// is out-of-range. If so, pick up the constant pool value and move it some +/// place in-range. Return true if we changed any addresses (thus must run +/// another pass of branch lengthening), false otherwise. +bool MaxisConstantIslands::handleConstantPoolUser(unsigned CPUserIndex) { + CPUser &U = CPUsers[CPUserIndex]; + MachineInstr *UserMI = U.MI; + MachineInstr *CPEMI = U.CPEMI; + unsigned CPI = CPEMI->getOperand(1).getIndex(); + unsigned Size = CPEMI->getOperand(2).getImm(); + // Compute this only once, it's expensive. + unsigned UserOffset = getUserOffset(U); + + // See if the current entry is within range, or there is a clone of it + // in range. + int result = findInRangeCPEntry(U, UserOffset); + if (result==1) return false; + else if (result==2) return true; + + // Look for water where we can place this CPE. + MachineBasicBlock *NewIsland = MF->CreateMachineBasicBlock(); + MachineBasicBlock *NewMBB; + water_iterator IP; + if (findAvailableWater(U, UserOffset, IP)) { + DEBUG(dbgs() << "Found water in range\n"); + MachineBasicBlock *WaterBB = *IP; + + // If the original WaterList entry was "new water" on this iteration, + // propagate that to the new island. This is just keeping NewWaterList + // updated to match the WaterList, which will be updated below. + if (NewWaterList.erase(WaterBB)) + NewWaterList.insert(NewIsland); + + // The new CPE goes before the following block (NewMBB). + NewMBB = &*++WaterBB->getIterator(); + } else { + // No water found. + // we first see if a longer form of the instrucion could have reached + // the constant. in that case we won't bother to split + if (!NoLoadRelaxation) { + result = findLongFormInRangeCPEntry(U, UserOffset); + if (result != 0) return true; + } + DEBUG(dbgs() << "No water found\n"); + createNewWater(CPUserIndex, UserOffset, NewMBB); + + // splitBlockBeforeInstr adds to WaterList, which is important when it is + // called while handling branches so that the water will be seen on the + // next iteration for constant pools, but in this context, we don't want + // it. Check for this so it will be removed from the WaterList. + // Also remove any entry from NewWaterList. + MachineBasicBlock *WaterBB = &*--NewMBB->getIterator(); + IP = llvm::find(WaterList, WaterBB); + if (IP != WaterList.end()) + NewWaterList.erase(WaterBB); + + // We are adding new water. Update NewWaterList. + NewWaterList.insert(NewIsland); + } + + // Remove the original WaterList entry; we want subsequent insertions in + // this vicinity to go after the one we're about to insert. This + // considerably reduces the number of times we have to move the same CPE + // more than once and is also important to ensure the algorithm terminates. + if (IP != WaterList.end()) + WaterList.erase(IP); + + // Okay, we know we can put an island before NewMBB now, do it! + MF->insert(NewMBB->getIterator(), NewIsland); + + // Update internal data structures to account for the newly inserted MBB. + updateForInsertedWaterBlock(NewIsland); + + // Decrement the old entry, and remove it if refcount becomes 0. + decrementCPEReferenceCount(CPI, CPEMI); + + // No existing clone of this CPE is within range. + // We will be generating a new clone. Get a UID for it. + unsigned ID = createPICLabelUId(); + + // Now that we have an island to add the CPE to, clone the original CPE and + // add it to the island. + U.HighWaterMark = NewIsland; + U.CPEMI = BuildMI(NewIsland, DebugLoc(), TII->get(Maxis::CONSTPOOL_ENTRY)) + .addImm(ID).addConstantPoolIndex(CPI).addImm(Size); + CPEntries[CPI].push_back(CPEntry(U.CPEMI, ID, 1)); + ++NumCPEs; + + // Mark the basic block as aligned as required by the const-pool entry. + NewIsland->setAlignment(getCPELogAlign(*U.CPEMI)); + + // Increase the size of the island block to account for the new entry. + BBInfo[NewIsland->getNumber()].Size += Size; + adjustBBOffsetsAfter(&*--NewIsland->getIterator()); + + // Finally, change the CPI in the instruction operand to be ID. + for (unsigned i = 0, e = UserMI->getNumOperands(); i != e; ++i) + if (UserMI->getOperand(i).isCPI()) { + UserMI->getOperand(i).setIndex(ID); + break; + } + + DEBUG(dbgs() << " Moved CPE to #" << ID << " CPI=" << CPI + << format(" offset=%#x\n", BBInfo[NewIsland->getNumber()].Offset)); + + return true; +} + +/// removeDeadCPEMI - Remove a dead constant pool entry instruction. Update +/// sizes and offsets of impacted basic blocks. +void MaxisConstantIslands::removeDeadCPEMI(MachineInstr *CPEMI) { + MachineBasicBlock *CPEBB = CPEMI->getParent(); + unsigned Size = CPEMI->getOperand(2).getImm(); + CPEMI->eraseFromParent(); + BBInfo[CPEBB->getNumber()].Size -= Size; + // All succeeding offsets have the current size value added in, fix this. + if (CPEBB->empty()) { + BBInfo[CPEBB->getNumber()].Size = 0; + + // This block no longer needs to be aligned. + CPEBB->setAlignment(0); + } else + // Entries are sorted by descending alignment, so realign from the front. + CPEBB->setAlignment(getCPELogAlign(*CPEBB->begin())); + + adjustBBOffsetsAfter(CPEBB); + // An island has only one predecessor BB and one successor BB. Check if + // this BB's predecessor jumps directly to this BB's successor. This + // shouldn't happen currently. + assert(!BBIsJumpedOver(CPEBB) && "How did this happen?"); + // FIXME: remove the empty blocks after all the work is done? +} + +/// removeUnusedCPEntries - Remove constant pool entries whose refcounts +/// are zero. +bool MaxisConstantIslands::removeUnusedCPEntries() { + unsigned MadeChange = false; + for (unsigned i = 0, e = CPEntries.size(); i != e; ++i) { + std::vector &CPEs = CPEntries[i]; + for (unsigned j = 0, ee = CPEs.size(); j != ee; ++j) { + if (CPEs[j].RefCount == 0 && CPEs[j].CPEMI) { + removeDeadCPEMI(CPEs[j].CPEMI); + CPEs[j].CPEMI = nullptr; + MadeChange = true; + } + } + } + return MadeChange; +} + +/// isBBInRange - Returns true if the distance between specific MI and +/// specific BB can fit in MI's displacement field. +bool MaxisConstantIslands::isBBInRange + (MachineInstr *MI,MachineBasicBlock *DestBB, unsigned MaxDisp) { + unsigned PCAdj = 4; + unsigned BrOffset = getOffsetOf(MI) + PCAdj; + unsigned DestOffset = BBInfo[DestBB->getNumber()].Offset; + + DEBUG(dbgs() << "Branch of destination " << printMBBReference(*DestBB) + << " from " << printMBBReference(*MI->getParent()) + << " max delta=" << MaxDisp << " from " << getOffsetOf(MI) + << " to " << DestOffset << " offset " + << int(DestOffset - BrOffset) << "\t" << *MI); + + if (BrOffset <= DestOffset) { + // Branch before the Dest. + if (DestOffset-BrOffset <= MaxDisp) + return true; + } else { + if (BrOffset-DestOffset <= MaxDisp) + return true; + } + return false; +} + +/// fixupImmediateBr - Fix up an immediate branch whose destination is too far +/// away to fit in its displacement field. +bool MaxisConstantIslands::fixupImmediateBr(ImmBranch &Br) { + MachineInstr *MI = Br.MI; + unsigned TargetOperand = branchTargetOperand(MI); + MachineBasicBlock *DestBB = MI->getOperand(TargetOperand).getMBB(); + + // Check to see if the DestBB is already in-range. + if (isBBInRange(MI, DestBB, Br.MaxDisp)) + return false; + + if (!Br.isCond) + return fixupUnconditionalBr(Br); + return fixupConditionalBr(Br); +} + +/// fixupUnconditionalBr - Fix up an unconditional branch whose destination is +/// too far away to fit in its displacement field. If the LR register has been +/// spilled in the epilogue, then we can use BL to implement a far jump. +/// Otherwise, add an intermediate branch instruction to a branch. +bool +MaxisConstantIslands::fixupUnconditionalBr(ImmBranch &Br) { + MachineInstr *MI = Br.MI; + MachineBasicBlock *MBB = MI->getParent(); + MachineBasicBlock *DestBB = MI->getOperand(0).getMBB(); + // Use BL to implement far jump. + unsigned BimmX16MaxDisp = ((1 << 16)-1) * 2; + if (isBBInRange(MI, DestBB, BimmX16MaxDisp)) { + Br.MaxDisp = BimmX16MaxDisp; + MI->setDesc(TII->get(Maxis::BimmX16)); + } + else { + // need to give the math a more careful look here + // this is really a segment address and not + // a PC relative address. FIXME. But I think that + // just reducing the bits by 1 as I've done is correct. + // The basic block we are branching too much be longword aligned. + // we know that RA is saved because we always save it right now. + // this requirement will be relaxed later but we also have an alternate + // way to implement this that I will implement that does not need jal. + // We should have a way to back out this alignment restriction if we "can" later. + // but it is not harmful. + // + DestBB->setAlignment(2); + Br.MaxDisp = ((1<<24)-1) * 2; + MI->setDesc(TII->get(Maxis::JalB16)); + } + BBInfo[MBB->getNumber()].Size += 2; + adjustBBOffsetsAfter(MBB); + HasFarJump = true; + ++NumUBrFixed; + + DEBUG(dbgs() << " Changed B to long jump " << *MI); + + return true; +} + +/// fixupConditionalBr - Fix up a conditional branch whose destination is too +/// far away to fit in its displacement field. It is converted to an inverse +/// conditional branch + an unconditional branch to the destination. +bool +MaxisConstantIslands::fixupConditionalBr(ImmBranch &Br) { + MachineInstr *MI = Br.MI; + unsigned TargetOperand = branchTargetOperand(MI); + MachineBasicBlock *DestBB = MI->getOperand(TargetOperand).getMBB(); + unsigned Opcode = MI->getOpcode(); + unsigned LongFormOpcode = longformBranchOpcode(Opcode); + unsigned LongFormMaxOff = branchMaxOffsets(LongFormOpcode); + + // Check to see if the DestBB is already in-range. + if (isBBInRange(MI, DestBB, LongFormMaxOff)) { + Br.MaxDisp = LongFormMaxOff; + MI->setDesc(TII->get(LongFormOpcode)); + return true; + } + + // Add an unconditional branch to the destination and invert the branch + // condition to jump over it: + // bteqz L1 + // => + // bnez L2 + // b L1 + // L2: + + // If the branch is at the end of its MBB and that has a fall-through block, + // direct the updated conditional branch to the fall-through block. Otherwise, + // split the MBB before the next instruction. + MachineBasicBlock *MBB = MI->getParent(); + MachineInstr *BMI = &MBB->back(); + bool NeedSplit = (BMI != MI) || !BBHasFallthrough(MBB); + unsigned OppositeBranchOpcode = TII->getOppositeBranchOpc(Opcode); + + ++NumCBrFixed; + if (BMI != MI) { + if (std::next(MachineBasicBlock::iterator(MI)) == std::prev(MBB->end()) && + BMI->isUnconditionalBranch()) { + // Last MI in the BB is an unconditional branch. Can we simply invert the + // condition and swap destinations: + // beqz L1 + // b L2 + // => + // bnez L2 + // b L1 + unsigned BMITargetOperand = branchTargetOperand(BMI); + MachineBasicBlock *NewDest = + BMI->getOperand(BMITargetOperand).getMBB(); + if (isBBInRange(MI, NewDest, Br.MaxDisp)) { + DEBUG(dbgs() << " Invert Bcc condition and swap its destination with " + << *BMI); + MI->setDesc(TII->get(OppositeBranchOpcode)); + BMI->getOperand(BMITargetOperand).setMBB(DestBB); + MI->getOperand(TargetOperand).setMBB(NewDest); + return true; + } + } + } + + if (NeedSplit) { + splitBlockBeforeInstr(*MI); + // No need for the branch to the next block. We're adding an unconditional + // branch to the destination. + int delta = TII->getInstSizeInBytes(MBB->back()); + BBInfo[MBB->getNumber()].Size -= delta; + MBB->back().eraseFromParent(); + // BBInfo[SplitBB].Offset is wrong temporarily, fixed below + } + MachineBasicBlock *NextBB = &*++MBB->getIterator(); + + DEBUG(dbgs() << " Insert B to " << printMBBReference(*DestBB) + << " also invert condition and change dest. to " + << printMBBReference(*NextBB) << "\n"); + + // Insert a new conditional branch and a new unconditional branch. + // Also update the ImmBranch as well as adding a new entry for the new branch. + if (MI->getNumExplicitOperands() == 2) { + BuildMI(MBB, DebugLoc(), TII->get(OppositeBranchOpcode)) + .addReg(MI->getOperand(0).getReg()) + .addMBB(NextBB); + } else { + BuildMI(MBB, DebugLoc(), TII->get(OppositeBranchOpcode)) + .addMBB(NextBB); + } + Br.MI = &MBB->back(); + BBInfo[MBB->getNumber()].Size += TII->getInstSizeInBytes(MBB->back()); + BuildMI(MBB, DebugLoc(), TII->get(Br.UncondBr)).addMBB(DestBB); + BBInfo[MBB->getNumber()].Size += TII->getInstSizeInBytes(MBB->back()); + unsigned MaxDisp = getUnconditionalBrDisp(Br.UncondBr); + ImmBranches.push_back(ImmBranch(&MBB->back(), MaxDisp, false, Br.UncondBr)); + + // Remove the old conditional branch. It may or may not still be in MBB. + BBInfo[MI->getParent()->getNumber()].Size -= TII->getInstSizeInBytes(*MI); + MI->eraseFromParent(); + adjustBBOffsetsAfter(MBB); + return true; +} + +void MaxisConstantIslands::prescanForConstants() { + unsigned J = 0; + (void)J; + for (MachineFunction::iterator B = + MF->begin(), E = MF->end(); B != E; ++B) { + for (MachineBasicBlock::instr_iterator I = + B->instr_begin(), EB = B->instr_end(); I != EB; ++I) { + switch(I->getDesc().getOpcode()) { + case Maxis::LwConstant32: { + PrescannedForConstants = true; + DEBUG(dbgs() << "constant island constant " << *I << "\n"); + J = I->getNumOperands(); + DEBUG(dbgs() << "num operands " << J << "\n"); + MachineOperand& Literal = I->getOperand(1); + if (Literal.isImm()) { + int64_t V = Literal.getImm(); + DEBUG(dbgs() << "literal " << V << "\n"); + Type *Int32Ty = + Type::getInt32Ty(MF->getFunction().getContext()); + const Constant *C = ConstantInt::get(Int32Ty, V); + unsigned index = MCP->getConstantPoolIndex(C, 4); + I->getOperand(2).ChangeToImmediate(index); + DEBUG(dbgs() << "constant island constant " << *I << "\n"); + I->setDesc(TII->get(Maxis::LwRxPcTcp16)); + I->RemoveOperand(1); + I->RemoveOperand(1); + I->addOperand(MachineOperand::CreateCPI(index, 0)); + I->addOperand(MachineOperand::CreateImm(4)); + } + break; + } + default: + break; + } + } + } +} + +/// Returns a pass that converts branches to long branches. +FunctionPass *llvm::createMaxisConstantIslandPass() { + return new MaxisConstantIslands(); +} diff --git a/lib/Target/Maxis/MaxisDSPInstrFormats.td b/lib/Target/Maxis/MaxisDSPInstrFormats.td new file mode 100644 index 00000000..89d8422b --- /dev/null +++ b/lib/Target/Maxis/MaxisDSPInstrFormats.td @@ -0,0 +1,369 @@ +//===- MaxisDSPInstrFormats.td - Maxis Instruction Formats ---*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +class DspMMRel; + +def Dsp2MicroMaxis : InstrMapping { + let FilterClass = "DspMMRel"; + // Instructions with the same BaseOpcode and isNVStore values form a row. + let RowFields = ["BaseOpcode"]; + // Instructions with the same predicate sense form a column. + let ColFields = ["Arch"]; + // The key column is the unpredicated instructions. + let KeyCol = ["dsp"]; + // Value columns are PredSense=true and PredSense=false + let ValueCols = [["dsp"], ["mmdsp"]]; +} + +def HasDSP : Predicate<"Subtarget->hasDSP()">, + AssemblerPredicate<"FeatureDSP">; +def HasDSPR2 : Predicate<"Subtarget->hasDSPR2()">, + AssemblerPredicate<"FeatureDSPR2">; +def HasDSPR3 : Predicate<"Subtarget->hasDSPR3()">, + AssemblerPredicate<"FeatureDSPR3">; + +class ISA_DSPR2 { + list InsnPredicates = [HasDSPR2]; +} + +class ISA_DSPR3 { + list InsnPredicates = [HasDSPR3]; +} + +// Fields. +class Field6 val> { + bits<6> V = val; +} + +def SPECIAL3_OPCODE : Field6<0b011111>; +def REGIMM_OPCODE : Field6<0b000001>; + +class DSPInst + : MaxisInst<(outs), (ins), "", [], NoItinerary, FrmOther>, PredicateControl { + let InsnPredicates = [HasDSP]; + string BaseOpcode = opstr; + string Arch = "dsp"; +} + +class PseudoDSP pattern, + InstrItinClass itin = IIPseudo> + : MaxisPseudo, PredicateControl { + let InsnPredicates = [HasDSP]; +} + +class DSPInstAlias + : InstAlias, PredicateControl { + let InsnPredicates = [HasDSP]; +} + +// ADDU.QB sub-class format. +class ADDU_QB_FMT op> : DSPInst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b010000; +} + +class RADDU_W_QB_FMT op> : DSPInst { + bits<5> rd; + bits<5> rs; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = 0; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b010000; +} + +// CMPU.EQ.QB sub-class format. +class CMP_EQ_QB_R2_FMT op> : DSPInst { + bits<5> rs; + bits<5> rt; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = 0; + let Inst{10-6} = op; + let Inst{5-0} = 0b010001; +} + +class CMP_EQ_QB_R3_FMT op> : DSPInst { + bits<5> rs; + bits<5> rt; + bits<5> rd; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b010001; +} + +class PRECR_SRA_PH_W_FMT op> : DSPInst { + bits<5> rs; + bits<5> rt; + bits<5> sa; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = sa; + let Inst{10-6} = op; + let Inst{5-0} = 0b010001; +} + +// ABSQ_S.PH sub-class format. +class ABSQ_S_PH_R2_FMT op> : DSPInst { + bits<5> rd; + bits<5> rt; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = 0; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b010010; +} + + +class REPL_FMT op> : DSPInst { + bits<5> rd; + bits<10> imm; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-16} = imm; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b010010; +} + +// SHLL.QB sub-class format. +class SHLL_QB_FMT op> : DSPInst { + bits<5> rd; + bits<5> rt; + bits<5> rs_sa; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs_sa; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b010011; +} + +// LX sub-class format. +class LX_FMT op> : DSPInst { + bits<5> rd; + bits<5> base; + bits<5> index; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = base; + let Inst{20-16} = index; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b001010; +} + +// ADDUH.QB sub-class format. +class ADDUH_QB_FMT op> : DSPInst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b011000; +} + +// APPEND sub-class format. +class APPEND_FMT op> : DSPInst { + bits<5> rt; + bits<5> rs; + bits<5> sa; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = sa; + let Inst{10-6} = op; + let Inst{5-0} = 0b110001; +} + +// DPA.W.PH sub-class format. +class DPA_W_PH_FMT op> : DSPInst { + bits<2> ac; + bits<5> rs; + bits<5> rt; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-13} = 0; + let Inst{12-11} = ac; + let Inst{10-6} = op; + let Inst{5-0} = 0b110000; +} + +// MULT sub-class format. +class MULT_FMT opcode, bits<6> funct> : DSPInst { + bits<2> ac; + bits<5> rs; + bits<5> rt; + + let Opcode = opcode; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-13} = 0; + let Inst{12-11} = ac; + let Inst{10-6} = 0; + let Inst{5-0} = funct; +} + +// MFHI sub-class format. +class MFHI_FMT funct> : DSPInst { + bits<5> rd; + bits<2> ac; + + let Inst{31-26} = 0; + let Inst{25-23} = 0; + let Inst{22-21} = ac; + let Inst{20-16} = 0; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = funct; +} + +// MTHI sub-class format. +class MTHI_FMT funct> : DSPInst { + bits<5> rs; + bits<2> ac; + + let Inst{31-26} = 0; + let Inst{25-21} = rs; + let Inst{20-13} = 0; + let Inst{12-11} = ac; + let Inst{10-6} = 0; + let Inst{5-0} = funct; +} + +// EXTR.W sub-class format (type 1). +class EXTR_W_TY1_FMT op> : DSPInst { + bits<5> rt; + bits<2> ac; + bits<5> shift_rs; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = shift_rs; + let Inst{20-16} = rt; + let Inst{15-13} = 0; + let Inst{12-11} = ac; + let Inst{10-6} = op; + let Inst{5-0} = 0b111000; +} + +// SHILO sub-class format. +class SHILO_R1_FMT op> : DSPInst { + bits<2> ac; + bits<6> shift; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-20} = shift; + let Inst{19-13} = 0; + let Inst{12-11} = ac; + let Inst{10-6} = op; + let Inst{5-0} = 0b111000; +} + +class SHILO_R2_FMT op> : DSPInst { + bits<2> ac; + bits<5> rs; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-13} = 0; + let Inst{12-11} = ac; + let Inst{10-6} = op; + let Inst{5-0} = 0b111000; +} + +class RDDSP_FMT op> : DSPInst { + bits<5> rd; + bits<10> mask; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-16} = mask; + let Inst{15-11} = rd; + let Inst{10-6} = op; + let Inst{5-0} = 0b111000; +} + +class WRDSP_FMT op> : DSPInst { + bits<5> rs; + bits<10> mask; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-11} = mask; + let Inst{10-6} = op; + let Inst{5-0} = 0b111000; +} + +class BPOSGE32_FMT op> : DSPInst { + bits<16> offset; + + let Opcode = REGIMM_OPCODE.V; + + let Inst{25-21} = 0; + let Inst{20-16} = op; + let Inst{15-0} = offset; +} + +// INSV sub-class format. +class INSV_FMT op> : DSPInst { + bits<5> rt; + bits<5> rs; + + let Opcode = SPECIAL3_OPCODE.V; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-6} = 0; + let Inst{5-0} = op; +} diff --git a/lib/Target/Maxis/MaxisDSPInstrInfo.td b/lib/Target/Maxis/MaxisDSPInstrInfo.td new file mode 100644 index 00000000..87b0eb71 --- /dev/null +++ b/lib/Target/Maxis/MaxisDSPInstrInfo.td @@ -0,0 +1,1467 @@ +//===- MaxisDSPInstrInfo.td - DSP ASE instructions -*- tablegen ------------*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis DSP ASE instructions. +// +//===----------------------------------------------------------------------===// + +// ImmLeaf +def immZExt1 : ImmLeaf(Imm);}]>; +def immZExt2 : ImmLeaf(Imm);}]>; +def immZExt3 : ImmLeaf(Imm);}]>; +def immZExt4 : ImmLeaf(Imm);}]>; +def immZExt8 : ImmLeaf(Imm);}]>; +def immZExt10 : ImmLeaf(Imm);}]>; +def immSExt6 : ImmLeaf(Imm);}]>; +def immSExt10 : ImmLeaf(Imm);}]>; + +// Maxis-specific dsp nodes +def SDT_MaxisExtr : SDTypeProfile<1, 2, [SDTCisVT<0, i32>, SDTCisSameAs<0, 1>, + SDTCisVT<2, untyped>]>; +def SDT_MaxisShilo : SDTypeProfile<1, 2, [SDTCisVT<0, untyped>, + SDTCisSameAs<0, 2>, SDTCisVT<1, i32>]>; +def SDT_MaxisDPA : SDTypeProfile<1, 3, [SDTCisVT<0, untyped>, SDTCisSameAs<0, 3>, + SDTCisVT<1, i32>, SDTCisSameAs<1, 2>]>; +def SDT_MaxisSHIFT_DSP : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisSameAs<0, 1>, + SDTCisVT<2, i32>]>; + +class MaxisDSPBase : + SDNode; + +class MaxisDSPSideEffectBase : + SDNode; + +def MaxisEXTP : MaxisDSPSideEffectBase<"EXTP", SDT_MaxisExtr>; +def MaxisEXTPDP : MaxisDSPSideEffectBase<"EXTPDP", SDT_MaxisExtr>; +def MaxisEXTR_S_H : MaxisDSPSideEffectBase<"EXTR_S_H", SDT_MaxisExtr>; +def MaxisEXTR_W : MaxisDSPSideEffectBase<"EXTR_W", SDT_MaxisExtr>; +def MaxisEXTR_R_W : MaxisDSPSideEffectBase<"EXTR_R_W", SDT_MaxisExtr>; +def MaxisEXTR_RS_W : MaxisDSPSideEffectBase<"EXTR_RS_W", SDT_MaxisExtr>; + +def MaxisSHILO : MaxisDSPBase<"SHILO", SDT_MaxisShilo>; +def MaxisMTHLIP : MaxisDSPSideEffectBase<"MTHLIP", SDT_MaxisShilo>; + +def MaxisMULSAQ_S_W_PH : MaxisDSPSideEffectBase<"MULSAQ_S_W_PH", SDT_MaxisDPA>; +def MaxisMAQ_S_W_PHL : MaxisDSPSideEffectBase<"MAQ_S_W_PHL", SDT_MaxisDPA>; +def MaxisMAQ_S_W_PHR : MaxisDSPSideEffectBase<"MAQ_S_W_PHR", SDT_MaxisDPA>; +def MaxisMAQ_SA_W_PHL : MaxisDSPSideEffectBase<"MAQ_SA_W_PHL", SDT_MaxisDPA>; +def MaxisMAQ_SA_W_PHR : MaxisDSPSideEffectBase<"MAQ_SA_W_PHR", SDT_MaxisDPA>; + +def MaxisDPAU_H_QBL : MaxisDSPBase<"DPAU_H_QBL", SDT_MaxisDPA>; +def MaxisDPAU_H_QBR : MaxisDSPBase<"DPAU_H_QBR", SDT_MaxisDPA>; +def MaxisDPSU_H_QBL : MaxisDSPBase<"DPSU_H_QBL", SDT_MaxisDPA>; +def MaxisDPSU_H_QBR : MaxisDSPBase<"DPSU_H_QBR", SDT_MaxisDPA>; +def MaxisDPAQ_S_W_PH : MaxisDSPSideEffectBase<"DPAQ_S_W_PH", SDT_MaxisDPA>; +def MaxisDPSQ_S_W_PH : MaxisDSPSideEffectBase<"DPSQ_S_W_PH", SDT_MaxisDPA>; +def MaxisDPAQ_SA_L_W : MaxisDSPSideEffectBase<"DPAQ_SA_L_W", SDT_MaxisDPA>; +def MaxisDPSQ_SA_L_W : MaxisDSPSideEffectBase<"DPSQ_SA_L_W", SDT_MaxisDPA>; + +def MaxisDPA_W_PH : MaxisDSPBase<"DPA_W_PH", SDT_MaxisDPA>; +def MaxisDPS_W_PH : MaxisDSPBase<"DPS_W_PH", SDT_MaxisDPA>; +def MaxisDPAQX_S_W_PH : MaxisDSPSideEffectBase<"DPAQX_S_W_PH", SDT_MaxisDPA>; +def MaxisDPAQX_SA_W_PH : MaxisDSPSideEffectBase<"DPAQX_SA_W_PH", SDT_MaxisDPA>; +def MaxisDPAX_W_PH : MaxisDSPBase<"DPAX_W_PH", SDT_MaxisDPA>; +def MaxisDPSX_W_PH : MaxisDSPBase<"DPSX_W_PH", SDT_MaxisDPA>; +def MaxisDPSQX_S_W_PH : MaxisDSPSideEffectBase<"DPSQX_S_W_PH", SDT_MaxisDPA>; +def MaxisDPSQX_SA_W_PH : MaxisDSPSideEffectBase<"DPSQX_SA_W_PH", SDT_MaxisDPA>; +def MaxisMULSA_W_PH : MaxisDSPBase<"MULSA_W_PH", SDT_MaxisDPA>; + +def MaxisMULT : MaxisDSPBase<"MULT", SDT_MaxisDPA>; +def MaxisMULTU : MaxisDSPBase<"MULTU", SDT_MaxisDPA>; +def MaxisMADD_DSP : MaxisDSPBase<"MADD_DSP", SDT_MaxisDPA>; +def MaxisMADDU_DSP : MaxisDSPBase<"MADDU_DSP", SDT_MaxisDPA>; +def MaxisMSUB_DSP : MaxisDSPBase<"MSUB_DSP", SDT_MaxisDPA>; +def MaxisMSUBU_DSP : MaxisDSPBase<"MSUBU_DSP", SDT_MaxisDPA>; +def MaxisSHLL_DSP : MaxisDSPBase<"SHLL_DSP", SDT_MaxisSHIFT_DSP>; +def MaxisSHRA_DSP : MaxisDSPBase<"SHRA_DSP", SDT_MaxisSHIFT_DSP>; +def MaxisSHRL_DSP : MaxisDSPBase<"SHRL_DSP", SDT_MaxisSHIFT_DSP>; +def MaxisSETCC_DSP : MaxisDSPBase<"SETCC_DSP", SDTSetCC>; +def MaxisSELECT_CC_DSP : MaxisDSPBase<"SELECT_CC_DSP", SDTSelectCC>; + +// Flags. +class Uses Regs> { + list Uses = Regs; +} + +class Defs Regs> { + list Defs = Regs; +} + +// Instruction encoding. +class ADDU_QB_ENC : ADDU_QB_FMT<0b00000>; +class ADDU_S_QB_ENC : ADDU_QB_FMT<0b00100>; +class SUBU_QB_ENC : ADDU_QB_FMT<0b00001>; +class SUBU_S_QB_ENC : ADDU_QB_FMT<0b00101>; +class ADDQ_PH_ENC : ADDU_QB_FMT<0b01010>; +class ADDQ_S_PH_ENC : ADDU_QB_FMT<0b01110>; +class SUBQ_PH_ENC : ADDU_QB_FMT<0b01011>; +class SUBQ_S_PH_ENC : ADDU_QB_FMT<0b01111>; +class ADDQ_S_W_ENC : ADDU_QB_FMT<0b10110>; +class SUBQ_S_W_ENC : ADDU_QB_FMT<0b10111>; +class ADDSC_ENC : ADDU_QB_FMT<0b10000>; +class ADDWC_ENC : ADDU_QB_FMT<0b10001>; +class MODSUB_ENC : ADDU_QB_FMT<0b10010>; +class RADDU_W_QB_ENC : RADDU_W_QB_FMT<0b10100>; +class ABSQ_S_PH_ENC : ABSQ_S_PH_R2_FMT<0b01001>; +class ABSQ_S_W_ENC : ABSQ_S_PH_R2_FMT<0b10001>; +class PRECRQ_QB_PH_ENC : CMP_EQ_QB_R3_FMT<0b01100>; +class PRECRQ_PH_W_ENC : CMP_EQ_QB_R3_FMT<0b10100>; +class PRECRQ_RS_PH_W_ENC : CMP_EQ_QB_R3_FMT<0b10101>; +class PRECRQU_S_QB_PH_ENC : CMP_EQ_QB_R3_FMT<0b01111>; +class PRECEQ_W_PHL_ENC : ABSQ_S_PH_R2_FMT<0b01100>; +class PRECEQ_W_PHR_ENC : ABSQ_S_PH_R2_FMT<0b01101>; +class PRECEQU_PH_QBL_ENC : ABSQ_S_PH_R2_FMT<0b00100>; +class PRECEQU_PH_QBR_ENC : ABSQ_S_PH_R2_FMT<0b00101>; +class PRECEQU_PH_QBLA_ENC : ABSQ_S_PH_R2_FMT<0b00110>; +class PRECEQU_PH_QBRA_ENC : ABSQ_S_PH_R2_FMT<0b00111>; +class PRECEU_PH_QBL_ENC : ABSQ_S_PH_R2_FMT<0b11100>; +class PRECEU_PH_QBR_ENC : ABSQ_S_PH_R2_FMT<0b11101>; +class PRECEU_PH_QBLA_ENC : ABSQ_S_PH_R2_FMT<0b11110>; +class PRECEU_PH_QBRA_ENC : ABSQ_S_PH_R2_FMT<0b11111>; +class SHLL_QB_ENC : SHLL_QB_FMT<0b00000>; +class SHLLV_QB_ENC : SHLL_QB_FMT<0b00010>; +class SHRL_QB_ENC : SHLL_QB_FMT<0b00001>; +class SHRLV_QB_ENC : SHLL_QB_FMT<0b00011>; +class SHLL_PH_ENC : SHLL_QB_FMT<0b01000>; +class SHLLV_PH_ENC : SHLL_QB_FMT<0b01010>; +class SHLL_S_PH_ENC : SHLL_QB_FMT<0b01100>; +class SHLLV_S_PH_ENC : SHLL_QB_FMT<0b01110>; +class SHRA_PH_ENC : SHLL_QB_FMT<0b01001>; +class SHRAV_PH_ENC : SHLL_QB_FMT<0b01011>; +class SHRA_R_PH_ENC : SHLL_QB_FMT<0b01101>; +class SHRAV_R_PH_ENC : SHLL_QB_FMT<0b01111>; +class SHLL_S_W_ENC : SHLL_QB_FMT<0b10100>; +class SHLLV_S_W_ENC : SHLL_QB_FMT<0b10110>; +class SHRA_R_W_ENC : SHLL_QB_FMT<0b10101>; +class SHRAV_R_W_ENC : SHLL_QB_FMT<0b10111>; +class MULEU_S_PH_QBL_ENC : ADDU_QB_FMT<0b00110>; +class MULEU_S_PH_QBR_ENC : ADDU_QB_FMT<0b00111>; +class MULEQ_S_W_PHL_ENC : ADDU_QB_FMT<0b11100>; +class MULEQ_S_W_PHR_ENC : ADDU_QB_FMT<0b11101>; +class MULQ_RS_PH_ENC : ADDU_QB_FMT<0b11111>; +class MULSAQ_S_W_PH_ENC : DPA_W_PH_FMT<0b00110>; +class MAQ_S_W_PHL_ENC : DPA_W_PH_FMT<0b10100>; +class MAQ_S_W_PHR_ENC : DPA_W_PH_FMT<0b10110>; +class MAQ_SA_W_PHL_ENC : DPA_W_PH_FMT<0b10000>; +class MAQ_SA_W_PHR_ENC : DPA_W_PH_FMT<0b10010>; +class MFHI_ENC : MFHI_FMT<0b010000>; +class MFLO_ENC : MFHI_FMT<0b010010>; +class MTHI_ENC : MTHI_FMT<0b010001>; +class MTLO_ENC : MTHI_FMT<0b010011>; +class DPAU_H_QBL_ENC : DPA_W_PH_FMT<0b00011>; +class DPAU_H_QBR_ENC : DPA_W_PH_FMT<0b00111>; +class DPSU_H_QBL_ENC : DPA_W_PH_FMT<0b01011>; +class DPSU_H_QBR_ENC : DPA_W_PH_FMT<0b01111>; +class DPAQ_S_W_PH_ENC : DPA_W_PH_FMT<0b00100>; +class DPSQ_S_W_PH_ENC : DPA_W_PH_FMT<0b00101>; +class DPAQ_SA_L_W_ENC : DPA_W_PH_FMT<0b01100>; +class DPSQ_SA_L_W_ENC : DPA_W_PH_FMT<0b01101>; +class MULT_DSP_ENC : MULT_FMT<0b000000, 0b011000>; +class MULTU_DSP_ENC : MULT_FMT<0b000000, 0b011001>; +class MADD_DSP_ENC : MULT_FMT<0b011100, 0b000000>; +class MADDU_DSP_ENC : MULT_FMT<0b011100, 0b000001>; +class MSUB_DSP_ENC : MULT_FMT<0b011100, 0b000100>; +class MSUBU_DSP_ENC : MULT_FMT<0b011100, 0b000101>; +class CMPU_EQ_QB_ENC : CMP_EQ_QB_R2_FMT<0b00000>; +class CMPU_LT_QB_ENC : CMP_EQ_QB_R2_FMT<0b00001>; +class CMPU_LE_QB_ENC : CMP_EQ_QB_R2_FMT<0b00010>; +class CMPGU_EQ_QB_ENC : CMP_EQ_QB_R3_FMT<0b00100>; +class CMPGU_LT_QB_ENC : CMP_EQ_QB_R3_FMT<0b00101>; +class CMPGU_LE_QB_ENC : CMP_EQ_QB_R3_FMT<0b00110>; +class CMP_EQ_PH_ENC : CMP_EQ_QB_R2_FMT<0b01000>; +class CMP_LT_PH_ENC : CMP_EQ_QB_R2_FMT<0b01001>; +class CMP_LE_PH_ENC : CMP_EQ_QB_R2_FMT<0b01010>; +class BITREV_ENC : ABSQ_S_PH_R2_FMT<0b11011>; +class PACKRL_PH_ENC : CMP_EQ_QB_R3_FMT<0b01110>; +class REPL_QB_ENC : REPL_FMT<0b00010>; +class REPL_PH_ENC : REPL_FMT<0b01010>; +class REPLV_QB_ENC : ABSQ_S_PH_R2_FMT<0b00011>; +class REPLV_PH_ENC : ABSQ_S_PH_R2_FMT<0b01011>; +class PICK_QB_ENC : CMP_EQ_QB_R3_FMT<0b00011>; +class PICK_PH_ENC : CMP_EQ_QB_R3_FMT<0b01011>; +class LWX_ENC : LX_FMT<0b00000>; +class LHX_ENC : LX_FMT<0b00100>; +class LBUX_ENC : LX_FMT<0b00110>; +class BPOSGE32_ENC : BPOSGE32_FMT<0b11100>; +class INSV_ENC : INSV_FMT<0b001100>; + +class EXTP_ENC : EXTR_W_TY1_FMT<0b00010>; +class EXTPV_ENC : EXTR_W_TY1_FMT<0b00011>; +class EXTPDP_ENC : EXTR_W_TY1_FMT<0b01010>; +class EXTPDPV_ENC : EXTR_W_TY1_FMT<0b01011>; +class EXTR_W_ENC : EXTR_W_TY1_FMT<0b00000>; +class EXTRV_W_ENC : EXTR_W_TY1_FMT<0b00001>; +class EXTR_R_W_ENC : EXTR_W_TY1_FMT<0b00100>; +class EXTRV_R_W_ENC : EXTR_W_TY1_FMT<0b00101>; +class EXTR_RS_W_ENC : EXTR_W_TY1_FMT<0b00110>; +class EXTRV_RS_W_ENC : EXTR_W_TY1_FMT<0b00111>; +class EXTR_S_H_ENC : EXTR_W_TY1_FMT<0b01110>; +class EXTRV_S_H_ENC : EXTR_W_TY1_FMT<0b01111>; +class SHILO_ENC : SHILO_R1_FMT<0b11010>; +class SHILOV_ENC : SHILO_R2_FMT<0b11011>; +class MTHLIP_ENC : SHILO_R2_FMT<0b11111>; + +class RDDSP_ENC : RDDSP_FMT<0b10010>; +class WRDSP_ENC : WRDSP_FMT<0b10011>; +class ADDU_PH_ENC : ADDU_QB_FMT<0b01000>; +class ADDU_S_PH_ENC : ADDU_QB_FMT<0b01100>; +class SUBU_PH_ENC : ADDU_QB_FMT<0b01001>; +class SUBU_S_PH_ENC : ADDU_QB_FMT<0b01101>; +class CMPGDU_EQ_QB_ENC : CMP_EQ_QB_R3_FMT<0b11000>; +class CMPGDU_LT_QB_ENC : CMP_EQ_QB_R3_FMT<0b11001>; +class CMPGDU_LE_QB_ENC : CMP_EQ_QB_R3_FMT<0b11010>; +class ABSQ_S_QB_ENC : ABSQ_S_PH_R2_FMT<0b00001>; +class ADDUH_QB_ENC : ADDUH_QB_FMT<0b00000>; +class ADDUH_R_QB_ENC : ADDUH_QB_FMT<0b00010>; +class SUBUH_QB_ENC : ADDUH_QB_FMT<0b00001>; +class SUBUH_R_QB_ENC : ADDUH_QB_FMT<0b00011>; +class ADDQH_PH_ENC : ADDUH_QB_FMT<0b01000>; +class ADDQH_R_PH_ENC : ADDUH_QB_FMT<0b01010>; +class SUBQH_PH_ENC : ADDUH_QB_FMT<0b01001>; +class SUBQH_R_PH_ENC : ADDUH_QB_FMT<0b01011>; +class ADDQH_W_ENC : ADDUH_QB_FMT<0b10000>; +class ADDQH_R_W_ENC : ADDUH_QB_FMT<0b10010>; +class SUBQH_W_ENC : ADDUH_QB_FMT<0b10001>; +class SUBQH_R_W_ENC : ADDUH_QB_FMT<0b10011>; +class MUL_PH_ENC : ADDUH_QB_FMT<0b01100>; +class MUL_S_PH_ENC : ADDUH_QB_FMT<0b01110>; +class MULQ_S_W_ENC : ADDUH_QB_FMT<0b10110>; +class MULQ_RS_W_ENC : ADDUH_QB_FMT<0b10111>; +class MULQ_S_PH_ENC : ADDU_QB_FMT<0b11110>; +class DPA_W_PH_ENC : DPA_W_PH_FMT<0b00000>; +class DPS_W_PH_ENC : DPA_W_PH_FMT<0b00001>; +class DPAQX_S_W_PH_ENC : DPA_W_PH_FMT<0b11000>; +class DPAQX_SA_W_PH_ENC : DPA_W_PH_FMT<0b11010>; +class DPAX_W_PH_ENC : DPA_W_PH_FMT<0b01000>; +class DPSX_W_PH_ENC : DPA_W_PH_FMT<0b01001>; +class DPSQX_S_W_PH_ENC : DPA_W_PH_FMT<0b11001>; +class DPSQX_SA_W_PH_ENC : DPA_W_PH_FMT<0b11011>; +class MULSA_W_PH_ENC : DPA_W_PH_FMT<0b00010>; +class PRECR_QB_PH_ENC : CMP_EQ_QB_R3_FMT<0b01101>; +class PRECR_SRA_PH_W_ENC : PRECR_SRA_PH_W_FMT<0b11110>; +class PRECR_SRA_R_PH_W_ENC : PRECR_SRA_PH_W_FMT<0b11111>; +class SHRA_QB_ENC : SHLL_QB_FMT<0b00100>; +class SHRAV_QB_ENC : SHLL_QB_FMT<0b00110>; +class SHRA_R_QB_ENC : SHLL_QB_FMT<0b00101>; +class SHRAV_R_QB_ENC : SHLL_QB_FMT<0b00111>; +class SHRL_PH_ENC : SHLL_QB_FMT<0b11001>; +class SHRLV_PH_ENC : SHLL_QB_FMT<0b11011>; +class APPEND_ENC : APPEND_FMT<0b00000>; +class BALIGN_ENC : APPEND_FMT<0b10000>; +class PREPEND_ENC : APPEND_FMT<0b00001>; + +// Instruction desc. +class ADDU_QB_DESC_BASE { + dag OutOperandList = (outs ROD:$rd); + dag InOperandList = (ins ROS:$rs, ROT:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt"); + list Pattern = [(set ROD:$rd, (OpNode ROS:$rs, ROT:$rt))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class RADDU_W_QB_DESC_BASE { + dag OutOperandList = (outs ROD:$rd); + dag InOperandList = (ins ROS:$rs); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs"); + list Pattern = [(set ROD:$rd, (OpNode ROS:$rs))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class CMP_EQ_QB_R2_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins ROS:$rs, ROT:$rt); + string AsmString = !strconcat(instr_asm, "\t$rs, $rt"); + list Pattern = [(OpNode ROS:$rs, ROT:$rt)]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class CMP_EQ_QB_R3_DESC_BASE { + dag OutOperandList = (outs ROD:$rd); + dag InOperandList = (ins ROS:$rs, ROT:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt"); + list Pattern = [(set ROD:$rd, (OpNode ROS:$rs, ROT:$rt))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class PRECR_SRA_PH_W_DESC_BASE { + dag OutOperandList = (outs ROT:$rt); + dag InOperandList = (ins ROS:$rs, uimm5:$sa, ROS:$src); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs, $sa"); + list Pattern = [(set ROT:$rt, (OpNode ROS:$src, ROS:$rs, immZExt5:$sa))]; + InstrItinClass Itinerary = itin; + string Constraints = "$src = $rt"; + string BaseOpcode = instr_asm; +} + +class ABSQ_S_PH_R2_DESC_BASE { + dag OutOperandList = (outs ROD:$rd); + dag InOperandList = (ins ROT:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rt"); + list Pattern = [(set ROD:$rd, (OpNode ROT:$rt))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class REPL_DESC_BASE { + dag OutOperandList = (outs RO:$rd); + dag InOperandList = (ins ImmOp:$imm); + string AsmString = !strconcat(instr_asm, "\t$rd, $imm"); + list Pattern = [(set RO:$rd, (OpNode immPat:$imm))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class SHLL_QB_R3_DESC_BASE { + dag OutOperandList = (outs RO:$rd); + dag InOperandList = (ins RO:$rt, GPR32Opnd:$rs_sa); + string AsmString = !strconcat(instr_asm, "\t$rd, $rt, $rs_sa"); + list Pattern = [(set RO:$rd, (OpNode RO:$rt, GPR32Opnd:$rs_sa))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class SHLL_QB_R2_DESC_BASE { + dag OutOperandList = (outs RO:$rd); + dag InOperandList = (ins RO:$rt, ImmOpnd:$rs_sa); + string AsmString = !strconcat(instr_asm, "\t$rd, $rt, $rs_sa"); + list Pattern = [(set RO:$rd, (OpNode RO:$rt, ImmPat:$rs_sa))]; + InstrItinClass Itinerary = itin; + bit hasSideEffects = 1; + string BaseOpcode = instr_asm; +} + +class LX_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rd); + dag InOperandList = (ins PtrRC:$base, PtrRC:$index); + string AsmString = !strconcat(instr_asm, "\t$rd, ${index}(${base})"); + list Pattern = [(set GPR32Opnd:$rd, (OpNode iPTR:$base, iPTR:$index))]; + InstrItinClass Itinerary = itin; + bit mayLoad = 1; + string BaseOpcode = instr_asm; +} + +class ADDUH_QB_DESC_BASE { + dag OutOperandList = (outs ROD:$rd); + dag InOperandList = (ins ROS:$rs, ROT:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt"); + list Pattern = [(set ROD:$rd, (OpNode ROS:$rs, ROT:$rt))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class APPEND_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins GPR32Opnd:$rs, ImmOp:$sa, GPR32Opnd:$src); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs, $sa"); + list Pattern = [(set GPR32Opnd:$rt, + (OpNode GPR32Opnd:$src, GPR32Opnd:$rs, Imm:$sa))]; + InstrItinClass Itinerary = itin; + string Constraints = "$src = $rt"; + string BaseOpcode = instr_asm; +} + +class EXTR_W_TY1_R2_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins ACC64DSPOpnd:$ac, GPR32Opnd:$shift_rs); + string AsmString = !strconcat(instr_asm, "\t$rt, $ac, $shift_rs"); + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class EXTR_W_TY1_R1_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins ACC64DSPOpnd:$ac, uimm5:$shift_rs); + string AsmString = !strconcat(instr_asm, "\t$rt, $ac, $shift_rs"); + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class SHILO_R1_DESC_BASE { + dag OutOperandList = (outs ACC64DSPOpnd:$ac); + dag InOperandList = (ins simm6:$shift, ACC64DSPOpnd:$acin); + string AsmString = !strconcat(instr_asm, "\t$ac, $shift"); + list Pattern = [(set ACC64DSPOpnd:$ac, + (OpNode immSExt6:$shift, ACC64DSPOpnd:$acin))]; + string Constraints = "$acin = $ac"; + string BaseOpcode = instr_asm; +} + +class SHILO_R2_DESC_BASE { + dag OutOperandList = (outs ACC64DSPOpnd:$ac); + dag InOperandList = (ins GPR32Opnd:$rs, ACC64DSPOpnd:$acin); + string AsmString = !strconcat(instr_asm, "\t$ac, $rs"); + list Pattern = [(set ACC64DSPOpnd:$ac, + (OpNode GPR32Opnd:$rs, ACC64DSPOpnd:$acin))]; + string Constraints = "$acin = $ac"; + string BaseOpcode = instr_asm; +} + +class MTHLIP_DESC_BASE { + dag OutOperandList = (outs ACC64DSPOpnd:$ac); + dag InOperandList = (ins GPR32Opnd:$rs, ACC64DSPOpnd:$acin); + string AsmString = !strconcat(instr_asm, "\t$rs, $ac"); + list Pattern = [(set ACC64DSPOpnd:$ac, + (OpNode GPR32Opnd:$rs, ACC64DSPOpnd:$acin))]; + string Constraints = "$acin = $ac"; + string BaseOpcode = instr_asm; +} + +class RDDSP_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rd); + dag InOperandList = (ins uimm10:$mask); + string AsmString = !strconcat(instr_asm, "\t$rd, $mask"); + list Pattern = [(set GPR32Opnd:$rd, (OpNode immZExt10:$mask))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class WRDSP_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins GPR32Opnd:$rs, uimm10:$mask); + string AsmString = !strconcat(instr_asm, "\t$rs, $mask"); + list Pattern = [(OpNode GPR32Opnd:$rs, immZExt10:$mask)]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class DPA_W_PH_DESC_BASE { + dag OutOperandList = (outs ACC64DSPOpnd:$ac); + dag InOperandList = (ins GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64DSPOpnd:$acin); + string AsmString = !strconcat(instr_asm, "\t$ac, $rs, $rt"); + list Pattern = [(set ACC64DSPOpnd:$ac, + (OpNode GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64DSPOpnd:$acin))]; + string Constraints = "$acin = $ac"; + string BaseOpcode = instr_asm; +} + +class MULT_DESC_BASE { + dag OutOperandList = (outs ACC64DSPOpnd:$ac); + dag InOperandList = (ins GPR32Opnd:$rs, GPR32Opnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$ac, $rs, $rt"); + list Pattern = [(set ACC64DSPOpnd:$ac, (OpNode GPR32Opnd:$rs, GPR32Opnd:$rt))]; + InstrItinClass Itinerary = itin; + bit isCommutable = 1; + string BaseOpcode = instr_asm; +} + +class MADD_DESC_BASE { + dag OutOperandList = (outs ACC64DSPOpnd:$ac); + dag InOperandList = (ins GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64DSPOpnd:$acin); + string AsmString = !strconcat(instr_asm, "\t$ac, $rs, $rt"); + list Pattern = [(set ACC64DSPOpnd:$ac, + (OpNode GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64DSPOpnd:$acin))]; + InstrItinClass Itinerary = itin; + string Constraints = "$acin = $ac"; + string BaseOpcode = instr_asm; +} + +class MFHI_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rd); + dag InOperandList = (ins RO:$ac); + string AsmString = !strconcat(instr_asm, "\t$rd, $ac"); + list Pattern = [(set GPR32Opnd:$rd, (OpNode RO:$ac))]; + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class MTHI_DESC_BASE { + dag OutOperandList = (outs RO:$ac); + dag InOperandList = (ins GPR32Opnd:$rs); + string AsmString = !strconcat(instr_asm, "\t$rs, $ac"); + InstrItinClass Itinerary = itin; + string BaseOpcode = instr_asm; +} + +class BPOSGE32_PSEUDO_DESC_BASE : + MaxisPseudo<(outs GPR32Opnd:$dst), (ins), [(set GPR32Opnd:$dst, (OpNode))]> { + bit usesCustomInserter = 1; +} + +class BPOSGE32_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins opnd:$offset); + string AsmString = !strconcat(instr_asm, "\t$offset"); + InstrItinClass Itinerary = itin; + bit isBranch = 1; + bit isTerminator = 1; + bit hasDelaySlot = 1; + string BaseOpcode = instr_asm; +} + +class INSV_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins GPR32Opnd:$src, GPR32Opnd:$rs); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs"); + list Pattern = [(set GPR32Opnd:$rt, (OpNode GPR32Opnd:$src, GPR32Opnd:$rs))]; + InstrItinClass Itinerary = itin; + string Constraints = "$src = $rt"; + string BaseOpcode = instr_asm; +} + +//===----------------------------------------------------------------------===// +// MAXIS DSP Rev 1 +//===----------------------------------------------------------------------===// + +// Addition/subtraction +class ADDU_QB_DESC : ADDU_QB_DESC_BASE<"addu.qb", null_frag, NoItinerary, + DSPROpnd, DSPROpnd>, IsCommutable, + Defs<[DSPOutFlag20]>; + +class ADDU_S_QB_DESC : ADDU_QB_DESC_BASE<"addu_s.qb", int_maxis_addu_s_qb, + NoItinerary, DSPROpnd, DSPROpnd>, + IsCommutable, Defs<[DSPOutFlag20]>; + +class SUBU_QB_DESC : ADDU_QB_DESC_BASE<"subu.qb", null_frag, NoItinerary, + DSPROpnd, DSPROpnd>, + Defs<[DSPOutFlag20]>; + +class SUBU_S_QB_DESC : ADDU_QB_DESC_BASE<"subu_s.qb", int_maxis_subu_s_qb, + NoItinerary, DSPROpnd, DSPROpnd>, + Defs<[DSPOutFlag20]>; + +class ADDQ_PH_DESC : ADDU_QB_DESC_BASE<"addq.ph", null_frag, NoItinerary, + DSPROpnd, DSPROpnd>, IsCommutable, + Defs<[DSPOutFlag20]>; + +class ADDQ_S_PH_DESC : ADDU_QB_DESC_BASE<"addq_s.ph", int_maxis_addq_s_ph, + NoItinerary, DSPROpnd, DSPROpnd>, + IsCommutable, Defs<[DSPOutFlag20]>; + +class SUBQ_PH_DESC : ADDU_QB_DESC_BASE<"subq.ph", null_frag, NoItinerary, + DSPROpnd, DSPROpnd>, + Defs<[DSPOutFlag20]>; + +class SUBQ_S_PH_DESC : ADDU_QB_DESC_BASE<"subq_s.ph", int_maxis_subq_s_ph, + NoItinerary, DSPROpnd, DSPROpnd>, + Defs<[DSPOutFlag20]>; + +class ADDQ_S_W_DESC : ADDU_QB_DESC_BASE<"addq_s.w", int_maxis_addq_s_w, + NoItinerary, GPR32Opnd, GPR32Opnd>, + IsCommutable, Defs<[DSPOutFlag20]>; + +class SUBQ_S_W_DESC : ADDU_QB_DESC_BASE<"subq_s.w", int_maxis_subq_s_w, + NoItinerary, GPR32Opnd, GPR32Opnd>, + Defs<[DSPOutFlag20]>; + +class ADDSC_DESC : ADDU_QB_DESC_BASE<"addsc", null_frag, NoItinerary, + GPR32Opnd, GPR32Opnd>, IsCommutable, + Defs<[DSPCarry]>; + +class ADDWC_DESC : ADDU_QB_DESC_BASE<"addwc", null_frag, NoItinerary, + GPR32Opnd, GPR32Opnd>, + IsCommutable, Uses<[DSPCarry]>, Defs<[DSPOutFlag20]>; + +class MODSUB_DESC : ADDU_QB_DESC_BASE<"modsub", int_maxis_modsub, NoItinerary, + GPR32Opnd, GPR32Opnd>; + +class RADDU_W_QB_DESC : RADDU_W_QB_DESC_BASE<"raddu.w.qb", int_maxis_raddu_w_qb, + NoItinerary, GPR32Opnd, DSPROpnd>; + +// Absolute value +class ABSQ_S_PH_DESC : ABSQ_S_PH_R2_DESC_BASE<"absq_s.ph", int_maxis_absq_s_ph, + NoItinerary, DSPROpnd>, + Defs<[DSPOutFlag20]>; + +class ABSQ_S_W_DESC : ABSQ_S_PH_R2_DESC_BASE<"absq_s.w", int_maxis_absq_s_w, + NoItinerary, GPR32Opnd>, + Defs<[DSPOutFlag20]>; + +// Precision reduce/expand +class PRECRQ_QB_PH_DESC : CMP_EQ_QB_R3_DESC_BASE<"precrq.qb.ph", + int_maxis_precrq_qb_ph, + NoItinerary, DSPROpnd, DSPROpnd>; + +class PRECRQ_PH_W_DESC : CMP_EQ_QB_R3_DESC_BASE<"precrq.ph.w", + int_maxis_precrq_ph_w, + NoItinerary, DSPROpnd, GPR32Opnd>; + +class PRECRQ_RS_PH_W_DESC : CMP_EQ_QB_R3_DESC_BASE<"precrq_rs.ph.w", + int_maxis_precrq_rs_ph_w, + NoItinerary, DSPROpnd, + GPR32Opnd>, + Defs<[DSPOutFlag22]>; + +class PRECRQU_S_QB_PH_DESC : CMP_EQ_QB_R3_DESC_BASE<"precrqu_s.qb.ph", + int_maxis_precrqu_s_qb_ph, + NoItinerary, DSPROpnd, + DSPROpnd>, + Defs<[DSPOutFlag22]>; + +class PRECEQ_W_PHL_DESC : ABSQ_S_PH_R2_DESC_BASE<"preceq.w.phl", + int_maxis_preceq_w_phl, + NoItinerary, GPR32Opnd, DSPROpnd>; + +class PRECEQ_W_PHR_DESC : ABSQ_S_PH_R2_DESC_BASE<"preceq.w.phr", + int_maxis_preceq_w_phr, + NoItinerary, GPR32Opnd, DSPROpnd>; + +class PRECEQU_PH_QBL_DESC : ABSQ_S_PH_R2_DESC_BASE<"precequ.ph.qbl", + int_maxis_precequ_ph_qbl, + NoItinerary, DSPROpnd>; + +class PRECEQU_PH_QBR_DESC : ABSQ_S_PH_R2_DESC_BASE<"precequ.ph.qbr", + int_maxis_precequ_ph_qbr, + NoItinerary, DSPROpnd>; + +class PRECEQU_PH_QBLA_DESC : ABSQ_S_PH_R2_DESC_BASE<"precequ.ph.qbla", + int_maxis_precequ_ph_qbla, + NoItinerary, DSPROpnd>; + +class PRECEQU_PH_QBRA_DESC : ABSQ_S_PH_R2_DESC_BASE<"precequ.ph.qbra", + int_maxis_precequ_ph_qbra, + NoItinerary, DSPROpnd>; + +class PRECEU_PH_QBL_DESC : ABSQ_S_PH_R2_DESC_BASE<"preceu.ph.qbl", + int_maxis_preceu_ph_qbl, + NoItinerary, DSPROpnd>; + +class PRECEU_PH_QBR_DESC : ABSQ_S_PH_R2_DESC_BASE<"preceu.ph.qbr", + int_maxis_preceu_ph_qbr, + NoItinerary, DSPROpnd>; + +class PRECEU_PH_QBLA_DESC : ABSQ_S_PH_R2_DESC_BASE<"preceu.ph.qbla", + int_maxis_preceu_ph_qbla, + NoItinerary, DSPROpnd>; + +class PRECEU_PH_QBRA_DESC : ABSQ_S_PH_R2_DESC_BASE<"preceu.ph.qbra", + int_maxis_preceu_ph_qbra, + NoItinerary, DSPROpnd>; + +// Shift +class SHLL_QB_DESC : SHLL_QB_R2_DESC_BASE<"shll.qb", null_frag, immZExt3, + NoItinerary, DSPROpnd, uimm3>, + Defs<[DSPOutFlag22]>; + +class SHLLV_QB_DESC : SHLL_QB_R3_DESC_BASE<"shllv.qb", int_maxis_shll_qb, + NoItinerary, DSPROpnd>, + Defs<[DSPOutFlag22]>; + +class SHRL_QB_DESC : SHLL_QB_R2_DESC_BASE<"shrl.qb", null_frag, immZExt3, + NoItinerary, DSPROpnd, uimm3>; + +class SHRLV_QB_DESC : SHLL_QB_R3_DESC_BASE<"shrlv.qb", int_maxis_shrl_qb, + NoItinerary, DSPROpnd>; + +class SHLL_PH_DESC : SHLL_QB_R2_DESC_BASE<"shll.ph", null_frag, immZExt4, + NoItinerary, DSPROpnd, uimm4>, + Defs<[DSPOutFlag22]>; + +class SHLLV_PH_DESC : SHLL_QB_R3_DESC_BASE<"shllv.ph", int_maxis_shll_ph, + NoItinerary, DSPROpnd>, + Defs<[DSPOutFlag22]>; + +class SHLL_S_PH_DESC : SHLL_QB_R2_DESC_BASE<"shll_s.ph", int_maxis_shll_s_ph, + immZExt4, NoItinerary, DSPROpnd, + uimm4>, + Defs<[DSPOutFlag22]>; + +class SHLLV_S_PH_DESC : SHLL_QB_R3_DESC_BASE<"shllv_s.ph", int_maxis_shll_s_ph, + NoItinerary, DSPROpnd>, + Defs<[DSPOutFlag22]>; + +class SHRA_PH_DESC : SHLL_QB_R2_DESC_BASE<"shra.ph", null_frag, immZExt4, + NoItinerary, DSPROpnd, uimm4>; + +class SHRAV_PH_DESC : SHLL_QB_R3_DESC_BASE<"shrav.ph", int_maxis_shra_ph, + NoItinerary, DSPROpnd>; + +class SHRA_R_PH_DESC : SHLL_QB_R2_DESC_BASE<"shra_r.ph", int_maxis_shra_r_ph, + immZExt4, NoItinerary, DSPROpnd, + uimm4>; + +class SHRAV_R_PH_DESC : SHLL_QB_R3_DESC_BASE<"shrav_r.ph", int_maxis_shra_r_ph, + NoItinerary, DSPROpnd>; + +class SHLL_S_W_DESC : SHLL_QB_R2_DESC_BASE<"shll_s.w", int_maxis_shll_s_w, + immZExt5, NoItinerary, GPR32Opnd, + uimm5>, + Defs<[DSPOutFlag22]>; + +class SHLLV_S_W_DESC : SHLL_QB_R3_DESC_BASE<"shllv_s.w", int_maxis_shll_s_w, + NoItinerary, GPR32Opnd>, + Defs<[DSPOutFlag22]>; + +class SHRA_R_W_DESC : SHLL_QB_R2_DESC_BASE<"shra_r.w", int_maxis_shra_r_w, + immZExt5, NoItinerary, GPR32Opnd, + uimm5>; + +class SHRAV_R_W_DESC : SHLL_QB_R3_DESC_BASE<"shrav_r.w", int_maxis_shra_r_w, + NoItinerary, GPR32Opnd>; + +// Multiplication +class MULEU_S_PH_QBL_DESC : ADDU_QB_DESC_BASE<"muleu_s.ph.qbl", + int_maxis_muleu_s_ph_qbl, + NoItinerary, DSPROpnd, DSPROpnd>, + Defs<[DSPOutFlag21]>; + +class MULEU_S_PH_QBR_DESC : ADDU_QB_DESC_BASE<"muleu_s.ph.qbr", + int_maxis_muleu_s_ph_qbr, + NoItinerary, DSPROpnd, DSPROpnd>, + Defs<[DSPOutFlag21]>; + +class MULEQ_S_W_PHL_DESC : ADDU_QB_DESC_BASE<"muleq_s.w.phl", + int_maxis_muleq_s_w_phl, + NoItinerary, GPR32Opnd, DSPROpnd>, + IsCommutable, Defs<[DSPOutFlag21]>; + +class MULEQ_S_W_PHR_DESC : ADDU_QB_DESC_BASE<"muleq_s.w.phr", + int_maxis_muleq_s_w_phr, + NoItinerary, GPR32Opnd, DSPROpnd>, + IsCommutable, Defs<[DSPOutFlag21]>; + +class MULQ_RS_PH_DESC : ADDU_QB_DESC_BASE<"mulq_rs.ph", int_maxis_mulq_rs_ph, + NoItinerary, DSPROpnd, DSPROpnd>, + IsCommutable, Defs<[DSPOutFlag21]>; + +class MULSAQ_S_W_PH_DESC : DPA_W_PH_DESC_BASE<"mulsaq_s.w.ph", + MaxisMULSAQ_S_W_PH>, + Defs<[DSPOutFlag16_19]>; + +class MAQ_S_W_PHL_DESC : DPA_W_PH_DESC_BASE<"maq_s.w.phl", MaxisMAQ_S_W_PHL>, + Defs<[DSPOutFlag16_19]>; + +class MAQ_S_W_PHR_DESC : DPA_W_PH_DESC_BASE<"maq_s.w.phr", MaxisMAQ_S_W_PHR>, + Defs<[DSPOutFlag16_19]>; + +class MAQ_SA_W_PHL_DESC : DPA_W_PH_DESC_BASE<"maq_sa.w.phl", MaxisMAQ_SA_W_PHL>, + Defs<[DSPOutFlag16_19]>; + +class MAQ_SA_W_PHR_DESC : DPA_W_PH_DESC_BASE<"maq_sa.w.phr", MaxisMAQ_SA_W_PHR>, + Defs<[DSPOutFlag16_19]>; + +// Move from/to hi/lo. +class MFHI_DESC : MFHI_DESC_BASE<"mfhi", ACC64DSPOpnd, MaxisMFHI, NoItinerary>; +class MFLO_DESC : MFHI_DESC_BASE<"mflo", ACC64DSPOpnd, MaxisMFLO, NoItinerary>; +class MTHI_DESC : MTHI_DESC_BASE<"mthi", HI32DSPOpnd, NoItinerary>; +class MTLO_DESC : MTHI_DESC_BASE<"mtlo", LO32DSPOpnd, NoItinerary>; + +// Dot product with accumulate/subtract +class DPAU_H_QBL_DESC : DPA_W_PH_DESC_BASE<"dpau.h.qbl", MaxisDPAU_H_QBL>; + +class DPAU_H_QBR_DESC : DPA_W_PH_DESC_BASE<"dpau.h.qbr", MaxisDPAU_H_QBR>; + +class DPSU_H_QBL_DESC : DPA_W_PH_DESC_BASE<"dpsu.h.qbl", MaxisDPSU_H_QBL>; + +class DPSU_H_QBR_DESC : DPA_W_PH_DESC_BASE<"dpsu.h.qbr", MaxisDPSU_H_QBR>; + +class DPAQ_S_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpaq_s.w.ph", MaxisDPAQ_S_W_PH>, + Defs<[DSPOutFlag16_19]>; + +class DPSQ_S_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpsq_s.w.ph", MaxisDPSQ_S_W_PH>, + Defs<[DSPOutFlag16_19]>; + +class DPAQ_SA_L_W_DESC : DPA_W_PH_DESC_BASE<"dpaq_sa.l.w", MaxisDPAQ_SA_L_W>, + Defs<[DSPOutFlag16_19]>; + +class DPSQ_SA_L_W_DESC : DPA_W_PH_DESC_BASE<"dpsq_sa.l.w", MaxisDPSQ_SA_L_W>, + Defs<[DSPOutFlag16_19]>; + +class MULT_DSP_DESC : MULT_DESC_BASE<"mult", MaxisMult, NoItinerary>; +class MULTU_DSP_DESC : MULT_DESC_BASE<"multu", MaxisMultu, NoItinerary>; +class MADD_DSP_DESC : MADD_DESC_BASE<"madd", MaxisMAdd, NoItinerary>; +class MADDU_DSP_DESC : MADD_DESC_BASE<"maddu", MaxisMAddu, NoItinerary>; +class MSUB_DSP_DESC : MADD_DESC_BASE<"msub", MaxisMSub, NoItinerary>; +class MSUBU_DSP_DESC : MADD_DESC_BASE<"msubu", MaxisMSubu, NoItinerary>; + +// Comparison +class CMPU_EQ_QB_DESC : CMP_EQ_QB_R2_DESC_BASE<"cmpu.eq.qb", + int_maxis_cmpu_eq_qb, NoItinerary, + DSPROpnd>, + IsCommutable, Defs<[DSPCCond]>; + +class CMPU_LT_QB_DESC : CMP_EQ_QB_R2_DESC_BASE<"cmpu.lt.qb", + int_maxis_cmpu_lt_qb, NoItinerary, + DSPROpnd>, Defs<[DSPCCond]>; + +class CMPU_LE_QB_DESC : CMP_EQ_QB_R2_DESC_BASE<"cmpu.le.qb", + int_maxis_cmpu_le_qb, NoItinerary, + DSPROpnd>, Defs<[DSPCCond]>; + +class CMPGU_EQ_QB_DESC : CMP_EQ_QB_R3_DESC_BASE<"cmpgu.eq.qb", + int_maxis_cmpgu_eq_qb, + NoItinerary, GPR32Opnd, DSPROpnd>, + IsCommutable; + +class CMPGU_LT_QB_DESC : CMP_EQ_QB_R3_DESC_BASE<"cmpgu.lt.qb", + int_maxis_cmpgu_lt_qb, + NoItinerary, GPR32Opnd, DSPROpnd>; + +class CMPGU_LE_QB_DESC : CMP_EQ_QB_R3_DESC_BASE<"cmpgu.le.qb", + int_maxis_cmpgu_le_qb, + NoItinerary, GPR32Opnd, DSPROpnd>; + +class CMP_EQ_PH_DESC : CMP_EQ_QB_R2_DESC_BASE<"cmp.eq.ph", int_maxis_cmp_eq_ph, + NoItinerary, DSPROpnd>, + IsCommutable, Defs<[DSPCCond]>; + +class CMP_LT_PH_DESC : CMP_EQ_QB_R2_DESC_BASE<"cmp.lt.ph", int_maxis_cmp_lt_ph, + NoItinerary, DSPROpnd>, + Defs<[DSPCCond]>; + +class CMP_LE_PH_DESC : CMP_EQ_QB_R2_DESC_BASE<"cmp.le.ph", int_maxis_cmp_le_ph, + NoItinerary, DSPROpnd>, + Defs<[DSPCCond]>; + +// Misc +class BITREV_DESC : ABSQ_S_PH_R2_DESC_BASE<"bitrev", int_maxis_bitrev, + NoItinerary, GPR32Opnd>; + +class PACKRL_PH_DESC : CMP_EQ_QB_R3_DESC_BASE<"packrl.ph", int_maxis_packrl_ph, + NoItinerary, DSPROpnd, DSPROpnd>; + +class REPL_QB_DESC : REPL_DESC_BASE<"repl.qb", int_maxis_repl_qb, uimm8, + immZExt8, NoItinerary, DSPROpnd>; + +class REPL_PH_DESC : REPL_DESC_BASE<"repl.ph", int_maxis_repl_ph, simm10, + immSExt10, NoItinerary, DSPROpnd>; + +class REPLV_QB_DESC : ABSQ_S_PH_R2_DESC_BASE<"replv.qb", int_maxis_repl_qb, + NoItinerary, DSPROpnd, GPR32Opnd>; + +class REPLV_PH_DESC : ABSQ_S_PH_R2_DESC_BASE<"replv.ph", int_maxis_repl_ph, + NoItinerary, DSPROpnd, GPR32Opnd>; + +class PICK_QB_DESC : CMP_EQ_QB_R3_DESC_BASE<"pick.qb", int_maxis_pick_qb, + NoItinerary, DSPROpnd, DSPROpnd>, + Uses<[DSPCCond]>; + +class PICK_PH_DESC : CMP_EQ_QB_R3_DESC_BASE<"pick.ph", int_maxis_pick_ph, + NoItinerary, DSPROpnd, DSPROpnd>, + Uses<[DSPCCond]>; + +class LWX_DESC : LX_DESC_BASE<"lwx", int_maxis_lwx, NoItinerary>; + +class LHX_DESC : LX_DESC_BASE<"lhx", int_maxis_lhx, NoItinerary>; + +class LBUX_DESC : LX_DESC_BASE<"lbux", int_maxis_lbux, NoItinerary>; + +class BPOSGE32_DESC : BPOSGE32_DESC_BASE<"bposge32", brtarget, NoItinerary>; + +// Extr +class EXTP_DESC : EXTR_W_TY1_R1_DESC_BASE<"extp", MaxisEXTP, NoItinerary>, + Uses<[DSPPos]>, Defs<[DSPEFI]>; + +class EXTPV_DESC : EXTR_W_TY1_R2_DESC_BASE<"extpv", MaxisEXTP, NoItinerary>, + Uses<[DSPPos]>, Defs<[DSPEFI]>; + +class EXTPDP_DESC : EXTR_W_TY1_R1_DESC_BASE<"extpdp", MaxisEXTPDP, NoItinerary>, + Uses<[DSPPos]>, Defs<[DSPPos, DSPEFI]>; + +class EXTPDPV_DESC : EXTR_W_TY1_R2_DESC_BASE<"extpdpv", MaxisEXTPDP, + NoItinerary>, + Uses<[DSPPos]>, Defs<[DSPPos, DSPEFI]>; + +class EXTR_W_DESC : EXTR_W_TY1_R1_DESC_BASE<"extr.w", MaxisEXTR_W, NoItinerary>, + Defs<[DSPOutFlag23]>; + +class EXTRV_W_DESC : EXTR_W_TY1_R2_DESC_BASE<"extrv.w", MaxisEXTR_W, + NoItinerary>, Defs<[DSPOutFlag23]>; + +class EXTR_R_W_DESC : EXTR_W_TY1_R1_DESC_BASE<"extr_r.w", MaxisEXTR_R_W, + NoItinerary>, + Defs<[DSPOutFlag23]>; + +class EXTRV_R_W_DESC : EXTR_W_TY1_R2_DESC_BASE<"extrv_r.w", MaxisEXTR_R_W, + NoItinerary>, + Defs<[DSPOutFlag23]>; + +class EXTR_RS_W_DESC : EXTR_W_TY1_R1_DESC_BASE<"extr_rs.w", MaxisEXTR_RS_W, + NoItinerary>, + Defs<[DSPOutFlag23]>; + +class EXTRV_RS_W_DESC : EXTR_W_TY1_R2_DESC_BASE<"extrv_rs.w", MaxisEXTR_RS_W, + NoItinerary>, + Defs<[DSPOutFlag23]>; + +class EXTR_S_H_DESC : EXTR_W_TY1_R1_DESC_BASE<"extr_s.h", MaxisEXTR_S_H, + NoItinerary>, + Defs<[DSPOutFlag23]>; + +class EXTRV_S_H_DESC : EXTR_W_TY1_R2_DESC_BASE<"extrv_s.h", MaxisEXTR_S_H, + NoItinerary>, + Defs<[DSPOutFlag23]>; + +class SHILO_DESC : SHILO_R1_DESC_BASE<"shilo", MaxisSHILO>; + +class SHILOV_DESC : SHILO_R2_DESC_BASE<"shilov", MaxisSHILO>; + +class MTHLIP_DESC : MTHLIP_DESC_BASE<"mthlip", MaxisMTHLIP>, Defs<[DSPPos]>; + +class RDDSP_DESC : RDDSP_DESC_BASE<"rddsp", int_maxis_rddsp, NoItinerary>; + +class WRDSP_DESC : WRDSP_DESC_BASE<"wrdsp", int_maxis_wrdsp, NoItinerary>; + +class INSV_DESC : INSV_DESC_BASE<"insv", int_maxis_insv, NoItinerary>, + Uses<[DSPPos, DSPSCount]>; + +//===----------------------------------------------------------------------===// +// MAXIS DSP Rev 2 +// Addition/subtraction +class ADDU_PH_DESC : ADDU_QB_DESC_BASE<"addu.ph", int_maxis_addu_ph, NoItinerary, + DSPROpnd, DSPROpnd>, IsCommutable, + Defs<[DSPOutFlag20]>; + +class ADDU_S_PH_DESC : ADDU_QB_DESC_BASE<"addu_s.ph", int_maxis_addu_s_ph, + NoItinerary, DSPROpnd, DSPROpnd>, + IsCommutable, Defs<[DSPOutFlag20]>; + +class SUBU_PH_DESC : ADDU_QB_DESC_BASE<"subu.ph", int_maxis_subu_ph, NoItinerary, + DSPROpnd, DSPROpnd>, + Defs<[DSPOutFlag20]>; + +class SUBU_S_PH_DESC : ADDU_QB_DESC_BASE<"subu_s.ph", int_maxis_subu_s_ph, + NoItinerary, DSPROpnd, DSPROpnd>, + Defs<[DSPOutFlag20]>; + +class ADDUH_QB_DESC : ADDUH_QB_DESC_BASE<"adduh.qb", int_maxis_adduh_qb, + NoItinerary, DSPROpnd>, IsCommutable; + +class ADDUH_R_QB_DESC : ADDUH_QB_DESC_BASE<"adduh_r.qb", int_maxis_adduh_r_qb, + NoItinerary, DSPROpnd>, IsCommutable; + +class SUBUH_QB_DESC : ADDUH_QB_DESC_BASE<"subuh.qb", int_maxis_subuh_qb, + NoItinerary, DSPROpnd>; + +class SUBUH_R_QB_DESC : ADDUH_QB_DESC_BASE<"subuh_r.qb", int_maxis_subuh_r_qb, + NoItinerary, DSPROpnd>; + +class ADDQH_PH_DESC : ADDUH_QB_DESC_BASE<"addqh.ph", int_maxis_addqh_ph, + NoItinerary, DSPROpnd>, IsCommutable; + +class ADDQH_R_PH_DESC : ADDUH_QB_DESC_BASE<"addqh_r.ph", int_maxis_addqh_r_ph, + NoItinerary, DSPROpnd>, IsCommutable; + +class SUBQH_PH_DESC : ADDUH_QB_DESC_BASE<"subqh.ph", int_maxis_subqh_ph, + NoItinerary, DSPROpnd>; + +class SUBQH_R_PH_DESC : ADDUH_QB_DESC_BASE<"subqh_r.ph", int_maxis_subqh_r_ph, + NoItinerary, DSPROpnd>; + +class ADDQH_W_DESC : ADDUH_QB_DESC_BASE<"addqh.w", int_maxis_addqh_w, + NoItinerary, GPR32Opnd>, IsCommutable; + +class ADDQH_R_W_DESC : ADDUH_QB_DESC_BASE<"addqh_r.w", int_maxis_addqh_r_w, + NoItinerary, GPR32Opnd>, IsCommutable; + +class SUBQH_W_DESC : ADDUH_QB_DESC_BASE<"subqh.w", int_maxis_subqh_w, + NoItinerary, GPR32Opnd>; + +class SUBQH_R_W_DESC : ADDUH_QB_DESC_BASE<"subqh_r.w", int_maxis_subqh_r_w, + NoItinerary, GPR32Opnd>; + +// Comparison +class CMPGDU_EQ_QB_DESC : CMP_EQ_QB_R3_DESC_BASE<"cmpgdu.eq.qb", + int_maxis_cmpgdu_eq_qb, + NoItinerary, GPR32Opnd, DSPROpnd>, + IsCommutable, Defs<[DSPCCond]>; + +class CMPGDU_LT_QB_DESC : CMP_EQ_QB_R3_DESC_BASE<"cmpgdu.lt.qb", + int_maxis_cmpgdu_lt_qb, + NoItinerary, GPR32Opnd, DSPROpnd>, + Defs<[DSPCCond]>; + +class CMPGDU_LE_QB_DESC : CMP_EQ_QB_R3_DESC_BASE<"cmpgdu.le.qb", + int_maxis_cmpgdu_le_qb, + NoItinerary, GPR32Opnd, DSPROpnd>, + Defs<[DSPCCond]>; + +// Absolute +class ABSQ_S_QB_DESC : ABSQ_S_PH_R2_DESC_BASE<"absq_s.qb", int_maxis_absq_s_qb, + NoItinerary, DSPROpnd>, + Defs<[DSPOutFlag20]>; + +// Multiplication +class MUL_PH_DESC : ADDUH_QB_DESC_BASE<"mul.ph", null_frag, NoItinerary, + DSPROpnd>, IsCommutable, + Defs<[DSPOutFlag21]>; + +class MUL_S_PH_DESC : ADDUH_QB_DESC_BASE<"mul_s.ph", int_maxis_mul_s_ph, + NoItinerary, DSPROpnd>, IsCommutable, + Defs<[DSPOutFlag21]>; + +class MULQ_S_W_DESC : ADDUH_QB_DESC_BASE<"mulq_s.w", int_maxis_mulq_s_w, + NoItinerary, GPR32Opnd>, IsCommutable, + Defs<[DSPOutFlag21]>; + +class MULQ_RS_W_DESC : ADDUH_QB_DESC_BASE<"mulq_rs.w", int_maxis_mulq_rs_w, + NoItinerary, GPR32Opnd>, IsCommutable, + Defs<[DSPOutFlag21]>; + +class MULQ_S_PH_DESC : ADDU_QB_DESC_BASE<"mulq_s.ph", int_maxis_mulq_s_ph, + NoItinerary, DSPROpnd, DSPROpnd>, + IsCommutable, Defs<[DSPOutFlag21]>; + +// Dot product with accumulate/subtract +class DPA_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpa.w.ph", MaxisDPA_W_PH>; + +class DPS_W_PH_DESC : DPA_W_PH_DESC_BASE<"dps.w.ph", MaxisDPS_W_PH>; + +class DPAQX_S_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpaqx_s.w.ph", MaxisDPAQX_S_W_PH>, + Defs<[DSPOutFlag16_19]>; + +class DPAQX_SA_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpaqx_sa.w.ph", + MaxisDPAQX_SA_W_PH>, + Defs<[DSPOutFlag16_19]>; + +class DPAX_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpax.w.ph", MaxisDPAX_W_PH>; + +class DPSX_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpsx.w.ph", MaxisDPSX_W_PH>; + +class DPSQX_S_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpsqx_s.w.ph", MaxisDPSQX_S_W_PH>, + Defs<[DSPOutFlag16_19]>; + +class DPSQX_SA_W_PH_DESC : DPA_W_PH_DESC_BASE<"dpsqx_sa.w.ph", + MaxisDPSQX_SA_W_PH>, + Defs<[DSPOutFlag16_19]>; + +class MULSA_W_PH_DESC : DPA_W_PH_DESC_BASE<"mulsa.w.ph", MaxisMULSA_W_PH>; + +// Precision reduce/expand +class PRECR_QB_PH_DESC : CMP_EQ_QB_R3_DESC_BASE<"precr.qb.ph", + int_maxis_precr_qb_ph, + NoItinerary, DSPROpnd, DSPROpnd>; + +class PRECR_SRA_PH_W_DESC : PRECR_SRA_PH_W_DESC_BASE<"precr_sra.ph.w", + int_maxis_precr_sra_ph_w, + NoItinerary, DSPROpnd, + GPR32Opnd>; + +class PRECR_SRA_R_PH_W_DESC : PRECR_SRA_PH_W_DESC_BASE<"precr_sra_r.ph.w", + int_maxis_precr_sra_r_ph_w, + NoItinerary, DSPROpnd, + GPR32Opnd>; + +// Shift +class SHRA_QB_DESC : SHLL_QB_R2_DESC_BASE<"shra.qb", null_frag, immZExt3, + NoItinerary, DSPROpnd, uimm3>; + +class SHRAV_QB_DESC : SHLL_QB_R3_DESC_BASE<"shrav.qb", int_maxis_shra_qb, + NoItinerary, DSPROpnd>; + +class SHRA_R_QB_DESC : SHLL_QB_R2_DESC_BASE<"shra_r.qb", int_maxis_shra_r_qb, + immZExt3, NoItinerary, DSPROpnd, + uimm3>; + +class SHRAV_R_QB_DESC : SHLL_QB_R3_DESC_BASE<"shrav_r.qb", int_maxis_shra_r_qb, + NoItinerary, DSPROpnd>; + +class SHRL_PH_DESC : SHLL_QB_R2_DESC_BASE<"shrl.ph", null_frag, immZExt4, + NoItinerary, DSPROpnd, uimm4>; + +class SHRLV_PH_DESC : SHLL_QB_R3_DESC_BASE<"shrlv.ph", int_maxis_shrl_ph, + NoItinerary, DSPROpnd>; + +// Misc +class APPEND_DESC : APPEND_DESC_BASE<"append", int_maxis_append, uimm5, immZExt5, + NoItinerary>; + +class BALIGN_DESC : APPEND_DESC_BASE<"balign", int_maxis_balign, uimm2, immZExt2, + NoItinerary>; + +class PREPEND_DESC : APPEND_DESC_BASE<"prepend", int_maxis_prepend, uimm5, + immZExt5, NoItinerary>; + +// Pseudos. +def BPOSGE32_PSEUDO : BPOSGE32_PSEUDO_DESC_BASE, Uses<[DSPPos]>; + +// Instruction defs. +// MAXIS DSP Rev 1 +def ADDU_QB : DspMMRel, ADDU_QB_ENC, ADDU_QB_DESC; +def ADDU_S_QB : DspMMRel, ADDU_S_QB_ENC, ADDU_S_QB_DESC; +def SUBU_QB : DspMMRel, SUBU_QB_ENC, SUBU_QB_DESC; +def SUBU_S_QB : DspMMRel, SUBU_S_QB_ENC, SUBU_S_QB_DESC; +def ADDQ_PH : DspMMRel, ADDQ_PH_ENC, ADDQ_PH_DESC; +def ADDQ_S_PH : DspMMRel, ADDQ_S_PH_ENC, ADDQ_S_PH_DESC; +def SUBQ_PH : DspMMRel, SUBQ_PH_ENC, SUBQ_PH_DESC; +def SUBQ_S_PH : DspMMRel, SUBQ_S_PH_ENC, SUBQ_S_PH_DESC; +def ADDQ_S_W : DspMMRel, ADDQ_S_W_ENC, ADDQ_S_W_DESC; +def SUBQ_S_W : DspMMRel, SUBQ_S_W_ENC, SUBQ_S_W_DESC; +def ADDSC : DspMMRel, ADDSC_ENC, ADDSC_DESC; +def ADDWC : DspMMRel, ADDWC_ENC, ADDWC_DESC; +def MODSUB : DspMMRel, MODSUB_ENC, MODSUB_DESC; +def RADDU_W_QB : DspMMRel, RADDU_W_QB_ENC, RADDU_W_QB_DESC; +def ABSQ_S_PH : DspMMRel, ABSQ_S_PH_ENC, ABSQ_S_PH_DESC; +def ABSQ_S_W : DspMMRel, ABSQ_S_W_ENC, ABSQ_S_W_DESC; +def PRECRQ_QB_PH : DspMMRel, PRECRQ_QB_PH_ENC, PRECRQ_QB_PH_DESC; +def PRECRQ_PH_W : DspMMRel, PRECRQ_PH_W_ENC, PRECRQ_PH_W_DESC; +def PRECRQ_RS_PH_W : DspMMRel, PRECRQ_RS_PH_W_ENC, PRECRQ_RS_PH_W_DESC; +def PRECRQU_S_QB_PH : DspMMRel, PRECRQU_S_QB_PH_ENC, PRECRQU_S_QB_PH_DESC; +def PRECEQ_W_PHL : DspMMRel, PRECEQ_W_PHL_ENC, PRECEQ_W_PHL_DESC; +def PRECEQ_W_PHR : DspMMRel, PRECEQ_W_PHR_ENC, PRECEQ_W_PHR_DESC; +def PRECEQU_PH_QBL : DspMMRel, PRECEQU_PH_QBL_ENC, PRECEQU_PH_QBL_DESC; +def PRECEQU_PH_QBR : DspMMRel, PRECEQU_PH_QBR_ENC, PRECEQU_PH_QBR_DESC; +def PRECEQU_PH_QBLA : DspMMRel, PRECEQU_PH_QBLA_ENC, PRECEQU_PH_QBLA_DESC; +def PRECEQU_PH_QBRA : DspMMRel, PRECEQU_PH_QBRA_ENC, PRECEQU_PH_QBRA_DESC; +def PRECEU_PH_QBL : DspMMRel, PRECEU_PH_QBL_ENC, PRECEU_PH_QBL_DESC; +def PRECEU_PH_QBR : DspMMRel, PRECEU_PH_QBR_ENC, PRECEU_PH_QBR_DESC; +def PRECEU_PH_QBLA : DspMMRel, PRECEU_PH_QBLA_ENC, PRECEU_PH_QBLA_DESC; +def PRECEU_PH_QBRA : DspMMRel, PRECEU_PH_QBRA_ENC, PRECEU_PH_QBRA_DESC; +def SHLL_QB : DspMMRel, SHLL_QB_ENC, SHLL_QB_DESC; +def SHLLV_QB : DspMMRel, SHLLV_QB_ENC, SHLLV_QB_DESC; +def SHRL_QB : DspMMRel, SHRL_QB_ENC, SHRL_QB_DESC; +def SHRLV_QB : DspMMRel, SHRLV_QB_ENC, SHRLV_QB_DESC; +def SHLL_PH : DspMMRel, SHLL_PH_ENC, SHLL_PH_DESC; +def SHLLV_PH : DspMMRel, SHLLV_PH_ENC, SHLLV_PH_DESC; +def SHLL_S_PH : DspMMRel, SHLL_S_PH_ENC, SHLL_S_PH_DESC; +def SHLLV_S_PH : DspMMRel, SHLLV_S_PH_ENC, SHLLV_S_PH_DESC; +def SHRA_PH : DspMMRel, SHRA_PH_ENC, SHRA_PH_DESC; +def SHRAV_PH : DspMMRel, SHRAV_PH_ENC, SHRAV_PH_DESC; +def SHRA_R_PH : DspMMRel, SHRA_R_PH_ENC, SHRA_R_PH_DESC; +def SHRAV_R_PH : DspMMRel, SHRAV_R_PH_ENC, SHRAV_R_PH_DESC; +def SHLL_S_W : DspMMRel, SHLL_S_W_ENC, SHLL_S_W_DESC; +def SHLLV_S_W : DspMMRel, SHLLV_S_W_ENC, SHLLV_S_W_DESC; +def SHRA_R_W : DspMMRel, SHRA_R_W_ENC, SHRA_R_W_DESC; +def SHRAV_R_W : DspMMRel, SHRAV_R_W_ENC, SHRAV_R_W_DESC; +def MULEU_S_PH_QBL : DspMMRel, MULEU_S_PH_QBL_ENC, MULEU_S_PH_QBL_DESC; +def MULEU_S_PH_QBR : DspMMRel, MULEU_S_PH_QBR_ENC, MULEU_S_PH_QBR_DESC; +def MULEQ_S_W_PHL : DspMMRel, MULEQ_S_W_PHL_ENC, MULEQ_S_W_PHL_DESC; +def MULEQ_S_W_PHR : DspMMRel, MULEQ_S_W_PHR_ENC, MULEQ_S_W_PHR_DESC; +def MULQ_RS_PH : DspMMRel, MULQ_RS_PH_ENC, MULQ_RS_PH_DESC; +def MULSAQ_S_W_PH : DspMMRel, MULSAQ_S_W_PH_ENC, MULSAQ_S_W_PH_DESC; +def MAQ_S_W_PHL : DspMMRel, MAQ_S_W_PHL_ENC, MAQ_S_W_PHL_DESC; +def MAQ_S_W_PHR : DspMMRel, MAQ_S_W_PHR_ENC, MAQ_S_W_PHR_DESC; +def MAQ_SA_W_PHL : DspMMRel, MAQ_SA_W_PHL_ENC, MAQ_SA_W_PHL_DESC; +def MAQ_SA_W_PHR : DspMMRel, MAQ_SA_W_PHR_ENC, MAQ_SA_W_PHR_DESC; +def MFHI_DSP : DspMMRel, MFHI_ENC, MFHI_DESC; +def MFLO_DSP : DspMMRel, MFLO_ENC, MFLO_DESC; +def MTHI_DSP : DspMMRel, MTHI_ENC, MTHI_DESC; +def MTLO_DSP : DspMMRel, MTLO_ENC, MTLO_DESC; +def DPAU_H_QBL : DspMMRel, DPAU_H_QBL_ENC, DPAU_H_QBL_DESC; +def DPAU_H_QBR : DspMMRel, DPAU_H_QBR_ENC, DPAU_H_QBR_DESC; +def DPSU_H_QBL : DspMMRel, DPSU_H_QBL_ENC, DPSU_H_QBL_DESC; +def DPSU_H_QBR : DspMMRel, DPSU_H_QBR_ENC, DPSU_H_QBR_DESC; +def DPAQ_S_W_PH : DspMMRel, DPAQ_S_W_PH_ENC, DPAQ_S_W_PH_DESC; +def DPSQ_S_W_PH : DspMMRel, DPSQ_S_W_PH_ENC, DPSQ_S_W_PH_DESC; +def DPAQ_SA_L_W : DspMMRel, DPAQ_SA_L_W_ENC, DPAQ_SA_L_W_DESC; +def DPSQ_SA_L_W : DspMMRel, DPSQ_SA_L_W_ENC, DPSQ_SA_L_W_DESC; +def MULT_DSP : DspMMRel, MULT_DSP_ENC, MULT_DSP_DESC; +def MULTU_DSP : DspMMRel, MULTU_DSP_ENC, MULTU_DSP_DESC; +def MADD_DSP : DspMMRel, MADD_DSP_ENC, MADD_DSP_DESC; +def MADDU_DSP : DspMMRel, MADDU_DSP_ENC, MADDU_DSP_DESC; +def MSUB_DSP : DspMMRel, MSUB_DSP_ENC, MSUB_DSP_DESC; +def MSUBU_DSP : DspMMRel, MSUBU_DSP_ENC, MSUBU_DSP_DESC; +def CMPU_EQ_QB : DspMMRel, CMPU_EQ_QB_ENC, CMPU_EQ_QB_DESC; +def CMPU_LT_QB : DspMMRel, CMPU_LT_QB_ENC, CMPU_LT_QB_DESC; +def CMPU_LE_QB : DspMMRel, CMPU_LE_QB_ENC, CMPU_LE_QB_DESC; +def CMPGU_EQ_QB : DspMMRel, CMPGU_EQ_QB_ENC, CMPGU_EQ_QB_DESC; +def CMPGU_LT_QB : DspMMRel, CMPGU_LT_QB_ENC, CMPGU_LT_QB_DESC; +def CMPGU_LE_QB : DspMMRel, CMPGU_LE_QB_ENC, CMPGU_LE_QB_DESC; +def CMP_EQ_PH : DspMMRel, CMP_EQ_PH_ENC, CMP_EQ_PH_DESC; +def CMP_LT_PH : DspMMRel, CMP_LT_PH_ENC, CMP_LT_PH_DESC; +def CMP_LE_PH : DspMMRel, CMP_LE_PH_ENC, CMP_LE_PH_DESC; +def BITREV : DspMMRel, BITREV_ENC, BITREV_DESC; +def PACKRL_PH : DspMMRel, PACKRL_PH_ENC, PACKRL_PH_DESC; +def REPL_QB : DspMMRel, REPL_QB_ENC, REPL_QB_DESC; +def REPL_PH : DspMMRel, REPL_PH_ENC, REPL_PH_DESC; +def REPLV_QB : DspMMRel, REPLV_QB_ENC, REPLV_QB_DESC; +def REPLV_PH : DspMMRel, REPLV_PH_ENC, REPLV_PH_DESC; +def PICK_QB : DspMMRel, PICK_QB_ENC, PICK_QB_DESC; +def PICK_PH : DspMMRel, PICK_PH_ENC, PICK_PH_DESC; +def LWX : DspMMRel, LWX_ENC, LWX_DESC; +def LHX : DspMMRel, LHX_ENC, LHX_DESC; +def LBUX : DspMMRel, LBUX_ENC, LBUX_DESC; +let AdditionalPredicates = [NotInMicroMaxis] in { + def BPOSGE32 : DspMMRel, BPOSGE32_ENC, BPOSGE32_DESC; +} +def INSV : DspMMRel, INSV_ENC, INSV_DESC; +def EXTP : DspMMRel, EXTP_ENC, EXTP_DESC; +def EXTPV : DspMMRel, EXTPV_ENC, EXTPV_DESC; +def EXTPDP : DspMMRel, EXTPDP_ENC, EXTPDP_DESC; +def EXTPDPV : DspMMRel, EXTPDPV_ENC, EXTPDPV_DESC; +def EXTR_W : DspMMRel, EXTR_W_ENC, EXTR_W_DESC; +def EXTRV_W : DspMMRel, EXTRV_W_ENC, EXTRV_W_DESC; +def EXTR_R_W : DspMMRel, EXTR_R_W_ENC, EXTR_R_W_DESC; +def EXTRV_R_W : DspMMRel, EXTRV_R_W_ENC, EXTRV_R_W_DESC; +def EXTR_RS_W : DspMMRel, EXTR_RS_W_ENC, EXTR_RS_W_DESC; +def EXTRV_RS_W : DspMMRel, EXTRV_RS_W_ENC, EXTRV_RS_W_DESC; +def EXTR_S_H : DspMMRel, EXTR_S_H_ENC, EXTR_S_H_DESC; +def EXTRV_S_H : DspMMRel, EXTRV_S_H_ENC, EXTRV_S_H_DESC; +def SHILO : DspMMRel, SHILO_ENC, SHILO_DESC; +def SHILOV : DspMMRel, SHILOV_ENC, SHILOV_DESC; +def MTHLIP : DspMMRel, MTHLIP_ENC, MTHLIP_DESC; +def RDDSP : DspMMRel, RDDSP_ENC, RDDSP_DESC; +let AdditionalPredicates = [NotInMicroMaxis] in { + def WRDSP : WRDSP_ENC, WRDSP_DESC; +} + +// MAXIS DSP Rev 2 +def ADDU_PH : DspMMRel, ADDU_PH_ENC, ADDU_PH_DESC, ISA_DSPR2; +def ADDU_S_PH : DspMMRel, ADDU_S_PH_ENC, ADDU_S_PH_DESC, ISA_DSPR2; +def SUBU_PH : DspMMRel, SUBU_PH_ENC, SUBU_PH_DESC, ISA_DSPR2; +def SUBU_S_PH : DspMMRel, SUBU_S_PH_ENC, SUBU_S_PH_DESC, ISA_DSPR2; +def CMPGDU_EQ_QB : DspMMRel, CMPGDU_EQ_QB_ENC, CMPGDU_EQ_QB_DESC, ISA_DSPR2; +def CMPGDU_LT_QB : DspMMRel, CMPGDU_LT_QB_ENC, CMPGDU_LT_QB_DESC, ISA_DSPR2; +def CMPGDU_LE_QB : DspMMRel, CMPGDU_LE_QB_ENC, CMPGDU_LE_QB_DESC, ISA_DSPR2; +def ABSQ_S_QB : DspMMRel, ABSQ_S_QB_ENC, ABSQ_S_QB_DESC, ISA_DSPR2; +def ADDUH_QB : DspMMRel, ADDUH_QB_ENC, ADDUH_QB_DESC, ISA_DSPR2; +def ADDUH_R_QB : DspMMRel, ADDUH_R_QB_ENC, ADDUH_R_QB_DESC, ISA_DSPR2; +def SUBUH_QB : DspMMRel, SUBUH_QB_ENC, SUBUH_QB_DESC, ISA_DSPR2; +def SUBUH_R_QB : DspMMRel, SUBUH_R_QB_ENC, SUBUH_R_QB_DESC, ISA_DSPR2; +def ADDQH_PH : DspMMRel, ADDQH_PH_ENC, ADDQH_PH_DESC, ISA_DSPR2; +def ADDQH_R_PH : DspMMRel, ADDQH_R_PH_ENC, ADDQH_R_PH_DESC, ISA_DSPR2; +def SUBQH_PH : DspMMRel, SUBQH_PH_ENC, SUBQH_PH_DESC, ISA_DSPR2; +def SUBQH_R_PH : DspMMRel, SUBQH_R_PH_ENC, SUBQH_R_PH_DESC, ISA_DSPR2; +def ADDQH_W : DspMMRel, ADDQH_W_ENC, ADDQH_W_DESC, ISA_DSPR2; +def ADDQH_R_W : DspMMRel, ADDQH_R_W_ENC, ADDQH_R_W_DESC, ISA_DSPR2; +def SUBQH_W : DspMMRel, SUBQH_W_ENC, SUBQH_W_DESC, ISA_DSPR2; +def SUBQH_R_W : DspMMRel, SUBQH_R_W_ENC, SUBQH_R_W_DESC, ISA_DSPR2; +def MUL_PH : DspMMRel, MUL_PH_ENC, MUL_PH_DESC, ISA_DSPR2; +def MUL_S_PH : DspMMRel, MUL_S_PH_ENC, MUL_S_PH_DESC, ISA_DSPR2; +def MULQ_S_W : DspMMRel, MULQ_S_W_ENC, MULQ_S_W_DESC, ISA_DSPR2; +def MULQ_RS_W : DspMMRel, MULQ_RS_W_ENC, MULQ_RS_W_DESC, ISA_DSPR2; +def MULQ_S_PH : DspMMRel, MULQ_S_PH_ENC, MULQ_S_PH_DESC, ISA_DSPR2; +def DPA_W_PH : DspMMRel, DPA_W_PH_ENC, DPA_W_PH_DESC, ISA_DSPR2; +def DPS_W_PH : DspMMRel, DPS_W_PH_ENC, DPS_W_PH_DESC, ISA_DSPR2; +def DPAQX_S_W_PH : DspMMRel, DPAQX_S_W_PH_ENC, DPAQX_S_W_PH_DESC, ISA_DSPR2; +def DPAQX_SA_W_PH : DspMMRel, DPAQX_SA_W_PH_ENC, DPAQX_SA_W_PH_DESC, ISA_DSPR2; +def DPAX_W_PH : DspMMRel, DPAX_W_PH_ENC, DPAX_W_PH_DESC, ISA_DSPR2; +def DPSX_W_PH : DspMMRel, DPSX_W_PH_ENC, DPSX_W_PH_DESC, ISA_DSPR2; +def DPSQX_S_W_PH : DspMMRel, DPSQX_S_W_PH_ENC, DPSQX_S_W_PH_DESC, ISA_DSPR2; +def DPSQX_SA_W_PH : DspMMRel, DPSQX_SA_W_PH_ENC, DPSQX_SA_W_PH_DESC, ISA_DSPR2; +def MULSA_W_PH : DspMMRel, MULSA_W_PH_ENC, MULSA_W_PH_DESC, ISA_DSPR2; +def PRECR_QB_PH : DspMMRel, PRECR_QB_PH_ENC, PRECR_QB_PH_DESC, ISA_DSPR2; +def PRECR_SRA_PH_W : DspMMRel, PRECR_SRA_PH_W_ENC, PRECR_SRA_PH_W_DESC, ISA_DSPR2; +def PRECR_SRA_R_PH_W : DspMMRel, PRECR_SRA_R_PH_W_ENC, PRECR_SRA_R_PH_W_DESC, ISA_DSPR2; +def SHRA_QB : DspMMRel, SHRA_QB_ENC, SHRA_QB_DESC, ISA_DSPR2; +def SHRAV_QB : DspMMRel, SHRAV_QB_ENC, SHRAV_QB_DESC, ISA_DSPR2; +def SHRA_R_QB : DspMMRel, SHRA_R_QB_ENC, SHRA_R_QB_DESC, ISA_DSPR2; +def SHRAV_R_QB : DspMMRel, SHRAV_R_QB_ENC, SHRAV_R_QB_DESC, ISA_DSPR2; +def SHRL_PH : DspMMRel, SHRL_PH_ENC, SHRL_PH_DESC, ISA_DSPR2; +def SHRLV_PH : DspMMRel, SHRLV_PH_ENC, SHRLV_PH_DESC, ISA_DSPR2; +def APPEND : DspMMRel, APPEND_ENC, APPEND_DESC, ISA_DSPR2; +def BALIGN : DspMMRel, BALIGN_ENC, BALIGN_DESC, ISA_DSPR2; +def PREPEND : DspMMRel, PREPEND_ENC, PREPEND_DESC, ISA_DSPR2; + +// Pseudos. +let isPseudo = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { + // Pseudo instructions for loading and storing accumulator registers. + def LOAD_ACC64DSP : Load<"", ACC64DSPOpnd>; + def STORE_ACC64DSP : Store<"", ACC64DSPOpnd>; + + // Pseudos for loading and storing ccond field of DSP control register. + def LOAD_CCOND_DSP : Load<"load_ccond_dsp", DSPCC>; + def STORE_CCOND_DSP : Store<"store_ccond_dsp", DSPCC>; +} + +let DecoderNamespace = "MaxisDSP", Arch = "dsp", + AdditionalPredicates = [HasDSP] in { + def LWDSP : Load<"lw", DSPROpnd, null_frag, II_LW>, DspMMRel, LW_FM<0x23>; + def SWDSP : Store<"sw", DSPROpnd, null_frag, II_SW>, DspMMRel, LW_FM<0x2b>; +} + +// Pseudo CMP and PICK instructions. +class PseudoCMP : + PseudoDSP<(outs DSPCC:$cmp), (ins DSPROpnd:$rs, DSPROpnd:$rt), []>, + PseudoInstExpansion<(RealInst DSPROpnd:$rs, DSPROpnd:$rt)>, NeverHasSideEffects; + +class PseudoPICK : + PseudoDSP<(outs DSPROpnd:$rd), (ins DSPCC:$cmp, DSPROpnd:$rs, DSPROpnd:$rt), []>, + PseudoInstExpansion<(RealInst DSPROpnd:$rd, DSPROpnd:$rs, DSPROpnd:$rt)>, + NeverHasSideEffects; + +def PseudoCMP_EQ_PH : PseudoCMP; +def PseudoCMP_LT_PH : PseudoCMP; +def PseudoCMP_LE_PH : PseudoCMP; +def PseudoCMPU_EQ_QB : PseudoCMP; +def PseudoCMPU_LT_QB : PseudoCMP; +def PseudoCMPU_LE_QB : PseudoCMP; + +def PseudoPICK_PH : PseudoPICK; +def PseudoPICK_QB : PseudoPICK; + +def PseudoMTLOHI_DSP : PseudoMTLOHI; + +// Patterns. +class DSPPat : + Pat, Requires<[pred]>; + +class BitconvertPat : + DSPPat<(DstVT (bitconvert (SrcVT SrcRC:$src))), + (COPY_TO_REGCLASS SrcRC:$src, DstRC)>; + +def : BitconvertPat; +def : BitconvertPat; +def : BitconvertPat; +def : BitconvertPat; +def : BitconvertPat; +def : BitconvertPat; +def : BitconvertPat; +def : BitconvertPat; + +def : DSPPat<(v2i16 (load addr:$a)), + (v2i16 (COPY_TO_REGCLASS (LW addr:$a), DSPR))>; +def : DSPPat<(v4i8 (load addr:$a)), + (v4i8 (COPY_TO_REGCLASS (LW addr:$a), DSPR))>; +def : DSPPat<(store (v2i16 DSPR:$val), addr:$a), + (SW (COPY_TO_REGCLASS DSPR:$val, GPR32), addr:$a)>; +def : DSPPat<(store (v4i8 DSPR:$val), addr:$a), + (SW (COPY_TO_REGCLASS DSPR:$val, GPR32), addr:$a)>; + +// Binary operations. +class DSPBinPat : + DSPPat<(Node ValTy:$a, ValTy:$b), (Inst ValTy:$a, ValTy:$b), Pred>; + +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; +def : DSPBinPat; + +// Shift immediate patterns. +class DSPShiftPat : + DSPPat<(Node ValTy:$a, Imm:$shamt), (Inst ValTy:$a, Imm:$shamt), Pred>; + +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; +def : DSPShiftPat; + +// SETCC/SELECT_CC patterns. +class DSPSetCCPat : + DSPPat<(ValTy (MaxisSETCC_DSP ValTy:$a, ValTy:$b, CC)), + (ValTy (Pick (ValTy (Cmp ValTy:$a, ValTy:$b)), + (ValTy (COPY_TO_REGCLASS (ADDiu ZERO, -1), DSPR)), + (ValTy ZERO)))>; + +class DSPSetCCPatInv : + DSPPat<(ValTy (MaxisSETCC_DSP ValTy:$a, ValTy:$b, CC)), + (ValTy (Pick (ValTy (Cmp ValTy:$a, ValTy:$b)), + (ValTy ZERO), + (ValTy (COPY_TO_REGCLASS (ADDiu ZERO, -1), DSPR))))>; + +class DSPSelectCCPat : + DSPPat<(ValTy (MaxisSELECT_CC_DSP ValTy:$a, ValTy:$b, ValTy:$c, ValTy:$d, CC)), + (ValTy (Pick (ValTy (Cmp ValTy:$a, ValTy:$b)), $c, $d))>; + +class DSPSelectCCPatInv : + DSPPat<(ValTy (MaxisSELECT_CC_DSP ValTy:$a, ValTy:$b, ValTy:$c, ValTy:$d, CC)), + (ValTy (Pick (ValTy (Cmp ValTy:$a, ValTy:$b)), $d, $c))>; + +def : DSPSetCCPat; +def : DSPSetCCPat; +def : DSPSetCCPat; +def : DSPSetCCPatInv; +def : DSPSetCCPatInv; +def : DSPSetCCPatInv; +def : DSPSetCCPat; +def : DSPSetCCPat; +def : DSPSetCCPat; +def : DSPSetCCPatInv; +def : DSPSetCCPatInv; +def : DSPSetCCPatInv; + +def : DSPSelectCCPat; +def : DSPSelectCCPat; +def : DSPSelectCCPat; +def : DSPSelectCCPatInv; +def : DSPSelectCCPatInv; +def : DSPSelectCCPatInv; +def : DSPSelectCCPat; +def : DSPSelectCCPat; +def : DSPSelectCCPat; +def : DSPSelectCCPatInv; +def : DSPSelectCCPatInv; +def : DSPSelectCCPatInv; + +// Extr patterns. +class EXTR_W_TY1_R2_Pat : + DSPPat<(i32 (OpNode GPR32:$rs, ACC64DSP:$ac)), + (Instr ACC64DSP:$ac, GPR32:$rs)>; + +class EXTR_W_TY1_R1_Pat : + DSPPat<(i32 (OpNode immZExt5:$shift, ACC64DSP:$ac)), + (Instr ACC64DSP:$ac, immZExt5:$shift)>; + +def : EXTR_W_TY1_R1_Pat; +def : EXTR_W_TY1_R2_Pat; +def : EXTR_W_TY1_R1_Pat; +def : EXTR_W_TY1_R2_Pat; +def : EXTR_W_TY1_R1_Pat; +def : EXTR_W_TY1_R2_Pat; +def : EXTR_W_TY1_R1_Pat; +def : EXTR_W_TY1_R2_Pat; +def : EXTR_W_TY1_R1_Pat; +def : EXTR_W_TY1_R2_Pat; +def : EXTR_W_TY1_R1_Pat; +def : EXTR_W_TY1_R2_Pat; + +// Indexed load patterns. +class IndexedLoadPat : + DSPPat<(i32 (LoadNode (add i32:$base, i32:$index))), + (Instr i32:$base, i32:$index)>; + +let AddedComplexity = 20 in { + def : IndexedLoadPat; + def : IndexedLoadPat; + def : IndexedLoadPat; +} + +// Instruction alias. +let AdditionalPredicates = [NotInMicroMaxis] in { + def : DSPInstAlias<"wrdsp $rt", (WRDSP GPR32Opnd:$rt, 0x1F), 1>; +} diff --git a/lib/Target/Maxis/MaxisDelaySlotFiller.cpp b/lib/Target/Maxis/MaxisDelaySlotFiller.cpp new file mode 100644 index 00000000..cd9d33e7 --- /dev/null +++ b/lib/Target/Maxis/MaxisDelaySlotFiller.cpp @@ -0,0 +1,912 @@ +//===- MaxisDelaySlotFiller.cpp - Maxis Delay Slot Filler -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Simple pass to fill delay slots with useful instructions. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/MaxisMCNaCl.h" +#include "Maxis.h" +#include "MaxisInstrInfo.h" +#include "MaxisRegisterInfo.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Analysis/ValueTracking.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineBranchProbabilityInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/PseudoSourceValue.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Target/TargetMachine.h" +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "delay-slot-filler" + +STATISTIC(FilledSlots, "Number of delay slots filled"); +STATISTIC(UsefulSlots, "Number of delay slots filled with instructions that" + " are not NOP."); + +static cl::opt DisableDelaySlotFiller( + "disable-maxis-delay-filler", + cl::init(false), + cl::desc("Fill all delay slots with NOPs."), + cl::Hidden); + +static cl::opt DisableForwardSearch( + "disable-maxis-df-forward-search", + cl::init(true), + cl::desc("Disallow MAXIS delay filler to search forward."), + cl::Hidden); + +static cl::opt DisableSuccBBSearch( + "disable-maxis-df-succbb-search", + cl::init(true), + cl::desc("Disallow MAXIS delay filler to search successor basic blocks."), + cl::Hidden); + +static cl::opt DisableBackwardSearch( + "disable-maxis-df-backward-search", + cl::init(false), + cl::desc("Disallow MAXIS delay filler to search backward."), + cl::Hidden); + +enum CompactBranchPolicy { + CB_Never, ///< The policy 'never' may in some circumstances or for some + ///< ISAs not be absolutely adhered to. + CB_Optimal, ///< Optimal is the default and will produce compact branches + ///< when delay slots cannot be filled. + CB_Always ///< 'always' may in some circumstances may not be + ///< absolutely adhered to there may not be a corresponding + ///< compact form of a branch. +}; + +static cl::opt MaxisCompactBranchPolicy( + "maxis-compact-branches",cl::Optional, + cl::init(CB_Optimal), + cl::desc("MAXIS Specific: Compact branch policy."), + cl::values( + clEnumValN(CB_Never, "never", "Do not use compact branches if possible."), + clEnumValN(CB_Optimal, "optimal", "Use compact branches where appropiate (default)."), + clEnumValN(CB_Always, "always", "Always use compact branches if possible.") + ) +); + +namespace { + + using Iter = MachineBasicBlock::iterator; + using ReverseIter = MachineBasicBlock::reverse_iterator; + using BB2BrMap = SmallDenseMap; + + class RegDefsUses { + public: + RegDefsUses(const TargetRegisterInfo &TRI); + + void init(const MachineInstr &MI); + + /// This function sets all caller-saved registers in Defs. + void setCallerSaved(const MachineInstr &MI); + + /// This function sets all unallocatable registers in Defs. + void setUnallocatableRegs(const MachineFunction &MF); + + /// Set bits in Uses corresponding to MBB's live-out registers except for + /// the registers that are live-in to SuccBB. + void addLiveOut(const MachineBasicBlock &MBB, + const MachineBasicBlock &SuccBB); + + bool update(const MachineInstr &MI, unsigned Begin, unsigned End); + + private: + bool checkRegDefsUses(BitVector &NewDefs, BitVector &NewUses, unsigned Reg, + bool IsDef) const; + + /// Returns true if Reg or its alias is in RegSet. + bool isRegInSet(const BitVector &RegSet, unsigned Reg) const; + + const TargetRegisterInfo &TRI; + BitVector Defs, Uses; + }; + + /// Base class for inspecting loads and stores. + class InspectMemInstr { + public: + InspectMemInstr(bool ForbidMemInstr_) : ForbidMemInstr(ForbidMemInstr_) {} + virtual ~InspectMemInstr() = default; + + /// Return true if MI cannot be moved to delay slot. + bool hasHazard(const MachineInstr &MI); + + protected: + /// Flags indicating whether loads or stores have been seen. + bool OrigSeenLoad = false; + bool OrigSeenStore = false; + bool SeenLoad = false; + bool SeenStore = false; + + /// Memory instructions are not allowed to move to delay slot if this flag + /// is true. + bool ForbidMemInstr; + + private: + virtual bool hasHazard_(const MachineInstr &MI) = 0; + }; + + /// This subclass rejects any memory instructions. + class NoMemInstr : public InspectMemInstr { + public: + NoMemInstr() : InspectMemInstr(true) {} + + private: + bool hasHazard_(const MachineInstr &MI) override { return true; } + }; + + /// This subclass accepts loads from stacks and constant loads. + class LoadFromStackOrConst : public InspectMemInstr { + public: + LoadFromStackOrConst() : InspectMemInstr(false) {} + + private: + bool hasHazard_(const MachineInstr &MI) override; + }; + + /// This subclass uses memory dependence information to determine whether a + /// memory instruction can be moved to a delay slot. + class MemDefsUses : public InspectMemInstr { + public: + MemDefsUses(const DataLayout &DL, const MachineFrameInfo *MFI); + + private: + using ValueType = PointerUnion; + + bool hasHazard_(const MachineInstr &MI) override; + + /// Update Defs and Uses. Return true if there exist dependences that + /// disqualify the delay slot candidate between V and values in Uses and + /// Defs. + bool updateDefsUses(ValueType V, bool MayStore); + + /// Get the list of underlying objects of MI's memory operand. + bool getUnderlyingObjects(const MachineInstr &MI, + SmallVectorImpl &Objects) const; + + const MachineFrameInfo *MFI; + SmallPtrSet Uses, Defs; + const DataLayout &DL; + + /// Flags indicating whether loads or stores with no underlying objects have + /// been seen. + bool SeenNoObjLoad = false; + bool SeenNoObjStore = false; + }; + + class Filler : public MachineFunctionPass { + public: + Filler() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "Maxis Delay Slot Filler"; } + + bool runOnMachineFunction(MachineFunction &F) override { + TM = &F.getTarget(); + bool Changed = false; + for (MachineFunction::iterator FI = F.begin(), FE = F.end(); + FI != FE; ++FI) + Changed |= runOnMachineBasicBlock(*FI); + + // This pass invalidates liveness information when it reorders + // instructions to fill delay slot. Without this, -verify-machineinstrs + // will fail. + if (Changed) + F.getRegInfo().invalidateLiveness(); + + return Changed; + } + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + private: + bool runOnMachineBasicBlock(MachineBasicBlock &MBB); + + Iter replaceWithCompactBranch(MachineBasicBlock &MBB, Iter Branch, + const DebugLoc &DL); + + /// This function checks if it is valid to move Candidate to the delay slot + /// and returns true if it isn't. It also updates memory and register + /// dependence information. + bool delayHasHazard(const MachineInstr &Candidate, RegDefsUses &RegDU, + InspectMemInstr &IM) const; + + /// This function searches range [Begin, End) for an instruction that can be + /// moved to the delay slot. Returns true on success. + template + bool searchRange(MachineBasicBlock &MBB, IterTy Begin, IterTy End, + RegDefsUses &RegDU, InspectMemInstr &IM, Iter Slot, + IterTy &Filler) const; + + /// This function searches in the backward direction for an instruction that + /// can be moved to the delay slot. Returns true on success. + bool searchBackward(MachineBasicBlock &MBB, MachineInstr &Slot) const; + + /// This function searches MBB in the forward direction for an instruction + /// that can be moved to the delay slot. Returns true on success. + bool searchForward(MachineBasicBlock &MBB, Iter Slot) const; + + /// This function searches one of MBB's successor blocks for an instruction + /// that can be moved to the delay slot and inserts clones of the + /// instruction into the successor's predecessor blocks. + bool searchSuccBBs(MachineBasicBlock &MBB, Iter Slot) const; + + /// Pick a successor block of MBB. Return NULL if MBB doesn't have a + /// successor block that is not a landing pad. + MachineBasicBlock *selectSuccBB(MachineBasicBlock &B) const; + + /// This function analyzes MBB and returns an instruction with an unoccupied + /// slot that branches to Dst. + std::pair + getBranch(MachineBasicBlock &MBB, const MachineBasicBlock &Dst) const; + + /// Examine Pred and see if it is possible to insert an instruction into + /// one of its branches delay slot or its end. + bool examinePred(MachineBasicBlock &Pred, const MachineBasicBlock &Succ, + RegDefsUses &RegDU, bool &HasMultipleSuccs, + BB2BrMap &BrMap) const; + + bool terminateSearch(const MachineInstr &Candidate) const; + + const TargetMachine *TM = nullptr; + + static char ID; + }; + +} // end anonymous namespace + +char Filler::ID = 0; + +static bool hasUnoccupiedSlot(const MachineInstr *MI) { + return MI->hasDelaySlot() && !MI->isBundledWithSucc(); +} + +/// This function inserts clones of Filler into predecessor blocks. +static void insertDelayFiller(Iter Filler, const BB2BrMap &BrMap) { + MachineFunction *MF = Filler->getParent()->getParent(); + + for (BB2BrMap::const_iterator I = BrMap.begin(); I != BrMap.end(); ++I) { + if (I->second) { + MIBundleBuilder(I->second).append(MF->CloneMachineInstr(&*Filler)); + ++UsefulSlots; + } else { + I->first->insert(I->first->end(), MF->CloneMachineInstr(&*Filler)); + } + } +} + +/// This function adds registers Filler defines to MBB's live-in register list. +static void addLiveInRegs(Iter Filler, MachineBasicBlock &MBB) { + for (unsigned I = 0, E = Filler->getNumOperands(); I != E; ++I) { + const MachineOperand &MO = Filler->getOperand(I); + unsigned R; + + if (!MO.isReg() || !MO.isDef() || !(R = MO.getReg())) + continue; + +#ifndef NDEBUG + const MachineFunction &MF = *MBB.getParent(); + assert(MF.getSubtarget().getRegisterInfo()->getAllocatableSet(MF).test(R) && + "Shouldn't move an instruction with unallocatable registers across " + "basic block boundaries."); +#endif + + if (!MBB.isLiveIn(R)) + MBB.addLiveIn(R); + } +} + +RegDefsUses::RegDefsUses(const TargetRegisterInfo &TRI) + : TRI(TRI), Defs(TRI.getNumRegs(), false), Uses(TRI.getNumRegs(), false) {} + +void RegDefsUses::init(const MachineInstr &MI) { + // Add all register operands which are explicit and non-variadic. + update(MI, 0, MI.getDesc().getNumOperands()); + + // If MI is a call, add RA to Defs to prevent users of RA from going into + // delay slot. + if (MI.isCall()) + Defs.set(Maxis::RA); + + // Add all implicit register operands of branch instructions except + // register AT. + if (MI.isBranch()) { + update(MI, MI.getDesc().getNumOperands(), MI.getNumOperands()); + Defs.reset(Maxis::AT); + } +} + +void RegDefsUses::setCallerSaved(const MachineInstr &MI) { + assert(MI.isCall()); + + // Add RA/RA_64 to Defs to prevent users of RA/RA_64 from going into + // the delay slot. The reason is that RA/RA_64 must not be changed + // in the delay slot so that the callee can return to the caller. + if (MI.definesRegister(Maxis::RA) || MI.definesRegister(Maxis::RA_64)) { + Defs.set(Maxis::RA); + Defs.set(Maxis::RA_64); + } + + // If MI is a call, add all caller-saved registers to Defs. + BitVector CallerSavedRegs(TRI.getNumRegs(), true); + + CallerSavedRegs.reset(Maxis::ZERO); + CallerSavedRegs.reset(Maxis::ZERO_64); + + for (const MCPhysReg *R = TRI.getCalleeSavedRegs(MI.getParent()->getParent()); + *R; ++R) + for (MCRegAliasIterator AI(*R, &TRI, true); AI.isValid(); ++AI) + CallerSavedRegs.reset(*AI); + + Defs |= CallerSavedRegs; +} + +void RegDefsUses::setUnallocatableRegs(const MachineFunction &MF) { + BitVector AllocSet = TRI.getAllocatableSet(MF); + + for (unsigned R : AllocSet.set_bits()) + for (MCRegAliasIterator AI(R, &TRI, false); AI.isValid(); ++AI) + AllocSet.set(*AI); + + AllocSet.set(Maxis::ZERO); + AllocSet.set(Maxis::ZERO_64); + + Defs |= AllocSet.flip(); +} + +void RegDefsUses::addLiveOut(const MachineBasicBlock &MBB, + const MachineBasicBlock &SuccBB) { + for (MachineBasicBlock::const_succ_iterator SI = MBB.succ_begin(), + SE = MBB.succ_end(); SI != SE; ++SI) + if (*SI != &SuccBB) + for (const auto &LI : (*SI)->liveins()) + Uses.set(LI.PhysReg); +} + +bool RegDefsUses::update(const MachineInstr &MI, unsigned Begin, unsigned End) { + BitVector NewDefs(TRI.getNumRegs()), NewUses(TRI.getNumRegs()); + bool HasHazard = false; + + for (unsigned I = Begin; I != End; ++I) { + const MachineOperand &MO = MI.getOperand(I); + + if (MO.isReg() && MO.getReg()) + HasHazard |= checkRegDefsUses(NewDefs, NewUses, MO.getReg(), MO.isDef()); + } + + Defs |= NewDefs; + Uses |= NewUses; + + return HasHazard; +} + +bool RegDefsUses::checkRegDefsUses(BitVector &NewDefs, BitVector &NewUses, + unsigned Reg, bool IsDef) const { + if (IsDef) { + NewDefs.set(Reg); + // check whether Reg has already been defined or used. + return (isRegInSet(Defs, Reg) || isRegInSet(Uses, Reg)); + } + + NewUses.set(Reg); + // check whether Reg has already been defined. + return isRegInSet(Defs, Reg); +} + +bool RegDefsUses::isRegInSet(const BitVector &RegSet, unsigned Reg) const { + // Check Reg and all aliased Registers. + for (MCRegAliasIterator AI(Reg, &TRI, true); AI.isValid(); ++AI) + if (RegSet.test(*AI)) + return true; + return false; +} + +bool InspectMemInstr::hasHazard(const MachineInstr &MI) { + if (!MI.mayStore() && !MI.mayLoad()) + return false; + + if (ForbidMemInstr) + return true; + + OrigSeenLoad = SeenLoad; + OrigSeenStore = SeenStore; + SeenLoad |= MI.mayLoad(); + SeenStore |= MI.mayStore(); + + // If MI is an ordered or volatile memory reference, disallow moving + // subsequent loads and stores to delay slot. + if (MI.hasOrderedMemoryRef() && (OrigSeenLoad || OrigSeenStore)) { + ForbidMemInstr = true; + return true; + } + + return hasHazard_(MI); +} + +bool LoadFromStackOrConst::hasHazard_(const MachineInstr &MI) { + if (MI.mayStore()) + return true; + + if (!MI.hasOneMemOperand() || !(*MI.memoperands_begin())->getPseudoValue()) + return true; + + if (const PseudoSourceValue *PSV = + (*MI.memoperands_begin())->getPseudoValue()) { + if (isa(PSV)) + return false; + return !PSV->isConstant(nullptr) && !PSV->isStack(); + } + + return true; +} + +MemDefsUses::MemDefsUses(const DataLayout &DL, const MachineFrameInfo *MFI_) + : InspectMemInstr(false), MFI(MFI_), DL(DL) {} + +bool MemDefsUses::hasHazard_(const MachineInstr &MI) { + bool HasHazard = false; + SmallVector Objs; + + // Check underlying object list. + if (getUnderlyingObjects(MI, Objs)) { + for (SmallVectorImpl::const_iterator I = Objs.begin(); + I != Objs.end(); ++I) + HasHazard |= updateDefsUses(*I, MI.mayStore()); + + return HasHazard; + } + + // No underlying objects found. + HasHazard = MI.mayStore() && (OrigSeenLoad || OrigSeenStore); + HasHazard |= MI.mayLoad() || OrigSeenStore; + + SeenNoObjLoad |= MI.mayLoad(); + SeenNoObjStore |= MI.mayStore(); + + return HasHazard; +} + +bool MemDefsUses::updateDefsUses(ValueType V, bool MayStore) { + if (MayStore) + return !Defs.insert(V).second || Uses.count(V) || SeenNoObjStore || + SeenNoObjLoad; + + Uses.insert(V); + return Defs.count(V) || SeenNoObjStore; +} + +bool MemDefsUses:: +getUnderlyingObjects(const MachineInstr &MI, + SmallVectorImpl &Objects) const { + if (!MI.hasOneMemOperand() || + (!(*MI.memoperands_begin())->getValue() && + !(*MI.memoperands_begin())->getPseudoValue())) + return false; + + if (const PseudoSourceValue *PSV = + (*MI.memoperands_begin())->getPseudoValue()) { + if (!PSV->isAliased(MFI)) + return false; + Objects.push_back(PSV); + return true; + } + + const Value *V = (*MI.memoperands_begin())->getValue(); + + SmallVector Objs; + GetUnderlyingObjects(const_cast(V), Objs, DL); + + for (SmallVectorImpl::iterator I = Objs.begin(), E = Objs.end(); + I != E; ++I) { + if (!isIdentifiedObject(V)) + return false; + + Objects.push_back(*I); + } + + return true; +} + +// Replace Branch with the compact branch instruction. +Iter Filler::replaceWithCompactBranch(MachineBasicBlock &MBB, Iter Branch, + const DebugLoc &DL) { + const MaxisSubtarget &STI = MBB.getParent()->getSubtarget(); + const MaxisInstrInfo *TII = STI.getInstrInfo(); + + unsigned NewOpcode = TII->getEquivalentCompactForm(Branch); + Branch = TII->genInstrWithNewOpc(NewOpcode, Branch); + + std::next(Branch)->eraseFromParent(); + return Branch; +} + +// For given opcode returns opcode of corresponding instruction with short +// delay slot. +// For the pseudo TAILCALL*_MM instructions return the short delay slot +// form. Unfortunately, TAILCALL<->b16 is denied as b16 has a limited range +// that is too short to make use of for tail calls. +static int getEquivalentCallShort(int Opcode) { + switch (Opcode) { + case Maxis::BGEZAL: + return Maxis::BGEZALS_MM; + case Maxis::BLTZAL: + return Maxis::BLTZALS_MM; + case Maxis::JAL: + return Maxis::JALS_MM; + case Maxis::JALR: + return Maxis::JALRS_MM; + case Maxis::JALR16_MM: + return Maxis::JALRS16_MM; + case Maxis::TAILCALL_MM: + llvm_unreachable("Attempting to shorten the TAILCALL_MM pseudo!"); + case Maxis::TAILCALLREG: + return Maxis::JR16_MM; + default: + llvm_unreachable("Unexpected call instruction for microMAXIS."); + } +} + +/// runOnMachineBasicBlock - Fill in delay slots for the given basic block. +/// We assume there is only one delay slot per delayed instruction. +bool Filler::runOnMachineBasicBlock(MachineBasicBlock &MBB) { + bool Changed = false; + const MaxisSubtarget &STI = MBB.getParent()->getSubtarget(); + bool InMicroMaxisMode = STI.inMicroMaxisMode(); + const MaxisInstrInfo *TII = STI.getInstrInfo(); + + for (Iter I = MBB.begin(); I != MBB.end(); ++I) { + if (!hasUnoccupiedSlot(&*I)) + continue; + + // Delay slot filling is disabled at -O0, or in microMAXIS32R6. + if (!DisableDelaySlotFiller && (TM->getOptLevel() != CodeGenOpt::None) && + !(InMicroMaxisMode && STI.hasMaxis32r6())) { + + bool Filled = false; + + if (MaxisCompactBranchPolicy.getValue() != CB_Always || + !TII->getEquivalentCompactForm(I)) { + if (searchBackward(MBB, *I)) { + Filled = true; + } else if (I->isTerminator()) { + if (searchSuccBBs(MBB, I)) { + Filled = true; + } + } else if (searchForward(MBB, I)) { + Filled = true; + } + } + + if (Filled) { + // Get instruction with delay slot. + MachineBasicBlock::instr_iterator DSI = I.getInstrIterator(); + + if (InMicroMaxisMode && TII->getInstSizeInBytes(*std::next(DSI)) == 2 && + DSI->isCall()) { + // If instruction in delay slot is 16b change opcode to + // corresponding instruction with short delay slot. + + // TODO: Implement an instruction mapping table of 16bit opcodes to + // 32bit opcodes so that an instruction can be expanded. This would + // save 16 bits as a TAILCALL_MM pseudo requires a fullsized nop. + // TODO: Permit b16 when branching backwards to the the same function + // if it is in range. + DSI->setDesc(TII->get(getEquivalentCallShort(DSI->getOpcode()))); + } + ++FilledSlots; + Changed = true; + continue; + } + } + + // For microMAXIS if instruction is BEQ or BNE with one ZERO register, then + // instead of adding NOP replace this instruction with the corresponding + // compact branch instruction, i.e. BEQZC or BNEZC. Additionally + // PseudoReturn and PseudoIndirectBranch are expanded to JR_MM, so they can + // be replaced with JRC16_MM. + + // For MAXISR6 attempt to produce the corresponding compact (no delay slot) + // form of the CTI. For indirect jumps this will not require inserting a + // NOP and for branches will hopefully avoid requiring a NOP. + if ((InMicroMaxisMode || + (STI.hasMaxis32r6() && MaxisCompactBranchPolicy != CB_Never)) && + TII->getEquivalentCompactForm(I)) { + I = replaceWithCompactBranch(MBB, I, I->getDebugLoc()); + Changed = true; + continue; + } + + // Bundle the NOP to the instruction with the delay slot. + BuildMI(MBB, std::next(I), I->getDebugLoc(), TII->get(Maxis::NOP)); + MIBundleBuilder(MBB, I, std::next(I, 2)); + ++FilledSlots; + Changed = true; + } + + return Changed; +} + +template +bool Filler::searchRange(MachineBasicBlock &MBB, IterTy Begin, IterTy End, + RegDefsUses &RegDU, InspectMemInstr& IM, Iter Slot, + IterTy &Filler) const { + for (IterTy I = Begin; I != End;) { + IterTy CurrI = I; + ++I; + + // skip debug value + if (CurrI->isDebugValue()) + continue; + + if (terminateSearch(*CurrI)) + break; + + assert((!CurrI->isCall() && !CurrI->isReturn() && !CurrI->isBranch()) && + "Cannot put calls, returns or branches in delay slot."); + + if (CurrI->isKill()) { + CurrI->eraseFromParent(); + continue; + } + + if (delayHasHazard(*CurrI, RegDU, IM)) + continue; + + const MaxisSubtarget &STI = MBB.getParent()->getSubtarget(); + if (STI.isTargetNaCl()) { + // In NaCl, instructions that must be masked are forbidden in delay slots. + // We only check for loads, stores and SP changes. Calls, returns and + // branches are not checked because non-NaCl targets never put them in + // delay slots. + unsigned AddrIdx; + if ((isBasePlusOffsetMemoryAccess(CurrI->getOpcode(), &AddrIdx) && + baseRegNeedsLoadStoreMask(CurrI->getOperand(AddrIdx).getReg())) || + CurrI->modifiesRegister(Maxis::SP, STI.getRegisterInfo())) + continue; + } + + bool InMicroMaxisMode = STI.inMicroMaxisMode(); + const MaxisInstrInfo *TII = STI.getInstrInfo(); + unsigned Opcode = (*Slot).getOpcode(); + // This is complicated by the tail call optimization. For non-PIC code + // there is only a 32bit sized unconditional branch which can be assumed + // to be able to reach the target. b16 only has a range of +/- 1 KB. + // It's entirely possible that the target function is reachable with b16 + // but we don't have enough information to make that decision. + if (InMicroMaxisMode && TII->getInstSizeInBytes(*CurrI) == 2 && + (Opcode == Maxis::JR || Opcode == Maxis::PseudoIndirectBranch || + Opcode == Maxis::PseudoReturn || Opcode == Maxis::TAILCALL)) + continue; + + Filler = CurrI; + return true; + } + + return false; +} + +bool Filler::searchBackward(MachineBasicBlock &MBB, MachineInstr &Slot) const { + if (DisableBackwardSearch) + return false; + + auto *Fn = MBB.getParent(); + RegDefsUses RegDU(*Fn->getSubtarget().getRegisterInfo()); + MemDefsUses MemDU(Fn->getDataLayout(), &Fn->getFrameInfo()); + ReverseIter Filler; + + RegDU.init(Slot); + + MachineBasicBlock::iterator SlotI = Slot; + if (!searchRange(MBB, ++SlotI.getReverse(), MBB.rend(), RegDU, MemDU, Slot, + Filler)) + return false; + + MBB.splice(std::next(SlotI), &MBB, Filler.getReverse()); + MIBundleBuilder(MBB, SlotI, std::next(SlotI, 2)); + ++UsefulSlots; + return true; +} + +bool Filler::searchForward(MachineBasicBlock &MBB, Iter Slot) const { + // Can handle only calls. + if (DisableForwardSearch || !Slot->isCall()) + return false; + + RegDefsUses RegDU(*MBB.getParent()->getSubtarget().getRegisterInfo()); + NoMemInstr NM; + Iter Filler; + + RegDU.setCallerSaved(*Slot); + + if (!searchRange(MBB, std::next(Slot), MBB.end(), RegDU, NM, Slot, Filler)) + return false; + + MBB.splice(std::next(Slot), &MBB, Filler); + MIBundleBuilder(MBB, Slot, std::next(Slot, 2)); + ++UsefulSlots; + return true; +} + +bool Filler::searchSuccBBs(MachineBasicBlock &MBB, Iter Slot) const { + if (DisableSuccBBSearch) + return false; + + MachineBasicBlock *SuccBB = selectSuccBB(MBB); + + if (!SuccBB) + return false; + + RegDefsUses RegDU(*MBB.getParent()->getSubtarget().getRegisterInfo()); + bool HasMultipleSuccs = false; + BB2BrMap BrMap; + std::unique_ptr IM; + Iter Filler; + auto *Fn = MBB.getParent(); + + // Iterate over SuccBB's predecessor list. + for (MachineBasicBlock::pred_iterator PI = SuccBB->pred_begin(), + PE = SuccBB->pred_end(); PI != PE; ++PI) + if (!examinePred(**PI, *SuccBB, RegDU, HasMultipleSuccs, BrMap)) + return false; + + // Do not allow moving instructions which have unallocatable register operands + // across basic block boundaries. + RegDU.setUnallocatableRegs(*Fn); + + // Only allow moving loads from stack or constants if any of the SuccBB's + // predecessors have multiple successors. + if (HasMultipleSuccs) { + IM.reset(new LoadFromStackOrConst()); + } else { + const MachineFrameInfo &MFI = Fn->getFrameInfo(); + IM.reset(new MemDefsUses(Fn->getDataLayout(), &MFI)); + } + + if (!searchRange(MBB, SuccBB->begin(), SuccBB->end(), RegDU, *IM, Slot, + Filler)) + return false; + + insertDelayFiller(Filler, BrMap); + addLiveInRegs(Filler, *SuccBB); + Filler->eraseFromParent(); + + return true; +} + +MachineBasicBlock *Filler::selectSuccBB(MachineBasicBlock &B) const { + if (B.succ_empty()) + return nullptr; + + // Select the successor with the larget edge weight. + auto &Prob = getAnalysis(); + MachineBasicBlock *S = *std::max_element( + B.succ_begin(), B.succ_end(), + [&](const MachineBasicBlock *Dst0, const MachineBasicBlock *Dst1) { + return Prob.getEdgeProbability(&B, Dst0) < + Prob.getEdgeProbability(&B, Dst1); + }); + return S->isEHPad() ? nullptr : S; +} + +std::pair +Filler::getBranch(MachineBasicBlock &MBB, const MachineBasicBlock &Dst) const { + const MaxisInstrInfo *TII = + MBB.getParent()->getSubtarget().getInstrInfo(); + MachineBasicBlock *TrueBB = nullptr, *FalseBB = nullptr; + SmallVector BranchInstrs; + SmallVector Cond; + + MaxisInstrInfo::BranchType R = + TII->analyzeBranch(MBB, TrueBB, FalseBB, Cond, false, BranchInstrs); + + if ((R == MaxisInstrInfo::BT_None) || (R == MaxisInstrInfo::BT_NoBranch)) + return std::make_pair(R, nullptr); + + if (R != MaxisInstrInfo::BT_CondUncond) { + if (!hasUnoccupiedSlot(BranchInstrs[0])) + return std::make_pair(MaxisInstrInfo::BT_None, nullptr); + + assert(((R != MaxisInstrInfo::BT_Uncond) || (TrueBB == &Dst))); + + return std::make_pair(R, BranchInstrs[0]); + } + + assert((TrueBB == &Dst) || (FalseBB == &Dst)); + + // Examine the conditional branch. See if its slot is occupied. + if (hasUnoccupiedSlot(BranchInstrs[0])) + return std::make_pair(MaxisInstrInfo::BT_Cond, BranchInstrs[0]); + + // If that fails, try the unconditional branch. + if (hasUnoccupiedSlot(BranchInstrs[1]) && (FalseBB == &Dst)) + return std::make_pair(MaxisInstrInfo::BT_Uncond, BranchInstrs[1]); + + return std::make_pair(MaxisInstrInfo::BT_None, nullptr); +} + +bool Filler::examinePred(MachineBasicBlock &Pred, const MachineBasicBlock &Succ, + RegDefsUses &RegDU, bool &HasMultipleSuccs, + BB2BrMap &BrMap) const { + std::pair P = + getBranch(Pred, Succ); + + // Return if either getBranch wasn't able to analyze the branches or there + // were no branches with unoccupied slots. + if (P.first == MaxisInstrInfo::BT_None) + return false; + + if ((P.first != MaxisInstrInfo::BT_Uncond) && + (P.first != MaxisInstrInfo::BT_NoBranch)) { + HasMultipleSuccs = true; + RegDU.addLiveOut(Pred, Succ); + } + + BrMap[&Pred] = P.second; + return true; +} + +bool Filler::delayHasHazard(const MachineInstr &Candidate, RegDefsUses &RegDU, + InspectMemInstr &IM) const { + assert(!Candidate.isKill() && + "KILL instructions should have been eliminated at this point."); + + bool HasHazard = Candidate.isImplicitDef(); + + HasHazard |= IM.hasHazard(Candidate); + HasHazard |= RegDU.update(Candidate, 0, Candidate.getNumOperands()); + + return HasHazard; +} + +bool Filler::terminateSearch(const MachineInstr &Candidate) const { + return (Candidate.isTerminator() || Candidate.isCall() || + Candidate.isPosition() || Candidate.isInlineAsm() || + Candidate.hasUnmodeledSideEffects()); +} + +/// createMaxisDelaySlotFillerPass - Returns a pass that fills in delay +/// slots in Maxis MachineFunctions +FunctionPass *llvm::createMaxisDelaySlotFillerPass() { return new Filler(); } diff --git a/lib/Target/Maxis/MaxisEVAInstrFormats.td b/lib/Target/Maxis/MaxisEVAInstrFormats.td new file mode 100644 index 00000000..f0746217 --- /dev/null +++ b/lib/Target/Maxis/MaxisEVAInstrFormats.td @@ -0,0 +1,84 @@ +//===- MaxisEVAInstrFormats.td - Maxis Instruction Formats ---*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis32r6 instruction formats. +// +//===----------------------------------------------------------------------===// + +class MaxisEVAInst : MaxisInst<(outs), (ins), "", [], NoItinerary, FrmOther>, + PredicateControl, StdArch { + let DecoderNamespace = "Maxis"; + let EncodingPredicates = [HasStdEnc]; +} + +//===----------------------------------------------------------------------===// +// +// Field Values +// +//===----------------------------------------------------------------------===// + +// Memory Load/Store EVA +def OPCODE6_LBE : OPCODE6<0b101100>; +def OPCODE6_LBuE : OPCODE6<0b101000>; +def OPCODE6_LHE : OPCODE6<0b101101>; +def OPCODE6_LHuE : OPCODE6<0b101001>; +def OPCODE6_LWE : OPCODE6<0b101111>; + +def OPCODE6_SBE : OPCODE6<0b011100>; +def OPCODE6_SHE : OPCODE6<0b011101>; +def OPCODE6_SWE : OPCODE6<0b011111>; + +// load/store left/right EVA +def OPCODE6_LWLE : OPCODE6<0b011001>; +def OPCODE6_LWRE : OPCODE6<0b011010>; +def OPCODE6_SWLE : OPCODE6<0b100001>; +def OPCODE6_SWRE : OPCODE6<0b100010>; + +// Load-linked EVA, Store-conditional EVA +def OPCODE6_LLE : OPCODE6<0b101110>; +def OPCODE6_SCE : OPCODE6<0b011110>; + +def OPCODE6_TLBINV : OPCODE6<0b000011>; +def OPCODE6_TLBINVF : OPCODE6<0b000100>; + +def OPCODE6_CACHEE : OPCODE6<0b011011>; +def OPCODE6_PREFE : OPCODE6<0b100011>; + +def OPGROUP_COP0_TLB : OPGROUP<0b010000>; + +//===----------------------------------------------------------------------===// +// +// Encoding Formats +// +//===----------------------------------------------------------------------===// + +class SPECIAL3_EVA_LOAD_STORE_FM : MaxisEVAInst { + bits<21> addr; + bits<5> hint; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = OPGROUP_SPECIAL3.Value; + let Inst{25-21} = base; + let Inst{20-16} = hint; + let Inst{15-7} = offset; + let Inst{6} = 0; + let Inst{5-0} = Operation.Value; +} + +class TLB_FM : MaxisEVAInst { + bits<32> Inst; + + let Inst{31-26} = OPGROUP_COP0_TLB.Value; + let Inst{25} = 1; // CO + let Inst{24-6} = 0; + let Inst{5-0} = Operation.Value; +} diff --git a/lib/Target/Maxis/MaxisEVAInstrInfo.td b/lib/Target/Maxis/MaxisEVAInstrInfo.td new file mode 100644 index 00000000..70f6cbc7 --- /dev/null +++ b/lib/Target/Maxis/MaxisEVAInstrInfo.td @@ -0,0 +1,209 @@ +//===- MaxisEVAInstrInfo.td - EVA ASE instructions -*- tablegen ------------*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis EVA ASE instructions. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// +// Instruction encodings +// +//===----------------------------------------------------------------------===// + +// Memory Load/Store EVA encodings +class LBE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class LBuE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class LHE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class LHuE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class LWE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; + +class SBE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class SHE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class SWE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; + +// load/store left/right EVA encodings +class LWLE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class LWRE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class SWLE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class SWRE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; + +// Load-linked EVA, Store-conditional EVA encodings +class LLE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class SCE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; + +class TLBINV_ENC : TLB_FM; +class TLBINVF_ENC : TLB_FM; + +class CACHEE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; +class PREFE_ENC : SPECIAL3_EVA_LOAD_STORE_FM; + +//===----------------------------------------------------------------------===// +// +// Instruction descriptions +// +//===----------------------------------------------------------------------===// + +// Memory Load/Store EVA descriptions +class LOAD_EVA_DESC_BASE { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins mem_simm9:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + string DecoderMethod = "DecodeMemEVA"; + bit canFoldAsLoad = 1; + bit mayLoad = 1; + InstrItinClass Itinerary = itin; +} + +class LBE_DESC : LOAD_EVA_DESC_BASE<"lbe", GPR32Opnd, II_LBE>; +class LBuE_DESC : LOAD_EVA_DESC_BASE<"lbue", GPR32Opnd, II_LBUE>; +class LHE_DESC : LOAD_EVA_DESC_BASE<"lhe", GPR32Opnd, II_LHE>; +class LHuE_DESC : LOAD_EVA_DESC_BASE<"lhue", GPR32Opnd, II_LHUE>; +class LWE_DESC : LOAD_EVA_DESC_BASE<"lwe", GPR32Opnd, II_LWE>; + +class STORE_EVA_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins GPROpnd:$rt, mem_simm9:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + string DecoderMethod = "DecodeMemEVA"; + bit mayStore = 1; + InstrItinClass Itinerary = itin; +} + +class SBE_DESC : STORE_EVA_DESC_BASE<"sbe", GPR32Opnd, null_frag, II_SBE>; +class SHE_DESC : STORE_EVA_DESC_BASE<"she", GPR32Opnd, null_frag, II_SHE>; +class SWE_DESC : STORE_EVA_DESC_BASE<"swe", GPR32Opnd, null_frag, II_SWE>; + +// Load/Store Left/Right EVA descriptions +class LOAD_LEFT_RIGHT_EVA_DESC_BASE { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins mem_simm9:$addr, GPROpnd:$src); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + string DecoderMethod = "DecodeMemEVA"; + string Constraints = "$src = $rt"; + bit canFoldAsLoad = 1; + InstrItinClass Itinerary = itin; +} + +class LWLE_DESC : LOAD_LEFT_RIGHT_EVA_DESC_BASE<"lwle", GPR32Opnd, II_LWLE>; +class LWRE_DESC : LOAD_LEFT_RIGHT_EVA_DESC_BASE<"lwre", GPR32Opnd, II_LWRE>; + +class STORE_LEFT_RIGHT_EVA_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins GPROpnd:$rt, mem_simm9:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + string DecoderMethod = "DecodeMemEVA"; + InstrItinClass Itinerary = itin; +} + +class SWLE_DESC : LOAD_LEFT_RIGHT_EVA_DESC_BASE<"swle", GPR32Opnd, II_SWLE>; +class SWRE_DESC : LOAD_LEFT_RIGHT_EVA_DESC_BASE<"swre", GPR32Opnd, II_SWRE>; + +// Load-linked EVA, Store-conditional EVA descriptions +class LLE_DESC_BASE { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins mem_simm9:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + bit mayLoad = 1; + string DecoderMethod = "DecodeMemEVA"; + InstrItinClass Itinerary = itin; +} + +class LLE_DESC : LLE_DESC_BASE<"lle", GPR32Opnd, II_LLE>; + +class SCE_DESC_BASE { + dag OutOperandList = (outs GPROpnd:$dst); + dag InOperandList = (ins GPROpnd:$rt, mem_simm9:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + list Pattern = []; + bit mayStore = 1; + string Constraints = "$rt = $dst"; + string DecoderMethod = "DecodeMemEVA"; + InstrItinClass Itinerary = itin; +} + +class SCE_DESC : SCE_DESC_BASE<"sce", GPR32Opnd, II_SCE>; + +class TLB_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins); + string AsmString = instr_asm; + list Pattern = []; + InstrItinClass Itinerary = itin; +} + +class TLBINV_DESC : TLB_DESC_BASE<"tlbinv", II_TLBINV>; +class TLBINVF_DESC : TLB_DESC_BASE<"tlbinvf", II_TLBINVF>; + +class CACHEE_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins MemOpnd:$addr, uimm5:$hint); + string AsmString = !strconcat(instr_asm, "\t$hint, $addr"); + list Pattern = []; + string DecoderMethod = "DecodeCacheeOp_CacheOpR6"; + InstrItinClass Itinerary = itin; +} + +class CACHEE_DESC : CACHEE_DESC_BASE<"cachee", mem_simm9, II_CACHEE>; +class PREFE_DESC : CACHEE_DESC_BASE<"prefe", mem_simm9, II_PREFE>; + +//===----------------------------------------------------------------------===// +// +// Instruction definitions +// +//===----------------------------------------------------------------------===// + +/// Load and Store EVA Instructions +def LBE : LBE_ENC, LBE_DESC, INSN_EVA; +def LBuE : LBuE_ENC, LBuE_DESC, INSN_EVA; +def LHE : LHE_ENC, LHE_DESC, INSN_EVA; +def LHuE : LHuE_ENC, LHuE_DESC, INSN_EVA; +let AdditionalPredicates = [NotInMicroMaxis] in { +def LWE : LWE_ENC, LWE_DESC, INSN_EVA; +} +def SBE : SBE_ENC, SBE_DESC, INSN_EVA; +def SHE : SHE_ENC, SHE_DESC, INSN_EVA; +let AdditionalPredicates = [NotInMicroMaxis] in { +def SWE : SWE_ENC, SWE_DESC, INSN_EVA; +} + +/// load/store left/right EVA +let AdditionalPredicates = [NotInMicroMaxis] in { +def LWLE : LWLE_ENC, LWLE_DESC, INSN_EVA_NOT_32R6_64R6; +def LWRE : LWRE_ENC, LWRE_DESC, INSN_EVA_NOT_32R6_64R6; +def SWLE : SWLE_ENC, SWLE_DESC, INSN_EVA_NOT_32R6_64R6; +def SWRE : SWRE_ENC, SWRE_DESC, INSN_EVA_NOT_32R6_64R6; +} + +/// Load-linked EVA, Store-conditional EVA +let AdditionalPredicates = [NotInMicroMaxis] in { +def LLE : LLE_ENC, LLE_DESC, INSN_EVA; +def SCE : SCE_ENC, SCE_DESC, INSN_EVA; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def TLBINV : TLBINV_ENC, TLBINV_DESC, INSN_EVA; + def TLBINVF : TLBINVF_ENC, TLBINVF_DESC, INSN_EVA; +} + +def CACHEE : CACHEE_ENC, CACHEE_DESC, INSN_EVA; +def PREFE : PREFE_ENC, PREFE_DESC, INSN_EVA; diff --git a/lib/Target/Maxis/MaxisFastISel.cpp b/lib/Target/Maxis/MaxisFastISel.cpp new file mode 100644 index 00000000..263f2bbc --- /dev/null +++ b/lib/Target/Maxis/MaxisFastISel.cpp @@ -0,0 +1,2120 @@ +//===- MaxisFastISel.cpp - Maxis FastISel implementation --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file defines the MAXIS-specific support for the FastISel class. +/// Some of the target-specific code is generated by tablegen in the file +/// MaxisGenFastISel.inc, which is #included here. +/// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MaxisCCState.h" +#include "MaxisISelLowering.h" +#include "MaxisInstrInfo.h" +#include "MaxisMachineFunction.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/FastISel.h" +#include "llvm/CodeGen/FunctionLoweringInfo.h" +#include "llvm/CodeGen/ISDOpcodes.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetLowering.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GetElementPtrTypeIterator.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Operator.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/User.h" +#include "llvm/IR/Value.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include + +#define DEBUG_TYPE "maxis-fastisel" + +using namespace llvm; + +namespace { + +class MaxisFastISel final : public FastISel { + + // All possible address modes. + class Address { + public: + using BaseKind = enum { RegBase, FrameIndexBase }; + + private: + BaseKind Kind = RegBase; + union { + unsigned Reg; + int FI; + } Base; + + int64_t Offset = 0; + + const GlobalValue *GV = nullptr; + + public: + // Innocuous defaults for our address. + Address() { Base.Reg = 0; } + + void setKind(BaseKind K) { Kind = K; } + BaseKind getKind() const { return Kind; } + bool isRegBase() const { return Kind == RegBase; } + bool isFIBase() const { return Kind == FrameIndexBase; } + + void setReg(unsigned Reg) { + assert(isRegBase() && "Invalid base register access!"); + Base.Reg = Reg; + } + + unsigned getReg() const { + assert(isRegBase() && "Invalid base register access!"); + return Base.Reg; + } + + void setFI(unsigned FI) { + assert(isFIBase() && "Invalid base frame index access!"); + Base.FI = FI; + } + + unsigned getFI() const { + assert(isFIBase() && "Invalid base frame index access!"); + return Base.FI; + } + + void setOffset(int64_t Offset_) { Offset = Offset_; } + int64_t getOffset() const { return Offset; } + void setGlobalValue(const GlobalValue *G) { GV = G; } + const GlobalValue *getGlobalValue() { return GV; } + }; + + /// Subtarget - Keep a pointer to the MaxisSubtarget around so that we can + /// make the right decision when generating code for different targets. + const TargetMachine &TM; + const MaxisSubtarget *Subtarget; + const TargetInstrInfo &TII; + const TargetLowering &TLI; + MaxisFunctionInfo *MFI; + + // Convenience variables to avoid some queries. + LLVMContext *Context; + + bool fastLowerArguments() override; + bool fastLowerCall(CallLoweringInfo &CLI) override; + bool fastLowerIntrinsicCall(const IntrinsicInst *II) override; + + bool UnsupportedFPMode; // To allow fast-isel to proceed and just not handle + // floating point but not reject doing fast-isel in other + // situations + +private: + // Selection routines. + bool selectLogicalOp(const Instruction *I); + bool selectLoad(const Instruction *I); + bool selectStore(const Instruction *I); + bool selectBranch(const Instruction *I); + bool selectSelect(const Instruction *I); + bool selectCmp(const Instruction *I); + bool selectFPExt(const Instruction *I); + bool selectFPTrunc(const Instruction *I); + bool selectFPToInt(const Instruction *I, bool IsSigned); + bool selectRet(const Instruction *I); + bool selectTrunc(const Instruction *I); + bool selectIntExt(const Instruction *I); + bool selectShift(const Instruction *I); + bool selectDivRem(const Instruction *I, unsigned ISDOpcode); + + // Utility helper routines. + bool isTypeLegal(Type *Ty, MVT &VT); + bool isTypeSupported(Type *Ty, MVT &VT); + bool isLoadTypeLegal(Type *Ty, MVT &VT); + bool computeAddress(const Value *Obj, Address &Addr); + bool computeCallAddress(const Value *V, Address &Addr); + void simplifyAddress(Address &Addr); + + // Emit helper routines. + bool emitCmp(unsigned DestReg, const CmpInst *CI); + bool emitLoad(MVT VT, unsigned &ResultReg, Address &Addr, + unsigned Alignment = 0); + bool emitStore(MVT VT, unsigned SrcReg, Address Addr, + MachineMemOperand *MMO = nullptr); + bool emitStore(MVT VT, unsigned SrcReg, Address &Addr, + unsigned Alignment = 0); + unsigned emitIntExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, bool isZExt); + bool emitIntExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, unsigned DestReg, + + bool IsZExt); + bool emitIntZExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, unsigned DestReg); + + bool emitIntSExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, unsigned DestReg); + bool emitIntSExt32r1(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg); + bool emitIntSExt32r2(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg); + + unsigned getRegEnsuringSimpleIntegerWidening(const Value *, bool IsUnsigned); + + unsigned emitLogicalOp(unsigned ISDOpc, MVT RetVT, const Value *LHS, + const Value *RHS); + + unsigned materializeFP(const ConstantFP *CFP, MVT VT); + unsigned materializeGV(const GlobalValue *GV, MVT VT); + unsigned materializeInt(const Constant *C, MVT VT); + unsigned materialize32BitInt(int64_t Imm, const TargetRegisterClass *RC); + unsigned materializeExternalCallSym(MCSymbol *Syn); + + MachineInstrBuilder emitInst(unsigned Opc) { + return BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)); + } + + MachineInstrBuilder emitInst(unsigned Opc, unsigned DstReg) { + return BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), + DstReg); + } + + MachineInstrBuilder emitInstStore(unsigned Opc, unsigned SrcReg, + unsigned MemReg, int64_t MemOffset) { + return emitInst(Opc).addReg(SrcReg).addReg(MemReg).addImm(MemOffset); + } + + MachineInstrBuilder emitInstLoad(unsigned Opc, unsigned DstReg, + unsigned MemReg, int64_t MemOffset) { + return emitInst(Opc, DstReg).addReg(MemReg).addImm(MemOffset); + } + + unsigned fastEmitInst_rr(unsigned MachineInstOpcode, + const TargetRegisterClass *RC, + unsigned Op0, bool Op0IsKill, + unsigned Op1, bool Op1IsKill); + + // for some reason, this default is not generated by tablegen + // so we explicitly generate it here. + unsigned fastEmitInst_riir(uint64_t inst, const TargetRegisterClass *RC, + unsigned Op0, bool Op0IsKill, uint64_t imm1, + uint64_t imm2, unsigned Op3, bool Op3IsKill) { + return 0; + } + + // Call handling routines. +private: + CCAssignFn *CCAssignFnForCall(CallingConv::ID CC) const; + bool processCallArgs(CallLoweringInfo &CLI, SmallVectorImpl &ArgVTs, + unsigned &NumBytes); + bool finishCall(CallLoweringInfo &CLI, MVT RetVT, unsigned NumBytes); + + const MaxisABIInfo &getABI() const { + return static_cast(TM).getABI(); + } + +public: + // Backend specific FastISel code. + explicit MaxisFastISel(FunctionLoweringInfo &funcInfo, + const TargetLibraryInfo *libInfo) + : FastISel(funcInfo, libInfo), TM(funcInfo.MF->getTarget()), + Subtarget(&funcInfo.MF->getSubtarget()), + TII(*Subtarget->getInstrInfo()), TLI(*Subtarget->getTargetLowering()) { + MFI = funcInfo.MF->getInfo(); + Context = &funcInfo.Fn->getContext(); + UnsupportedFPMode = Subtarget->isFP64bit() || Subtarget->useSoftFloat(); + } + + unsigned fastMaterializeAlloca(const AllocaInst *AI) override; + unsigned fastMaterializeConstant(const Constant *C) override; + bool fastSelectInstruction(const Instruction *I) override; + +#include "MaxisGenFastISel.inc" +}; + +} // end anonymous namespace + +static bool CC_Maxis(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State) LLVM_ATTRIBUTE_UNUSED; + +static bool CC_MaxisO32_FP32(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State) { + llvm_unreachable("should not be called"); +} + +static bool CC_MaxisO32_FP64(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State) { + llvm_unreachable("should not be called"); +} + +#include "MaxisGenCallingConv.inc" + +CCAssignFn *MaxisFastISel::CCAssignFnForCall(CallingConv::ID CC) const { + return CC_MaxisO32; +} + +unsigned MaxisFastISel::emitLogicalOp(unsigned ISDOpc, MVT RetVT, + const Value *LHS, const Value *RHS) { + // Canonicalize immediates to the RHS first. + if (isa(LHS) && !isa(RHS)) + std::swap(LHS, RHS); + + unsigned Opc; + switch (ISDOpc) { + case ISD::AND: + Opc = Maxis::AND; + break; + case ISD::OR: + Opc = Maxis::OR; + break; + case ISD::XOR: + Opc = Maxis::XOR; + break; + default: + llvm_unreachable("unexpected opcode"); + } + + unsigned LHSReg = getRegForValue(LHS); + if (!LHSReg) + return 0; + + unsigned RHSReg; + if (const auto *C = dyn_cast(RHS)) + RHSReg = materializeInt(C, MVT::i32); + else + RHSReg = getRegForValue(RHS); + if (!RHSReg) + return 0; + + unsigned ResultReg = createResultReg(&Maxis::GPR32RegClass); + if (!ResultReg) + return 0; + + emitInst(Opc, ResultReg).addReg(LHSReg).addReg(RHSReg); + return ResultReg; +} + +unsigned MaxisFastISel::fastMaterializeAlloca(const AllocaInst *AI) { + assert(TLI.getValueType(DL, AI->getType(), true) == MVT::i32 && + "Alloca should always return a pointer."); + + DenseMap::iterator SI = + FuncInfo.StaticAllocaMap.find(AI); + + if (SI != FuncInfo.StaticAllocaMap.end()) { + unsigned ResultReg = createResultReg(&Maxis::GPR32RegClass); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Maxis::LEA_ADDiu), + ResultReg) + .addFrameIndex(SI->second) + .addImm(0); + return ResultReg; + } + + return 0; +} + +unsigned MaxisFastISel::materializeInt(const Constant *C, MVT VT) { + if (VT != MVT::i32 && VT != MVT::i16 && VT != MVT::i8 && VT != MVT::i1) + return 0; + const TargetRegisterClass *RC = &Maxis::GPR32RegClass; + const ConstantInt *CI = cast(C); + return materialize32BitInt(CI->getZExtValue(), RC); +} + +unsigned MaxisFastISel::materialize32BitInt(int64_t Imm, + const TargetRegisterClass *RC) { + unsigned ResultReg = createResultReg(RC); + + if (isInt<16>(Imm)) { + unsigned Opc = Maxis::ADDiu; + emitInst(Opc, ResultReg).addReg(Maxis::ZERO).addImm(Imm); + return ResultReg; + } else if (isUInt<16>(Imm)) { + emitInst(Maxis::ORi, ResultReg).addReg(Maxis::ZERO).addImm(Imm); + return ResultReg; + } + unsigned Lo = Imm & 0xFFFF; + unsigned Hi = (Imm >> 16) & 0xFFFF; + if (Lo) { + // Both Lo and Hi have nonzero bits. + unsigned TmpReg = createResultReg(RC); + emitInst(Maxis::LUi, TmpReg).addImm(Hi); + emitInst(Maxis::ORi, ResultReg).addReg(TmpReg).addImm(Lo); + } else { + emitInst(Maxis::LUi, ResultReg).addImm(Hi); + } + return ResultReg; +} + +unsigned MaxisFastISel::materializeFP(const ConstantFP *CFP, MVT VT) { + if (UnsupportedFPMode) + return 0; + int64_t Imm = CFP->getValueAPF().bitcastToAPInt().getZExtValue(); + if (VT == MVT::f32) { + const TargetRegisterClass *RC = &Maxis::FGR32RegClass; + unsigned DestReg = createResultReg(RC); + unsigned TempReg = materialize32BitInt(Imm, &Maxis::GPR32RegClass); + emitInst(Maxis::MTC1, DestReg).addReg(TempReg); + return DestReg; + } else if (VT == MVT::f64) { + const TargetRegisterClass *RC = &Maxis::AFGR64RegClass; + unsigned DestReg = createResultReg(RC); + unsigned TempReg1 = materialize32BitInt(Imm >> 32, &Maxis::GPR32RegClass); + unsigned TempReg2 = + materialize32BitInt(Imm & 0xFFFFFFFF, &Maxis::GPR32RegClass); + emitInst(Maxis::BuildPairF64, DestReg).addReg(TempReg2).addReg(TempReg1); + return DestReg; + } + return 0; +} + +unsigned MaxisFastISel::materializeGV(const GlobalValue *GV, MVT VT) { + // For now 32-bit only. + if (VT != MVT::i32) + return 0; + const TargetRegisterClass *RC = &Maxis::GPR32RegClass; + unsigned DestReg = createResultReg(RC); + const GlobalVariable *GVar = dyn_cast(GV); + bool IsThreadLocal = GVar && GVar->isThreadLocal(); + // TLS not supported at this time. + if (IsThreadLocal) + return 0; + emitInst(Maxis::LW, DestReg) + .addReg(MFI->getGlobalBaseReg()) + .addGlobalAddress(GV, 0, MaxisII::MO_GOT); + if ((GV->hasInternalLinkage() || + (GV->hasLocalLinkage() && !isa(GV)))) { + unsigned TempReg = createResultReg(RC); + emitInst(Maxis::ADDiu, TempReg) + .addReg(DestReg) + .addGlobalAddress(GV, 0, MaxisII::MO_ABS_LO); + DestReg = TempReg; + } + return DestReg; +} + +unsigned MaxisFastISel::materializeExternalCallSym(MCSymbol *Sym) { + const TargetRegisterClass *RC = &Maxis::GPR32RegClass; + unsigned DestReg = createResultReg(RC); + emitInst(Maxis::LW, DestReg) + .addReg(MFI->getGlobalBaseReg()) + .addSym(Sym, MaxisII::MO_GOT); + return DestReg; +} + +// Materialize a constant into a register, and return the register +// number (or zero if we failed to handle it). +unsigned MaxisFastISel::fastMaterializeConstant(const Constant *C) { + EVT CEVT = TLI.getValueType(DL, C->getType(), true); + + // Only handle simple types. + if (!CEVT.isSimple()) + return 0; + MVT VT = CEVT.getSimpleVT(); + + if (const ConstantFP *CFP = dyn_cast(C)) + return (UnsupportedFPMode) ? 0 : materializeFP(CFP, VT); + else if (const GlobalValue *GV = dyn_cast(C)) + return materializeGV(GV, VT); + else if (isa(C)) + return materializeInt(C, VT); + + return 0; +} + +bool MaxisFastISel::computeAddress(const Value *Obj, Address &Addr) { + const User *U = nullptr; + unsigned Opcode = Instruction::UserOp1; + if (const Instruction *I = dyn_cast(Obj)) { + // Don't walk into other basic blocks unless the object is an alloca from + // another block, otherwise it may not have a virtual register assigned. + if (FuncInfo.StaticAllocaMap.count(static_cast(Obj)) || + FuncInfo.MBBMap[I->getParent()] == FuncInfo.MBB) { + Opcode = I->getOpcode(); + U = I; + } + } else if (const ConstantExpr *C = dyn_cast(Obj)) { + Opcode = C->getOpcode(); + U = C; + } + switch (Opcode) { + default: + break; + case Instruction::BitCast: + // Look through bitcasts. + return computeAddress(U->getOperand(0), Addr); + case Instruction::GetElementPtr: { + Address SavedAddr = Addr; + int64_t TmpOffset = Addr.getOffset(); + // Iterate through the GEP folding the constants into offsets where + // we can. + gep_type_iterator GTI = gep_type_begin(U); + for (User::const_op_iterator i = U->op_begin() + 1, e = U->op_end(); i != e; + ++i, ++GTI) { + const Value *Op = *i; + if (StructType *STy = GTI.getStructTypeOrNull()) { + const StructLayout *SL = DL.getStructLayout(STy); + unsigned Idx = cast(Op)->getZExtValue(); + TmpOffset += SL->getElementOffset(Idx); + } else { + uint64_t S = DL.getTypeAllocSize(GTI.getIndexedType()); + while (true) { + if (const ConstantInt *CI = dyn_cast(Op)) { + // Constant-offset addressing. + TmpOffset += CI->getSExtValue() * S; + break; + } + if (canFoldAddIntoGEP(U, Op)) { + // A compatible add with a constant operand. Fold the constant. + ConstantInt *CI = + cast(cast(Op)->getOperand(1)); + TmpOffset += CI->getSExtValue() * S; + // Iterate on the other operand. + Op = cast(Op)->getOperand(0); + continue; + } + // Unsupported + goto unsupported_gep; + } + } + } + // Try to grab the base operand now. + Addr.setOffset(TmpOffset); + if (computeAddress(U->getOperand(0), Addr)) + return true; + // We failed, restore everything and try the other options. + Addr = SavedAddr; + unsupported_gep: + break; + } + case Instruction::Alloca: { + const AllocaInst *AI = cast(Obj); + DenseMap::iterator SI = + FuncInfo.StaticAllocaMap.find(AI); + if (SI != FuncInfo.StaticAllocaMap.end()) { + Addr.setKind(Address::FrameIndexBase); + Addr.setFI(SI->second); + return true; + } + break; + } + } + Addr.setReg(getRegForValue(Obj)); + return Addr.getReg() != 0; +} + +bool MaxisFastISel::computeCallAddress(const Value *V, Address &Addr) { + const User *U = nullptr; + unsigned Opcode = Instruction::UserOp1; + + if (const auto *I = dyn_cast(V)) { + // Check if the value is defined in the same basic block. This information + // is crucial to know whether or not folding an operand is valid. + if (I->getParent() == FuncInfo.MBB->getBasicBlock()) { + Opcode = I->getOpcode(); + U = I; + } + } else if (const auto *C = dyn_cast(V)) { + Opcode = C->getOpcode(); + U = C; + } + + switch (Opcode) { + default: + break; + case Instruction::BitCast: + // Look past bitcasts if its operand is in the same BB. + return computeCallAddress(U->getOperand(0), Addr); + break; + case Instruction::IntToPtr: + // Look past no-op inttoptrs if its operand is in the same BB. + if (TLI.getValueType(DL, U->getOperand(0)->getType()) == + TLI.getPointerTy(DL)) + return computeCallAddress(U->getOperand(0), Addr); + break; + case Instruction::PtrToInt: + // Look past no-op ptrtoints if its operand is in the same BB. + if (TLI.getValueType(DL, U->getType()) == TLI.getPointerTy(DL)) + return computeCallAddress(U->getOperand(0), Addr); + break; + } + + if (const GlobalValue *GV = dyn_cast(V)) { + Addr.setGlobalValue(GV); + return true; + } + + // If all else fails, try to materialize the value in a register. + if (!Addr.getGlobalValue()) { + Addr.setReg(getRegForValue(V)); + return Addr.getReg() != 0; + } + + return false; +} + +bool MaxisFastISel::isTypeLegal(Type *Ty, MVT &VT) { + EVT evt = TLI.getValueType(DL, Ty, true); + // Only handle simple types. + if (evt == MVT::Other || !evt.isSimple()) + return false; + VT = evt.getSimpleVT(); + + // Handle all legal types, i.e. a register that will directly hold this + // value. + return TLI.isTypeLegal(VT); +} + +bool MaxisFastISel::isTypeSupported(Type *Ty, MVT &VT) { + if (Ty->isVectorTy()) + return false; + + if (isTypeLegal(Ty, VT)) + return true; + + // If this is a type than can be sign or zero-extended to a basic operation + // go ahead and accept it now. + if (VT == MVT::i1 || VT == MVT::i8 || VT == MVT::i16) + return true; + + return false; +} + +bool MaxisFastISel::isLoadTypeLegal(Type *Ty, MVT &VT) { + if (isTypeLegal(Ty, VT)) + return true; + // We will extend this in a later patch: + // If this is a type than can be sign or zero-extended to a basic operation + // go ahead and accept it now. + if (VT == MVT::i8 || VT == MVT::i16) + return true; + return false; +} + +// Because of how EmitCmp is called with fast-isel, you can +// end up with redundant "andi" instructions after the sequences emitted below. +// We should try and solve this issue in the future. +// +bool MaxisFastISel::emitCmp(unsigned ResultReg, const CmpInst *CI) { + const Value *Left = CI->getOperand(0), *Right = CI->getOperand(1); + bool IsUnsigned = CI->isUnsigned(); + unsigned LeftReg = getRegEnsuringSimpleIntegerWidening(Left, IsUnsigned); + if (LeftReg == 0) + return false; + unsigned RightReg = getRegEnsuringSimpleIntegerWidening(Right, IsUnsigned); + if (RightReg == 0) + return false; + CmpInst::Predicate P = CI->getPredicate(); + + switch (P) { + default: + return false; + case CmpInst::ICMP_EQ: { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::XOR, TempReg).addReg(LeftReg).addReg(RightReg); + emitInst(Maxis::SLTiu, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_NE: { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::XOR, TempReg).addReg(LeftReg).addReg(RightReg); + emitInst(Maxis::SLTu, ResultReg).addReg(Maxis::ZERO).addReg(TempReg); + break; + } + case CmpInst::ICMP_UGT: + emitInst(Maxis::SLTu, ResultReg).addReg(RightReg).addReg(LeftReg); + break; + case CmpInst::ICMP_ULT: + emitInst(Maxis::SLTu, ResultReg).addReg(LeftReg).addReg(RightReg); + break; + case CmpInst::ICMP_UGE: { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::SLTu, TempReg).addReg(LeftReg).addReg(RightReg); + emitInst(Maxis::XORi, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_ULE: { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::SLTu, TempReg).addReg(RightReg).addReg(LeftReg); + emitInst(Maxis::XORi, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_SGT: + emitInst(Maxis::SLT, ResultReg).addReg(RightReg).addReg(LeftReg); + break; + case CmpInst::ICMP_SLT: + emitInst(Maxis::SLT, ResultReg).addReg(LeftReg).addReg(RightReg); + break; + case CmpInst::ICMP_SGE: { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::SLT, TempReg).addReg(LeftReg).addReg(RightReg); + emitInst(Maxis::XORi, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::ICMP_SLE: { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::SLT, TempReg).addReg(RightReg).addReg(LeftReg); + emitInst(Maxis::XORi, ResultReg).addReg(TempReg).addImm(1); + break; + } + case CmpInst::FCMP_OEQ: + case CmpInst::FCMP_UNE: + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_OLE: + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_OGE: { + if (UnsupportedFPMode) + return false; + bool IsFloat = Left->getType()->isFloatTy(); + bool IsDouble = Left->getType()->isDoubleTy(); + if (!IsFloat && !IsDouble) + return false; + unsigned Opc, CondMovOpc; + switch (P) { + case CmpInst::FCMP_OEQ: + Opc = IsFloat ? Maxis::C_EQ_S : Maxis::C_EQ_D32; + CondMovOpc = Maxis::MOVT_I; + break; + case CmpInst::FCMP_UNE: + Opc = IsFloat ? Maxis::C_EQ_S : Maxis::C_EQ_D32; + CondMovOpc = Maxis::MOVF_I; + break; + case CmpInst::FCMP_OLT: + Opc = IsFloat ? Maxis::C_OLT_S : Maxis::C_OLT_D32; + CondMovOpc = Maxis::MOVT_I; + break; + case CmpInst::FCMP_OLE: + Opc = IsFloat ? Maxis::C_OLE_S : Maxis::C_OLE_D32; + CondMovOpc = Maxis::MOVT_I; + break; + case CmpInst::FCMP_OGT: + Opc = IsFloat ? Maxis::C_ULE_S : Maxis::C_ULE_D32; + CondMovOpc = Maxis::MOVF_I; + break; + case CmpInst::FCMP_OGE: + Opc = IsFloat ? Maxis::C_ULT_S : Maxis::C_ULT_D32; + CondMovOpc = Maxis::MOVF_I; + break; + default: + llvm_unreachable("Only switching of a subset of CCs."); + } + unsigned RegWithZero = createResultReg(&Maxis::GPR32RegClass); + unsigned RegWithOne = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::ADDiu, RegWithZero).addReg(Maxis::ZERO).addImm(0); + emitInst(Maxis::ADDiu, RegWithOne).addReg(Maxis::ZERO).addImm(1); + emitInst(Opc).addReg(Maxis::FCC0, RegState::Define).addReg(LeftReg) + .addReg(RightReg); + emitInst(CondMovOpc, ResultReg) + .addReg(RegWithOne) + .addReg(Maxis::FCC0) + .addReg(RegWithZero); + break; + } + } + return true; +} + +bool MaxisFastISel::emitLoad(MVT VT, unsigned &ResultReg, Address &Addr, + unsigned Alignment) { + // + // more cases will be handled here in following patches. + // + unsigned Opc; + switch (VT.SimpleTy) { + case MVT::i32: + ResultReg = createResultReg(&Maxis::GPR32RegClass); + Opc = Maxis::LW; + break; + case MVT::i16: + ResultReg = createResultReg(&Maxis::GPR32RegClass); + Opc = Maxis::LHu; + break; + case MVT::i8: + ResultReg = createResultReg(&Maxis::GPR32RegClass); + Opc = Maxis::LBu; + break; + case MVT::f32: + if (UnsupportedFPMode) + return false; + ResultReg = createResultReg(&Maxis::FGR32RegClass); + Opc = Maxis::LWC1; + break; + case MVT::f64: + if (UnsupportedFPMode) + return false; + ResultReg = createResultReg(&Maxis::AFGR64RegClass); + Opc = Maxis::LDC1; + break; + default: + return false; + } + if (Addr.isRegBase()) { + simplifyAddress(Addr); + emitInstLoad(Opc, ResultReg, Addr.getReg(), Addr.getOffset()); + return true; + } + if (Addr.isFIBase()) { + unsigned FI = Addr.getFI(); + unsigned Align = 4; + int64_t Offset = Addr.getOffset(); + MachineFrameInfo &MFI = MF->getFrameInfo(); + MachineMemOperand *MMO = MF->getMachineMemOperand( + MachinePointerInfo::getFixedStack(*MF, FI), MachineMemOperand::MOLoad, + MFI.getObjectSize(FI), Align); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc), ResultReg) + .addFrameIndex(FI) + .addImm(Offset) + .addMemOperand(MMO); + return true; + } + return false; +} + +bool MaxisFastISel::emitStore(MVT VT, unsigned SrcReg, Address &Addr, + unsigned Alignment) { + // + // more cases will be handled here in following patches. + // + unsigned Opc; + switch (VT.SimpleTy) { + case MVT::i8: + Opc = Maxis::SB; + break; + case MVT::i16: + Opc = Maxis::SH; + break; + case MVT::i32: + Opc = Maxis::SW; + break; + case MVT::f32: + if (UnsupportedFPMode) + return false; + Opc = Maxis::SWC1; + break; + case MVT::f64: + if (UnsupportedFPMode) + return false; + Opc = Maxis::SDC1; + break; + default: + return false; + } + if (Addr.isRegBase()) { + simplifyAddress(Addr); + emitInstStore(Opc, SrcReg, Addr.getReg(), Addr.getOffset()); + return true; + } + if (Addr.isFIBase()) { + unsigned FI = Addr.getFI(); + unsigned Align = 4; + int64_t Offset = Addr.getOffset(); + MachineFrameInfo &MFI = MF->getFrameInfo(); + MachineMemOperand *MMO = MF->getMachineMemOperand( + MachinePointerInfo::getFixedStack(*MF, FI), MachineMemOperand::MOStore, + MFI.getObjectSize(FI), Align); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Opc)) + .addReg(SrcReg) + .addFrameIndex(FI) + .addImm(Offset) + .addMemOperand(MMO); + return true; + } + return false; +} + +bool MaxisFastISel::selectLogicalOp(const Instruction *I) { + MVT VT; + if (!isTypeSupported(I->getType(), VT)) + return false; + + unsigned ResultReg; + switch (I->getOpcode()) { + default: + llvm_unreachable("Unexpected instruction."); + case Instruction::And: + ResultReg = emitLogicalOp(ISD::AND, VT, I->getOperand(0), I->getOperand(1)); + break; + case Instruction::Or: + ResultReg = emitLogicalOp(ISD::OR, VT, I->getOperand(0), I->getOperand(1)); + break; + case Instruction::Xor: + ResultReg = emitLogicalOp(ISD::XOR, VT, I->getOperand(0), I->getOperand(1)); + break; + } + + if (!ResultReg) + return false; + + updateValueMap(I, ResultReg); + return true; +} + +bool MaxisFastISel::selectLoad(const Instruction *I) { + // Atomic loads need special handling. + if (cast(I)->isAtomic()) + return false; + + // Verify we have a legal type before going any further. + MVT VT; + if (!isLoadTypeLegal(I->getType(), VT)) + return false; + + // See if we can handle this address. + Address Addr; + if (!computeAddress(I->getOperand(0), Addr)) + return false; + + unsigned ResultReg; + if (!emitLoad(VT, ResultReg, Addr, cast(I)->getAlignment())) + return false; + updateValueMap(I, ResultReg); + return true; +} + +bool MaxisFastISel::selectStore(const Instruction *I) { + Value *Op0 = I->getOperand(0); + unsigned SrcReg = 0; + + // Atomic stores need special handling. + if (cast(I)->isAtomic()) + return false; + + // Verify we have a legal type before going any further. + MVT VT; + if (!isLoadTypeLegal(I->getOperand(0)->getType(), VT)) + return false; + + // Get the value to be stored into a register. + SrcReg = getRegForValue(Op0); + if (SrcReg == 0) + return false; + + // See if we can handle this address. + Address Addr; + if (!computeAddress(I->getOperand(1), Addr)) + return false; + + if (!emitStore(VT, SrcReg, Addr, cast(I)->getAlignment())) + return false; + return true; +} + +// This can cause a redundant sltiu to be generated. +// FIXME: try and eliminate this in a future patch. +bool MaxisFastISel::selectBranch(const Instruction *I) { + const BranchInst *BI = cast(I); + MachineBasicBlock *BrBB = FuncInfo.MBB; + // + // TBB is the basic block for the case where the comparison is true. + // FBB is the basic block for the case where the comparison is false. + // if (cond) goto TBB + // goto FBB + // TBB: + // + MachineBasicBlock *TBB = FuncInfo.MBBMap[BI->getSuccessor(0)]; + MachineBasicBlock *FBB = FuncInfo.MBBMap[BI->getSuccessor(1)]; + BI->getCondition(); + // For now, just try the simplest case where it's fed by a compare. + if (const CmpInst *CI = dyn_cast(BI->getCondition())) { + unsigned CondReg = createResultReg(&Maxis::GPR32RegClass); + if (!emitCmp(CondReg, CI)) + return false; + BuildMI(*BrBB, FuncInfo.InsertPt, DbgLoc, TII.get(Maxis::BGTZ)) + .addReg(CondReg) + .addMBB(TBB); + finishCondBranch(BI->getParent(), TBB, FBB); + return true; + } + return false; +} + +bool MaxisFastISel::selectCmp(const Instruction *I) { + const CmpInst *CI = cast(I); + unsigned ResultReg = createResultReg(&Maxis::GPR32RegClass); + if (!emitCmp(ResultReg, CI)) + return false; + updateValueMap(I, ResultReg); + return true; +} + +// Attempt to fast-select a floating-point extend instruction. +bool MaxisFastISel::selectFPExt(const Instruction *I) { + if (UnsupportedFPMode) + return false; + Value *Src = I->getOperand(0); + EVT SrcVT = TLI.getValueType(DL, Src->getType(), true); + EVT DestVT = TLI.getValueType(DL, I->getType(), true); + + if (SrcVT != MVT::f32 || DestVT != MVT::f64) + return false; + + unsigned SrcReg = + getRegForValue(Src); // this must be a 32bit floating point register class + // maybe we should handle this differently + if (!SrcReg) + return false; + + unsigned DestReg = createResultReg(&Maxis::AFGR64RegClass); + emitInst(Maxis::CVT_D32_S, DestReg).addReg(SrcReg); + updateValueMap(I, DestReg); + return true; +} + +bool MaxisFastISel::selectSelect(const Instruction *I) { + assert(isa(I) && "Expected a select instruction."); + + DEBUG(dbgs() << "selectSelect\n"); + + MVT VT; + if (!isTypeSupported(I->getType(), VT) || UnsupportedFPMode) { + DEBUG(dbgs() << ".. .. gave up (!isTypeSupported || UnsupportedFPMode)\n"); + return false; + } + + unsigned CondMovOpc; + const TargetRegisterClass *RC; + + if (VT.isInteger() && !VT.isVector() && VT.getSizeInBits() <= 32) { + CondMovOpc = Maxis::MOVN_I_I; + RC = &Maxis::GPR32RegClass; + } else if (VT == MVT::f32) { + CondMovOpc = Maxis::MOVN_I_S; + RC = &Maxis::FGR32RegClass; + } else if (VT == MVT::f64) { + CondMovOpc = Maxis::MOVN_I_D32; + RC = &Maxis::AFGR64RegClass; + } else + return false; + + const SelectInst *SI = cast(I); + const Value *Cond = SI->getCondition(); + unsigned Src1Reg = getRegForValue(SI->getTrueValue()); + unsigned Src2Reg = getRegForValue(SI->getFalseValue()); + unsigned CondReg = getRegForValue(Cond); + + if (!Src1Reg || !Src2Reg || !CondReg) + return false; + + unsigned ZExtCondReg = createResultReg(&Maxis::GPR32RegClass); + if (!ZExtCondReg) + return false; + + if (!emitIntExt(MVT::i1, CondReg, MVT::i32, ZExtCondReg, true)) + return false; + + unsigned ResultReg = createResultReg(RC); + unsigned TempReg = createResultReg(RC); + + if (!ResultReg || !TempReg) + return false; + + emitInst(TargetOpcode::COPY, TempReg).addReg(Src2Reg); + emitInst(CondMovOpc, ResultReg) + .addReg(Src1Reg).addReg(ZExtCondReg).addReg(TempReg); + updateValueMap(I, ResultReg); + return true; +} + +// Attempt to fast-select a floating-point truncate instruction. +bool MaxisFastISel::selectFPTrunc(const Instruction *I) { + if (UnsupportedFPMode) + return false; + Value *Src = I->getOperand(0); + EVT SrcVT = TLI.getValueType(DL, Src->getType(), true); + EVT DestVT = TLI.getValueType(DL, I->getType(), true); + + if (SrcVT != MVT::f64 || DestVT != MVT::f32) + return false; + + unsigned SrcReg = getRegForValue(Src); + if (!SrcReg) + return false; + + unsigned DestReg = createResultReg(&Maxis::FGR32RegClass); + if (!DestReg) + return false; + + emitInst(Maxis::CVT_S_D32, DestReg).addReg(SrcReg); + updateValueMap(I, DestReg); + return true; +} + +// Attempt to fast-select a floating-point-to-integer conversion. +bool MaxisFastISel::selectFPToInt(const Instruction *I, bool IsSigned) { + if (UnsupportedFPMode) + return false; + MVT DstVT, SrcVT; + if (!IsSigned) + return false; // We don't handle this case yet. There is no native + // instruction for this but it can be synthesized. + Type *DstTy = I->getType(); + if (!isTypeLegal(DstTy, DstVT)) + return false; + + if (DstVT != MVT::i32) + return false; + + Value *Src = I->getOperand(0); + Type *SrcTy = Src->getType(); + if (!isTypeLegal(SrcTy, SrcVT)) + return false; + + if (SrcVT != MVT::f32 && SrcVT != MVT::f64) + return false; + + unsigned SrcReg = getRegForValue(Src); + if (SrcReg == 0) + return false; + + // Determine the opcode for the conversion, which takes place + // entirely within FPRs. + unsigned DestReg = createResultReg(&Maxis::GPR32RegClass); + unsigned TempReg = createResultReg(&Maxis::FGR32RegClass); + unsigned Opc = (SrcVT == MVT::f32) ? Maxis::TRUNC_W_S : Maxis::TRUNC_W_D32; + + // Generate the convert. + emitInst(Opc, TempReg).addReg(SrcReg); + emitInst(Maxis::MFC1, DestReg).addReg(TempReg); + + updateValueMap(I, DestReg); + return true; +} + +bool MaxisFastISel::processCallArgs(CallLoweringInfo &CLI, + SmallVectorImpl &OutVTs, + unsigned &NumBytes) { + CallingConv::ID CC = CLI.CallConv; + SmallVector ArgLocs; + CCState CCInfo(CC, false, *FuncInfo.MF, ArgLocs, *Context); + CCInfo.AnalyzeCallOperands(OutVTs, CLI.OutFlags, CCAssignFnForCall(CC)); + // Get a count of how many bytes are to be pushed on the stack. + NumBytes = CCInfo.getNextStackOffset(); + // This is the minimum argument area used for A0-A3. + if (NumBytes < 16) + NumBytes = 16; + + emitInst(Maxis::ADJCALLSTACKDOWN).addImm(16).addImm(0); + // Process the args. + MVT firstMVT; + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + const Value *ArgVal = CLI.OutVals[VA.getValNo()]; + MVT ArgVT = OutVTs[VA.getValNo()]; + + if (i == 0) { + firstMVT = ArgVT; + if (ArgVT == MVT::f32) { + VA.convertToReg(Maxis::F12); + } else if (ArgVT == MVT::f64) { + VA.convertToReg(Maxis::D6); + } + } else if (i == 1) { + if ((firstMVT == MVT::f32) || (firstMVT == MVT::f64)) { + if (ArgVT == MVT::f32) { + VA.convertToReg(Maxis::F14); + } else if (ArgVT == MVT::f64) { + VA.convertToReg(Maxis::D7); + } + } + } + if (((ArgVT == MVT::i32) || (ArgVT == MVT::f32) || (ArgVT == MVT::i16) || + (ArgVT == MVT::i8)) && + VA.isMemLoc()) { + switch (VA.getLocMemOffset()) { + case 0: + VA.convertToReg(Maxis::A0); + break; + case 4: + VA.convertToReg(Maxis::A1); + break; + case 8: + VA.convertToReg(Maxis::A2); + break; + case 12: + VA.convertToReg(Maxis::A3); + break; + default: + break; + } + } + unsigned ArgReg = getRegForValue(ArgVal); + if (!ArgReg) + return false; + + // Handle arg promotion: SExt, ZExt, AExt. + switch (VA.getLocInfo()) { + case CCValAssign::Full: + break; + case CCValAssign::AExt: + case CCValAssign::SExt: { + MVT DestVT = VA.getLocVT(); + MVT SrcVT = ArgVT; + ArgReg = emitIntExt(SrcVT, ArgReg, DestVT, /*isZExt=*/false); + if (!ArgReg) + return false; + break; + } + case CCValAssign::ZExt: { + MVT DestVT = VA.getLocVT(); + MVT SrcVT = ArgVT; + ArgReg = emitIntExt(SrcVT, ArgReg, DestVT, /*isZExt=*/true); + if (!ArgReg) + return false; + break; + } + default: + llvm_unreachable("Unknown arg promotion!"); + } + + // Now copy/store arg to correct locations. + if (VA.isRegLoc() && !VA.needsCustom()) { + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(TargetOpcode::COPY), VA.getLocReg()).addReg(ArgReg); + CLI.OutRegs.push_back(VA.getLocReg()); + } else if (VA.needsCustom()) { + llvm_unreachable("Maxis does not use custom args."); + return false; + } else { + // + // FIXME: This path will currently return false. It was copied + // from the AArch64 port and should be essentially fine for Maxis too. + // The work to finish up this path will be done in a follow-on patch. + // + assert(VA.isMemLoc() && "Assuming store on stack."); + // Don't emit stores for undef values. + if (isa(ArgVal)) + continue; + + // Need to store on the stack. + // FIXME: This alignment is incorrect but this path is disabled + // for now (will return false). We need to determine the right alignment + // based on the normal alignment for the underlying machine type. + // + unsigned ArgSize = alignTo(ArgVT.getSizeInBits(), 4); + + unsigned BEAlign = 0; + if (ArgSize < 8 && !Subtarget->isLittle()) + BEAlign = 8 - ArgSize; + + Address Addr; + Addr.setKind(Address::RegBase); + Addr.setReg(Maxis::SP); + Addr.setOffset(VA.getLocMemOffset() + BEAlign); + + unsigned Alignment = DL.getABITypeAlignment(ArgVal->getType()); + MachineMemOperand *MMO = FuncInfo.MF->getMachineMemOperand( + MachinePointerInfo::getStack(*FuncInfo.MF, Addr.getOffset()), + MachineMemOperand::MOStore, ArgVT.getStoreSize(), Alignment); + (void)(MMO); + // if (!emitStore(ArgVT, ArgReg, Addr, MMO)) + return false; // can't store on the stack yet. + } + } + + return true; +} + +bool MaxisFastISel::finishCall(CallLoweringInfo &CLI, MVT RetVT, + unsigned NumBytes) { + CallingConv::ID CC = CLI.CallConv; + emitInst(Maxis::ADJCALLSTACKUP).addImm(16).addImm(0); + if (RetVT != MVT::isVoid) { + SmallVector RVLocs; + MaxisCCState CCInfo(CC, false, *FuncInfo.MF, RVLocs, *Context); + + CCInfo.AnalyzeCallResult(CLI.Ins, RetCC_Maxis, CLI.RetTy, + CLI.Symbol ? CLI.Symbol->getName().data() + : nullptr); + + // Only handle a single return value. + if (RVLocs.size() != 1) + return false; + // Copy all of the result registers out of their specified physreg. + MVT CopyVT = RVLocs[0].getValVT(); + // Special handling for extended integers. + if (RetVT == MVT::i1 || RetVT == MVT::i8 || RetVT == MVT::i16) + CopyVT = MVT::i32; + + unsigned ResultReg = createResultReg(TLI.getRegClassFor(CopyVT)); + if (!ResultReg) + return false; + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(TargetOpcode::COPY), + ResultReg).addReg(RVLocs[0].getLocReg()); + CLI.InRegs.push_back(RVLocs[0].getLocReg()); + + CLI.ResultReg = ResultReg; + CLI.NumResultRegs = 1; + } + return true; +} + +bool MaxisFastISel::fastLowerArguments() { + DEBUG(dbgs() << "fastLowerArguments\n"); + + if (!FuncInfo.CanLowerReturn) { + DEBUG(dbgs() << ".. gave up (!CanLowerReturn)\n"); + return false; + } + + const Function *F = FuncInfo.Fn; + if (F->isVarArg()) { + DEBUG(dbgs() << ".. gave up (varargs)\n"); + return false; + } + + CallingConv::ID CC = F->getCallingConv(); + if (CC != CallingConv::C) { + DEBUG(dbgs() << ".. gave up (calling convention is not C)\n"); + return false; + } + + const ArrayRef GPR32ArgRegs = {Maxis::A0, Maxis::A1, Maxis::A2, + Maxis::A3}; + const ArrayRef FGR32ArgRegs = {Maxis::F12, Maxis::F14}; + const ArrayRef AFGR64ArgRegs = {Maxis::D6, Maxis::D7}; + ArrayRef::iterator NextGPR32 = GPR32ArgRegs.begin(); + ArrayRef::iterator NextFGR32 = FGR32ArgRegs.begin(); + ArrayRef::iterator NextAFGR64 = AFGR64ArgRegs.begin(); + + struct AllocatedReg { + const TargetRegisterClass *RC; + unsigned Reg; + AllocatedReg(const TargetRegisterClass *RC, unsigned Reg) + : RC(RC), Reg(Reg) {} + }; + + // Only handle simple cases. i.e. All arguments are directly mapped to + // registers of the appropriate type. + SmallVector Allocation; + for (const auto &FormalArg : F->args()) { + if (FormalArg.hasAttribute(Attribute::InReg) || + FormalArg.hasAttribute(Attribute::StructRet) || + FormalArg.hasAttribute(Attribute::ByVal)) { + DEBUG(dbgs() << ".. gave up (inreg, structret, byval)\n"); + return false; + } + + Type *ArgTy = FormalArg.getType(); + if (ArgTy->isStructTy() || ArgTy->isArrayTy() || ArgTy->isVectorTy()) { + DEBUG(dbgs() << ".. gave up (struct, array, or vector)\n"); + return false; + } + + EVT ArgVT = TLI.getValueType(DL, ArgTy); + DEBUG(dbgs() << ".. " << FormalArg.getArgNo() << ": " + << ArgVT.getEVTString() << "\n"); + if (!ArgVT.isSimple()) { + DEBUG(dbgs() << ".. .. gave up (not a simple type)\n"); + return false; + } + + switch (ArgVT.getSimpleVT().SimpleTy) { + case MVT::i1: + case MVT::i8: + case MVT::i16: + if (!FormalArg.hasAttribute(Attribute::SExt) && + !FormalArg.hasAttribute(Attribute::ZExt)) { + // It must be any extend, this shouldn't happen for clang-generated IR + // so just fall back on SelectionDAG. + DEBUG(dbgs() << ".. .. gave up (i8/i16 arg is not extended)\n"); + return false; + } + + if (NextGPR32 == GPR32ArgRegs.end()) { + DEBUG(dbgs() << ".. .. gave up (ran out of GPR32 arguments)\n"); + return false; + } + + DEBUG(dbgs() << ".. .. GPR32(" << *NextGPR32 << ")\n"); + Allocation.emplace_back(&Maxis::GPR32RegClass, *NextGPR32++); + + // Allocating any GPR32 prohibits further use of floating point arguments. + NextFGR32 = FGR32ArgRegs.end(); + NextAFGR64 = AFGR64ArgRegs.end(); + break; + + case MVT::i32: + if (FormalArg.hasAttribute(Attribute::ZExt)) { + // The O32 ABI does not permit a zero-extended i32. + DEBUG(dbgs() << ".. .. gave up (i32 arg is zero extended)\n"); + return false; + } + + if (NextGPR32 == GPR32ArgRegs.end()) { + DEBUG(dbgs() << ".. .. gave up (ran out of GPR32 arguments)\n"); + return false; + } + + DEBUG(dbgs() << ".. .. GPR32(" << *NextGPR32 << ")\n"); + Allocation.emplace_back(&Maxis::GPR32RegClass, *NextGPR32++); + + // Allocating any GPR32 prohibits further use of floating point arguments. + NextFGR32 = FGR32ArgRegs.end(); + NextAFGR64 = AFGR64ArgRegs.end(); + break; + + case MVT::f32: + if (UnsupportedFPMode) { + DEBUG(dbgs() << ".. .. gave up (UnsupportedFPMode)\n"); + return false; + } + if (NextFGR32 == FGR32ArgRegs.end()) { + DEBUG(dbgs() << ".. .. gave up (ran out of FGR32 arguments)\n"); + return false; + } + DEBUG(dbgs() << ".. .. FGR32(" << *NextFGR32 << ")\n"); + Allocation.emplace_back(&Maxis::FGR32RegClass, *NextFGR32++); + // Allocating an FGR32 also allocates the super-register AFGR64, and + // ABI rules require us to skip the corresponding GPR32. + if (NextGPR32 != GPR32ArgRegs.end()) + NextGPR32++; + if (NextAFGR64 != AFGR64ArgRegs.end()) + NextAFGR64++; + break; + + case MVT::f64: + if (UnsupportedFPMode) { + DEBUG(dbgs() << ".. .. gave up (UnsupportedFPMode)\n"); + return false; + } + if (NextAFGR64 == AFGR64ArgRegs.end()) { + DEBUG(dbgs() << ".. .. gave up (ran out of AFGR64 arguments)\n"); + return false; + } + DEBUG(dbgs() << ".. .. AFGR64(" << *NextAFGR64 << ")\n"); + Allocation.emplace_back(&Maxis::AFGR64RegClass, *NextAFGR64++); + // Allocating an FGR32 also allocates the super-register AFGR64, and + // ABI rules require us to skip the corresponding GPR32 pair. + if (NextGPR32 != GPR32ArgRegs.end()) + NextGPR32++; + if (NextGPR32 != GPR32ArgRegs.end()) + NextGPR32++; + if (NextFGR32 != FGR32ArgRegs.end()) + NextFGR32++; + break; + + default: + DEBUG(dbgs() << ".. .. gave up (unknown type)\n"); + return false; + } + } + + for (const auto &FormalArg : F->args()) { + unsigned ArgNo = FormalArg.getArgNo(); + unsigned SrcReg = Allocation[ArgNo].Reg; + unsigned DstReg = FuncInfo.MF->addLiveIn(SrcReg, Allocation[ArgNo].RC); + // FIXME: Unfortunately it's necessary to emit a copy from the livein copy. + // Without this, EmitLiveInCopies may eliminate the livein if its only + // use is a bitcast (which isn't turned into an instruction). + unsigned ResultReg = createResultReg(Allocation[ArgNo].RC); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(TargetOpcode::COPY), ResultReg) + .addReg(DstReg, getKillRegState(true)); + updateValueMap(&FormalArg, ResultReg); + } + + // Calculate the size of the incoming arguments area. + // We currently reject all the cases where this would be non-zero. + unsigned IncomingArgSizeInBytes = 0; + + // Account for the reserved argument area on ABI's that have one (O32). + // It seems strange to do this on the caller side but it's necessary in + // SelectionDAG's implementation. + IncomingArgSizeInBytes = std::min(getABI().GetCalleeAllocdArgSizeInBytes(CC), + IncomingArgSizeInBytes); + + MF->getInfo()->setFormalArgInfo(IncomingArgSizeInBytes, + false); + + return true; +} + +bool MaxisFastISel::fastLowerCall(CallLoweringInfo &CLI) { + CallingConv::ID CC = CLI.CallConv; + bool IsTailCall = CLI.IsTailCall; + bool IsVarArg = CLI.IsVarArg; + const Value *Callee = CLI.Callee; + MCSymbol *Symbol = CLI.Symbol; + + // Do not handle FastCC. + if (CC == CallingConv::Fast) + return false; + + // Allow SelectionDAG isel to handle tail calls. + if (IsTailCall) + return false; + + // Let SDISel handle vararg functions. + if (IsVarArg) + return false; + + // FIXME: Only handle *simple* calls for now. + MVT RetVT; + if (CLI.RetTy->isVoidTy()) + RetVT = MVT::isVoid; + else if (!isTypeSupported(CLI.RetTy, RetVT)) + return false; + + for (auto Flag : CLI.OutFlags) + if (Flag.isInReg() || Flag.isSRet() || Flag.isNest() || Flag.isByVal()) + return false; + + // Set up the argument vectors. + SmallVector OutVTs; + OutVTs.reserve(CLI.OutVals.size()); + + for (auto *Val : CLI.OutVals) { + MVT VT; + if (!isTypeLegal(Val->getType(), VT) && + !(VT == MVT::i1 || VT == MVT::i8 || VT == MVT::i16)) + return false; + + // We don't handle vector parameters yet. + if (VT.isVector() || VT.getSizeInBits() > 64) + return false; + + OutVTs.push_back(VT); + } + + Address Addr; + if (!computeCallAddress(Callee, Addr)) + return false; + + // Handle the arguments now that we've gotten them. + unsigned NumBytes; + if (!processCallArgs(CLI, OutVTs, NumBytes)) + return false; + + if (!Addr.getGlobalValue()) + return false; + + // Issue the call. + unsigned DestAddress; + if (Symbol) + DestAddress = materializeExternalCallSym(Symbol); + else + DestAddress = materializeGV(Addr.getGlobalValue(), MVT::i32); + emitInst(TargetOpcode::COPY, Maxis::T9).addReg(DestAddress); + MachineInstrBuilder MIB = + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, TII.get(Maxis::JALR), + Maxis::RA).addReg(Maxis::T9); + + // Add implicit physical register uses to the call. + for (auto Reg : CLI.OutRegs) + MIB.addReg(Reg, RegState::Implicit); + + // Add a register mask with the call-preserved registers. + // Proper defs for return values will be added by setPhysRegsDeadExcept(). + MIB.addRegMask(TRI.getCallPreservedMask(*FuncInfo.MF, CC)); + + CLI.Call = MIB; + + // Finish off the call including any return values. + return finishCall(CLI, RetVT, NumBytes); +} + +bool MaxisFastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { + switch (II->getIntrinsicID()) { + default: + return false; + case Intrinsic::bswap: { + Type *RetTy = II->getCalledFunction()->getReturnType(); + + MVT VT; + if (!isTypeSupported(RetTy, VT)) + return false; + + unsigned SrcReg = getRegForValue(II->getOperand(0)); + if (SrcReg == 0) + return false; + unsigned DestReg = createResultReg(&Maxis::GPR32RegClass); + if (DestReg == 0) + return false; + if (VT == MVT::i16) { + if (Subtarget->hasMaxis32r2()) { + emitInst(Maxis::WSBH, DestReg).addReg(SrcReg); + updateValueMap(II, DestReg); + return true; + } else { + unsigned TempReg[3]; + for (int i = 0; i < 3; i++) { + TempReg[i] = createResultReg(&Maxis::GPR32RegClass); + if (TempReg[i] == 0) + return false; + } + emitInst(Maxis::SLL, TempReg[0]).addReg(SrcReg).addImm(8); + emitInst(Maxis::SRL, TempReg[1]).addReg(SrcReg).addImm(8); + emitInst(Maxis::OR, TempReg[2]).addReg(TempReg[0]).addReg(TempReg[1]); + emitInst(Maxis::ANDi, DestReg).addReg(TempReg[2]).addImm(0xFFFF); + updateValueMap(II, DestReg); + return true; + } + } else if (VT == MVT::i32) { + if (Subtarget->hasMaxis32r2()) { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::WSBH, TempReg).addReg(SrcReg); + emitInst(Maxis::ROTR, DestReg).addReg(TempReg).addImm(16); + updateValueMap(II, DestReg); + return true; + } else { + unsigned TempReg[8]; + for (int i = 0; i < 8; i++) { + TempReg[i] = createResultReg(&Maxis::GPR32RegClass); + if (TempReg[i] == 0) + return false; + } + + emitInst(Maxis::SRL, TempReg[0]).addReg(SrcReg).addImm(8); + emitInst(Maxis::SRL, TempReg[1]).addReg(SrcReg).addImm(24); + emitInst(Maxis::ANDi, TempReg[2]).addReg(TempReg[0]).addImm(0xFF00); + emitInst(Maxis::OR, TempReg[3]).addReg(TempReg[1]).addReg(TempReg[2]); + + emitInst(Maxis::ANDi, TempReg[4]).addReg(SrcReg).addImm(0xFF00); + emitInst(Maxis::SLL, TempReg[5]).addReg(TempReg[4]).addImm(8); + + emitInst(Maxis::SLL, TempReg[6]).addReg(SrcReg).addImm(24); + emitInst(Maxis::OR, TempReg[7]).addReg(TempReg[3]).addReg(TempReg[5]); + emitInst(Maxis::OR, DestReg).addReg(TempReg[6]).addReg(TempReg[7]); + updateValueMap(II, DestReg); + return true; + } + } + return false; + } + case Intrinsic::memcpy: + case Intrinsic::memmove: { + const auto *MTI = cast(II); + // Don't handle volatile. + if (MTI->isVolatile()) + return false; + if (!MTI->getLength()->getType()->isIntegerTy(32)) + return false; + const char *IntrMemName = isa(II) ? "memcpy" : "memmove"; + return lowerCallTo(II, IntrMemName, II->getNumArgOperands() - 2); + } + case Intrinsic::memset: { + const MemSetInst *MSI = cast(II); + // Don't handle volatile. + if (MSI->isVolatile()) + return false; + if (!MSI->getLength()->getType()->isIntegerTy(32)) + return false; + return lowerCallTo(II, "memset", II->getNumArgOperands() - 2); + } + } + return false; +} + +bool MaxisFastISel::selectRet(const Instruction *I) { + const Function &F = *I->getParent()->getParent(); + const ReturnInst *Ret = cast(I); + + DEBUG(dbgs() << "selectRet\n"); + + if (!FuncInfo.CanLowerReturn) + return false; + + // Build a list of return value registers. + SmallVector RetRegs; + + if (Ret->getNumOperands() > 0) { + CallingConv::ID CC = F.getCallingConv(); + + // Do not handle FastCC. + if (CC == CallingConv::Fast) + return false; + + SmallVector Outs; + GetReturnInfo(F.getReturnType(), F.getAttributes(), Outs, TLI, DL); + + // Analyze operands of the call, assigning locations to each operand. + SmallVector ValLocs; + MaxisCCState CCInfo(CC, F.isVarArg(), *FuncInfo.MF, ValLocs, + I->getContext()); + CCAssignFn *RetCC = RetCC_Maxis; + CCInfo.AnalyzeReturn(Outs, RetCC); + + // Only handle a single return value for now. + if (ValLocs.size() != 1) + return false; + + CCValAssign &VA = ValLocs[0]; + const Value *RV = Ret->getOperand(0); + + // Don't bother handling odd stuff for now. + if ((VA.getLocInfo() != CCValAssign::Full) && + (VA.getLocInfo() != CCValAssign::BCvt)) + return false; + + // Only handle register returns for now. + if (!VA.isRegLoc()) + return false; + + unsigned Reg = getRegForValue(RV); + if (Reg == 0) + return false; + + unsigned SrcReg = Reg + VA.getValNo(); + unsigned DestReg = VA.getLocReg(); + // Avoid a cross-class copy. This is very unlikely. + if (!MRI.getRegClass(SrcReg)->contains(DestReg)) + return false; + + EVT RVEVT = TLI.getValueType(DL, RV->getType()); + if (!RVEVT.isSimple()) + return false; + + if (RVEVT.isVector()) + return false; + + MVT RVVT = RVEVT.getSimpleVT(); + if (RVVT == MVT::f128) + return false; + + // Do not handle FGR64 returns for now. + if (RVVT == MVT::f64 && UnsupportedFPMode) { + DEBUG(dbgs() << ".. .. gave up (UnsupportedFPMode\n"); + return false; + } + + MVT DestVT = VA.getValVT(); + // Special handling for extended integers. + if (RVVT != DestVT) { + if (RVVT != MVT::i1 && RVVT != MVT::i8 && RVVT != MVT::i16) + return false; + + if (Outs[0].Flags.isZExt() || Outs[0].Flags.isSExt()) { + bool IsZExt = Outs[0].Flags.isZExt(); + SrcReg = emitIntExt(RVVT, SrcReg, DestVT, IsZExt); + if (SrcReg == 0) + return false; + } + } + + // Make the copy. + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, + TII.get(TargetOpcode::COPY), DestReg).addReg(SrcReg); + + // Add register to return instruction. + RetRegs.push_back(VA.getLocReg()); + } + MachineInstrBuilder MIB = emitInst(Maxis::RetRA); + for (unsigned i = 0, e = RetRegs.size(); i != e; ++i) + MIB.addReg(RetRegs[i], RegState::Implicit); + return true; +} + +bool MaxisFastISel::selectTrunc(const Instruction *I) { + // The high bits for a type smaller than the register size are assumed to be + // undefined. + Value *Op = I->getOperand(0); + + EVT SrcVT, DestVT; + SrcVT = TLI.getValueType(DL, Op->getType(), true); + DestVT = TLI.getValueType(DL, I->getType(), true); + + if (SrcVT != MVT::i32 && SrcVT != MVT::i16 && SrcVT != MVT::i8) + return false; + if (DestVT != MVT::i16 && DestVT != MVT::i8 && DestVT != MVT::i1) + return false; + + unsigned SrcReg = getRegForValue(Op); + if (!SrcReg) + return false; + + // Because the high bits are undefined, a truncate doesn't generate + // any code. + updateValueMap(I, SrcReg); + return true; +} + +bool MaxisFastISel::selectIntExt(const Instruction *I) { + Type *DestTy = I->getType(); + Value *Src = I->getOperand(0); + Type *SrcTy = Src->getType(); + + bool isZExt = isa(I); + unsigned SrcReg = getRegForValue(Src); + if (!SrcReg) + return false; + + EVT SrcEVT, DestEVT; + SrcEVT = TLI.getValueType(DL, SrcTy, true); + DestEVT = TLI.getValueType(DL, DestTy, true); + if (!SrcEVT.isSimple()) + return false; + if (!DestEVT.isSimple()) + return false; + + MVT SrcVT = SrcEVT.getSimpleVT(); + MVT DestVT = DestEVT.getSimpleVT(); + unsigned ResultReg = createResultReg(&Maxis::GPR32RegClass); + + if (!emitIntExt(SrcVT, SrcReg, DestVT, ResultReg, isZExt)) + return false; + updateValueMap(I, ResultReg); + return true; +} + +bool MaxisFastISel::emitIntSExt32r1(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg) { + unsigned ShiftAmt; + switch (SrcVT.SimpleTy) { + default: + return false; + case MVT::i8: + ShiftAmt = 24; + break; + case MVT::i16: + ShiftAmt = 16; + break; + } + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::SLL, TempReg).addReg(SrcReg).addImm(ShiftAmt); + emitInst(Maxis::SRA, DestReg).addReg(TempReg).addImm(ShiftAmt); + return true; +} + +bool MaxisFastISel::emitIntSExt32r2(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg) { + switch (SrcVT.SimpleTy) { + default: + return false; + case MVT::i8: + emitInst(Maxis::SEB, DestReg).addReg(SrcReg); + break; + case MVT::i16: + emitInst(Maxis::SEH, DestReg).addReg(SrcReg); + break; + } + return true; +} + +bool MaxisFastISel::emitIntSExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg) { + if ((DestVT != MVT::i32) && (DestVT != MVT::i16)) + return false; + if (Subtarget->hasMaxis32r2()) + return emitIntSExt32r2(SrcVT, SrcReg, DestVT, DestReg); + return emitIntSExt32r1(SrcVT, SrcReg, DestVT, DestReg); +} + +bool MaxisFastISel::emitIntZExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg) { + int64_t Imm; + + switch (SrcVT.SimpleTy) { + default: + return false; + case MVT::i1: + Imm = 1; + break; + case MVT::i8: + Imm = 0xff; + break; + case MVT::i16: + Imm = 0xffff; + break; + } + + emitInst(Maxis::ANDi, DestReg).addReg(SrcReg).addImm(Imm); + return true; +} + +bool MaxisFastISel::emitIntExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, + unsigned DestReg, bool IsZExt) { + // FastISel does not have plumbing to deal with extensions where the SrcVT or + // DestVT are odd things, so test to make sure that they are both types we can + // handle (i1/i8/i16/i32 for SrcVT and i8/i16/i32/i64 for DestVT), otherwise + // bail out to SelectionDAG. + if (((DestVT != MVT::i8) && (DestVT != MVT::i16) && (DestVT != MVT::i32)) || + ((SrcVT != MVT::i1) && (SrcVT != MVT::i8) && (SrcVT != MVT::i16))) + return false; + if (IsZExt) + return emitIntZExt(SrcVT, SrcReg, DestVT, DestReg); + return emitIntSExt(SrcVT, SrcReg, DestVT, DestReg); +} + +unsigned MaxisFastISel::emitIntExt(MVT SrcVT, unsigned SrcReg, MVT DestVT, + bool isZExt) { + unsigned DestReg = createResultReg(&Maxis::GPR32RegClass); + bool Success = emitIntExt(SrcVT, SrcReg, DestVT, DestReg, isZExt); + return Success ? DestReg : 0; +} + +bool MaxisFastISel::selectDivRem(const Instruction *I, unsigned ISDOpcode) { + EVT DestEVT = TLI.getValueType(DL, I->getType(), true); + if (!DestEVT.isSimple()) + return false; + + MVT DestVT = DestEVT.getSimpleVT(); + if (DestVT != MVT::i32) + return false; + + unsigned DivOpc; + switch (ISDOpcode) { + default: + return false; + case ISD::SDIV: + case ISD::SREM: + DivOpc = Maxis::SDIV; + break; + case ISD::UDIV: + case ISD::UREM: + DivOpc = Maxis::UDIV; + break; + } + + unsigned Src0Reg = getRegForValue(I->getOperand(0)); + unsigned Src1Reg = getRegForValue(I->getOperand(1)); + if (!Src0Reg || !Src1Reg) + return false; + + emitInst(DivOpc).addReg(Src0Reg).addReg(Src1Reg); + emitInst(Maxis::TEQ).addReg(Src1Reg).addReg(Maxis::ZERO).addImm(7); + + unsigned ResultReg = createResultReg(&Maxis::GPR32RegClass); + if (!ResultReg) + return false; + + unsigned MFOpc = (ISDOpcode == ISD::SREM || ISDOpcode == ISD::UREM) + ? Maxis::MFHI + : Maxis::MFLO; + emitInst(MFOpc, ResultReg); + + updateValueMap(I, ResultReg); + return true; +} + +bool MaxisFastISel::selectShift(const Instruction *I) { + MVT RetVT; + + if (!isTypeSupported(I->getType(), RetVT)) + return false; + + unsigned ResultReg = createResultReg(&Maxis::GPR32RegClass); + if (!ResultReg) + return false; + + unsigned Opcode = I->getOpcode(); + const Value *Op0 = I->getOperand(0); + unsigned Op0Reg = getRegForValue(Op0); + if (!Op0Reg) + return false; + + // If AShr or LShr, then we need to make sure the operand0 is sign extended. + if (Opcode == Instruction::AShr || Opcode == Instruction::LShr) { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + if (!TempReg) + return false; + + MVT Op0MVT = TLI.getValueType(DL, Op0->getType(), true).getSimpleVT(); + bool IsZExt = Opcode == Instruction::LShr; + if (!emitIntExt(Op0MVT, Op0Reg, MVT::i32, TempReg, IsZExt)) + return false; + + Op0Reg = TempReg; + } + + if (const auto *C = dyn_cast(I->getOperand(1))) { + uint64_t ShiftVal = C->getZExtValue(); + + switch (Opcode) { + default: + llvm_unreachable("Unexpected instruction."); + case Instruction::Shl: + Opcode = Maxis::SLL; + break; + case Instruction::AShr: + Opcode = Maxis::SRA; + break; + case Instruction::LShr: + Opcode = Maxis::SRL; + break; + } + + emitInst(Opcode, ResultReg).addReg(Op0Reg).addImm(ShiftVal); + updateValueMap(I, ResultReg); + return true; + } + + unsigned Op1Reg = getRegForValue(I->getOperand(1)); + if (!Op1Reg) + return false; + + switch (Opcode) { + default: + llvm_unreachable("Unexpected instruction."); + case Instruction::Shl: + Opcode = Maxis::SLLV; + break; + case Instruction::AShr: + Opcode = Maxis::SRAV; + break; + case Instruction::LShr: + Opcode = Maxis::SRLV; + break; + } + + emitInst(Opcode, ResultReg).addReg(Op0Reg).addReg(Op1Reg); + updateValueMap(I, ResultReg); + return true; +} + +bool MaxisFastISel::fastSelectInstruction(const Instruction *I) { + switch (I->getOpcode()) { + default: + break; + case Instruction::Load: + return selectLoad(I); + case Instruction::Store: + return selectStore(I); + case Instruction::SDiv: + if (!selectBinaryOp(I, ISD::SDIV)) + return selectDivRem(I, ISD::SDIV); + return true; + case Instruction::UDiv: + if (!selectBinaryOp(I, ISD::UDIV)) + return selectDivRem(I, ISD::UDIV); + return true; + case Instruction::SRem: + if (!selectBinaryOp(I, ISD::SREM)) + return selectDivRem(I, ISD::SREM); + return true; + case Instruction::URem: + if (!selectBinaryOp(I, ISD::UREM)) + return selectDivRem(I, ISD::UREM); + return true; + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + return selectShift(I); + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + return selectLogicalOp(I); + case Instruction::Br: + return selectBranch(I); + case Instruction::Ret: + return selectRet(I); + case Instruction::Trunc: + return selectTrunc(I); + case Instruction::ZExt: + case Instruction::SExt: + return selectIntExt(I); + case Instruction::FPTrunc: + return selectFPTrunc(I); + case Instruction::FPExt: + return selectFPExt(I); + case Instruction::FPToSI: + return selectFPToInt(I, /*isSigned*/ true); + case Instruction::FPToUI: + return selectFPToInt(I, /*isSigned*/ false); + case Instruction::ICmp: + case Instruction::FCmp: + return selectCmp(I); + case Instruction::Select: + return selectSelect(I); + } + return false; +} + +unsigned MaxisFastISel::getRegEnsuringSimpleIntegerWidening(const Value *V, + bool IsUnsigned) { + unsigned VReg = getRegForValue(V); + if (VReg == 0) + return 0; + MVT VMVT = TLI.getValueType(DL, V->getType(), true).getSimpleVT(); + if ((VMVT == MVT::i8) || (VMVT == MVT::i16)) { + unsigned TempReg = createResultReg(&Maxis::GPR32RegClass); + if (!emitIntExt(VMVT, VReg, MVT::i32, TempReg, IsUnsigned)) + return 0; + VReg = TempReg; + } + return VReg; +} + +void MaxisFastISel::simplifyAddress(Address &Addr) { + if (!isInt<16>(Addr.getOffset())) { + unsigned TempReg = + materialize32BitInt(Addr.getOffset(), &Maxis::GPR32RegClass); + unsigned DestReg = createResultReg(&Maxis::GPR32RegClass); + emitInst(Maxis::ADDu, DestReg).addReg(TempReg).addReg(Addr.getReg()); + Addr.setReg(DestReg); + Addr.setOffset(0); + } +} + +unsigned MaxisFastISel::fastEmitInst_rr(unsigned MachineInstOpcode, + const TargetRegisterClass *RC, + unsigned Op0, bool Op0IsKill, + unsigned Op1, bool Op1IsKill) { + // We treat the MUL instruction in a special way because it clobbers + // the HI0 & LO0 registers. The TableGen definition of this instruction can + // mark these registers only as implicitly defined. As a result, the + // register allocator runs out of registers when this instruction is + // followed by another instruction that defines the same registers too. + // We can fix this by explicitly marking those registers as dead. + if (MachineInstOpcode == Maxis::MUL) { + unsigned ResultReg = createResultReg(RC); + const MCInstrDesc &II = TII.get(MachineInstOpcode); + Op0 = constrainOperandRegClass(II, Op0, II.getNumDefs()); + Op1 = constrainOperandRegClass(II, Op1, II.getNumDefs() + 1); + BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, ResultReg) + .addReg(Op0, getKillRegState(Op0IsKill)) + .addReg(Op1, getKillRegState(Op1IsKill)) + .addReg(Maxis::HI0, RegState::ImplicitDefine | RegState::Dead) + .addReg(Maxis::LO0, RegState::ImplicitDefine | RegState::Dead); + return ResultReg; + } + + return FastISel::fastEmitInst_rr(MachineInstOpcode, RC, Op0, Op0IsKill, Op1, + Op1IsKill); +} + +namespace llvm { + +FastISel *Maxis::createFastISel(FunctionLoweringInfo &funcInfo, + const TargetLibraryInfo *libInfo) { + return new MaxisFastISel(funcInfo, libInfo); +} + +} // end namespace llvm diff --git a/lib/Target/Maxis/MaxisFrameLowering.cpp b/lib/Target/Maxis/MaxisFrameLowering.cpp new file mode 100644 index 00000000..0799bde6 --- /dev/null +++ b/lib/Target/Maxis/MaxisFrameLowering.cpp @@ -0,0 +1,152 @@ +//===-- MaxisFrameLowering.cpp - Maxis Frame Information --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "MaxisFrameLowering.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MaxisInstrInfo.h" +#include "MaxisMachineFunction.h" +#include "MaxisTargetMachine.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/Target/TargetOptions.h" + +using namespace llvm; + + +//===----------------------------------------------------------------------===// +// +// Stack Frame Processing methods +// +----------------------------+ +// +// The stack is allocated decrementing the stack pointer on +// the first instruction of a function prologue. Once decremented, +// all stack references are done thought a positive offset +// from the stack/frame pointer, so the stack is considering +// to grow up! Otherwise terrible hacks would have to be made +// to get this stack ABI compliant :) +// +// The stack frame required by the ABI (after call): +// Offset +// +// 0 ---------- +// 4 Args to pass +// . saved $GP (used in PIC) +// . Alloca allocations +// . Local Area +// . CPU "Callee Saved" Registers +// . saved FP +// . saved RA +// . FPU "Callee Saved" Registers +// StackSize ----------- +// +// Offset - offset from sp after stack allocation on function prologue +// +// The sp is the stack pointer subtracted/added from the stack size +// at the Prologue/Epilogue +// +// References to the previous stack (to obtain arguments) are done +// with offsets that exceeds the stack size: (stacksize+(4*(num_arg-1)) +// +// Examples: +// - reference to the actual stack frame +// for any local area var there is smt like : FI >= 0, StackOffset: 4 +// sw REGX, 4(SP) +// +// - reference to previous stack frame +// suppose there's a load to the 5th arguments : FI < 0, StackOffset: 16. +// The emitted instruction will be something like: +// lw REGX, 16+StackSize(SP) +// +// Since the total stack size is unknown on LowerFormalArguments, all +// stack references (ObjectOffset) created to reference the function +// arguments, are negative numbers. This way, on eliminateFrameIndex it's +// possible to detect those references and the offsets are adjusted to +// their real location. +// +//===----------------------------------------------------------------------===// + +const MaxisFrameLowering *MaxisFrameLowering::create(const MaxisSubtarget &ST) { + if (ST.inMaxis16Mode()) + return llvm::createMaxis16FrameLowering(ST); + + return llvm::createMaxisSEFrameLowering(ST); +} + +// hasFP - Return true if the specified function should have a dedicated frame +// pointer register. This is true if the function has variable sized allocas, +// if it needs dynamic stack realignment, if frame pointer elimination is +// disabled, or if the frame address is taken. +bool MaxisFrameLowering::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); +} + +bool MaxisFrameLowering::hasBP(const MachineFunction &MF) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const TargetRegisterInfo *TRI = STI.getRegisterInfo(); + + return MFI.hasVarSizedObjects() && TRI->needsStackRealignment(MF); +} + +// Estimate the size of the stack, including the incoming arguments. We need to +// account for register spills, local objects, reserved call frame and incoming +// arguments. This is required to determine the largest possible positive offset +// from $sp so that it can be determined if an emergency spill slot for stack +// addresses is required. +uint64_t MaxisFrameLowering::estimateStackSize(const MachineFunction &MF) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + const TargetRegisterInfo &TRI = *STI.getRegisterInfo(); + + int64_t Size = 0; + + // Iterate over fixed sized objects which are incoming arguments. + for (int I = MFI.getObjectIndexBegin(); I != 0; ++I) + if (MFI.getObjectOffset(I) > 0) + Size += MFI.getObjectSize(I); + + // Conservatively assume all callee-saved registers will be saved. + for (const MCPhysReg *R = TRI.getCalleeSavedRegs(&MF); *R; ++R) { + unsigned RegSize = TRI.getSpillSize(*TRI.getMinimalPhysRegClass(*R)); + Size = alignTo(Size + RegSize, RegSize); + } + + // Get the size of the rest of the frame objects and any possible reserved + // call frame, accounting for alignment. + return Size + MFI.estimateStackSize(MF); +} + +// Eliminate ADJCALLSTACKDOWN, ADJCALLSTACKUP pseudo instructions +MachineBasicBlock::iterator MaxisFrameLowering:: +eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + unsigned SP = STI.getABI().IsN64() ? Maxis::SP_64 : Maxis::SP; + + if (!hasReservedCallFrame(MF)) { + int64_t Amount = I->getOperand(0).getImm(); + if (I->getOpcode() == Maxis::ADJCALLSTACKDOWN) + Amount = -Amount; + + STI.getInstrInfo()->adjustStackPtr(SP, Amount, MBB, I); + } + + return MBB.erase(I); +} diff --git a/lib/Target/Maxis/MaxisFrameLowering.h b/lib/Target/Maxis/MaxisFrameLowering.h new file mode 100644 index 00000000..c16051ad --- /dev/null +++ b/lib/Target/Maxis/MaxisFrameLowering.h @@ -0,0 +1,54 @@ +//===-- MaxisFrameLowering.h - Define frame lowering for Maxis ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISFRAMELOWERING_H +#define LLVM_LIB_TARGET_MAXIS_MAXISFRAMELOWERING_H + +#include "Maxis.h" +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { + class MaxisSubtarget; + +class MaxisFrameLowering : public TargetFrameLowering { +protected: + const MaxisSubtarget &STI; + +public: + explicit MaxisFrameLowering(const MaxisSubtarget &sti, unsigned Alignment) + : TargetFrameLowering(StackGrowsDown, Alignment, 0, Alignment), STI(sti) {} + + static const MaxisFrameLowering *create(const MaxisSubtarget &ST); + + bool hasFP(const MachineFunction &MF) const override; + + bool hasBP(const MachineFunction &MF) const; + + bool isFPCloseToIncomingSP() const override { return false; } + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const override; + +protected: + uint64_t estimateStackSize(const MachineFunction &MF) const; +}; + +/// Create MaxisFrameLowering objects. +const MaxisFrameLowering *createMaxis16FrameLowering(const MaxisSubtarget &ST); +const MaxisFrameLowering *createMaxisSEFrameLowering(const MaxisSubtarget &ST); + +} // End llvm namespace + +#endif diff --git a/lib/Target/Maxis/MaxisHazardSchedule.cpp b/lib/Target/Maxis/MaxisHazardSchedule.cpp new file mode 100644 index 00000000..edf59faa --- /dev/null +++ b/lib/Target/Maxis/MaxisHazardSchedule.cpp @@ -0,0 +1,163 @@ +//===- MaxisHazardSchedule.cpp - Workaround pipeline hazards ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// \file +/// This pass is used to workaround certain pipeline hazards. For now, this +/// covers compact branch hazards. In future this pass can be extended to other +/// pipeline hazards, such as various MAXIS1 hazards, processor errata that +/// require instruction reorganization, etc. +/// +/// This pass has to run after the delay slot filler as that pass can introduce +/// pipeline hazards, hence the existing hazard recognizer is not suitable. +/// +/// Hazards handled: forbidden slots for MAXISR6. +/// +/// A forbidden slot hazard occurs when a compact branch instruction is executed +/// and the adjacent instruction in memory is a control transfer instruction +/// such as a branch or jump, ERET, ERETNC, DERET, WAIT and PAUSE. +/// +/// For example: +/// +/// 0x8004 bnec a1,v0, +/// 0x8008 beqc a1,a2, +/// +/// In such cases, the processor is required to signal a Reserved Instruction +/// exception. +/// +/// Here, if the instruction at 0x8004 is executed, the processor will raise an +/// exception as there is a control transfer instruction at 0x8008. +/// +/// There are two sources of forbidden slot hazards: +/// +/// A) A previous pass has created a compact branch directly. +/// B) Transforming a delay slot branch into compact branch. This case can be +/// difficult to process as lookahead for hazards is insufficient, as +/// backwards delay slot fillling can also produce hazards in previously +/// processed instuctions. +/// +//===----------------------------------------------------------------------===// + +#include "Maxis.h" +#include "MaxisInstrInfo.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-hazard-schedule" + +STATISTIC(NumInsertedNops, "Number of nops inserted"); + +namespace { + +using Iter = MachineBasicBlock::iterator; +using ReverseIter = MachineBasicBlock::reverse_iterator; + +class MaxisHazardSchedule : public MachineFunctionPass { +public: + MaxisHazardSchedule() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "Maxis Hazard Schedule"; } + + bool runOnMachineFunction(MachineFunction &F) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + +private: + static char ID; +}; + +} // end of anonymous namespace + +char MaxisHazardSchedule::ID = 0; + +/// Returns a pass that clears pipeline hazards. +FunctionPass *llvm::createMaxisHazardSchedule() { + return new MaxisHazardSchedule(); +} + +// Find the next real instruction from the current position in current basic +// block. +static Iter getNextMachineInstrInBB(Iter Position) { + Iter I = Position, E = Position->getParent()->end(); + I = std::find_if_not(I, E, + [](const Iter &Insn) { return Insn->isTransient(); }); + + return I; +} + +// Find the next real instruction from the current position, looking through +// basic block boundaries. +static std::pair getNextMachineInstr(Iter Position, MachineBasicBlock * Parent) { + if (Position == Parent->end()) { + do { + MachineBasicBlock *Succ = Parent->getNextNode(); + if (Succ != nullptr && Parent->isSuccessor(Succ)) { + Position = Succ->begin(); + Parent = Succ; + } else { + return std::make_pair(Position, true); + } + } while (Parent->empty()); + } + + Iter Instr = getNextMachineInstrInBB(Position); + if (Instr == Parent->end()) { + return getNextMachineInstr(Instr, Parent); + } + return std::make_pair(Instr, false); +} + +bool MaxisHazardSchedule::runOnMachineFunction(MachineFunction &MF) { + + const MaxisSubtarget *STI = + &static_cast(MF.getSubtarget()); + + // Forbidden slot hazards are only defined for MAXISR6 but not microMAXISR6. + if (!STI->hasMaxis32r6() || STI->inMicroMaxisMode()) + return false; + + bool Changed = false; + const MaxisInstrInfo *TII = STI->getInstrInfo(); + + for (MachineFunction::iterator FI = MF.begin(); FI != MF.end(); ++FI) { + for (Iter I = FI->begin(); I != FI->end(); ++I) { + + // Forbidden slot hazard handling. Use lookahead over state. + if (!TII->HasForbiddenSlot(*I)) + continue; + + Iter Inst; + bool LastInstInFunction = + std::next(I) == FI->end() && std::next(FI) == MF.end(); + if (!LastInstInFunction) { + std::pair Res = getNextMachineInstr(std::next(I), &*FI); + LastInstInFunction |= Res.second; + Inst = Res.first; + } + + if (LastInstInFunction || !TII->SafeInForbiddenSlot(*Inst)) { + Changed = true; + MIBundleBuilder(&*I) + .append(BuildMI(MF, I->getDebugLoc(), TII->get(Maxis::NOP))); + NumInsertedNops++; + } + } + } + return Changed; +} diff --git a/lib/Target/Maxis/MaxisISelDAGToDAG.cpp b/lib/Target/Maxis/MaxisISelDAGToDAG.cpp new file mode 100644 index 00000000..afb41815 --- /dev/null +++ b/lib/Target/Maxis/MaxisISelDAGToDAG.cpp @@ -0,0 +1,270 @@ +//===-- MaxisISelDAGToDAG.cpp - A Dag to Dag Inst Selector for Maxis --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines an instruction selector for the MAXIS target. +// +//===----------------------------------------------------------------------===// + +#include "MaxisISelDAGToDAG.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "Maxis.h" +#include "Maxis16ISelDAGToDAG.h" +#include "MaxisMachineFunction.h" +#include "MaxisRegisterInfo.h" +#include "MaxisSEISelDAGToDAG.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +#define DEBUG_TYPE "maxis-isel" + +//===----------------------------------------------------------------------===// +// Instruction Selector Implementation +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// MaxisDAGToDAGISel - MAXIS specific code to select MAXIS machine +// instructions for SelectionDAG operations. +//===----------------------------------------------------------------------===// + +bool MaxisDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { + Subtarget = &static_cast(MF.getSubtarget()); + bool Ret = SelectionDAGISel::runOnMachineFunction(MF); + + processFunctionAfterISel(MF); + + return Ret; +} + +/// getGlobalBaseReg - Output the instructions required to put the +/// GOT address into a register. +SDNode *MaxisDAGToDAGISel::getGlobalBaseReg() { + unsigned GlobalBaseReg = MF->getInfo()->getGlobalBaseReg(); + return CurDAG->getRegister(GlobalBaseReg, getTargetLowering()->getPointerTy( + CurDAG->getDataLayout())) + .getNode(); +} + +/// ComplexPattern used on MaxisInstrInfo +/// Used on Maxis Load/Store instructions +bool MaxisDAGToDAGISel::selectAddrRegImm(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectAddrDefault(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddr(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddr11MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddr12MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddr16MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddrLSL2MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddrSImm10(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddrSImm10Lsl1(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddrSImm10Lsl2(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectIntAddrSImm10Lsl3(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectAddr16(SDValue Addr, SDValue &Base, + SDValue &Offset) { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectAddr16SP(SDValue Addr, SDValue &Base, + SDValue &Offset) { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplat(SDNode *N, APInt &Imm, + unsigned MinSizeInBits) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimm1(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimm2(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimm3(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimm4(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimm5(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimm6(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimm8(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatSimm5(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimmPow2(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatUimmInvPow2(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatMaskL(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +bool MaxisDAGToDAGISel::selectVSplatMaskR(SDValue N, SDValue &Imm) const { + llvm_unreachable("Unimplemented function."); + return false; +} + +/// Select instructions not customized! Used for +/// expanded, promoted and normal instructions +void MaxisDAGToDAGISel::Select(SDNode *Node) { + unsigned Opcode = Node->getOpcode(); + + // Dump information about the Node being selected + DEBUG(errs() << "Selecting: "; Node->dump(CurDAG); errs() << "\n"); + + // If we have a custom node, we already have selected! + if (Node->isMachineOpcode()) { + DEBUG(errs() << "== "; Node->dump(CurDAG); errs() << "\n"); + Node->setNodeId(-1); + return; + } + + // See if subclasses can handle this node. + if (trySelect(Node)) + return; + + switch(Opcode) { + default: break; + + // Get target GOT address. + case ISD::GLOBAL_OFFSET_TABLE: + ReplaceNode(Node, getGlobalBaseReg()); + return; + +#ifndef NDEBUG + case ISD::LOAD: + case ISD::STORE: + assert((Subtarget->systemSupportsUnalignedAccess() || + cast(Node)->getMemoryVT().getSizeInBits() / 8 <= + cast(Node)->getAlignment()) && + "Unexpected unaligned loads/stores."); + break; +#endif + } + + // Select the default instruction + SelectCode(Node); +} + +bool MaxisDAGToDAGISel:: +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector &OutOps) { + // All memory constraints can at least accept raw pointers. + switch(ConstraintID) { + default: + llvm_unreachable("Unexpected asm memory constraint"); + case InlineAsm::Constraint_i: + case InlineAsm::Constraint_m: + case InlineAsm::Constraint_R: + case InlineAsm::Constraint_ZC: + OutOps.push_back(Op); + return false; + } + return true; +} diff --git a/lib/Target/Maxis/MaxisISelDAGToDAG.h b/lib/Target/Maxis/MaxisISelDAGToDAG.h new file mode 100644 index 00000000..a7b1dc92 --- /dev/null +++ b/lib/Target/Maxis/MaxisISelDAGToDAG.h @@ -0,0 +1,144 @@ +//===---- MaxisISelDAGToDAG.h - A Dag to Dag Inst Selector for Maxis --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines an instruction selector for the MAXIS target. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISISELDAGTODAG_H +#define LLVM_LIB_TARGET_MAXIS_MAXISISELDAGTODAG_H + +#include "Maxis.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.h" +#include "llvm/CodeGen/SelectionDAGISel.h" + +//===----------------------------------------------------------------------===// +// Instruction Selector Implementation +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// MaxisDAGToDAGISel - MAXIS specific code to select MAXIS machine +// instructions for SelectionDAG operations. +//===----------------------------------------------------------------------===// +namespace llvm { + +class MaxisDAGToDAGISel : public SelectionDAGISel { +public: + explicit MaxisDAGToDAGISel(MaxisTargetMachine &TM, CodeGenOpt::Level OL) + : SelectionDAGISel(TM, OL), Subtarget(nullptr) {} + + // Pass Name + StringRef getPassName() const override { + return "MAXIS DAG->DAG Pattern Instruction Selection"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; + +protected: + SDNode *getGlobalBaseReg(); + + /// Keep a pointer to the MaxisSubtarget around so that we can make the right + /// decision when generating code for different targets. + const MaxisSubtarget *Subtarget; + +private: + // Include the pieces autogenerated from the target description. + #include "MaxisGenDAGISel.inc" + + // Complex Pattern. + /// (reg + imm). + virtual bool selectAddrRegImm(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + /// Fall back on this function if all else fails. + virtual bool selectAddrDefault(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + /// Match integer address pattern. + virtual bool selectIntAddr(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + virtual bool selectIntAddr11MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + virtual bool selectIntAddr12MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + virtual bool selectIntAddr16MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + virtual bool selectIntAddrLSL2MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + /// Match addr+simm10 and addr + virtual bool selectIntAddrSImm10(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + virtual bool selectIntAddrSImm10Lsl1(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + virtual bool selectIntAddrSImm10Lsl2(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + virtual bool selectIntAddrSImm10Lsl3(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + virtual bool selectAddr16(SDValue Addr, SDValue &Base, SDValue &Offset); + virtual bool selectAddr16SP(SDValue Addr, SDValue &Base, SDValue &Offset); + + /// \brief Select constant vector splats. + virtual bool selectVSplat(SDNode *N, APInt &Imm, + unsigned MinSizeInBits) const; + /// \brief Select constant vector splats whose value fits in a uimm1. + virtual bool selectVSplatUimm1(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value fits in a uimm2. + virtual bool selectVSplatUimm2(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value fits in a uimm3. + virtual bool selectVSplatUimm3(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value fits in a uimm4. + virtual bool selectVSplatUimm4(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value fits in a uimm5. + virtual bool selectVSplatUimm5(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value fits in a uimm6. + virtual bool selectVSplatUimm6(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value fits in a uimm8. + virtual bool selectVSplatUimm8(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value fits in a simm5. + virtual bool selectVSplatSimm5(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value is a power of 2. + virtual bool selectVSplatUimmPow2(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value is the inverse of a + /// power of 2. + virtual bool selectVSplatUimmInvPow2(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value is a run of set bits + /// ending at the most significant bit + virtual bool selectVSplatMaskL(SDValue N, SDValue &Imm) const; + /// \brief Select constant vector splats whose value is a run of set bits + /// starting at bit zero. + virtual bool selectVSplatMaskR(SDValue N, SDValue &Imm) const; + + void Select(SDNode *N) override; + + virtual bool trySelect(SDNode *Node) = 0; + + // getImm - Return a target constant with the specified value. + inline SDValue getImm(const SDNode *Node, uint64_t Imm) { + return CurDAG->getTargetConstant(Imm, SDLoc(Node), Node->getValueType(0)); + } + + virtual void processFunctionAfterISel(MachineFunction &MF) = 0; + + bool SelectInlineAsmMemoryOperand(const SDValue &Op, + unsigned ConstraintID, + std::vector &OutOps) override; +}; +} + +#endif diff --git a/lib/Target/Maxis/MaxisISelLowering.cpp b/lib/Target/Maxis/MaxisISelLowering.cpp new file mode 100644 index 00000000..e9330e65 --- /dev/null +++ b/lib/Target/Maxis/MaxisISelLowering.cpp @@ -0,0 +1,4380 @@ +//===- MaxisISelLowering.cpp - Maxis DAG Lowering Implementation ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that Maxis uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "MaxisISelLowering.h" +#include "InstPrinter/MaxisInstPrinter.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "MaxisCCState.h" +#include "MaxisInstrInfo.h" +#include "MaxisMachineFunction.h" +#include "MaxisRegisterInfo.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.h" +#include "MaxisTargetObjectFile.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/FunctionLoweringInfo.h" +#include "llvm/CodeGen/ISDOpcodes.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/CodeGen/RuntimeLibcalls.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/Type.h" +#include "llvm/IR/Value.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-lower" + +STATISTIC(NumTailCalls, "Number of tail calls"); + +static cl::opt +LargeGOT("mxgot", cl::Hidden, + cl::desc("MAXIS: Enable GOT larger than 64k."), cl::init(false)); + +static cl::opt +NoZeroDivCheck("mno-check-zero-division", cl::Hidden, + cl::desc("MAXIS: Don't trap on integer division by zero."), + cl::init(false)); + +static const MCPhysReg Maxis64DPRegs[8] = { + Maxis::D12_64, Maxis::D13_64, Maxis::D14_64, Maxis::D15_64, + Maxis::D16_64, Maxis::D17_64, Maxis::D18_64, Maxis::D19_64 +}; + +// If I is a shifted mask, set the size (Size) and the first bit of the +// mask (Pos), and return true. +// For example, if I is 0x003ff800, (Pos, Size) = (11, 11). +static bool isShiftedMask(uint64_t I, uint64_t &Pos, uint64_t &Size) { + if (!isShiftedMask_64(I)) + return false; + + Size = countPopulation(I); + Pos = countTrailingZeros(I); + return true; +} + +// The MAXIS MSA ABI passes vector arguments in the integer register set. +// The number of integer registers used is dependant on the ABI used. +MVT MaxisTargetLowering::getRegisterTypeForCallingConv(MVT VT) const { + if (VT.isVector() && Subtarget.hasMSA()) + return Subtarget.isABI_O32() ? MVT::i32 : MVT::i64; + return MaxisTargetLowering::getRegisterType(VT); +} + +MVT MaxisTargetLowering::getRegisterTypeForCallingConv(LLVMContext &Context, + EVT VT) const { + if (VT.isVector()) { + if (Subtarget.isABI_O32()) { + return MVT::i32; + } else { + return (VT.getSizeInBits() == 32) ? MVT::i32 : MVT::i64; + } + } + return MaxisTargetLowering::getRegisterType(Context, VT); +} + +unsigned MaxisTargetLowering::getNumRegistersForCallingConv(LLVMContext &Context, + EVT VT) const { + if (VT.isVector()) + return std::max((VT.getSizeInBits() / (Subtarget.isABI_O32() ? 32 : 64)), + 1U); + return MaxisTargetLowering::getNumRegisters(Context, VT); +} + +unsigned MaxisTargetLowering::getVectorTypeBreakdownForCallingConv( + LLVMContext &Context, EVT VT, EVT &IntermediateVT, + unsigned &NumIntermediates, MVT &RegisterVT) const { + // Break down vector types to either 2 i64s or 4 i32s. + RegisterVT = getRegisterTypeForCallingConv(Context, VT) ; + IntermediateVT = RegisterVT; + NumIntermediates = VT.getSizeInBits() < RegisterVT.getSizeInBits() + ? VT.getVectorNumElements() + : VT.getSizeInBits() / RegisterVT.getSizeInBits(); + + return NumIntermediates; +} + +SDValue MaxisTargetLowering::getGlobalReg(SelectionDAG &DAG, EVT Ty) const { + MaxisFunctionInfo *FI = DAG.getMachineFunction().getInfo(); + return DAG.getRegister(FI->getGlobalBaseReg(), Ty); +} + +SDValue MaxisTargetLowering::getTargetNode(GlobalAddressSDNode *N, EVT Ty, + SelectionDAG &DAG, + unsigned Flag) const { + return DAG.getTargetGlobalAddress(N->getGlobal(), SDLoc(N), Ty, 0, Flag); +} + +SDValue MaxisTargetLowering::getTargetNode(ExternalSymbolSDNode *N, EVT Ty, + SelectionDAG &DAG, + unsigned Flag) const { + return DAG.getTargetExternalSymbol(N->getSymbol(), Ty, Flag); +} + +SDValue MaxisTargetLowering::getTargetNode(BlockAddressSDNode *N, EVT Ty, + SelectionDAG &DAG, + unsigned Flag) const { + return DAG.getTargetBlockAddress(N->getBlockAddress(), Ty, 0, Flag); +} + +SDValue MaxisTargetLowering::getTargetNode(JumpTableSDNode *N, EVT Ty, + SelectionDAG &DAG, + unsigned Flag) const { + return DAG.getTargetJumpTable(N->getIndex(), Ty, Flag); +} + +SDValue MaxisTargetLowering::getTargetNode(ConstantPoolSDNode *N, EVT Ty, + SelectionDAG &DAG, + unsigned Flag) const { + return DAG.getTargetConstantPool(N->getConstVal(), Ty, N->getAlignment(), + N->getOffset(), Flag); +} + +const char *MaxisTargetLowering::getTargetNodeName(unsigned Opcode) const { + switch ((MaxisISD::NodeType)Opcode) { + case MaxisISD::FIRST_NUMBER: break; + case MaxisISD::JmpLink: return "MaxisISD::JmpLink"; + case MaxisISD::TailCall: return "MaxisISD::TailCall"; + case MaxisISD::Highest: return "MaxisISD::Highest"; + case MaxisISD::Higher: return "MaxisISD::Higher"; + case MaxisISD::Hi: return "MaxisISD::Hi"; + case MaxisISD::Lo: return "MaxisISD::Lo"; + case MaxisISD::GotHi: return "MaxisISD::GotHi"; + case MaxisISD::GPRel: return "MaxisISD::GPRel"; + case MaxisISD::ThreadPointer: return "MaxisISD::ThreadPointer"; + case MaxisISD::Ret: return "MaxisISD::Ret"; + case MaxisISD::ERet: return "MaxisISD::ERet"; + case MaxisISD::EH_RETURN: return "MaxisISD::EH_RETURN"; + case MaxisISD::FPBrcond: return "MaxisISD::FPBrcond"; + case MaxisISD::FPCmp: return "MaxisISD::FPCmp"; + case MaxisISD::FSELECT: return "MaxisISD::FSELECT"; + case MaxisISD::MTC1_D64: return "MaxisISD::MTC1_D64"; + case MaxisISD::CMovFP_T: return "MaxisISD::CMovFP_T"; + case MaxisISD::CMovFP_F: return "MaxisISD::CMovFP_F"; + case MaxisISD::TruncIntFP: return "MaxisISD::TruncIntFP"; + case MaxisISD::MFHI: return "MaxisISD::MFHI"; + case MaxisISD::MFLO: return "MaxisISD::MFLO"; + case MaxisISD::MTLOHI: return "MaxisISD::MTLOHI"; + case MaxisISD::Mult: return "MaxisISD::Mult"; + case MaxisISD::Multu: return "MaxisISD::Multu"; + case MaxisISD::MAdd: return "MaxisISD::MAdd"; + case MaxisISD::MAddu: return "MaxisISD::MAddu"; + case MaxisISD::MSub: return "MaxisISD::MSub"; + case MaxisISD::MSubu: return "MaxisISD::MSubu"; + case MaxisISD::DivRem: return "MaxisISD::DivRem"; + case MaxisISD::DivRemU: return "MaxisISD::DivRemU"; + case MaxisISD::DivRem16: return "MaxisISD::DivRem16"; + case MaxisISD::DivRemU16: return "MaxisISD::DivRemU16"; + case MaxisISD::BuildPairF64: return "MaxisISD::BuildPairF64"; + case MaxisISD::ExtractElementF64: return "MaxisISD::ExtractElementF64"; + case MaxisISD::Wrapper: return "MaxisISD::Wrapper"; + case MaxisISD::DynAlloc: return "MaxisISD::DynAlloc"; + case MaxisISD::Sync: return "MaxisISD::Sync"; + case MaxisISD::Ext: return "MaxisISD::Ext"; + case MaxisISD::Ins: return "MaxisISD::Ins"; + case MaxisISD::CIns: return "MaxisISD::CIns"; + case MaxisISD::LWL: return "MaxisISD::LWL"; + case MaxisISD::LWR: return "MaxisISD::LWR"; + case MaxisISD::SWL: return "MaxisISD::SWL"; + case MaxisISD::SWR: return "MaxisISD::SWR"; + case MaxisISD::LDL: return "MaxisISD::LDL"; + case MaxisISD::LDR: return "MaxisISD::LDR"; + case MaxisISD::SDL: return "MaxisISD::SDL"; + case MaxisISD::SDR: return "MaxisISD::SDR"; + case MaxisISD::EXTP: return "MaxisISD::EXTP"; + case MaxisISD::EXTPDP: return "MaxisISD::EXTPDP"; + case MaxisISD::EXTR_S_H: return "MaxisISD::EXTR_S_H"; + case MaxisISD::EXTR_W: return "MaxisISD::EXTR_W"; + case MaxisISD::EXTR_R_W: return "MaxisISD::EXTR_R_W"; + case MaxisISD::EXTR_RS_W: return "MaxisISD::EXTR_RS_W"; + case MaxisISD::SHILO: return "MaxisISD::SHILO"; + case MaxisISD::MTHLIP: return "MaxisISD::MTHLIP"; + case MaxisISD::MULSAQ_S_W_PH: return "MaxisISD::MULSAQ_S_W_PH"; + case MaxisISD::MAQ_S_W_PHL: return "MaxisISD::MAQ_S_W_PHL"; + case MaxisISD::MAQ_S_W_PHR: return "MaxisISD::MAQ_S_W_PHR"; + case MaxisISD::MAQ_SA_W_PHL: return "MaxisISD::MAQ_SA_W_PHL"; + case MaxisISD::MAQ_SA_W_PHR: return "MaxisISD::MAQ_SA_W_PHR"; + case MaxisISD::DPAU_H_QBL: return "MaxisISD::DPAU_H_QBL"; + case MaxisISD::DPAU_H_QBR: return "MaxisISD::DPAU_H_QBR"; + case MaxisISD::DPSU_H_QBL: return "MaxisISD::DPSU_H_QBL"; + case MaxisISD::DPSU_H_QBR: return "MaxisISD::DPSU_H_QBR"; + case MaxisISD::DPAQ_S_W_PH: return "MaxisISD::DPAQ_S_W_PH"; + case MaxisISD::DPSQ_S_W_PH: return "MaxisISD::DPSQ_S_W_PH"; + case MaxisISD::DPAQ_SA_L_W: return "MaxisISD::DPAQ_SA_L_W"; + case MaxisISD::DPSQ_SA_L_W: return "MaxisISD::DPSQ_SA_L_W"; + case MaxisISD::DPA_W_PH: return "MaxisISD::DPA_W_PH"; + case MaxisISD::DPS_W_PH: return "MaxisISD::DPS_W_PH"; + case MaxisISD::DPAQX_S_W_PH: return "MaxisISD::DPAQX_S_W_PH"; + case MaxisISD::DPAQX_SA_W_PH: return "MaxisISD::DPAQX_SA_W_PH"; + case MaxisISD::DPAX_W_PH: return "MaxisISD::DPAX_W_PH"; + case MaxisISD::DPSX_W_PH: return "MaxisISD::DPSX_W_PH"; + case MaxisISD::DPSQX_S_W_PH: return "MaxisISD::DPSQX_S_W_PH"; + case MaxisISD::DPSQX_SA_W_PH: return "MaxisISD::DPSQX_SA_W_PH"; + case MaxisISD::MULSA_W_PH: return "MaxisISD::MULSA_W_PH"; + case MaxisISD::MULT: return "MaxisISD::MULT"; + case MaxisISD::MULTU: return "MaxisISD::MULTU"; + case MaxisISD::MADD_DSP: return "MaxisISD::MADD_DSP"; + case MaxisISD::MADDU_DSP: return "MaxisISD::MADDU_DSP"; + case MaxisISD::MSUB_DSP: return "MaxisISD::MSUB_DSP"; + case MaxisISD::MSUBU_DSP: return "MaxisISD::MSUBU_DSP"; + case MaxisISD::SHLL_DSP: return "MaxisISD::SHLL_DSP"; + case MaxisISD::SHRA_DSP: return "MaxisISD::SHRA_DSP"; + case MaxisISD::SHRL_DSP: return "MaxisISD::SHRL_DSP"; + case MaxisISD::SETCC_DSP: return "MaxisISD::SETCC_DSP"; + case MaxisISD::SELECT_CC_DSP: return "MaxisISD::SELECT_CC_DSP"; + case MaxisISD::VALL_ZERO: return "MaxisISD::VALL_ZERO"; + case MaxisISD::VANY_ZERO: return "MaxisISD::VANY_ZERO"; + case MaxisISD::VALL_NONZERO: return "MaxisISD::VALL_NONZERO"; + case MaxisISD::VANY_NONZERO: return "MaxisISD::VANY_NONZERO"; + case MaxisISD::VCEQ: return "MaxisISD::VCEQ"; + case MaxisISD::VCLE_S: return "MaxisISD::VCLE_S"; + case MaxisISD::VCLE_U: return "MaxisISD::VCLE_U"; + case MaxisISD::VCLT_S: return "MaxisISD::VCLT_S"; + case MaxisISD::VCLT_U: return "MaxisISD::VCLT_U"; + case MaxisISD::VSMAX: return "MaxisISD::VSMAX"; + case MaxisISD::VSMIN: return "MaxisISD::VSMIN"; + case MaxisISD::VUMAX: return "MaxisISD::VUMAX"; + case MaxisISD::VUMIN: return "MaxisISD::VUMIN"; + case MaxisISD::VEXTRACT_SEXT_ELT: return "MaxisISD::VEXTRACT_SEXT_ELT"; + case MaxisISD::VEXTRACT_ZEXT_ELT: return "MaxisISD::VEXTRACT_ZEXT_ELT"; + case MaxisISD::VNOR: return "MaxisISD::VNOR"; + case MaxisISD::VSHF: return "MaxisISD::VSHF"; + case MaxisISD::SHF: return "MaxisISD::SHF"; + case MaxisISD::ILVEV: return "MaxisISD::ILVEV"; + case MaxisISD::ILVOD: return "MaxisISD::ILVOD"; + case MaxisISD::ILVL: return "MaxisISD::ILVL"; + case MaxisISD::ILVR: return "MaxisISD::ILVR"; + case MaxisISD::PCKEV: return "MaxisISD::PCKEV"; + case MaxisISD::PCKOD: return "MaxisISD::PCKOD"; + case MaxisISD::INSVE: return "MaxisISD::INSVE"; + } + return nullptr; +} + +MaxisTargetLowering::MaxisTargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI) + : TargetLowering(TM), Subtarget(STI), ABI(TM.getABI()) { + // Maxis does not have i1 type, so use i32 for + // setcc operations results (slt, sgt, ...). + setBooleanContents(ZeroOrOneBooleanContent); + setBooleanVectorContents(ZeroOrNegativeOneBooleanContent); + // The cmp.cond.fmt instruction in MAXIS32r6/MAXIS64r6 uses 0 and -1 like MSA + // does. Integer booleans still use 0 and 1. + if (Subtarget.hasMaxis32r6()) + setBooleanContents(ZeroOrOneBooleanContent, + ZeroOrNegativeOneBooleanContent); + + // Load extented operations for i1 types must be promoted + for (MVT VT : MVT::integer_valuetypes()) { + setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote); + setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i1, Promote); + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); + } + + // MAXIS doesn't have extending float->double load/store. Set LoadExtAction + // for f32, f16 + for (MVT VT : MVT::fp_valuetypes()) { + setLoadExtAction(ISD::EXTLOAD, VT, MVT::f32, Expand); + setLoadExtAction(ISD::EXTLOAD, VT, MVT::f16, Expand); + } + + // Set LoadExtAction for f16 vectors to Expand + for (MVT VT : MVT::fp_vector_valuetypes()) { + MVT F16VT = MVT::getVectorVT(MVT::f16, VT.getVectorNumElements()); + if (F16VT.isValid()) + setLoadExtAction(ISD::EXTLOAD, VT, F16VT, Expand); + } + + setTruncStoreAction(MVT::f32, MVT::f16, Expand); + setTruncStoreAction(MVT::f64, MVT::f16, Expand); + + setTruncStoreAction(MVT::f64, MVT::f32, Expand); + + // Used by legalize types to correctly generate the setcc result. + // Without this, every float setcc comes with a AND/OR with the result, + // we don't want this, since the fpcmp result goes to a flag register, + // which is used implicitly by brcond and select operations. + AddPromotedToType(ISD::SETCC, MVT::i1, MVT::i32); + + // Maxis Custom Operations + setOperationAction(ISD::BR_JT, MVT::Other, Expand); + setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); + setOperationAction(ISD::BlockAddress, MVT::i32, Custom); + setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom); + setOperationAction(ISD::JumpTable, MVT::i32, Custom); + setOperationAction(ISD::ConstantPool, MVT::i32, Custom); + setOperationAction(ISD::SELECT, MVT::f32, Custom); + setOperationAction(ISD::SELECT, MVT::f64, Custom); + setOperationAction(ISD::SELECT, MVT::i32, Custom); + setOperationAction(ISD::SETCC, MVT::f32, Custom); + setOperationAction(ISD::SETCC, MVT::f64, Custom); + setOperationAction(ISD::BRCOND, MVT::Other, Custom); + setOperationAction(ISD::FCOPYSIGN, MVT::f32, Custom); + setOperationAction(ISD::FCOPYSIGN, MVT::f64, Custom); + setOperationAction(ISD::FP_TO_SINT, MVT::i32, Custom); + + if (Subtarget.isGP64bit()) { + setOperationAction(ISD::GlobalAddress, MVT::i64, Custom); + setOperationAction(ISD::BlockAddress, MVT::i64, Custom); + setOperationAction(ISD::GlobalTLSAddress, MVT::i64, Custom); + setOperationAction(ISD::JumpTable, MVT::i64, Custom); + setOperationAction(ISD::ConstantPool, MVT::i64, Custom); + setOperationAction(ISD::SELECT, MVT::i64, Custom); + setOperationAction(ISD::LOAD, MVT::i64, Custom); + setOperationAction(ISD::STORE, MVT::i64, Custom); + setOperationAction(ISD::FP_TO_SINT, MVT::i64, Custom); + setOperationAction(ISD::SHL_PARTS, MVT::i64, Custom); + setOperationAction(ISD::SRA_PARTS, MVT::i64, Custom); + setOperationAction(ISD::SRL_PARTS, MVT::i64, Custom); + } + + if (!Subtarget.isGP64bit()) { + setOperationAction(ISD::SHL_PARTS, MVT::i32, Custom); + setOperationAction(ISD::SRA_PARTS, MVT::i32, Custom); + setOperationAction(ISD::SRL_PARTS, MVT::i32, Custom); + } + + setOperationAction(ISD::EH_DWARF_CFA, MVT::i32, Custom); + if (Subtarget.isGP64bit()) + setOperationAction(ISD::EH_DWARF_CFA, MVT::i64, Custom); + + setOperationAction(ISD::SDIV, MVT::i32, Expand); + setOperationAction(ISD::SREM, MVT::i32, Expand); + setOperationAction(ISD::UDIV, MVT::i32, Expand); + setOperationAction(ISD::UREM, MVT::i32, Expand); + setOperationAction(ISD::SDIV, MVT::i64, Expand); + setOperationAction(ISD::SREM, MVT::i64, Expand); + setOperationAction(ISD::UDIV, MVT::i64, Expand); + setOperationAction(ISD::UREM, MVT::i64, Expand); + + if (!(Subtarget.hasDSP() && Subtarget.hasMaxis32r2())) { + setOperationAction(ISD::ADDC, MVT::i32, Expand); + setOperationAction(ISD::ADDE, MVT::i32, Expand); + } + + setOperationAction(ISD::ADDC, MVT::i64, Expand); + setOperationAction(ISD::ADDE, MVT::i64, Expand); + setOperationAction(ISD::SUBC, MVT::i32, Expand); + setOperationAction(ISD::SUBE, MVT::i32, Expand); + setOperationAction(ISD::SUBC, MVT::i64, Expand); + setOperationAction(ISD::SUBE, MVT::i64, Expand); + + // Operations not directly supported by Maxis. + setOperationAction(ISD::BR_CC, MVT::f32, Expand); + setOperationAction(ISD::BR_CC, MVT::f64, Expand); + setOperationAction(ISD::BR_CC, MVT::i32, Expand); + setOperationAction(ISD::BR_CC, MVT::i64, Expand); + setOperationAction(ISD::SELECT_CC, MVT::i32, Expand); + setOperationAction(ISD::SELECT_CC, MVT::i64, Expand); + setOperationAction(ISD::SELECT_CC, MVT::f32, Expand); + setOperationAction(ISD::SELECT_CC, MVT::f64, Expand); + setOperationAction(ISD::UINT_TO_FP, MVT::i32, Expand); + setOperationAction(ISD::UINT_TO_FP, MVT::i64, Expand); + setOperationAction(ISD::FP_TO_UINT, MVT::i32, Expand); + setOperationAction(ISD::FP_TO_UINT, MVT::i64, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); + if (Subtarget.hasCnMaxis()) { + setOperationAction(ISD::CTPOP, MVT::i32, Legal); + setOperationAction(ISD::CTPOP, MVT::i64, Legal); + } else { + setOperationAction(ISD::CTPOP, MVT::i32, Expand); + setOperationAction(ISD::CTPOP, MVT::i64, Expand); + } + setOperationAction(ISD::CTTZ, MVT::i32, Expand); + setOperationAction(ISD::CTTZ, MVT::i64, Expand); + setOperationAction(ISD::ROTL, MVT::i32, Expand); + setOperationAction(ISD::ROTL, MVT::i64, Expand); + setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Expand); + setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i64, Expand); + + if (!Subtarget.hasMaxis32r2()) + setOperationAction(ISD::ROTR, MVT::i32, Expand); + + if (!Subtarget.hasMaxis64r2()) + setOperationAction(ISD::ROTR, MVT::i64, Expand); + + setOperationAction(ISD::FSIN, MVT::f32, Expand); + setOperationAction(ISD::FSIN, MVT::f64, Expand); + setOperationAction(ISD::FCOS, MVT::f32, Expand); + setOperationAction(ISD::FCOS, MVT::f64, Expand); + setOperationAction(ISD::FSINCOS, MVT::f32, Expand); + setOperationAction(ISD::FSINCOS, MVT::f64, Expand); + setOperationAction(ISD::FPOW, MVT::f32, Expand); + setOperationAction(ISD::FPOW, MVT::f64, Expand); + setOperationAction(ISD::FLOG, MVT::f32, Expand); + setOperationAction(ISD::FLOG2, MVT::f32, Expand); + setOperationAction(ISD::FLOG10, MVT::f32, Expand); + setOperationAction(ISD::FEXP, MVT::f32, Expand); + setOperationAction(ISD::FMA, MVT::f32, Expand); + setOperationAction(ISD::FMA, MVT::f64, Expand); + setOperationAction(ISD::FREM, MVT::f32, Expand); + setOperationAction(ISD::FREM, MVT::f64, Expand); + + // Lower f16 conversion operations into library calls + setOperationAction(ISD::FP16_TO_FP, MVT::f32, Expand); + setOperationAction(ISD::FP_TO_FP16, MVT::f32, Expand); + setOperationAction(ISD::FP16_TO_FP, MVT::f64, Expand); + setOperationAction(ISD::FP_TO_FP16, MVT::f64, Expand); + + setOperationAction(ISD::EH_RETURN, MVT::Other, Custom); + + setOperationAction(ISD::VASTART, MVT::Other, Custom); + setOperationAction(ISD::VAARG, MVT::Other, Custom); + setOperationAction(ISD::VACOPY, MVT::Other, Expand); + setOperationAction(ISD::VAEND, MVT::Other, Expand); + + // Use the default for now + setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); + setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); + + if (!Subtarget.isGP64bit()) { + setOperationAction(ISD::ATOMIC_LOAD, MVT::i64, Expand); + setOperationAction(ISD::ATOMIC_STORE, MVT::i64, Expand); + } + + if (!Subtarget.hasMaxis32r2()) { + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand); + setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand); + } + + // MAXIS16 lacks MAXIS32's clz and clo instructions. + if (!Subtarget.hasMaxis32() || Subtarget.inMaxis16Mode()) + setOperationAction(ISD::CTLZ, MVT::i32, Expand); + if (!Subtarget.hasMaxis64()) + setOperationAction(ISD::CTLZ, MVT::i64, Expand); + + if (!Subtarget.hasMaxis32r2()) + setOperationAction(ISD::BSWAP, MVT::i32, Expand); + if (!Subtarget.hasMaxis64r2()) + setOperationAction(ISD::BSWAP, MVT::i64, Expand); + + if (Subtarget.isGP64bit()) { + setLoadExtAction(ISD::SEXTLOAD, MVT::i64, MVT::i32, Custom); + setLoadExtAction(ISD::ZEXTLOAD, MVT::i64, MVT::i32, Custom); + setLoadExtAction(ISD::EXTLOAD, MVT::i64, MVT::i32, Custom); + setTruncStoreAction(MVT::i64, MVT::i32, Custom); + } + + setOperationAction(ISD::TRAP, MVT::Other, Legal); + + setTargetDAGCombine(ISD::SDIVREM); + setTargetDAGCombine(ISD::UDIVREM); + setTargetDAGCombine(ISD::SELECT); + setTargetDAGCombine(ISD::AND); + setTargetDAGCombine(ISD::OR); + setTargetDAGCombine(ISD::ADD); + setTargetDAGCombine(ISD::SUB); + setTargetDAGCombine(ISD::AssertZext); + setTargetDAGCombine(ISD::SHL); + + if (ABI.IsO32()) { + // These libcalls are not available in 32-bit. + setLibcallName(RTLIB::SHL_I128, nullptr); + setLibcallName(RTLIB::SRL_I128, nullptr); + setLibcallName(RTLIB::SRA_I128, nullptr); + } + + setMinFunctionAlignment(Subtarget.isGP64bit() ? 3 : 2); + + // The arguments on the stack are defined in terms of 4-byte slots on O32 + // and 8-byte slots on N32/N64. + setMinStackArgumentAlignment((ABI.IsN32() || ABI.IsN64()) ? 8 : 4); + + setStackPointerRegisterToSaveRestore(ABI.IsN64() ? Maxis::SP_64 : Maxis::SP); + + MaxStoresPerMemcpy = 16; + + isMicroMaxis = Subtarget.inMicroMaxisMode(); +} + +const MaxisTargetLowering *MaxisTargetLowering::create(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI) { + if (STI.inMaxis16Mode()) + return createMaxis16TargetLowering(TM, STI); + + return createMaxisSETargetLowering(TM, STI); +} + +// Create a fast isel object. +FastISel * +MaxisTargetLowering::createFastISel(FunctionLoweringInfo &funcInfo, + const TargetLibraryInfo *libInfo) const { + const MaxisTargetMachine &TM = + static_cast(funcInfo.MF->getTarget()); + + // We support only the standard encoding [MAXIS32,MAXIS32R5] ISAs. + bool UseFastISel = TM.Options.EnableFastISel && Subtarget.hasMaxis32() && + !Subtarget.hasMaxis32r6() && !Subtarget.inMaxis16Mode() && + !Subtarget.inMicroMaxisMode(); + + // Disable if either of the following is true: + // We do not generate PIC, the ABI is not O32, LargeGOT is being used. + if (!TM.isPositionIndependent() || !TM.getABI().IsO32() || LargeGOT) + UseFastISel = false; + + return UseFastISel ? Maxis::createFastISel(funcInfo, libInfo) : nullptr; +} + +EVT MaxisTargetLowering::getSetCCResultType(const DataLayout &, LLVMContext &, + EVT VT) const { + if (!VT.isVector()) + return MVT::i32; + return VT.changeVectorElementTypeToInteger(); +} + +static SDValue performDivRemCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + if (DCI.isBeforeLegalizeOps()) + return SDValue(); + + EVT Ty = N->getValueType(0); + unsigned LO = (Ty == MVT::i32) ? Maxis::LO0 : Maxis::LO0_64; + unsigned HI = (Ty == MVT::i32) ? Maxis::HI0 : Maxis::HI0_64; + unsigned Opc = N->getOpcode() == ISD::SDIVREM ? MaxisISD::DivRem16 : + MaxisISD::DivRemU16; + SDLoc DL(N); + + SDValue DivRem = DAG.getNode(Opc, DL, MVT::Glue, + N->getOperand(0), N->getOperand(1)); + SDValue InChain = DAG.getEntryNode(); + SDValue InGlue = DivRem; + + // insert MFLO + if (N->hasAnyUseOfValue(0)) { + SDValue CopyFromLo = DAG.getCopyFromReg(InChain, DL, LO, Ty, + InGlue); + DAG.ReplaceAllUsesOfValueWith(SDValue(N, 0), CopyFromLo); + InChain = CopyFromLo.getValue(1); + InGlue = CopyFromLo.getValue(2); + } + + // insert MFHI + if (N->hasAnyUseOfValue(1)) { + SDValue CopyFromHi = DAG.getCopyFromReg(InChain, DL, + HI, Ty, InGlue); + DAG.ReplaceAllUsesOfValueWith(SDValue(N, 1), CopyFromHi); + } + + return SDValue(); +} + +static Maxis::CondCode condCodeToFCC(ISD::CondCode CC) { + switch (CC) { + default: llvm_unreachable("Unknown fp condition code!"); + case ISD::SETEQ: + case ISD::SETOEQ: return Maxis::FCOND_OEQ; + case ISD::SETUNE: return Maxis::FCOND_UNE; + case ISD::SETLT: + case ISD::SETOLT: return Maxis::FCOND_OLT; + case ISD::SETGT: + case ISD::SETOGT: return Maxis::FCOND_OGT; + case ISD::SETLE: + case ISD::SETOLE: return Maxis::FCOND_OLE; + case ISD::SETGE: + case ISD::SETOGE: return Maxis::FCOND_OGE; + case ISD::SETULT: return Maxis::FCOND_ULT; + case ISD::SETULE: return Maxis::FCOND_ULE; + case ISD::SETUGT: return Maxis::FCOND_UGT; + case ISD::SETUGE: return Maxis::FCOND_UGE; + case ISD::SETUO: return Maxis::FCOND_UN; + case ISD::SETO: return Maxis::FCOND_OR; + case ISD::SETNE: + case ISD::SETONE: return Maxis::FCOND_ONE; + case ISD::SETUEQ: return Maxis::FCOND_UEQ; + } +} + +/// This function returns true if the floating point conditional branches and +/// conditional moves which use condition code CC should be inverted. +static bool invertFPCondCodeUser(Maxis::CondCode CC) { + if (CC >= Maxis::FCOND_F && CC <= Maxis::FCOND_NGT) + return false; + + assert((CC >= Maxis::FCOND_T && CC <= Maxis::FCOND_GT) && + "Illegal Condition Code"); + + return true; +} + +// Creates and returns an FPCmp node from a setcc node. +// Returns Op if setcc is not a floating point comparison. +static SDValue createFPCmp(SelectionDAG &DAG, const SDValue &Op) { + // must be a SETCC node + if (Op.getOpcode() != ISD::SETCC) + return Op; + + SDValue LHS = Op.getOperand(0); + + if (!LHS.getValueType().isFloatingPoint()) + return Op; + + SDValue RHS = Op.getOperand(1); + SDLoc DL(Op); + + // Assume the 3rd operand is a CondCodeSDNode. Add code to check the type of + // node if necessary. + ISD::CondCode CC = cast(Op.getOperand(2))->get(); + + return DAG.getNode(MaxisISD::FPCmp, DL, MVT::Glue, LHS, RHS, + DAG.getConstant(condCodeToFCC(CC), DL, MVT::i32)); +} + +// Creates and returns a CMovFPT/F node. +static SDValue createCMovFP(SelectionDAG &DAG, SDValue Cond, SDValue True, + SDValue False, const SDLoc &DL) { + ConstantSDNode *CC = cast(Cond.getOperand(2)); + bool invert = invertFPCondCodeUser((Maxis::CondCode)CC->getSExtValue()); + SDValue FCC0 = DAG.getRegister(Maxis::FCC0, MVT::i32); + + return DAG.getNode((invert ? MaxisISD::CMovFP_F : MaxisISD::CMovFP_T), DL, + True.getValueType(), True, FCC0, False, Cond); +} + +static SDValue performSELECTCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + if (DCI.isBeforeLegalizeOps()) + return SDValue(); + + SDValue SetCC = N->getOperand(0); + + if ((SetCC.getOpcode() != ISD::SETCC) || + !SetCC.getOperand(0).getValueType().isInteger()) + return SDValue(); + + SDValue False = N->getOperand(2); + EVT FalseTy = False.getValueType(); + + if (!FalseTy.isInteger()) + return SDValue(); + + ConstantSDNode *FalseC = dyn_cast(False); + + // If the RHS (False) is 0, we swap the order of the operands + // of ISD::SELECT (obviously also inverting the condition) so that we can + // take advantage of conditional moves using the $0 register. + // Example: + // return (a != 0) ? x : 0; + // load $reg, x + // movz $reg, $0, a + if (!FalseC) + return SDValue(); + + const SDLoc DL(N); + + if (!FalseC->getZExtValue()) { + ISD::CondCode CC = cast(SetCC.getOperand(2))->get(); + SDValue True = N->getOperand(1); + + SetCC = DAG.getSetCC(DL, SetCC.getValueType(), SetCC.getOperand(0), + SetCC.getOperand(1), ISD::getSetCCInverse(CC, true)); + + return DAG.getNode(ISD::SELECT, DL, FalseTy, SetCC, False, True); + } + + // If both operands are integer constants there's a possibility that we + // can do some interesting optimizations. + SDValue True = N->getOperand(1); + ConstantSDNode *TrueC = dyn_cast(True); + + if (!TrueC || !True.getValueType().isInteger()) + return SDValue(); + + // We'll also ignore MVT::i64 operands as this optimizations proves + // to be ineffective because of the required sign extensions as the result + // of a SETCC operator is always MVT::i32 for non-vector types. + if (True.getValueType() == MVT::i64) + return SDValue(); + + int64_t Diff = TrueC->getSExtValue() - FalseC->getSExtValue(); + + // 1) (a < x) ? y : y-1 + // slti $reg1, a, x + // addiu $reg2, $reg1, y-1 + if (Diff == 1) + return DAG.getNode(ISD::ADD, DL, SetCC.getValueType(), SetCC, False); + + // 2) (a < x) ? y-1 : y + // slti $reg1, a, x + // xor $reg1, $reg1, 1 + // addiu $reg2, $reg1, y-1 + if (Diff == -1) { + ISD::CondCode CC = cast(SetCC.getOperand(2))->get(); + SetCC = DAG.getSetCC(DL, SetCC.getValueType(), SetCC.getOperand(0), + SetCC.getOperand(1), ISD::getSetCCInverse(CC, true)); + return DAG.getNode(ISD::ADD, DL, SetCC.getValueType(), SetCC, True); + } + + // Couldn't optimize. + return SDValue(); +} + +static SDValue performCMovFPCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + if (DCI.isBeforeLegalizeOps()) + return SDValue(); + + SDValue ValueIfTrue = N->getOperand(0), ValueIfFalse = N->getOperand(2); + + ConstantSDNode *FalseC = dyn_cast(ValueIfFalse); + if (!FalseC || FalseC->getZExtValue()) + return SDValue(); + + // Since RHS (False) is 0, we swap the order of the True/False operands + // (obviously also inverting the condition) so that we can + // take advantage of conditional moves using the $0 register. + // Example: + // return (a != 0) ? x : 0; + // load $reg, x + // movz $reg, $0, a + unsigned Opc = (N->getOpcode() == MaxisISD::CMovFP_T) ? MaxisISD::CMovFP_F : + MaxisISD::CMovFP_T; + + SDValue FCC = N->getOperand(1), Glue = N->getOperand(3); + return DAG.getNode(Opc, SDLoc(N), ValueIfFalse.getValueType(), + ValueIfFalse, FCC, ValueIfTrue, Glue); +} + +static SDValue performANDCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + if (DCI.isBeforeLegalizeOps() || !Subtarget.hasExtractInsert()) + return SDValue(); + + SDValue FirstOperand = N->getOperand(0); + unsigned FirstOperandOpc = FirstOperand.getOpcode(); + SDValue Mask = N->getOperand(1); + EVT ValTy = N->getValueType(0); + SDLoc DL(N); + + uint64_t Pos = 0, SMPos, SMSize; + ConstantSDNode *CN; + SDValue NewOperand; + unsigned Opc; + + // Op's second operand must be a shifted mask. + if (!(CN = dyn_cast(Mask)) || + !isShiftedMask(CN->getZExtValue(), SMPos, SMSize)) + return SDValue(); + + if (FirstOperandOpc == ISD::SRA || FirstOperandOpc == ISD::SRL) { + // Pattern match EXT. + // $dst = and ((sra or srl) $src , pos), (2**size - 1) + // => ext $dst, $src, pos, size + + // The second operand of the shift must be an immediate. + if (!(CN = dyn_cast(FirstOperand.getOperand(1)))) + return SDValue(); + + Pos = CN->getZExtValue(); + + // Return if the shifted mask does not start at bit 0 or the sum of its size + // and Pos exceeds the word's size. + if (SMPos != 0 || Pos + SMSize > ValTy.getSizeInBits()) + return SDValue(); + + Opc = MaxisISD::Ext; + NewOperand = FirstOperand.getOperand(0); + } else if (FirstOperandOpc == ISD::SHL && Subtarget.hasCnMaxis()) { + // Pattern match CINS. + // $dst = and (shl $src , pos), mask + // => cins $dst, $src, pos, size + // mask is a shifted mask with consecutive 1's, pos = shift amount, + // size = population count. + + // The second operand of the shift must be an immediate. + if (!(CN = dyn_cast(FirstOperand.getOperand(1)))) + return SDValue(); + + Pos = CN->getZExtValue(); + + if (SMPos != Pos || Pos >= ValTy.getSizeInBits() || SMSize >= 32 || + Pos + SMSize > ValTy.getSizeInBits()) + return SDValue(); + + NewOperand = FirstOperand.getOperand(0); + // SMSize is 'location' (position) in this case, not size. + SMSize--; + Opc = MaxisISD::CIns; + } else { + // Pattern match EXT. + // $dst = and $src, (2**size - 1) , if size > 16 + // => ext $dst, $src, pos, size , pos = 0 + + // If the mask is <= 0xffff, andi can be used instead. + if (CN->getZExtValue() <= 0xffff) + return SDValue(); + + // Return if the mask doesn't start at position 0. + if (SMPos) + return SDValue(); + + Opc = MaxisISD::Ext; + NewOperand = FirstOperand; + } + return DAG.getNode(Opc, DL, ValTy, NewOperand, + DAG.getConstant(Pos, DL, MVT::i32), + DAG.getConstant(SMSize, DL, MVT::i32)); +} + +static SDValue performORCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + // Pattern match INS. + // $dst = or (and $src1 , mask0), (and (shl $src, pos), mask1), + // where mask1 = (2**size - 1) << pos, mask0 = ~mask1 + // => ins $dst, $src, size, pos, $src1 + if (DCI.isBeforeLegalizeOps() || !Subtarget.hasExtractInsert()) + return SDValue(); + + SDValue And0 = N->getOperand(0), And1 = N->getOperand(1); + uint64_t SMPos0, SMSize0, SMPos1, SMSize1; + ConstantSDNode *CN, *CN1; + + // See if Op's first operand matches (and $src1 , mask0). + if (And0.getOpcode() != ISD::AND) + return SDValue(); + + if (!(CN = dyn_cast(And0.getOperand(1))) || + !isShiftedMask(~CN->getSExtValue(), SMPos0, SMSize0)) + return SDValue(); + + // See if Op's second operand matches (and (shl $src, pos), mask1). + if (And1.getOpcode() == ISD::AND && + And1.getOperand(0).getOpcode() == ISD::SHL) { + + if (!(CN = dyn_cast(And1.getOperand(1))) || + !isShiftedMask(CN->getZExtValue(), SMPos1, SMSize1)) + return SDValue(); + + // The shift masks must have the same position and size. + if (SMPos0 != SMPos1 || SMSize0 != SMSize1) + return SDValue(); + + SDValue Shl = And1.getOperand(0); + + if (!(CN = dyn_cast(Shl.getOperand(1)))) + return SDValue(); + + unsigned Shamt = CN->getZExtValue(); + + // Return if the shift amount and the first bit position of mask are not the + // same. + EVT ValTy = N->getValueType(0); + if ((Shamt != SMPos0) || (SMPos0 + SMSize0 > ValTy.getSizeInBits())) + return SDValue(); + + SDLoc DL(N); + return DAG.getNode(MaxisISD::Ins, DL, ValTy, Shl.getOperand(0), + DAG.getConstant(SMPos0, DL, MVT::i32), + DAG.getConstant(SMSize0, DL, MVT::i32), + And0.getOperand(0)); + } else { + // Pattern match DINS. + // $dst = or (and $src, mask0), mask1 + // where mask0 = ((1 << SMSize0) -1) << SMPos0 + // => dins $dst, $src, pos, size + if (~CN->getSExtValue() == ((((int64_t)1 << SMSize0) - 1) << SMPos0) && + ((SMSize0 + SMPos0 <= 64 && Subtarget.hasMaxis64r2()) || + (SMSize0 + SMPos0 <= 32))) { + // Check if AND instruction has constant as argument + bool isConstCase = And1.getOpcode() != ISD::AND; + if (And1.getOpcode() == ISD::AND) { + if (!(CN1 = dyn_cast(And1->getOperand(1)))) + return SDValue(); + } else { + if (!(CN1 = dyn_cast(N->getOperand(1)))) + return SDValue(); + } + // Don't generate INS if constant OR operand doesn't fit into bits + // cleared by constant AND operand. + if (CN->getSExtValue() & CN1->getSExtValue()) + return SDValue(); + + SDLoc DL(N); + EVT ValTy = N->getOperand(0)->getValueType(0); + SDValue Const1; + SDValue SrlX; + if (!isConstCase) { + Const1 = DAG.getConstant(SMPos0, DL, MVT::i32); + SrlX = DAG.getNode(ISD::SRL, DL, And1->getValueType(0), And1, Const1); + } + return DAG.getNode( + MaxisISD::Ins, DL, N->getValueType(0), + isConstCase + ? DAG.getConstant(CN1->getSExtValue() >> SMPos0, DL, ValTy) + : SrlX, + DAG.getConstant(SMPos0, DL, MVT::i32), + DAG.getConstant(ValTy.getSizeInBits() / 8 < 8 ? SMSize0 & 31 + : SMSize0, + DL, MVT::i32), + And0->getOperand(0)); + + } + return SDValue(); + } +} + +static SDValue performMADD_MSUBCombine(SDNode *ROOTNode, SelectionDAG &CurDAG, + const MaxisSubtarget &Subtarget) { + // ROOTNode must have a multiplication as an operand for the match to be + // successful. + if (ROOTNode->getOperand(0).getOpcode() != ISD::MUL && + ROOTNode->getOperand(1).getOpcode() != ISD::MUL) + return SDValue(); + + // We don't handle vector types here. + if (ROOTNode->getValueType(0).isVector()) + return SDValue(); + + // For MAXIS64, madd / msub instructions are inefficent to use with 64 bit + // arithmetic. E.g. + // (add (mul a b) c) => + // let res = (madd (mthi (drotr c 32))x(mtlo c) a b) in + // MAXIS64: (or (dsll (mfhi res) 32) (dsrl (dsll (mflo res) 32) 32) + // or + // MAXIS64R2: (dins (mflo res) (mfhi res) 32 32) + // + // The overhead of setting up the Hi/Lo registers and reassembling the + // result makes this a dubious optimzation for MAXIS64. The core of the + // problem is that Hi/Lo contain the upper and lower 32 bits of the + // operand and result. + // + // It requires a chain of 4 add/mul for MAXIS64R2 to get better code + // density than doing it naively, 5 for MAXIS64. Additionally, using + // madd/msub on MAXIS64 requires the operands actually be 32 bit sign + // extended operands, not true 64 bit values. + // + // FIXME: For the moment, disable this completely for MAXIS64. + if (Subtarget.hasMaxis64()) + return SDValue(); + + SDValue Mult = ROOTNode->getOperand(0).getOpcode() == ISD::MUL + ? ROOTNode->getOperand(0) + : ROOTNode->getOperand(1); + + SDValue AddOperand = ROOTNode->getOperand(0).getOpcode() == ISD::MUL + ? ROOTNode->getOperand(1) + : ROOTNode->getOperand(0); + + // Transform this to a MADD only if the user of this node is the add. + // If there are other users of the mul, this function returns here. + if (!Mult.hasOneUse()) + return SDValue(); + + // maddu and madd are unusual instructions in that on MAXIS64 bits 63..31 + // must be in canonical form, i.e. sign extended. For MAXIS32, the operands + // of the multiply must have 32 or more sign bits, otherwise we cannot + // perform this optimization. We have to check this here as we're performing + // this optimization pre-legalization. + SDValue MultLHS = Mult->getOperand(0); + SDValue MultRHS = Mult->getOperand(1); + + bool IsSigned = MultLHS->getOpcode() == ISD::SIGN_EXTEND && + MultRHS->getOpcode() == ISD::SIGN_EXTEND; + bool IsUnsigned = MultLHS->getOpcode() == ISD::ZERO_EXTEND && + MultRHS->getOpcode() == ISD::ZERO_EXTEND; + + if (!IsSigned && !IsUnsigned) + return SDValue(); + + // Initialize accumulator. + SDLoc DL(ROOTNode); + SDValue TopHalf; + SDValue BottomHalf; + BottomHalf = CurDAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, AddOperand, + CurDAG.getIntPtrConstant(0, DL)); + + TopHalf = CurDAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, AddOperand, + CurDAG.getIntPtrConstant(1, DL)); + SDValue ACCIn = CurDAG.getNode(MaxisISD::MTLOHI, DL, MVT::Untyped, + BottomHalf, + TopHalf); + + // Create MaxisMAdd(u) / MaxisMSub(u) node. + bool IsAdd = ROOTNode->getOpcode() == ISD::ADD; + unsigned Opcode = IsAdd ? (IsUnsigned ? MaxisISD::MAddu : MaxisISD::MAdd) + : (IsUnsigned ? MaxisISD::MSubu : MaxisISD::MSub); + SDValue MAddOps[3] = { + CurDAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Mult->getOperand(0)), + CurDAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Mult->getOperand(1)), ACCIn}; + EVT VTs[2] = {MVT::i32, MVT::i32}; + SDValue MAdd = CurDAG.getNode(Opcode, DL, VTs, MAddOps); + + SDValue ResLo = CurDAG.getNode(MaxisISD::MFLO, DL, MVT::i32, MAdd); + SDValue ResHi = CurDAG.getNode(MaxisISD::MFHI, DL, MVT::i32, MAdd); + SDValue Combined = + CurDAG.getNode(ISD::BUILD_PAIR, DL, MVT::i64, ResLo, ResHi); + return Combined; +} + +static SDValue performSUBCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + // (sub v0 (mul v1, v2)) => (msub v1, v2, v0) + if (DCI.isBeforeLegalizeOps()) { + if (Subtarget.hasMaxis32() && !Subtarget.hasMaxis32r6() && + !Subtarget.inMaxis16Mode() && N->getValueType(0) == MVT::i64) + return performMADD_MSUBCombine(N, DAG, Subtarget); + + return SDValue(); + } + + return SDValue(); +} + +static SDValue performADDCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + // (add v0 (mul v1, v2)) => (madd v1, v2, v0) + if (DCI.isBeforeLegalizeOps()) { + if (Subtarget.hasMaxis32() && !Subtarget.hasMaxis32r6() && + !Subtarget.inMaxis16Mode() && N->getValueType(0) == MVT::i64) + return performMADD_MSUBCombine(N, DAG, Subtarget); + + return SDValue(); + } + + // (add v0, (add v1, abs_lo(tjt))) => (add (add v0, v1), abs_lo(tjt)) + SDValue Add = N->getOperand(1); + + if (Add.getOpcode() != ISD::ADD) + return SDValue(); + + SDValue Lo = Add.getOperand(1); + + if ((Lo.getOpcode() != MaxisISD::Lo) || + (Lo.getOperand(0).getOpcode() != ISD::TargetJumpTable)) + return SDValue(); + + EVT ValTy = N->getValueType(0); + SDLoc DL(N); + + SDValue Add1 = DAG.getNode(ISD::ADD, DL, ValTy, N->getOperand(0), + Add.getOperand(0)); + return DAG.getNode(ISD::ADD, DL, ValTy, Add1, Lo); +} + +static SDValue performSHLCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + // Pattern match CINS. + // $dst = shl (and $src , imm), pos + // => cins $dst, $src, pos, size + + if (DCI.isBeforeLegalizeOps() || !Subtarget.hasCnMaxis()) + return SDValue(); + + SDValue FirstOperand = N->getOperand(0); + unsigned FirstOperandOpc = FirstOperand.getOpcode(); + SDValue SecondOperand = N->getOperand(1); + EVT ValTy = N->getValueType(0); + SDLoc DL(N); + + uint64_t Pos = 0, SMPos, SMSize; + ConstantSDNode *CN; + SDValue NewOperand; + + // The second operand of the shift must be an immediate. + if (!(CN = dyn_cast(SecondOperand))) + return SDValue(); + + Pos = CN->getZExtValue(); + + if (Pos >= ValTy.getSizeInBits()) + return SDValue(); + + if (FirstOperandOpc != ISD::AND) + return SDValue(); + + // AND's second operand must be a shifted mask. + if (!(CN = dyn_cast(FirstOperand.getOperand(1))) || + !isShiftedMask(CN->getZExtValue(), SMPos, SMSize)) + return SDValue(); + + // Return if the shifted mask does not start at bit 0 or the sum of its size + // and Pos exceeds the word's size. + if (SMPos != 0 || SMSize > 32 || Pos + SMSize > ValTy.getSizeInBits()) + return SDValue(); + + NewOperand = FirstOperand.getOperand(0); + // SMSize is 'location' (position) in this case, not size. + SMSize--; + + return DAG.getNode(MaxisISD::CIns, DL, ValTy, NewOperand, + DAG.getConstant(Pos, DL, MVT::i32), + DAG.getConstant(SMSize, DL, MVT::i32)); +} + +SDValue MaxisTargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) + const { + SelectionDAG &DAG = DCI.DAG; + unsigned Opc = N->getOpcode(); + + switch (Opc) { + default: break; + case ISD::SDIVREM: + case ISD::UDIVREM: + return performDivRemCombine(N, DAG, DCI, Subtarget); + case ISD::SELECT: + return performSELECTCombine(N, DAG, DCI, Subtarget); + case MaxisISD::CMovFP_F: + case MaxisISD::CMovFP_T: + return performCMovFPCombine(N, DAG, DCI, Subtarget); + case ISD::AND: + return performANDCombine(N, DAG, DCI, Subtarget); + case ISD::OR: + return performORCombine(N, DAG, DCI, Subtarget); + case ISD::ADD: + return performADDCombine(N, DAG, DCI, Subtarget); + case ISD::SHL: + return performSHLCombine(N, DAG, DCI, Subtarget); + case ISD::SUB: + return performSUBCombine(N, DAG, DCI, Subtarget); + } + + return SDValue(); +} + +bool MaxisTargetLowering::isCheapToSpeculateCttz() const { + return Subtarget.hasMaxis32(); +} + +bool MaxisTargetLowering::isCheapToSpeculateCtlz() const { + return Subtarget.hasMaxis32(); +} + +void +MaxisTargetLowering::LowerOperationWrapper(SDNode *N, + SmallVectorImpl &Results, + SelectionDAG &DAG) const { + SDValue Res = LowerOperation(SDValue(N, 0), DAG); + + for (unsigned I = 0, E = Res->getNumValues(); I != E; ++I) + Results.push_back(Res.getValue(I)); +} + +void +MaxisTargetLowering::ReplaceNodeResults(SDNode *N, + SmallVectorImpl &Results, + SelectionDAG &DAG) const { + return LowerOperationWrapper(N, Results, DAG); +} + +SDValue MaxisTargetLowering:: +LowerOperation(SDValue Op, SelectionDAG &DAG) const +{ + switch (Op.getOpcode()) + { + case ISD::BRCOND: return lowerBRCOND(Op, DAG); + case ISD::ConstantPool: return lowerConstantPool(Op, DAG); + case ISD::GlobalAddress: return lowerGlobalAddress(Op, DAG); + case ISD::BlockAddress: return lowerBlockAddress(Op, DAG); + case ISD::GlobalTLSAddress: return lowerGlobalTLSAddress(Op, DAG); + case ISD::JumpTable: return lowerJumpTable(Op, DAG); + case ISD::SELECT: return lowerSELECT(Op, DAG); + case ISD::SETCC: return lowerSETCC(Op, DAG); + case ISD::VASTART: return lowerVASTART(Op, DAG); + case ISD::VAARG: return lowerVAARG(Op, DAG); + case ISD::FCOPYSIGN: return lowerFCOPYSIGN(Op, DAG); + case ISD::FRAMEADDR: return lowerFRAMEADDR(Op, DAG); + case ISD::RETURNADDR: return lowerRETURNADDR(Op, DAG); + case ISD::EH_RETURN: return lowerEH_RETURN(Op, DAG); + case ISD::ATOMIC_FENCE: return lowerATOMIC_FENCE(Op, DAG); + case ISD::SHL_PARTS: return lowerShiftLeftParts(Op, DAG); + case ISD::SRA_PARTS: return lowerShiftRightParts(Op, DAG, true); + case ISD::SRL_PARTS: return lowerShiftRightParts(Op, DAG, false); + case ISD::LOAD: return lowerLOAD(Op, DAG); + case ISD::STORE: return lowerSTORE(Op, DAG); + case ISD::EH_DWARF_CFA: return lowerEH_DWARF_CFA(Op, DAG); + case ISD::FP_TO_SINT: return lowerFP_TO_SINT(Op, DAG); + } + return SDValue(); +} + +//===----------------------------------------------------------------------===// +// Lower helper functions +//===----------------------------------------------------------------------===// + +// addLiveIn - This helper function adds the specified physical register to the +// MachineFunction as a live in value. It also creates a corresponding +// virtual register for it. +static unsigned +addLiveIn(MachineFunction &MF, unsigned PReg, const TargetRegisterClass *RC) +{ + unsigned VReg = MF.getRegInfo().createVirtualRegister(RC); + MF.getRegInfo().addLiveIn(PReg, VReg); + return VReg; +} + +static MachineBasicBlock *insertDivByZeroTrap(MachineInstr &MI, + MachineBasicBlock &MBB, + const TargetInstrInfo &TII, + bool Is64Bit, bool IsMicroMaxis) { + if (NoZeroDivCheck) + return &MBB; + + // Insert instruction "teq $divisor_reg, $zero, 7". + MachineBasicBlock::iterator I(MI); + MachineInstrBuilder MIB; + MachineOperand &Divisor = MI.getOperand(2); + MIB = BuildMI(MBB, std::next(I), MI.getDebugLoc(), + TII.get(IsMicroMaxis ? Maxis::TEQ_MM : Maxis::TEQ)) + .addReg(Divisor.getReg(), getKillRegState(Divisor.isKill())) + .addReg(Maxis::ZERO) + .addImm(7); + + // Use the 32-bit sub-register if this is a 64-bit division. + if (Is64Bit) + MIB->getOperand(0).setSubReg(Maxis::sub_32); + + // Clear Divisor's kill flag. + Divisor.setIsKill(false); + + // We would normally delete the original instruction here but in this case + // we only needed to inject an additional instruction rather than replace it. + + return &MBB; +} + +MachineBasicBlock * +MaxisTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + switch (MI.getOpcode()) { + default: + llvm_unreachable("Unexpected instr type to insert"); + case Maxis::ATOMIC_LOAD_ADD_I8: + return emitAtomicBinaryPartword(MI, BB, 1, Maxis::ADDu); + case Maxis::ATOMIC_LOAD_ADD_I16: + return emitAtomicBinaryPartword(MI, BB, 2, Maxis::ADDu); + case Maxis::ATOMIC_LOAD_ADD_I32: + return emitAtomicBinary(MI, BB, 4, Maxis::ADDu); + case Maxis::ATOMIC_LOAD_ADD_I64: + return emitAtomicBinary(MI, BB, 8, Maxis::DADDu); + + case Maxis::ATOMIC_LOAD_AND_I8: + return emitAtomicBinaryPartword(MI, BB, 1, Maxis::AND); + case Maxis::ATOMIC_LOAD_AND_I16: + return emitAtomicBinaryPartword(MI, BB, 2, Maxis::AND); + case Maxis::ATOMIC_LOAD_AND_I32: + return emitAtomicBinary(MI, BB, 4, Maxis::AND); + case Maxis::ATOMIC_LOAD_AND_I64: + return emitAtomicBinary(MI, BB, 8, Maxis::AND64); + + case Maxis::ATOMIC_LOAD_OR_I8: + return emitAtomicBinaryPartword(MI, BB, 1, Maxis::OR); + case Maxis::ATOMIC_LOAD_OR_I16: + return emitAtomicBinaryPartword(MI, BB, 2, Maxis::OR); + case Maxis::ATOMIC_LOAD_OR_I32: + return emitAtomicBinary(MI, BB, 4, Maxis::OR); + case Maxis::ATOMIC_LOAD_OR_I64: + return emitAtomicBinary(MI, BB, 8, Maxis::OR64); + + case Maxis::ATOMIC_LOAD_XOR_I8: + return emitAtomicBinaryPartword(MI, BB, 1, Maxis::XOR); + case Maxis::ATOMIC_LOAD_XOR_I16: + return emitAtomicBinaryPartword(MI, BB, 2, Maxis::XOR); + case Maxis::ATOMIC_LOAD_XOR_I32: + return emitAtomicBinary(MI, BB, 4, Maxis::XOR); + case Maxis::ATOMIC_LOAD_XOR_I64: + return emitAtomicBinary(MI, BB, 8, Maxis::XOR64); + + case Maxis::ATOMIC_LOAD_NAND_I8: + return emitAtomicBinaryPartword(MI, BB, 1, 0, true); + case Maxis::ATOMIC_LOAD_NAND_I16: + return emitAtomicBinaryPartword(MI, BB, 2, 0, true); + case Maxis::ATOMIC_LOAD_NAND_I32: + return emitAtomicBinary(MI, BB, 4, 0, true); + case Maxis::ATOMIC_LOAD_NAND_I64: + return emitAtomicBinary(MI, BB, 8, 0, true); + + case Maxis::ATOMIC_LOAD_SUB_I8: + return emitAtomicBinaryPartword(MI, BB, 1, Maxis::SUBu); + case Maxis::ATOMIC_LOAD_SUB_I16: + return emitAtomicBinaryPartword(MI, BB, 2, Maxis::SUBu); + case Maxis::ATOMIC_LOAD_SUB_I32: + return emitAtomicBinary(MI, BB, 4, Maxis::SUBu); + case Maxis::ATOMIC_LOAD_SUB_I64: + return emitAtomicBinary(MI, BB, 8, Maxis::DSUBu); + + case Maxis::ATOMIC_SWAP_I8: + return emitAtomicBinaryPartword(MI, BB, 1, 0); + case Maxis::ATOMIC_SWAP_I16: + return emitAtomicBinaryPartword(MI, BB, 2, 0); + case Maxis::ATOMIC_SWAP_I32: + return emitAtomicBinary(MI, BB, 4, 0); + case Maxis::ATOMIC_SWAP_I64: + return emitAtomicBinary(MI, BB, 8, 0); + + case Maxis::ATOMIC_CMP_SWAP_I8: + return emitAtomicCmpSwapPartword(MI, BB, 1); + case Maxis::ATOMIC_CMP_SWAP_I16: + return emitAtomicCmpSwapPartword(MI, BB, 2); + case Maxis::ATOMIC_CMP_SWAP_I32: + return emitAtomicCmpSwap(MI, BB, 4); + case Maxis::ATOMIC_CMP_SWAP_I64: + return emitAtomicCmpSwap(MI, BB, 8); + case Maxis::PseudoSDIV: + case Maxis::PseudoUDIV: + case Maxis::DIV: + case Maxis::DIVU: + case Maxis::MOD: + case Maxis::MODU: + return insertDivByZeroTrap(MI, *BB, *Subtarget.getInstrInfo(), false, + false); + case Maxis::SDIV_MM_Pseudo: + case Maxis::UDIV_MM_Pseudo: + case Maxis::SDIV_MM: + case Maxis::UDIV_MM: + case Maxis::DIV_MMR6: + case Maxis::DIVU_MMR6: + case Maxis::MOD_MMR6: + case Maxis::MODU_MMR6: + return insertDivByZeroTrap(MI, *BB, *Subtarget.getInstrInfo(), false, true); + case Maxis::PseudoDSDIV: + case Maxis::PseudoDUDIV: + case Maxis::DDIV: + case Maxis::DDIVU: + case Maxis::DMOD: + case Maxis::DMODU: + return insertDivByZeroTrap(MI, *BB, *Subtarget.getInstrInfo(), true, false); + + case Maxis::PseudoSELECT_I: + case Maxis::PseudoSELECT_I64: + case Maxis::PseudoSELECT_S: + case Maxis::PseudoSELECT_D32: + case Maxis::PseudoSELECT_D64: + return emitPseudoSELECT(MI, BB, false, Maxis::BNE); + case Maxis::PseudoSELECTFP_F_I: + case Maxis::PseudoSELECTFP_F_I64: + case Maxis::PseudoSELECTFP_F_S: + case Maxis::PseudoSELECTFP_F_D32: + case Maxis::PseudoSELECTFP_F_D64: + return emitPseudoSELECT(MI, BB, true, Maxis::BC1F); + case Maxis::PseudoSELECTFP_T_I: + case Maxis::PseudoSELECTFP_T_I64: + case Maxis::PseudoSELECTFP_T_S: + case Maxis::PseudoSELECTFP_T_D32: + case Maxis::PseudoSELECTFP_T_D64: + return emitPseudoSELECT(MI, BB, true, Maxis::BC1T); + } +} + +// This function also handles Maxis::ATOMIC_SWAP_I32 (when BinOpcode == 0), and +// Maxis::ATOMIC_LOAD_NAND_I32 (when Nand == true) +MachineBasicBlock *MaxisTargetLowering::emitAtomicBinary(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned Size, + unsigned BinOpcode, + bool Nand) const { + assert((Size == 4 || Size == 8) && "Unsupported size for EmitAtomicBinary."); + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &RegInfo = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::getIntegerVT(Size * 8)); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + const bool ArePtrs64bit = ABI.ArePtrs64bit(); + DebugLoc DL = MI.getDebugLoc(); + unsigned LL, SC, AND, NOR, ZERO, BEQ; + + if (Size == 4) { + if (isMicroMaxis) { + LL = Maxis::LL_MM; + SC = Maxis::SC_MM; + } else { + LL = Subtarget.hasMaxis32r6() + ? (ArePtrs64bit ? Maxis::LL64_R6 : Maxis::LL_R6) + : (ArePtrs64bit ? Maxis::LL64 : Maxis::LL); + SC = Subtarget.hasMaxis32r6() + ? (ArePtrs64bit ? Maxis::SC64_R6 : Maxis::SC_R6) + : (ArePtrs64bit ? Maxis::SC64 : Maxis::SC); + } + + AND = Maxis::AND; + NOR = Maxis::NOR; + ZERO = Maxis::ZERO; + BEQ = Maxis::BEQ; + } else { + LL = Subtarget.hasMaxis64r6() ? Maxis::LLD_R6 : Maxis::LLD; + SC = Subtarget.hasMaxis64r6() ? Maxis::SCD_R6 : Maxis::SCD; + AND = Maxis::AND64; + NOR = Maxis::NOR64; + ZERO = Maxis::ZERO_64; + BEQ = Maxis::BEQ64; + } + + unsigned OldVal = MI.getOperand(0).getReg(); + unsigned Ptr = MI.getOperand(1).getReg(); + unsigned Incr = MI.getOperand(2).getReg(); + + unsigned StoreVal = RegInfo.createVirtualRegister(RC); + unsigned AndRes = RegInfo.createVirtualRegister(RC); + unsigned Success = RegInfo.createVirtualRegister(RC); + + // insert new blocks after the current block + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineBasicBlock *loopMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineFunction::iterator It = ++BB->getIterator(); + MF->insert(It, loopMBB); + MF->insert(It, exitMBB); + + // Transfer the remainder of BB and its successor edges to exitMBB. + exitMBB->splice(exitMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + exitMBB->transferSuccessorsAndUpdatePHIs(BB); + + // thisMBB: + // ... + // fallthrough --> loopMBB + BB->addSuccessor(loopMBB); + loopMBB->addSuccessor(loopMBB); + loopMBB->addSuccessor(exitMBB); + + // loopMBB: + // ll oldval, 0(ptr) + // storeval, oldval, incr + // sc success, storeval, 0(ptr) + // beq success, $0, loopMBB + BB = loopMBB; + BuildMI(BB, DL, TII->get(LL), OldVal).addReg(Ptr).addImm(0); + if (Nand) { + // and andres, oldval, incr + // nor storeval, $0, andres + BuildMI(BB, DL, TII->get(AND), AndRes).addReg(OldVal).addReg(Incr); + BuildMI(BB, DL, TII->get(NOR), StoreVal).addReg(ZERO).addReg(AndRes); + } else if (BinOpcode) { + // storeval, oldval, incr + BuildMI(BB, DL, TII->get(BinOpcode), StoreVal).addReg(OldVal).addReg(Incr); + } else { + StoreVal = Incr; + } + BuildMI(BB, DL, TII->get(SC), Success).addReg(StoreVal).addReg(Ptr).addImm(0); + BuildMI(BB, DL, TII->get(BEQ)).addReg(Success).addReg(ZERO).addMBB(loopMBB); + + MI.eraseFromParent(); // The instruction is gone now. + + return exitMBB; +} + +MachineBasicBlock *MaxisTargetLowering::emitSignExtendToI32InReg( + MachineInstr &MI, MachineBasicBlock *BB, unsigned Size, unsigned DstReg, + unsigned SrcReg) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + const DebugLoc &DL = MI.getDebugLoc(); + + if (Subtarget.hasMaxis32r2() && Size == 1) { + BuildMI(BB, DL, TII->get(Maxis::SEB), DstReg).addReg(SrcReg); + return BB; + } + + if (Subtarget.hasMaxis32r2() && Size == 2) { + BuildMI(BB, DL, TII->get(Maxis::SEH), DstReg).addReg(SrcReg); + return BB; + } + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &RegInfo = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + unsigned ScrReg = RegInfo.createVirtualRegister(RC); + + assert(Size < 32); + int64_t ShiftImm = 32 - (Size * 8); + + BuildMI(BB, DL, TII->get(Maxis::SLL), ScrReg).addReg(SrcReg).addImm(ShiftImm); + BuildMI(BB, DL, TII->get(Maxis::SRA), DstReg).addReg(ScrReg).addImm(ShiftImm); + + return BB; +} + +MachineBasicBlock *MaxisTargetLowering::emitAtomicBinaryPartword( + MachineInstr &MI, MachineBasicBlock *BB, unsigned Size, unsigned BinOpcode, + bool Nand) const { + assert((Size == 1 || Size == 2) && + "Unsupported size for EmitAtomicBinaryPartial."); + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &RegInfo = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + const bool ArePtrs64bit = ABI.ArePtrs64bit(); + const TargetRegisterClass *RCp = + getRegClassFor(ArePtrs64bit ? MVT::i64 : MVT::i32); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + unsigned Dest = MI.getOperand(0).getReg(); + unsigned Ptr = MI.getOperand(1).getReg(); + unsigned Incr = MI.getOperand(2).getReg(); + + unsigned AlignedAddr = RegInfo.createVirtualRegister(RCp); + unsigned ShiftAmt = RegInfo.createVirtualRegister(RC); + unsigned Mask = RegInfo.createVirtualRegister(RC); + unsigned Mask2 = RegInfo.createVirtualRegister(RC); + unsigned NewVal = RegInfo.createVirtualRegister(RC); + unsigned OldVal = RegInfo.createVirtualRegister(RC); + unsigned Incr2 = RegInfo.createVirtualRegister(RC); + unsigned MaskLSB2 = RegInfo.createVirtualRegister(RCp); + unsigned PtrLSB2 = RegInfo.createVirtualRegister(RC); + unsigned MaskUpper = RegInfo.createVirtualRegister(RC); + unsigned AndRes = RegInfo.createVirtualRegister(RC); + unsigned BinOpRes = RegInfo.createVirtualRegister(RC); + unsigned MaskedOldVal0 = RegInfo.createVirtualRegister(RC); + unsigned StoreVal = RegInfo.createVirtualRegister(RC); + unsigned MaskedOldVal1 = RegInfo.createVirtualRegister(RC); + unsigned SrlRes = RegInfo.createVirtualRegister(RC); + unsigned Success = RegInfo.createVirtualRegister(RC); + + unsigned LL, SC; + if (isMicroMaxis) { + LL = Maxis::LL_MM; + SC = Maxis::SC_MM; + } else { + LL = Subtarget.hasMaxis32r6() ? (ArePtrs64bit ? Maxis::LL64_R6 : Maxis::LL_R6) + : (ArePtrs64bit ? Maxis::LL64 : Maxis::LL); + SC = Subtarget.hasMaxis32r6() ? (ArePtrs64bit ? Maxis::SC64_R6 : Maxis::SC_R6) + : (ArePtrs64bit ? Maxis::SC64 : Maxis::SC); + } + + // insert new blocks after the current block + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineBasicBlock *loopMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineFunction::iterator It = ++BB->getIterator(); + MF->insert(It, loopMBB); + MF->insert(It, sinkMBB); + MF->insert(It, exitMBB); + + // Transfer the remainder of BB and its successor edges to exitMBB. + exitMBB->splice(exitMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + exitMBB->transferSuccessorsAndUpdatePHIs(BB); + + BB->addSuccessor(loopMBB); + loopMBB->addSuccessor(loopMBB); + loopMBB->addSuccessor(sinkMBB); + sinkMBB->addSuccessor(exitMBB); + + // thisMBB: + // addiu masklsb2,$0,-4 # 0xfffffffc + // and alignedaddr,ptr,masklsb2 + // andi ptrlsb2,ptr,3 + // sll shiftamt,ptrlsb2,3 + // ori maskupper,$0,255 # 0xff + // sll mask,maskupper,shiftamt + // nor mask2,$0,mask + // sll incr2,incr,shiftamt + + int64_t MaskImm = (Size == 1) ? 255 : 65535; + BuildMI(BB, DL, TII->get(ABI.GetPtrAddiuOp()), MaskLSB2) + .addReg(ABI.GetNullPtr()).addImm(-4); + BuildMI(BB, DL, TII->get(ABI.GetPtrAndOp()), AlignedAddr) + .addReg(Ptr).addReg(MaskLSB2); + BuildMI(BB, DL, TII->get(Maxis::ANDi), PtrLSB2) + .addReg(Ptr, 0, ArePtrs64bit ? Maxis::sub_32 : 0).addImm(3); + if (Subtarget.isLittle()) { + BuildMI(BB, DL, TII->get(Maxis::SLL), ShiftAmt).addReg(PtrLSB2).addImm(3); + } else { + unsigned Off = RegInfo.createVirtualRegister(RC); + BuildMI(BB, DL, TII->get(Maxis::XORi), Off) + .addReg(PtrLSB2).addImm((Size == 1) ? 3 : 2); + BuildMI(BB, DL, TII->get(Maxis::SLL), ShiftAmt).addReg(Off).addImm(3); + } + BuildMI(BB, DL, TII->get(Maxis::ORi), MaskUpper) + .addReg(Maxis::ZERO).addImm(MaskImm); + BuildMI(BB, DL, TII->get(Maxis::SLLV), Mask) + .addReg(MaskUpper).addReg(ShiftAmt); + BuildMI(BB, DL, TII->get(Maxis::NOR), Mask2).addReg(Maxis::ZERO).addReg(Mask); + BuildMI(BB, DL, TII->get(Maxis::SLLV), Incr2).addReg(Incr).addReg(ShiftAmt); + + // atomic.load.binop + // loopMBB: + // ll oldval,0(alignedaddr) + // binop binopres,oldval,incr2 + // and newval,binopres,mask + // and maskedoldval0,oldval,mask2 + // or storeval,maskedoldval0,newval + // sc success,storeval,0(alignedaddr) + // beq success,$0,loopMBB + + // atomic.swap + // loopMBB: + // ll oldval,0(alignedaddr) + // and newval,incr2,mask + // and maskedoldval0,oldval,mask2 + // or storeval,maskedoldval0,newval + // sc success,storeval,0(alignedaddr) + // beq success,$0,loopMBB + + BB = loopMBB; + BuildMI(BB, DL, TII->get(LL), OldVal).addReg(AlignedAddr).addImm(0); + if (Nand) { + // and andres, oldval, incr2 + // nor binopres, $0, andres + // and newval, binopres, mask + BuildMI(BB, DL, TII->get(Maxis::AND), AndRes).addReg(OldVal).addReg(Incr2); + BuildMI(BB, DL, TII->get(Maxis::NOR), BinOpRes) + .addReg(Maxis::ZERO).addReg(AndRes); + BuildMI(BB, DL, TII->get(Maxis::AND), NewVal).addReg(BinOpRes).addReg(Mask); + } else if (BinOpcode) { + // binopres, oldval, incr2 + // and newval, binopres, mask + BuildMI(BB, DL, TII->get(BinOpcode), BinOpRes).addReg(OldVal).addReg(Incr2); + BuildMI(BB, DL, TII->get(Maxis::AND), NewVal).addReg(BinOpRes).addReg(Mask); + } else { // atomic.swap + // and newval, incr2, mask + BuildMI(BB, DL, TII->get(Maxis::AND), NewVal).addReg(Incr2).addReg(Mask); + } + + BuildMI(BB, DL, TII->get(Maxis::AND), MaskedOldVal0) + .addReg(OldVal).addReg(Mask2); + BuildMI(BB, DL, TII->get(Maxis::OR), StoreVal) + .addReg(MaskedOldVal0).addReg(NewVal); + BuildMI(BB, DL, TII->get(SC), Success) + .addReg(StoreVal).addReg(AlignedAddr).addImm(0); + BuildMI(BB, DL, TII->get(Maxis::BEQ)) + .addReg(Success).addReg(Maxis::ZERO).addMBB(loopMBB); + + // sinkMBB: + // and maskedoldval1,oldval,mask + // srl srlres,maskedoldval1,shiftamt + // sign_extend dest,srlres + BB = sinkMBB; + + BuildMI(BB, DL, TII->get(Maxis::AND), MaskedOldVal1) + .addReg(OldVal).addReg(Mask); + BuildMI(BB, DL, TII->get(Maxis::SRLV), SrlRes) + .addReg(MaskedOldVal1).addReg(ShiftAmt); + BB = emitSignExtendToI32InReg(MI, BB, Size, Dest, SrlRes); + + MI.eraseFromParent(); // The instruction is gone now. + + return exitMBB; +} + +MachineBasicBlock *MaxisTargetLowering::emitAtomicCmpSwap(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned Size) const { + assert((Size == 4 || Size == 8) && "Unsupported size for EmitAtomicCmpSwap."); + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &RegInfo = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::getIntegerVT(Size * 8)); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + const bool ArePtrs64bit = ABI.ArePtrs64bit(); + DebugLoc DL = MI.getDebugLoc(); + unsigned LL, SC, ZERO, BNE, BEQ; + + if (Size == 4) { + if (isMicroMaxis) { + LL = Maxis::LL_MM; + SC = Maxis::SC_MM; + } else { + LL = Subtarget.hasMaxis32r6() + ? (ArePtrs64bit ? Maxis::LL64_R6 : Maxis::LL_R6) + : (ArePtrs64bit ? Maxis::LL64 : Maxis::LL); + SC = Subtarget.hasMaxis32r6() + ? (ArePtrs64bit ? Maxis::SC64_R6 : Maxis::SC_R6) + : (ArePtrs64bit ? Maxis::SC64 : Maxis::SC); + } + + ZERO = Maxis::ZERO; + BNE = Maxis::BNE; + BEQ = Maxis::BEQ; + } else { + LL = Subtarget.hasMaxis64r6() ? Maxis::LLD_R6 : Maxis::LLD; + SC = Subtarget.hasMaxis64r6() ? Maxis::SCD_R6 : Maxis::SCD; + ZERO = Maxis::ZERO_64; + BNE = Maxis::BNE64; + BEQ = Maxis::BEQ64; + } + + unsigned Dest = MI.getOperand(0).getReg(); + unsigned Ptr = MI.getOperand(1).getReg(); + unsigned OldVal = MI.getOperand(2).getReg(); + unsigned NewVal = MI.getOperand(3).getReg(); + + unsigned Success = RegInfo.createVirtualRegister(RC); + + // insert new blocks after the current block + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineBasicBlock *loop1MBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *loop2MBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineFunction::iterator It = ++BB->getIterator(); + MF->insert(It, loop1MBB); + MF->insert(It, loop2MBB); + MF->insert(It, exitMBB); + + // Transfer the remainder of BB and its successor edges to exitMBB. + exitMBB->splice(exitMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + exitMBB->transferSuccessorsAndUpdatePHIs(BB); + + // thisMBB: + // ... + // fallthrough --> loop1MBB + BB->addSuccessor(loop1MBB); + loop1MBB->addSuccessor(exitMBB); + loop1MBB->addSuccessor(loop2MBB); + loop2MBB->addSuccessor(loop1MBB); + loop2MBB->addSuccessor(exitMBB); + + // loop1MBB: + // ll dest, 0(ptr) + // bne dest, oldval, exitMBB + BB = loop1MBB; + BuildMI(BB, DL, TII->get(LL), Dest).addReg(Ptr).addImm(0); + BuildMI(BB, DL, TII->get(BNE)) + .addReg(Dest).addReg(OldVal).addMBB(exitMBB); + + // loop2MBB: + // sc success, newval, 0(ptr) + // beq success, $0, loop1MBB + BB = loop2MBB; + BuildMI(BB, DL, TII->get(SC), Success) + .addReg(NewVal).addReg(Ptr).addImm(0); + BuildMI(BB, DL, TII->get(BEQ)) + .addReg(Success).addReg(ZERO).addMBB(loop1MBB); + + MI.eraseFromParent(); // The instruction is gone now. + + return exitMBB; +} + +MachineBasicBlock *MaxisTargetLowering::emitAtomicCmpSwapPartword( + MachineInstr &MI, MachineBasicBlock *BB, unsigned Size) const { + assert((Size == 1 || Size == 2) && + "Unsupported size for EmitAtomicCmpSwapPartial."); + + MachineFunction *MF = BB->getParent(); + MachineRegisterInfo &RegInfo = MF->getRegInfo(); + const TargetRegisterClass *RC = getRegClassFor(MVT::i32); + const bool ArePtrs64bit = ABI.ArePtrs64bit(); + const TargetRegisterClass *RCp = + getRegClassFor(ArePtrs64bit ? MVT::i64 : MVT::i32); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + unsigned Dest = MI.getOperand(0).getReg(); + unsigned Ptr = MI.getOperand(1).getReg(); + unsigned CmpVal = MI.getOperand(2).getReg(); + unsigned NewVal = MI.getOperand(3).getReg(); + + unsigned AlignedAddr = RegInfo.createVirtualRegister(RCp); + unsigned ShiftAmt = RegInfo.createVirtualRegister(RC); + unsigned Mask = RegInfo.createVirtualRegister(RC); + unsigned Mask2 = RegInfo.createVirtualRegister(RC); + unsigned ShiftedCmpVal = RegInfo.createVirtualRegister(RC); + unsigned OldVal = RegInfo.createVirtualRegister(RC); + unsigned MaskedOldVal0 = RegInfo.createVirtualRegister(RC); + unsigned ShiftedNewVal = RegInfo.createVirtualRegister(RC); + unsigned MaskLSB2 = RegInfo.createVirtualRegister(RCp); + unsigned PtrLSB2 = RegInfo.createVirtualRegister(RC); + unsigned MaskUpper = RegInfo.createVirtualRegister(RC); + unsigned MaskedCmpVal = RegInfo.createVirtualRegister(RC); + unsigned MaskedNewVal = RegInfo.createVirtualRegister(RC); + unsigned MaskedOldVal1 = RegInfo.createVirtualRegister(RC); + unsigned StoreVal = RegInfo.createVirtualRegister(RC); + unsigned SrlRes = RegInfo.createVirtualRegister(RC); + unsigned Success = RegInfo.createVirtualRegister(RC); + unsigned LL, SC; + + if (isMicroMaxis) { + LL = Maxis::LL_MM; + SC = Maxis::SC_MM; + } else { + LL = Subtarget.hasMaxis32r6() ? (ArePtrs64bit ? Maxis::LL64_R6 : Maxis::LL_R6) + : (ArePtrs64bit ? Maxis::LL64 : Maxis::LL); + SC = Subtarget.hasMaxis32r6() ? (ArePtrs64bit ? Maxis::SC64_R6 : Maxis::SC_R6) + : (ArePtrs64bit ? Maxis::SC64 : Maxis::SC); + } + + // insert new blocks after the current block + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineBasicBlock *loop1MBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *loop2MBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineFunction::iterator It = ++BB->getIterator(); + MF->insert(It, loop1MBB); + MF->insert(It, loop2MBB); + MF->insert(It, sinkMBB); + MF->insert(It, exitMBB); + + // Transfer the remainder of BB and its successor edges to exitMBB. + exitMBB->splice(exitMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + exitMBB->transferSuccessorsAndUpdatePHIs(BB); + + BB->addSuccessor(loop1MBB); + loop1MBB->addSuccessor(sinkMBB); + loop1MBB->addSuccessor(loop2MBB); + loop2MBB->addSuccessor(loop1MBB); + loop2MBB->addSuccessor(sinkMBB); + sinkMBB->addSuccessor(exitMBB); + + // FIXME: computation of newval2 can be moved to loop2MBB. + // thisMBB: + // addiu masklsb2,$0,-4 # 0xfffffffc + // and alignedaddr,ptr,masklsb2 + // andi ptrlsb2,ptr,3 + // xori ptrlsb2,ptrlsb2,3 # Only for BE + // sll shiftamt,ptrlsb2,3 + // ori maskupper,$0,255 # 0xff + // sll mask,maskupper,shiftamt + // nor mask2,$0,mask + // andi maskedcmpval,cmpval,255 + // sll shiftedcmpval,maskedcmpval,shiftamt + // andi maskednewval,newval,255 + // sll shiftednewval,maskednewval,shiftamt + int64_t MaskImm = (Size == 1) ? 255 : 65535; + BuildMI(BB, DL, TII->get(ArePtrs64bit ? Maxis::DADDiu : Maxis::ADDiu), MaskLSB2) + .addReg(ABI.GetNullPtr()).addImm(-4); + BuildMI(BB, DL, TII->get(ArePtrs64bit ? Maxis::AND64 : Maxis::AND), AlignedAddr) + .addReg(Ptr).addReg(MaskLSB2); + BuildMI(BB, DL, TII->get(Maxis::ANDi), PtrLSB2) + .addReg(Ptr, 0, ArePtrs64bit ? Maxis::sub_32 : 0).addImm(3); + if (Subtarget.isLittle()) { + BuildMI(BB, DL, TII->get(Maxis::SLL), ShiftAmt).addReg(PtrLSB2).addImm(3); + } else { + unsigned Off = RegInfo.createVirtualRegister(RC); + BuildMI(BB, DL, TII->get(Maxis::XORi), Off) + .addReg(PtrLSB2).addImm((Size == 1) ? 3 : 2); + BuildMI(BB, DL, TII->get(Maxis::SLL), ShiftAmt).addReg(Off).addImm(3); + } + BuildMI(BB, DL, TII->get(Maxis::ORi), MaskUpper) + .addReg(Maxis::ZERO).addImm(MaskImm); + BuildMI(BB, DL, TII->get(Maxis::SLLV), Mask) + .addReg(MaskUpper).addReg(ShiftAmt); + BuildMI(BB, DL, TII->get(Maxis::NOR), Mask2).addReg(Maxis::ZERO).addReg(Mask); + BuildMI(BB, DL, TII->get(Maxis::ANDi), MaskedCmpVal) + .addReg(CmpVal).addImm(MaskImm); + BuildMI(BB, DL, TII->get(Maxis::SLLV), ShiftedCmpVal) + .addReg(MaskedCmpVal).addReg(ShiftAmt); + BuildMI(BB, DL, TII->get(Maxis::ANDi), MaskedNewVal) + .addReg(NewVal).addImm(MaskImm); + BuildMI(BB, DL, TII->get(Maxis::SLLV), ShiftedNewVal) + .addReg(MaskedNewVal).addReg(ShiftAmt); + + // loop1MBB: + // ll oldval,0(alginedaddr) + // and maskedoldval0,oldval,mask + // bne maskedoldval0,shiftedcmpval,sinkMBB + BB = loop1MBB; + BuildMI(BB, DL, TII->get(LL), OldVal).addReg(AlignedAddr).addImm(0); + BuildMI(BB, DL, TII->get(Maxis::AND), MaskedOldVal0) + .addReg(OldVal).addReg(Mask); + BuildMI(BB, DL, TII->get(Maxis::BNE)) + .addReg(MaskedOldVal0).addReg(ShiftedCmpVal).addMBB(sinkMBB); + + // loop2MBB: + // and maskedoldval1,oldval,mask2 + // or storeval,maskedoldval1,shiftednewval + // sc success,storeval,0(alignedaddr) + // beq success,$0,loop1MBB + BB = loop2MBB; + BuildMI(BB, DL, TII->get(Maxis::AND), MaskedOldVal1) + .addReg(OldVal).addReg(Mask2); + BuildMI(BB, DL, TII->get(Maxis::OR), StoreVal) + .addReg(MaskedOldVal1).addReg(ShiftedNewVal); + BuildMI(BB, DL, TII->get(SC), Success) + .addReg(StoreVal).addReg(AlignedAddr).addImm(0); + BuildMI(BB, DL, TII->get(Maxis::BEQ)) + .addReg(Success).addReg(Maxis::ZERO).addMBB(loop1MBB); + + // sinkMBB: + // srl srlres,maskedoldval0,shiftamt + // sign_extend dest,srlres + BB = sinkMBB; + + BuildMI(BB, DL, TII->get(Maxis::SRLV), SrlRes) + .addReg(MaskedOldVal0).addReg(ShiftAmt); + BB = emitSignExtendToI32InReg(MI, BB, Size, Dest, SrlRes); + + MI.eraseFromParent(); // The instruction is gone now. + + return exitMBB; +} + +SDValue MaxisTargetLowering::lowerBRCOND(SDValue Op, SelectionDAG &DAG) const { + // The first operand is the chain, the second is the condition, the third is + // the block to branch to if the condition is true. + SDValue Chain = Op.getOperand(0); + SDValue Dest = Op.getOperand(2); + SDLoc DL(Op); + + assert(!Subtarget.hasMaxis32r6() && !Subtarget.hasMaxis64r6()); + SDValue CondRes = createFPCmp(DAG, Op.getOperand(1)); + + // Return if flag is not set by a floating point comparison. + if (CondRes.getOpcode() != MaxisISD::FPCmp) + return Op; + + SDValue CCNode = CondRes.getOperand(2); + Maxis::CondCode CC = + (Maxis::CondCode)cast(CCNode)->getZExtValue(); + unsigned Opc = invertFPCondCodeUser(CC) ? Maxis::BRANCH_F : Maxis::BRANCH_T; + SDValue BrCode = DAG.getConstant(Opc, DL, MVT::i32); + SDValue FCC0 = DAG.getRegister(Maxis::FCC0, MVT::i32); + return DAG.getNode(MaxisISD::FPBrcond, DL, Op.getValueType(), Chain, BrCode, + FCC0, Dest, CondRes); +} + +SDValue MaxisTargetLowering:: +lowerSELECT(SDValue Op, SelectionDAG &DAG) const +{ + assert(!Subtarget.hasMaxis32r6() && !Subtarget.hasMaxis64r6()); + SDValue Cond = createFPCmp(DAG, Op.getOperand(0)); + + // Return if flag is not set by a floating point comparison. + if (Cond.getOpcode() != MaxisISD::FPCmp) + return Op; + + return createCMovFP(DAG, Cond, Op.getOperand(1), Op.getOperand(2), + SDLoc(Op)); +} + +SDValue MaxisTargetLowering::lowerSETCC(SDValue Op, SelectionDAG &DAG) const { + assert(!Subtarget.hasMaxis32r6() && !Subtarget.hasMaxis64r6()); + SDValue Cond = createFPCmp(DAG, Op); + + assert(Cond.getOpcode() == MaxisISD::FPCmp && + "Floating point operand expected."); + + SDLoc DL(Op); + SDValue True = DAG.getConstant(1, DL, MVT::i32); + SDValue False = DAG.getConstant(0, DL, MVT::i32); + + return createCMovFP(DAG, Cond, True, False, DL); +} + +SDValue MaxisTargetLowering::lowerGlobalAddress(SDValue Op, + SelectionDAG &DAG) const { + EVT Ty = Op.getValueType(); + GlobalAddressSDNode *N = cast(Op); + const GlobalValue *GV = N->getGlobal(); + + if (!isPositionIndependent()) { + const MaxisTargetObjectFile *TLOF = + static_cast( + getTargetMachine().getObjFileLowering()); + const GlobalObject *GO = GV->getBaseObject(); + if (GO && TLOF->IsGlobalInSmallSection(GO, getTargetMachine())) + // %gp_rel relocation + return getAddrGPRel(N, SDLoc(N), Ty, DAG, ABI.IsN64()); + + // %hi/%lo relocation + return Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) + // %highest/%higher/%hi/%lo relocation + : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + } + + // Every other architecture would use shouldAssumeDSOLocal in here, but + // maxis is special. + // * In PIC code maxis requires got loads even for local statics! + // * To save on got entries, for local statics the got entry contains the + // page and an additional add instruction takes care of the low bits. + // * It is legal to access a hidden symbol with a non hidden undefined, + // so one cannot guarantee that all access to a hidden symbol will know + // it is hidden. + // * Maxis linkers don't support creating a page and a full got entry for + // the same symbol. + // * Given all that, we have to use a full got entry for hidden symbols :-( + if (GV->hasLocalLinkage()) + return getAddrLocal(N, SDLoc(N), Ty, DAG, ABI.IsN32() || ABI.IsN64()); + + if (LargeGOT) + return getAddrGlobalLargeGOT( + N, SDLoc(N), Ty, DAG, MaxisII::MO_GOT_HI16, MaxisII::MO_GOT_LO16, + DAG.getEntryNode(), + MachinePointerInfo::getGOT(DAG.getMachineFunction())); + + return getAddrGlobal( + N, SDLoc(N), Ty, DAG, + (ABI.IsN32() || ABI.IsN64()) ? MaxisII::MO_GOT_DISP : MaxisII::MO_GOT, + DAG.getEntryNode(), MachinePointerInfo::getGOT(DAG.getMachineFunction())); +} + +SDValue MaxisTargetLowering::lowerBlockAddress(SDValue Op, + SelectionDAG &DAG) const { + BlockAddressSDNode *N = cast(Op); + EVT Ty = Op.getValueType(); + + if (!isPositionIndependent()) + return Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) + : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + + return getAddrLocal(N, SDLoc(N), Ty, DAG, ABI.IsN32() || ABI.IsN64()); +} + +SDValue MaxisTargetLowering:: +lowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const +{ + // If the relocation model is PIC, use the General Dynamic TLS Model or + // Local Dynamic TLS model, otherwise use the Initial Exec or + // Local Exec TLS Model. + + GlobalAddressSDNode *GA = cast(Op); + if (DAG.getTarget().Options.EmulatedTLS) + return LowerToTLSEmulatedModel(GA, DAG); + + SDLoc DL(GA); + const GlobalValue *GV = GA->getGlobal(); + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + + TLSModel::Model model = getTargetMachine().getTLSModel(GV); + + if (model == TLSModel::GeneralDynamic || model == TLSModel::LocalDynamic) { + // General Dynamic and Local Dynamic TLS Model. + unsigned Flag = (model == TLSModel::LocalDynamic) ? MaxisII::MO_TLSLDM + : MaxisII::MO_TLSGD; + + SDValue TGA = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, Flag); + SDValue Argument = DAG.getNode(MaxisISD::Wrapper, DL, PtrVT, + getGlobalReg(DAG, PtrVT), TGA); + unsigned PtrSize = PtrVT.getSizeInBits(); + IntegerType *PtrTy = Type::getIntNTy(*DAG.getContext(), PtrSize); + + SDValue TlsGetAddr = DAG.getExternalSymbol("__tls_get_addr", PtrVT); + + ArgListTy Args; + ArgListEntry Entry; + Entry.Node = Argument; + Entry.Ty = PtrTy; + Args.push_back(Entry); + + TargetLowering::CallLoweringInfo CLI(DAG); + CLI.setDebugLoc(DL) + .setChain(DAG.getEntryNode()) + .setLibCallee(CallingConv::C, PtrTy, TlsGetAddr, std::move(Args)); + std::pair CallResult = LowerCallTo(CLI); + + SDValue Ret = CallResult.first; + + if (model != TLSModel::LocalDynamic) + return Ret; + + SDValue TGAHi = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, + MaxisII::MO_DTPREL_HI); + SDValue Hi = DAG.getNode(MaxisISD::Hi, DL, PtrVT, TGAHi); + SDValue TGALo = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, + MaxisII::MO_DTPREL_LO); + SDValue Lo = DAG.getNode(MaxisISD::Lo, DL, PtrVT, TGALo); + SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, Hi, Ret); + return DAG.getNode(ISD::ADD, DL, PtrVT, Add, Lo); + } + + SDValue Offset; + if (model == TLSModel::InitialExec) { + // Initial Exec TLS Model + SDValue TGA = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, + MaxisII::MO_GOTTPREL); + TGA = DAG.getNode(MaxisISD::Wrapper, DL, PtrVT, getGlobalReg(DAG, PtrVT), + TGA); + Offset = + DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), TGA, MachinePointerInfo()); + } else { + // Local Exec TLS Model + assert(model == TLSModel::LocalExec); + SDValue TGAHi = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, + MaxisII::MO_TPREL_HI); + SDValue TGALo = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, + MaxisII::MO_TPREL_LO); + SDValue Hi = DAG.getNode(MaxisISD::Hi, DL, PtrVT, TGAHi); + SDValue Lo = DAG.getNode(MaxisISD::Lo, DL, PtrVT, TGALo); + Offset = DAG.getNode(ISD::ADD, DL, PtrVT, Hi, Lo); + } + + SDValue ThreadPointer = DAG.getNode(MaxisISD::ThreadPointer, DL, PtrVT); + return DAG.getNode(ISD::ADD, DL, PtrVT, ThreadPointer, Offset); +} + +SDValue MaxisTargetLowering:: +lowerJumpTable(SDValue Op, SelectionDAG &DAG) const +{ + JumpTableSDNode *N = cast(Op); + EVT Ty = Op.getValueType(); + + if (!isPositionIndependent()) + return Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) + : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + + return getAddrLocal(N, SDLoc(N), Ty, DAG, ABI.IsN32() || ABI.IsN64()); +} + +SDValue MaxisTargetLowering:: +lowerConstantPool(SDValue Op, SelectionDAG &DAG) const +{ + ConstantPoolSDNode *N = cast(Op); + EVT Ty = Op.getValueType(); + + if (!isPositionIndependent()) { + const MaxisTargetObjectFile *TLOF = + static_cast( + getTargetMachine().getObjFileLowering()); + + if (TLOF->IsConstantInSmallSection(DAG.getDataLayout(), N->getConstVal(), + getTargetMachine())) + // %gp_rel relocation + return getAddrGPRel(N, SDLoc(N), Ty, DAG, ABI.IsN64()); + + return Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) + : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + } + + return getAddrLocal(N, SDLoc(N), Ty, DAG, ABI.IsN32() || ABI.IsN64()); +} + +SDValue MaxisTargetLowering::lowerVASTART(SDValue Op, SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + MaxisFunctionInfo *FuncInfo = MF.getInfo(); + + SDLoc DL(Op); + SDValue FI = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), + getPointerTy(MF.getDataLayout())); + + // vastart just stores the address of the VarArgsFrameIndex slot into the + // memory location argument. + const Value *SV = cast(Op.getOperand(2))->getValue(); + return DAG.getStore(Op.getOperand(0), DL, FI, Op.getOperand(1), + MachinePointerInfo(SV)); +} + +SDValue MaxisTargetLowering::lowerVAARG(SDValue Op, SelectionDAG &DAG) const { + SDNode *Node = Op.getNode(); + EVT VT = Node->getValueType(0); + SDValue Chain = Node->getOperand(0); + SDValue VAListPtr = Node->getOperand(1); + unsigned Align = Node->getConstantOperandVal(3); + const Value *SV = cast(Node->getOperand(2))->getValue(); + SDLoc DL(Node); + unsigned ArgSlotSizeInBytes = (ABI.IsN32() || ABI.IsN64()) ? 8 : 4; + + SDValue VAListLoad = DAG.getLoad(getPointerTy(DAG.getDataLayout()), DL, Chain, + VAListPtr, MachinePointerInfo(SV)); + SDValue VAList = VAListLoad; + + // Re-align the pointer if necessary. + // It should only ever be necessary for 64-bit types on O32 since the minimum + // argument alignment is the same as the maximum type alignment for N32/N64. + // + // FIXME: We currently align too often. The code generator doesn't notice + // when the pointer is still aligned from the last va_arg (or pair of + // va_args for the i64 on O32 case). + if (Align > getMinStackArgumentAlignment()) { + assert(((Align & (Align-1)) == 0) && "Expected Align to be a power of 2"); + + VAList = DAG.getNode(ISD::ADD, DL, VAList.getValueType(), VAList, + DAG.getConstant(Align - 1, DL, VAList.getValueType())); + + VAList = DAG.getNode(ISD::AND, DL, VAList.getValueType(), VAList, + DAG.getConstant(-(int64_t)Align, DL, + VAList.getValueType())); + } + + // Increment the pointer, VAList, to the next vaarg. + auto &TD = DAG.getDataLayout(); + unsigned ArgSizeInBytes = + TD.getTypeAllocSize(VT.getTypeForEVT(*DAG.getContext())); + SDValue Tmp3 = + DAG.getNode(ISD::ADD, DL, VAList.getValueType(), VAList, + DAG.getConstant(alignTo(ArgSizeInBytes, ArgSlotSizeInBytes), + DL, VAList.getValueType())); + // Store the incremented VAList to the legalized pointer + Chain = DAG.getStore(VAListLoad.getValue(1), DL, Tmp3, VAListPtr, + MachinePointerInfo(SV)); + + // In big-endian mode we must adjust the pointer when the load size is smaller + // than the argument slot size. We must also reduce the known alignment to + // match. For example in the N64 ABI, we must add 4 bytes to the offset to get + // the correct half of the slot, and reduce the alignment from 8 (slot + // alignment) down to 4 (type alignment). + if (!Subtarget.isLittle() && ArgSizeInBytes < ArgSlotSizeInBytes) { + unsigned Adjustment = ArgSlotSizeInBytes - ArgSizeInBytes; + VAList = DAG.getNode(ISD::ADD, DL, VAListPtr.getValueType(), VAList, + DAG.getIntPtrConstant(Adjustment, DL)); + } + // Load the actual argument out of the pointer VAList + return DAG.getLoad(VT, DL, Chain, VAList, MachinePointerInfo()); +} + +static SDValue lowerFCOPYSIGN32(SDValue Op, SelectionDAG &DAG, + bool HasExtractInsert) { + EVT TyX = Op.getOperand(0).getValueType(); + EVT TyY = Op.getOperand(1).getValueType(); + SDLoc DL(Op); + SDValue Const1 = DAG.getConstant(1, DL, MVT::i32); + SDValue Const31 = DAG.getConstant(31, DL, MVT::i32); + SDValue Res; + + // If operand is of type f64, extract the upper 32-bit. Otherwise, bitcast it + // to i32. + SDValue X = (TyX == MVT::f32) ? + DAG.getNode(ISD::BITCAST, DL, MVT::i32, Op.getOperand(0)) : + DAG.getNode(MaxisISD::ExtractElementF64, DL, MVT::i32, Op.getOperand(0), + Const1); + SDValue Y = (TyY == MVT::f32) ? + DAG.getNode(ISD::BITCAST, DL, MVT::i32, Op.getOperand(1)) : + DAG.getNode(MaxisISD::ExtractElementF64, DL, MVT::i32, Op.getOperand(1), + Const1); + + if (HasExtractInsert) { + // ext E, Y, 31, 1 ; extract bit31 of Y + // ins X, E, 31, 1 ; insert extracted bit at bit31 of X + SDValue E = DAG.getNode(MaxisISD::Ext, DL, MVT::i32, Y, Const31, Const1); + Res = DAG.getNode(MaxisISD::Ins, DL, MVT::i32, E, Const31, Const1, X); + } else { + // sll SllX, X, 1 + // srl SrlX, SllX, 1 + // srl SrlY, Y, 31 + // sll SllY, SrlX, 31 + // or Or, SrlX, SllY + SDValue SllX = DAG.getNode(ISD::SHL, DL, MVT::i32, X, Const1); + SDValue SrlX = DAG.getNode(ISD::SRL, DL, MVT::i32, SllX, Const1); + SDValue SrlY = DAG.getNode(ISD::SRL, DL, MVT::i32, Y, Const31); + SDValue SllY = DAG.getNode(ISD::SHL, DL, MVT::i32, SrlY, Const31); + Res = DAG.getNode(ISD::OR, DL, MVT::i32, SrlX, SllY); + } + + if (TyX == MVT::f32) + return DAG.getNode(ISD::BITCAST, DL, Op.getOperand(0).getValueType(), Res); + + SDValue LowX = DAG.getNode(MaxisISD::ExtractElementF64, DL, MVT::i32, + Op.getOperand(0), + DAG.getConstant(0, DL, MVT::i32)); + return DAG.getNode(MaxisISD::BuildPairF64, DL, MVT::f64, LowX, Res); +} + +static SDValue lowerFCOPYSIGN64(SDValue Op, SelectionDAG &DAG, + bool HasExtractInsert) { + unsigned WidthX = Op.getOperand(0).getValueSizeInBits(); + unsigned WidthY = Op.getOperand(1).getValueSizeInBits(); + EVT TyX = MVT::getIntegerVT(WidthX), TyY = MVT::getIntegerVT(WidthY); + SDLoc DL(Op); + SDValue Const1 = DAG.getConstant(1, DL, MVT::i32); + + // Bitcast to integer nodes. + SDValue X = DAG.getNode(ISD::BITCAST, DL, TyX, Op.getOperand(0)); + SDValue Y = DAG.getNode(ISD::BITCAST, DL, TyY, Op.getOperand(1)); + + if (HasExtractInsert) { + // ext E, Y, width(Y) - 1, 1 ; extract bit width(Y)-1 of Y + // ins X, E, width(X) - 1, 1 ; insert extracted bit at bit width(X)-1 of X + SDValue E = DAG.getNode(MaxisISD::Ext, DL, TyY, Y, + DAG.getConstant(WidthY - 1, DL, MVT::i32), Const1); + + if (WidthX > WidthY) + E = DAG.getNode(ISD::ZERO_EXTEND, DL, TyX, E); + else if (WidthY > WidthX) + E = DAG.getNode(ISD::TRUNCATE, DL, TyX, E); + + SDValue I = DAG.getNode(MaxisISD::Ins, DL, TyX, E, + DAG.getConstant(WidthX - 1, DL, MVT::i32), Const1, + X); + return DAG.getNode(ISD::BITCAST, DL, Op.getOperand(0).getValueType(), I); + } + + // (d)sll SllX, X, 1 + // (d)srl SrlX, SllX, 1 + // (d)srl SrlY, Y, width(Y)-1 + // (d)sll SllY, SrlX, width(Y)-1 + // or Or, SrlX, SllY + SDValue SllX = DAG.getNode(ISD::SHL, DL, TyX, X, Const1); + SDValue SrlX = DAG.getNode(ISD::SRL, DL, TyX, SllX, Const1); + SDValue SrlY = DAG.getNode(ISD::SRL, DL, TyY, Y, + DAG.getConstant(WidthY - 1, DL, MVT::i32)); + + if (WidthX > WidthY) + SrlY = DAG.getNode(ISD::ZERO_EXTEND, DL, TyX, SrlY); + else if (WidthY > WidthX) + SrlY = DAG.getNode(ISD::TRUNCATE, DL, TyX, SrlY); + + SDValue SllY = DAG.getNode(ISD::SHL, DL, TyX, SrlY, + DAG.getConstant(WidthX - 1, DL, MVT::i32)); + SDValue Or = DAG.getNode(ISD::OR, DL, TyX, SrlX, SllY); + return DAG.getNode(ISD::BITCAST, DL, Op.getOperand(0).getValueType(), Or); +} + +SDValue +MaxisTargetLowering::lowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const { + if (Subtarget.isGP64bit()) + return lowerFCOPYSIGN64(Op, DAG, Subtarget.hasExtractInsert()); + + return lowerFCOPYSIGN32(Op, DAG, Subtarget.hasExtractInsert()); +} + +SDValue MaxisTargetLowering:: +lowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const { + // check the depth + assert((cast(Op.getOperand(0))->getZExtValue() == 0) && + "Frame address can only be determined for current frame."); + + MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); + MFI.setFrameAddressIsTaken(true); + EVT VT = Op.getValueType(); + SDLoc DL(Op); + SDValue FrameAddr = DAG.getCopyFromReg( + DAG.getEntryNode(), DL, ABI.IsN64() ? Maxis::FP_64 : Maxis::FP, VT); + return FrameAddr; +} + +SDValue MaxisTargetLowering::lowerRETURNADDR(SDValue Op, + SelectionDAG &DAG) const { + if (verifyReturnAddressArgumentIsConstant(Op, DAG)) + return SDValue(); + + // check the depth + assert((cast(Op.getOperand(0))->getZExtValue() == 0) && + "Return address can be determined only for current frame."); + + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MVT VT = Op.getSimpleValueType(); + unsigned RA = ABI.IsN64() ? Maxis::RA_64 : Maxis::RA; + MFI.setReturnAddressIsTaken(true); + + // Return RA, which contains the return address. Mark it an implicit live-in. + unsigned Reg = MF.addLiveIn(RA, getRegClassFor(VT)); + return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), Reg, VT); +} + +// An EH_RETURN is the result of lowering llvm.eh.return which in turn is +// generated from __builtin_eh_return (offset, handler) +// The effect of this is to adjust the stack pointer by "offset" +// and then branch to "handler". +SDValue MaxisTargetLowering::lowerEH_RETURN(SDValue Op, SelectionDAG &DAG) + const { + MachineFunction &MF = DAG.getMachineFunction(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + MaxisFI->setCallsEhReturn(); + SDValue Chain = Op.getOperand(0); + SDValue Offset = Op.getOperand(1); + SDValue Handler = Op.getOperand(2); + SDLoc DL(Op); + EVT Ty = ABI.IsN64() ? MVT::i64 : MVT::i32; + + // Store stack offset in V1, store jump target in V0. Glue CopyToReg and + // EH_RETURN nodes, so that instructions are emitted back-to-back. + unsigned OffsetReg = ABI.IsN64() ? Maxis::V1_64 : Maxis::V1; + unsigned AddrReg = ABI.IsN64() ? Maxis::V0_64 : Maxis::V0; + Chain = DAG.getCopyToReg(Chain, DL, OffsetReg, Offset, SDValue()); + Chain = DAG.getCopyToReg(Chain, DL, AddrReg, Handler, Chain.getValue(1)); + return DAG.getNode(MaxisISD::EH_RETURN, DL, MVT::Other, Chain, + DAG.getRegister(OffsetReg, Ty), + DAG.getRegister(AddrReg, getPointerTy(MF.getDataLayout())), + Chain.getValue(1)); +} + +SDValue MaxisTargetLowering::lowerATOMIC_FENCE(SDValue Op, + SelectionDAG &DAG) const { + // FIXME: Need pseudo-fence for 'singlethread' fences + // FIXME: Set SType for weaker fences where supported/appropriate. + unsigned SType = 0; + SDLoc DL(Op); + return DAG.getNode(MaxisISD::Sync, DL, MVT::Other, Op.getOperand(0), + DAG.getConstant(SType, DL, MVT::i32)); +} + +SDValue MaxisTargetLowering::lowerShiftLeftParts(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + MVT VT = Subtarget.isGP64bit() ? MVT::i64 : MVT::i32; + + SDValue Lo = Op.getOperand(0), Hi = Op.getOperand(1); + SDValue Shamt = Op.getOperand(2); + // if shamt < (VT.bits): + // lo = (shl lo, shamt) + // hi = (or (shl hi, shamt) (srl (srl lo, 1), ~shamt)) + // else: + // lo = 0 + // hi = (shl lo, shamt[4:0]) + SDValue Not = DAG.getNode(ISD::XOR, DL, MVT::i32, Shamt, + DAG.getConstant(-1, DL, MVT::i32)); + SDValue ShiftRight1Lo = DAG.getNode(ISD::SRL, DL, VT, Lo, + DAG.getConstant(1, DL, VT)); + SDValue ShiftRightLo = DAG.getNode(ISD::SRL, DL, VT, ShiftRight1Lo, Not); + SDValue ShiftLeftHi = DAG.getNode(ISD::SHL, DL, VT, Hi, Shamt); + SDValue Or = DAG.getNode(ISD::OR, DL, VT, ShiftLeftHi, ShiftRightLo); + SDValue ShiftLeftLo = DAG.getNode(ISD::SHL, DL, VT, Lo, Shamt); + SDValue Cond = DAG.getNode(ISD::AND, DL, MVT::i32, Shamt, + DAG.getConstant(VT.getSizeInBits(), DL, MVT::i32)); + Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, + DAG.getConstant(0, DL, VT), ShiftLeftLo); + Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, ShiftLeftLo, Or); + + SDValue Ops[2] = {Lo, Hi}; + return DAG.getMergeValues(Ops, DL); +} + +SDValue MaxisTargetLowering::lowerShiftRightParts(SDValue Op, SelectionDAG &DAG, + bool IsSRA) const { + SDLoc DL(Op); + SDValue Lo = Op.getOperand(0), Hi = Op.getOperand(1); + SDValue Shamt = Op.getOperand(2); + MVT VT = Subtarget.isGP64bit() ? MVT::i64 : MVT::i32; + + // if shamt < (VT.bits): + // lo = (or (shl (shl hi, 1), ~shamt) (srl lo, shamt)) + // if isSRA: + // hi = (sra hi, shamt) + // else: + // hi = (srl hi, shamt) + // else: + // if isSRA: + // lo = (sra hi, shamt[4:0]) + // hi = (sra hi, 31) + // else: + // lo = (srl hi, shamt[4:0]) + // hi = 0 + SDValue Not = DAG.getNode(ISD::XOR, DL, MVT::i32, Shamt, + DAG.getConstant(-1, DL, MVT::i32)); + SDValue ShiftLeft1Hi = DAG.getNode(ISD::SHL, DL, VT, Hi, + DAG.getConstant(1, DL, VT)); + SDValue ShiftLeftHi = DAG.getNode(ISD::SHL, DL, VT, ShiftLeft1Hi, Not); + SDValue ShiftRightLo = DAG.getNode(ISD::SRL, DL, VT, Lo, Shamt); + SDValue Or = DAG.getNode(ISD::OR, DL, VT, ShiftLeftHi, ShiftRightLo); + SDValue ShiftRightHi = DAG.getNode(IsSRA ? ISD::SRA : ISD::SRL, + DL, VT, Hi, Shamt); + SDValue Cond = DAG.getNode(ISD::AND, DL, MVT::i32, Shamt, + DAG.getConstant(VT.getSizeInBits(), DL, MVT::i32)); + SDValue Ext = DAG.getNode(ISD::SRA, DL, VT, Hi, + DAG.getConstant(VT.getSizeInBits() - 1, DL, VT)); + Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, ShiftRightHi, Or); + Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, + IsSRA ? Ext : DAG.getConstant(0, DL, VT), ShiftRightHi); + + SDValue Ops[2] = {Lo, Hi}; + return DAG.getMergeValues(Ops, DL); +} + +static SDValue createLoadLR(unsigned Opc, SelectionDAG &DAG, LoadSDNode *LD, + SDValue Chain, SDValue Src, unsigned Offset) { + SDValue Ptr = LD->getBasePtr(); + EVT VT = LD->getValueType(0), MemVT = LD->getMemoryVT(); + EVT BasePtrVT = Ptr.getValueType(); + SDLoc DL(LD); + SDVTList VTList = DAG.getVTList(VT, MVT::Other); + + if (Offset) + Ptr = DAG.getNode(ISD::ADD, DL, BasePtrVT, Ptr, + DAG.getConstant(Offset, DL, BasePtrVT)); + + SDValue Ops[] = { Chain, Ptr, Src }; + return DAG.getMemIntrinsicNode(Opc, DL, VTList, Ops, MemVT, + LD->getMemOperand()); +} + +// Expand an unaligned 32 or 64-bit integer load node. +SDValue MaxisTargetLowering::lowerLOAD(SDValue Op, SelectionDAG &DAG) const { + LoadSDNode *LD = cast(Op); + EVT MemVT = LD->getMemoryVT(); + + if (Subtarget.systemSupportsUnalignedAccess()) + return Op; + + // Return if load is aligned or if MemVT is neither i32 nor i64. + if ((LD->getAlignment() >= MemVT.getSizeInBits() / 8) || + ((MemVT != MVT::i32) && (MemVT != MVT::i64))) + return SDValue(); + + bool IsLittle = Subtarget.isLittle(); + EVT VT = Op.getValueType(); + ISD::LoadExtType ExtType = LD->getExtensionType(); + SDValue Chain = LD->getChain(), Undef = DAG.getUNDEF(VT); + + assert((VT == MVT::i32) || (VT == MVT::i64)); + + // Expand + // (set dst, (i64 (load baseptr))) + // to + // (set tmp, (ldl (add baseptr, 7), undef)) + // (set dst, (ldr baseptr, tmp)) + if ((VT == MVT::i64) && (ExtType == ISD::NON_EXTLOAD)) { + SDValue LDL = createLoadLR(MaxisISD::LDL, DAG, LD, Chain, Undef, + IsLittle ? 7 : 0); + return createLoadLR(MaxisISD::LDR, DAG, LD, LDL.getValue(1), LDL, + IsLittle ? 0 : 7); + } + + SDValue LWL = createLoadLR(MaxisISD::LWL, DAG, LD, Chain, Undef, + IsLittle ? 3 : 0); + SDValue LWR = createLoadLR(MaxisISD::LWR, DAG, LD, LWL.getValue(1), LWL, + IsLittle ? 0 : 3); + + // Expand + // (set dst, (i32 (load baseptr))) or + // (set dst, (i64 (sextload baseptr))) or + // (set dst, (i64 (extload baseptr))) + // to + // (set tmp, (lwl (add baseptr, 3), undef)) + // (set dst, (lwr baseptr, tmp)) + if ((VT == MVT::i32) || (ExtType == ISD::SEXTLOAD) || + (ExtType == ISD::EXTLOAD)) + return LWR; + + assert((VT == MVT::i64) && (ExtType == ISD::ZEXTLOAD)); + + // Expand + // (set dst, (i64 (zextload baseptr))) + // to + // (set tmp0, (lwl (add baseptr, 3), undef)) + // (set tmp1, (lwr baseptr, tmp0)) + // (set tmp2, (shl tmp1, 32)) + // (set dst, (srl tmp2, 32)) + SDLoc DL(LD); + SDValue Const32 = DAG.getConstant(32, DL, MVT::i32); + SDValue SLL = DAG.getNode(ISD::SHL, DL, MVT::i64, LWR, Const32); + SDValue SRL = DAG.getNode(ISD::SRL, DL, MVT::i64, SLL, Const32); + SDValue Ops[] = { SRL, LWR.getValue(1) }; + return DAG.getMergeValues(Ops, DL); +} + +static SDValue createStoreLR(unsigned Opc, SelectionDAG &DAG, StoreSDNode *SD, + SDValue Chain, unsigned Offset) { + SDValue Ptr = SD->getBasePtr(), Value = SD->getValue(); + EVT MemVT = SD->getMemoryVT(), BasePtrVT = Ptr.getValueType(); + SDLoc DL(SD); + SDVTList VTList = DAG.getVTList(MVT::Other); + + if (Offset) + Ptr = DAG.getNode(ISD::ADD, DL, BasePtrVT, Ptr, + DAG.getConstant(Offset, DL, BasePtrVT)); + + SDValue Ops[] = { Chain, Value, Ptr }; + return DAG.getMemIntrinsicNode(Opc, DL, VTList, Ops, MemVT, + SD->getMemOperand()); +} + +// Expand an unaligned 32 or 64-bit integer store node. +static SDValue lowerUnalignedIntStore(StoreSDNode *SD, SelectionDAG &DAG, + bool IsLittle) { + SDValue Value = SD->getValue(), Chain = SD->getChain(); + EVT VT = Value.getValueType(); + + // Expand + // (store val, baseptr) or + // (truncstore val, baseptr) + // to + // (swl val, (add baseptr, 3)) + // (swr val, baseptr) + if ((VT == MVT::i32) || SD->isTruncatingStore()) { + SDValue SWL = createStoreLR(MaxisISD::SWL, DAG, SD, Chain, + IsLittle ? 3 : 0); + return createStoreLR(MaxisISD::SWR, DAG, SD, SWL, IsLittle ? 0 : 3); + } + + assert(VT == MVT::i64); + + // Expand + // (store val, baseptr) + // to + // (sdl val, (add baseptr, 7)) + // (sdr val, baseptr) + SDValue SDL = createStoreLR(MaxisISD::SDL, DAG, SD, Chain, IsLittle ? 7 : 0); + return createStoreLR(MaxisISD::SDR, DAG, SD, SDL, IsLittle ? 0 : 7); +} + +// Lower (store (fp_to_sint $fp) $ptr) to (store (TruncIntFP $fp), $ptr). +static SDValue lowerFP_TO_SINT_STORE(StoreSDNode *SD, SelectionDAG &DAG) { + SDValue Val = SD->getValue(); + + if (Val.getOpcode() != ISD::FP_TO_SINT) + return SDValue(); + + EVT FPTy = EVT::getFloatingPointVT(Val.getValueSizeInBits()); + SDValue Tr = DAG.getNode(MaxisISD::TruncIntFP, SDLoc(Val), FPTy, + Val.getOperand(0)); + return DAG.getStore(SD->getChain(), SDLoc(SD), Tr, SD->getBasePtr(), + SD->getPointerInfo(), SD->getAlignment(), + SD->getMemOperand()->getFlags()); +} + +SDValue MaxisTargetLowering::lowerSTORE(SDValue Op, SelectionDAG &DAG) const { + StoreSDNode *SD = cast(Op); + EVT MemVT = SD->getMemoryVT(); + + // Lower unaligned integer stores. + if (!Subtarget.systemSupportsUnalignedAccess() && + (SD->getAlignment() < MemVT.getSizeInBits() / 8) && + ((MemVT == MVT::i32) || (MemVT == MVT::i64))) + return lowerUnalignedIntStore(SD, DAG, Subtarget.isLittle()); + + return lowerFP_TO_SINT_STORE(SD, DAG); +} + +SDValue MaxisTargetLowering::lowerEH_DWARF_CFA(SDValue Op, + SelectionDAG &DAG) const { + + // Return a fixed StackObject with offset 0 which points to the old stack + // pointer. + MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); + EVT ValTy = Op->getValueType(0); + int FI = MFI.CreateFixedObject(Op.getValueSizeInBits() / 8, 0, false); + return DAG.getFrameIndex(FI, ValTy); +} + +SDValue MaxisTargetLowering::lowerFP_TO_SINT(SDValue Op, + SelectionDAG &DAG) const { + EVT FPTy = EVT::getFloatingPointVT(Op.getValueSizeInBits()); + SDValue Trunc = DAG.getNode(MaxisISD::TruncIntFP, SDLoc(Op), FPTy, + Op.getOperand(0)); + return DAG.getNode(ISD::BITCAST, SDLoc(Op), Op.getValueType(), Trunc); +} + +//===----------------------------------------------------------------------===// +// Calling Convention Implementation +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// TODO: Implement a generic logic using tblgen that can support this. +// Maxis O32 ABI rules: +// --- +// i32 - Passed in A0, A1, A2, A3 and stack +// f32 - Only passed in f32 registers if no int reg has been used yet to hold +// an argument. Otherwise, passed in A1, A2, A3 and stack. +// f64 - Only passed in two aliased f32 registers if no int reg has been used +// yet to hold an argument. Otherwise, use A2, A3 and stack. If A1 is +// not used, it must be shadowed. If only A3 is available, shadow it and +// go to stack. +// vXiX - Received as scalarized i32s, passed in A0 - A3 and the stack. +// vXf32 - Passed in either a pair of registers {A0, A1}, {A2, A3} or {A0 - A3} +// with the remainder spilled to the stack. +// vXf64 - Passed in either {A0, A1, A2, A3} or {A2, A3} and in both cases +// spilling the remainder to the stack. +// +// For vararg functions, all arguments are passed in A0, A1, A2, A3 and stack. +//===----------------------------------------------------------------------===// + +static bool CC_MaxisO32(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State, ArrayRef F64Regs) { + const MaxisSubtarget &Subtarget = static_cast( + State.getMachineFunction().getSubtarget()); + + static const MCPhysReg IntRegs[] = { Maxis::A0, Maxis::A1, Maxis::A2, Maxis::A3 }; + + const MaxisCCState * MaxisState = static_cast(&State); + + static const MCPhysReg F32Regs[] = { Maxis::F12, Maxis::F14 }; + + static const MCPhysReg FloatVectorIntRegs[] = { Maxis::A0, Maxis::A2 }; + + // Do not process byval args here. + if (ArgFlags.isByVal()) + return true; + + // Promote i8 and i16 + if (ArgFlags.isInReg() && !Subtarget.isLittle()) { + if (LocVT == MVT::i8 || LocVT == MVT::i16 || LocVT == MVT::i32) { + LocVT = MVT::i32; + if (ArgFlags.isSExt()) + LocInfo = CCValAssign::SExtUpper; + else if (ArgFlags.isZExt()) + LocInfo = CCValAssign::ZExtUpper; + else + LocInfo = CCValAssign::AExtUpper; + } + } + + // Promote i8 and i16 + if (LocVT == MVT::i8 || LocVT == MVT::i16) { + LocVT = MVT::i32; + if (ArgFlags.isSExt()) + LocInfo = CCValAssign::SExt; + else if (ArgFlags.isZExt()) + LocInfo = CCValAssign::ZExt; + else + LocInfo = CCValAssign::AExt; + } + + unsigned Reg; + + // f32 and f64 are allocated in A0, A1, A2, A3 when either of the following + // is true: function is vararg, argument is 3rd or higher, there is previous + // argument which is not f32 or f64. + bool AllocateFloatsInIntReg = State.isVarArg() || ValNo > 1 || + State.getFirstUnallocated(F32Regs) != ValNo; + unsigned OrigAlign = ArgFlags.getOrigAlign(); + bool isI64 = (ValVT == MVT::i32 && OrigAlign == 8); + bool isVectorFloat = MaxisState->WasOriginalArgVectorFloat(ValNo); + + // The MAXIS vector ABI for floats passes them in a pair of registers + if (ValVT == MVT::i32 && isVectorFloat) { + // This is the start of an vector that was scalarized into an unknown number + // of components. It doesn't matter how many there are. Allocate one of the + // notional 8 byte aligned registers which map onto the argument stack, and + // shadow the register lost to alignment requirements. + if (ArgFlags.isSplit()) { + Reg = State.AllocateReg(FloatVectorIntRegs); + if (Reg == Maxis::A2) + State.AllocateReg(Maxis::A1); + else if (Reg == 0) + State.AllocateReg(Maxis::A3); + } else { + // If we're an intermediate component of the split, we can just attempt to + // allocate a register directly. + Reg = State.AllocateReg(IntRegs); + } + } else if (ValVT == MVT::i32 || (ValVT == MVT::f32 && AllocateFloatsInIntReg)) { + Reg = State.AllocateReg(IntRegs); + // If this is the first part of an i64 arg, + // the allocated register must be either A0 or A2. + if (isI64 && (Reg == Maxis::A1 || Reg == Maxis::A3)) + Reg = State.AllocateReg(IntRegs); + LocVT = MVT::i32; + } else if (ValVT == MVT::f64 && AllocateFloatsInIntReg) { + // Allocate int register and shadow next int register. If first + // available register is Maxis::A1 or Maxis::A3, shadow it too. + Reg = State.AllocateReg(IntRegs); + if (Reg == Maxis::A1 || Reg == Maxis::A3) + Reg = State.AllocateReg(IntRegs); + State.AllocateReg(IntRegs); + LocVT = MVT::i32; + } else if (ValVT.isFloatingPoint() && !AllocateFloatsInIntReg) { + // we are guaranteed to find an available float register + if (ValVT == MVT::f32) { + Reg = State.AllocateReg(F32Regs); + // Shadow int register + State.AllocateReg(IntRegs); + } else { + Reg = State.AllocateReg(F64Regs); + // Shadow int registers + unsigned Reg2 = State.AllocateReg(IntRegs); + if (Reg2 == Maxis::A1 || Reg2 == Maxis::A3) + State.AllocateReg(IntRegs); + State.AllocateReg(IntRegs); + } + } else + llvm_unreachable("Cannot handle this ValVT."); + + if (!Reg) { + unsigned Offset = State.AllocateStack(ValVT.getStoreSize(), OrigAlign); + State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo)); + } else + State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); + + return false; +} + +static bool CC_MaxisO32_FP32(unsigned ValNo, MVT ValVT, + MVT LocVT, CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State) { + static const MCPhysReg F64Regs[] = { Maxis::D6, Maxis::D7 }; + + return CC_MaxisO32(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State, F64Regs); +} + +static bool CC_MaxisO32_FP64(unsigned ValNo, MVT ValVT, + MVT LocVT, CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State) { + static const MCPhysReg F64Regs[] = { Maxis::D12_64, Maxis::D14_64 }; + + return CC_MaxisO32(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State, F64Regs); +} + +static bool CC_MaxisO32(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State) LLVM_ATTRIBUTE_UNUSED; + +#include "MaxisGenCallingConv.inc" + +//===----------------------------------------------------------------------===// +// Call Calling Convention Implementation +//===----------------------------------------------------------------------===// + +// Return next O32 integer argument register. +static unsigned getNextIntArgReg(unsigned Reg) { + assert((Reg == Maxis::A0) || (Reg == Maxis::A2)); + return (Reg == Maxis::A0) ? Maxis::A1 : Maxis::A3; +} + +SDValue MaxisTargetLowering::passArgOnStack(SDValue StackPtr, unsigned Offset, + SDValue Chain, SDValue Arg, + const SDLoc &DL, bool IsTailCall, + SelectionDAG &DAG) const { + if (!IsTailCall) { + SDValue PtrOff = + DAG.getNode(ISD::ADD, DL, getPointerTy(DAG.getDataLayout()), StackPtr, + DAG.getIntPtrConstant(Offset, DL)); + return DAG.getStore(Chain, DL, Arg, PtrOff, MachinePointerInfo()); + } + + MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); + int FI = MFI.CreateFixedObject(Arg.getValueSizeInBits() / 8, Offset, false); + SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + return DAG.getStore(Chain, DL, Arg, FIN, MachinePointerInfo(), + /* Alignment = */ 0, MachineMemOperand::MOVolatile); +} + +void MaxisTargetLowering:: +getOpndList(SmallVectorImpl &Ops, + std::deque> &RegsToPass, + bool IsPICCall, bool GlobalOrExternal, bool InternalLinkage, + bool IsCallReloc, CallLoweringInfo &CLI, SDValue Callee, + SDValue Chain) const { + // Insert node "GP copy globalreg" before call to function. + // + // R_MAXIS_CALL* operators (emitted when non-internal functions are called + // in PIC mode) allow symbols to be resolved via lazy binding. + // The lazy binding stub requires GP to point to the GOT. + // Note that we don't need GP to point to the GOT for indirect calls + // (when R_MAXIS_CALL* is not used for the call) because Maxis linker generates + // lazy binding stub for a function only when R_MAXIS_CALL* are the only relocs + // used for the function (that is, Maxis linker doesn't generate lazy binding + // stub for a function whose address is taken in the program). + if (IsPICCall && !InternalLinkage && IsCallReloc) { + unsigned GPReg = ABI.IsN64() ? Maxis::GP_64 : Maxis::GP; + EVT Ty = ABI.IsN64() ? MVT::i64 : MVT::i32; + RegsToPass.push_back(std::make_pair(GPReg, getGlobalReg(CLI.DAG, Ty))); + } + + // Build a sequence of copy-to-reg nodes chained together with token + // chain and flag operands which copy the outgoing args into registers. + // The InFlag in necessary since all emitted instructions must be + // stuck together. + SDValue InFlag; + + for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { + Chain = CLI.DAG.getCopyToReg(Chain, CLI.DL, RegsToPass[i].first, + RegsToPass[i].second, InFlag); + InFlag = Chain.getValue(1); + } + + // 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(CLI.DAG.getRegister(RegsToPass[i].first, + RegsToPass[i].second.getValueType())); + + // Add a register mask operand representing the call-preserved registers. + const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); + const uint32_t *Mask = + TRI->getCallPreservedMask(CLI.DAG.getMachineFunction(), CLI.CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + if (Subtarget.inMaxis16HardFloat()) { + if (GlobalAddressSDNode *G = dyn_cast(CLI.Callee)) { + StringRef Sym = G->getGlobal()->getName(); + Function *F = G->getGlobal()->getParent()->getFunction(Sym); + if (F && F->hasFnAttribute("__Maxis16RetHelper")) { + Mask = MaxisRegisterInfo::getMaxis16RetHelperMask(); + } + } + } + Ops.push_back(CLI.DAG.getRegisterMask(Mask)); + + if (InFlag.getNode()) + Ops.push_back(InFlag); +} + +/// LowerCall - functions arguments are copied from virtual regs to +/// (physical regs)/(stack frame), CALLSEQ_START and CALLSEQ_END are emitted. +SDValue +MaxisTargetLowering::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; + bool &IsTailCall = CLI.IsTailCall; + CallingConv::ID CallConv = CLI.CallConv; + bool IsVarArg = CLI.IsVarArg; + + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + const TargetFrameLowering *TFL = Subtarget.getFrameLowering(); + MaxisFunctionInfo *FuncInfo = MF.getInfo(); + bool IsPIC = isPositionIndependent(); + + // Analyze operands of the call, assigning locations to each operand. + SmallVector ArgLocs; + MaxisCCState CCInfo( + CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, *DAG.getContext(), + MaxisCCState::getSpecialCallingConvForCallee(Callee.getNode(), Subtarget)); + + // Allocate the reserved argument area. It seems strange to do this from the + // caller side but removing it breaks the frame size calculation. + CCInfo.AllocateStack(ABI.GetCalleeAllocdArgSizeInBytes(CallConv), 1); + + const ExternalSymbolSDNode *ES = + dyn_cast_or_null(Callee.getNode()); + CCInfo.AnalyzeCallOperands(Outs, CC_Maxis, CLI.getArgs(), + ES ? ES->getSymbol() : nullptr); + + // Get a count of how many bytes are to be pushed on the stack. + unsigned NextStackOffset = CCInfo.getNextStackOffset(); + + // Check if it's really possible to do a tail call. Restrict it to functions + // that are part of this compilation unit. + bool InternalLinkage = false; + if (IsTailCall) { + IsTailCall = isEligibleForTailCallOptimization( + CCInfo, NextStackOffset, *MF.getInfo()); + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + InternalLinkage = G->getGlobal()->hasInternalLinkage(); + IsTailCall &= (InternalLinkage || G->getGlobal()->hasLocalLinkage() || + G->getGlobal()->hasPrivateLinkage() || + G->getGlobal()->hasHiddenVisibility() || + G->getGlobal()->hasProtectedVisibility()); + } + } + if (!IsTailCall && CLI.CS && CLI.CS.isMustTailCall()) + report_fatal_error("failed to perform tail call elimination on a call " + "site marked musttail"); + + if (IsTailCall) + ++NumTailCalls; + + // Chain is the output chain of the last Load/Store or CopyToReg node. + // ByValChain is the output chain of the last Memcpy node created for copying + // byval arguments to the stack. + unsigned StackAlignment = TFL->getStackAlignment(); + NextStackOffset = alignTo(NextStackOffset, StackAlignment); + SDValue NextStackOffsetVal = DAG.getIntPtrConstant(NextStackOffset, DL, true); + + if (!IsTailCall) + Chain = DAG.getCALLSEQ_START(Chain, NextStackOffset, 0, DL); + + SDValue StackPtr = + DAG.getCopyFromReg(Chain, DL, ABI.IsN64() ? Maxis::SP_64 : Maxis::SP, + getPointerTy(DAG.getDataLayout())); + + std::deque> RegsToPass; + SmallVector MemOpChains; + + CCInfo.rewindByValRegsInfo(); + + // Walk the register/memloc assignments, inserting copies/loads. + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + SDValue Arg = OutVals[i]; + CCValAssign &VA = ArgLocs[i]; + MVT ValVT = VA.getValVT(), LocVT = VA.getLocVT(); + ISD::ArgFlagsTy Flags = Outs[i].Flags; + bool UseUpperBits = false; + + // ByVal Arg. + if (Flags.isByVal()) { + unsigned FirstByValReg, LastByValReg; + unsigned ByValIdx = CCInfo.getInRegsParamsProcessed(); + CCInfo.getInRegsParamInfo(ByValIdx, FirstByValReg, LastByValReg); + + assert(Flags.getByValSize() && + "ByVal args of size 0 should have been ignored by front-end."); + assert(ByValIdx < CCInfo.getInRegsParamsCount()); + assert(!IsTailCall && + "Do not tail-call optimize if there is a byval argument."); + passByValArg(Chain, DL, RegsToPass, MemOpChains, StackPtr, MFI, DAG, Arg, + FirstByValReg, LastByValReg, Flags, Subtarget.isLittle(), + VA); + CCInfo.nextInRegsParam(); + continue; + } + + // Promote the value if needed. + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: + if (VA.isRegLoc()) { + if ((ValVT == MVT::f32 && LocVT == MVT::i32) || + (ValVT == MVT::f64 && LocVT == MVT::i64) || + (ValVT == MVT::i64 && LocVT == MVT::f64)) + Arg = DAG.getNode(ISD::BITCAST, DL, LocVT, Arg); + else if (ValVT == MVT::f64 && LocVT == MVT::i32) { + SDValue Lo = DAG.getNode(MaxisISD::ExtractElementF64, DL, MVT::i32, + Arg, DAG.getConstant(0, DL, MVT::i32)); + SDValue Hi = DAG.getNode(MaxisISD::ExtractElementF64, DL, MVT::i32, + Arg, DAG.getConstant(1, DL, MVT::i32)); + if (!Subtarget.isLittle()) + std::swap(Lo, Hi); + unsigned LocRegLo = VA.getLocReg(); + unsigned LocRegHigh = getNextIntArgReg(LocRegLo); + RegsToPass.push_back(std::make_pair(LocRegLo, Lo)); + RegsToPass.push_back(std::make_pair(LocRegHigh, Hi)); + continue; + } + } + break; + case CCValAssign::BCvt: + Arg = DAG.getNode(ISD::BITCAST, DL, LocVT, Arg); + break; + case CCValAssign::SExtUpper: + UseUpperBits = true; + LLVM_FALLTHROUGH; + case CCValAssign::SExt: + Arg = DAG.getNode(ISD::SIGN_EXTEND, DL, LocVT, Arg); + break; + case CCValAssign::ZExtUpper: + UseUpperBits = true; + LLVM_FALLTHROUGH; + case CCValAssign::ZExt: + Arg = DAG.getNode(ISD::ZERO_EXTEND, DL, LocVT, Arg); + break; + case CCValAssign::AExtUpper: + UseUpperBits = true; + LLVM_FALLTHROUGH; + case CCValAssign::AExt: + Arg = DAG.getNode(ISD::ANY_EXTEND, DL, LocVT, Arg); + break; + } + + if (UseUpperBits) { + unsigned ValSizeInBits = Outs[i].ArgVT.getSizeInBits(); + unsigned LocSizeInBits = VA.getLocVT().getSizeInBits(); + Arg = DAG.getNode( + ISD::SHL, DL, VA.getLocVT(), Arg, + DAG.getConstant(LocSizeInBits - ValSizeInBits, DL, VA.getLocVT())); + } + + // Arguments that can be passed on register must be kept at + // RegsToPass vector + if (VA.isRegLoc()) { + RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); + continue; + } + + // Register can't get to this point... + assert(VA.isMemLoc()); + + // emit ISD::STORE whichs stores the + // parameter value to a stack Location + MemOpChains.push_back(passArgOnStack(StackPtr, VA.getLocMemOffset(), + Chain, Arg, DL, IsTailCall, DAG)); + } + + // Transform all store nodes into one single node because all store + // nodes are independent of each other. + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); + + // If the callee is a GlobalAddress/ExternalSymbol node (quite common, every + // direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol + // node so that legalize doesn't hack it. + + EVT Ty = Callee.getValueType(); + bool GlobalOrExternal = false, IsCallReloc = false; + + // The long-calls feature is ignored in case of PIC. + // While we do not support -mshared / -mno-shared properly, + // ignore long-calls in case of -mabicalls too. + if (!Subtarget.isABICalls() && !IsPIC) { + // If the function should be called using "long call", + // get its address into a register to prevent using + // of the `jal` instruction for the direct call. + if (auto *N = dyn_cast(Callee)) { + if (Subtarget.useLongCalls()) + Callee = Subtarget.hasSym32() + ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) + : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + } else if (auto *N = dyn_cast(Callee)) { + bool UseLongCalls = Subtarget.useLongCalls(); + // If the function has long-call/far/near attribute + // it overrides command line switch pased to the backend. + if (auto *F = dyn_cast(N->getGlobal())) { + if (F->hasFnAttribute("long-call")) + UseLongCalls = true; + else if (F->hasFnAttribute("short-call")) + UseLongCalls = false; + } + if (UseLongCalls) + Callee = Subtarget.hasSym32() + ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) + : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); + } + } + + if (GlobalAddressSDNode *G = dyn_cast(Callee)) { + if (IsPIC) { + const GlobalValue *Val = G->getGlobal(); + InternalLinkage = Val->hasInternalLinkage(); + + if (InternalLinkage) + Callee = getAddrLocal(G, DL, Ty, DAG, ABI.IsN32() || ABI.IsN64()); + else if (LargeGOT) { + Callee = getAddrGlobalLargeGOT(G, DL, Ty, DAG, MaxisII::MO_CALL_HI16, + MaxisII::MO_CALL_LO16, Chain, + FuncInfo->callPtrInfo(Val)); + IsCallReloc = true; + } else { + Callee = getAddrGlobal(G, DL, Ty, DAG, MaxisII::MO_GOT_CALL, Chain, + FuncInfo->callPtrInfo(Val)); + IsCallReloc = true; + } + } else + Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, + getPointerTy(DAG.getDataLayout()), 0, + MaxisII::MO_NO_FLAG); + GlobalOrExternal = true; + } + else if (ExternalSymbolSDNode *S = dyn_cast(Callee)) { + const char *Sym = S->getSymbol(); + + if (!IsPIC) // static + Callee = DAG.getTargetExternalSymbol( + Sym, getPointerTy(DAG.getDataLayout()), MaxisII::MO_NO_FLAG); + else if (LargeGOT) { + Callee = getAddrGlobalLargeGOT(S, DL, Ty, DAG, MaxisII::MO_CALL_HI16, + MaxisII::MO_CALL_LO16, Chain, + FuncInfo->callPtrInfo(Sym)); + IsCallReloc = true; + } else { // PIC + Callee = getAddrGlobal(S, DL, Ty, DAG, MaxisII::MO_GOT_CALL, Chain, + FuncInfo->callPtrInfo(Sym)); + IsCallReloc = true; + } + + GlobalOrExternal = true; + } + + SmallVector Ops(1, Chain); + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + + getOpndList(Ops, RegsToPass, IsPIC, GlobalOrExternal, InternalLinkage, + IsCallReloc, CLI, Callee, Chain); + + if (IsTailCall) { + MF.getFrameInfo().setHasTailCall(); + return DAG.getNode(MaxisISD::TailCall, DL, MVT::Other, Ops); + } + + Chain = DAG.getNode(MaxisISD::JmpLink, DL, NodeTys, Ops); + SDValue InFlag = Chain.getValue(1); + + // Create the CALLSEQ_END node. + Chain = DAG.getCALLSEQ_END(Chain, NextStackOffsetVal, + DAG.getIntPtrConstant(0, 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, CLI); +} + +/// LowerCallResult - Lower the result values of a call into the +/// appropriate copies out of appropriate physical registers. +SDValue MaxisTargetLowering::LowerCallResult( + SDValue Chain, SDValue InFlag, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals, + TargetLowering::CallLoweringInfo &CLI) const { + // Assign locations to each value returned by this call. + SmallVector RVLocs; + MaxisCCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs, + *DAG.getContext()); + + const ExternalSymbolSDNode *ES = + dyn_cast_or_null(CLI.Callee.getNode()); + CCInfo.AnalyzeCallResult(Ins, RetCC_Maxis, CLI.RetTy, + ES ? ES->getSymbol() : nullptr); + + // Copy all of the result registers out of their specified physreg. + for (unsigned i = 0; i != RVLocs.size(); ++i) { + CCValAssign &VA = RVLocs[i]; + assert(VA.isRegLoc() && "Can only return in registers!"); + + SDValue Val = DAG.getCopyFromReg(Chain, DL, RVLocs[i].getLocReg(), + RVLocs[i].getLocVT(), InFlag); + Chain = Val.getValue(1); + InFlag = Val.getValue(2); + + if (VA.isUpperBitsInLoc()) { + unsigned ValSizeInBits = Ins[i].ArgVT.getSizeInBits(); + unsigned LocSizeInBits = VA.getLocVT().getSizeInBits(); + unsigned Shift = + VA.getLocInfo() == CCValAssign::ZExtUpper ? ISD::SRL : ISD::SRA; + Val = DAG.getNode( + Shift, DL, VA.getLocVT(), Val, + DAG.getConstant(LocSizeInBits - ValSizeInBits, DL, VA.getLocVT())); + } + + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: + break; + case CCValAssign::BCvt: + Val = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), Val); + break; + case CCValAssign::AExt: + case CCValAssign::AExtUpper: + Val = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val); + break; + case CCValAssign::ZExt: + case CCValAssign::ZExtUpper: + Val = DAG.getNode(ISD::AssertZext, DL, VA.getLocVT(), Val, + DAG.getValueType(VA.getValVT())); + Val = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val); + break; + case CCValAssign::SExt: + case CCValAssign::SExtUpper: + Val = DAG.getNode(ISD::AssertSext, DL, VA.getLocVT(), Val, + DAG.getValueType(VA.getValVT())); + Val = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val); + break; + } + + InVals.push_back(Val); + } + + return Chain; +} + +static SDValue UnpackFromArgumentSlot(SDValue Val, const CCValAssign &VA, + EVT ArgVT, const SDLoc &DL, + SelectionDAG &DAG) { + MVT LocVT = VA.getLocVT(); + EVT ValVT = VA.getValVT(); + + // Shift into the upper bits if necessary. + switch (VA.getLocInfo()) { + default: + break; + case CCValAssign::AExtUpper: + case CCValAssign::SExtUpper: + case CCValAssign::ZExtUpper: { + unsigned ValSizeInBits = ArgVT.getSizeInBits(); + unsigned LocSizeInBits = VA.getLocVT().getSizeInBits(); + unsigned Opcode = + VA.getLocInfo() == CCValAssign::ZExtUpper ? ISD::SRL : ISD::SRA; + Val = DAG.getNode( + Opcode, DL, VA.getLocVT(), Val, + DAG.getConstant(LocSizeInBits - ValSizeInBits, DL, VA.getLocVT())); + break; + } + } + + // If this is an value smaller than the argument slot size (32-bit for O32, + // 64-bit for N32/N64), it has been promoted in some way to the argument slot + // size. Extract the value and insert any appropriate assertions regarding + // sign/zero extension. + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: + break; + case CCValAssign::AExtUpper: + case CCValAssign::AExt: + Val = DAG.getNode(ISD::TRUNCATE, DL, ValVT, Val); + break; + case CCValAssign::SExtUpper: + case CCValAssign::SExt: + Val = DAG.getNode(ISD::AssertSext, DL, LocVT, Val, DAG.getValueType(ValVT)); + Val = DAG.getNode(ISD::TRUNCATE, DL, ValVT, Val); + break; + case CCValAssign::ZExtUpper: + case CCValAssign::ZExt: + Val = DAG.getNode(ISD::AssertZext, DL, LocVT, Val, DAG.getValueType(ValVT)); + Val = DAG.getNode(ISD::TRUNCATE, DL, ValVT, Val); + break; + case CCValAssign::BCvt: + Val = DAG.getNode(ISD::BITCAST, DL, ValVT, Val); + break; + } + + return Val; +} + +//===----------------------------------------------------------------------===// +// Formal Arguments Calling Convention Implementation +//===----------------------------------------------------------------------===// +/// LowerFormalArguments - transform physical registers into virtual registers +/// and generate load operations for arguments places on the stack. +SDValue MaxisTargetLowering::LowerFormalArguments( + SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, + const SmallVectorImpl &Ins, const SDLoc &DL, + SelectionDAG &DAG, SmallVectorImpl &InVals) const { + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + MaxisFI->setVarArgsFrameIndex(0); + + // Used with vargs to acumulate store chains. + std::vector OutChains; + + // Assign locations to all of the incoming arguments. + SmallVector ArgLocs; + MaxisCCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, + *DAG.getContext()); + CCInfo.AllocateStack(ABI.GetCalleeAllocdArgSizeInBytes(CallConv), 1); + const Function &Func = DAG.getMachineFunction().getFunction(); + Function::const_arg_iterator FuncArg = Func.arg_begin(); + + if (Func.hasFnAttribute("interrupt") && !Func.arg_empty()) + report_fatal_error( + "Functions with the interrupt attribute cannot have arguments!"); + + CCInfo.AnalyzeFormalArguments(Ins, CC_Maxis_FixedArg); + MaxisFI->setFormalArgInfo(CCInfo.getNextStackOffset(), + CCInfo.getInRegsParamsCount() > 0); + + unsigned CurArgIdx = 0; + CCInfo.rewindByValRegsInfo(); + + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + CCValAssign &VA = ArgLocs[i]; + if (Ins[i].isOrigArg()) { + std::advance(FuncArg, Ins[i].getOrigArgIndex() - CurArgIdx); + CurArgIdx = Ins[i].getOrigArgIndex(); + } + EVT ValVT = VA.getValVT(); + ISD::ArgFlagsTy Flags = Ins[i].Flags; + bool IsRegLoc = VA.isRegLoc(); + + if (Flags.isByVal()) { + assert(Ins[i].isOrigArg() && "Byval arguments cannot be implicit"); + unsigned FirstByValReg, LastByValReg; + unsigned ByValIdx = CCInfo.getInRegsParamsProcessed(); + CCInfo.getInRegsParamInfo(ByValIdx, FirstByValReg, LastByValReg); + + assert(Flags.getByValSize() && + "ByVal args of size 0 should have been ignored by front-end."); + assert(ByValIdx < CCInfo.getInRegsParamsCount()); + copyByValRegs(Chain, DL, OutChains, DAG, Flags, InVals, &*FuncArg, + FirstByValReg, LastByValReg, VA, CCInfo); + CCInfo.nextInRegsParam(); + continue; + } + + // Arguments stored on registers + if (IsRegLoc) { + MVT RegVT = VA.getLocVT(); + unsigned ArgReg = VA.getLocReg(); + const TargetRegisterClass *RC = getRegClassFor(RegVT); + + // Transform the arguments stored on + // physical registers into virtual ones + unsigned Reg = addLiveIn(DAG.getMachineFunction(), ArgReg, RC); + SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegVT); + + ArgValue = UnpackFromArgumentSlot(ArgValue, VA, Ins[i].ArgVT, DL, DAG); + + // Handle floating point arguments passed in integer registers and + // long double arguments passed in floating point registers. + if ((RegVT == MVT::i32 && ValVT == MVT::f32) || + (RegVT == MVT::i64 && ValVT == MVT::f64) || + (RegVT == MVT::f64 && ValVT == MVT::i64)) + ArgValue = DAG.getNode(ISD::BITCAST, DL, ValVT, ArgValue); + else if (ABI.IsO32() && RegVT == MVT::i32 && + ValVT == MVT::f64) { + unsigned Reg2 = addLiveIn(DAG.getMachineFunction(), + getNextIntArgReg(ArgReg), RC); + SDValue ArgValue2 = DAG.getCopyFromReg(Chain, DL, Reg2, RegVT); + if (!Subtarget.isLittle()) + std::swap(ArgValue, ArgValue2); + ArgValue = DAG.getNode(MaxisISD::BuildPairF64, DL, MVT::f64, + ArgValue, ArgValue2); + } + + InVals.push_back(ArgValue); + } else { // VA.isRegLoc() + MVT LocVT = VA.getLocVT(); + + if (ABI.IsO32()) { + // We ought to be able to use LocVT directly but O32 sets it to i32 + // when allocating floating point values to integer registers. + // This shouldn't influence how we load the value into registers unless + // we are targeting softfloat. + if (VA.getValVT().isFloatingPoint() && !Subtarget.useSoftFloat()) + LocVT = VA.getValVT(); + } + + // sanity check + assert(VA.isMemLoc()); + + // The stack pointer offset is relative to the caller stack frame. + int FI = MFI.CreateFixedObject(LocVT.getSizeInBits() / 8, + VA.getLocMemOffset(), true); + + // Create load nodes to retrieve arguments from the stack + SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + SDValue ArgValue = DAG.getLoad( + LocVT, DL, Chain, FIN, + MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); + OutChains.push_back(ArgValue.getValue(1)); + + ArgValue = UnpackFromArgumentSlot(ArgValue, VA, Ins[i].ArgVT, DL, DAG); + + InVals.push_back(ArgValue); + } + } + + for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { + // The maxis ABIs for returning structs by value requires that we copy + // the sret argument into $v0 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 = MaxisFI->getSRetReturnReg(); + if (!Reg) { + Reg = MF.getRegInfo().createVirtualRegister( + getRegClassFor(ABI.IsN64() ? MVT::i64 : MVT::i32)); + MaxisFI->setSRetReturnReg(Reg); + } + SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), DL, Reg, InVals[i]); + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Copy, Chain); + break; + } + } + + if (IsVarArg) + writeVarArgRegs(OutChains, Chain, DL, DAG, CCInfo); + + // All stores are grouped in one node to allow the matching between + // the size of Ins and InVals. This only happens when on varg functions + if (!OutChains.empty()) { + OutChains.push_back(Chain); + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, OutChains); + } + + return Chain; +} + +//===----------------------------------------------------------------------===// +// Return Value Calling Convention Implementation +//===----------------------------------------------------------------------===// + +bool +MaxisTargetLowering::CanLowerReturn(CallingConv::ID CallConv, + MachineFunction &MF, bool IsVarArg, + const SmallVectorImpl &Outs, + LLVMContext &Context) const { + SmallVector RVLocs; + MaxisCCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context); + return CCInfo.CheckReturn(Outs, RetCC_Maxis); +} + +bool +MaxisTargetLowering::shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const { + if ((ABI.IsN32() || ABI.IsN64()) && Type == MVT::i32) + return true; + + return IsSigned; +} + +SDValue +MaxisTargetLowering::LowerInterruptReturn(SmallVectorImpl &RetOps, + const SDLoc &DL, + SelectionDAG &DAG) const { + MachineFunction &MF = DAG.getMachineFunction(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + MaxisFI->setISR(); + + return DAG.getNode(MaxisISD::ERet, DL, MVT::Other, RetOps); +} + +SDValue +MaxisTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool IsVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SDLoc &DL, SelectionDAG &DAG) const { + // CCValAssign - represent the assignment of + // the return value to a location + SmallVector RVLocs; + MachineFunction &MF = DAG.getMachineFunction(); + + // CCState - Info about the registers and stack slot. + MaxisCCState CCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext()); + + // Analyze return values. + CCInfo.AnalyzeReturn(Outs, RetCC_Maxis); + + SDValue Flag; + SmallVector RetOps(1, Chain); + + // Copy the result values into the output registers. + for (unsigned i = 0; i != RVLocs.size(); ++i) { + SDValue Val = OutVals[i]; + CCValAssign &VA = RVLocs[i]; + assert(VA.isRegLoc() && "Can only return in registers!"); + bool UseUpperBits = false; + + switch (VA.getLocInfo()) { + default: + llvm_unreachable("Unknown loc info!"); + case CCValAssign::Full: + break; + case CCValAssign::BCvt: + Val = DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), Val); + break; + case CCValAssign::AExtUpper: + UseUpperBits = true; + LLVM_FALLTHROUGH; + case CCValAssign::AExt: + Val = DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Val); + break; + case CCValAssign::ZExtUpper: + UseUpperBits = true; + LLVM_FALLTHROUGH; + case CCValAssign::ZExt: + Val = DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Val); + break; + case CCValAssign::SExtUpper: + UseUpperBits = true; + LLVM_FALLTHROUGH; + case CCValAssign::SExt: + Val = DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), Val); + break; + } + + if (UseUpperBits) { + unsigned ValSizeInBits = Outs[i].ArgVT.getSizeInBits(); + unsigned LocSizeInBits = VA.getLocVT().getSizeInBits(); + Val = DAG.getNode( + ISD::SHL, DL, VA.getLocVT(), Val, + DAG.getConstant(LocSizeInBits - ValSizeInBits, DL, VA.getLocVT())); + } + + Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Flag); + + // Guarantee that all emitted copies are stuck together with flags. + Flag = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); + } + + // The maxis ABIs for returning structs by value requires that we copy + // the sret argument into $v0 for the return. We saved the argument into + // a virtual register in the entry block, so now we copy the value out + // and into $v0. + if (MF.getFunction().hasStructRetAttr()) { + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + unsigned Reg = MaxisFI->getSRetReturnReg(); + + if (!Reg) + llvm_unreachable("sret virtual register not created in the entry block"); + SDValue Val = + DAG.getCopyFromReg(Chain, DL, Reg, getPointerTy(DAG.getDataLayout())); + unsigned V0 = ABI.IsN64() ? Maxis::V0_64 : Maxis::V0; + + Chain = DAG.getCopyToReg(Chain, DL, V0, Val, Flag); + Flag = Chain.getValue(1); + RetOps.push_back(DAG.getRegister(V0, getPointerTy(DAG.getDataLayout()))); + } + + RetOps[0] = Chain; // Update chain. + + // Add the flag if we have it. + if (Flag.getNode()) + RetOps.push_back(Flag); + + // ISRs must use "eret". + if (DAG.getMachineFunction().getFunction().hasFnAttribute("interrupt")) + return LowerInterruptReturn(RetOps, DL, DAG); + + // Standard return on Maxis is a "jr $ra" + return DAG.getNode(MaxisISD::Ret, DL, MVT::Other, RetOps); +} + +//===----------------------------------------------------------------------===// +// Maxis Inline Assembly Support +//===----------------------------------------------------------------------===// + +/// getConstraintType - Given a constraint letter, return the type of +/// constraint it is for this target. +MaxisTargetLowering::ConstraintType +MaxisTargetLowering::getConstraintType(StringRef Constraint) const { + // Maxis specific constraints + // GCC config/maxis/constraints.md + // + // 'd' : An address register. Equivalent to r + // unless generating MAXIS16 code. + // 'y' : Equivalent to r; retained for + // backwards compatibility. + // 'c' : A register suitable for use in an indirect + // jump. This will always be $25 for -mabicalls. + // 'l' : The lo register. 1 word storage. + // 'x' : The hilo register pair. Double word storage. + if (Constraint.size() == 1) { + switch (Constraint[0]) { + default : break; + case 'd': + case 'y': + case 'f': + case 'c': + case 'l': + case 'x': + return C_RegisterClass; + case 'R': + return C_Memory; + } + } + + if (Constraint == "ZC") + return C_Memory; + + return TargetLowering::getConstraintType(Constraint); +} + +/// Examine constraint type and operand type and determine a weight value. +/// This object must already have been set up with the operand type +/// and the current alternative constraint selected. +TargetLowering::ConstraintWeight +MaxisTargetLowering::getSingleConstraintMatchWeight( + AsmOperandInfo &info, const char *constraint) const { + ConstraintWeight weight = CW_Invalid; + Value *CallOperandVal = info.CallOperandVal; + // If we don't have a value, we can't do a match, + // but allow it at the lowest weight. + if (!CallOperandVal) + return CW_Default; + Type *type = CallOperandVal->getType(); + // Look at the constraint type. + switch (*constraint) { + default: + weight = TargetLowering::getSingleConstraintMatchWeight(info, constraint); + break; + case 'd': + case 'y': + if (type->isIntegerTy()) + weight = CW_Register; + break; + case 'f': // FPU or MSA register + if (Subtarget.hasMSA() && type->isVectorTy() && + cast(type)->getBitWidth() == 128) + weight = CW_Register; + else if (type->isFloatTy()) + weight = CW_Register; + break; + case 'c': // $25 for indirect jumps + case 'l': // lo register + case 'x': // hilo register pair + if (type->isIntegerTy()) + weight = CW_SpecificReg; + break; + case 'I': // signed 16 bit immediate + case 'J': // integer zero + case 'K': // unsigned 16 bit immediate + case 'L': // signed 32 bit immediate where lower 16 bits are 0 + case 'N': // immediate in the range of -65535 to -1 (inclusive) + case 'O': // signed 15 bit immediate (+- 16383) + case 'P': // immediate in the range of 65535 to 1 (inclusive) + if (isa(CallOperandVal)) + weight = CW_Constant; + break; + case 'R': + weight = CW_Memory; + break; + } + return weight; +} + +/// This is a helper function to parse a physical register string and split it +/// into non-numeric and numeric parts (Prefix and Reg). The first boolean flag +/// that is returned indicates whether parsing was successful. The second flag +/// is true if the numeric part exists. +static std::pair parsePhysicalReg(StringRef C, StringRef &Prefix, + unsigned long long &Reg) { + if (C.front() != '{' || C.back() != '}') + return std::make_pair(false, false); + + // Search for the first numeric character. + StringRef::const_iterator I, B = C.begin() + 1, E = C.end() - 1; + I = std::find_if(B, E, isdigit); + + Prefix = StringRef(B, I - B); + + // The second flag is set to false if no numeric characters were found. + if (I == E) + return std::make_pair(true, false); + + // Parse the numeric characters. + return std::make_pair(!getAsUnsignedInteger(StringRef(I, E - I), 10, Reg), + true); +} + +std::pair MaxisTargetLowering:: +parseRegForInlineAsmConstraint(StringRef C, MVT VT) const { + const TargetRegisterInfo *TRI = + Subtarget.getRegisterInfo(); + const TargetRegisterClass *RC; + StringRef Prefix; + unsigned long long Reg; + + std::pair R = parsePhysicalReg(C, Prefix, Reg); + + if (!R.first) + return std::make_pair(0U, nullptr); + + if ((Prefix == "hi" || Prefix == "lo")) { // Parse hi/lo. + // No numeric characters follow "hi" or "lo". + if (R.second) + return std::make_pair(0U, nullptr); + + RC = TRI->getRegClass(Prefix == "hi" ? + Maxis::HI32RegClassID : Maxis::LO32RegClassID); + return std::make_pair(*(RC->begin()), RC); + } else if (Prefix.startswith("$msa")) { + // Parse $msa(ir|csr|access|save|modify|request|map|unmap) + + // No numeric characters follow the name. + if (R.second) + return std::make_pair(0U, nullptr); + + Reg = StringSwitch(Prefix) + .Case("$msair", Maxis::MSAIR) + .Case("$msacsr", Maxis::MSACSR) + .Case("$msaaccess", Maxis::MSAAccess) + .Case("$msasave", Maxis::MSASave) + .Case("$msamodify", Maxis::MSAModify) + .Case("$msarequest", Maxis::MSARequest) + .Case("$msamap", Maxis::MSAMap) + .Case("$msaunmap", Maxis::MSAUnmap) + .Default(0); + + if (!Reg) + return std::make_pair(0U, nullptr); + + RC = TRI->getRegClass(Maxis::MSACtrlRegClassID); + return std::make_pair(Reg, RC); + } + + if (!R.second) + return std::make_pair(0U, nullptr); + + if (Prefix == "$f") { // Parse $f0-$f31. + // If the size of FP registers is 64-bit or Reg is an even number, select + // the 64-bit register class. Otherwise, select the 32-bit register class. + if (VT == MVT::Other) + VT = (Subtarget.isFP64bit() || !(Reg % 2)) ? MVT::f64 : MVT::f32; + + RC = getRegClassFor(VT); + + if (RC == &Maxis::AFGR64RegClass) { + assert(Reg % 2 == 0); + Reg >>= 1; + } + } else if (Prefix == "$fcc") // Parse $fcc0-$fcc7. + RC = TRI->getRegClass(Maxis::FCCRegClassID); + else if (Prefix == "$w") { // Parse $w0-$w31. + RC = getRegClassFor((VT == MVT::Other) ? MVT::v16i8 : VT); + } else { // Parse $0-$31. + assert(Prefix == "$"); + RC = getRegClassFor((VT == MVT::Other) ? MVT::i32 : VT); + } + + assert(Reg < RC->getNumRegs()); + return std::make_pair(*(RC->begin() + Reg), RC); +} + +/// Given a register class constraint, like 'r', if this corresponds directly +/// to an LLVM register class, return a register of 0 and the register class +/// pointer. +std::pair +MaxisTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, + MVT VT) const { + if (Constraint.size() == 1) { + switch (Constraint[0]) { + case 'd': // Address register. Same as 'r' unless generating MAXIS16 code. + case 'y': // Same as 'r'. Exists for compatibility. + case 'r': + if (VT == MVT::i32 || VT == MVT::i16 || VT == MVT::i8) { + if (Subtarget.inMaxis16Mode()) + return std::make_pair(0U, &Maxis::CPU16RegsRegClass); + return std::make_pair(0U, &Maxis::GPR32RegClass); + } + if (VT == MVT::i64 && !Subtarget.isGP64bit()) + return std::make_pair(0U, &Maxis::GPR32RegClass); + if (VT == MVT::i64 && Subtarget.isGP64bit()) + return std::make_pair(0U, &Maxis::GPR64RegClass); + // This will generate an error message + return std::make_pair(0U, nullptr); + case 'f': // FPU or MSA register + if (VT == MVT::v16i8) + return std::make_pair(0U, &Maxis::MSA128BRegClass); + else if (VT == MVT::v8i16 || VT == MVT::v8f16) + return std::make_pair(0U, &Maxis::MSA128HRegClass); + else if (VT == MVT::v4i32 || VT == MVT::v4f32) + return std::make_pair(0U, &Maxis::MSA128WRegClass); + else if (VT == MVT::v2i64 || VT == MVT::v2f64) + return std::make_pair(0U, &Maxis::MSA128DRegClass); + else if (VT == MVT::f32) + return std::make_pair(0U, &Maxis::FGR32RegClass); + else if ((VT == MVT::f64) && (!Subtarget.isSingleFloat())) { + if (Subtarget.isFP64bit()) + return std::make_pair(0U, &Maxis::FGR64RegClass); + return std::make_pair(0U, &Maxis::AFGR64RegClass); + } + break; + case 'c': // register suitable for indirect jump + if (VT == MVT::i32) + return std::make_pair((unsigned)Maxis::T9, &Maxis::GPR32RegClass); + if (VT == MVT::i64) + return std::make_pair((unsigned)Maxis::T9_64, &Maxis::GPR64RegClass); + // This will generate an error message + return std::make_pair(0U, nullptr); + case 'l': // use the `lo` register to store values + // that are no bigger than a word + if (VT == MVT::i32) + return std::make_pair((unsigned)Maxis::LO0, &Maxis::LO32RegClass); + return std::make_pair((unsigned)Maxis::LO0_64, &Maxis::LO64RegClass); + case 'x': // use the concatenated `hi` and `lo` registers + // to store doubleword values + // Fixme: Not triggering the use of both hi and low + // This will generate an error message + return std::make_pair(0U, nullptr); + } + } + + std::pair R; + R = parseRegForInlineAsmConstraint(Constraint, VT); + + if (R.second) + return R; + + return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); +} + +/// LowerAsmOperandForConstraint - Lower the specified operand into the Ops +/// vector. If it is invalid, don't add anything to Ops. +void MaxisTargetLowering::LowerAsmOperandForConstraint(SDValue Op, + std::string &Constraint, + std::vector&Ops, + SelectionDAG &DAG) const { + SDLoc DL(Op); + SDValue Result; + + // Only support length 1 constraints for now. + if (Constraint.length() > 1) return; + + char ConstraintLetter = Constraint[0]; + switch (ConstraintLetter) { + default: break; // This will fall through to the generic implementation + case 'I': // Signed 16 bit constant + // If this fails, the parent routine will give an error + if (ConstantSDNode *C = dyn_cast(Op)) { + EVT Type = Op.getValueType(); + int64_t Val = C->getSExtValue(); + if (isInt<16>(Val)) { + Result = DAG.getTargetConstant(Val, DL, Type); + break; + } + } + return; + case 'J': // integer zero + if (ConstantSDNode *C = dyn_cast(Op)) { + EVT Type = Op.getValueType(); + int64_t Val = C->getZExtValue(); + if (Val == 0) { + Result = DAG.getTargetConstant(0, DL, Type); + break; + } + } + return; + case 'K': // unsigned 16 bit immediate + if (ConstantSDNode *C = dyn_cast(Op)) { + EVT Type = Op.getValueType(); + uint64_t Val = (uint64_t)C->getZExtValue(); + if (isUInt<16>(Val)) { + Result = DAG.getTargetConstant(Val, DL, Type); + break; + } + } + return; + case 'L': // signed 32 bit immediate where lower 16 bits are 0 + if (ConstantSDNode *C = dyn_cast(Op)) { + EVT Type = Op.getValueType(); + int64_t Val = C->getSExtValue(); + if ((isInt<32>(Val)) && ((Val & 0xffff) == 0)){ + Result = DAG.getTargetConstant(Val, DL, Type); + break; + } + } + return; + case 'N': // immediate in the range of -65535 to -1 (inclusive) + if (ConstantSDNode *C = dyn_cast(Op)) { + EVT Type = Op.getValueType(); + int64_t Val = C->getSExtValue(); + if ((Val >= -65535) && (Val <= -1)) { + Result = DAG.getTargetConstant(Val, DL, Type); + break; + } + } + return; + case 'O': // signed 15 bit immediate + if (ConstantSDNode *C = dyn_cast(Op)) { + EVT Type = Op.getValueType(); + int64_t Val = C->getSExtValue(); + if ((isInt<15>(Val))) { + Result = DAG.getTargetConstant(Val, DL, Type); + break; + } + } + return; + case 'P': // immediate in the range of 1 to 65535 (inclusive) + if (ConstantSDNode *C = dyn_cast(Op)) { + EVT Type = Op.getValueType(); + int64_t Val = C->getSExtValue(); + if ((Val <= 65535) && (Val >= 1)) { + Result = DAG.getTargetConstant(Val, DL, Type); + break; + } + } + return; + } + + if (Result.getNode()) { + Ops.push_back(Result); + return; + } + + TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); +} + +bool MaxisTargetLowering::isLegalAddressingMode(const DataLayout &DL, + const AddrMode &AM, Type *Ty, + unsigned AS, Instruction *I) const { + // No global is ever allowed as a base. + if (AM.BaseGV) + return false; + + switch (AM.Scale) { + case 0: // "r+i" or just "i", depending on HasBaseReg. + break; + case 1: + if (!AM.HasBaseReg) // allow "r+i". + break; + return false; // disallow "r+r" or "r+r+i". + default: + return false; + } + + return true; +} + +bool +MaxisTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const { + // The Maxis target isn't yet aware of offsets. + return false; +} + +EVT MaxisTargetLowering::getOptimalMemOpType(uint64_t Size, unsigned DstAlign, + unsigned SrcAlign, + bool IsMemset, bool ZeroMemset, + bool MemcpyStrSrc, + MachineFunction &MF) const { + if (Subtarget.hasMaxis64()) + return MVT::i64; + + return MVT::i32; +} + +bool MaxisTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT) const { + if (VT != MVT::f32 && VT != MVT::f64) + return false; + if (Imm.isNegZero()) + return false; + return Imm.isZero(); +} + +unsigned MaxisTargetLowering::getJumpTableEncoding() const { + + // FIXME: For space reasons this should be: EK_GPRel32BlockAddress. + if (ABI.IsN64() && isPositionIndependent()) + return MachineJumpTableInfo::EK_GPRel64BlockAddress; + + return TargetLowering::getJumpTableEncoding(); +} + +bool MaxisTargetLowering::useSoftFloat() const { + return Subtarget.useSoftFloat(); +} + +void MaxisTargetLowering::copyByValRegs( + SDValue Chain, const SDLoc &DL, std::vector &OutChains, + SelectionDAG &DAG, const ISD::ArgFlagsTy &Flags, + SmallVectorImpl &InVals, const Argument *FuncArg, + unsigned FirstReg, unsigned LastReg, const CCValAssign &VA, + MaxisCCState &State) const { + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + unsigned GPRSizeInBytes = Subtarget.getGPRSizeInBytes(); + unsigned NumRegs = LastReg - FirstReg; + unsigned RegAreaSize = NumRegs * GPRSizeInBytes; + unsigned FrameObjSize = std::max(Flags.getByValSize(), RegAreaSize); + int FrameObjOffset; + ArrayRef ByValArgRegs = ABI.GetByValArgRegs(); + + if (RegAreaSize) + FrameObjOffset = + (int)ABI.GetCalleeAllocdArgSizeInBytes(State.getCallingConv()) - + (int)((ByValArgRegs.size() - FirstReg) * GPRSizeInBytes); + else + FrameObjOffset = VA.getLocMemOffset(); + + // Create frame object. + EVT PtrTy = getPointerTy(DAG.getDataLayout()); + int FI = MFI.CreateFixedObject(FrameObjSize, FrameObjOffset, true); + SDValue FIN = DAG.getFrameIndex(FI, PtrTy); + InVals.push_back(FIN); + + if (!NumRegs) + return; + + // Copy arg registers. + MVT RegTy = MVT::getIntegerVT(GPRSizeInBytes * 8); + const TargetRegisterClass *RC = getRegClassFor(RegTy); + + for (unsigned I = 0; I < NumRegs; ++I) { + unsigned ArgReg = ByValArgRegs[FirstReg + I]; + unsigned VReg = addLiveIn(MF, ArgReg, RC); + unsigned Offset = I * GPRSizeInBytes; + SDValue StorePtr = DAG.getNode(ISD::ADD, DL, PtrTy, FIN, + DAG.getConstant(Offset, DL, PtrTy)); + SDValue Store = DAG.getStore(Chain, DL, DAG.getRegister(VReg, RegTy), + StorePtr, MachinePointerInfo(FuncArg, Offset)); + OutChains.push_back(Store); + } +} + +// Copy byVal arg to registers and stack. +void MaxisTargetLowering::passByValArg( + SDValue Chain, const SDLoc &DL, + std::deque> &RegsToPass, + SmallVectorImpl &MemOpChains, SDValue StackPtr, + MachineFrameInfo &MFI, SelectionDAG &DAG, SDValue Arg, unsigned FirstReg, + unsigned LastReg, const ISD::ArgFlagsTy &Flags, bool isLittle, + const CCValAssign &VA) const { + unsigned ByValSizeInBytes = Flags.getByValSize(); + unsigned OffsetInBytes = 0; // From beginning of struct + unsigned RegSizeInBytes = Subtarget.getGPRSizeInBytes(); + unsigned Alignment = std::min(Flags.getByValAlign(), RegSizeInBytes); + EVT PtrTy = getPointerTy(DAG.getDataLayout()), + RegTy = MVT::getIntegerVT(RegSizeInBytes * 8); + unsigned NumRegs = LastReg - FirstReg; + + if (NumRegs) { + ArrayRef ArgRegs = ABI.GetByValArgRegs(); + bool LeftoverBytes = (NumRegs * RegSizeInBytes > ByValSizeInBytes); + unsigned I = 0; + + // Copy words to registers. + for (; I < NumRegs - LeftoverBytes; ++I, OffsetInBytes += RegSizeInBytes) { + SDValue LoadPtr = DAG.getNode(ISD::ADD, DL, PtrTy, Arg, + DAG.getConstant(OffsetInBytes, DL, PtrTy)); + SDValue LoadVal = DAG.getLoad(RegTy, DL, Chain, LoadPtr, + MachinePointerInfo(), Alignment); + MemOpChains.push_back(LoadVal.getValue(1)); + unsigned ArgReg = ArgRegs[FirstReg + I]; + RegsToPass.push_back(std::make_pair(ArgReg, LoadVal)); + } + + // Return if the struct has been fully copied. + if (ByValSizeInBytes == OffsetInBytes) + return; + + // Copy the remainder of the byval argument with sub-word loads and shifts. + if (LeftoverBytes) { + SDValue Val; + + for (unsigned LoadSizeInBytes = RegSizeInBytes / 2, TotalBytesLoaded = 0; + OffsetInBytes < ByValSizeInBytes; LoadSizeInBytes /= 2) { + unsigned RemainingSizeInBytes = ByValSizeInBytes - OffsetInBytes; + + if (RemainingSizeInBytes < LoadSizeInBytes) + continue; + + // Load subword. + SDValue LoadPtr = DAG.getNode(ISD::ADD, DL, PtrTy, Arg, + DAG.getConstant(OffsetInBytes, DL, + PtrTy)); + SDValue LoadVal = DAG.getExtLoad( + ISD::ZEXTLOAD, DL, RegTy, Chain, LoadPtr, MachinePointerInfo(), + MVT::getIntegerVT(LoadSizeInBytes * 8), Alignment); + MemOpChains.push_back(LoadVal.getValue(1)); + + // Shift the loaded value. + unsigned Shamt; + + if (isLittle) + Shamt = TotalBytesLoaded * 8; + else + Shamt = (RegSizeInBytes - (TotalBytesLoaded + LoadSizeInBytes)) * 8; + + SDValue Shift = DAG.getNode(ISD::SHL, DL, RegTy, LoadVal, + DAG.getConstant(Shamt, DL, MVT::i32)); + + if (Val.getNode()) + Val = DAG.getNode(ISD::OR, DL, RegTy, Val, Shift); + else + Val = Shift; + + OffsetInBytes += LoadSizeInBytes; + TotalBytesLoaded += LoadSizeInBytes; + Alignment = std::min(Alignment, LoadSizeInBytes); + } + + unsigned ArgReg = ArgRegs[FirstReg + I]; + RegsToPass.push_back(std::make_pair(ArgReg, Val)); + return; + } + } + + // Copy remainder of byval arg to it with memcpy. + unsigned MemCpySize = ByValSizeInBytes - OffsetInBytes; + SDValue Src = DAG.getNode(ISD::ADD, DL, PtrTy, Arg, + DAG.getConstant(OffsetInBytes, DL, PtrTy)); + SDValue Dst = DAG.getNode(ISD::ADD, DL, PtrTy, StackPtr, + DAG.getIntPtrConstant(VA.getLocMemOffset(), DL)); + Chain = DAG.getMemcpy(Chain, DL, Dst, Src, + DAG.getConstant(MemCpySize, DL, PtrTy), + Alignment, /*isVolatile=*/false, /*AlwaysInline=*/false, + /*isTailCall=*/false, + MachinePointerInfo(), MachinePointerInfo()); + MemOpChains.push_back(Chain); +} + +void MaxisTargetLowering::writeVarArgRegs(std::vector &OutChains, + SDValue Chain, const SDLoc &DL, + SelectionDAG &DAG, + CCState &State) const { + ArrayRef ArgRegs = ABI.GetVarArgRegs(); + unsigned Idx = State.getFirstUnallocated(ArgRegs); + unsigned RegSizeInBytes = Subtarget.getGPRSizeInBytes(); + MVT RegTy = MVT::getIntegerVT(RegSizeInBytes * 8); + const TargetRegisterClass *RC = getRegClassFor(RegTy); + MachineFunction &MF = DAG.getMachineFunction(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + // Offset of the first variable argument from stack pointer. + int VaArgOffset; + + if (ArgRegs.size() == Idx) + VaArgOffset = alignTo(State.getNextStackOffset(), RegSizeInBytes); + else { + VaArgOffset = + (int)ABI.GetCalleeAllocdArgSizeInBytes(State.getCallingConv()) - + (int)(RegSizeInBytes * (ArgRegs.size() - Idx)); + } + + // Record the frame index of the first variable argument + // which is a value necessary to VASTART. + int FI = MFI.CreateFixedObject(RegSizeInBytes, VaArgOffset, true); + MaxisFI->setVarArgsFrameIndex(FI); + + // Copy the integer registers that have not been used for argument passing + // to the argument register save area. For O32, the save area is allocated + // in the caller's stack frame, while for N32/64, it is allocated in the + // callee's stack frame. + for (unsigned I = Idx; I < ArgRegs.size(); + ++I, VaArgOffset += RegSizeInBytes) { + unsigned Reg = addLiveIn(MF, ArgRegs[I], RC); + SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegTy); + FI = MFI.CreateFixedObject(RegSizeInBytes, VaArgOffset, true); + SDValue PtrOff = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); + SDValue Store = + DAG.getStore(Chain, DL, ArgValue, PtrOff, MachinePointerInfo()); + cast(Store.getNode())->getMemOperand()->setValue( + (Value *)nullptr); + OutChains.push_back(Store); + } +} + +void MaxisTargetLowering::HandleByVal(CCState *State, unsigned &Size, + unsigned Align) const { + const TargetFrameLowering *TFL = Subtarget.getFrameLowering(); + + assert(Size && "Byval argument's size shouldn't be 0."); + + Align = std::min(Align, TFL->getStackAlignment()); + + unsigned FirstReg = 0; + unsigned NumRegs = 0; + + if (State->getCallingConv() != CallingConv::Fast) { + unsigned RegSizeInBytes = Subtarget.getGPRSizeInBytes(); + ArrayRef IntArgRegs = ABI.GetByValArgRegs(); + // FIXME: The O32 case actually describes no shadow registers. + const MCPhysReg *ShadowRegs = + ABI.IsO32() ? IntArgRegs.data() : Maxis64DPRegs; + + // We used to check the size as well but we can't do that anymore since + // CCState::HandleByVal() rounds up the size after calling this function. + assert(!(Align % RegSizeInBytes) && + "Byval argument's alignment should be a multiple of" + "RegSizeInBytes."); + + FirstReg = State->getFirstUnallocated(IntArgRegs); + + // If Align > RegSizeInBytes, the first arg register must be even. + // FIXME: This condition happens to do the right thing but it's not the + // right way to test it. We want to check that the stack frame offset + // of the register is aligned. + if ((Align > RegSizeInBytes) && (FirstReg % 2)) { + State->AllocateReg(IntArgRegs[FirstReg], ShadowRegs[FirstReg]); + ++FirstReg; + } + + // Mark the registers allocated. + Size = alignTo(Size, RegSizeInBytes); + for (unsigned I = FirstReg; Size > 0 && (I < IntArgRegs.size()); + Size -= RegSizeInBytes, ++I, ++NumRegs) + State->AllocateReg(IntArgRegs[I], ShadowRegs[I]); + } + + State->addInRegsParamInfo(FirstReg, FirstReg + NumRegs); +} + +MachineBasicBlock *MaxisTargetLowering::emitPseudoSELECT(MachineInstr &MI, + MachineBasicBlock *BB, + bool isFPCmp, + unsigned Opc) const { + assert(!(Subtarget.hasMaxis4() || Subtarget.hasMaxis32()) && + "Subtarget already supports SELECT nodes with the use of" + "conditional-move instructions."); + + const TargetInstrInfo *TII = + Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + // To "insert" a SELECT 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 = ... + // setcc r1, r2, r3 + // bNE r1, r0, copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, copy0MBB); + F->insert(It, sinkMBB); + + // Transfer the remainder of BB and its successor edges to sinkMBB. + sinkMBB->splice(sinkMBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + sinkMBB->transferSuccessorsAndUpdatePHIs(BB); + + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(copy0MBB); + BB->addSuccessor(sinkMBB); + + if (isFPCmp) { + // bc1[tf] cc, sinkMBB + BuildMI(BB, DL, TII->get(Opc)) + .addReg(MI.getOperand(1).getReg()) + .addMBB(sinkMBB); + } else { + // bne rs, $0, sinkMBB + BuildMI(BB, DL, TII->get(Opc)) + .addReg(MI.getOperand(1).getReg()) + .addReg(Maxis::ZERO) + .addMBB(sinkMBB); + } + + // copy0MBB: + // %FalseValue = ... + // # fallthrough to sinkMBB + BB = copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(sinkMBB); + + // sinkMBB: + // %Result = phi [ %TrueValue, thisMBB ], [ %FalseValue, copy0MBB ] + // ... + BB = sinkMBB; + + BuildMI(*BB, BB->begin(), DL, TII->get(Maxis::PHI), MI.getOperand(0).getReg()) + .addReg(MI.getOperand(2).getReg()) + .addMBB(thisMBB) + .addReg(MI.getOperand(3).getReg()) + .addMBB(copy0MBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + + return BB; +} + +// FIXME? Maybe this could be a TableGen attribute on some registers and +// this table could be generated automatically from RegInfo. +unsigned MaxisTargetLowering::getRegisterByName(const char* RegName, EVT VT, + SelectionDAG &DAG) const { + // Named registers is expected to be fairly rare. For now, just support $28 + // since the linux kernel uses it. + if (Subtarget.isGP64bit()) { + unsigned Reg = StringSwitch(RegName) + .Case("$28", Maxis::GP_64) + .Default(0); + if (Reg) + return Reg; + } else { + unsigned Reg = StringSwitch(RegName) + .Case("$28", Maxis::GP) + .Default(0); + if (Reg) + return Reg; + } + report_fatal_error("Invalid register name global variable"); +} diff --git a/lib/Target/Maxis/MaxisISelLowering.h b/lib/Target/Maxis/MaxisISelLowering.h new file mode 100644 index 00000000..e26ca0d5 --- /dev/null +++ b/lib/Target/Maxis/MaxisISelLowering.h @@ -0,0 +1,720 @@ +//===- MaxisISelLowering.h - Maxis DAG Lowering Interface ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that Maxis uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISISELLOWERING_H +#define LLVM_LIB_TARGET_MAXIS_MAXISISELLOWERING_H + +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "Maxis.h" +#include "llvm/CodeGen/ISDOpcodes.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/CodeGen/TargetLowering.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/InlineAsm.h" +#include "llvm/IR/Type.h" +#include "llvm/Target/TargetMachine.h" +#include +#include +#include +#include +#include +#include + +namespace llvm { + +class Argument; +class CCState; +class CCValAssign; +class FastISel; +class FunctionLoweringInfo; +class MachineBasicBlock; +class MachineFrameInfo; +class MachineInstr; +class MaxisCCState; +class MaxisFunctionInfo; +class MaxisSubtarget; +class MaxisTargetMachine; +class TargetLibraryInfo; +class TargetRegisterClass; + + namespace MaxisISD { + + enum NodeType : unsigned { + // Start the numbering from where ISD NodeType finishes. + FIRST_NUMBER = ISD::BUILTIN_OP_END, + + // Jump and link (call) + JmpLink, + + // Tail call + TailCall, + + // Get the Highest (63-48) 16 bits from a 64-bit immediate + Highest, + + // Get the Higher (47-32) 16 bits from a 64-bit immediate + Higher, + + // Get the High 16 bits from a 32/64-bit immediate + // No relation with Maxis Hi register + Hi, + + // Get the Lower 16 bits from a 32/64-bit immediate + // No relation with Maxis Lo register + Lo, + + // Get the High 16 bits from a 32 bit immediate for accessing the GOT. + GotHi, + + // Handle gp_rel (small data/bss sections) relocation. + GPRel, + + // Thread Pointer + ThreadPointer, + + // Floating Point Branch Conditional + FPBrcond, + + // Floating Point Compare + FPCmp, + + // Floating point select + FSELECT, + + // Node used to generate an MTC1 i32 to f64 instruction + MTC1_D64, + + // Floating Point Conditional Moves + CMovFP_T, + CMovFP_F, + + // FP-to-int truncation node. + TruncIntFP, + + // Return + Ret, + + // Interrupt, exception, error trap Return + ERet, + + // Software Exception Return. + EH_RETURN, + + // Node used to extract integer from accumulator. + MFHI, + MFLO, + + // Node used to insert integers to accumulator. + MTLOHI, + + // Mult nodes. + Mult, + Multu, + + // MAdd/Sub nodes + MAdd, + MAddu, + MSub, + MSubu, + + // DivRem(u) + DivRem, + DivRemU, + DivRem16, + DivRemU16, + + BuildPairF64, + ExtractElementF64, + + Wrapper, + + DynAlloc, + + Sync, + + Ext, + Ins, + CIns, + + // EXTR.W instrinsic nodes. + EXTP, + EXTPDP, + EXTR_S_H, + EXTR_W, + EXTR_R_W, + EXTR_RS_W, + SHILO, + MTHLIP, + + // DPA.W intrinsic nodes. + MULSAQ_S_W_PH, + MAQ_S_W_PHL, + MAQ_S_W_PHR, + MAQ_SA_W_PHL, + MAQ_SA_W_PHR, + DPAU_H_QBL, + DPAU_H_QBR, + DPSU_H_QBL, + DPSU_H_QBR, + DPAQ_S_W_PH, + DPSQ_S_W_PH, + DPAQ_SA_L_W, + DPSQ_SA_L_W, + DPA_W_PH, + DPS_W_PH, + DPAQX_S_W_PH, + DPAQX_SA_W_PH, + DPAX_W_PH, + DPSX_W_PH, + DPSQX_S_W_PH, + DPSQX_SA_W_PH, + MULSA_W_PH, + + MULT, + MULTU, + MADD_DSP, + MADDU_DSP, + MSUB_DSP, + MSUBU_DSP, + + // DSP shift nodes. + SHLL_DSP, + SHRA_DSP, + SHRL_DSP, + + // DSP setcc and select_cc nodes. + SETCC_DSP, + SELECT_CC_DSP, + + // Vector comparisons. + // These take a vector and return a boolean. + VALL_ZERO, + VANY_ZERO, + VALL_NONZERO, + VANY_NONZERO, + + // These take a vector and return a vector bitmask. + VCEQ, + VCLE_S, + VCLE_U, + VCLT_S, + VCLT_U, + + // Element-wise vector max/min. + VSMAX, + VSMIN, + VUMAX, + VUMIN, + + // Vector Shuffle with mask as an operand + VSHF, // Generic shuffle + SHF, // 4-element set shuffle. + ILVEV, // Interleave even elements + ILVOD, // Interleave odd elements + ILVL, // Interleave left elements + ILVR, // Interleave right elements + PCKEV, // Pack even elements + PCKOD, // Pack odd elements + + // Vector Lane Copy + INSVE, // Copy element from one vector to another + + // Combined (XOR (OR $a, $b), -1) + VNOR, + + // Extended vector element extraction + VEXTRACT_SEXT_ELT, + VEXTRACT_ZEXT_ELT, + + // Load/Store Left/Right nodes. + LWL = ISD::FIRST_TARGET_MEMORY_OPCODE, + LWR, + SWL, + SWR, + LDL, + LDR, + SDL, + SDR + }; + + } // ene namespace MaxisISD + + //===--------------------------------------------------------------------===// + // TargetLowering Implementation + //===--------------------------------------------------------------------===// + + class MaxisTargetLowering : public TargetLowering { + bool isMicroMaxis; + + public: + explicit MaxisTargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI); + + static const MaxisTargetLowering *create(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI); + + /// createFastISel - This method returns a target specific FastISel object, + /// or null if the target does not support "fast" ISel. + FastISel *createFastISel(FunctionLoweringInfo &funcInfo, + const TargetLibraryInfo *libInfo) const override; + + MVT getScalarShiftAmountTy(const DataLayout &, EVT) const override { + return MVT::i32; + } + + bool isCheapToSpeculateCttz() const override; + bool isCheapToSpeculateCtlz() const override; + + /// Return the register type for a given MVT, ensuring vectors are treated + /// as a series of gpr sized integers. + MVT getRegisterTypeForCallingConv(MVT VT) const override; + + /// Return the register type for a given MVT, ensuring vectors are treated + /// as a series of gpr sized integers. + MVT getRegisterTypeForCallingConv(LLVMContext &Context, + EVT VT) const override; + + /// Return the number of registers for a given MVT, ensuring vectors are + /// treated as a series of gpr sized integers. + unsigned getNumRegistersForCallingConv(LLVMContext &Context, + EVT VT) const override; + + /// Break down vectors to the correct number of gpr sized integers. + unsigned getVectorTypeBreakdownForCallingConv( + LLVMContext &Context, EVT VT, EVT &IntermediateVT, + unsigned &NumIntermediates, MVT &RegisterVT) const override; + + /// Return the correct alignment for the current calling convention. + unsigned getABIAlignmentForCallingConv(Type *ArgTy, + DataLayout DL) const override { + if (ArgTy->isVectorTy()) + return std::min(DL.getABITypeAlignment(ArgTy), 8U); + return DL.getABITypeAlignment(ArgTy); + } + + ISD::NodeType getExtendForAtomicOps() const override { + return ISD::SIGN_EXTEND; + } + + void LowerOperationWrapper(SDNode *N, + SmallVectorImpl &Results, + SelectionDAG &DAG) const override; + + /// LowerOperation - Provide custom lowering hooks for some operations. + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + + /// ReplaceNodeResults - 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; + + /// getTargetNodeName - This method returns the name of a target specific + // DAG node. + const char *getTargetNodeName(unsigned Opcode) const override; + + /// getSetCCResultType - get the ISD::SETCC result ValueType + EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, + EVT VT) const override; + + SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *MBB) const override; + + void HandleByVal(CCState *, unsigned &, unsigned) const override; + + unsigned getRegisterByName(const char* RegName, EVT VT, + SelectionDAG &DAG) const override; + + /// If a physical register, this returns the register that receives the + /// exception address on entry to an EH pad. + unsigned + getExceptionPointerRegister(const Constant *PersonalityFn) const override { + return ABI.IsN64() ? Maxis::A0_64 : Maxis::A0; + } + + /// If a physical register, this returns the register that receives the + /// exception typeid on entry to a landing pad. + unsigned + getExceptionSelectorRegister(const Constant *PersonalityFn) const override { + return ABI.IsN64() ? Maxis::A1_64 : Maxis::A1; + } + + /// Returns true if a cast between SrcAS and DestAS is a noop. + bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const override { + // Maxis doesn't have any special address spaces so we just reserve + // the first 256 for software use (e.g. OpenCL) and treat casts + // between them as noops. + return SrcAS < 256 && DestAS < 256; + } + + bool isJumpTableRelative() const override { + return getTargetMachine().isPositionIndependent(); + } + + protected: + SDValue getGlobalReg(SelectionDAG &DAG, EVT Ty) const; + + // This method creates the following nodes, which are necessary for + // computing a local symbol's address: + // + // (add (load (wrapper $gp, %got(sym)), %lo(sym)) + template + SDValue getAddrLocal(NodeTy *N, const SDLoc &DL, EVT Ty, SelectionDAG &DAG, + bool IsN32OrN64) const { + unsigned GOTFlag = IsN32OrN64 ? MaxisII::MO_GOT_PAGE : MaxisII::MO_GOT; + SDValue GOT = DAG.getNode(MaxisISD::Wrapper, DL, Ty, getGlobalReg(DAG, Ty), + getTargetNode(N, Ty, DAG, GOTFlag)); + SDValue Load = + DAG.getLoad(Ty, DL, DAG.getEntryNode(), GOT, + MachinePointerInfo::getGOT(DAG.getMachineFunction())); + unsigned LoFlag = IsN32OrN64 ? MaxisII::MO_GOT_OFST : MaxisII::MO_ABS_LO; + SDValue Lo = DAG.getNode(MaxisISD::Lo, DL, Ty, + getTargetNode(N, Ty, DAG, LoFlag)); + return DAG.getNode(ISD::ADD, DL, Ty, Load, Lo); + } + + // This method creates the following nodes, which are necessary for + // computing a global symbol's address: + // + // (load (wrapper $gp, %got(sym))) + template + SDValue getAddrGlobal(NodeTy *N, const SDLoc &DL, EVT Ty, SelectionDAG &DAG, + unsigned Flag, SDValue Chain, + const MachinePointerInfo &PtrInfo) const { + SDValue Tgt = DAG.getNode(MaxisISD::Wrapper, DL, Ty, getGlobalReg(DAG, Ty), + getTargetNode(N, Ty, DAG, Flag)); + return DAG.getLoad(Ty, DL, Chain, Tgt, PtrInfo); + } + + // This method creates the following nodes, which are necessary for + // computing a global symbol's address in large-GOT mode: + // + // (load (wrapper (add %hi(sym), $gp), %lo(sym))) + template + SDValue getAddrGlobalLargeGOT(NodeTy *N, const SDLoc &DL, EVT Ty, + SelectionDAG &DAG, unsigned HiFlag, + unsigned LoFlag, SDValue Chain, + const MachinePointerInfo &PtrInfo) const { + SDValue Hi = DAG.getNode(MaxisISD::GotHi, DL, Ty, + getTargetNode(N, Ty, DAG, HiFlag)); + Hi = DAG.getNode(ISD::ADD, DL, Ty, Hi, getGlobalReg(DAG, Ty)); + SDValue Wrapper = DAG.getNode(MaxisISD::Wrapper, DL, Ty, Hi, + getTargetNode(N, Ty, DAG, LoFlag)); + return DAG.getLoad(Ty, DL, Chain, Wrapper, PtrInfo); + } + + // This method creates the following nodes, which are necessary for + // computing a symbol's address in non-PIC mode: + // + // (add %hi(sym), %lo(sym)) + // + // This method covers O32, N32 and N64 in sym32 mode. + template + SDValue getAddrNonPIC(NodeTy *N, const SDLoc &DL, EVT Ty, + SelectionDAG &DAG) const { + SDValue Hi = getTargetNode(N, Ty, DAG, MaxisII::MO_ABS_HI); + SDValue Lo = getTargetNode(N, Ty, DAG, MaxisII::MO_ABS_LO); + return DAG.getNode(ISD::ADD, DL, Ty, + DAG.getNode(MaxisISD::Hi, DL, Ty, Hi), + DAG.getNode(MaxisISD::Lo, DL, Ty, Lo)); + } + + // This method creates the following nodes, which are necessary for + // computing a symbol's address in non-PIC mode for N64. + // + // (add (shl (add (shl (add %highest(sym), %higher(sim)), 16), %high(sym)), + // 16), %lo(%sym)) + // + // FIXME: This method is not efficent for (micro)MAXIS64R6. + template + SDValue getAddrNonPICSym64(NodeTy *N, const SDLoc &DL, EVT Ty, + SelectionDAG &DAG) const { + SDValue Hi = getTargetNode(N, Ty, DAG, MaxisII::MO_ABS_HI); + SDValue Lo = getTargetNode(N, Ty, DAG, MaxisII::MO_ABS_LO); + + SDValue Highest = + DAG.getNode(MaxisISD::Highest, DL, Ty, + getTargetNode(N, Ty, DAG, MaxisII::MO_HIGHEST)); + SDValue Higher = getTargetNode(N, Ty, DAG, MaxisII::MO_HIGHER); + SDValue HigherPart = + DAG.getNode(ISD::ADD, DL, Ty, Highest, + DAG.getNode(MaxisISD::Higher, DL, Ty, Higher)); + SDValue Cst = DAG.getConstant(16, DL, MVT::i32); + SDValue Shift = DAG.getNode(ISD::SHL, DL, Ty, HigherPart, Cst); + SDValue Add = DAG.getNode(ISD::ADD, DL, Ty, Shift, + DAG.getNode(MaxisISD::Hi, DL, Ty, Hi)); + SDValue Shift2 = DAG.getNode(ISD::SHL, DL, Ty, Add, Cst); + + return DAG.getNode(ISD::ADD, DL, Ty, Shift2, + DAG.getNode(MaxisISD::Lo, DL, Ty, Lo)); + } + + // This method creates the following nodes, which are necessary for + // computing a symbol's address using gp-relative addressing: + // + // (add $gp, %gp_rel(sym)) + template + SDValue getAddrGPRel(NodeTy *N, const SDLoc &DL, EVT Ty, + SelectionDAG &DAG, bool IsN64) const { + SDValue GPRel = getTargetNode(N, Ty, DAG, MaxisII::MO_GPREL); + return DAG.getNode( + ISD::ADD, DL, Ty, + DAG.getRegister(IsN64 ? Maxis::GP_64 : Maxis::GP, Ty), + DAG.getNode(MaxisISD::GPRel, DL, DAG.getVTList(Ty), GPRel)); + } + + /// This function fills Ops, which is the list of operands that will later + /// be used when a function call node is created. It also generates + /// copyToReg nodes to set up argument registers. + virtual void + getOpndList(SmallVectorImpl &Ops, + std::deque> &RegsToPass, + bool IsPICCall, bool GlobalOrExternal, bool InternalLinkage, + bool IsCallReloc, CallLoweringInfo &CLI, SDValue Callee, + SDValue Chain) const; + + protected: + SDValue lowerLOAD(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerSTORE(SDValue Op, SelectionDAG &DAG) const; + + // Subtarget Info + const MaxisSubtarget &Subtarget; + // Cache the ABI from the TargetMachine, we use it everywhere. + const MaxisABIInfo &ABI; + + private: + // Create a TargetGlobalAddress node. + SDValue getTargetNode(GlobalAddressSDNode *N, EVT Ty, SelectionDAG &DAG, + unsigned Flag) const; + + // Create a TargetExternalSymbol node. + SDValue getTargetNode(ExternalSymbolSDNode *N, EVT Ty, SelectionDAG &DAG, + unsigned Flag) const; + + // Create a TargetBlockAddress node. + SDValue getTargetNode(BlockAddressSDNode *N, EVT Ty, SelectionDAG &DAG, + unsigned Flag) const; + + // Create a TargetJumpTable node. + SDValue getTargetNode(JumpTableSDNode *N, EVT Ty, SelectionDAG &DAG, + unsigned Flag) const; + + // Create a TargetConstantPool node. + SDValue getTargetNode(ConstantPoolSDNode *N, EVT Ty, SelectionDAG &DAG, + unsigned Flag) const; + + // Lower Operand helpers + SDValue LowerCallResult(SDValue Chain, SDValue InFlag, + CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &dl, SelectionDAG &DAG, + SmallVectorImpl &InVals, + TargetLowering::CallLoweringInfo &CLI) const; + + // Lower Operand specifics + SDValue lowerBRCOND(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerConstantPool(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerJumpTable(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerSETCC(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerVAARG(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerFABS(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerATOMIC_FENCE(SDValue Op, SelectionDAG& DAG) const; + SDValue lowerShiftLeftParts(SDValue Op, SelectionDAG& DAG) const; + SDValue lowerShiftRightParts(SDValue Op, SelectionDAG& DAG, + bool IsSRA) const; + SDValue lowerEH_DWARF_CFA(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerFP_TO_SINT(SDValue Op, SelectionDAG &DAG) const; + + /// isEligibleForTailCallOptimization - Check whether the call is eligible + /// for tail call optimization. + virtual bool + isEligibleForTailCallOptimization(const CCState &CCInfo, + unsigned NextStackOffset, + const MaxisFunctionInfo &FI) const = 0; + + /// copyByValArg - Copy argument registers which were used to pass a byval + /// argument to the stack. Create a stack frame object for the byval + /// argument. + void copyByValRegs(SDValue Chain, const SDLoc &DL, + std::vector &OutChains, SelectionDAG &DAG, + const ISD::ArgFlagsTy &Flags, + SmallVectorImpl &InVals, + const Argument *FuncArg, unsigned FirstReg, + unsigned LastReg, const CCValAssign &VA, + MaxisCCState &State) const; + + /// passByValArg - Pass a byval argument in registers or on stack. + void passByValArg(SDValue Chain, const SDLoc &DL, + std::deque> &RegsToPass, + SmallVectorImpl &MemOpChains, SDValue StackPtr, + MachineFrameInfo &MFI, SelectionDAG &DAG, SDValue Arg, + unsigned FirstReg, unsigned LastReg, + const ISD::ArgFlagsTy &Flags, bool isLittle, + const CCValAssign &VA) const; + + /// writeVarArgRegs - Write variable function arguments passed in registers + /// to the stack. Also create a stack frame object for the first variable + /// argument. + void writeVarArgRegs(std::vector &OutChains, SDValue Chain, + const SDLoc &DL, SelectionDAG &DAG, + CCState &State) const; + + SDValue + LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Ins, + const SDLoc &dl, SelectionDAG &DAG, + SmallVectorImpl &InVals) const override; + + SDValue passArgOnStack(SDValue StackPtr, unsigned Offset, SDValue Chain, + SDValue Arg, const SDLoc &DL, bool IsTailCall, + SelectionDAG &DAG) const; + + SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; + + bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, + bool isVarArg, + const SmallVectorImpl &Outs, + LLVMContext &Context) const override; + + SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, + const SmallVectorImpl &Outs, + const SmallVectorImpl &OutVals, + const SDLoc &dl, SelectionDAG &DAG) const override; + + SDValue LowerInterruptReturn(SmallVectorImpl &RetOps, + const SDLoc &DL, SelectionDAG &DAG) const; + + bool shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const override; + + // Inline asm support + ConstraintType getConstraintType(StringRef Constraint) const override; + + /// Examine constraint string and operand type and determine a weight value. + /// The operand object must already have been set up with the operand type. + ConstraintWeight getSingleConstraintMatchWeight( + AsmOperandInfo &info, const char *constraint) const override; + + /// This function parses registers that appear in inline-asm constraints. + /// It returns pair (0, 0) on failure. + std::pair + parseRegForInlineAsmConstraint(StringRef C, MVT VT) const; + + std::pair + getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, + StringRef Constraint, MVT VT) const override; + + /// LowerAsmOperandForConstraint - Lower the specified operand into the Ops + /// vector. If it is invalid, don't add anything to Ops. If hasMemory is + /// true it means one of the asm constraint of the inline asm instruction + /// being processed is 'm'. + void LowerAsmOperandForConstraint(SDValue Op, + std::string &Constraint, + std::vector &Ops, + SelectionDAG &DAG) const override; + + unsigned + getInlineAsmMemConstraint(StringRef ConstraintCode) const override { + if (ConstraintCode == "R") + return InlineAsm::Constraint_R; + else if (ConstraintCode == "ZC") + return InlineAsm::Constraint_ZC; + return TargetLowering::getInlineAsmMemConstraint(ConstraintCode); + } + + bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, + Type *Ty, unsigned AS, + Instruction *I = nullptr) const override; + + bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; + + EVT getOptimalMemOpType(uint64_t Size, unsigned DstAlign, + unsigned SrcAlign, + bool IsMemset, bool ZeroMemset, + bool MemcpyStrSrc, + MachineFunction &MF) const override; + + /// isFPImmLegal - Returns true if the target can instruction select the + /// specified FP immediate natively. If false, the legalizer will + /// materialize the FP immediate as a load from a constant pool. + bool isFPImmLegal(const APFloat &Imm, EVT VT) const override; + + unsigned getJumpTableEncoding() const override; + bool useSoftFloat() const override; + + bool shouldInsertFencesForAtomic(const Instruction *I) const override { + return true; + } + + /// Emit a sign-extension using sll/sra, seb, or seh appropriately. + MachineBasicBlock *emitSignExtendToI32InReg(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned Size, unsigned DstReg, + unsigned SrcRec) const; + + MachineBasicBlock *emitAtomicBinary(MachineInstr &MI, MachineBasicBlock *BB, + unsigned Size, unsigned BinOpcode, + bool Nand = false) const; + MachineBasicBlock *emitAtomicBinaryPartword(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned Size, + unsigned BinOpcode, + bool Nand = false) const; + MachineBasicBlock *emitAtomicCmpSwap(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned Size) const; + MachineBasicBlock *emitAtomicCmpSwapPartword(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned Size) const; + MachineBasicBlock *emitSEL_D(MachineInstr &MI, MachineBasicBlock *BB) const; + MachineBasicBlock *emitPseudoSELECT(MachineInstr &MI, MachineBasicBlock *BB, + bool isFPCmp, unsigned Opc) const; + }; + + /// Create MaxisTargetLowering objects. + const MaxisTargetLowering * + createMaxis16TargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI); + const MaxisTargetLowering * + createMaxisSETargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI); + +namespace Maxis { + +FastISel *createFastISel(FunctionLoweringInfo &funcInfo, + const TargetLibraryInfo *libInfo); + +} // end namespace Maxis + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISISELLOWERING_H diff --git a/lib/Target/Maxis/MaxisInstrFPU.td b/lib/Target/Maxis/MaxisInstrFPU.td new file mode 100644 index 00000000..ff226d32 --- /dev/null +++ b/lib/Target/Maxis/MaxisInstrFPU.td @@ -0,0 +1,915 @@ +//===-- MaxisInstrFPU.td - Maxis FPU Instruction Information -*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the Maxis FPU instruction set. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Floating Point Instructions +// ------------------------ +// * 64bit fp: +// - 32 64-bit registers (default mode) +// - 16 even 32-bit registers (32-bit compatible mode) for +// single and double access. +// * 32bit fp: +// - 16 even 32-bit registers - single and double (aliased) +// - 32 32-bit registers (within single-only mode) +//===----------------------------------------------------------------------===// + +// Floating Point Compare and Branch +def SDT_MaxisFPBrcond : SDTypeProfile<0, 3, [SDTCisInt<0>, + SDTCisVT<1, i32>, + SDTCisVT<2, OtherVT>]>; +def SDT_MaxisFPCmp : SDTypeProfile<0, 3, [SDTCisSameAs<0, 1>, SDTCisFP<1>, + SDTCisVT<2, i32>]>; +def SDT_MaxisCMovFP : SDTypeProfile<1, 3, [SDTCisSameAs<0, 1>, SDTCisVT<2, i32>, + SDTCisSameAs<1, 3>]>; +def SDT_MaxisTruncIntFP : SDTypeProfile<1, 1, [SDTCisFP<0>, SDTCisFP<1>]>; +def SDT_MaxisBuildPairF64 : SDTypeProfile<1, 2, [SDTCisVT<0, f64>, + SDTCisVT<1, i32>, + SDTCisSameAs<1, 2>]>; +def SDT_MaxisExtractElementF64 : SDTypeProfile<1, 2, [SDTCisVT<0, i32>, + SDTCisVT<1, f64>, + SDTCisVT<2, i32>]>; + +def SDT_MaxisMTC1_D64 : SDTypeProfile<1, 1, [SDTCisVT<0, f64>, + SDTCisVT<1, i32>]>; + +def MaxisFPCmp : SDNode<"MaxisISD::FPCmp", SDT_MaxisFPCmp, [SDNPOutGlue]>; +def MaxisCMovFP_T : SDNode<"MaxisISD::CMovFP_T", SDT_MaxisCMovFP, [SDNPInGlue]>; +def MaxisCMovFP_F : SDNode<"MaxisISD::CMovFP_F", SDT_MaxisCMovFP, [SDNPInGlue]>; +def MaxisFPBrcond : SDNode<"MaxisISD::FPBrcond", SDT_MaxisFPBrcond, + [SDNPHasChain, SDNPOptInGlue]>; +def MaxisTruncIntFP : SDNode<"MaxisISD::TruncIntFP", SDT_MaxisTruncIntFP>; +def MaxisBuildPairF64 : SDNode<"MaxisISD::BuildPairF64", SDT_MaxisBuildPairF64>; +def MaxisExtractElementF64 : SDNode<"MaxisISD::ExtractElementF64", + SDT_MaxisExtractElementF64>; + +def MaxisMTC1_D64 : SDNode<"MaxisISD::MTC1_D64", SDT_MaxisMTC1_D64>; + +// Operand for printing out a condition code. +let PrintMethod = "printFCCOperand", DecoderMethod = "DecodeCondCode" in + def condcode : Operand; + +//===----------------------------------------------------------------------===// +// Feature predicates. +//===----------------------------------------------------------------------===// + +def IsFP64bit : Predicate<"Subtarget->isFP64bit()">, + AssemblerPredicate<"FeatureFP64Bit">; +def NotFP64bit : Predicate<"!Subtarget->isFP64bit()">, + AssemblerPredicate<"!FeatureFP64Bit">; +def IsSingleFloat : Predicate<"Subtarget->isSingleFloat()">, + AssemblerPredicate<"FeatureSingleFloat">; +def IsNotSingleFloat : Predicate<"!Subtarget->isSingleFloat()">, + AssemblerPredicate<"!FeatureSingleFloat">; +def IsNotSoftFloat : Predicate<"!Subtarget->useSoftFloat()">, + AssemblerPredicate<"!FeatureSoftFloat">; + +//===----------------------------------------------------------------------===// +// Maxis FGR size adjectives. +// They are mutually exclusive. +//===----------------------------------------------------------------------===// + +class FGR_32 { list FGRPredicates = [NotFP64bit]; } +class FGR_64 { list FGRPredicates = [IsFP64bit]; } +class HARDFLOAT { list HardFloatPredicate = [IsNotSoftFloat]; } + +//===----------------------------------------------------------------------===// + +// FP immediate patterns. +def fpimm0 : PatLeaf<(fpimm), [{ + return N->isExactlyValue(+0.0); +}]>; + +def fpimm0neg : PatLeaf<(fpimm), [{ + return N->isExactlyValue(-0.0); +}]>; + +//===----------------------------------------------------------------------===// +// Instruction Class Templates +// +// A set of multiclasses is used to address the register usage. +// +// S32 - single precision in 16 32bit even fp registers +// single precision in 32 32bit fp registers in SingleOnly mode +// S64 - single precision in 32 64bit fp registers (In64BitMode) +// D32 - double precision in 16 32bit even fp registers +// D64 - double precision in 32 64bit fp registers (In64BitMode) +// +// Only S32 and D32 are supported right now. +//===----------------------------------------------------------------------===// +class ADDS_FT : + InstSE<(outs RC:$fd), (ins RC:$fs, RC:$ft), + !strconcat(opstr, "\t$fd, $fs, $ft"), + [(set RC:$fd, (OpNode RC:$fs, RC:$ft))], Itin, FrmFR, opstr>, + HARDFLOAT { + let isCommutable = IsComm; +} + +multiclass ADDS_M { + def _D32 : MMRel, ADDS_FT, FGR_32; + def _D64 : ADDS_FT, FGR_64 { + string DecoderNamespace = "MaxisFP64"; + } +} + +class ABSS_FT : + InstSE<(outs DstRC:$fd), (ins SrcRC:$fs), !strconcat(opstr, "\t$fd, $fs"), + [(set DstRC:$fd, (OpNode SrcRC:$fs))], Itin, FrmFR, opstr>, + HARDFLOAT, + NeverHasSideEffects; + +multiclass ABSS_M { + def _D32 : MMRel, ABSS_FT, + FGR_32; + def _D64 : ABSS_FT, FGR_64 { + string DecoderNamespace = "MaxisFP64"; + } +} + +multiclass ROUND_M { + def _D32 : MMRel, ABSS_FT, FGR_32; + def _D64 : StdMMR6Rel, ABSS_FT, FGR_64 { + let DecoderNamespace = "MaxisFP64"; + } +} + +class MFC1_FT : + InstSE<(outs DstRC:$rt), (ins SrcRC:$fs), !strconcat(opstr, "\t$rt, $fs"), + [(set DstRC:$rt, (OpNode SrcRC:$fs))], Itin, FrmFR, opstr>, HARDFLOAT; + +class MTC1_FT : + InstSE<(outs DstRC:$fs), (ins SrcRC:$rt), !strconcat(opstr, "\t$rt, $fs"), + [(set DstRC:$fs, (OpNode SrcRC:$rt))], Itin, FrmFR, opstr>, HARDFLOAT; + +class MTC1_64_FT : + InstSE<(outs DstRC:$fs), (ins DstRC:$fs_in, SrcRC:$rt), + !strconcat(opstr, "\t$rt, $fs"), [], Itin, FrmFR, opstr>, HARDFLOAT { + // $fs_in is part of a white lie to work around a widespread bug in the FPU + // implementation. See expandBuildPairF64 for details. + let Constraints = "$fs = $fs_in"; +} + +class LW_FT : + InstSE<(outs RC:$rt), (ins MO:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(set RC:$rt, (OpNode addrDefault:$addr))], Itin, FrmFI, opstr>, + HARDFLOAT { + let DecoderMethod = "DecodeFMem"; + let mayLoad = 1; +} + +class SW_FT : + InstSE<(outs), (ins RC:$rt, MO:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(OpNode RC:$rt, addrDefault:$addr)], Itin, FrmFI, opstr>, HARDFLOAT { + let DecoderMethod = "DecodeFMem"; + let mayStore = 1; +} + +class MADDS_FT : + InstSE<(outs RC:$fd), (ins RC:$fr, RC:$fs, RC:$ft), + !strconcat(opstr, "\t$fd, $fr, $fs, $ft"), + [(set RC:$fd, (OpNode (fmul RC:$fs, RC:$ft), RC:$fr))], Itin, + FrmFR, opstr>, HARDFLOAT; + +class NMADDS_FT : + InstSE<(outs RC:$fd), (ins RC:$fr, RC:$fs, RC:$ft), + !strconcat(opstr, "\t$fd, $fr, $fs, $ft"), + [(set RC:$fd, (fsub fpimm0, (OpNode (fmul RC:$fs, RC:$ft), RC:$fr)))], + Itin, FrmFR, opstr>, HARDFLOAT; + +class LWXC1_FT : + InstSE<(outs DRC:$fd), (ins PtrRC:$base, PtrRC:$index), + !strconcat(opstr, "\t$fd, ${index}(${base})"), + [(set DRC:$fd, (OpNode (add iPTR:$base, iPTR:$index)))], Itin, + FrmFI, opstr>, HARDFLOAT { + let AddedComplexity = 20; +} + +class SWXC1_FT : + InstSE<(outs), (ins DRC:$fs, PtrRC:$base, PtrRC:$index), + !strconcat(opstr, "\t$fs, ${index}(${base})"), + [(OpNode DRC:$fs, (add iPTR:$base, iPTR:$index))], Itin, + FrmFI, opstr>, HARDFLOAT { + let AddedComplexity = 20; +} + +class BC1F_FT : + InstSE<(outs), (ins FCCRegsOpnd:$fcc, opnd:$offset), + !strconcat(opstr, "\t$fcc, $offset"), + [(MaxisFPBrcond Op, FCCRegsOpnd:$fcc, bb:$offset)], Itin, + FrmFI, opstr>, HARDFLOAT { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 1; + let Defs = [AT]; + let hasFCCRegOperand = 1; +} + +class BC1XL_FT : + InstSE<(outs), (ins FCCRegsOpnd:$fcc, opnd:$offset), + !strconcat(opstr, "\t$fcc, $offset"), [], Itin, + FrmFI, opstr>, HARDFLOAT { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 1; + let Defs = [AT]; + let hasFCCRegOperand = 1; +} + +class CEQS_FT : + InstSE<(outs), (ins RC:$fs, RC:$ft, condcode:$cond), + !strconcat("c.$cond.", typestr, "\t$fs, $ft"), + [(OpNode RC:$fs, RC:$ft, imm:$cond)], Itin, FrmFR, + !strconcat("c.$cond.", typestr)>, HARDFLOAT { + let Defs = [FCC0]; + let isCodeGenOnly = 1; + let hasFCCRegOperand = 1; +} + + +// Note: MAXIS-IV introduced $fcc1-$fcc7 and renamed FCSR31[23] $fcc0. Rather +// duplicating the instruction definition for MAXIS1 - MAXIS3, we expand +// c.cond.ft if necessary, and reject it after constructing the +// instruction if the ISA doesn't support it. +class C_COND_FT : + InstSE<(outs FCCRegsOpnd:$fcc), (ins RC:$fs, RC:$ft), + !strconcat("c.", CondStr, ".", Typestr, "\t$fcc, $fs, $ft"), [], itin, + FrmFR>, HARDFLOAT { + let isCompare = 1; + let hasFCCRegOperand = 1; +} + + +multiclass C_COND_M fmt, + InstrItinClass itin> { + def C_F_#NAME : MMRel, C_COND_FT<"f", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.f."#NAME; + let isCommutable = 1; + } + def C_UN_#NAME : MMRel, C_COND_FT<"un", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.un."#NAME; + let isCommutable = 1; + } + def C_EQ_#NAME : MMRel, C_COND_FT<"eq", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.eq."#NAME; + let isCommutable = 1; + } + def C_UEQ_#NAME : MMRel, C_COND_FT<"ueq", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.ueq."#NAME; + let isCommutable = 1; + } + def C_OLT_#NAME : MMRel, C_COND_FT<"olt", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.olt."#NAME; + } + def C_ULT_#NAME : MMRel, C_COND_FT<"ult", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.ult."#NAME; + } + def C_OLE_#NAME : MMRel, C_COND_FT<"ole", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.ole."#NAME; + } + def C_ULE_#NAME : MMRel, C_COND_FT<"ule", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.ule."#NAME; + } + def C_SF_#NAME : MMRel, C_COND_FT<"sf", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.sf."#NAME; + let isCommutable = 1; + } + def C_NGLE_#NAME : MMRel, C_COND_FT<"ngle", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.ngle."#NAME; + } + def C_SEQ_#NAME : MMRel, C_COND_FT<"seq", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.seq."#NAME; + let isCommutable = 1; + } + def C_NGL_#NAME : MMRel, C_COND_FT<"ngl", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.ngl."#NAME; + } + def C_LT_#NAME : MMRel, C_COND_FT<"lt", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.lt."#NAME; + } + def C_NGE_#NAME : MMRel, C_COND_FT<"nge", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.nge."#NAME; + } + def C_LE_#NAME : MMRel, C_COND_FT<"le", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.le."#NAME; + } + def C_NGT_#NAME : MMRel, C_COND_FT<"ngt", TypeStr, RC, itin>, + C_COND_FM { + let BaseOpcode = "c.ngt."#NAME; + } +} + +let AdditionalPredicates = [NotInMicroMaxis] in { +defm S : C_COND_M<"s", FGR32Opnd, 16, II_C_CC_S>, ISA_MAXIS1_NOT_32R6_64R6; +defm D32 : C_COND_M<"d", AFGR64Opnd, 17, II_C_CC_D>, ISA_MAXIS1_NOT_32R6_64R6, + FGR_32; +let DecoderNamespace = "MaxisFP64" in +defm D64 : C_COND_M<"d", FGR64Opnd, 17, II_C_CC_D>, ISA_MAXIS1_NOT_32R6_64R6, + FGR_64; +} +//===----------------------------------------------------------------------===// +// Floating Point Instructions +//===----------------------------------------------------------------------===// +def ROUND_W_S : MMRel, StdMMR6Rel, ABSS_FT<"round.w.s", FGR32Opnd, FGR32Opnd, II_ROUND>, + ABSS_FM<0xc, 16>, ISA_MAXIS2; +defm ROUND_W : ROUND_M<"round.w.d", II_ROUND>, ABSS_FM<0xc, 17>, ISA_MAXIS2; +def TRUNC_W_S : MMRel, StdMMR6Rel, ABSS_FT<"trunc.w.s", FGR32Opnd, FGR32Opnd, II_TRUNC>, + ABSS_FM<0xd, 16>, ISA_MAXIS2; +def CEIL_W_S : MMRel, StdMMR6Rel, ABSS_FT<"ceil.w.s", FGR32Opnd, FGR32Opnd, II_CEIL>, + ABSS_FM<0xe, 16>, ISA_MAXIS2; +def FLOOR_W_S : MMRel, StdMMR6Rel, ABSS_FT<"floor.w.s", FGR32Opnd, FGR32Opnd, II_FLOOR>, + ABSS_FM<0xf, 16>, ISA_MAXIS2; +def CVT_W_S : MMRel, ABSS_FT<"cvt.w.s", FGR32Opnd, FGR32Opnd, II_CVT>, + ABSS_FM<0x24, 16>; + +defm TRUNC_W : ROUND_M<"trunc.w.d", II_TRUNC>, ABSS_FM<0xd, 17>, ISA_MAXIS2; +defm CEIL_W : ROUND_M<"ceil.w.d", II_CEIL>, ABSS_FM<0xe, 17>, ISA_MAXIS2; +defm FLOOR_W : ROUND_M<"floor.w.d", II_FLOOR>, ABSS_FM<0xf, 17>, ISA_MAXIS2; +defm CVT_W : ROUND_M<"cvt.w.d", II_CVT>, ABSS_FM<0x24, 17>; + +let AdditionalPredicates = [NotInMicroMaxis] in { + def RECIP_S : MMRel, ABSS_FT<"recip.s", FGR32Opnd, FGR32Opnd, II_RECIP_S>, + ABSS_FM<0b010101, 0x10>, INSN_MAXIS4_32R2; + def RECIP_D32 : MMRel, ABSS_FT<"recip.d", AFGR64Opnd, AFGR64Opnd, II_RECIP_D>, + ABSS_FM<0b010101, 0x11>, INSN_MAXIS4_32R2, FGR_32 { + let BaseOpcode = "RECIP_D32"; + } + let DecoderNamespace = "MaxisFP64" in + def RECIP_D64 : MMRel, ABSS_FT<"recip.d", FGR64Opnd, FGR64Opnd, + II_RECIP_D>, ABSS_FM<0b010101, 0x11>, + INSN_MAXIS4_32R2, FGR_64; + def RSQRT_S : MMRel, ABSS_FT<"rsqrt.s", FGR32Opnd, FGR32Opnd, II_RSQRT_S>, + ABSS_FM<0b010110, 0x10>, INSN_MAXIS4_32R2; + def RSQRT_D32 : MMRel, ABSS_FT<"rsqrt.d", AFGR64Opnd, AFGR64Opnd, II_RSQRT_D>, + ABSS_FM<0b010110, 0x11>, INSN_MAXIS4_32R2, FGR_32 { + let BaseOpcode = "RSQRT_D32"; + } + let DecoderNamespace = "MaxisFP64" in + def RSQRT_D64 : MMRel, ABSS_FT<"rsqrt.d", FGR64Opnd, FGR64Opnd, + II_RSQRT_D>, ABSS_FM<0b010110, 0x11>, + INSN_MAXIS4_32R2, FGR_64; +} +let DecoderNamespace = "MaxisFP64" in { + let AdditionalPredicates = [NotInMicroMaxis] in { + def ROUND_L_S : ABSS_FT<"round.l.s", FGR64Opnd, FGR32Opnd, II_ROUND>, + ABSS_FM<0x8, 16>, FGR_64; + def ROUND_L_D64 : ABSS_FT<"round.l.d", FGR64Opnd, FGR64Opnd, II_ROUND>, + ABSS_FM<0x8, 17>, FGR_64; + def TRUNC_L_S : ABSS_FT<"trunc.l.s", FGR64Opnd, FGR32Opnd, II_TRUNC>, + ABSS_FM<0x9, 16>, FGR_64; + def TRUNC_L_D64 : ABSS_FT<"trunc.l.d", FGR64Opnd, FGR64Opnd, II_TRUNC>, + ABSS_FM<0x9, 17>, FGR_64; + def CEIL_L_S : ABSS_FT<"ceil.l.s", FGR64Opnd, FGR32Opnd, II_CEIL>, + ABSS_FM<0xa, 16>, FGR_64; + def CEIL_L_D64 : ABSS_FT<"ceil.l.d", FGR64Opnd, FGR64Opnd, II_CEIL>, + ABSS_FM<0xa, 17>, FGR_64; + def FLOOR_L_S : ABSS_FT<"floor.l.s", FGR64Opnd, FGR32Opnd, II_FLOOR>, + ABSS_FM<0xb, 16>, FGR_64; + def FLOOR_L_D64 : ABSS_FT<"floor.l.d", FGR64Opnd, FGR64Opnd, II_FLOOR>, + ABSS_FM<0xb, 17>, FGR_64; + } +} + +def CVT_S_W : MMRel, ABSS_FT<"cvt.s.w", FGR32Opnd, FGR32Opnd, II_CVT>, + ABSS_FM<0x20, 20>; +let AdditionalPredicates = [NotInMicroMaxis] in{ + def CVT_L_S : MMRel, ABSS_FT<"cvt.l.s", FGR64Opnd, FGR32Opnd, II_CVT>, + ABSS_FM<0x25, 16>, INSN_MAXIS3_32R2; + def CVT_L_D64: MMRel, ABSS_FT<"cvt.l.d", FGR64Opnd, FGR64Opnd, II_CVT>, + ABSS_FM<0x25, 17>, INSN_MAXIS3_32R2; +} + +def CVT_S_D32 : MMRel, ABSS_FT<"cvt.s.d", FGR32Opnd, AFGR64Opnd, II_CVT>, + ABSS_FM<0x20, 17>, FGR_32; +def CVT_D32_W : MMRel, ABSS_FT<"cvt.d.w", AFGR64Opnd, FGR32Opnd, II_CVT>, + ABSS_FM<0x21, 20>, FGR_32; +def CVT_D32_S : MMRel, ABSS_FT<"cvt.d.s", AFGR64Opnd, FGR32Opnd, II_CVT>, + ABSS_FM<0x21, 16>, FGR_32; + +let DecoderNamespace = "MaxisFP64" in { + def CVT_S_D64 : ABSS_FT<"cvt.s.d", FGR32Opnd, FGR64Opnd, II_CVT>, + ABSS_FM<0x20, 17>, FGR_64; + let AdditionalPredicates = [NotInMicroMaxis] in{ + def CVT_S_L : ABSS_FT<"cvt.s.l", FGR32Opnd, FGR64Opnd, II_CVT>, + ABSS_FM<0x20, 21>, FGR_64; + } + def CVT_D64_W : ABSS_FT<"cvt.d.w", FGR64Opnd, FGR32Opnd, II_CVT>, + ABSS_FM<0x21, 20>, FGR_64; + def CVT_D64_S : ABSS_FT<"cvt.d.s", FGR64Opnd, FGR32Opnd, II_CVT>, + ABSS_FM<0x21, 16>, FGR_64; + def CVT_D64_L : ABSS_FT<"cvt.d.l", FGR64Opnd, FGR64Opnd, II_CVT>, + ABSS_FM<0x21, 21>, FGR_64; +} + +let isPseudo = 1, isCodeGenOnly = 1 in { + def PseudoCVT_S_W : ABSS_FT<"", FGR32Opnd, GPR32Opnd, II_CVT>; + def PseudoCVT_D32_W : ABSS_FT<"", AFGR64Opnd, GPR32Opnd, II_CVT>; + def PseudoCVT_S_L : ABSS_FT<"", FGR64Opnd, GPR64Opnd, II_CVT>; + def PseudoCVT_D64_W : ABSS_FT<"", FGR64Opnd, GPR32Opnd, II_CVT>; + def PseudoCVT_D64_L : ABSS_FT<"", FGR64Opnd, GPR64Opnd, II_CVT>; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def FABS_S : MMRel, ABSS_FT<"abs.s", FGR32Opnd, FGR32Opnd, II_ABS, fabs>, + ABSS_FM<0x5, 16>; + defm FABS : ABSS_M<"abs.d", II_ABS, fabs>, ABSS_FM<0x5, 17>; +} + +def FNEG_S : MMRel, ABSS_FT<"neg.s", FGR32Opnd, FGR32Opnd, II_NEG, fneg>, + ABSS_FM<0x7, 16>; +defm FNEG : ABSS_M<"neg.d", II_NEG, fneg>, ABSS_FM<0x7, 17>; + +def FSQRT_S : MMRel, StdMMR6Rel, ABSS_FT<"sqrt.s", FGR32Opnd, FGR32Opnd, + II_SQRT_S, fsqrt>, ABSS_FM<0x4, 16>, ISA_MAXIS2; +defm FSQRT : ABSS_M<"sqrt.d", II_SQRT_D, fsqrt>, ABSS_FM<0x4, 17>, ISA_MAXIS2; + +// The odd-numbered registers are only referenced when doing loads, +// stores, and moves between floating-point and integer registers. +// When defining instructions, we reference all 32-bit registers, +// regardless of register aliasing. + +/// Move Control Registers From/To CPU Registers +let AdditionalPredicates = [NotInMicroMaxis] in { + def CFC1 : MMRel, MFC1_FT<"cfc1", GPR32Opnd, CCROpnd, II_CFC1>, MFC1_FM<2>; + def CTC1 : MMRel, MTC1_FT<"ctc1", CCROpnd, GPR32Opnd, II_CTC1>, MFC1_FM<6>; +} +def MFC1 : MMRel, MFC1_FT<"mfc1", GPR32Opnd, FGR32Opnd, II_MFC1, + bitconvert>, MFC1_FM<0>; +def MFC1_D64 : MFC1_FT<"mfc1", GPR32Opnd, FGR64Opnd, II_MFC1>, MFC1_FM<0>, + FGR_64 { + let DecoderNamespace = "MaxisFP64"; +} +def MTC1 : MMRel, MTC1_FT<"mtc1", FGR32Opnd, GPR32Opnd, II_MTC1, + bitconvert>, MFC1_FM<4>; +def MTC1_D64 : MTC1_FT<"mtc1", FGR64Opnd, GPR32Opnd, II_MTC1>, MFC1_FM<4>, + FGR_64 { + let DecoderNamespace = "MaxisFP64"; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def MFHC1_D32 : MMRel, MFC1_FT<"mfhc1", GPR32Opnd, AFGR64Opnd, II_MFHC1>, + MFC1_FM<3>, ISA_MAXIS32R2, FGR_32; + def MFHC1_D64 : MFC1_FT<"mfhc1", GPR32Opnd, FGR64Opnd, II_MFHC1>, + MFC1_FM<3>, ISA_MAXIS32R2, FGR_64 { + let DecoderNamespace = "MaxisFP64"; + } +} +let AdditionalPredicates = [NotInMicroMaxis] in { + def MTHC1_D32 : MMRel, StdMMR6Rel, MTC1_64_FT<"mthc1", AFGR64Opnd, GPR32Opnd, II_MTHC1>, + MFC1_FM<7>, ISA_MAXIS32R2, FGR_32; + def MTHC1_D64 : MTC1_64_FT<"mthc1", FGR64Opnd, GPR32Opnd, II_MTHC1>, + MFC1_FM<7>, ISA_MAXIS32R2, FGR_64 { + let DecoderNamespace = "MaxisFP64"; + } +} +let AdditionalPredicates = [NotInMicroMaxis] in { + def DMTC1 : MTC1_FT<"dmtc1", FGR64Opnd, GPR64Opnd, II_DMTC1, + bitconvert>, MFC1_FM<5>, ISA_MAXIS3; + def DMFC1 : MFC1_FT<"dmfc1", GPR64Opnd, FGR64Opnd, II_DMFC1, + bitconvert>, MFC1_FM<1>, ISA_MAXIS3; +} + +def FMOV_S : MMRel, ABSS_FT<"mov.s", FGR32Opnd, FGR32Opnd, II_MOV_S>, + ABSS_FM<0x6, 16>; +def FMOV_D32 : MMRel, ABSS_FT<"mov.d", AFGR64Opnd, AFGR64Opnd, II_MOV_D>, + ABSS_FM<0x6, 17>, FGR_32; +def FMOV_D64 : ABSS_FT<"mov.d", FGR64Opnd, FGR64Opnd, II_MOV_D>, + ABSS_FM<0x6, 17>, FGR_64 { + let DecoderNamespace = "MaxisFP64"; +} + +/// Floating Point Memory Instructions +let AdditionalPredicates = [NotInMicroMaxis] in { + def LWC1 : MMRel, LW_FT<"lwc1", FGR32Opnd, mem_simm16, II_LWC1, load>, + LW_FM<0x31>; + def SWC1 : MMRel, SW_FT<"swc1", FGR32Opnd, mem_simm16, II_SWC1, store>, + LW_FM<0x39>; +} + +let DecoderNamespace = "MaxisFP64", AdditionalPredicates = [NotInMicroMaxis] in { + def LDC164 : StdMMR6Rel, LW_FT<"ldc1", FGR64Opnd, mem_simm16, II_LDC1, load>, + LW_FM<0x35>, ISA_MAXIS2, FGR_64 { + let BaseOpcode = "LDC164"; + } + def SDC164 : StdMMR6Rel, SW_FT<"sdc1", FGR64Opnd, mem_simm16, II_SDC1, store>, + LW_FM<0x3d>, ISA_MAXIS2, FGR_64; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def LDC1 : MMRel, StdMMR6Rel, LW_FT<"ldc1", AFGR64Opnd, mem_simm16, II_LDC1, + load>, LW_FM<0x35>, ISA_MAXIS2, FGR_32 { + let BaseOpcode = "LDC132"; + } + def SDC1 : MMRel, SW_FT<"sdc1", AFGR64Opnd, mem_simm16, II_SDC1, store>, + LW_FM<0x3d>, ISA_MAXIS2, FGR_32; +} + +// Indexed loads and stores. +// Base register + offset register addressing mode (indicated by "x" in the +// instruction mnemonic) is disallowed under NaCl. +let AdditionalPredicates = [IsNotNaCl] in { + def LWXC1 : MMRel, LWXC1_FT<"lwxc1", FGR32Opnd, II_LWXC1, load>, LWXC1_FM<0>, + INSN_MAXIS4_32R2_NOT_32R6_64R6; + def SWXC1 : MMRel, SWXC1_FT<"swxc1", FGR32Opnd, II_SWXC1, store>, SWXC1_FM<8>, + INSN_MAXIS4_32R2_NOT_32R6_64R6; +} + +let AdditionalPredicates = [NotInMicroMaxis, IsNotNaCl] in { + def LDXC1 : LWXC1_FT<"ldxc1", AFGR64Opnd, II_LDXC1, load>, LWXC1_FM<1>, + INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_32; + def SDXC1 : SWXC1_FT<"sdxc1", AFGR64Opnd, II_SDXC1, store>, SWXC1_FM<9>, + INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_32; +} + +let DecoderNamespace="MaxisFP64" in { + def LDXC164 : LWXC1_FT<"ldxc1", FGR64Opnd, II_LDXC1, load>, LWXC1_FM<1>, + INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_64; + def SDXC164 : SWXC1_FT<"sdxc1", FGR64Opnd, II_SDXC1, store>, SWXC1_FM<9>, + INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_64; +} + +// Load/store doubleword indexed unaligned. +// FIXME: This instruction should not be defined for FGR_32. +let AdditionalPredicates = [IsNotNaCl] in { + def LUXC1 : MMRel, LWXC1_FT<"luxc1", AFGR64Opnd, II_LUXC1>, LWXC1_FM<0x5>, + INSN_MAXIS5_32R2_NOT_32R6_64R6, FGR_32; + def SUXC1 : MMRel, SWXC1_FT<"suxc1", AFGR64Opnd, II_SUXC1>, SWXC1_FM<0xd>, + INSN_MAXIS5_32R2_NOT_32R6_64R6, FGR_32; +} + +let DecoderNamespace="MaxisFP64" in { + def LUXC164 : LWXC1_FT<"luxc1", FGR64Opnd, II_LUXC1>, LWXC1_FM<0x5>, + INSN_MAXIS5_32R2_NOT_32R6_64R6, FGR_64; + def SUXC164 : SWXC1_FT<"suxc1", FGR64Opnd, II_SUXC1>, SWXC1_FM<0xd>, + INSN_MAXIS5_32R2_NOT_32R6_64R6, FGR_64; +} + +/// Floating-point Aritmetic +def FADD_S : MMRel, ADDS_FT<"add.s", FGR32Opnd, II_ADD_S, 1, fadd>, + ADDS_FM<0x00, 16>; +defm FADD : ADDS_M<"add.d", II_ADD_D, 1, fadd>, ADDS_FM<0x00, 17>; +def FDIV_S : MMRel, ADDS_FT<"div.s", FGR32Opnd, II_DIV_S, 0, fdiv>, + ADDS_FM<0x03, 16>; +defm FDIV : ADDS_M<"div.d", II_DIV_D, 0, fdiv>, ADDS_FM<0x03, 17>; +def FMUL_S : MMRel, ADDS_FT<"mul.s", FGR32Opnd, II_MUL_S, 1, fmul>, + ADDS_FM<0x02, 16>; +defm FMUL : ADDS_M<"mul.d", II_MUL_D, 1, fmul>, ADDS_FM<0x02, 17>; +def FSUB_S : MMRel, ADDS_FT<"sub.s", FGR32Opnd, II_SUB_S, 0, fsub>, + ADDS_FM<0x01, 16>; +defm FSUB : ADDS_M<"sub.d", II_SUB_D, 0, fsub>, ADDS_FM<0x01, 17>; + +def MADD_S : MMRel, MADDS_FT<"madd.s", FGR32Opnd, II_MADD_S, fadd>, + MADDS_FM<4, 0>, INSN_MAXIS4_32R2_NOT_32R6_64R6, MADD4; +def MSUB_S : MMRel, MADDS_FT<"msub.s", FGR32Opnd, II_MSUB_S, fsub>, + MADDS_FM<5, 0>, INSN_MAXIS4_32R2_NOT_32R6_64R6, MADD4; + +let AdditionalPredicates = [NoNaNsFPMath, HasMadd4] in { + def NMADD_S : MMRel, NMADDS_FT<"nmadd.s", FGR32Opnd, II_NMADD_S, fadd>, + MADDS_FM<6, 0>, INSN_MAXIS4_32R2_NOT_32R6_64R6; + def NMSUB_S : MMRel, NMADDS_FT<"nmsub.s", FGR32Opnd, II_NMSUB_S, fsub>, + MADDS_FM<7, 0>, INSN_MAXIS4_32R2_NOT_32R6_64R6; +} + +def MADD_D32 : MMRel, MADDS_FT<"madd.d", AFGR64Opnd, II_MADD_D, fadd>, + MADDS_FM<4, 1>, INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_32, MADD4; +def MSUB_D32 : MMRel, MADDS_FT<"msub.d", AFGR64Opnd, II_MSUB_D, fsub>, + MADDS_FM<5, 1>, INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_32, MADD4; + +let AdditionalPredicates = [NoNaNsFPMath, HasMadd4] in { + def NMADD_D32 : MMRel, NMADDS_FT<"nmadd.d", AFGR64Opnd, II_NMADD_D, fadd>, + MADDS_FM<6, 1>, INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_32; + def NMSUB_D32 : MMRel, NMADDS_FT<"nmsub.d", AFGR64Opnd, II_NMSUB_D, fsub>, + MADDS_FM<7, 1>, INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_32; +} + +let DecoderNamespace = "MaxisFP64" in { + def MADD_D64 : MADDS_FT<"madd.d", FGR64Opnd, II_MADD_D, fadd>, + MADDS_FM<4, 1>, INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_64, MADD4; + def MSUB_D64 : MADDS_FT<"msub.d", FGR64Opnd, II_MSUB_D, fsub>, + MADDS_FM<5, 1>, INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_64, MADD4; +} + +let AdditionalPredicates = [NoNaNsFPMath, HasMadd4], + DecoderNamespace = "MaxisFP64" in { + def NMADD_D64 : NMADDS_FT<"nmadd.d", FGR64Opnd, II_NMADD_D, fadd>, + MADDS_FM<6, 1>, INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_64; + def NMSUB_D64 : NMADDS_FT<"nmsub.d", FGR64Opnd, II_NMSUB_D, fsub>, + MADDS_FM<7, 1>, INSN_MAXIS4_32R2_NOT_32R6_64R6, FGR_64; +} + +//===----------------------------------------------------------------------===// +// Floating Point Branch Codes +//===----------------------------------------------------------------------===// +// Maxis branch codes. These correspond to condcode in MaxisInstrInfo.h. +// They must be kept in synch. +def MAXIS_BRANCH_F : PatLeaf<(i32 0)>; +def MAXIS_BRANCH_T : PatLeaf<(i32 1)>; + +let AdditionalPredicates = [NotInMicroMaxis] in { + def BC1F : MMRel, BC1F_FT<"bc1f", brtarget, II_BC1F, MAXIS_BRANCH_F>, + BC1F_FM<0, 0>, ISA_MAXIS1_NOT_32R6_64R6; + def BC1FL : MMRel, BC1XL_FT<"bc1fl", brtarget, II_BC1FL>, + BC1F_FM<1, 0>, ISA_MAXIS2_NOT_32R6_64R6; + def BC1T : MMRel, BC1F_FT<"bc1t", brtarget, II_BC1T, MAXIS_BRANCH_T>, + BC1F_FM<0, 1>, ISA_MAXIS1_NOT_32R6_64R6; + def BC1TL : MMRel, BC1XL_FT<"bc1tl", brtarget, II_BC1TL>, + BC1F_FM<1, 1>, ISA_MAXIS2_NOT_32R6_64R6; + +/// Floating Point Compare + def FCMP_S32 : MMRel, CEQS_FT<"s", FGR32, II_C_CC_S, MaxisFPCmp>, CEQS_FM<16>, + ISA_MAXIS1_NOT_32R6_64R6 { + + // FIXME: This is a required to work around the fact that these instructions + // only use $fcc0. Ideally, MaxisFPCmp nodes could be removed and the + // fcc register set is used directly. + bits<3> fcc = 0; + } + def FCMP_D32 : MMRel, CEQS_FT<"d", AFGR64, II_C_CC_D, MaxisFPCmp>, CEQS_FM<17>, + ISA_MAXIS1_NOT_32R6_64R6, FGR_32 { + // FIXME: This is a required to work around the fact that these instructions + // only use $fcc0. Ideally, MaxisFPCmp nodes could be removed and the + // fcc register set is used directly. + bits<3> fcc = 0; + } +} +let DecoderNamespace = "MaxisFP64" in +def FCMP_D64 : CEQS_FT<"d", FGR64, II_C_CC_D, MaxisFPCmp>, CEQS_FM<17>, + ISA_MAXIS1_NOT_32R6_64R6, FGR_64 { + // FIXME: This is a required to work around the fact that thiese instructions + // only use $fcc0. Ideally, MaxisFPCmp nodes could be removed and the + // fcc register set is used directly. + bits<3> fcc = 0; +} + +//===----------------------------------------------------------------------===// +// Floating Point Pseudo-Instructions +//===----------------------------------------------------------------------===// + +// This pseudo instr gets expanded into 2 mtc1 instrs after register +// allocation. +class BuildPairF64Base : + PseudoSE<(outs RO:$dst), (ins GPR32Opnd:$lo, GPR32Opnd:$hi), + [(set RO:$dst, (MaxisBuildPairF64 GPR32Opnd:$lo, GPR32Opnd:$hi))], + II_MTC1>; + +def BuildPairF64 : BuildPairF64Base, FGR_32, HARDFLOAT; +def BuildPairF64_64 : BuildPairF64Base, FGR_64, HARDFLOAT; + +// This pseudo instr gets expanded into 2 mfc1 instrs after register +// allocation. +// if n is 0, lower part of src is extracted. +// if n is 1, higher part of src is extracted. +// This node has associated scheduling information as the pre RA scheduler +// asserts otherwise. +class ExtractElementF64Base : + PseudoSE<(outs GPR32Opnd:$dst), (ins RO:$src, i32imm:$n), + [(set GPR32Opnd:$dst, (MaxisExtractElementF64 RO:$src, imm:$n))], + II_MFC1>; + +def ExtractElementF64 : ExtractElementF64Base, FGR_32, HARDFLOAT; +def ExtractElementF64_64 : ExtractElementF64Base, FGR_64, HARDFLOAT; + +def PseudoTRUNC_W_S : MaxisAsmPseudoInst<(outs FGR32Opnd:$fd), + (ins FGR32Opnd:$fs, GPR32Opnd:$rs), + "trunc.w.s\t$fd, $fs, $rs">; + +def PseudoTRUNC_W_D32 : MaxisAsmPseudoInst<(outs FGR32Opnd:$fd), + (ins AFGR64Opnd:$fs, GPR32Opnd:$rs), + "trunc.w.d\t$fd, $fs, $rs">, + FGR_32, HARDFLOAT; + +def PseudoTRUNC_W_D : MaxisAsmPseudoInst<(outs FGR32Opnd:$fd), + (ins FGR64Opnd:$fs, GPR32Opnd:$rs), + "trunc.w.d\t$fd, $fs, $rs">, + FGR_64, HARDFLOAT; + +def LoadImmSingleGPR : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), + (ins imm64:$fpimm), + "li.s\t$rd, $fpimm">; + +def LoadImmSingleFGR : MaxisAsmPseudoInst<(outs StrictlyFGR32Opnd:$rd), + (ins imm64:$fpimm), + "li.s\t$rd, $fpimm">, + HARDFLOAT; + +def LoadImmDoubleGPR : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), + (ins imm64:$fpimm), + "li.d\t$rd, $fpimm">; + +def LoadImmDoubleFGR_32 : MaxisAsmPseudoInst<(outs StrictlyAFGR64Opnd:$rd), + (ins imm64:$fpimm), + "li.d\t$rd, $fpimm">, + FGR_32, HARDFLOAT; + +def LoadImmDoubleFGR : MaxisAsmPseudoInst<(outs StrictlyFGR64Opnd:$rd), + (ins imm64:$fpimm), + "li.d\t$rd, $fpimm">, + FGR_64, HARDFLOAT; + +//===----------------------------------------------------------------------===// +// InstAliases. +//===----------------------------------------------------------------------===// +def : MaxisInstAlias + <"s.s $fd, $addr", (SWC1 FGR32Opnd:$fd, mem_simm16:$addr), 0>, + ISA_MAXIS2, HARDFLOAT; +def : MaxisInstAlias + <"s.d $fd, $addr", (SDC1 AFGR64Opnd:$fd, mem_simm16:$addr), 0>, + FGR_32, ISA_MAXIS2, HARDFLOAT; +def : MaxisInstAlias + <"s.d $fd, $addr", (SDC164 FGR64Opnd:$fd, mem_simm16:$addr), 0>, + FGR_64, ISA_MAXIS2, HARDFLOAT; + +def : MaxisInstAlias + <"l.s $fd, $addr", (LWC1 FGR32Opnd:$fd, mem_simm16:$addr), 0>, + ISA_MAXIS2, HARDFLOAT; +def : MaxisInstAlias + <"l.d $fd, $addr", (LDC1 AFGR64Opnd:$fd, mem_simm16:$addr), 0>, + FGR_32, ISA_MAXIS2, HARDFLOAT; +def : MaxisInstAlias + <"l.d $fd, $addr", (LDC164 FGR64Opnd:$fd, mem_simm16:$addr), 0>, + FGR_64, ISA_MAXIS2, HARDFLOAT; + +multiclass C_COND_ALIASES { + def : MaxisInstAlias("C_F_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_UN_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_EQ_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_UEQ_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_OLT_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_ULT_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_OLE_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_ULE_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_SF_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_NGLE_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_SEQ_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_NGL_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_LT_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_NGE_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_LE_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; + def : MaxisInstAlias("C_NGT_"#NAME) FCC0, + RC:$fs, RC:$ft), 1>; +} + +multiclass BC1_ALIASES { + def : MaxisInstAlias; + + def : MaxisInstAlias; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + defm S : C_COND_ALIASES<"s", FGR32Opnd>, HARDFLOAT, + ISA_MAXIS1_NOT_32R6_64R6; + defm D32 : C_COND_ALIASES<"d", AFGR64Opnd>, HARDFLOAT, + ISA_MAXIS1_NOT_32R6_64R6, FGR_32; + defm D64 : C_COND_ALIASES<"d", FGR64Opnd>, HARDFLOAT, + ISA_MAXIS1_NOT_32R6_64R6, FGR_64; + + defm : BC1_ALIASES, ISA_MAXIS1_NOT_32R6_64R6, + HARDFLOAT; + defm : BC1_ALIASES, ISA_MAXIS2_NOT_32R6_64R6, + HARDFLOAT; +} +//===----------------------------------------------------------------------===// +// Floating Point Patterns +//===----------------------------------------------------------------------===// +def : MaxisPat<(f32 fpimm0), (MTC1 ZERO)>; +def : MaxisPat<(f32 fpimm0neg), (FNEG_S (MTC1 ZERO))>; + +def : MaxisPat<(f32 (sint_to_fp GPR32Opnd:$src)), + (PseudoCVT_S_W GPR32Opnd:$src)>; +def : MaxisPat<(MaxisTruncIntFP FGR32Opnd:$src), + (TRUNC_W_S FGR32Opnd:$src)>; + +def : MaxisPat<(MaxisMTC1_D64 GPR32Opnd:$src), + (MTC1_D64 GPR32Opnd:$src)>, FGR_64; + +def : MaxisPat<(f64 (sint_to_fp GPR32Opnd:$src)), + (PseudoCVT_D32_W GPR32Opnd:$src)>, FGR_32; +def : MaxisPat<(MaxisTruncIntFP AFGR64Opnd:$src), + (TRUNC_W_D32 AFGR64Opnd:$src)>, FGR_32; +def : MaxisPat<(f32 (fpround AFGR64Opnd:$src)), + (CVT_S_D32 AFGR64Opnd:$src)>, FGR_32; +def : MaxisPat<(f64 (fpextend FGR32Opnd:$src)), + (CVT_D32_S FGR32Opnd:$src)>, FGR_32; + +def : MaxisPat<(f64 fpimm0), (DMTC1 ZERO_64)>, FGR_64; +def : MaxisPat<(f64 fpimm0neg), (FNEG_D64 (DMTC1 ZERO_64))>, FGR_64; + +def : MaxisPat<(f64 (sint_to_fp GPR32Opnd:$src)), + (PseudoCVT_D64_W GPR32Opnd:$src)>, FGR_64; +def : MaxisPat<(f32 (sint_to_fp GPR64Opnd:$src)), + (EXTRACT_SUBREG (PseudoCVT_S_L GPR64Opnd:$src), sub_lo)>, FGR_64; +def : MaxisPat<(f64 (sint_to_fp GPR64Opnd:$src)), + (PseudoCVT_D64_L GPR64Opnd:$src)>, FGR_64; + +def : MaxisPat<(MaxisTruncIntFP FGR64Opnd:$src), + (TRUNC_W_D64 FGR64Opnd:$src)>, FGR_64; +def : MaxisPat<(MaxisTruncIntFP FGR32Opnd:$src), + (TRUNC_L_S FGR32Opnd:$src)>, FGR_64; +def : MaxisPat<(MaxisTruncIntFP FGR64Opnd:$src), + (TRUNC_L_D64 FGR64Opnd:$src)>, FGR_64; + +def : MaxisPat<(f32 (fpround FGR64Opnd:$src)), + (CVT_S_D64 FGR64Opnd:$src)>, FGR_64; +def : MaxisPat<(f64 (fpextend FGR32Opnd:$src)), + (CVT_D64_S FGR32Opnd:$src)>, FGR_64; + +// To generate NMADD and NMSUB instructions when fneg node is present +multiclass NMADD_NMSUB { + def : MaxisPat<(fneg (fadd (fmul RC:$fs, RC:$ft), RC:$fr)), + (Nmadd RC:$fr, RC:$fs, RC:$ft)>; + def : MaxisPat<(fneg (fsub (fmul RC:$fs, RC:$ft), RC:$fr)), + (Nmsub RC:$fr, RC:$fs, RC:$ft)>; +} + +let AdditionalPredicates = [NoNaNsFPMath, HasMadd4, NotInMicroMaxis] in { + defm : NMADD_NMSUB, INSN_MAXIS4_32R2_NOT_32R6_64R6; + defm : NMADD_NMSUB, FGR_32, INSN_MAXIS4_32R2_NOT_32R6_64R6; + defm : NMADD_NMSUB, FGR_64, INSN_MAXIS4_32R2_NOT_32R6_64R6; +} + +// Patterns for loads/stores with a reg+imm operand. +let AdditionalPredicates = [NotInMicroMaxis] in { + let AddedComplexity = 40 in { + def : LoadRegImmPat; + def : StoreRegImmPat; + + def : LoadRegImmPat, FGR_64; + def : StoreRegImmPat, FGR_64; + + def : LoadRegImmPat, FGR_32; + def : StoreRegImmPat, FGR_32; + } +} diff --git a/lib/Target/Maxis/MaxisInstrFormats.td b/lib/Target/Maxis/MaxisInstrFormats.td new file mode 100644 index 00000000..e69f8b4a --- /dev/null +++ b/lib/Target/Maxis/MaxisInstrFormats.td @@ -0,0 +1,972 @@ +//===-- MaxisInstrFormats.td - Maxis Instruction Formats -----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Describe MAXIS instructions format +// +// CPU INSTRUCTION FORMATS +// +// opcode - operation code. +// rs - src reg. +// rt - dst reg (on a 2 regs instr) or src reg (on a 3 reg instr). +// rd - dst reg, only used on 3 regs instr. +// shamt - only used on shift instructions, contains the shift amount. +// funct - combined with opcode field give us an operation code. +// +//===----------------------------------------------------------------------===// + +// Format specifies the encoding used by the instruction. This is part of the +// ad-hoc solution used to emit machine instruction encodings by our machine +// code emitter. +class Format val> { + bits<4> Value = val; +} + +def Pseudo : Format<0>; +def FrmR : Format<1>; +def FrmI : Format<2>; +def FrmJ : Format<3>; +def FrmFR : Format<4>; +def FrmFI : Format<5>; +def FrmOther : Format<6>; // Instruction w/ a custom format + +class MMRel; + +def Std2MicroMaxis : InstrMapping { + let FilterClass = "MMRel"; + // Instructions with the same BaseOpcode and isNVStore values form a row. + let RowFields = ["BaseOpcode"]; + // Instructions with the same predicate sense form a column. + let ColFields = ["Arch"]; + // The key column is the unpredicated instructions. + let KeyCol = ["se"]; + // Value columns are PredSense=true and PredSense=false + let ValueCols = [["se"], ["micromaxis"]]; +} + +class StdMMR6Rel; + +def Std2MicroMaxisR6 : InstrMapping { + let FilterClass = "StdMMR6Rel"; + // Instructions with the same BaseOpcode and isNVStore values form a row. + let RowFields = ["BaseOpcode"]; + // Instructions with the same predicate sense form a column. + let ColFields = ["Arch"]; + // The key column is the unpredicated instructions. + let KeyCol = ["se"]; + // Value columns are PredSense=true and PredSense=false + let ValueCols = [["se"], ["micromaxisr6"]]; +} + +class StdArch { + string Arch = "se"; +} + +// Generic Maxis Format +class MaxisInst pattern, + InstrItinClass itin, Format f>: Instruction +{ + field bits<32> Inst; + Format Form = f; + + let Namespace = "Maxis"; + + let Size = 4; + + bits<6> Opcode = 0; + + // Top 6 bits are the 'opcode' field + let Inst{31-26} = Opcode; + + let OutOperandList = outs; + let InOperandList = ins; + + let AsmString = asmstr; + let Pattern = pattern; + let Itinerary = itin; + + // + // Attributes specific to Maxis instructions... + // + bits<4> FormBits = Form.Value; + bit isCTI = 0; // Any form of Control Transfer Instruction. + // Required for MAXISR6 + bit hasForbiddenSlot = 0; // Instruction has a forbidden slot. + bit IsPCRelativeLoad = 0; // Load instruction with implicit source register + // ($pc) and with explicit offset and destination + // register + bit hasFCCRegOperand = 0; // Instruction uses $fcc register and is + // present in MAXIS-I to MAXIS-III. + + // TSFlags layout should be kept in sync with MCTargetDesc/MaxisBaseInfo.h. + let TSFlags{3-0} = FormBits; + let TSFlags{4} = isCTI; + let TSFlags{5} = hasForbiddenSlot; + let TSFlags{6} = IsPCRelativeLoad; + let TSFlags{7} = hasFCCRegOperand; + + let DecoderNamespace = "Maxis"; + + field bits<32> SoftFail = 0; +} + +// Maxis32/64 Instruction Format +class InstSE pattern, + InstrItinClass itin, Format f, string opstr = ""> : + MaxisInst, PredicateControl { + let EncodingPredicates = [HasStdEnc]; + string BaseOpcode = opstr; + string Arch; +} + +// Maxis Pseudo Instructions Format +class MaxisPseudo pattern, + InstrItinClass itin = IIPseudo> : + MaxisInst { + let isCodeGenOnly = 1; + let isPseudo = 1; +} + +// Maxis32/64 Pseudo Instruction Format +class PseudoSE pattern, + InstrItinClass itin = IIPseudo> : + MaxisPseudo, PredicateControl { + let EncodingPredicates = [HasStdEnc]; +} + +// Pseudo-instructions for alternate assembly syntax (never used by codegen). +// These are aliases that require C++ handling to convert to the target +// instruction, while InstAliases can be handled directly by tblgen. +class MaxisAsmPseudoInst: + MaxisInst, PredicateControl { + let isPseudo = 1; + let Pattern = []; +} +//===----------------------------------------------------------------------===// +// Format R instruction class in Maxis : <|opcode|rs|rt|rd|shamt|funct|> +//===----------------------------------------------------------------------===// + +class FR op, bits<6> _funct, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + InstSE +{ + bits<5> rd; + bits<5> rs; + bits<5> rt; + bits<5> shamt; + bits<6> funct; + + let Opcode = op; + let funct = _funct; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = shamt; + let Inst{5-0} = funct; +} + +//===----------------------------------------------------------------------===// +// Format I instruction class in Maxis : <|opcode|rs|rt|immediate|> +//===----------------------------------------------------------------------===// + +class FI op, dag outs, dag ins, string asmstr, list pattern, + InstrItinClass itin>: InstSE +{ + bits<5> rt; + bits<5> rs; + bits<16> imm16; + + let Opcode = op; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-0} = imm16; +} + +class BranchBase op, dag outs, dag ins, string asmstr, + list pattern, InstrItinClass itin>: + InstSE +{ + bits<5> rs; + bits<5> rt; + bits<16> imm16; + + let Opcode = op; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-0} = imm16; +} + +//===----------------------------------------------------------------------===// +// Format J instruction class in Maxis : <|opcode|address|> +//===----------------------------------------------------------------------===// + +class FJ op> : StdArch +{ + bits<26> target; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-0} = target; +} + +//===----------------------------------------------------------------------===// +// MFC instruction class in Maxis : <|op|mf|rt|rd|0000000|sel|> +//===----------------------------------------------------------------------===// +class MFC3OP_FM op, bits<5> mfmt> +{ + bits<5> rt; + bits<5> rd; + bits<3> sel; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = mfmt; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-3} = 0; + let Inst{2-0} = sel; +} + +class MFC2OP_FM op, bits<5> mfmt> : StdArch { + bits<5> rt; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = mfmt; + let Inst{20-16} = rt; + let Inst{15-0} = imm16; +} + +class ADD_FM op, bits<6> funct> : StdArch { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = funct; +} + +class ADDI_FM op> : StdArch { + bits<5> rs; + bits<5> rt; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-0} = imm16; +} + +class SRA_FM funct, bit rotate> : StdArch { + bits<5> rd; + bits<5> rt; + bits<5> shamt; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-22} = 0; + let Inst{21} = rotate; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = shamt; + let Inst{5-0} = funct; +} + +class SRLV_FM funct, bit rotate> : StdArch { + bits<5> rd; + bits<5> rt; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-7} = 0; + let Inst{6} = rotate; + let Inst{5-0} = funct; +} + +class BEQ_FM op> : StdArch { + bits<5> rs; + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-0} = offset; +} + +class BGEZ_FM op, bits<5> funct> : StdArch { + bits<5> rs; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rs; + let Inst{20-16} = funct; + let Inst{15-0} = offset; +} + +class BBIT_FM op> : StdArch { + bits<5> rs; + bits<5> p; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rs; + let Inst{20-16} = p; + let Inst{15-0} = offset; +} + +class SLTI_FM op> : StdArch { + bits<5> rt; + bits<5> rs; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-0} = imm16; +} + +class MFLO_FM funct> : StdArch { + bits<5> rd; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-16} = 0; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = funct; +} + +class MTLO_FM funct> : StdArch { + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rs; + let Inst{20-6} = 0; + let Inst{5-0} = funct; +} + +class SEB_FM funct, bits<6> funct2> : StdArch { + bits<5> rd; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0x1f; + let Inst{25-21} = 0; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = funct; + let Inst{5-0} = funct2; +} + +class CLO_FM funct> : StdArch { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0x1c; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = funct; + let rt = rd; +} + +class LUI_FM : StdArch { + bits<5> rt; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = 0xf; + let Inst{25-21} = 0; + let Inst{20-16} = rt; + let Inst{15-0} = imm16; +} + +class JALR_FM { + bits<5> rd; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rs; + let Inst{20-16} = 0; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = 9; +} + +class BGEZAL_FM funct> : StdArch { + bits<5> rs; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 1; + let Inst{25-21} = rs; + let Inst{20-16} = funct; + let Inst{15-0} = offset; +} + +class SYNC_FM : StdArch { + bits<5> stype; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{10-6} = stype; + let Inst{5-0} = 0xf; +} + +class SYNCI_FM : StdArch { + // Produced by the mem_simm16 address as reg << 16 | imm (see getMemEncoding). + bits<21> addr; + bits<5> rs = addr{20-16}; + bits<16> offset = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = 0b000001; + let Inst{25-21} = rs; + let Inst{20-16} = 0b11111; + let Inst{15-0} = offset; +} + +class MULT_FM op, bits<6> funct> : StdArch { + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-6} = 0; + let Inst{5-0} = funct; +} + +class EXT_FM funct> : StdArch { + bits<5> rt; + bits<5> rs; + bits<5> pos; + bits<5> size; + + bits<32> Inst; + + let Inst{31-26} = 0x1f; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = size; + let Inst{10-6} = pos; + let Inst{5-0} = funct; +} + +class RDHWR_FM : StdArch { + bits<5> rt; + bits<5> rd; + + bits<32> Inst; + + let Inst{31-26} = 0x1f; + let Inst{25-21} = 0; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = 0x3b; +} + +class TEQ_FM funct> : StdArch { + bits<5> rs; + bits<5> rt; + bits<10> code_; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-6} = code_; + let Inst{5-0} = funct; +} + +class TEQI_FM funct> : StdArch { + bits<5> rs; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = 1; + let Inst{25-21} = rs; + let Inst{20-16} = funct; + let Inst{15-0} = imm16; +} + +class WAIT_FM : StdArch { + bits<32> Inst; + + let Inst{31-26} = 0x10; + let Inst{25} = 1; + let Inst{24-6} = 0; + let Inst{5-0} = 0x20; +} + +class EXTS_FM funct> : StdArch { + bits<5> rt; + bits<5> rs; + bits<5> pos; + bits<5> lenm1; + + bits<32> Inst; + + let Inst{31-26} = 0x1c; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = lenm1; + let Inst{10-6} = pos; + let Inst{5-0} = funct; +} + +class MTMR_FM funct> : StdArch { + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0x1c; + let Inst{25-21} = rs; + let Inst{20-6} = 0; + let Inst{5-0} = funct; +} + +class POP_FM funct> : StdArch { + bits<5> rd; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0x1c; + let Inst{25-21} = rs; + let Inst{20-16} = 0; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = funct; +} + +class SEQ_FM funct> : StdArch { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0x1c; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = funct; +} + +class SEQI_FM funct> : StdArch { + bits<5> rs; + bits<5> rt; + bits<10> imm10; + + bits<32> Inst; + + let Inst{31-26} = 0x1c; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-6} = imm10; + let Inst{5-0} = funct; +} + +//===----------------------------------------------------------------------===// +// System calls format +//===----------------------------------------------------------------------===// + +class SYS_FM funct> : StdArch +{ + bits<20> code_; + bits<32> Inst; + let Inst{31-26} = 0x0; + let Inst{25-6} = code_; + let Inst{5-0} = funct; +} + +//===----------------------------------------------------------------------===// +// Break instruction format +//===----------------------------------------------------------------------===// + +class BRK_FM funct> : StdArch +{ + bits<10> code_1; + bits<10> code_2; + bits<32> Inst; + let Inst{31-26} = 0x0; + let Inst{25-16} = code_1; + let Inst{15-6} = code_2; + let Inst{5-0} = funct; +} + +//===----------------------------------------------------------------------===// +// Exception return format +//===----------------------------------------------------------------------===// + +class ER_FM funct, bit LLBit> : StdArch +{ + bits<32> Inst; + let Inst{31-26} = 0x10; + let Inst{25} = 1; + let Inst{24-7} = 0; + let Inst{6} = LLBit; + let Inst{5-0} = funct; +} + +//===----------------------------------------------------------------------===// +// Enable/disable interrupt instruction format +//===----------------------------------------------------------------------===// + +class EI_FM sc> : StdArch +{ + bits<32> Inst; + bits<5> rt; + let Inst{31-26} = 0x10; + let Inst{25-21} = 0xb; + let Inst{20-16} = rt; + let Inst{15-11} = 0xc; + let Inst{10-6} = 0; + let Inst{5} = sc; + let Inst{4-0} = 0; +} + +//===----------------------------------------------------------------------===// +// +// FLOATING POINT INSTRUCTION FORMATS +// +// opcode - operation code. +// fs - src reg. +// ft - dst reg (on a 2 regs instr) or src reg (on a 3 reg instr). +// fd - dst reg, only used on 3 regs instr. +// fmt - double or single precision. +// funct - combined with opcode field give us an operation code. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Format FI instruction class in Maxis : <|opcode|base|ft|immediate|> +//===----------------------------------------------------------------------===// + +class FFI op, dag outs, dag ins, string asmstr, list pattern>: + InstSE +{ + bits<5> ft; + bits<5> base; + bits<16> imm16; + + let Opcode = op; + + let Inst{25-21} = base; + let Inst{20-16} = ft; + let Inst{15-0} = imm16; +} + +class ADDS_FM funct, bits<5> fmt> : StdArch { + bits<5> fd; + bits<5> fs; + bits<5> ft; + + bits<32> Inst; + + let Inst{31-26} = 0x11; + let Inst{25-21} = fmt; + let Inst{20-16} = ft; + let Inst{15-11} = fs; + let Inst{10-6} = fd; + let Inst{5-0} = funct; +} + +class ABSS_FM funct, bits<5> fmt> : StdArch { + bits<5> fd; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0x11; + let Inst{25-21} = fmt; + let Inst{20-16} = 0; + let Inst{15-11} = fs; + let Inst{10-6} = fd; + let Inst{5-0} = funct; +} + +class MFC1_FM funct> : StdArch { + bits<5> rt; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0x11; + let Inst{25-21} = funct; + let Inst{20-16} = rt; + let Inst{15-11} = fs; + let Inst{10-0} = 0; +} + +class LW_FM op> : StdArch { + bits<5> rt; + bits<21> addr; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = addr{20-16}; + let Inst{20-16} = rt; + let Inst{15-0} = addr{15-0}; +} + +class MADDS_FM funct, bits<3> fmt> : StdArch { + bits<5> fd; + bits<5> fr; + bits<5> fs; + bits<5> ft; + + bits<32> Inst; + + let Inst{31-26} = 0x13; + let Inst{25-21} = fr; + let Inst{20-16} = ft; + let Inst{15-11} = fs; + let Inst{10-6} = fd; + let Inst{5-3} = funct; + let Inst{2-0} = fmt; +} + +class LWXC1_FM funct> : StdArch { + bits<5> fd; + bits<5> base; + bits<5> index; + + bits<32> Inst; + + let Inst{31-26} = 0x13; + let Inst{25-21} = base; + let Inst{20-16} = index; + let Inst{15-11} = 0; + let Inst{10-6} = fd; + let Inst{5-0} = funct; +} + +class SWXC1_FM funct> : StdArch { + bits<5> fs; + bits<5> base; + bits<5> index; + + bits<32> Inst; + + let Inst{31-26} = 0x13; + let Inst{25-21} = base; + let Inst{20-16} = index; + let Inst{15-11} = fs; + let Inst{10-6} = 0; + let Inst{5-0} = funct; +} + +class BC1F_FM : StdArch { + bits<3> fcc; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 0x11; + let Inst{25-21} = 0x8; + let Inst{20-18} = fcc; + let Inst{17} = nd; + let Inst{16} = tf; + let Inst{15-0} = offset; +} + +class CEQS_FM fmt> : StdArch { + bits<5> fs; + bits<5> ft; + bits<3> fcc; + bits<4> cond; + + bits<32> Inst; + + let Inst{31-26} = 0x11; + let Inst{25-21} = fmt; + let Inst{20-16} = ft; + let Inst{15-11} = fs; + let Inst{10-8} = fcc; + let Inst{7-4} = 0x3; + let Inst{3-0} = cond; +} + +class C_COND_FM fmt, bits<4> c> : CEQS_FM { + let cond = c; +} + +class CMov_I_F_FM funct, bits<5> fmt> : StdArch { + bits<5> fd; + bits<5> fs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0x11; + let Inst{25-21} = fmt; + let Inst{20-16} = rt; + let Inst{15-11} = fs; + let Inst{10-6} = fd; + let Inst{5-0} = funct; +} + +class CMov_F_I_FM : StdArch { + bits<5> rd; + bits<5> rs; + bits<3> fcc; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rs; + let Inst{20-18} = fcc; + let Inst{17} = 0; + let Inst{16} = tf; + let Inst{15-11} = rd; + let Inst{10-6} = 0; + let Inst{5-0} = 1; +} + +class CMov_F_F_FM fmt, bit tf> : StdArch { + bits<5> fd; + bits<5> fs; + bits<3> fcc; + + bits<32> Inst; + + let Inst{31-26} = 0x11; + let Inst{25-21} = fmt; + let Inst{20-18} = fcc; + let Inst{17} = 0; + let Inst{16} = tf; + let Inst{15-11} = fs; + let Inst{10-6} = fd; + let Inst{5-0} = 0x11; +} + +class BARRIER_FM op> : StdArch { + bits<32> Inst; + + let Inst{31-26} = 0; // SPECIAL + let Inst{25-21} = 0; + let Inst{20-16} = 0; // rt = 0 + let Inst{15-11} = 0; // rd = 0 + let Inst{10-6} = op; // Operation + let Inst{5-0} = 0; // SLL +} + +class SDBBP_FM : StdArch { + bits<20> code_; + + bits<32> Inst; + + let Inst{31-26} = 0b011100; // SPECIAL2 + let Inst{25-6} = code_; + let Inst{5-0} = 0b111111; // SDBBP +} + +class JR_HB_FM op> : StdArch{ + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0; // SPECIAL + let Inst{25-21} = rs; + let Inst{20-11} = 0; + let Inst{10} = 1; + let Inst{9-6} = 0; + let Inst{5-0} = op; +} + +class JALR_HB_FM op> : StdArch { + bits<5> rd; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0; // SPECIAL + let Inst{25-21} = rs; + let Inst{20-16} = 0; + let Inst{15-11} = rd; + let Inst{10} = 1; + let Inst{9-6} = 0; + let Inst{5-0} = op; +} + +class COP0_TLB_FM op> : StdArch { + bits<32> Inst; + + let Inst{31-26} = 0x10; // COP0 + let Inst{25} = 1; // CO + let Inst{24-6} = 0; + let Inst{5-0} = op; // Operation +} + +class CACHEOP_FM op> : StdArch { + bits<21> addr; + bits<5> hint; + bits<5> base = addr{20-16}; + bits<16> offset = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = base; + let Inst{20-16} = hint; + let Inst{15-0} = offset; +} diff --git a/lib/Target/Maxis/MaxisInstrInfo.cpp b/lib/Target/Maxis/MaxisInstrInfo.cpp new file mode 100644 index 00000000..b8af7a79 --- /dev/null +++ b/lib/Target/Maxis/MaxisInstrInfo.cpp @@ -0,0 +1,662 @@ +//===- MaxisInstrInfo.cpp - Maxis Instruction Information -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "MaxisInstrInfo.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/TargetOpcodes.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/Target/TargetMachine.h" +#include + +using namespace llvm; + +#define GET_INSTRINFO_CTOR_DTOR +#include "MaxisGenInstrInfo.inc" + +// Pin the vtable to this file. +void MaxisInstrInfo::anchor() {} + +MaxisInstrInfo::MaxisInstrInfo(const MaxisSubtarget &STI, unsigned UncondBr) + : MaxisGenInstrInfo(Maxis::ADJCALLSTACKDOWN, Maxis::ADJCALLSTACKUP), + Subtarget(STI), UncondBrOpc(UncondBr) {} + +const MaxisInstrInfo *MaxisInstrInfo::create(MaxisSubtarget &STI) { + if (STI.inMaxis16Mode()) + return createMaxis16InstrInfo(STI); + + return createMaxisSEInstrInfo(STI); +} + +bool MaxisInstrInfo::isZeroImm(const MachineOperand &op) const { + return op.isImm() && op.getImm() == 0; +} + +/// insertNoop - If data hazard condition is found insert the target nop +/// instruction. +// FIXME: This appears to be dead code. +void MaxisInstrInfo:: +insertNoop(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI) const +{ + DebugLoc DL; + BuildMI(MBB, MI, DL, get(Maxis::NOP)); +} + +MachineMemOperand * +MaxisInstrInfo::GetMemOperand(MachineBasicBlock &MBB, int FI, + MachineMemOperand::Flags Flags) const { + MachineFunction &MF = *MBB.getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + unsigned Align = MFI.getObjectAlignment(FI); + + return MF.getMachineMemOperand(MachinePointerInfo::getFixedStack(MF, FI), + Flags, MFI.getObjectSize(FI), Align); +} + +//===----------------------------------------------------------------------===// +// Branch Analysis +//===----------------------------------------------------------------------===// + +void MaxisInstrInfo::AnalyzeCondBr(const MachineInstr *Inst, unsigned Opc, + MachineBasicBlock *&BB, + SmallVectorImpl &Cond) const { + assert(getAnalyzableBrOpc(Opc) && "Not an analyzable branch"); + int NumOp = Inst->getNumExplicitOperands(); + + // for both int and fp branches, the last explicit operand is the + // MBB. + BB = Inst->getOperand(NumOp-1).getMBB(); + Cond.push_back(MachineOperand::CreateImm(Opc)); + + for (int i = 0; i < NumOp-1; i++) + Cond.push_back(Inst->getOperand(i)); +} + +bool MaxisInstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + SmallVector BranchInstrs; + BranchType BT = analyzeBranch(MBB, TBB, FBB, Cond, AllowModify, BranchInstrs); + + return (BT == BT_None) || (BT == BT_Indirect); +} + +void MaxisInstrInfo::BuildCondBr(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + const DebugLoc &DL, + ArrayRef Cond) const { + unsigned Opc = Cond[0].getImm(); + const MCInstrDesc &MCID = get(Opc); + MachineInstrBuilder MIB = BuildMI(&MBB, DL, MCID); + + for (unsigned i = 1; i < Cond.size(); ++i) { + assert((Cond[i].isImm() || Cond[i].isReg()) && + "Cannot copy operand for conditional branch!"); + MIB.add(Cond[i]); + } + MIB.addMBB(TBB); +} + +unsigned MaxisInstrInfo::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(!BytesAdded && "code size not handled"); + + // # of condition operands: + // Unconditional branches: 0 + // Floating point branches: 1 (opc) + // Int BranchZero: 2 (opc, reg) + // Int Branch: 3 (opc, reg0, reg1) + assert((Cond.size() <= 3) && + "# of Maxis branch conditions must be <= 3!"); + + // Two-way Conditional branch. + if (FBB) { + BuildCondBr(MBB, TBB, DL, Cond); + BuildMI(&MBB, DL, get(UncondBrOpc)).addMBB(FBB); + return 2; + } + + // One way branch. + // Unconditional branch. + if (Cond.empty()) + BuildMI(&MBB, DL, get(UncondBrOpc)).addMBB(TBB); + else // Conditional branch. + BuildCondBr(MBB, TBB, DL, Cond); + return 1; +} + +unsigned MaxisInstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "code size not handled"); + + MachineBasicBlock::reverse_iterator I = MBB.rbegin(), REnd = MBB.rend(); + unsigned removed = 0; + + // Up to 2 branches are removed. + // Note that indirect branches are not removed. + while (I != REnd && removed < 2) { + // Skip past debug instructions. + if (I->isDebugValue()) { + ++I; + continue; + } + if (!getAnalyzableBrOpc(I->getOpcode())) + break; + // Remove the branch. + I->eraseFromParent(); + I = MBB.rbegin(); + ++removed; + } + + return removed; +} + +/// reverseBranchCondition - Return the inverse opcode of the +/// specified Branch instruction. +bool MaxisInstrInfo::reverseBranchCondition( + SmallVectorImpl &Cond) const { + assert( (Cond.size() && Cond.size() <= 3) && + "Invalid Maxis branch condition!"); + Cond[0].setImm(getOppositeBranchOpc(Cond[0].getImm())); + return false; +} + +MaxisInstrInfo::BranchType MaxisInstrInfo::analyzeBranch( + MachineBasicBlock &MBB, MachineBasicBlock *&TBB, MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, bool AllowModify, + SmallVectorImpl &BranchInstrs) const { + MachineBasicBlock::reverse_iterator I = MBB.rbegin(), REnd = MBB.rend(); + + // Skip all the debug instructions. + while (I != REnd && I->isDebugValue()) + ++I; + + if (I == REnd || !isUnpredicatedTerminator(*I)) { + // This block ends with no branches (it just falls through to its succ). + // Leave TBB/FBB null. + TBB = FBB = nullptr; + return BT_NoBranch; + } + + MachineInstr *LastInst = &*I; + unsigned LastOpc = LastInst->getOpcode(); + BranchInstrs.push_back(LastInst); + + // Not an analyzable branch (e.g., indirect jump). + if (!getAnalyzableBrOpc(LastOpc)) + return LastInst->isIndirectBranch() ? BT_Indirect : BT_None; + + // Get the second to last instruction in the block. + unsigned SecondLastOpc = 0; + MachineInstr *SecondLastInst = nullptr; + + // Skip past any debug instruction to see if the second last actual + // is a branch. + ++I; + while (I != REnd && I->isDebugValue()) + ++I; + + if (I != REnd) { + SecondLastInst = &*I; + SecondLastOpc = getAnalyzableBrOpc(SecondLastInst->getOpcode()); + + // Not an analyzable branch (must be an indirect jump). + if (isUnpredicatedTerminator(*SecondLastInst) && !SecondLastOpc) + return BT_None; + } + + // If there is only one terminator instruction, process it. + if (!SecondLastOpc) { + // Unconditional branch. + if (LastInst->isUnconditionalBranch()) { + TBB = LastInst->getOperand(0).getMBB(); + return BT_Uncond; + } + + // Conditional branch + AnalyzeCondBr(LastInst, LastOpc, TBB, Cond); + return BT_Cond; + } + + // If we reached here, there are two branches. + // If there are three terminators, we don't know what sort of block this is. + if (++I != REnd && isUnpredicatedTerminator(*I)) + return BT_None; + + BranchInstrs.insert(BranchInstrs.begin(), SecondLastInst); + + // If second to last instruction is an unconditional branch, + // analyze it and remove the last instruction. + if (SecondLastInst->isUnconditionalBranch()) { + // Return if the last instruction cannot be removed. + if (!AllowModify) + return BT_None; + + TBB = SecondLastInst->getOperand(0).getMBB(); + LastInst->eraseFromParent(); + BranchInstrs.pop_back(); + return BT_Uncond; + } + + // Conditional branch followed by an unconditional branch. + // The last one must be unconditional. + if (!LastInst->isUnconditionalBranch()) + return BT_None; + + AnalyzeCondBr(SecondLastInst, SecondLastOpc, TBB, Cond); + FBB = LastInst->getOperand(0).getMBB(); + + return BT_CondUncond; +} + +/// Return the corresponding compact (no delay slot) form of a branch. +unsigned MaxisInstrInfo::getEquivalentCompactForm( + const MachineBasicBlock::iterator I) const { + unsigned Opcode = I->getOpcode(); + bool canUseShortMicroMaxisCTI = false; + + if (Subtarget.inMicroMaxisMode()) { + switch (Opcode) { + case Maxis::BNE: + case Maxis::BNE_MM: + case Maxis::BEQ: + case Maxis::BEQ_MM: + // microMAXIS has NE,EQ branches that do not have delay slots provided one + // of the operands is zero. + if (I->getOperand(1).getReg() == Subtarget.getABI().GetZeroReg()) + canUseShortMicroMaxisCTI = true; + break; + // For microMAXIS the PseudoReturn and PseudoIndirectBranch are always + // expanded to JR_MM, so they can be replaced with JRC16_MM. + case Maxis::JR: + case Maxis::PseudoReturn: + case Maxis::PseudoIndirectBranch: + case Maxis::TAILCALLREG: + canUseShortMicroMaxisCTI = true; + break; + } + } + + // MAXISR6 forbids both operands being the zero register. + if (Subtarget.hasMaxis32r6() && (I->getNumOperands() > 1) && + (I->getOperand(0).isReg() && + (I->getOperand(0).getReg() == Maxis::ZERO || + I->getOperand(0).getReg() == Maxis::ZERO_64)) && + (I->getOperand(1).isReg() && + (I->getOperand(1).getReg() == Maxis::ZERO || + I->getOperand(1).getReg() == Maxis::ZERO_64))) + return 0; + + if (Subtarget.hasMaxis32r6() || canUseShortMicroMaxisCTI) { + switch (Opcode) { + case Maxis::B: + return Maxis::BC; + case Maxis::BAL: + return Maxis::BALC; + case Maxis::BEQ: + case Maxis::BEQ_MM: + if (canUseShortMicroMaxisCTI) + return Maxis::BEQZC_MM; + else if (I->getOperand(0).getReg() == I->getOperand(1).getReg()) + return 0; + return Maxis::BEQC; + case Maxis::BNE: + case Maxis::BNE_MM: + if (canUseShortMicroMaxisCTI) + return Maxis::BNEZC_MM; + else if (I->getOperand(0).getReg() == I->getOperand(1).getReg()) + return 0; + return Maxis::BNEC; + case Maxis::BGE: + if (I->getOperand(0).getReg() == I->getOperand(1).getReg()) + return 0; + return Maxis::BGEC; + case Maxis::BGEU: + if (I->getOperand(0).getReg() == I->getOperand(1).getReg()) + return 0; + return Maxis::BGEUC; + case Maxis::BGEZ: + return Maxis::BGEZC; + case Maxis::BGTZ: + return Maxis::BGTZC; + case Maxis::BLEZ: + return Maxis::BLEZC; + case Maxis::BLT: + if (I->getOperand(0).getReg() == I->getOperand(1).getReg()) + return 0; + return Maxis::BLTC; + case Maxis::BLTU: + if (I->getOperand(0).getReg() == I->getOperand(1).getReg()) + return 0; + return Maxis::BLTUC; + case Maxis::BLTZ: + return Maxis::BLTZC; + case Maxis::BEQ64: + if (I->getOperand(0).getReg() == I->getOperand(1).getReg()) + return 0; + return Maxis::BEQC64; + case Maxis::BNE64: + if (I->getOperand(0).getReg() == I->getOperand(1).getReg()) + return 0; + return Maxis::BNEC64; + case Maxis::BGTZ64: + return Maxis::BGTZC64; + case Maxis::BGEZ64: + return Maxis::BGEZC64; + case Maxis::BLTZ64: + return Maxis::BLTZC64; + case Maxis::BLEZ64: + return Maxis::BLEZC64; + // For MAXISR6, the instruction 'jic' can be used for these cases. Some + // tools will accept 'jrc reg' as an alias for 'jic 0, $reg'. + case Maxis::JR: + case Maxis::PseudoReturn: + case Maxis::PseudoIndirectBranch: + case Maxis::TAILCALLREG: + if (canUseShortMicroMaxisCTI) + return Maxis::JRC16_MM; + return Maxis::JIC; + case Maxis::JALRPseudo: + return Maxis::JIALC; + case Maxis::JR64: + case Maxis::PseudoReturn64: + case Maxis::PseudoIndirectBranch64: + case Maxis::TAILCALLREG64: + return Maxis::JIC64; + case Maxis::JALR64Pseudo: + return Maxis::JIALC64; + default: + return 0; + } + } + + return 0; +} + +/// Predicate for distingushing between control transfer instructions and all +/// other instructions for handling forbidden slots. Consider inline assembly +/// as unsafe as well. +bool MaxisInstrInfo::SafeInForbiddenSlot(const MachineInstr &MI) const { + if (MI.isInlineAsm()) + return false; + + return (MI.getDesc().TSFlags & MaxisII::IsCTI) == 0; +} + +/// Predicate for distingushing instructions that have forbidden slots. +bool MaxisInstrInfo::HasForbiddenSlot(const MachineInstr &MI) const { + return (MI.getDesc().TSFlags & MaxisII::HasForbiddenSlot) != 0; +} + +/// Return the number of bytes of code the specified instruction may be. +unsigned MaxisInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + switch (MI.getOpcode()) { + default: + return MI.getDesc().getSize(); + case TargetOpcode::INLINEASM: { // Inline Asm: Variable size. + const MachineFunction *MF = MI.getParent()->getParent(); + const char *AsmStr = MI.getOperand(0).getSymbolName(); + return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo()); + } + case Maxis::CONSTPOOL_ENTRY: + // If this machine instr is a constant pool entry, its size is recorded as + // operand #2. + return MI.getOperand(2).getImm(); + } +} + +MachineInstrBuilder +MaxisInstrInfo::genInstrWithNewOpc(unsigned NewOpc, + MachineBasicBlock::iterator I) const { + MachineInstrBuilder MIB; + + // Certain branches have two forms: e.g beq $1, $zero, dest vs beqz $1, dest + // Pick the zero form of the branch for readable assembly and for greater + // branch distance in non-microMAXIS mode. + // Additional MAXISR6 does not permit the use of register $zero for compact + // branches. + // FIXME: Certain atomic sequences on maxis64 generate 32bit references to + // Maxis::ZERO, which is incorrect. This test should be updated to use + // Subtarget.getABI().GetZeroReg() when those atomic sequences and others + // are fixed. + int ZeroOperandPosition = -1; + bool BranchWithZeroOperand = false; + if (I->isBranch() && !I->isPseudo()) { + auto TRI = I->getParent()->getParent()->getSubtarget().getRegisterInfo(); + ZeroOperandPosition = I->findRegisterUseOperandIdx(Maxis::ZERO, false, TRI); + BranchWithZeroOperand = ZeroOperandPosition != -1; + } + + if (BranchWithZeroOperand) { + switch (NewOpc) { + case Maxis::BEQC: + NewOpc = Maxis::BEQZC; + break; + case Maxis::BNEC: + NewOpc = Maxis::BNEZC; + break; + case Maxis::BGEC: + NewOpc = Maxis::BGEZC; + break; + case Maxis::BLTC: + NewOpc = Maxis::BLTZC; + break; + case Maxis::BEQC64: + NewOpc = Maxis::BEQZC64; + break; + case Maxis::BNEC64: + NewOpc = Maxis::BNEZC64; + break; + } + } + + MIB = BuildMI(*I->getParent(), I, I->getDebugLoc(), get(NewOpc)); + + // For MAXISR6 JI*C requires an immediate 0 as an operand, JIALC(64) an + // immediate 0 as an operand and requires the removal of it's implicit-def %ra + // implicit operand as copying the implicit operations of the instructio we're + // looking at will give us the correct flags. + if (NewOpc == Maxis::JIC || NewOpc == Maxis::JIALC || NewOpc == Maxis::JIC64 || + NewOpc == Maxis::JIALC64) { + + if (NewOpc == Maxis::JIALC || NewOpc == Maxis::JIALC64) + MIB->RemoveOperand(0); + + for (unsigned J = 0, E = I->getDesc().getNumOperands(); J < E; ++J) { + MIB.add(I->getOperand(J)); + } + + MIB.addImm(0); + + } else { + for (unsigned J = 0, E = I->getDesc().getNumOperands(); J < E; ++J) { + if (BranchWithZeroOperand && (unsigned)ZeroOperandPosition == J) + continue; + + MIB.add(I->getOperand(J)); + } + } + + MIB.copyImplicitOps(*I); + + MIB.setMemRefs(I->memoperands_begin(), I->memoperands_end()); + return MIB; +} + +bool MaxisInstrInfo::findCommutedOpIndices(MachineInstr &MI, unsigned &SrcOpIdx1, + unsigned &SrcOpIdx2) const { + assert(!MI.isBundle() && + "TargetInstrInfo::findCommutedOpIndices() can't handle bundles"); + + const MCInstrDesc &MCID = MI.getDesc(); + if (!MCID.isCommutable()) + return false; + + switch (MI.getOpcode()) { + case Maxis::DPADD_U_H: + case Maxis::DPADD_U_W: + case Maxis::DPADD_U_D: + case Maxis::DPADD_S_H: + case Maxis::DPADD_S_W: + case Maxis::DPADD_S_D: + // The first operand is both input and output, so it should not commute + if (!fixCommutedOpIndices(SrcOpIdx1, SrcOpIdx2, 2, 3)) + return false; + + if (!MI.getOperand(SrcOpIdx1).isReg() || !MI.getOperand(SrcOpIdx2).isReg()) + return false; + return true; + } + return TargetInstrInfo::findCommutedOpIndices(MI, SrcOpIdx1, SrcOpIdx2); +} + +// ins, ext, dext*, dins have the following constraints: +// X <= pos < Y +// X < size <= Y +// X < pos+size <= Y +// +// dinsm and dinsu have the following constraints: +// X <= pos < Y +// X <= size <= Y +// X < pos+size <= Y +// +// The callee of verifyInsExtInstruction however gives the bounds of +// dins[um] like the other (d)ins (d)ext(um) instructions, so that this +// function doesn't have to vary it's behaviour based on the instruction +// being checked. +static bool verifyInsExtInstruction(const MachineInstr &MI, StringRef &ErrInfo, + const int64_t PosLow, const int64_t PosHigh, + const int64_t SizeLow, + const int64_t SizeHigh, + const int64_t BothLow, + const int64_t BothHigh) { + MachineOperand MOPos = MI.getOperand(2); + if (!MOPos.isImm()) { + ErrInfo = "Position is not an immediate!"; + return false; + } + int64_t Pos = MOPos.getImm(); + if (!((PosLow <= Pos) && (Pos < PosHigh))) { + ErrInfo = "Position operand is out of range!"; + return false; + } + + MachineOperand MOSize = MI.getOperand(3); + if (!MOSize.isImm()) { + ErrInfo = "Size operand is not an immediate!"; + return false; + } + int64_t Size = MOSize.getImm(); + if (!((SizeLow < Size) && (Size <= SizeHigh))) { + ErrInfo = "Size operand is out of range!"; + return false; + } + + if (!((BothLow < (Pos + Size)) && ((Pos + Size) <= BothHigh))) { + ErrInfo = "Position + Size is out of range!"; + return false; + } + + return true; +} + +// Perform target specific instruction verification. +bool MaxisInstrInfo::verifyInstruction(const MachineInstr &MI, + StringRef &ErrInfo) const { + // Verify that ins and ext instructions are well formed. + switch (MI.getOpcode()) { + case Maxis::EXT: + case Maxis::EXT_MM: + case Maxis::INS: + case Maxis::INS_MM: + case Maxis::DINS: + return verifyInsExtInstruction(MI, ErrInfo, 0, 32, 0, 32, 0, 32); + case Maxis::DINSM: + // The ISA spec has a subtle difference difference between dinsm and dextm + // in that it says: + // 2 <= size <= 64 for 'dinsm' but 'dextm' has 32 < size <= 64. + // To make the bounds checks similar, the range 1 < size <= 64 is checked + // for 'dinsm'. + return verifyInsExtInstruction(MI, ErrInfo, 0, 32, 1, 64, 32, 64); + case Maxis::DINSU: + // The ISA spec has a subtle difference between dinsu and dextu in that + // the size range of dinsu is specified as 1 <= size <= 32 whereas size + // for dextu is 0 < size <= 32. The range checked for dinsu here is + // 0 < size <= 32, which is equivalent and similar to dextu. + return verifyInsExtInstruction(MI, ErrInfo, 32, 64, 0, 32, 32, 64); + case Maxis::DEXT: + return verifyInsExtInstruction(MI, ErrInfo, 0, 32, 0, 32, 0, 63); + case Maxis::DEXTM: + return verifyInsExtInstruction(MI, ErrInfo, 0, 32, 32, 64, 32, 64); + case Maxis::DEXTU: + return verifyInsExtInstruction(MI, ErrInfo, 32, 64, 0, 32, 32, 64); + default: + return true; + } + + return true; +} + +std::pair +MaxisInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const { + return std::make_pair(TF, 0u); +} + +ArrayRef> +MaxisInstrInfo::getSerializableDirectMachineOperandTargetFlags() const { + using namespace MaxisII; + + static const std::pair Flags[] = { + {MO_GOT, "maxis-got"}, + {MO_GOT_CALL, "maxis-got-call"}, + {MO_GPREL, "maxis-gprel"}, + {MO_ABS_HI, "maxis-abs-hi"}, + {MO_ABS_LO, "maxis-abs-lo"}, + {MO_TLSGD, "maxis-tlsgd"}, + {MO_TLSLDM, "maxis-tlsldm"}, + {MO_DTPREL_HI, "maxis-dtprel-hi"}, + {MO_DTPREL_LO, "maxis-dtprel-lo"}, + {MO_GOTTPREL, "maxis-gottprel"}, + {MO_TPREL_HI, "maxis-tprel-hi"}, + {MO_TPREL_LO, "maxis-tprel-lo"}, + {MO_GPOFF_HI, "maxis-gpoff-hi"}, + {MO_GPOFF_LO, "maxis-gpoff-lo"}, + {MO_GOT_DISP, "maxis-got-disp"}, + {MO_GOT_PAGE, "maxis-got-page"}, + {MO_GOT_OFST, "maxis-got-ofst"}, + {MO_HIGHER, "maxis-higher"}, + {MO_HIGHEST, "maxis-highest"}, + {MO_GOT_HI16, "maxis-got-hi16"}, + {MO_GOT_LO16, "maxis-got-lo16"}, + {MO_CALL_HI16, "maxis-call-hi16"}, + {MO_CALL_LO16, "maxis-call-lo16"} + }; + return makeArrayRef(Flags); +} diff --git a/lib/Target/Maxis/MaxisInstrInfo.h b/lib/Target/Maxis/MaxisInstrInfo.h new file mode 100644 index 00000000..c13251f8 --- /dev/null +++ b/lib/Target/Maxis/MaxisInstrInfo.h @@ -0,0 +1,184 @@ +//===- MaxisInstrInfo.h - Maxis Instruction Information -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis implementation of the TargetInstrInfo class. +// +// FIXME: We need to override TargetInstrInfo::getInlineAsmLength method in +// order for MaxisLongBranch pass to work correctly when the code has inline +// assembly. The returned value doesn't have to be the asm instruction's exact +// size in bytes; MaxisLongBranch only expects it to be the correct upper bound. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISINSTRINFO_H +#define LLVM_LIB_TARGET_MAXIS_MAXISINSTRINFO_H + +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "Maxis.h" +#include "MaxisRegisterInfo.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include + +#define GET_INSTRINFO_HEADER +#include "MaxisGenInstrInfo.inc" + +namespace llvm { + +class MachineInstr; +class MachineOperand; +class MaxisSubtarget; +class TargetRegisterClass; +class TargetRegisterInfo; + +class MaxisInstrInfo : public MaxisGenInstrInfo { + virtual void anchor(); + +protected: + const MaxisSubtarget &Subtarget; + unsigned UncondBrOpc; + +public: + enum BranchType { + BT_None, // Couldn't analyze branch. + BT_NoBranch, // No branches found. + BT_Uncond, // One unconditional branch. + BT_Cond, // One conditional branch. + BT_CondUncond, // A conditional branch followed by an unconditional branch. + BT_Indirect // One indirct branch. + }; + + explicit MaxisInstrInfo(const MaxisSubtarget &STI, unsigned UncondBrOpc); + + static const MaxisInstrInfo *create(MaxisSubtarget &STI); + + /// Branch Analysis + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const override; + + 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; + + bool + reverseBranchCondition(SmallVectorImpl &Cond) const override; + + BranchType analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify, + SmallVectorImpl &BranchInstrs) const; + + /// Determine the opcode of a non-delay slot form for a branch if one exists. + unsigned getEquivalentCompactForm(const MachineBasicBlock::iterator I) const; + + /// Predicate to determine if an instruction can go in a forbidden slot. + bool SafeInForbiddenSlot(const MachineInstr &MI) const; + + /// Predicate to determine if an instruction has a forbidden slot. + bool HasForbiddenSlot(const MachineInstr &MI) const; + + /// Insert nop instruction when hazard condition is found + void insertNoop(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const override; + + /// getRegisterInfo - TargetInstrInfo is a superset of MRegister info. As + /// such, whenever a client has an instance of instruction info, it should + /// always be able to get register info as well (through this method). + virtual const MaxisRegisterInfo &getRegisterInfo() const = 0; + + virtual unsigned getOppositeBranchOpc(unsigned Opc) const = 0; + + /// Return the number of bytes of code the specified instruction may be. + unsigned getInstSizeInBytes(const MachineInstr &MI) const override; + + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned SrcReg, bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override { + storeRegToStack(MBB, MBBI, SrcReg, isKill, FrameIndex, RC, TRI, 0); + } + + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + unsigned DestReg, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override { + loadRegFromStack(MBB, MBBI, DestReg, FrameIndex, RC, TRI, 0); + } + + virtual void storeRegToStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + unsigned SrcReg, bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + int64_t Offset) const = 0; + + virtual void loadRegFromStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + unsigned DestReg, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + int64_t Offset) const = 0; + + virtual void adjustStackPtr(unsigned SP, int64_t Amount, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const = 0; + + /// Create an instruction which has the same operands and memory operands + /// as MI but has a new opcode. + MachineInstrBuilder genInstrWithNewOpc(unsigned NewOpc, + MachineBasicBlock::iterator I) const; + + bool findCommutedOpIndices(MachineInstr &MI, unsigned &SrcOpIdx1, + unsigned &SrcOpIdx2) const override; + + /// Perform target specific instruction verification. + bool verifyInstruction(const MachineInstr &MI, + StringRef &ErrInfo) const override; + + std::pair + decomposeMachineOperandsTargetFlags(unsigned TF) const override; + + ArrayRef> + getSerializableDirectMachineOperandTargetFlags() const override; + +protected: + bool isZeroImm(const MachineOperand &op) const; + + MachineMemOperand *GetMemOperand(MachineBasicBlock &MBB, int FI, + MachineMemOperand::Flags Flags) const; + +private: + virtual unsigned getAnalyzableBrOpc(unsigned Opc) const = 0; + + void AnalyzeCondBr(const MachineInstr *Inst, unsigned Opc, + MachineBasicBlock *&BB, + SmallVectorImpl &Cond) const; + + void BuildCondBr(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + const DebugLoc &DL, ArrayRef Cond) const; +}; + +/// Create MaxisInstrInfo objects. +const MaxisInstrInfo *createMaxis16InstrInfo(const MaxisSubtarget &STI); +const MaxisInstrInfo *createMaxisSEInstrInfo(const MaxisSubtarget &STI); + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISINSTRINFO_H diff --git a/lib/Target/Maxis/MaxisInstrInfo.td b/lib/Target/Maxis/MaxisInstrInfo.td new file mode 100644 index 00000000..9d496729 --- /dev/null +++ b/lib/Target/Maxis/MaxisInstrInfo.td @@ -0,0 +1,3014 @@ +//===- MaxisInstrInfo.td - Target Description for Maxis Target -*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + + +//===----------------------------------------------------------------------===// +// Maxis profiles and nodes +//===----------------------------------------------------------------------===// + +def SDT_MaxisJmpLink : SDTypeProfile<0, 1, [SDTCisVT<0, iPTR>]>; +def SDT_MaxisCMov : SDTypeProfile<1, 4, [SDTCisSameAs<0, 1>, + SDTCisSameAs<1, 2>, + SDTCisSameAs<3, 4>, + SDTCisInt<4>]>; +def SDT_MaxisCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_MaxisCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; +def SDT_MFLOHI : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisVT<1, untyped>]>; +def SDT_MTLOHI : SDTypeProfile<1, 2, [SDTCisVT<0, untyped>, + SDTCisInt<1>, SDTCisSameAs<1, 2>]>; +def SDT_MaxisMultDiv : SDTypeProfile<1, 2, [SDTCisVT<0, untyped>, SDTCisInt<1>, + SDTCisSameAs<1, 2>]>; +def SDT_MaxisMAddMSub : SDTypeProfile<1, 3, + [SDTCisVT<0, untyped>, SDTCisSameAs<0, 3>, + SDTCisVT<1, i32>, SDTCisSameAs<1, 2>]>; +def SDT_MaxisDivRem16 : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>]>; + +def SDT_MaxisThreadPointer : SDTypeProfile<1, 0, [SDTCisPtrTy<0>]>; + +def SDT_Sync : SDTypeProfile<0, 1, [SDTCisVT<0, i32>]>; + +def SDT_Ext : SDTypeProfile<1, 3, [SDTCisInt<0>, SDTCisSameAs<0, 1>, + SDTCisVT<2, i32>, SDTCisSameAs<2, 3>]>; +def SDT_Ins : SDTypeProfile<1, 4, [SDTCisInt<0>, SDTCisSameAs<0, 1>, + SDTCisVT<2, i32>, SDTCisSameAs<2, 3>, + SDTCisSameAs<0, 4>]>; + +def SDTMaxisLoadLR : SDTypeProfile<1, 2, + [SDTCisInt<0>, SDTCisPtrTy<1>, + SDTCisSameAs<0, 2>]>; + +// Call +def MaxisJmpLink : SDNode<"MaxisISD::JmpLink",SDT_MaxisJmpLink, + [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, + SDNPVariadic]>; + +// Tail call +def MaxisTailCall : SDNode<"MaxisISD::TailCall", SDT_MaxisJmpLink, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +// Hi and Lo nodes are used to handle global addresses. Used on +// MaxisISelLowering to lower stuff like GlobalAddress, ExternalSymbol +// static model. (nothing to do with Maxis Registers Hi and Lo) + +// Hi is the odd node out, on MAXIS64 it can expand to either daddiu when +// using static relocations with 64 bit symbols, or lui when using 32 bit +// symbols. +def MaxisHigher : SDNode<"MaxisISD::Higher", SDTIntUnaryOp>; +def MaxisHighest : SDNode<"MaxisISD::Highest", SDTIntUnaryOp>; +def MaxisHi : SDNode<"MaxisISD::Hi", SDTIntUnaryOp>; +def MaxisLo : SDNode<"MaxisISD::Lo", SDTIntUnaryOp>; + +def MaxisGPRel : SDNode<"MaxisISD::GPRel", SDTIntUnaryOp>; + +// Hi node for accessing the GOT. +def MaxisGotHi : SDNode<"MaxisISD::GotHi", SDTIntUnaryOp>; + +// TlsGd node is used to handle General Dynamic TLS +def MaxisTlsGd : SDNode<"MaxisISD::TlsGd", SDTIntUnaryOp>; + +// TprelHi and TprelLo nodes are used to handle Local Exec TLS +def MaxisTprelHi : SDNode<"MaxisISD::TprelHi", SDTIntUnaryOp>; +def MaxisTprelLo : SDNode<"MaxisISD::TprelLo", SDTIntUnaryOp>; + +// Thread pointer +def MaxisThreadPointer: SDNode<"MaxisISD::ThreadPointer", SDT_MaxisThreadPointer>; + +// Return +def MaxisRet : SDNode<"MaxisISD::Ret", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +def MaxisERet : SDNode<"MaxisISD::ERet", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPSideEffect]>; + +// These are target-independent nodes, but have target-specific formats. +def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_MaxisCallSeqStart, + [SDNPHasChain, SDNPSideEffect, SDNPOutGlue]>; +def callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_MaxisCallSeqEnd, + [SDNPHasChain, SDNPSideEffect, + SDNPOptInGlue, SDNPOutGlue]>; + +// Nodes used to extract LO/HI registers. +def MaxisMFHI : SDNode<"MaxisISD::MFHI", SDT_MFLOHI>; +def MaxisMFLO : SDNode<"MaxisISD::MFLO", SDT_MFLOHI>; + +// Node used to insert 32-bit integers to LOHI register pair. +def MaxisMTLOHI : SDNode<"MaxisISD::MTLOHI", SDT_MTLOHI>; + +// Mult nodes. +def MaxisMult : SDNode<"MaxisISD::Mult", SDT_MaxisMultDiv>; +def MaxisMultu : SDNode<"MaxisISD::Multu", SDT_MaxisMultDiv>; + +// MAdd*/MSub* nodes +def MaxisMAdd : SDNode<"MaxisISD::MAdd", SDT_MaxisMAddMSub>; +def MaxisMAddu : SDNode<"MaxisISD::MAddu", SDT_MaxisMAddMSub>; +def MaxisMSub : SDNode<"MaxisISD::MSub", SDT_MaxisMAddMSub>; +def MaxisMSubu : SDNode<"MaxisISD::MSubu", SDT_MaxisMAddMSub>; + +// DivRem(u) nodes +def MaxisDivRem : SDNode<"MaxisISD::DivRem", SDT_MaxisMultDiv>; +def MaxisDivRemU : SDNode<"MaxisISD::DivRemU", SDT_MaxisMultDiv>; +def MaxisDivRem16 : SDNode<"MaxisISD::DivRem16", SDT_MaxisDivRem16, + [SDNPOutGlue]>; +def MaxisDivRemU16 : SDNode<"MaxisISD::DivRemU16", SDT_MaxisDivRem16, + [SDNPOutGlue]>; + +// Target constant nodes that are not part of any isel patterns and remain +// unchanged can cause instructions with illegal operands to be emitted. +// Wrapper node patterns give the instruction selector a chance to replace +// target constant nodes that would otherwise remain unchanged with ADDiu +// nodes. Without these wrapper node patterns, the following conditional move +// instruction is emitted when function cmov2 in test/CodeGen/Maxis/cmov.ll is +// compiled: +// movn %got(d)($gp), %got(c)($gp), $4 +// This instruction is illegal since movn can take only register operands. + +def MaxisWrapper : SDNode<"MaxisISD::Wrapper", SDTIntBinOp>; + +def MaxisSync : SDNode<"MaxisISD::Sync", SDT_Sync, [SDNPHasChain,SDNPSideEffect]>; + +def MaxisExt : SDNode<"MaxisISD::Ext", SDT_Ext>; +def MaxisIns : SDNode<"MaxisISD::Ins", SDT_Ins>; +def MaxisCIns : SDNode<"MaxisISD::CIns", SDT_Ext>; + +def MaxisLWL : SDNode<"MaxisISD::LWL", SDTMaxisLoadLR, + [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; +def MaxisLWR : SDNode<"MaxisISD::LWR", SDTMaxisLoadLR, + [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; +def MaxisSWL : SDNode<"MaxisISD::SWL", SDTStore, + [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; +def MaxisSWR : SDNode<"MaxisISD::SWR", SDTStore, + [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; +def MaxisLDL : SDNode<"MaxisISD::LDL", SDTMaxisLoadLR, + [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; +def MaxisLDR : SDNode<"MaxisISD::LDR", SDTMaxisLoadLR, + [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; +def MaxisSDL : SDNode<"MaxisISD::SDL", SDTStore, + [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; +def MaxisSDR : SDNode<"MaxisISD::SDR", SDTStore, + [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; + +//===----------------------------------------------------------------------===// +// Maxis Instruction Predicate Definitions. +//===----------------------------------------------------------------------===// +def HasMaxis2 : Predicate<"Subtarget->hasMaxis2()">, + AssemblerPredicate<"FeatureMaxis2">; +def HasMaxis3_32 : Predicate<"Subtarget->hasMaxis3_32()">, + AssemblerPredicate<"FeatureMaxis3_32">; +def HasMaxis3_32r2 : Predicate<"Subtarget->hasMaxis3_32r2()">, + AssemblerPredicate<"FeatureMaxis3_32r2">; +def HasMaxis3 : Predicate<"Subtarget->hasMaxis3()">, + AssemblerPredicate<"FeatureMaxis3">; +def NotMaxis3 : Predicate<"!Subtarget->hasMaxis3()">, + AssemblerPredicate<"!FeatureMaxis3">; +def HasMaxis4_32 : Predicate<"Subtarget->hasMaxis4_32()">, + AssemblerPredicate<"FeatureMaxis4_32">; +def NotMaxis4_32 : Predicate<"!Subtarget->hasMaxis4_32()">, + AssemblerPredicate<"!FeatureMaxis4_32">; +def HasMaxis4_32r2 : Predicate<"Subtarget->hasMaxis4_32r2()">, + AssemblerPredicate<"FeatureMaxis4_32r2">; +def HasMaxis5_32r2 : Predicate<"Subtarget->hasMaxis5_32r2()">, + AssemblerPredicate<"FeatureMaxis5_32r2">; +def HasMaxis32 : Predicate<"Subtarget->hasMaxis32()">, + AssemblerPredicate<"FeatureMaxis32">; +def HasMaxis32r2 : Predicate<"Subtarget->hasMaxis32r2()">, + AssemblerPredicate<"FeatureMaxis32r2">; +def HasMaxis32r5 : Predicate<"Subtarget->hasMaxis32r5()">, + AssemblerPredicate<"FeatureMaxis32r5">; +def HasMaxis32r6 : Predicate<"Subtarget->hasMaxis32r6()">, + AssemblerPredicate<"FeatureMaxis32r6">; +def NotMaxis32r6 : Predicate<"!Subtarget->hasMaxis32r6()">, + AssemblerPredicate<"!FeatureMaxis32r6">; +def IsGP64bit : Predicate<"Subtarget->isGP64bit()">, + AssemblerPredicate<"FeatureGP64Bit">; +def IsGP32bit : Predicate<"!Subtarget->isGP64bit()">, + AssemblerPredicate<"!FeatureGP64Bit">; +def IsPTR64bit : Predicate<"Subtarget->isABI_N64()">, + AssemblerPredicate<"FeaturePTR64Bit">; +def IsPTR32bit : Predicate<"!Subtarget->isABI_N64()">, + AssemblerPredicate<"!FeaturePTR64Bit">; +def HasMaxis64 : Predicate<"Subtarget->hasMaxis64()">, + AssemblerPredicate<"FeatureMaxis64">; +def NotMaxis64 : Predicate<"!Subtarget->hasMaxis64()">, + AssemblerPredicate<"!FeatureMaxis64">; +def HasMaxis64r2 : Predicate<"Subtarget->hasMaxis64r2()">, + AssemblerPredicate<"FeatureMaxis64r2">; +def HasMaxis64r6 : Predicate<"Subtarget->hasMaxis64r6()">, + AssemblerPredicate<"FeatureMaxis64r6">; +def NotMaxis64r6 : Predicate<"!Subtarget->hasMaxis64r6()">, + AssemblerPredicate<"!FeatureMaxis64r6">; +def HasMicroMaxis32r6 : Predicate<"Subtarget->inMicroMaxis32r6Mode()">, + AssemblerPredicate<"FeatureMicroMaxis,FeatureMaxis32r6">; +def InMaxis16Mode : Predicate<"Subtarget->inMaxis16Mode()">, + AssemblerPredicate<"FeatureMaxis16">; +def NotInMaxis16Mode : Predicate<"!Subtarget->inMaxis16Mode()">, + AssemblerPredicate<"!FeatureMaxis16">; +def HasCnMaxis : Predicate<"Subtarget->hasCnMaxis()">, + AssemblerPredicate<"FeatureCnMaxis">; +def NotCnMaxis : Predicate<"!Subtarget->hasCnMaxis()">, + AssemblerPredicate<"!FeatureCnMaxis">; +def IsSym32 : Predicate<"Subtarget->HasSym32()">, + AssemblerPredicate<"FeatureSym32">; +def IsSym64 : Predicate<"!Subtarget->HasSym32()">, + AssemblerPredicate<"!FeatureSym32">; +def IsN64 : Predicate<"Subtarget->isABI_N64()">; +def IsNotN64 : Predicate<"!Subtarget->isABI_N64()">; +def RelocNotPIC : Predicate<"!TM.isPositionIndependent()">; +def RelocPIC : Predicate<"TM.isPositionIndependent()">; +def NoNaNsFPMath : Predicate<"TM.Options.NoNaNsFPMath">; +def HasStdEnc : Predicate<"Subtarget->hasStandardEncoding()">, + AssemblerPredicate<"!FeatureMaxis16">; +def NotDSP : Predicate<"!Subtarget->hasDSP()">; +def InMicroMaxis : Predicate<"Subtarget->inMicroMaxisMode()">, + AssemblerPredicate<"FeatureMicroMaxis">; +def NotInMicroMaxis : Predicate<"!Subtarget->inMicroMaxisMode()">, + AssemblerPredicate<"!FeatureMicroMaxis">; +def IsLE : Predicate<"Subtarget->isLittle()">; +def IsBE : Predicate<"!Subtarget->isLittle()">; +def IsNotNaCl : Predicate<"!Subtarget->isTargetNaCl()">; +def UseTCCInDIV : AssemblerPredicate<"FeatureUseTCCInDIV">; +def HasEVA : Predicate<"Subtarget->hasEVA()">, + AssemblerPredicate<"FeatureEVA,FeatureMaxis32r2">; +def HasMSA : Predicate<"Subtarget->hasMSA()">, + AssemblerPredicate<"FeatureMSA">; +def HasMadd4 : Predicate<"!Subtarget->disableMadd4()">, + AssemblerPredicate<"!FeatureMadd4">; +def HasMT : Predicate<"Subtarget->hasMT()">, + AssemblerPredicate<"FeatureMT">; + +//===----------------------------------------------------------------------===// +// Maxis GPR size adjectives. +// They are mutually exclusive. +//===----------------------------------------------------------------------===// + +class GPR_32 { list GPRPredicates = [IsGP32bit]; } +class GPR_64 { list GPRPredicates = [IsGP64bit]; } + +class PTR_32 { list PTRPredicates = [IsPTR32bit]; } +class PTR_64 { list PTRPredicates = [IsPTR64bit]; } + +//===----------------------------------------------------------------------===// +// Maxis Symbol size adjectives. +// They are mutally exculsive. +//===----------------------------------------------------------------------===// + +class SYM_32 { list SYMPredicates = [IsSym32]; } +class SYM_64 { list SYMPredicates = [IsSym64]; } + +//===----------------------------------------------------------------------===// +// Maxis ISA/ASE membership and instruction group membership adjectives. +// They are mutually exclusive. +//===----------------------------------------------------------------------===// + +// FIXME: I'd prefer to use additive predicates to build the instruction sets +// but we are short on assembler feature bits at the moment. Using a +// subtractive predicate will hopefully keep us under the 32 predicate +// limit long enough to develop an alternative way to handle P1||P2 +// predicates. +class ISA_MAXIS1_NOT_MAXIS3 { + list InsnPredicates = [NotMaxis3]; +} +class ISA_MAXIS1_NOT_4_32 { + list InsnPredicates = [NotMaxis4_32]; +} +class ISA_MAXIS1_NOT_32R6_64R6 { + list InsnPredicates = [NotMaxis32r6, NotMaxis64r6]; +} +class ISA_MAXIS2 { list InsnPredicates = [HasMaxis2]; } +class ISA_MAXIS2_NOT_32R6_64R6 { + list InsnPredicates = [HasMaxis2, NotMaxis32r6, NotMaxis64r6]; +} +class ISA_MAXIS3 { list InsnPredicates = [HasMaxis3]; } +class ISA_MAXIS3_NOT_32R6_64R6 { + list InsnPredicates = [HasMaxis3, NotMaxis32r6, NotMaxis64r6]; +} +class ISA_MAXIS32 { list InsnPredicates = [HasMaxis32]; } +class ISA_MAXIS32_NOT_32R6_64R6 { + list InsnPredicates = [HasMaxis32, NotMaxis32r6, NotMaxis64r6]; +} +class ISA_MAXIS32R2 { list InsnPredicates = [HasMaxis32r2]; } +class ISA_MAXIS32R2_NOT_32R6_64R6 { + list InsnPredicates = [HasMaxis32r2, NotMaxis32r6, NotMaxis64r6]; +} +class ISA_MAXIS32R5 { list InsnPredicates = [HasMaxis32r5]; } +class ISA_MAXIS64 { list InsnPredicates = [HasMaxis64]; } +class ISA_MAXIS64_NOT_64R6 { + list InsnPredicates = [HasMaxis64, NotMaxis64r6]; +} +class ISA_MAXIS64R2 { list InsnPredicates = [HasMaxis64r2]; } +class ISA_MAXIS32R6 { list InsnPredicates = [HasMaxis32r6]; } +class ISA_MAXIS64R6 { list InsnPredicates = [HasMaxis64r6]; } +class ISA_MICROMAXIS { list InsnPredicates = [InMicroMaxis]; } +class ISA_MICROMAXIS32R6 { + list InsnPredicates = [HasMicroMaxis32r6]; +} +class ISA_MICROMAXIS32_NOT_MAXIS32R6 { + list InsnPredicates = [InMicroMaxis, NotMaxis32r6]; +} + +class INSN_EVA { list InsnPredicates = [HasEVA]; } +class INSN_EVA_NOT_32R6_64R6 { + list InsnPredicates = [NotMaxis32r6, NotMaxis64r6, HasEVA]; +} + +// The portions of MAXIS-III that were also added to MAXIS32 +class INSN_MAXIS3_32 { list InsnPredicates = [HasMaxis3_32]; } + +// The portions of MAXIS-III that were also added to MAXIS32 but were removed in +// MAXIS32r6 and MAXIS64r6. +class INSN_MAXIS3_32_NOT_32R6_64R6 { + list InsnPredicates = [HasMaxis3_32, NotMaxis32r6, NotMaxis64r6]; +} + +// The portions of MAXIS-III that were also added to MAXIS32 +class INSN_MAXIS3_32R2 { list InsnPredicates = [HasMaxis3_32r2]; } + +// The portions of MAXIS-IV that were also added to MAXIS32. +class INSN_MAXIS4_32 { list InsnPredicates = [HasMaxis4_32]; } + +// The portions of MAXIS-IV that were also added to MAXIS32 but were removed in +// MAXIS32r6 and MAXIS64r6. +class INSN_MAXIS4_32_NOT_32R6_64R6 { + list InsnPredicates = [HasMaxis4_32, NotMaxis32r6, NotMaxis64r6]; +} + +// The portions of MAXIS-IV that were also added to MAXIS32r2 but were removed in +// MAXIS32r6 and MAXIS64r6. +class INSN_MAXIS4_32R2_NOT_32R6_64R6 { + list InsnPredicates = [HasMaxis4_32r2, NotMaxis32r6, NotMaxis64r6]; +} + +// The portions of MAXIS-IV that were also added to MAXIS32r2. +class INSN_MAXIS4_32R2 { + list InsnPredicates = [HasMaxis4_32r2]; +} + +// The portions of MAXIS-V that were also added to MAXIS32r2 but were removed in +// MAXIS32r6 and MAXIS64r6. +class INSN_MAXIS5_32R2_NOT_32R6_64R6 { + list InsnPredicates = [HasMaxis5_32r2, NotMaxis32r6, NotMaxis64r6]; +} + +class ASE_CNMAXIS { + list InsnPredicates = [HasCnMaxis]; +} + +class NOT_ASE_CNMAXIS { + list InsnPredicates = [NotCnMaxis]; +} + +class ASE_MAXIS64_CNMAXIS { + list InsnPredicates = [HasMaxis64, HasCnMaxis]; +} + +class ASE_MSA { + list InsnPredicates = [HasMSA]; +} + +class ASE_MSA_NOT_MSA64 { + list InsnPredicates = [HasMSA, NotMaxis64]; +} + +class ASE_MSA64 { + list InsnPredicates = [HasMSA, HasMaxis64]; +} + +class ASE_MT { + list InsnPredicates = [HasMT]; +} + +// Class used for separating microMAXISr6 and microMAXIS (r3) instruction. +// It can be used only on instructions that doesn't inherit PredicateControl. +class ISA_MICROMAXIS_NOT_32R6 : PredicateControl { + let InsnPredicates = [InMicroMaxis, NotMaxis32r6]; +} + +class ASE_NOT_DSP { + list InsnPredicates = [NotDSP]; +} + +class MADD4 { + list AdditionalPredicates = [HasMadd4]; +} + +// Classses used for separating expansions that differ based on the ABI in +// use. +class ABI_N64 { + list AdditionalPredicates = [IsN64]; +} + +class ABI_NOT_N64 { + list AdditionalPredicates = [IsNotN64]; +} + +//===----------------------------------------------------------------------===// + +class MaxisPat : Pat, PredicateControl { + let EncodingPredicates = [HasStdEnc]; +} + +class MaxisInstAlias : + InstAlias, PredicateControl; + +class IsCommutable { + bit isCommutable = 1; +} + +class IsBranch { + bit isBranch = 1; + bit isCTI = 1; +} + +class IsReturn { + bit isReturn = 1; + bit isCTI = 1; +} + +class IsCall { + bit isCall = 1; + bit isCTI = 1; +} + +class IsTailCall { + bit isCall = 1; + bit isTerminator = 1; + bit isReturn = 1; + bit isBarrier = 1; + bit hasExtraSrcRegAllocReq = 1; + bit isCodeGenOnly = 1; + bit isCTI = 1; +} + +class IsAsCheapAsAMove { + bit isAsCheapAsAMove = 1; +} + +class NeverHasSideEffects { + bit hasSideEffects = 0; +} + +//===----------------------------------------------------------------------===// +// Instruction format superclass +//===----------------------------------------------------------------------===// + +include "MaxisInstrFormats.td" + +//===----------------------------------------------------------------------===// +// Maxis Operand, Complex Patterns and Transformations Definitions. +//===----------------------------------------------------------------------===// + +class ConstantSImmAsmOperandClass Supers = [], + int Offset = 0> : AsmOperandClass { + let Name = "ConstantSImm" # Bits # "_" # Offset; + let RenderMethod = "addConstantSImmOperands<" # Bits # ", " # Offset # ">"; + let PredicateMethod = "isConstantSImm<" # Bits # ", " # Offset # ">"; + let SuperClasses = Supers; + let DiagnosticType = "SImm" # Bits # "_" # Offset; +} + +class SimmLslAsmOperandClass Supers = [], + int Shift = 0> : AsmOperandClass { + let Name = "Simm" # Bits # "_Lsl" # Shift; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isScaledSImm<" # Bits # ", " # Shift # ">"; + let SuperClasses = Supers; + let DiagnosticType = "SImm" # Bits # "_Lsl" # Shift; +} + +class ConstantUImmAsmOperandClass Supers = [], + int Offset = 0> : AsmOperandClass { + let Name = "ConstantUImm" # Bits # "_" # Offset; + let RenderMethod = "addConstantUImmOperands<" # Bits # ", " # Offset # ">"; + let PredicateMethod = "isConstantUImm<" # Bits # ", " # Offset # ">"; + let SuperClasses = Supers; + let DiagnosticType = "UImm" # Bits # "_" # Offset; +} + +class ConstantUImmRangeAsmOperandClass Supers = []> + : AsmOperandClass { + let Name = "ConstantUImmRange" # Bottom # "_" # Top; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isConstantUImmRange<" # Bottom # ", " # Top # ">"; + let SuperClasses = Supers; + let DiagnosticType = "UImmRange" # Bottom # "_" # Top; +} + +class SImmAsmOperandClass Supers = []> + : AsmOperandClass { + let Name = "SImm" # Bits; + let RenderMethod = "addSImmOperands<" # Bits # ">"; + let PredicateMethod = "isSImm<" # Bits # ">"; + let SuperClasses = Supers; + let DiagnosticType = "SImm" # Bits; +} + +class UImmAsmOperandClass Supers = []> + : AsmOperandClass { + let Name = "UImm" # Bits; + let RenderMethod = "addUImmOperands<" # Bits # ">"; + let PredicateMethod = "isUImm<" # Bits # ">"; + let SuperClasses = Supers; + let DiagnosticType = "UImm" # Bits; +} + +// Generic case - only to support certain assembly pseudo instructions. +class UImmAnyAsmOperandClass Supers = []> + : AsmOperandClass { + let Name = "ImmAny"; + let RenderMethod = "addConstantUImmOperands<32>"; + let PredicateMethod = "isSImm<" # Bits # ">"; + let SuperClasses = Supers; + let DiagnosticType = "ImmAny"; +} + +// AsmOperandClasses require a strict ordering which is difficult to manage +// as a hierarchy. Instead, we use a linear ordering and impose an order that +// is in some places arbitrary. +// +// Here the rules that are in use: +// * Wider immediates are a superset of narrower immediates: +// uimm4 < uimm5 < uimm6 +// * For the same bit-width, unsigned immediates are a superset of signed +// immediates:: +// simm4 < uimm4 < simm5 < uimm5 +// * For the same upper-bound, signed immediates are a superset of unsigned +// immediates: +// uimm3 < simm4 < uimm4 < simm4 +// * Modified immediates are a superset of ordinary immediates: +// uimm5 < uimm5_plus1 (1..32) < uimm5_plus32 (32..63) < uimm6 +// The term 'superset' starts to break down here since the uimm5_plus* classes +// are not true supersets of uimm5 (but they are still subsets of uimm6). +// * 'Relaxed' immediates are supersets of the corresponding unsigned immediate. +// uimm16 < uimm16_relaxed +// * The codeGen pattern type is arbitrarily ordered. +// uimm5 < uimm5_64, and uimm5 < vsplat_uimm5 +// This is entirely arbitrary. We need an ordering and what we pick is +// unimportant since only one is possible for a given mnemonic. + +def UImm32CoercedAsmOperandClass : UImmAnyAsmOperandClass<33, []> { + let Name = "UImm32_Coerced"; + let DiagnosticType = "UImm32_Coerced"; +} +def SImm32RelaxedAsmOperandClass + : SImmAsmOperandClass<32, [UImm32CoercedAsmOperandClass]> { + let Name = "SImm32_Relaxed"; + let PredicateMethod = "isAnyImm<33>"; + let DiagnosticType = "SImm32_Relaxed"; +} +def SImm32AsmOperandClass + : SImmAsmOperandClass<32, [SImm32RelaxedAsmOperandClass]>; +def ConstantUImm26AsmOperandClass + : ConstantUImmAsmOperandClass<26, [SImm32AsmOperandClass]>; +def ConstantUImm20AsmOperandClass + : ConstantUImmAsmOperandClass<20, [ConstantUImm26AsmOperandClass]>; +def ConstantSImm19Lsl2AsmOperandClass : AsmOperandClass { + let Name = "SImm19Lsl2"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isScaledSImm<19, 2>"; + let SuperClasses = [ConstantUImm20AsmOperandClass]; + let DiagnosticType = "SImm19_Lsl2"; +} +def UImm16RelaxedAsmOperandClass + : UImmAsmOperandClass<16, [ConstantUImm20AsmOperandClass]> { + let Name = "UImm16_Relaxed"; + let PredicateMethod = "isAnyImm<16>"; + let DiagnosticType = "UImm16_Relaxed"; +} +// Similar to the relaxed classes which take an SImm and render it as +// an UImm, this takes a UImm and renders it as an SImm. +def UImm16AltRelaxedAsmOperandClass + : SImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]> { + let Name = "UImm16_AltRelaxed"; + let PredicateMethod = "isUImm<16>"; + let DiagnosticType = "UImm16_AltRelaxed"; +} +// FIXME: One of these should probably have UImm16AsmOperandClass as the +// superclass instead of UImm16RelaxedasmOPerandClass. +def UImm16AsmOperandClass + : UImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]>; +def SImm16RelaxedAsmOperandClass + : SImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]> { + let Name = "SImm16_Relaxed"; + let PredicateMethod = "isAnyImm<16>"; + let DiagnosticType = "SImm16_Relaxed"; +} +def SImm16AsmOperandClass + : SImmAsmOperandClass<16, [SImm16RelaxedAsmOperandClass]>; +def ConstantSImm10Lsl3AsmOperandClass : AsmOperandClass { + let Name = "SImm10Lsl3"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isScaledSImm<10, 3>"; + let SuperClasses = [SImm16AsmOperandClass]; + let DiagnosticType = "SImm10_Lsl3"; +} +def ConstantSImm10Lsl2AsmOperandClass : AsmOperandClass { + let Name = "SImm10Lsl2"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isScaledSImm<10, 2>"; + let SuperClasses = [ConstantSImm10Lsl3AsmOperandClass]; + let DiagnosticType = "SImm10_Lsl2"; +} +def ConstantSImm11AsmOperandClass + : ConstantSImmAsmOperandClass<11, [ConstantSImm10Lsl2AsmOperandClass]>; +def ConstantSImm10Lsl1AsmOperandClass : AsmOperandClass { + let Name = "SImm10Lsl1"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isScaledSImm<10, 1>"; + let SuperClasses = [ConstantSImm11AsmOperandClass]; + let DiagnosticType = "SImm10_Lsl1"; +} +def ConstantUImm10AsmOperandClass + : ConstantUImmAsmOperandClass<10, [ConstantSImm10Lsl1AsmOperandClass]>; +def ConstantSImm10AsmOperandClass + : ConstantSImmAsmOperandClass<10, [ConstantUImm10AsmOperandClass]>; +def ConstantSImm9AsmOperandClass + : ConstantSImmAsmOperandClass<9, [ConstantSImm10AsmOperandClass]>; +def ConstantSImm7Lsl2AsmOperandClass : AsmOperandClass { + let Name = "SImm7Lsl2"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isScaledSImm<7, 2>"; + let SuperClasses = [ConstantSImm9AsmOperandClass]; + let DiagnosticType = "SImm7_Lsl2"; +} +def ConstantUImm8AsmOperandClass + : ConstantUImmAsmOperandClass<8, [ConstantSImm7Lsl2AsmOperandClass]>; +def ConstantUImm7Sub1AsmOperandClass + : ConstantUImmAsmOperandClass<7, [ConstantUImm8AsmOperandClass], -1> { + // Specify the names since the -1 offset causes invalid identifiers otherwise. + let Name = "UImm7_N1"; + let DiagnosticType = "UImm7_N1"; +} +def ConstantUImm7AsmOperandClass + : ConstantUImmAsmOperandClass<7, [ConstantUImm7Sub1AsmOperandClass]>; +def ConstantUImm6Lsl2AsmOperandClass : AsmOperandClass { + let Name = "UImm6Lsl2"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isScaledUImm<6, 2>"; + let SuperClasses = [ConstantUImm7AsmOperandClass]; + let DiagnosticType = "UImm6_Lsl2"; +} +def ConstantUImm6AsmOperandClass + : ConstantUImmAsmOperandClass<6, [ConstantUImm6Lsl2AsmOperandClass]>; +def ConstantSImm6AsmOperandClass + : ConstantSImmAsmOperandClass<6, [ConstantUImm6AsmOperandClass]>; +def ConstantUImm5Lsl2AsmOperandClass : AsmOperandClass { + let Name = "UImm5Lsl2"; + let RenderMethod = "addImmOperands"; + let PredicateMethod = "isScaledUImm<5, 2>"; + let SuperClasses = [ConstantSImm6AsmOperandClass]; + let DiagnosticType = "UImm5_Lsl2"; +} +def ConstantUImm5_Range2_64AsmOperandClass + : ConstantUImmRangeAsmOperandClass<2, 64, [ConstantUImm5Lsl2AsmOperandClass]>; +def ConstantUImm5Plus33AsmOperandClass + : ConstantUImmAsmOperandClass<5, [ConstantUImm5_Range2_64AsmOperandClass], + 33>; +def ConstantUImm5ReportUImm6AsmOperandClass + : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus33AsmOperandClass]> { + let Name = "ConstantUImm5_0_Report_UImm6"; + let DiagnosticType = "UImm5_0_Report_UImm6"; +} +def ConstantUImm5Plus32AsmOperandClass + : ConstantUImmAsmOperandClass< + 5, [ConstantUImm5ReportUImm6AsmOperandClass], 32>; +def ConstantUImm5Plus32NormalizeAsmOperandClass + : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus32AsmOperandClass], 32> { + let Name = "ConstantUImm5_32_Norm"; + // We must also subtract 32 when we render the operand. + let RenderMethod = "addConstantUImmOperands<5, 32, -32>"; +} +def ConstantUImm5Plus1ReportUImm6AsmOperandClass + : ConstantUImmAsmOperandClass< + 5, [ConstantUImm5Plus32NormalizeAsmOperandClass], 1>{ + let Name = "ConstantUImm5_Plus1_Report_UImm6"; +} +def ConstantUImm5Plus1AsmOperandClass + : ConstantUImmAsmOperandClass< + 5, [ConstantUImm5Plus1ReportUImm6AsmOperandClass], 1>; +def ConstantUImm5AsmOperandClass + : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus1AsmOperandClass]>; +def ConstantSImm5AsmOperandClass + : ConstantSImmAsmOperandClass<5, [ConstantUImm5AsmOperandClass]>; +def ConstantUImm4AsmOperandClass + : ConstantUImmAsmOperandClass<4, [ConstantSImm5AsmOperandClass]>; +def ConstantSImm4AsmOperandClass + : ConstantSImmAsmOperandClass<4, [ConstantUImm4AsmOperandClass]>; +def ConstantUImm3AsmOperandClass + : ConstantUImmAsmOperandClass<3, [ConstantSImm4AsmOperandClass]>; +def ConstantUImm2Plus1AsmOperandClass + : ConstantUImmAsmOperandClass<2, [ConstantUImm3AsmOperandClass], 1>; +def ConstantUImm2AsmOperandClass + : ConstantUImmAsmOperandClass<2, [ConstantUImm3AsmOperandClass]>; +def ConstantUImm1AsmOperandClass + : ConstantUImmAsmOperandClass<1, [ConstantUImm2AsmOperandClass]>; +def ConstantImmzAsmOperandClass : AsmOperandClass { + let Name = "ConstantImmz"; + let RenderMethod = "addConstantUImmOperands<1>"; + let PredicateMethod = "isConstantImmz"; + let SuperClasses = [ConstantUImm1AsmOperandClass]; + let DiagnosticType = "Immz"; +} + +def Simm19Lsl2AsmOperand + : SimmLslAsmOperandClass<19, [], 2>; + +def MaxisJumpTargetAsmOperand : AsmOperandClass { + let Name = "JumpTarget"; + let ParserMethod = "parseJumpTarget"; + let PredicateMethod = "isImm"; + let RenderMethod = "addImmOperands"; +} + +// Instruction operand types +def jmptarget : Operand { + let EncoderMethod = "getJumpTargetOpValue"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} +def brtarget : Operand { + let EncoderMethod = "getBranchTargetOpValue"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTarget"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} +def brtarget1SImm16 : Operand { + let EncoderMethod = "getBranchTargetOpValue1SImm16"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTarget1SImm16"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} +def calltarget : Operand { + let EncoderMethod = "getJumpTargetOpValue"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def imm64: Operand; + +def simm19_lsl2 : Operand { + let EncoderMethod = "getSimm19Lsl2Encoding"; + let DecoderMethod = "DecodeSimm19Lsl2"; + let ParserMatchClass = Simm19Lsl2AsmOperand; +} + +def simm18_lsl3 : Operand { + let EncoderMethod = "getSimm18Lsl3Encoding"; + let DecoderMethod = "DecodeSimm18Lsl3"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +// Zero +def uimmz : Operand { + let PrintMethod = "printUImm<0>"; + let ParserMatchClass = ConstantImmzAsmOperandClass; +} + +// size operand of ins instruction +def uimm_range_2_64 : Operand { + let PrintMethod = "printUImm<6, 2>"; + let EncoderMethod = "getSizeInsEncoding"; + let DecoderMethod = "DecodeInsSize"; + let ParserMatchClass = ConstantUImm5_Range2_64AsmOperandClass; +} + +// Unsigned Operands +foreach I = {1, 2, 3, 4, 5, 6, 7, 8, 10, 20, 26} in + def uimm # I : Operand { + let PrintMethod = "printUImm<" # I # ">"; + let ParserMatchClass = + !cast("ConstantUImm" # I # "AsmOperandClass"); + } + +def uimm2_plus1 : Operand { + let PrintMethod = "printUImm<2, 1>"; + let EncoderMethod = "getUImmWithOffsetEncoding<2, 1>"; + let DecoderMethod = "DecodeUImmWithOffset<2, 1>"; + let ParserMatchClass = ConstantUImm2Plus1AsmOperandClass; +} + +def uimm5_plus1 : Operand { + let PrintMethod = "printUImm<5, 1>"; + let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; + let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; + let ParserMatchClass = ConstantUImm5Plus1AsmOperandClass; +} + +def uimm5_plus1_report_uimm6 : Operand { + let PrintMethod = "printUImm<6, 1>"; + let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; + let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; + let ParserMatchClass = ConstantUImm5Plus1ReportUImm6AsmOperandClass; +} + +def uimm5_plus32 : Operand { + let PrintMethod = "printUImm<5, 32>"; + let ParserMatchClass = ConstantUImm5Plus32AsmOperandClass; +} + +def uimm5_plus33 : Operand { + let PrintMethod = "printUImm<5, 33>"; + let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; + let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; + let ParserMatchClass = ConstantUImm5Plus33AsmOperandClass; +} + +def uimm5_inssize_plus1 : Operand { + let PrintMethod = "printUImm<6>"; + let ParserMatchClass = ConstantUImm5Plus1AsmOperandClass; + let EncoderMethod = "getSizeInsEncoding"; + let DecoderMethod = "DecodeInsSize"; +} + +def uimm5_plus32_normalize : Operand { + let PrintMethod = "printUImm<5>"; + let ParserMatchClass = ConstantUImm5Plus32NormalizeAsmOperandClass; +} + +def uimm5_lsl2 : Operand { + let EncoderMethod = "getUImm5Lsl2Encoding"; + let DecoderMethod = "DecodeUImmWithOffsetAndScale<5, 0, 4>"; + let ParserMatchClass = ConstantUImm5Lsl2AsmOperandClass; +} + +def uimm5_plus32_normalize_64 : Operand { + let PrintMethod = "printUImm<5>"; + let ParserMatchClass = ConstantUImm5Plus32NormalizeAsmOperandClass; +} + +def uimm6_lsl2 : Operand { + let EncoderMethod = "getUImm6Lsl2Encoding"; + let DecoderMethod = "DecodeUImmWithOffsetAndScale<6, 0, 4>"; + let ParserMatchClass = ConstantUImm6Lsl2AsmOperandClass; +} + +foreach I = {16} in + def uimm # I : Operand { + let PrintMethod = "printUImm<" # I # ">"; + let ParserMatchClass = + !cast("UImm" # I # "AsmOperandClass"); + } + +// Like uimm16_64 but coerces simm16 to uimm16. +def uimm16_relaxed : Operand { + let PrintMethod = "printUImm<16>"; + let ParserMatchClass = + !cast("UImm16RelaxedAsmOperandClass"); +} + +foreach I = {5} in + def uimm # I # _64 : Operand { + let PrintMethod = "printUImm<" # I # ">"; + let ParserMatchClass = + !cast("ConstantUImm" # I # "AsmOperandClass"); + } + +foreach I = {16} in + def uimm # I # _64 : Operand { + let PrintMethod = "printUImm<" # I # ">"; + let ParserMatchClass = + !cast("UImm" # I # "AsmOperandClass"); + } + +// Like uimm16_64 but coerces simm16 to uimm16. +def uimm16_64_relaxed : Operand { + let PrintMethod = "printUImm<16>"; + let ParserMatchClass = + !cast("UImm16RelaxedAsmOperandClass"); +} + +def uimm16_altrelaxed : Operand { + let PrintMethod = "printUImm<16>"; + let ParserMatchClass = + !cast("UImm16AltRelaxedAsmOperandClass"); +} +// Like uimm5 but reports a less confusing error for 32-63 when +// an instruction alias permits that. +def uimm5_report_uimm6 : Operand { + let PrintMethod = "printUImm<6>"; + let ParserMatchClass = ConstantUImm5ReportUImm6AsmOperandClass; +} + +// Like uimm5_64 but reports a less confusing error for 32-63 when +// an instruction alias permits that. +def uimm5_64_report_uimm6 : Operand { + let PrintMethod = "printUImm<5>"; + let ParserMatchClass = ConstantUImm5ReportUImm6AsmOperandClass; +} + +foreach I = {1, 2, 3, 4} in + def uimm # I # _ptr : Operand { + let PrintMethod = "printUImm<" # I # ">"; + let ParserMatchClass = + !cast("ConstantUImm" # I # "AsmOperandClass"); + } + +foreach I = {1, 2, 3, 4, 5, 6, 8} in + def vsplat_uimm # I : Operand { + let PrintMethod = "printUImm<" # I # ">"; + let ParserMatchClass = + !cast("ConstantUImm" # I # "AsmOperandClass"); + } + +// Signed operands +foreach I = {4, 5, 6, 9, 10, 11} in + def simm # I : Operand { + let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; + let ParserMatchClass = + !cast("ConstantSImm" # I # "AsmOperandClass"); + } + +foreach I = {1, 2, 3} in + def simm10_lsl # I : Operand { + let DecoderMethod = "DecodeSImmWithOffsetAndScale<10, " # I # ">"; + let ParserMatchClass = + !cast("ConstantSImm10Lsl" # I # "AsmOperandClass"); + } + +foreach I = {10} in + def simm # I # _64 : Operand { + let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; + let ParserMatchClass = + !cast("ConstantSImm" # I # "AsmOperandClass"); + } + +foreach I = {5, 10} in + def vsplat_simm # I : Operand { + let ParserMatchClass = + !cast("ConstantSImm" # I # "AsmOperandClass"); + } + +def simm7_lsl2 : Operand { + let EncoderMethod = "getSImm7Lsl2Encoding"; + let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ", 0, 4>"; + let ParserMatchClass = ConstantSImm7Lsl2AsmOperandClass; +} + +foreach I = {16, 32} in + def simm # I : Operand { + let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; + let ParserMatchClass = !cast("SImm" # I # "AsmOperandClass"); + } + +// Like simm16 but coerces uimm16 to simm16. +def simm16_relaxed : Operand { + let DecoderMethod = "DecodeSImmWithOffsetAndScale<16>"; + let ParserMatchClass = !cast("SImm16RelaxedAsmOperandClass"); +} + +def simm16_64 : Operand { + let DecoderMethod = "DecodeSImmWithOffsetAndScale<16>"; + let ParserMatchClass = !cast("SImm16AsmOperandClass"); +} + +// like simm32 but coerces simm32 to uimm32. +def uimm32_coerced : Operand { + let ParserMatchClass = !cast("UImm32CoercedAsmOperandClass"); +} +// Like simm32 but coerces uimm32 to simm32. +def simm32_relaxed : Operand { + let DecoderMethod = "DecodeSImmWithOffsetAndScale<32>"; + let ParserMatchClass = !cast("SImm32RelaxedAsmOperandClass"); +} + +// This is almost the same as a uimm7 but 0x7f is interpreted as -1. +def li16_imm : Operand { + let DecoderMethod = "DecodeLi16Imm"; + let ParserMatchClass = ConstantUImm7Sub1AsmOperandClass; +} + +def MaxisMemAsmOperand : AsmOperandClass { + let Name = "Mem"; + let ParserMethod = "parseMemOperand"; +} + +def MaxisMemSimm9AsmOperand : AsmOperandClass { + let Name = "MemOffsetSimm9"; + let SuperClasses = [MaxisMemAsmOperand]; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithSimmOffset<9>"; + let DiagnosticType = "MemSImm9"; +} + +def MaxisMemSimm10AsmOperand : AsmOperandClass { + let Name = "MemOffsetSimm10"; + let SuperClasses = [MaxisMemAsmOperand]; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithSimmOffset<10>"; + let DiagnosticType = "MemSImm10"; +} + +def MaxisMemSimm12AsmOperand : AsmOperandClass { + let Name = "MemOffsetSimm12"; + let SuperClasses = [MaxisMemAsmOperand]; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithSimmOffset<12>"; + let DiagnosticType = "MemSImm12"; +} + +foreach I = {1, 2, 3} in + def MaxisMemSimm10Lsl # I # AsmOperand : AsmOperandClass { + let Name = "MemOffsetSimm10_" # I; + let SuperClasses = [MaxisMemAsmOperand]; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithSimmOffset<10, " # I # ">"; + let DiagnosticType = "MemSImm10Lsl" # I; + } + +def MaxisMemSimm11AsmOperand : AsmOperandClass { + let Name = "MemOffsetSimm11"; + let SuperClasses = [MaxisMemAsmOperand]; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithSimmOffset<11>"; + let DiagnosticType = "MemSImm11"; +} + +def MaxisMemSimm16AsmOperand : AsmOperandClass { + let Name = "MemOffsetSimm16"; + let SuperClasses = [MaxisMemAsmOperand]; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithSimmOffset<16>"; + let DiagnosticType = "MemSImm16"; +} + +def MaxisInvertedImmoperand : AsmOperandClass { + let Name = "InvNum"; + let RenderMethod = "addImmOperands"; + let ParserMethod = "parseInvNum"; +} + +def InvertedImOperand : Operand { + let ParserMatchClass = MaxisInvertedImmoperand; +} + +def InvertedImOperand64 : Operand { + let ParserMatchClass = MaxisInvertedImmoperand; +} + +class mem_generic : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops ptr_rc, simm16); + let EncoderMethod = "getMemEncoding"; + let ParserMatchClass = MaxisMemAsmOperand; + let OperandType = "OPERAND_MEMORY"; +} + +// Address operand +def mem : mem_generic; + +// MSA specific address operand +def mem_msa : mem_generic { + let MIOperandInfo = (ops ptr_rc, simm10); + let EncoderMethod = "getMSAMemEncoding"; +} + +def simm12 : Operand { + let DecoderMethod = "DecodeSimm12"; +} + +def mem_simm9 : mem_generic { + let MIOperandInfo = (ops ptr_rc, simm9); + let EncoderMethod = "getMemEncoding"; + let ParserMatchClass = MaxisMemSimm9AsmOperand; +} + +def mem_simm10 : mem_generic { + let MIOperandInfo = (ops ptr_rc, simm10); + let EncoderMethod = "getMemEncoding"; + let ParserMatchClass = MaxisMemSimm10AsmOperand; +} + +foreach I = {1, 2, 3} in + def mem_simm10_lsl # I : mem_generic { + let MIOperandInfo = (ops ptr_rc, !cast("simm10_lsl" # I)); + let EncoderMethod = "getMemEncoding<" # I # ">"; + let ParserMatchClass = + !cast("MaxisMemSimm10Lsl" # I # "AsmOperand"); + } + +def mem_simm11 : mem_generic { + let MIOperandInfo = (ops ptr_rc, simm11); + let EncoderMethod = "getMemEncoding"; + let ParserMatchClass = MaxisMemSimm11AsmOperand; +} + +def mem_simm12 : mem_generic { + let MIOperandInfo = (ops ptr_rc, simm12); + let EncoderMethod = "getMemEncoding"; + let ParserMatchClass = MaxisMemSimm12AsmOperand; +} + +def mem_simm16 : mem_generic { + let MIOperandInfo = (ops ptr_rc, simm16); + let EncoderMethod = "getMemEncoding"; + let ParserMatchClass = MaxisMemSimm16AsmOperand; +} + +def mem_ea : Operand { + let PrintMethod = "printMemOperandEA"; + let MIOperandInfo = (ops ptr_rc, simm16); + let EncoderMethod = "getMemEncoding"; + let OperandType = "OPERAND_MEMORY"; +} + +def PtrRC : Operand { + let MIOperandInfo = (ops ptr_rc); + let DecoderMethod = "DecodePtrRegisterClass"; + let ParserMatchClass = GPR32AsmOperand; +} + +// size operand of ins instruction +def size_ins : Operand { + let EncoderMethod = "getSizeInsEncoding"; + let DecoderMethod = "DecodeInsSize"; +} + +// Transformation Function - get the lower 16 bits. +def LO16 : SDNodeXFormgetZExtValue() & 0xFFFF); +}]>; + +// Transformation Function - get the higher 16 bits. +def HI16 : SDNodeXFormgetZExtValue() >> 16) & 0xFFFF); +}]>; + +// Plus 1. +def Plus1 : SDNodeXFormgetSExtValue() + 1); }]>; + +// Node immediate is zero (e.g. insve.d) +def immz : PatLeaf<(imm), [{ return N->getSExtValue() == 0; }]>; + +// Node immediate fits as 16-bit sign extended on target immediate. +// e.g. addi, andi +def immSExt8 : PatLeaf<(imm), [{ return isInt<8>(N->getSExtValue()); }]>; + +// Node immediate fits as 16-bit sign extended on target immediate. +// e.g. addi, andi +def immSExt16 : PatLeaf<(imm), [{ return isInt<16>(N->getSExtValue()); }]>; + +// Node immediate fits as 7-bit zero extended on target immediate. +def immZExt7 : PatLeaf<(imm), [{ return isUInt<7>(N->getZExtValue()); }]>; + +// Node immediate fits as 16-bit zero extended on target immediate. +// The LO16 param means that only the lower 16 bits of the node +// immediate are caught. +// e.g. addiu, sltiu +def immZExt16 : PatLeaf<(imm), [{ + if (N->getValueType(0) == MVT::i32) + return (uint32_t)N->getZExtValue() == (unsigned short)N->getZExtValue(); + else + return (uint64_t)N->getZExtValue() == (unsigned short)N->getZExtValue(); +}], LO16>; + +// Immediate can be loaded with LUi (32-bit int with lower 16-bit cleared). +def immSExt32Low16Zero : PatLeaf<(imm), [{ + int64_t Val = N->getSExtValue(); + return isInt<32>(Val) && !(Val & 0xffff); +}]>; + +// Zero-extended 32-bit unsigned int with lower 16-bit cleared. +def immZExt32Low16Zero : PatLeaf<(imm), [{ + uint64_t Val = N->getZExtValue(); + return isUInt<32>(Val) && !(Val & 0xffff); +}]>; + +// Note immediate fits as a 32 bit signed extended on target immediate. +def immSExt32 : PatLeaf<(imm), [{ return isInt<32>(N->getSExtValue()); }]>; + +// Note immediate fits as a 32 bit zero extended on target immediate. +def immZExt32 : PatLeaf<(imm), [{ return isUInt<32>(N->getZExtValue()); }]>; + +// shamt field must fit in 5 bits. +def immZExt5 : ImmLeaf; + +def immZExt5Plus1 : PatLeaf<(imm), [{ + return isUInt<5>(N->getZExtValue() - 1); +}]>; +def immZExt5Plus32 : PatLeaf<(imm), [{ + return isUInt<5>(N->getZExtValue() - 32); +}]>; +def immZExt5Plus33 : PatLeaf<(imm), [{ + return isUInt<5>(N->getZExtValue() - 33); +}]>; + +def immZExt5To31 : SDNodeXFormgetZExtValue()); +}]>; + +// True if (N + 1) fits in 16-bit field. +def immSExt16Plus1 : PatLeaf<(imm), [{ + return isInt<17>(N->getSExtValue()) && isInt<16>(N->getSExtValue() + 1); +}]>; + +def immZExtRange2To64 : PatLeaf<(imm), [{ + return isUInt<7>(N->getZExtValue()) && (N->getZExtValue() >= 2) && + (N->getZExtValue() <= 64); +}]>; + +def ORiPred : PatLeaf<(imm), [{ + return isUInt<16>(N->getZExtValue()) && !isInt<16>(N->getSExtValue()); +}], LO16>; + +def LUiPred : PatLeaf<(imm), [{ + int64_t Val = N->getSExtValue(); + return !isInt<16>(Val) && isInt<32>(Val) && !(Val & 0xffff); +}]>; + +def LUiORiPred : PatLeaf<(imm), [{ + int64_t SVal = N->getSExtValue(); + return isInt<32>(SVal) && (SVal & 0xffff); +}]>; + +// Maxis Address Mode! SDNode frameindex could possibily be a match +// since load and store instructions from stack used it. +def addr : + ComplexPattern; + +def addrRegImm : + ComplexPattern; + +def addrDefault : + ComplexPattern; + +def addrimm10 : ComplexPattern; +def addrimm10lsl1 : ComplexPattern; +def addrimm10lsl2 : ComplexPattern; +def addrimm10lsl3 : ComplexPattern; + +//===----------------------------------------------------------------------===// +// Instructions specific format +//===----------------------------------------------------------------------===// + +// Arithmetic and logical instructions with 3 register operands. +class ArithLogicR: + InstSE<(outs RO:$rd), (ins RO:$rs, RO:$rt), + !strconcat(opstr, "\t$rd, $rs, $rt"), + [(set RO:$rd, (OpNode RO:$rs, RO:$rt))], Itin, FrmR, opstr> { + let isCommutable = isComm; + let isReMaterializable = 1; + let TwoOperandAliasConstraint = "$rd = $rs"; +} + +// Arithmetic and logical instructions with 2 register operands. +class ArithLogicI : + InstSE<(outs RO:$rt), (ins RO:$rs, Od:$imm16), + !strconcat(opstr, "\t$rt, $rs, $imm16"), + [(set RO:$rt, (OpNode RO:$rs, imm_type:$imm16))], + Itin, FrmI, opstr> { + let isReMaterializable = 1; + let TwoOperandAliasConstraint = "$rs = $rt"; +} + +// Arithmetic Multiply ADD/SUB +class MArithR : + InstSE<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), + !strconcat(opstr, "\t$rs, $rt"), [], itin, FrmR, opstr> { + let Defs = [HI0, LO0]; + let Uses = [HI0, LO0]; + let isCommutable = isComm; +} + +// Logical +class LogicNOR: + InstSE<(outs RO:$rd), (ins RO:$rs, RO:$rt), + !strconcat(opstr, "\t$rd, $rs, $rt"), + [(set RO:$rd, (not (or RO:$rs, RO:$rt)))], II_NOR, FrmR, opstr> { + let isCommutable = 1; +} + +// Shifts +class shift_rotate_imm : + InstSE<(outs RO:$rd), (ins RO:$rt, ImmOpnd:$shamt), + !strconcat(opstr, "\t$rd, $rt, $shamt"), + [(set RO:$rd, (OpNode RO:$rt, PF:$shamt))], itin, FrmR, opstr> { + let TwoOperandAliasConstraint = "$rt = $rd"; +} + +class shift_rotate_reg: + InstSE<(outs RO:$rd), (ins RO:$rt, GPR32Opnd:$rs), + !strconcat(opstr, "\t$rd, $rt, $rs"), + [(set RO:$rd, (OpNode RO:$rt, GPR32Opnd:$rs))], itin, FrmR, + opstr>; + +// Load Upper Immediate +class LoadUpper: + InstSE<(outs RO:$rt), (ins Imm:$imm16), !strconcat(opstr, "\t$rt, $imm16"), + [], II_LUI, FrmI, opstr>, IsAsCheapAsAMove { + let hasSideEffects = 0; + let isReMaterializable = 1; +} + +// Memory Load/Store +class LoadMemory : + InstSE<(outs RO:$rt), (ins MO:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(set RO:$rt, (OpNode Addr:$addr))], Itin, FrmI, opstr> { + let DecoderMethod = "DecodeMem"; + let canFoldAsLoad = 1; + let mayLoad = 1; +} + +class Load : + LoadMemory; + +class StoreMemory : + InstSE<(outs), (ins RO:$rt, MO:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(OpNode RO:$rt, Addr:$addr)], Itin, FrmI, opstr> { + let DecoderMethod = "DecodeMem"; + let mayStore = 1; +} + +class Store : + StoreMemory; + +// Load/Store Left/Right +let canFoldAsLoad = 1 in +class LoadLeftRight : + InstSE<(outs RO:$rt), (ins mem:$addr, RO:$src), + !strconcat(opstr, "\t$rt, $addr"), + [(set RO:$rt, (OpNode addr:$addr, RO:$src))], Itin, FrmI> { + let DecoderMethod = "DecodeMem"; + string Constraints = "$src = $rt"; +} + +class StoreLeftRight : + InstSE<(outs), (ins RO:$rt, mem:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(OpNode RO:$rt, addr:$addr)], Itin, FrmI> { + let DecoderMethod = "DecodeMem"; +} + +// COP2 Load/Store +class LW_FT2 : + InstSE<(outs RC:$rt), (ins mem_simm16:$addr), + !strconcat(opstr, "\t$rt, $addr"), + [(set RC:$rt, (OpNode addrDefault:$addr))], Itin, FrmFI, opstr> { + let DecoderMethod = "DecodeFMem2"; + let mayLoad = 1; +} + +class SW_FT2 : + InstSE<(outs), (ins RC:$rt, mem_simm16:$addr), + !strconcat(opstr, "\t$rt, $addr"), + [(OpNode RC:$rt, addrDefault:$addr)], Itin, FrmFI, opstr> { + let DecoderMethod = "DecodeFMem2"; + let mayStore = 1; +} + +// COP3 Load/Store +class LW_FT3 : + InstSE<(outs RC:$rt), (ins mem:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(set RC:$rt, (OpNode addrDefault:$addr))], Itin, FrmFI, opstr> { + let DecoderMethod = "DecodeFMem3"; + let mayLoad = 1; +} + +class SW_FT3 : + InstSE<(outs), (ins RC:$rt, mem:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(OpNode RC:$rt, addrDefault:$addr)], Itin, FrmFI, opstr> { + let DecoderMethod = "DecodeFMem3"; + let mayStore = 1; +} + +// Conditional Branch +class CBranch : + InstSE<(outs), (ins RO:$rs, RO:$rt, opnd:$offset), + !strconcat(opstr, "\t$rs, $rt, $offset"), + [(brcond (i32 (cond_op RO:$rs, RO:$rt)), bb:$offset)], II_BCC, + FrmI, opstr> { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 1; + let Defs = [AT]; + bit isCTI = 1; +} + +class CBranchLikely : + InstSE<(outs), (ins RO:$rs, RO:$rt, opnd:$offset), + !strconcat(opstr, "\t$rs, $rt, $offset"), [], II_BCC, FrmI, opstr> { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 1; + let Defs = [AT]; + bit isCTI = 1; +} + +class CBranchZero : + InstSE<(outs), (ins RO:$rs, opnd:$offset), + !strconcat(opstr, "\t$rs, $offset"), + [(brcond (i32 (cond_op RO:$rs, 0)), bb:$offset)], II_BCCZ, + FrmI, opstr> { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 1; + let Defs = [AT]; + bit isCTI = 1; +} + +class CBranchZeroLikely : + InstSE<(outs), (ins RO:$rs, opnd:$offset), + !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZ, FrmI, opstr> { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 1; + let Defs = [AT]; + bit isCTI = 1; +} + +// SetCC +class SetCC_R : + InstSE<(outs GPR32Opnd:$rd), (ins RO:$rs, RO:$rt), + !strconcat(opstr, "\t$rd, $rs, $rt"), + [(set GPR32Opnd:$rd, (cond_op RO:$rs, RO:$rt))], + II_SLT_SLTU, FrmR, opstr>; + +class SetCC_I: + InstSE<(outs GPR32Opnd:$rt), (ins RO:$rs, Od:$imm16), + !strconcat(opstr, "\t$rt, $rs, $imm16"), + [(set GPR32Opnd:$rt, (cond_op RO:$rs, imm_type:$imm16))], + II_SLTI_SLTIU, FrmI, opstr>; + +// Jump +class JumpFJ : + InstSE<(outs), (ins opnd:$target), !strconcat(opstr, "\t$target"), + [(operator targetoperator:$target)], II_J, FrmJ, bopstr> { + let isTerminator=1; + let isBarrier=1; + let hasDelaySlot = 1; + let DecoderMethod = "DecodeJumpTarget"; + let Defs = [AT]; + bit isCTI = 1; +} + +// Unconditional branch +class UncondBranch : + PseudoSE<(outs), (ins brtarget:$offset), [(br bb:$offset)], II_B>, + PseudoInstExpansion<(BEQInst ZERO, ZERO, opnd:$offset)> { + let isBranch = 1; + let isTerminator = 1; + let isBarrier = 1; + let hasDelaySlot = 1; + let AdditionalPredicates = [RelocPIC]; + let Defs = [AT]; + bit isCTI = 1; +} + +// Base class for indirect branch and return instruction classes. +let isTerminator=1, isBarrier=1, hasDelaySlot = 1, isCTI = 1 in +class JumpFR: + InstSE<(outs), (ins RO:$rs), "jr\t$rs", [(operator RO:$rs)], II_JR, + FrmR, opstr>; + +// Indirect branch +class IndirectBranch : JumpFR { + let isBranch = 1; + let isIndirectBranch = 1; +} + +// Jump and Link (Call) +let isCall=1, hasDelaySlot=1, isCTI=1, Defs = [RA] in { + class JumpLink : + InstSE<(outs), (ins opnd:$target), !strconcat(opstr, "\t$target"), + [(MaxisJmpLink tglobaladdr:$target)], II_JAL, FrmJ, opstr> { + let DecoderMethod = "DecodeJumpTarget"; + } + + class JumpLinkRegPseudo: + PseudoSE<(outs), (ins RO:$rs), [(MaxisJmpLink RO:$rs)], II_JALR>, + PseudoInstExpansion<(JALRInst RetReg, ResRO:$rs)>; + + class JumpLinkReg: + InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), + [], II_JALR, FrmR, opstr>; + + class BGEZAL_FT : + InstSE<(outs), (ins RO:$rs, opnd:$offset), + !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZAL, FrmI, opstr> { + let hasDelaySlot = 1; + } + +} + +let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, hasDelaySlot = 1, + hasExtraSrcRegAllocReq = 1, isCTI = 1, Defs = [AT] in { + class TailCall : + PseudoSE<(outs), (ins calltarget:$target), [], II_J>, + PseudoInstExpansion<(JumpInst Opnd:$target)>; + + class TailCallReg : + PseudoSE<(outs), (ins RO:$rs), [(MaxisTailCall RO:$rs)], II_JR>; +} + +class BAL_BR_Pseudo : + PseudoSE<(outs), (ins brtarget:$offset), [], II_BCCZAL>, + PseudoInstExpansion<(RealInst ZERO, brtarget:$offset)> { + let isBranch = 1; + let isTerminator = 1; + let isBarrier = 1; + let hasDelaySlot = 1; + let Defs = [RA]; + bit isCTI = 1; +} + +let isCTI = 1 in { +// Syscall +class SYS_FT : + InstSE<(outs), (ins ImmOp:$code_), + !strconcat(opstr, "\t$code_"), [], itin, FrmI, opstr>; +// Break +class BRK_FT : + InstSE<(outs), (ins uimm10:$code_1, uimm10:$code_2), + !strconcat(opstr, "\t$code_1, $code_2"), [], II_BREAK, + FrmOther, opstr>; + +// (D)Eret +class ER_FT : + InstSE<(outs), (ins), + opstr, [], itin, FrmOther, opstr>; + +// Wait +class WAIT_FT : + InstSE<(outs), (ins), opstr, [], II_WAIT, FrmOther, opstr>; +} + +// Interrupts +class DEI_FT : + InstSE<(outs RO:$rt), (ins), + !strconcat(opstr, "\t$rt"), [], itin, FrmOther, opstr>; + +// Sync +let hasSideEffects = 1 in +class SYNC_FT : + InstSE<(outs), (ins uimm5:$stype), "sync $stype", + [(MaxisSync immZExt5:$stype)], II_SYNC, FrmOther, opstr>; + +class SYNCI_FT : + InstSE<(outs), (ins mem_simm16:$addr), !strconcat(opstr, "\t$addr"), [], + II_SYNCI, FrmOther, opstr> { + let hasSideEffects = 1; + let DecoderMethod = "DecodeSyncI"; +} + +let hasSideEffects = 1, isCTI = 1 in { +class TEQ_FT : + InstSE<(outs), (ins RO:$rs, RO:$rt, ImmOp:$code_), + !strconcat(opstr, "\t$rs, $rt, $code_"), [], itin, FrmI, opstr>; + +class TEQI_FT : + InstSE<(outs), (ins RO:$rs, simm16:$imm16), + !strconcat(opstr, "\t$rs, $imm16"), [], itin, FrmOther, opstr>; +} + +// Mul, Div +class Mult DefRegs> : + InstSE<(outs), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rs, $rt"), [], + itin, FrmR, opstr> { + let isCommutable = 1; + let Defs = DefRegs; + let hasSideEffects = 0; +} + +// Pseudo multiply/divide instruction with explicit accumulator register +// operands. +class MultDivPseudo : + PseudoSE<(outs R0:$ac), (ins R1:$rs, R1:$rt), + [(set R0:$ac, (OpNode R1:$rs, R1:$rt))], Itin>, + PseudoInstExpansion<(RealInst R1:$rs, R1:$rt)> { + let isCommutable = IsComm; + let hasSideEffects = HasSideEffects; + let usesCustomInserter = UsesCustomInserter; +} + +// Pseudo multiply add/sub instruction with explicit accumulator register +// operands. +class MAddSubPseudo + : PseudoSE<(outs ACC64:$ac), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64:$acin), + [(set ACC64:$ac, + (OpNode GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64:$acin))], + itin>, + PseudoInstExpansion<(RealInst GPR32Opnd:$rs, GPR32Opnd:$rt)> { + string Constraints = "$acin = $ac"; +} + +class Div DefRegs> : + InstSE<(outs), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$$zero, $rs, $rt"), + [], itin, FrmR, opstr> { + let Defs = DefRegs; +} + +// Move from Hi/Lo +class PseudoMFLOHI + : PseudoSE<(outs DstRC:$rd), (ins SrcRC:$hilo), + [(set DstRC:$rd, (OpNode SrcRC:$hilo))], II_MFHI_MFLO>; + +class MoveFromLOHI: + InstSE<(outs RO:$rd), (ins), !strconcat(opstr, "\t$rd"), [], II_MFHI_MFLO, + FrmR, opstr> { + let Uses = [UseReg]; + let hasSideEffects = 0; +} + +class PseudoMTLOHI + : PseudoSE<(outs DstRC:$lohi), (ins SrcRC:$lo, SrcRC:$hi), + [(set DstRC:$lohi, (MaxisMTLOHI SrcRC:$lo, SrcRC:$hi))], + II_MTHI_MTLO>; + +class MoveToLOHI DefRegs>: + InstSE<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), [], II_MTHI_MTLO, + FrmR, opstr> { + let Defs = DefRegs; + let hasSideEffects = 0; +} + +class EffectiveAddress : + InstSE<(outs RO:$rt), (ins mem_ea:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(set RO:$rt, addr:$addr)], II_ADDIU, FrmI, + !strconcat(opstr, "_lea")> { + let isCodeGenOnly = 1; + let hasNoSchedulingInfo = 1; + let DecoderMethod = "DecodeMem"; +} + +// Count Leading Ones/Zeros in Word +class CountLeading0: + InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), + [(set RO:$rd, (ctlz RO:$rs))], itin, FrmR, opstr>; + +class CountLeading1: + InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), + [(set RO:$rd, (ctlz (not RO:$rs)))], itin, FrmR, opstr>; + +// Sign Extend in Register. +class SignExtInReg : + InstSE<(outs RO:$rd), (ins RO:$rt), !strconcat(opstr, "\t$rd, $rt"), + [(set RO:$rd, (sext_inreg RO:$rt, vt))], itin, FrmR, opstr>; + +// Subword Swap +class SubwordSwap: + InstSE<(outs RO:$rd), (ins RO:$rt), !strconcat(opstr, "\t$rd, $rt"), [], itin, + FrmR, opstr> { + let hasSideEffects = 0; +} + +// Read Hardware +class ReadHardware : + InstSE<(outs CPURegOperand:$rt), (ins RO:$rd), "rdhwr\t$rt, $rd", [], + II_RDHWR, FrmR, "rdhwr">; + +// Ext and Ins +class ExtBase : + InstSE<(outs RO:$rt), (ins RO:$rs, PosOpnd:$pos, SizeOpnd:$size), + !strconcat(opstr, "\t$rt, $rs, $pos, $size"), + [(set RO:$rt, (Op RO:$rs, PosImm:$pos, SizeImm:$size))], II_EXT, + FrmR, opstr>, ISA_MAXIS32R2; + +// 'ins' and its' 64 bit variants are matched by C++ code. +class InsBase: + InstSE<(outs RO:$rt), (ins RO:$rs, PosOpnd:$pos, SizeOpnd:$size, RO:$src), + !strconcat(opstr, "\t$rt, $rs, $pos, $size"), + [(set RO:$rt, (null_frag RO:$rs, PosImm:$pos, SizeImm:$size, + RO:$src))], + II_INS, FrmR, opstr>, ISA_MAXIS32R2 { + let Constraints = "$src = $rt"; +} + +// Atomic instructions with 2 source operands (ATOMIC_SWAP & ATOMIC_LOAD_*). +class Atomic2Ops : + PseudoSE<(outs DRC:$dst), (ins PtrRC:$ptr, DRC:$incr), + [(set DRC:$dst, (Op iPTR:$ptr, DRC:$incr))]>; + +// Atomic Compare & Swap. +class AtomicCmpSwap : + PseudoSE<(outs DRC:$dst), (ins PtrRC:$ptr, DRC:$cmp, DRC:$swap), + [(set DRC:$dst, (Op iPTR:$ptr, DRC:$cmp, DRC:$swap))]>; + +class LLBase : + InstSE<(outs RO:$rt), (ins MO:$addr), !strconcat(opstr, "\t$rt, $addr"), + [], II_LL, FrmI, opstr> { + let DecoderMethod = "DecodeMem"; + let mayLoad = 1; +} + +class SCBase : + InstSE<(outs RO:$dst), (ins RO:$rt, mem:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], II_SC, FrmI> { + let DecoderMethod = "DecodeMem"; + let mayStore = 1; + let Constraints = "$rt = $dst"; +} + +class MFC3OP : + InstSE<(outs RO:$rt), (ins RD:$rd, uimm3:$sel), + !strconcat(asmstr, "\t$rt, $rd, $sel"), [], itin, FrmFR>; + +class MTC3OP : + InstSE<(outs RO:$rd), (ins RD:$rt, uimm3:$sel), + !strconcat(asmstr, "\t$rt, $rd, $sel"), [], itin, FrmFR>; + +class TrapBase + : PseudoSE<(outs), (ins), [(trap)], II_TRAP>, + PseudoInstExpansion<(RealInst 0, 0)> { + let isBarrier = 1; + let isTerminator = 1; + let isCodeGenOnly = 1; + let isCTI = 1; +} + +//===----------------------------------------------------------------------===// +// Pseudo instructions +//===----------------------------------------------------------------------===// + +// Return RA. +let isReturn=1, isTerminator=1, isBarrier=1, hasCtrlDep=1, isCTI=1 in { + let hasDelaySlot=1 in + def RetRA : PseudoSE<(outs), (ins), [(MaxisRet)]>; + + let hasSideEffects=1 in + def ERet : PseudoSE<(outs), (ins), [(MaxisERet)]>; +} + +let Defs = [SP], Uses = [SP], hasSideEffects = 1 in { +def ADJCALLSTACKDOWN : MaxisPseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + [(callseq_start timm:$amt1, timm:$amt2)]>; +def ADJCALLSTACKUP : MaxisPseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + [(callseq_end timm:$amt1, timm:$amt2)]>; +} + +let usesCustomInserter = 1 in { + def ATOMIC_LOAD_ADD_I8 : Atomic2Ops; + def ATOMIC_LOAD_ADD_I16 : Atomic2Ops; + def ATOMIC_LOAD_ADD_I32 : Atomic2Ops; + def ATOMIC_LOAD_SUB_I8 : Atomic2Ops; + def ATOMIC_LOAD_SUB_I16 : Atomic2Ops; + def ATOMIC_LOAD_SUB_I32 : Atomic2Ops; + def ATOMIC_LOAD_AND_I8 : Atomic2Ops; + def ATOMIC_LOAD_AND_I16 : Atomic2Ops; + def ATOMIC_LOAD_AND_I32 : Atomic2Ops; + def ATOMIC_LOAD_OR_I8 : Atomic2Ops; + def ATOMIC_LOAD_OR_I16 : Atomic2Ops; + def ATOMIC_LOAD_OR_I32 : Atomic2Ops; + def ATOMIC_LOAD_XOR_I8 : Atomic2Ops; + def ATOMIC_LOAD_XOR_I16 : Atomic2Ops; + def ATOMIC_LOAD_XOR_I32 : Atomic2Ops; + def ATOMIC_LOAD_NAND_I8 : Atomic2Ops; + def ATOMIC_LOAD_NAND_I16 : Atomic2Ops; + def ATOMIC_LOAD_NAND_I32 : Atomic2Ops; + + def ATOMIC_SWAP_I8 : Atomic2Ops; + def ATOMIC_SWAP_I16 : Atomic2Ops; + def ATOMIC_SWAP_I32 : Atomic2Ops; + + def ATOMIC_CMP_SWAP_I8 : AtomicCmpSwap; + def ATOMIC_CMP_SWAP_I16 : AtomicCmpSwap; + def ATOMIC_CMP_SWAP_I32 : AtomicCmpSwap; +} + +/// Pseudo instructions for loading and storing accumulator registers. +let isPseudo = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { + def LOAD_ACC64 : Load<"", ACC64>; + def STORE_ACC64 : Store<"", ACC64>; +} + +// We need these two pseudo instructions to avoid offset calculation for long +// branches. See the comment in file MaxisLongBranch.cpp for detailed +// explanation. + +// Expands to: lui $dst, %hi($tgt - $baltgt) +def LONG_BRANCH_LUi : PseudoSE<(outs GPR32Opnd:$dst), + (ins brtarget:$tgt, brtarget:$baltgt), []>; + +// Expands to: addiu $dst, $src, %lo($tgt - $baltgt) +def LONG_BRANCH_ADDiu : PseudoSE<(outs GPR32Opnd:$dst), + (ins GPR32Opnd:$src, brtarget:$tgt, brtarget:$baltgt), []>; + +//===----------------------------------------------------------------------===// +// Instruction definition +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// +// MaxisI Instructions +//===----------------------------------------------------------------------===// + +/// Arithmetic Instructions (ALU Immediate) +let AdditionalPredicates = [NotInMicroMaxis] in { + def ADDiu : MMRel, StdMMR6Rel, ArithLogicI<"addiu", simm16_relaxed, GPR32Opnd, + II_ADDIU, immSExt16, add>, + ADDI_FM<0x9>, IsAsCheapAsAMove; + + def ANDi : MMRel, StdMMR6Rel, + ArithLogicI<"andi", uimm16, GPR32Opnd, II_ANDI, immZExt16, and>, + ADDI_FM<0xc>; + def ORi : MMRel, StdMMR6Rel, + ArithLogicI<"ori", uimm16, GPR32Opnd, II_ORI, immZExt16, or>, + ADDI_FM<0xd>; + def XORi : MMRel, StdMMR6Rel, + ArithLogicI<"xori", uimm16, GPR32Opnd, II_XORI, immZExt16, xor>, + ADDI_FM<0xe>; +} +def ADDi : MMRel, ArithLogicI<"addi", simm16_relaxed, GPR32Opnd, II_ADDI>, ADDI_FM<0x8>, + ISA_MAXIS1_NOT_32R6_64R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def SLTi : MMRel, SetCC_I<"slti", setlt, simm16, immSExt16, GPR32Opnd>, + SLTI_FM<0xa>; + def SLTiu : MMRel, SetCC_I<"sltiu", setult, simm16, immSExt16, GPR32Opnd>, + SLTI_FM<0xb>; +} +def LUi : MMRel, LoadUpper<"lui", GPR32Opnd, uimm16_relaxed>, LUI_FM; +let AdditionalPredicates = [NotInMicroMaxis] in { + /// Arithmetic Instructions (3-Operand, R-Type) + def ADDu : MMRel, StdMMR6Rel, ArithLogicR<"addu", GPR32Opnd, 1, II_ADDU, add>, + ADD_FM<0, 0x21>; + def SUBu : MMRel, StdMMR6Rel, ArithLogicR<"subu", GPR32Opnd, 0, II_SUBU, sub>, + ADD_FM<0, 0x23>; +} +let Defs = [HI0, LO0] in +def MUL : MMRel, ArithLogicR<"mul", GPR32Opnd, 1, II_MUL, mul>, + ADD_FM<0x1c, 2>, ISA_MAXIS32_NOT_32R6_64R6; +def ADD : MMRel, StdMMR6Rel, ArithLogicR<"add", GPR32Opnd, 1, II_ADD>, ADD_FM<0, 0x20>; +def SUB : MMRel, StdMMR6Rel, ArithLogicR<"sub", GPR32Opnd, 0, II_SUB>, ADD_FM<0, 0x22>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def SLT : MMRel, SetCC_R<"slt", setlt, GPR32Opnd>, ADD_FM<0, 0x2a>; + def SLTu : MMRel, SetCC_R<"sltu", setult, GPR32Opnd>, ADD_FM<0, 0x2b>; + def AND : MMRel, StdMMR6Rel, ArithLogicR<"and", GPR32Opnd, 1, II_AND, and>, + ADD_FM<0, 0x24>; + def OR : MMRel, StdMMR6Rel, ArithLogicR<"or", GPR32Opnd, 1, II_OR, or>, + ADD_FM<0, 0x25>; + def XOR : MMRel, StdMMR6Rel, ArithLogicR<"xor", GPR32Opnd, 1, II_XOR, xor>, + ADD_FM<0, 0x26>; + def NOR : MMRel, StdMMR6Rel, LogicNOR<"nor", GPR32Opnd>, ADD_FM<0, 0x27>; +} + +/// Shift Instructions +let AdditionalPredicates = [NotInMicroMaxis] in { +def SLL : MMRel, shift_rotate_imm<"sll", uimm5, GPR32Opnd, II_SLL, shl, + immZExt5>, SRA_FM<0, 0>; +def SRL : MMRel, shift_rotate_imm<"srl", uimm5, GPR32Opnd, II_SRL, srl, + immZExt5>, SRA_FM<2, 0>; +def SRA : MMRel, shift_rotate_imm<"sra", uimm5, GPR32Opnd, II_SRA, sra, + immZExt5>, SRA_FM<3, 0>; +def SLLV : MMRel, shift_rotate_reg<"sllv", GPR32Opnd, II_SLLV, shl>, + SRLV_FM<4, 0>; +def SRLV : MMRel, shift_rotate_reg<"srlv", GPR32Opnd, II_SRLV, srl>, + SRLV_FM<6, 0>; +def SRAV : MMRel, shift_rotate_reg<"srav", GPR32Opnd, II_SRAV, sra>, + SRLV_FM<7, 0>; +} + +// Rotate Instructions +let AdditionalPredicates = [NotInMicroMaxis] in { + def ROTR : MMRel, shift_rotate_imm<"rotr", uimm5, GPR32Opnd, II_ROTR, rotr, + immZExt5>, + SRA_FM<2, 1>, ISA_MAXIS32R2; + def ROTRV : MMRel, shift_rotate_reg<"rotrv", GPR32Opnd, II_ROTRV, rotr>, + SRLV_FM<6, 1>, ISA_MAXIS32R2; +} + +/// Load and Store Instructions +/// aligned +def LB : LoadMemory<"lb", GPR32Opnd, mem_simm16, sextloadi8, II_LB>, MMRel, + LW_FM<0x20>; +def LBu : LoadMemory<"lbu", GPR32Opnd, mem_simm16, zextloadi8, II_LBU, + addrDefault>, MMRel, LW_FM<0x24>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def LH : LoadMemory<"lh", GPR32Opnd, mem_simm16, sextloadi16, II_LH, + addrDefault>, MMRel, LW_FM<0x21>; + def LHu : LoadMemory<"lhu", GPR32Opnd, mem_simm16, zextloadi16, II_LHU>, + MMRel, LW_FM<0x25>; + def LW : StdMMR6Rel, Load<"lw", GPR32Opnd, load, II_LW, addrDefault>, MMRel, + LW_FM<0x23>; +} +def SB : StdMMR6Rel, Store<"sb", GPR32Opnd, truncstorei8, II_SB>, MMRel, + LW_FM<0x28>; +def SH : Store<"sh", GPR32Opnd, truncstorei16, II_SH>, MMRel, LW_FM<0x29>; +let AdditionalPredicates = [NotInMicroMaxis] in { +def SW : Store<"sw", GPR32Opnd, store, II_SW>, MMRel, LW_FM<0x2b>; +} + +/// load/store left/right +let EncodingPredicates = [], // FIXME: Lack of HasStdEnc is probably a bug + AdditionalPredicates = [NotInMicroMaxis] in { +def LWL : LoadLeftRight<"lwl", MaxisLWL, GPR32Opnd, II_LWL>, LW_FM<0x22>, + ISA_MAXIS1_NOT_32R6_64R6; +def LWR : LoadLeftRight<"lwr", MaxisLWR, GPR32Opnd, II_LWR>, LW_FM<0x26>, + ISA_MAXIS1_NOT_32R6_64R6; +def SWL : StoreLeftRight<"swl", MaxisSWL, GPR32Opnd, II_SWL>, LW_FM<0x2a>, + ISA_MAXIS1_NOT_32R6_64R6; +def SWR : StoreLeftRight<"swr", MaxisSWR, GPR32Opnd, II_SWR>, LW_FM<0x2e>, + ISA_MAXIS1_NOT_32R6_64R6; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { +// COP2 Memory Instructions +def LWC2 : StdMMR6Rel, LW_FT2<"lwc2", COP2Opnd, II_LWC2, load>, LW_FM<0x32>, + ISA_MAXIS1_NOT_32R6_64R6; +def SWC2 : StdMMR6Rel, SW_FT2<"swc2", COP2Opnd, II_SWC2, store>, + LW_FM<0x3a>, ISA_MAXIS1_NOT_32R6_64R6; +def LDC2 : StdMMR6Rel, LW_FT2<"ldc2", COP2Opnd, II_LDC2, load>, LW_FM<0x36>, + ISA_MAXIS2_NOT_32R6_64R6; +def SDC2 : StdMMR6Rel, SW_FT2<"sdc2", COP2Opnd, II_SDC2, store>, + LW_FM<0x3e>, ISA_MAXIS2_NOT_32R6_64R6; + +// COP3 Memory Instructions +let DecoderNamespace = "COP3_" in { + def LWC3 : LW_FT3<"lwc3", COP3Opnd, II_LWC3, load>, LW_FM<0x33>; + def SWC3 : SW_FT3<"swc3", COP3Opnd, II_SWC3, store>, LW_FM<0x3b>; + def LDC3 : LW_FT3<"ldc3", COP3Opnd, II_LDC3, load>, LW_FM<0x37>, + ISA_MAXIS2; + def SDC3 : SW_FT3<"sdc3", COP3Opnd, II_SDC3, store>, LW_FM<0x3f>, + ISA_MAXIS2; +} + + def SYNC : MMRel, StdMMR6Rel, SYNC_FT<"sync">, SYNC_FM, ISA_MAXIS2; + def SYNCI : MMRel, StdMMR6Rel, SYNCI_FT<"synci">, SYNCI_FM, ISA_MAXIS32R2; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def TEQ : MMRel, TEQ_FT<"teq", GPR32Opnd, uimm10, II_TEQ>, TEQ_FM<0x34>, ISA_MAXIS2; + def TGE : MMRel, TEQ_FT<"tge", GPR32Opnd, uimm10, II_TGE>, TEQ_FM<0x30>, ISA_MAXIS2; + def TGEU : MMRel, TEQ_FT<"tgeu", GPR32Opnd, uimm10, II_TGEU>, TEQ_FM<0x31>, ISA_MAXIS2; + def TLT : MMRel, TEQ_FT<"tlt", GPR32Opnd, uimm10, II_TLT>, TEQ_FM<0x32>, ISA_MAXIS2; + def TLTU : MMRel, TEQ_FT<"tltu", GPR32Opnd, uimm10, II_TLTU>, TEQ_FM<0x33>, ISA_MAXIS2; + def TNE : MMRel, TEQ_FT<"tne", GPR32Opnd, uimm10, II_TNE>, TEQ_FM<0x36>, ISA_MAXIS2; +} + +def TEQI : MMRel, TEQI_FT<"teqi", GPR32Opnd, II_TEQI>, TEQI_FM<0xc>, + ISA_MAXIS2_NOT_32R6_64R6; +def TGEI : MMRel, TEQI_FT<"tgei", GPR32Opnd, II_TGEI>, TEQI_FM<0x8>, + ISA_MAXIS2_NOT_32R6_64R6; +def TGEIU : MMRel, TEQI_FT<"tgeiu", GPR32Opnd, II_TGEIU>, TEQI_FM<0x9>, + ISA_MAXIS2_NOT_32R6_64R6; +def TLTI : MMRel, TEQI_FT<"tlti", GPR32Opnd, II_TLTI>, TEQI_FM<0xa>, + ISA_MAXIS2_NOT_32R6_64R6; +def TTLTIU : MMRel, TEQI_FT<"tltiu", GPR32Opnd, II_TTLTIU>, TEQI_FM<0xb>, + ISA_MAXIS2_NOT_32R6_64R6; +def TNEI : MMRel, TEQI_FT<"tnei", GPR32Opnd, II_TNEI>, TEQI_FM<0xe>, + ISA_MAXIS2_NOT_32R6_64R6; + +let AdditionalPredicates = [NotInMicroMaxis] in { +def BREAK : MMRel, StdMMR6Rel, BRK_FT<"break">, BRK_FM<0xd>; +def SYSCALL : MMRel, SYS_FT<"syscall", uimm20, II_SYSCALL>, SYS_FM<0xc>; +} +def TRAP : TrapBase; +let AdditionalPredicates = [NotInMicroMaxis] in { +def SDBBP : MMRel, SYS_FT<"sdbbp", uimm20, II_SDBBP>, SDBBP_FM, ISA_MAXIS32_NOT_32R6_64R6; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def ERET : MMRel, ER_FT<"eret", II_ERET>, ER_FM<0x18, 0x0>, INSN_MAXIS3_32; + def ERETNC : MMRel, ER_FT<"eretnc", II_ERETNC>, ER_FM<0x18, 0x1>, ISA_MAXIS32R5; + def DERET : MMRel, ER_FT<"deret", II_DERET>, ER_FM<0x1f, 0x0>, ISA_MAXIS32; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def EI : MMRel, StdMMR6Rel, DEI_FT<"ei", GPR32Opnd, II_EI>, EI_FM<1>, ISA_MAXIS32R2; + def DI : MMRel, StdMMR6Rel, DEI_FT<"di", GPR32Opnd, II_DI>, EI_FM<0>, ISA_MAXIS32R2; +} + +let EncodingPredicates = [], // FIXME: Lack of HasStdEnc is probably a bug + AdditionalPredicates = [NotInMicroMaxis] in { +def WAIT : WAIT_FT<"wait">, WAIT_FM; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { +/// Load-linked, Store-conditional +def LL : LLBase<"ll", GPR32Opnd>, LW_FM<0x30>, PTR_32, ISA_MAXIS2_NOT_32R6_64R6; +def SC : SCBase<"sc", GPR32Opnd>, LW_FM<0x38>, PTR_32, ISA_MAXIS2_NOT_32R6_64R6; +} + +/// Jump and Branch Instructions +def J : MMRel, JumpFJ, FJ<2>, + AdditionalRequires<[RelocNotPIC, NotInMicroMaxis]>, IsBranch; +def JR : MMRel, IndirectBranch<"jr", GPR32Opnd>, MTLO_FM<8>, ISA_MAXIS1_NOT_32R6_64R6; +def BEQ : MMRel, CBranch<"beq", brtarget, seteq, GPR32Opnd>, BEQ_FM<4>; +def BEQL : MMRel, CBranchLikely<"beql", brtarget, GPR32Opnd>, + BEQ_FM<20>, ISA_MAXIS2_NOT_32R6_64R6; +def BNE : MMRel, CBranch<"bne", brtarget, setne, GPR32Opnd>, BEQ_FM<5>; +def BNEL : MMRel, CBranchLikely<"bnel", brtarget, GPR32Opnd>, + BEQ_FM<21>, ISA_MAXIS2_NOT_32R6_64R6; +def BGEZ : MMRel, CBranchZero<"bgez", brtarget, setge, GPR32Opnd>, + BGEZ_FM<1, 1>; +def BGEZL : MMRel, CBranchZeroLikely<"bgezl", brtarget, GPR32Opnd>, + BGEZ_FM<1, 3>, ISA_MAXIS2_NOT_32R6_64R6; +def BGTZ : MMRel, CBranchZero<"bgtz", brtarget, setgt, GPR32Opnd>, + BGEZ_FM<7, 0>; +def BGTZL : MMRel, CBranchZeroLikely<"bgtzl", brtarget, GPR32Opnd>, + BGEZ_FM<23, 0>, ISA_MAXIS2_NOT_32R6_64R6; +def BLEZ : MMRel, CBranchZero<"blez", brtarget, setle, GPR32Opnd>, + BGEZ_FM<6, 0>; +def BLEZL : MMRel, CBranchZeroLikely<"blezl", brtarget, GPR32Opnd>, + BGEZ_FM<22, 0>, ISA_MAXIS2_NOT_32R6_64R6; +def BLTZ : MMRel, CBranchZero<"bltz", brtarget, setlt, GPR32Opnd>, + BGEZ_FM<1, 0>; +def BLTZL : MMRel, CBranchZeroLikely<"bltzl", brtarget, GPR32Opnd>, + BGEZ_FM<1, 2>, ISA_MAXIS2_NOT_32R6_64R6; +def B : UncondBranch, + AdditionalRequires<[NotInMicroMaxis]>; + +def JAL : MMRel, JumpLink<"jal", calltarget>, FJ<3>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def JALR : JumpLinkReg<"jalr", GPR32Opnd>, JALR_FM; + def JALRPseudo : JumpLinkRegPseudo; +} + +def JALX : MMRel, JumpLink<"jalx", calltarget>, FJ<0x1D>, + ISA_MAXIS32_NOT_32R6_64R6; +def BGEZAL : MMRel, BGEZAL_FT<"bgezal", brtarget, GPR32Opnd>, BGEZAL_FM<0x11>, + ISA_MAXIS1_NOT_32R6_64R6; +def BGEZALL : MMRel, BGEZAL_FT<"bgezall", brtarget, GPR32Opnd>, + BGEZAL_FM<0x13>, ISA_MAXIS2_NOT_32R6_64R6; +def BLTZAL : MMRel, BGEZAL_FT<"bltzal", brtarget, GPR32Opnd>, BGEZAL_FM<0x10>, + ISA_MAXIS1_NOT_32R6_64R6; +def BLTZALL : MMRel, BGEZAL_FT<"bltzall", brtarget, GPR32Opnd>, + BGEZAL_FM<0x12>, ISA_MAXIS2_NOT_32R6_64R6; +def BAL_BR : BAL_BR_Pseudo; + +let AdditionalPredicates = [NotInMaxis16Mode, NotInMicroMaxis] in { + def TAILCALL : TailCall; +} + +def TAILCALLREG : TailCallReg; + +// Indirect branches are matched as PseudoIndirectBranch/PseudoIndirectBranch64 +// then are expanded to JR, JR64, JALR, or JALR64 depending on the ISA. +class PseudoIndirectBranchBase : + MaxisPseudo<(outs), (ins RO:$rs), [(brind RO:$rs)], + II_IndirectBranchPseudo> { + let isTerminator=1; + let isBarrier=1; + let hasDelaySlot = 1; + let isBranch = 1; + let isIndirectBranch = 1; + bit isCTI = 1; + let Predicates = [NotInMaxis16Mode]; +} + +def PseudoIndirectBranch : PseudoIndirectBranchBase; + +// Return instructions are matched as a RetRA instruction, then are expanded +// into PseudoReturn/PseudoReturn64 after register allocation. Finally, +// MaxisAsmPrinter expands this into JR, JR64, JALR, or JALR64 depending on the +// ISA. +class PseudoReturnBase : MaxisPseudo<(outs), (ins RO:$rs), + [], II_ReturnPseudo> { + let isTerminator = 1; + let isBarrier = 1; + let hasDelaySlot = 1; + let isReturn = 1; + let isCodeGenOnly = 1; + let hasCtrlDep = 1; + let hasExtraSrcRegAllocReq = 1; + bit isCTI = 1; +} + +def PseudoReturn : PseudoReturnBase; + +// Exception handling related node and instructions. +// The conversion sequence is: +// ISD::EH_RETURN -> MaxisISD::EH_RETURN -> +// MAXISeh_return -> (stack change + indirect branch) +// +// MAXISeh_return takes the place of regular return instruction +// but takes two arguments (V1, V0) which are used for storing +// the offset and return address respectively. +def SDT_MaxisEHRET : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisPtrTy<1>]>; + +def MAXISehret : SDNode<"MaxisISD::EH_RETURN", SDT_MaxisEHRET, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; + +let Uses = [V0, V1], isTerminator = 1, isReturn = 1, isBarrier = 1, isCTI = 1 in { + def MAXISeh_return32 : MaxisPseudo<(outs), (ins GPR32:$spoff, GPR32:$dst), + [(MAXISehret GPR32:$spoff, GPR32:$dst)]>; + def MAXISeh_return64 : MaxisPseudo<(outs), (ins GPR64:$spoff, + GPR64:$dst), + [(MAXISehret GPR64:$spoff, GPR64:$dst)]>; +} + +/// Multiply and Divide Instructions. +def MULT : MMRel, Mult<"mult", II_MULT, GPR32Opnd, [HI0, LO0]>, + MULT_FM<0, 0x18>, ISA_MAXIS1_NOT_32R6_64R6; +def MULTu : MMRel, Mult<"multu", II_MULTU, GPR32Opnd, [HI0, LO0]>, + MULT_FM<0, 0x19>, ISA_MAXIS1_NOT_32R6_64R6; +let AdditionalPredicates = [NotInMicroMaxis] in { + def SDIV : MMRel, Div<"div", II_DIV, GPR32Opnd, [HI0, LO0]>, + MULT_FM<0, 0x1a>, ISA_MAXIS1_NOT_32R6_64R6; + def UDIV : MMRel, Div<"divu", II_DIVU, GPR32Opnd, [HI0, LO0]>, + MULT_FM<0, 0x1b>, ISA_MAXIS1_NOT_32R6_64R6; +} +def MTHI : MMRel, MoveToLOHI<"mthi", GPR32Opnd, [HI0]>, MTLO_FM<0x11>, + ISA_MAXIS1_NOT_32R6_64R6; +def MTLO : MMRel, MoveToLOHI<"mtlo", GPR32Opnd, [LO0]>, MTLO_FM<0x13>, + ISA_MAXIS1_NOT_32R6_64R6; +let EncodingPredicates = [], // FIXME: Lack of HasStdEnc is probably a bug + AdditionalPredicates = [NotInMicroMaxis] in { +def MFHI : MMRel, MoveFromLOHI<"mfhi", GPR32Opnd, AC0>, MFLO_FM<0x10>, + ISA_MAXIS1_NOT_32R6_64R6; +def MFLO : MMRel, MoveFromLOHI<"mflo", GPR32Opnd, AC0>, MFLO_FM<0x12>, + ISA_MAXIS1_NOT_32R6_64R6; +} + +/// Sign Ext In Register Instructions. +def SEB : MMRel, StdMMR6Rel, SignExtInReg<"seb", i8, GPR32Opnd, II_SEB>, + SEB_FM<0x10, 0x20>, ISA_MAXIS32R2; +def SEH : MMRel, StdMMR6Rel, SignExtInReg<"seh", i16, GPR32Opnd, II_SEH>, + SEB_FM<0x18, 0x20>, ISA_MAXIS32R2; + +/// Count Leading +def CLZ : MMRel, CountLeading0<"clz", GPR32Opnd, II_CLZ>, CLO_FM<0x20>, + ISA_MAXIS32_NOT_32R6_64R6; +def CLO : MMRel, CountLeading1<"clo", GPR32Opnd, II_CLO>, CLO_FM<0x21>, + ISA_MAXIS32_NOT_32R6_64R6; + +let AdditionalPredicates = [NotInMicroMaxis] in { + /// Word Swap Bytes Within Halfwords + def WSBH : MMRel, SubwordSwap<"wsbh", GPR32Opnd, II_WSBH>, SEB_FM<2, 0x20>, + ISA_MAXIS32R2; +} + +/// No operation. +def NOP : PseudoSE<(outs), (ins), []>, PseudoInstExpansion<(SLL ZERO, ZERO, 0)>; + +// FrameIndexes are legalized when they are operands from load/store +// instructions. The same not happens for stack address copies, so an +// add op with mem ComplexPattern is used and the stack address copy +// can be matched. It's similar to Sparc LEA_ADDRi +def LEA_ADDiu : MMRel, EffectiveAddress<"addiu", GPR32Opnd>, LW_FM<9>; + +// MADD*/MSUB* +def MADD : MMRel, MArithR<"madd", II_MADD, 1>, MULT_FM<0x1c, 0>, + ISA_MAXIS32_NOT_32R6_64R6; +def MADDU : MMRel, MArithR<"maddu", II_MADDU, 1>, MULT_FM<0x1c, 1>, + ISA_MAXIS32_NOT_32R6_64R6; +def MSUB : MMRel, MArithR<"msub", II_MSUB>, MULT_FM<0x1c, 4>, + ISA_MAXIS32_NOT_32R6_64R6; +def MSUBU : MMRel, MArithR<"msubu", II_MSUBU>, MULT_FM<0x1c, 5>, + ISA_MAXIS32_NOT_32R6_64R6; + +let AdditionalPredicates = [NotDSP] in { +def PseudoMULT : MultDivPseudo, + ISA_MAXIS1_NOT_32R6_64R6; +def PseudoMULTu : MultDivPseudo, + ISA_MAXIS1_NOT_32R6_64R6; +def PseudoMFHI : PseudoMFLOHI, ISA_MAXIS1_NOT_32R6_64R6; +def PseudoMFLO : PseudoMFLOHI, ISA_MAXIS1_NOT_32R6_64R6; +def PseudoMTLOHI : PseudoMTLOHI, ISA_MAXIS1_NOT_32R6_64R6; +def PseudoMADD : MAddSubPseudo, + ISA_MAXIS32_NOT_32R6_64R6; +def PseudoMADDU : MAddSubPseudo, + ISA_MAXIS32_NOT_32R6_64R6; +def PseudoMSUB : MAddSubPseudo, + ISA_MAXIS32_NOT_32R6_64R6; +def PseudoMSUBU : MAddSubPseudo, + ISA_MAXIS32_NOT_32R6_64R6; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + def PseudoSDIV : MultDivPseudo, ISA_MAXIS1_NOT_32R6_64R6; + def PseudoUDIV : MultDivPseudo, ISA_MAXIS1_NOT_32R6_64R6; + def RDHWR : MMRel, ReadHardware, RDHWR_FM; + // TODO: Add '0 < pos+size <= 32' constraint check to ext instruction + def EXT : MMRel, StdMMR6Rel, ExtBase<"ext", GPR32Opnd, uimm5, uimm5_plus1, + immZExt5, immZExt5Plus1, MaxisExt>, + EXT_FM<0>; + def INS : MMRel, StdMMR6Rel, InsBase<"ins", GPR32Opnd, uimm5, + uimm5_inssize_plus1, immZExt5, + immZExt5Plus1>, + EXT_FM<4>; +} +/// Move Control Registers From/To CPU Registers +let AdditionalPredicates = [NotInMicroMaxis] in { + def MTC0 : MTC3OP<"mtc0", COP0Opnd, GPR32Opnd, II_MTC0>, MFC3OP_FM<0x10, 4>, + ISA_MAXIS32; + def MFC0 : MFC3OP<"mfc0", GPR32Opnd, COP0Opnd, II_MFC0>, MFC3OP_FM<0x10, 0>, + ISA_MAXIS32; +} +def MFC2 : MFC3OP<"mfc2", GPR32Opnd, COP2Opnd, II_MFC2>, MFC3OP_FM<0x12, 0>; +def MTC2 : MTC3OP<"mtc2", COP2Opnd, GPR32Opnd, II_MTC2>, MFC3OP_FM<0x12, 4>; + +class Barrier : + InstSE<(outs), (ins), asmstr, [], itin, FrmOther, asmstr>; + +def SSNOP : MMRel, StdMMR6Rel, Barrier<"ssnop", II_SSNOP>, BARRIER_FM<1>; +def EHB : MMRel, Barrier<"ehb", II_EHB>, BARRIER_FM<3>; + +let isCTI = 1 in +def PAUSE : MMRel, StdMMR6Rel, Barrier<"pause", II_PAUSE>, BARRIER_FM<5>, + ISA_MAXIS32R2; + +// JR_HB and JALR_HB are defined here using the new style naming +// scheme because some of this code is shared with Maxis32r6InstrInfo.td +// and because of that it doesn't follow the naming convention of the +// rest of the file. To avoid a mixture of old vs new style, the new +// style was chosen. +class JR_HB_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins GPROpnd:$rs); + string AsmString = !strconcat(instr_asm, "\t$rs"); + list Pattern = []; +} + +class JALR_HB_DESC_BASE { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs"); + list Pattern = []; +} + +class JR_HB_DESC : InstSE<(outs), (ins), "", [], II_JR_HB, FrmJ>, + JR_HB_DESC_BASE<"jr.hb", GPR32Opnd> { + let isBranch=1; + let isIndirectBranch=1; + let hasDelaySlot=1; + let isTerminator=1; + let isBarrier=1; + bit isCTI = 1; +} + +class JALR_HB_DESC : InstSE<(outs), (ins), "", [], II_JALR_HB, FrmJ>, + JALR_HB_DESC_BASE<"jalr.hb", GPR32Opnd> { + let isIndirectBranch=1; + let hasDelaySlot=1; + bit isCTI = 1; +} + +class JR_HB_ENC : JR_HB_FM<8>; +class JALR_HB_ENC : JALR_HB_FM<9>; + +def JR_HB : JR_HB_DESC, JR_HB_ENC, ISA_MAXIS32_NOT_32R6_64R6; +def JALR_HB : JALR_HB_DESC, JALR_HB_ENC, ISA_MAXIS32; + +class TLB : + InstSE<(outs), (ins), asmstr, [], itin, FrmOther, asmstr>; +let AdditionalPredicates = [NotInMicroMaxis] in { +def TLBP : MMRel, TLB<"tlbp", II_TLBP>, COP0_TLB_FM<0x08>; +def TLBR : MMRel, TLB<"tlbr", II_TLBR>, COP0_TLB_FM<0x01>; +def TLBWI : MMRel, TLB<"tlbwi", II_TLBWI>, COP0_TLB_FM<0x02>; +def TLBWR : MMRel, TLB<"tlbwr", II_TLBWR>, COP0_TLB_FM<0x06>; +} +class CacheOp : + InstSE<(outs), (ins MemOpnd:$addr, uimm5:$hint), + !strconcat(instr_asm, "\t$hint, $addr"), [], itin, FrmOther, + instr_asm> { + let DecoderMethod = "DecodeCacheOp"; +} + +def CACHE : MMRel, CacheOp<"cache", mem, II_CACHE>, CACHEOP_FM<0b101111>, + INSN_MAXIS3_32_NOT_32R6_64R6; +def PREF : MMRel, CacheOp<"pref", mem, II_PREF>, CACHEOP_FM<0b110011>, + INSN_MAXIS3_32_NOT_32R6_64R6; + +def ROL : MaxisAsmPseudoInst<(outs), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), + "rol\t$rs, $rt, $rd">; +def ROLImm : MaxisAsmPseudoInst<(outs), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), + "rol\t$rs, $rt, $imm">; +def : MaxisInstAlias<"rol $rd, $rs", + (ROL GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>; +def : MaxisInstAlias<"rol $rd, $imm", + (ROLImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>; + +def ROR : MaxisAsmPseudoInst<(outs), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), + "ror\t$rs, $rt, $rd">; +def RORImm : MaxisAsmPseudoInst<(outs), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), + "ror\t$rs, $rt, $imm">; +def : MaxisInstAlias<"ror $rd, $rs", + (ROR GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>; +def : MaxisInstAlias<"ror $rd, $imm", + (RORImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>; + +def DROL : MaxisAsmPseudoInst<(outs), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), + "drol\t$rs, $rt, $rd">, ISA_MAXIS64; +def DROLImm : MaxisAsmPseudoInst<(outs), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), + "drol\t$rs, $rt, $imm">, ISA_MAXIS64; +def : MaxisInstAlias<"drol $rd, $rs", + (DROL GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, ISA_MAXIS64; +def : MaxisInstAlias<"drol $rd, $imm", + (DROLImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>, ISA_MAXIS64; + +def DROR : MaxisAsmPseudoInst<(outs), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), + "dror\t$rs, $rt, $rd">, ISA_MAXIS64; +def DRORImm : MaxisAsmPseudoInst<(outs), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), + "dror\t$rs, $rt, $imm">, ISA_MAXIS64; +def : MaxisInstAlias<"dror $rd, $rs", + (DROR GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, ISA_MAXIS64; +def : MaxisInstAlias<"dror $rd, $imm", + (DRORImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>, ISA_MAXIS64; + +def ABSMacro : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs), + "abs\t$rd, $rs">; + +def SEQMacro : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt), + "seq $rd, $rs, $rt">, NOT_ASE_CNMAXIS; + +def : MaxisInstAlias<"seq $rd, $rs", + (SEQMacro GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, + NOT_ASE_CNMAXIS; + +def SEQIMacro : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), + (ins GPR32Opnd:$rs, simm32_relaxed:$imm), + "seq $rd, $rs, $imm">, NOT_ASE_CNMAXIS; + +def : MaxisInstAlias<"seq $rd, $imm", + (SEQIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32:$imm), 0>, + NOT_ASE_CNMAXIS; + +def MULImmMacro : MaxisAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, + simm32_relaxed:$imm), + "mul\t$rd, $rs, $imm">, + ISA_MAXIS1_NOT_32R6_64R6; +def MULOMacro : MaxisAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, + GPR32Opnd:$rt), + "mulo\t$rd, $rs, $rt">, + ISA_MAXIS1_NOT_32R6_64R6; +def MULOUMacro : MaxisAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, + GPR32Opnd:$rt), + "mulou\t$rd, $rs, $rt">, + ISA_MAXIS1_NOT_32R6_64R6; + +//===----------------------------------------------------------------------===// +// Instruction aliases +//===----------------------------------------------------------------------===// + +multiclass OneOrTwoOperandMacroImmediateAlias { + def : MaxisInstAlias; + def : MaxisInstAlias; +} + +def : MaxisInstAlias<"move $dst, $src", + (OR GPR32Opnd:$dst, GPR32Opnd:$src, ZERO), 1>, + GPR_32 { + let AdditionalPredicates = [NotInMicroMaxis]; +} +def : MaxisInstAlias<"move $dst, $src", + (ADDu GPR32Opnd:$dst, GPR32Opnd:$src, ZERO), 1>, + GPR_32 { + let AdditionalPredicates = [NotInMicroMaxis]; +} +def : MaxisInstAlias<"bal $offset", (BGEZAL ZERO, brtarget:$offset), 0>, + ISA_MAXIS1_NOT_32R6_64R6; + +def : MaxisInstAlias<"j $rs", (JR GPR32Opnd:$rs), 0>; +let Predicates = [NotInMicroMaxis] in { +def : MaxisInstAlias<"jalr $rs", (JALR RA, GPR32Opnd:$rs), 0>; +} +def : MaxisInstAlias<"jalr.hb $rs", (JALR_HB RA, GPR32Opnd:$rs), 1>, ISA_MAXIS32; +def : MaxisInstAlias<"neg $rt, $rs", + (SUB GPR32Opnd:$rt, ZERO, GPR32Opnd:$rs), 1>; +def : MaxisInstAlias<"neg $rt", + (SUB GPR32Opnd:$rt, ZERO, GPR32Opnd:$rt), 1>; +def : MaxisInstAlias<"negu $rt, $rs", + (SUBu GPR32Opnd:$rt, ZERO, GPR32Opnd:$rs), 1>; +def : MaxisInstAlias<"negu $rt", + (SUBu GPR32Opnd:$rt, ZERO, GPR32Opnd:$rt), 1>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias< + "sgt $rd, $rs, $rt", + (SLT GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias< + "sgt $rs, $rt", + (SLT GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias< + "sgtu $rd, $rs, $rt", + (SLTu GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias< + "sgtu $$rs, $rt", + (SLTu GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias< + "not $rt, $rs", + (NOR GPR32Opnd:$rt, GPR32Opnd:$rs, ZERO), 0>; + def : MaxisInstAlias< + "not $rt", + (NOR GPR32Opnd:$rt, GPR32Opnd:$rt, ZERO), 0>; + def : MaxisInstAlias<"nop", (SLL ZERO, ZERO, 0), 1>; + + defm : OneOrTwoOperandMacroImmediateAlias<"add", ADDi>, ISA_MAXIS1_NOT_32R6_64R6; + + defm : OneOrTwoOperandMacroImmediateAlias<"addu", ADDiu>; + + defm : OneOrTwoOperandMacroImmediateAlias<"and", ANDi>, GPR_32; + + defm : OneOrTwoOperandMacroImmediateAlias<"or", ORi>, GPR_32; + + defm : OneOrTwoOperandMacroImmediateAlias<"xor", XORi>, GPR_32; + + defm : OneOrTwoOperandMacroImmediateAlias<"slt", SLTi>, GPR_32; + + defm : OneOrTwoOperandMacroImmediateAlias<"sltu", SLTiu>, GPR_32; +} +def : MaxisInstAlias<"mfc0 $rt, $rd", (MFC0 GPR32Opnd:$rt, COP0Opnd:$rd, 0), 0>; +def : MaxisInstAlias<"mtc0 $rt, $rd", (MTC0 COP0Opnd:$rd, GPR32Opnd:$rt, 0), 0>; +def : MaxisInstAlias<"mfc2 $rt, $rd", (MFC2 GPR32Opnd:$rt, COP2Opnd:$rd, 0), 0>; +def : MaxisInstAlias<"mtc2 $rt, $rd", (MTC2 COP2Opnd:$rd, GPR32Opnd:$rt, 0), 0>; +let AdditionalPredicates = [NotInMicroMaxis] in { +def : MaxisInstAlias<"b $offset", (BEQ ZERO, ZERO, brtarget:$offset), 0>; +} +def : MaxisInstAlias<"bnez $rs,$offset", + (BNE GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>; +def : MaxisInstAlias<"bnezl $rs,$offset", + (BNEL GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>; +def : MaxisInstAlias<"beqz $rs,$offset", + (BEQ GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>; +def : MaxisInstAlias<"beqzl $rs,$offset", + (BEQL GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"syscall", (SYSCALL 0), 1>; +} + +def : MaxisInstAlias<"break", (BREAK 0, 0), 1>; +def : MaxisInstAlias<"break $imm", (BREAK uimm10:$imm, 0), 1>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"ei", (EI ZERO), 1>, ISA_MAXIS32R2; + def : MaxisInstAlias<"di", (DI ZERO), 1>, ISA_MAXIS32R2; +} +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"teq $rs, $rt", + (TEQ GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MAXIS2; + def : MaxisInstAlias<"tge $rs, $rt", + (TGE GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MAXIS2; + def : MaxisInstAlias<"tgeu $rs, $rt", + (TGEU GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MAXIS2; + def : MaxisInstAlias<"tlt $rs, $rt", + (TLT GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MAXIS2; + def : MaxisInstAlias<"tltu $rs, $rt", + (TLTU GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MAXIS2; + def : MaxisInstAlias<"tne $rs, $rt", + (TNE GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MAXIS2; +} +def : MaxisInstAlias<"sub, $rd, $rs, $imm", + (ADDi GPR32Opnd:$rd, GPR32Opnd:$rs, + InvertedImOperand:$imm), 0>, ISA_MAXIS1_NOT_32R6_64R6; +def : MaxisInstAlias<"sub $rs, $imm", + (ADDi GPR32Opnd:$rs, GPR32Opnd:$rs, InvertedImOperand:$imm), + 0>, ISA_MAXIS1_NOT_32R6_64R6; +def : MaxisInstAlias<"subu, $rd, $rs, $imm", + (ADDiu GPR32Opnd:$rd, GPR32Opnd:$rs, + InvertedImOperand:$imm), 0>; +def : MaxisInstAlias<"subu $rs, $imm", (ADDiu GPR32Opnd:$rs, GPR32Opnd:$rs, + InvertedImOperand:$imm), 0>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"sll $rd, $rt, $rs", + (SLLV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias<"sra $rd, $rt, $rs", + (SRAV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias<"srl $rd, $rt, $rs", + (SRLV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias<"sll $rd, $rt", + (SLLV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; + def : MaxisInstAlias<"sra $rd, $rt", + (SRAV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; + def : MaxisInstAlias<"srl $rd, $rt", + (SRLV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; + def : MaxisInstAlias<"seh $rd", (SEH GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, + ISA_MAXIS32R2; + def : MaxisInstAlias<"seb $rd", (SEB GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, + ISA_MAXIS32R2; +} +def : MaxisInstAlias<"sdbbp", (SDBBP 0)>, ISA_MAXIS32_NOT_32R6_64R6; +def : MaxisInstAlias<"sync", + (SYNC 0), 1>, ISA_MAXIS2; + +def : MaxisInstAlias<"mulo $rs, $rt", + (MULOMacro GPR32Opnd:$rs, GPR32Opnd:$rs, GPR32Opnd:$rt), 0>, + ISA_MAXIS1_NOT_32R6_64R6; +def : MaxisInstAlias<"mulou $rs, $rt", + (MULOUMacro GPR32Opnd:$rs, GPR32Opnd:$rs, GPR32Opnd:$rt), 0>, + ISA_MAXIS1_NOT_32R6_64R6; + +//===----------------------------------------------------------------------===// +// Assembler Pseudo Instructions +//===----------------------------------------------------------------------===// + +// We use uimm32_coerced to accept a 33 bit signed number that is rendered into +// a 32 bit number. +class LoadImmediate32 : + MaxisAsmPseudoInst<(outs RO:$rt), (ins Od:$imm32), + !strconcat(instr_asm, "\t$rt, $imm32")> ; +def LoadImm32 : LoadImmediate32<"li", uimm32_coerced, GPR32Opnd>; + +class LoadAddressFromReg32 : + MaxisAsmPseudoInst<(outs RO:$rt), (ins MemOpnd:$addr), + !strconcat(instr_asm, "\t$rt, $addr")> ; +def LoadAddrReg32 : LoadAddressFromReg32<"la", mem, GPR32Opnd>; + +class LoadAddressFromImm32 : + MaxisAsmPseudoInst<(outs RO:$rt), (ins Od:$imm32), + !strconcat(instr_asm, "\t$rt, $imm32")> ; +def LoadAddrImm32 : LoadAddressFromImm32<"la", i32imm, GPR32Opnd>; + +def JalTwoReg : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs), + "jal\t$rd, $rs"> ; +def JalOneReg : MaxisAsmPseudoInst<(outs), (ins GPR32Opnd:$rs), + "jal\t$rs"> ; + +class NORIMM_DESC_BASE : + MaxisAsmPseudoInst<(outs RO:$rs), (ins RO:$rt, Imm:$imm), + "nor\t$rs, $rt, $imm">; +def NORImm : NORIMM_DESC_BASE, GPR_32; +def : MaxisInstAlias<"nor\t$rs, $imm", (NORImm GPR32Opnd:$rs, GPR32Opnd:$rs, + simm32_relaxed:$imm)>, GPR_32; + +let hasDelaySlot = 1, isCTI = 1 in { +def BneImm : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), + (ins imm64:$imm64, brtarget:$offset), + "bne\t$rt, $imm64, $offset">; +def BeqImm : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), + (ins imm64:$imm64, brtarget:$offset), + "beq\t$rt, $imm64, $offset">; + +class CondBranchPseudo : + MaxisAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, + brtarget:$offset), + !strconcat(instr_asm, "\t$rs, $rt, $offset")>; +} + +def BLT : CondBranchPseudo<"blt">; +def BLE : CondBranchPseudo<"ble">; +def BGE : CondBranchPseudo<"bge">; +def BGT : CondBranchPseudo<"bgt">; +def BLTU : CondBranchPseudo<"bltu">; +def BLEU : CondBranchPseudo<"bleu">; +def BGEU : CondBranchPseudo<"bgeu">; +def BGTU : CondBranchPseudo<"bgtu">; +def BLTL : CondBranchPseudo<"bltl">, ISA_MAXIS2_NOT_32R6_64R6; +def BLEL : CondBranchPseudo<"blel">, ISA_MAXIS2_NOT_32R6_64R6; +def BGEL : CondBranchPseudo<"bgel">, ISA_MAXIS2_NOT_32R6_64R6; +def BGTL : CondBranchPseudo<"bgtl">, ISA_MAXIS2_NOT_32R6_64R6; +def BLTUL: CondBranchPseudo<"bltul">, ISA_MAXIS2_NOT_32R6_64R6; +def BLEUL: CondBranchPseudo<"bleul">, ISA_MAXIS2_NOT_32R6_64R6; +def BGEUL: CondBranchPseudo<"bgeul">, ISA_MAXIS2_NOT_32R6_64R6; +def BGTUL: CondBranchPseudo<"bgtul">, ISA_MAXIS2_NOT_32R6_64R6; + +let isCTI = 1 in +class CondBranchImmPseudo : + MaxisAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, imm64:$imm, brtarget:$offset), + !strconcat(instr_asm, "\t$rs, $imm, $offset")>; + +def BEQLImmMacro : CondBranchImmPseudo<"beql">, ISA_MAXIS2_NOT_32R6_64R6; +def BNELImmMacro : CondBranchImmPseudo<"bnel">, ISA_MAXIS2_NOT_32R6_64R6; + +def BLTImmMacro : CondBranchImmPseudo<"blt">; +def BLEImmMacro : CondBranchImmPseudo<"ble">; +def BGEImmMacro : CondBranchImmPseudo<"bge">; +def BGTImmMacro : CondBranchImmPseudo<"bgt">; +def BLTUImmMacro : CondBranchImmPseudo<"bltu">; +def BLEUImmMacro : CondBranchImmPseudo<"bleu">; +def BGEUImmMacro : CondBranchImmPseudo<"bgeu">; +def BGTUImmMacro : CondBranchImmPseudo<"bgtu">; +def BLTLImmMacro : CondBranchImmPseudo<"bltl">, ISA_MAXIS2_NOT_32R6_64R6; +def BLELImmMacro : CondBranchImmPseudo<"blel">, ISA_MAXIS2_NOT_32R6_64R6; +def BGELImmMacro : CondBranchImmPseudo<"bgel">, ISA_MAXIS2_NOT_32R6_64R6; +def BGTLImmMacro : CondBranchImmPseudo<"bgtl">, ISA_MAXIS2_NOT_32R6_64R6; +def BLTULImmMacro : CondBranchImmPseudo<"bltul">, ISA_MAXIS2_NOT_32R6_64R6; +def BLEULImmMacro : CondBranchImmPseudo<"bleul">, ISA_MAXIS2_NOT_32R6_64R6; +def BGEULImmMacro : CondBranchImmPseudo<"bgeul">, ISA_MAXIS2_NOT_32R6_64R6; +def BGTULImmMacro : CondBranchImmPseudo<"bgtul">, ISA_MAXIS2_NOT_32R6_64R6; + +// FIXME: Predicates are removed because instructions are matched regardless of +// predicates, because PredicateControl was not in the hierarchy. This was +// done to emit more precise error message from expansion function. +// Once the tablegen-erated errors are made better, this needs to be fixed and +// predicates needs to be restored. + +def SDivMacro : MaxisAsmPseudoInst<(outs GPR32NonZeroOpnd:$rd), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt), + "div\t$rd, $rs, $rt">, + ISA_MAXIS1_NOT_32R6_64R6; +def SDivIMacro : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), + (ins GPR32Opnd:$rs, simm32:$imm), + "div\t$rd, $rs, $imm">, + ISA_MAXIS1_NOT_32R6_64R6; +def UDivMacro : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), + (ins GPR32Opnd:$rs, GPR32Opnd:$rt), + "divu\t$rd, $rs, $rt">, + ISA_MAXIS1_NOT_32R6_64R6; +def UDivIMacro : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), + (ins GPR32Opnd:$rs, simm32:$imm), + "divu\t$rd, $rs, $imm">, + ISA_MAXIS1_NOT_32R6_64R6; + + +def : MaxisInstAlias<"div $rs, $rt", (SDIV GPR32ZeroOpnd:$rs, + GPR32Opnd:$rt), 0>, + ISA_MAXIS1_NOT_32R6_64R6; +def : MaxisInstAlias<"div $rs, $rt", (SDivMacro GPR32NonZeroOpnd:$rs, + GPR32NonZeroOpnd:$rs, + GPR32Opnd:$rt), 0>, + ISA_MAXIS1_NOT_32R6_64R6; +def : MaxisInstAlias<"div $rd, $imm", (SDivIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, + simm32:$imm), 0>, + ISA_MAXIS1_NOT_32R6_64R6; + +def : MaxisInstAlias<"divu $rt, $rs", (UDIV GPR32ZeroOpnd:$rt, + GPR32Opnd:$rs), 0>, + ISA_MAXIS1_NOT_32R6_64R6; +def : MaxisInstAlias<"divu $rt, $rs", (UDivMacro GPR32NonZeroOpnd:$rt, + GPR32NonZeroOpnd:$rt, + GPR32Opnd:$rs), 0>, + ISA_MAXIS1_NOT_32R6_64R6; + +def : MaxisInstAlias<"divu $rd, $imm", (UDivIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, + simm32:$imm), 0>, + ISA_MAXIS1_NOT_32R6_64R6; + +def Ulh : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), + "ulh\t$rt, $addr">; //, ISA_MAXIS1_NOT_32R6_64R6; + +def Ulhu : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), + "ulhu\t$rt, $addr">; //, ISA_MAXIS1_NOT_32R6_64R6; + +def Ulw : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), + "ulw\t$rt, $addr">; //, ISA_MAXIS1_NOT_32R6_64R6; + +def Ush : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), + "ush\t$rt, $addr">; //, ISA_MAXIS1_NOT_32R6_64R6; + +def Usw : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), + "usw\t$rt, $addr">; //, ISA_MAXIS1_NOT_32R6_64R6; + +def LDMacro : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), + (ins mem_simm16:$addr), "ld $rt, $addr">, + ISA_MAXIS1_NOT_MAXIS3; +def SDMacro : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), + (ins mem_simm16:$addr), "sd $rt, $addr">, + ISA_MAXIS1_NOT_MAXIS3; +//===----------------------------------------------------------------------===// +// Arbitrary patterns that map to one or more instructions +//===----------------------------------------------------------------------===// + +// Load/store pattern templates. +class LoadRegImmPat : + MaxisPat<(ValTy (Node addrRegImm:$a)), (LoadInst addrRegImm:$a)>; + +class StoreRegImmPat : + MaxisPat<(store ValTy:$v, addrRegImm:$a), (StoreInst ValTy:$v, addrRegImm:$a)>; + +// Materialize constants. +multiclass MaterializeImms { + +// Constant synthesis previously relied on the ordering of the patterns below. +// By making the predicates they use non-overlapping, the patterns were +// reordered so that the effect of the newly introduced predicates can be +// observed. + +// Arbitrary immediates +def : MaxisPat<(VT LUiORiPred:$imm), (ORiOp (LUiOp (HI16 imm:$imm)), (LO16 imm:$imm))>; + +// Bits 32-16 set, sign/zero extended. +def : MaxisPat<(VT LUiPred:$imm), (LUiOp (HI16 imm:$imm))>; + +// Small immediates +def : MaxisPat<(VT ORiPred:$imm), (ORiOp ZEROReg, imm:$imm)>; +def : MaxisPat<(VT immSExt16:$imm), (ADDiuOp ZEROReg, imm:$imm)>; +} + +let AdditionalPredicates = [NotInMicroMaxis] in + defm : MaterializeImms; + +// Carry MaxisPatterns +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisPat<(subc GPR32:$lhs, GPR32:$rhs), + (SUBu GPR32:$lhs, GPR32:$rhs)>; +} +def : MaxisPat<(addc GPR32:$lhs, GPR32:$rhs), + (ADDu GPR32:$lhs, GPR32:$rhs)>, ASE_NOT_DSP; +def : MaxisPat<(addc GPR32:$src, immSExt16:$imm), + (ADDiu GPR32:$src, imm:$imm)>, ASE_NOT_DSP; + +// Support multiplication for pre-Maxis32 targets that don't have +// the MUL instruction. +def : MaxisPat<(mul GPR32:$lhs, GPR32:$rhs), + (PseudoMFLO (PseudoMULT GPR32:$lhs, GPR32:$rhs))>, + ISA_MAXIS1_NOT_32R6_64R6; + +// SYNC +def : MaxisPat<(MaxisSync (i32 immz)), + (SYNC 0)>, ISA_MAXIS2; + +// Call +def : MaxisPat<(MaxisJmpLink (i32 texternalsym:$dst)), + (JAL texternalsym:$dst)>; +//def : MaxisPat<(MaxisJmpLink GPR32:$dst), +// (JALR GPR32:$dst)>; + +// Tail call +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisPat<(MaxisTailCall (iPTR tglobaladdr:$dst)), + (TAILCALL tglobaladdr:$dst)>; + def : MaxisPat<(MaxisTailCall (iPTR texternalsym:$dst)), + (TAILCALL texternalsym:$dst)>; +} +// hi/lo relocs +multiclass MaxisHiLoRelocs { + def : MaxisPat<(MaxisHi tglobaladdr:$in), (Lui tglobaladdr:$in)>; + def : MaxisPat<(MaxisHi tblockaddress:$in), (Lui tblockaddress:$in)>; + def : MaxisPat<(MaxisHi tjumptable:$in), (Lui tjumptable:$in)>; + def : MaxisPat<(MaxisHi tconstpool:$in), (Lui tconstpool:$in)>; + def : MaxisPat<(MaxisHi tglobaltlsaddr:$in), (Lui tglobaltlsaddr:$in)>; + def : MaxisPat<(MaxisHi texternalsym:$in), (Lui texternalsym:$in)>; + + def : MaxisPat<(MaxisLo tglobaladdr:$in), (Addiu ZeroReg, tglobaladdr:$in)>; + def : MaxisPat<(MaxisLo tblockaddress:$in), + (Addiu ZeroReg, tblockaddress:$in)>; + def : MaxisPat<(MaxisLo tjumptable:$in), (Addiu ZeroReg, tjumptable:$in)>; + def : MaxisPat<(MaxisLo tconstpool:$in), (Addiu ZeroReg, tconstpool:$in)>; + def : MaxisPat<(MaxisLo tglobaltlsaddr:$in), + (Addiu ZeroReg, tglobaltlsaddr:$in)>; + def : MaxisPat<(MaxisLo texternalsym:$in), (Addiu ZeroReg, texternalsym:$in)>; + + def : MaxisPat<(add GPROpnd:$hi, (MaxisLo tglobaladdr:$lo)), + (Addiu GPROpnd:$hi, tglobaladdr:$lo)>; + def : MaxisPat<(add GPROpnd:$hi, (MaxisLo tblockaddress:$lo)), + (Addiu GPROpnd:$hi, tblockaddress:$lo)>; + def : MaxisPat<(add GPROpnd:$hi, (MaxisLo tjumptable:$lo)), + (Addiu GPROpnd:$hi, tjumptable:$lo)>; + def : MaxisPat<(add GPROpnd:$hi, (MaxisLo tconstpool:$lo)), + (Addiu GPROpnd:$hi, tconstpool:$lo)>; + def : MaxisPat<(add GPROpnd:$hi, (MaxisLo tglobaltlsaddr:$lo)), + (Addiu GPROpnd:$hi, tglobaltlsaddr:$lo)>; +} + +defm : MaxisHiLoRelocs; + +def : MaxisPat<(MaxisGotHi tglobaladdr:$in), (LUi tglobaladdr:$in)>; +def : MaxisPat<(MaxisGotHi texternalsym:$in), (LUi texternalsym:$in)>; + +// gp_rel relocs +def : MaxisPat<(add GPR32:$gp, (MaxisGPRel tglobaladdr:$in)), + (ADDiu GPR32:$gp, tglobaladdr:$in)>, ABI_NOT_N64; +def : MaxisPat<(add GPR32:$gp, (MaxisGPRel tconstpool:$in)), + (ADDiu GPR32:$gp, tconstpool:$in)>, ABI_NOT_N64; + +// wrapper_pic +class WrapperPat: + MaxisPat<(MaxisWrapper RC:$gp, node:$in), + (ADDiuOp RC:$gp, node:$in)>; + +def : WrapperPat; +def : WrapperPat; +def : WrapperPat; +def : WrapperPat; +def : WrapperPat; +def : WrapperPat; + +let AdditionalPredicates = [NotInMicroMaxis] in { +// Maxis does not have "not", so we expand our way +def : MaxisPat<(not GPR32:$in), + (NOR GPR32Opnd:$in, ZERO)>; +} + +// extended loads +def : MaxisPat<(i32 (extloadi1 addr:$src)), (LBu addr:$src)>; +def : MaxisPat<(i32 (extloadi8 addr:$src)), (LBu addr:$src)>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisPat<(i32 (extloadi16 addr:$src)), (LHu addr:$src)>; +} + +// peepholes +def : MaxisPat<(store (i32 0), addr:$dst), (SW ZERO, addr:$dst)>; + +// brcond patterns +multiclass BrcondPats { +def : MaxisPat<(brcond (i32 (setne RC:$lhs, 0)), bb:$dst), + (BNEOp RC:$lhs, ZEROReg, bb:$dst)>; +def : MaxisPat<(brcond (i32 (seteq RC:$lhs, 0)), bb:$dst), + (BEQOp RC:$lhs, ZEROReg, bb:$dst)>; + +def : MaxisPat<(brcond (i32 (setge RC:$lhs, RC:$rhs)), bb:$dst), + (BEQOp1 (SLTOp RC:$lhs, RC:$rhs), ZERO, bb:$dst)>; +def : MaxisPat<(brcond (i32 (setuge RC:$lhs, RC:$rhs)), bb:$dst), + (BEQOp1 (SLTuOp RC:$lhs, RC:$rhs), ZERO, bb:$dst)>; +def : MaxisPat<(brcond (i32 (setge RC:$lhs, immSExt16:$rhs)), bb:$dst), + (BEQOp1 (SLTiOp RC:$lhs, immSExt16:$rhs), ZERO, bb:$dst)>; +def : MaxisPat<(brcond (i32 (setuge RC:$lhs, immSExt16:$rhs)), bb:$dst), + (BEQOp1 (SLTiuOp RC:$lhs, immSExt16:$rhs), ZERO, bb:$dst)>; +def : MaxisPat<(brcond (i32 (setgt RC:$lhs, immSExt16Plus1:$rhs)), bb:$dst), + (BEQOp1 (SLTiOp RC:$lhs, (Plus1 imm:$rhs)), ZERO, bb:$dst)>; +def : MaxisPat<(brcond (i32 (setugt RC:$lhs, immSExt16Plus1:$rhs)), bb:$dst), + (BEQOp1 (SLTiuOp RC:$lhs, (Plus1 imm:$rhs)), ZERO, bb:$dst)>; + +def : MaxisPat<(brcond (i32 (setle RC:$lhs, RC:$rhs)), bb:$dst), + (BEQOp1 (SLTOp RC:$rhs, RC:$lhs), ZERO, bb:$dst)>; +def : MaxisPat<(brcond (i32 (setule RC:$lhs, RC:$rhs)), bb:$dst), + (BEQOp1 (SLTuOp RC:$rhs, RC:$lhs), ZERO, bb:$dst)>; + +def : MaxisPat<(brcond RC:$cond, bb:$dst), + (BNEOp RC:$cond, ZEROReg, bb:$dst)>; +} +let AdditionalPredicates = [NotInMicroMaxis] in { + defm : BrcondPats; +} +def : MaxisPat<(brcond (i32 (setlt i32:$lhs, 1)), bb:$dst), + (BLEZ i32:$lhs, bb:$dst)>; +def : MaxisPat<(brcond (i32 (setgt i32:$lhs, -1)), bb:$dst), + (BGEZ i32:$lhs, bb:$dst)>; + +// setcc patterns +multiclass SeteqPats { + def : MaxisPat<(seteq RC:$lhs, 0), + (SLTiuOp RC:$lhs, 1)>; + def : MaxisPat<(setne RC:$lhs, 0), + (SLTuOp ZEROReg, RC:$lhs)>; + def : MaxisPat<(seteq RC:$lhs, RC:$rhs), + (SLTiuOp (XOROp RC:$lhs, RC:$rhs), 1)>; + def : MaxisPat<(setne RC:$lhs, RC:$rhs), + (SLTuOp ZEROReg, (XOROp RC:$lhs, RC:$rhs))>; +} + +multiclass SetlePats { + def : MaxisPat<(setle RC:$lhs, RC:$rhs), + (XORiOp (SLTOp RC:$rhs, RC:$lhs), 1)>; + def : MaxisPat<(setule RC:$lhs, RC:$rhs), + (XORiOp (SLTuOp RC:$rhs, RC:$lhs), 1)>; +} + +multiclass SetgtPats { + def : MaxisPat<(setgt RC:$lhs, RC:$rhs), + (SLTOp RC:$rhs, RC:$lhs)>; + def : MaxisPat<(setugt RC:$lhs, RC:$rhs), + (SLTuOp RC:$rhs, RC:$lhs)>; +} + +multiclass SetgePats { + def : MaxisPat<(setge RC:$lhs, RC:$rhs), + (XORiOp (SLTOp RC:$lhs, RC:$rhs), 1)>; + def : MaxisPat<(setuge RC:$lhs, RC:$rhs), + (XORiOp (SLTuOp RC:$lhs, RC:$rhs), 1)>; +} + +multiclass SetgeImmPats { + def : MaxisPat<(setge RC:$lhs, immSExt16:$rhs), + (XORiOp (SLTiOp RC:$lhs, immSExt16:$rhs), 1)>; + def : MaxisPat<(setuge RC:$lhs, immSExt16:$rhs), + (XORiOp (SLTiuOp RC:$lhs, immSExt16:$rhs), 1)>; +} + +let AdditionalPredicates = [NotInMicroMaxis] in { + defm : SeteqPats; + defm : SetlePats; + defm : SetgtPats; + defm : SetgePats; + defm : SetgeImmPats; +} + +// bswap pattern +def : MaxisPat<(bswap GPR32:$rt), (ROTR (WSBH GPR32:$rt), 16)>; + +// Load halfword/word patterns. +let AddedComplexity = 40 in { + def : LoadRegImmPat; + let AdditionalPredicates = [NotInMicroMaxis] in { + def : LoadRegImmPat; + def : LoadRegImmPat; + } +} + +// Atomic load patterns. +def : MaxisPat<(atomic_load_8 addr:$a), (LB addr:$a)>; +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisPat<(atomic_load_16 addr:$a), (LH addr:$a)>; +} +def : MaxisPat<(atomic_load_32 addr:$a), (LW addr:$a)>; + +// Atomic store patterns. +def : MaxisPat<(atomic_store_8 addr:$a, GPR32:$v), (SB GPR32:$v, addr:$a)>; +def : MaxisPat<(atomic_store_16 addr:$a, GPR32:$v), (SH GPR32:$v, addr:$a)>; +def : MaxisPat<(atomic_store_32 addr:$a, GPR32:$v), (SW GPR32:$v, addr:$a)>; + +//===----------------------------------------------------------------------===// +// Floating Point Support +//===----------------------------------------------------------------------===// + +include "MaxisInstrFPU.td" +include "Maxis64InstrInfo.td" +include "MaxisCondMov.td" + +include "Maxis32r6InstrInfo.td" +include "Maxis64r6InstrInfo.td" + +// +// Maxis16 + +include "Maxis16InstrFormats.td" +include "Maxis16InstrInfo.td" + +// DSP +include "MaxisDSPInstrFormats.td" +include "MaxisDSPInstrInfo.td" + +// MSA +include "MaxisMSAInstrFormats.td" +include "MaxisMSAInstrInfo.td" + +// EVA +include "MaxisEVAInstrFormats.td" +include "MaxisEVAInstrInfo.td" + +// MT +include "MaxisMTInstrFormats.td" +include "MaxisMTInstrInfo.td" + +// Micromaxis +include "MicroMaxisInstrFormats.td" +include "MicroMaxisInstrInfo.td" +include "MicroMaxisInstrFPU.td" + +// Micromaxis r6 +include "MicroMaxis32r6InstrFormats.td" +include "MicroMaxis32r6InstrInfo.td" + +// Micromaxis DSP +include "MicroMaxisDSPInstrFormats.td" +include "MicroMaxisDSPInstrInfo.td" diff --git a/lib/Target/Maxis/MaxisLongBranch.cpp b/lib/Target/Maxis/MaxisLongBranch.cpp new file mode 100644 index 00000000..0e8f6267 --- /dev/null +++ b/lib/Target/Maxis/MaxisLongBranch.cpp @@ -0,0 +1,619 @@ +//===- MaxisLongBranch.cpp - Emit long branches ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass expands a branch or jump instruction into a long branch if its +// offset is too large to fit into its immediate field. +// +// FIXME: Fix pc-region jump instructions which cross 256MB segment boundaries. +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MCTargetDesc/MaxisMCNaCl.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "Maxis.h" +#include "MaxisInstrInfo.h" +#include "MaxisMachineFunction.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Target/TargetMachine.h" +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-long-branch" + +STATISTIC(LongBranches, "Number of long branches."); + +static cl::opt SkipLongBranch( + "skip-maxis-long-branch", + cl::init(false), + cl::desc("MAXIS: Skip long branch pass."), + cl::Hidden); + +static cl::opt ForceLongBranch( + "force-maxis-long-branch", + cl::init(false), + cl::desc("MAXIS: Expand all branches to long format."), + cl::Hidden); + +namespace { + + using Iter = MachineBasicBlock::iterator; + using ReverseIter = MachineBasicBlock::reverse_iterator; + + struct MBBInfo { + uint64_t Size = 0; + uint64_t Address; + bool HasLongBranch = false; + MachineInstr *Br = nullptr; + + MBBInfo() = default; + }; + + class MaxisLongBranch : public MachineFunctionPass { + public: + static char ID; + + MaxisLongBranch() + : MachineFunctionPass(ID), ABI(MaxisABIInfo::Unknown()) {} + + StringRef getPassName() const override { return "Maxis Long Branch"; } + + bool runOnMachineFunction(MachineFunction &F) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + private: + void splitMBB(MachineBasicBlock *MBB); + void initMBBInfo(); + int64_t computeOffset(const MachineInstr *Br); + void replaceBranch(MachineBasicBlock &MBB, Iter Br, const DebugLoc &DL, + MachineBasicBlock *MBBOpnd); + void expandToLongBranch(MBBInfo &Info); + + MachineFunction *MF; + SmallVector MBBInfos; + bool IsPIC; + MaxisABIInfo ABI; + unsigned LongBranchSeqSize; + }; + +} // end anonymous namespace + +char MaxisLongBranch::ID = 0; + +/// Iterate over list of Br's operands and search for a MachineBasicBlock +/// operand. +static MachineBasicBlock *getTargetMBB(const MachineInstr &Br) { + for (unsigned I = 0, E = Br.getDesc().getNumOperands(); I < E; ++I) { + const MachineOperand &MO = Br.getOperand(I); + + if (MO.isMBB()) + return MO.getMBB(); + } + + llvm_unreachable("This instruction does not have an MBB operand."); +} + +// Traverse the list of instructions backwards until a non-debug instruction is +// found or it reaches E. +static ReverseIter getNonDebugInstr(ReverseIter B, const ReverseIter &E) { + for (; B != E; ++B) + if (!B->isDebugValue()) + return B; + + return E; +} + +// Split MBB if it has two direct jumps/branches. +void MaxisLongBranch::splitMBB(MachineBasicBlock *MBB) { + ReverseIter End = MBB->rend(); + ReverseIter LastBr = getNonDebugInstr(MBB->rbegin(), End); + + // Return if MBB has no branch instructions. + if ((LastBr == End) || + (!LastBr->isConditionalBranch() && !LastBr->isUnconditionalBranch())) + return; + + ReverseIter FirstBr = getNonDebugInstr(std::next(LastBr), End); + + // MBB has only one branch instruction if FirstBr is not a branch + // instruction. + if ((FirstBr == End) || + (!FirstBr->isConditionalBranch() && !FirstBr->isUnconditionalBranch())) + return; + + assert(!FirstBr->isIndirectBranch() && "Unexpected indirect branch found."); + + // Create a new MBB. Move instructions in MBB to the newly created MBB. + MachineBasicBlock *NewMBB = + MF->CreateMachineBasicBlock(MBB->getBasicBlock()); + + // Insert NewMBB and fix control flow. + MachineBasicBlock *Tgt = getTargetMBB(*FirstBr); + NewMBB->transferSuccessors(MBB); + NewMBB->removeSuccessor(Tgt, true); + MBB->addSuccessor(NewMBB); + MBB->addSuccessor(Tgt); + MF->insert(std::next(MachineFunction::iterator(MBB)), NewMBB); + + NewMBB->splice(NewMBB->end(), MBB, LastBr.getReverse(), MBB->end()); +} + +// Fill MBBInfos. +void MaxisLongBranch::initMBBInfo() { + // Split the MBBs if they have two branches. Each basic block should have at + // most one branch after this loop is executed. + for (auto &MBB : *MF) + splitMBB(&MBB); + + MF->RenumberBlocks(); + MBBInfos.clear(); + MBBInfos.resize(MF->size()); + + const MaxisInstrInfo *TII = + static_cast(MF->getSubtarget().getInstrInfo()); + for (unsigned I = 0, E = MBBInfos.size(); I < E; ++I) { + MachineBasicBlock *MBB = MF->getBlockNumbered(I); + + // Compute size of MBB. + for (MachineBasicBlock::instr_iterator MI = MBB->instr_begin(); + MI != MBB->instr_end(); ++MI) + MBBInfos[I].Size += TII->getInstSizeInBytes(*MI); + + // Search for MBB's branch instruction. + ReverseIter End = MBB->rend(); + ReverseIter Br = getNonDebugInstr(MBB->rbegin(), End); + + if ((Br != End) && !Br->isIndirectBranch() && + (Br->isConditionalBranch() || (Br->isUnconditionalBranch() && IsPIC))) + MBBInfos[I].Br = &*Br; + } +} + +// Compute offset of branch in number of bytes. +int64_t MaxisLongBranch::computeOffset(const MachineInstr *Br) { + int64_t Offset = 0; + int ThisMBB = Br->getParent()->getNumber(); + int TargetMBB = getTargetMBB(*Br)->getNumber(); + + // Compute offset of a forward branch. + if (ThisMBB < TargetMBB) { + for (int N = ThisMBB + 1; N < TargetMBB; ++N) + Offset += MBBInfos[N].Size; + + return Offset + 4; + } + + // Compute offset of a backward branch. + for (int N = ThisMBB; N >= TargetMBB; --N) + Offset += MBBInfos[N].Size; + + return -Offset + 4; +} + +// Replace Br with a branch which has the opposite condition code and a +// MachineBasicBlock operand MBBOpnd. +void MaxisLongBranch::replaceBranch(MachineBasicBlock &MBB, Iter Br, + const DebugLoc &DL, + MachineBasicBlock *MBBOpnd) { + const MaxisInstrInfo *TII = static_cast( + MBB.getParent()->getSubtarget().getInstrInfo()); + unsigned NewOpc = TII->getOppositeBranchOpc(Br->getOpcode()); + const MCInstrDesc &NewDesc = TII->get(NewOpc); + + MachineInstrBuilder MIB = BuildMI(MBB, Br, DL, NewDesc); + + for (unsigned I = 0, E = Br->getDesc().getNumOperands(); I < E; ++I) { + MachineOperand &MO = Br->getOperand(I); + + if (!MO.isReg()) { + assert(MO.isMBB() && "MBB operand expected."); + break; + } + + MIB.addReg(MO.getReg()); + } + + MIB.addMBB(MBBOpnd); + + if (Br->hasDelaySlot()) { + // Bundle the instruction in the delay slot to the newly created branch + // and erase the original branch. + assert(Br->isBundledWithSucc()); + MachineBasicBlock::instr_iterator II = Br.getInstrIterator(); + MIBundleBuilder(&*MIB).append((++II)->removeFromBundle()); + } + Br->eraseFromParent(); +} + +// Expand branch instructions to long branches. +// TODO: This function has to be fixed for beqz16 and bnez16, because it +// currently assumes that all branches have 16-bit offsets, and will produce +// wrong code if branches whose allowed offsets are [-128, -126, ..., 126] +// are present. +void MaxisLongBranch::expandToLongBranch(MBBInfo &I) { + MachineBasicBlock::iterator Pos; + MachineBasicBlock *MBB = I.Br->getParent(), *TgtMBB = getTargetMBB(*I.Br); + DebugLoc DL = I.Br->getDebugLoc(); + const BasicBlock *BB = MBB->getBasicBlock(); + MachineFunction::iterator FallThroughMBB = ++MachineFunction::iterator(MBB); + MachineBasicBlock *LongBrMBB = MF->CreateMachineBasicBlock(BB); + const MaxisSubtarget &Subtarget = + static_cast(MF->getSubtarget()); + const MaxisInstrInfo *TII = + static_cast(Subtarget.getInstrInfo()); + + MF->insert(FallThroughMBB, LongBrMBB); + MBB->replaceSuccessor(TgtMBB, LongBrMBB); + + if (IsPIC) { + MachineBasicBlock *BalTgtMBB = MF->CreateMachineBasicBlock(BB); + MF->insert(FallThroughMBB, BalTgtMBB); + LongBrMBB->addSuccessor(BalTgtMBB); + BalTgtMBB->addSuccessor(TgtMBB); + + // We must select between the MAXIS32r6/MAXIS64r6 BALC (which is a normal + // instruction) and the pre-MAXIS32r6/MAXIS64r6 definition (which is an + // pseudo-instruction wrapping BGEZAL). + const unsigned BalOp = + Subtarget.hasMaxis32r6() + ? Subtarget.inMicroMaxisMode() ? Maxis::BALC_MMR6 : Maxis::BALC + : Maxis::BAL_BR; + + if (!ABI.IsN64()) { + // Pre R6: + // $longbr: + // addiu $sp, $sp, -8 + // sw $ra, 0($sp) + // lui $at, %hi($tgt - $baltgt) + // bal $baltgt + // addiu $at, $at, %lo($tgt - $baltgt) + // $baltgt: + // addu $at, $ra, $at + // lw $ra, 0($sp) + // jr $at + // addiu $sp, $sp, 8 + // $fallthrough: + // + + // R6: + // $longbr: + // addiu $sp, $sp, -8 + // sw $ra, 0($sp) + // lui $at, %hi($tgt - $baltgt) + // addiu $at, $at, %lo($tgt - $baltgt) + // balc $baltgt + // $baltgt: + // addu $at, $ra, $at + // lw $ra, 0($sp) + // addiu $sp, $sp, 8 + // jic $at, 0 + // $fallthrough: + + Pos = LongBrMBB->begin(); + + BuildMI(*LongBrMBB, Pos, DL, TII->get(Maxis::ADDiu), Maxis::SP) + .addReg(Maxis::SP).addImm(-8); + BuildMI(*LongBrMBB, Pos, DL, TII->get(Maxis::SW)).addReg(Maxis::RA) + .addReg(Maxis::SP).addImm(0); + + // LUi and ADDiu instructions create 32-bit offset of the target basic + // block from the target of BAL(C) instruction. We cannot use immediate + // value for this offset because it cannot be determined accurately when + // the program has inline assembly statements. We therefore use the + // relocation expressions %hi($tgt-$baltgt) and %lo($tgt-$baltgt) which + // are resolved during the fixup, so the values will always be correct. + // + // Since we cannot create %hi($tgt-$baltgt) and %lo($tgt-$baltgt) + // expressions at this point (it is possible only at the MC layer), + // we replace LUi and ADDiu with pseudo instructions + // LONG_BRANCH_LUi and LONG_BRANCH_ADDiu, and add both basic + // blocks as operands to these instructions. When lowering these pseudo + // instructions to LUi and ADDiu in the MC layer, we will create + // %hi($tgt-$baltgt) and %lo($tgt-$baltgt) expressions and add them as + // operands to lowered instructions. + + BuildMI(*LongBrMBB, Pos, DL, TII->get(Maxis::LONG_BRANCH_LUi), Maxis::AT) + .addMBB(TgtMBB).addMBB(BalTgtMBB); + + MachineInstrBuilder BalInstr = + BuildMI(*MF, DL, TII->get(BalOp)).addMBB(BalTgtMBB); + MachineInstrBuilder ADDiuInstr = + BuildMI(*MF, DL, TII->get(Maxis::LONG_BRANCH_ADDiu), Maxis::AT) + .addReg(Maxis::AT) + .addMBB(TgtMBB) + .addMBB(BalTgtMBB); + if (Subtarget.hasMaxis32r6()) { + LongBrMBB->insert(Pos, ADDiuInstr); + LongBrMBB->insert(Pos, BalInstr); + } else { + LongBrMBB->insert(Pos, BalInstr); + LongBrMBB->insert(Pos, ADDiuInstr); + LongBrMBB->rbegin()->bundleWithPred(); + } + + Pos = BalTgtMBB->begin(); + + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::ADDu), Maxis::AT) + .addReg(Maxis::RA).addReg(Maxis::AT); + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::LW), Maxis::RA) + .addReg(Maxis::SP).addImm(0); + if (Subtarget.isTargetNaCl()) + // Bundle-align the target of indirect branch JR. + TgtMBB->setAlignment(MAXIS_NACL_BUNDLE_ALIGN); + + // In NaCl, modifying the sp is not allowed in branch delay slot. + // For MAXIS32R6, we can skip using a delay slot branch. + if (Subtarget.isTargetNaCl() || Subtarget.hasMaxis32r6()) + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::ADDiu), Maxis::SP) + .addReg(Maxis::SP).addImm(8); + + if (Subtarget.hasMaxis32r6()) { + const unsigned JICOp = + Subtarget.inMicroMaxisMode() ? Maxis::JIC_MMR6 : Maxis::JIC; + BuildMI(*BalTgtMBB, Pos, DL, TII->get(JICOp)) + .addReg(Maxis::AT) + .addImm(0); + + } else { + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::JR)).addReg(Maxis::AT); + + if (Subtarget.isTargetNaCl()) { + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::NOP)); + } else + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::ADDiu), Maxis::SP) + .addReg(Maxis::SP) + .addImm(8); + + BalTgtMBB->rbegin()->bundleWithPred(); + } + } else { + // Pre R6: + // $longbr: + // daddiu $sp, $sp, -16 + // sd $ra, 0($sp) + // daddiu $at, $zero, %hi($tgt - $baltgt) + // dsll $at, $at, 16 + // bal $baltgt + // daddiu $at, $at, %lo($tgt - $baltgt) + // $baltgt: + // daddu $at, $ra, $at + // ld $ra, 0($sp) + // jr64 $at + // daddiu $sp, $sp, 16 + // $fallthrough: + + // R6: + // $longbr: + // daddiu $sp, $sp, -16 + // sd $ra, 0($sp) + // daddiu $at, $zero, %hi($tgt - $baltgt) + // dsll $at, $at, 16 + // daddiu $at, $at, %lo($tgt - $baltgt) + // balc $baltgt + // $baltgt: + // daddu $at, $ra, $at + // ld $ra, 0($sp) + // daddiu $sp, $sp, 16 + // jic $at, 0 + // $fallthrough: + + // We assume the branch is within-function, and that offset is within + // +/- 2GB. High 32 bits will therefore always be zero. + + // Note that this will work even if the offset is negative, because + // of the +1 modification that's added in that case. For example, if the + // offset is -1MB (0xFFFFFFFFFFF00000), the computation for %higher is + // + // 0xFFFFFFFFFFF00000 + 0x80008000 = 0x000000007FF08000 + // + // and the bits [47:32] are zero. For %highest + // + // 0xFFFFFFFFFFF00000 + 0x800080008000 = 0x000080007FF08000 + // + // and the bits [63:48] are zero. + + Pos = LongBrMBB->begin(); + + BuildMI(*LongBrMBB, Pos, DL, TII->get(Maxis::DADDiu), Maxis::SP_64) + .addReg(Maxis::SP_64).addImm(-16); + BuildMI(*LongBrMBB, Pos, DL, TII->get(Maxis::SD)).addReg(Maxis::RA_64) + .addReg(Maxis::SP_64).addImm(0); + BuildMI(*LongBrMBB, Pos, DL, TII->get(Maxis::LONG_BRANCH_DADDiu), + Maxis::AT_64).addReg(Maxis::ZERO_64) + .addMBB(TgtMBB, MaxisII::MO_ABS_HI).addMBB(BalTgtMBB); + BuildMI(*LongBrMBB, Pos, DL, TII->get(Maxis::DSLL), Maxis::AT_64) + .addReg(Maxis::AT_64).addImm(16); + + MachineInstrBuilder BalInstr = + BuildMI(*MF, DL, TII->get(BalOp)).addMBB(BalTgtMBB); + MachineInstrBuilder DADDiuInstr = + BuildMI(*MF, DL, TII->get(Maxis::LONG_BRANCH_DADDiu), Maxis::AT_64) + .addReg(Maxis::AT_64) + .addMBB(TgtMBB, MaxisII::MO_ABS_LO) + .addMBB(BalTgtMBB); + if (Subtarget.hasMaxis32r6()) { + LongBrMBB->insert(Pos, DADDiuInstr); + LongBrMBB->insert(Pos, BalInstr); + } else { + LongBrMBB->insert(Pos, BalInstr); + LongBrMBB->insert(Pos, DADDiuInstr); + LongBrMBB->rbegin()->bundleWithPred(); + } + + Pos = BalTgtMBB->begin(); + + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::DADDu), Maxis::AT_64) + .addReg(Maxis::RA_64).addReg(Maxis::AT_64); + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::LD), Maxis::RA_64) + .addReg(Maxis::SP_64).addImm(0); + + if (Subtarget.hasMaxis64r6()) { + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::DADDiu), Maxis::SP_64) + .addReg(Maxis::SP_64) + .addImm(16); + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::JIC64)) + .addReg(Maxis::AT_64) + .addImm(0); + } else { + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::JR64)).addReg(Maxis::AT_64); + BuildMI(*BalTgtMBB, Pos, DL, TII->get(Maxis::DADDiu), Maxis::SP_64) + .addReg(Maxis::SP_64) + .addImm(16); + BalTgtMBB->rbegin()->bundleWithPred(); + } + } + + assert(LongBrMBB->size() + BalTgtMBB->size() == LongBranchSeqSize); + } else { + // Pre R6: R6: + // $longbr: $longbr: + // j $tgt bc $tgt + // nop $fallthrough + // $fallthrough: + // + Pos = LongBrMBB->begin(); + LongBrMBB->addSuccessor(TgtMBB); + if (Subtarget.hasMaxis32r6()) + BuildMI(*LongBrMBB, Pos, DL, + TII->get(Subtarget.inMicroMaxisMode() ? Maxis::BC_MMR6 : Maxis::BC)) + .addMBB(TgtMBB); + else + MIBundleBuilder(*LongBrMBB, Pos) + .append(BuildMI(*MF, DL, TII->get(Maxis::J)).addMBB(TgtMBB)) + .append(BuildMI(*MF, DL, TII->get(Maxis::NOP))); + + assert(LongBrMBB->size() == LongBranchSeqSize); + } + + if (I.Br->isUnconditionalBranch()) { + // Change branch destination. + assert(I.Br->getDesc().getNumOperands() == 1); + I.Br->RemoveOperand(0); + I.Br->addOperand(MachineOperand::CreateMBB(LongBrMBB)); + } else + // Change branch destination and reverse condition. + replaceBranch(*MBB, I.Br, DL, &*FallThroughMBB); +} + +static void emitGPDisp(MachineFunction &F, const MaxisInstrInfo *TII) { + MachineBasicBlock &MBB = F.front(); + MachineBasicBlock::iterator I = MBB.begin(); + DebugLoc DL = MBB.findDebugLoc(MBB.begin()); + BuildMI(MBB, I, DL, TII->get(Maxis::LUi), Maxis::V0) + .addExternalSymbol("_gp_disp", MaxisII::MO_ABS_HI); + BuildMI(MBB, I, DL, TII->get(Maxis::ADDiu), Maxis::V0) + .addReg(Maxis::V0).addExternalSymbol("_gp_disp", MaxisII::MO_ABS_LO); + MBB.removeLiveIn(Maxis::V0); +} + +bool MaxisLongBranch::runOnMachineFunction(MachineFunction &F) { + const MaxisSubtarget &STI = + static_cast(F.getSubtarget()); + const MaxisInstrInfo *TII = + static_cast(STI.getInstrInfo()); + + const TargetMachine& TM = F.getTarget(); + IsPIC = TM.isPositionIndependent(); + ABI = static_cast(TM).getABI(); + + LongBranchSeqSize = IsPIC ? ((ABI.IsN64() || STI.isTargetNaCl()) ? 10 : 9) + : (STI.hasMaxis32r6() ? 1 : 2); + + if (STI.inMaxis16Mode() || !STI.enableLongBranchPass()) + return false; + if (IsPIC && static_cast(TM).getABI().IsO32() && + F.getInfo()->globalBaseRegSet()) + emitGPDisp(F, TII); + + if (SkipLongBranch) + return true; + + MF = &F; + initMBBInfo(); + + SmallVectorImpl::iterator I, E = MBBInfos.end(); + bool EverMadeChange = false, MadeChange = true; + + while (MadeChange) { + MadeChange = false; + + for (I = MBBInfos.begin(); I != E; ++I) { + // Skip if this MBB doesn't have a branch or the branch has already been + // converted to a long branch. + if (!I->Br || I->HasLongBranch) + continue; + + int ShVal = STI.inMicroMaxisMode() ? 2 : 4; + int64_t Offset = computeOffset(I->Br) / ShVal; + + if (STI.isTargetNaCl()) { + // The offset calculation does not include sandboxing instructions + // that will be added later in the MC layer. Since at this point we + // don't know the exact amount of code that "sandboxing" will add, we + // conservatively estimate that code will not grow more than 100%. + Offset *= 2; + } + + // Check if offset fits into 16-bit immediate field of branches. + if (!ForceLongBranch && isInt<16>(Offset)) + continue; + + I->HasLongBranch = true; + I->Size += LongBranchSeqSize * 4; + ++LongBranches; + EverMadeChange = MadeChange = true; + } + } + + if (!EverMadeChange) + return true; + + // Compute basic block addresses. + if (IsPIC) { + uint64_t Address = 0; + + for (I = MBBInfos.begin(); I != E; Address += I->Size, ++I) + I->Address = Address; + } + + // Do the expansion. + for (I = MBBInfos.begin(); I != E; ++I) + if (I->HasLongBranch) + expandToLongBranch(*I); + + MF->RenumberBlocks(); + + return true; +} + +/// createMaxisLongBranchPass - Returns a pass that converts branches to long +/// branches. +FunctionPass *llvm::createMaxisLongBranchPass() { return new MaxisLongBranch(); } diff --git a/lib/Target/Maxis/MaxisMCInstLower.cpp b/lib/Target/Maxis/MaxisMCInstLower.cpp new file mode 100644 index 00000000..eb1a31b2 --- /dev/null +++ b/lib/Target/Maxis/MaxisMCInstLower.cpp @@ -0,0 +1,280 @@ +//===- MaxisMCInstLower.cpp - Convert Maxis MachineInstr to MCInst ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains code to lower Maxis MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "MaxisMCInstLower.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "MCTargetDesc/MaxisMCExpr.h" +#include "MaxisAsmPrinter.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include + +using namespace llvm; + +MaxisMCInstLower::MaxisMCInstLower(MaxisAsmPrinter &asmprinter) + : AsmPrinter(asmprinter) {} + +void MaxisMCInstLower::Initialize(MCContext *C) { + Ctx = C; +} + +MCOperand MaxisMCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MachineOperandType MOTy, + unsigned Offset) const { + MCSymbolRefExpr::VariantKind Kind = MCSymbolRefExpr::VK_None; + MaxisMCExpr::MaxisExprKind TargetKind = MaxisMCExpr::MEK_None; + bool IsGpOff = false; + const MCSymbol *Symbol; + + switch(MO.getTargetFlags()) { + default: + llvm_unreachable("Invalid target flag!"); + case MaxisII::MO_NO_FLAG: + break; + case MaxisII::MO_GPREL: + TargetKind = MaxisMCExpr::MEK_GPREL; + break; + case MaxisII::MO_GOT_CALL: + TargetKind = MaxisMCExpr::MEK_GOT_CALL; + break; + case MaxisII::MO_GOT: + TargetKind = MaxisMCExpr::MEK_GOT; + break; + case MaxisII::MO_ABS_HI: + TargetKind = MaxisMCExpr::MEK_HI; + break; + case MaxisII::MO_ABS_LO: + TargetKind = MaxisMCExpr::MEK_LO; + break; + case MaxisII::MO_TLSGD: + TargetKind = MaxisMCExpr::MEK_TLSGD; + break; + case MaxisII::MO_TLSLDM: + TargetKind = MaxisMCExpr::MEK_TLSLDM; + break; + case MaxisII::MO_DTPREL_HI: + TargetKind = MaxisMCExpr::MEK_DTPREL_HI; + break; + case MaxisII::MO_DTPREL_LO: + TargetKind = MaxisMCExpr::MEK_DTPREL_LO; + break; + case MaxisII::MO_GOTTPREL: + TargetKind = MaxisMCExpr::MEK_GOTTPREL; + break; + case MaxisII::MO_TPREL_HI: + TargetKind = MaxisMCExpr::MEK_TPREL_HI; + break; + case MaxisII::MO_TPREL_LO: + TargetKind = MaxisMCExpr::MEK_TPREL_LO; + break; + case MaxisII::MO_GPOFF_HI: + TargetKind = MaxisMCExpr::MEK_HI; + IsGpOff = true; + break; + case MaxisII::MO_GPOFF_LO: + TargetKind = MaxisMCExpr::MEK_LO; + IsGpOff = true; + break; + case MaxisII::MO_GOT_DISP: + TargetKind = MaxisMCExpr::MEK_GOT_DISP; + break; + case MaxisII::MO_GOT_HI16: + TargetKind = MaxisMCExpr::MEK_GOT_HI16; + break; + case MaxisII::MO_GOT_LO16: + TargetKind = MaxisMCExpr::MEK_GOT_LO16; + break; + case MaxisII::MO_GOT_PAGE: + TargetKind = MaxisMCExpr::MEK_GOT_PAGE; + break; + case MaxisII::MO_GOT_OFST: + TargetKind = MaxisMCExpr::MEK_GOT_OFST; + break; + case MaxisII::MO_HIGHER: + TargetKind = MaxisMCExpr::MEK_HIGHER; + break; + case MaxisII::MO_HIGHEST: + TargetKind = MaxisMCExpr::MEK_HIGHEST; + break; + case MaxisII::MO_CALL_HI16: + TargetKind = MaxisMCExpr::MEK_CALL_HI16; + break; + case MaxisII::MO_CALL_LO16: + TargetKind = MaxisMCExpr::MEK_CALL_LO16; + break; + } + + switch (MOTy) { + case MachineOperand::MO_MachineBasicBlock: + Symbol = MO.getMBB()->getSymbol(); + break; + + case MachineOperand::MO_GlobalAddress: + Symbol = AsmPrinter.getSymbol(MO.getGlobal()); + Offset += MO.getOffset(); + break; + + case MachineOperand::MO_BlockAddress: + Symbol = AsmPrinter.GetBlockAddressSymbol(MO.getBlockAddress()); + Offset += MO.getOffset(); + break; + + case MachineOperand::MO_ExternalSymbol: + Symbol = AsmPrinter.GetExternalSymbolSymbol(MO.getSymbolName()); + Offset += MO.getOffset(); + break; + + case MachineOperand::MO_MCSymbol: + Symbol = MO.getMCSymbol(); + Offset += MO.getOffset(); + break; + + case MachineOperand::MO_JumpTableIndex: + Symbol = AsmPrinter.GetJTISymbol(MO.getIndex()); + break; + + case MachineOperand::MO_ConstantPoolIndex: + Symbol = AsmPrinter.GetCPISymbol(MO.getIndex()); + Offset += MO.getOffset(); + break; + + default: + llvm_unreachable(""); + } + + const MCExpr *Expr = MCSymbolRefExpr::create(Symbol, Kind, *Ctx); + + if (Offset) { + // Assume offset is never negative. + assert(Offset > 0); + + Expr = MCBinaryExpr::createAdd(Expr, MCConstantExpr::create(Offset, *Ctx), + *Ctx); + } + + if (IsGpOff) + Expr = MaxisMCExpr::createGpOff(TargetKind, Expr, *Ctx); + else if (TargetKind != MaxisMCExpr::MEK_None) + Expr = MaxisMCExpr::create(TargetKind, Expr, *Ctx); + + return MCOperand::createExpr(Expr); +} + +MCOperand MaxisMCInstLower::LowerOperand(const MachineOperand &MO, + unsigned offset) const { + MachineOperandType MOTy = MO.getType(); + + switch (MOTy) { + default: llvm_unreachable("unknown operand type"); + case MachineOperand::MO_Register: + // Ignore all implicit register operands. + if (MO.isImplicit()) break; + return MCOperand::createReg(MO.getReg()); + case MachineOperand::MO_Immediate: + return MCOperand::createImm(MO.getImm() + offset); + case MachineOperand::MO_MachineBasicBlock: + case MachineOperand::MO_GlobalAddress: + case MachineOperand::MO_ExternalSymbol: + case MachineOperand::MO_MCSymbol: + case MachineOperand::MO_JumpTableIndex: + case MachineOperand::MO_ConstantPoolIndex: + case MachineOperand::MO_BlockAddress: + return LowerSymbolOperand(MO, MOTy, offset); + case MachineOperand::MO_RegisterMask: + break; + } + + return MCOperand(); +} + +MCOperand MaxisMCInstLower::createSub(MachineBasicBlock *BB1, + MachineBasicBlock *BB2, + MaxisMCExpr::MaxisExprKind Kind) const { + const MCSymbolRefExpr *Sym1 = MCSymbolRefExpr::create(BB1->getSymbol(), *Ctx); + const MCSymbolRefExpr *Sym2 = MCSymbolRefExpr::create(BB2->getSymbol(), *Ctx); + const MCBinaryExpr *Sub = MCBinaryExpr::createSub(Sym1, Sym2, *Ctx); + + return MCOperand::createExpr(MaxisMCExpr::create(Kind, Sub, *Ctx)); +} + +void MaxisMCInstLower:: +lowerLongBranchLUi(const MachineInstr *MI, MCInst &OutMI) const { + OutMI.setOpcode(Maxis::LUi); + + // Lower register operand. + OutMI.addOperand(LowerOperand(MI->getOperand(0))); + + // Create %hi($tgt-$baltgt). + OutMI.addOperand(createSub(MI->getOperand(1).getMBB(), + MI->getOperand(2).getMBB(), + MaxisMCExpr::MEK_HI)); +} + +void MaxisMCInstLower::lowerLongBranchADDiu( + const MachineInstr *MI, MCInst &OutMI, int Opcode, + MaxisMCExpr::MaxisExprKind Kind) const { + OutMI.setOpcode(Opcode); + + // Lower two register operands. + for (unsigned I = 0, E = 2; I != E; ++I) { + const MachineOperand &MO = MI->getOperand(I); + OutMI.addOperand(LowerOperand(MO)); + } + + // Create %lo($tgt-$baltgt) or %hi($tgt-$baltgt). + OutMI.addOperand(createSub(MI->getOperand(2).getMBB(), + MI->getOperand(3).getMBB(), Kind)); +} + +bool MaxisMCInstLower::lowerLongBranch(const MachineInstr *MI, + MCInst &OutMI) const { + switch (MI->getOpcode()) { + default: + return false; + case Maxis::LONG_BRANCH_LUi: + lowerLongBranchLUi(MI, OutMI); + return true; + case Maxis::LONG_BRANCH_ADDiu: + lowerLongBranchADDiu(MI, OutMI, Maxis::ADDiu, MaxisMCExpr::MEK_LO); + return true; + case Maxis::LONG_BRANCH_DADDiu: + unsigned TargetFlags = MI->getOperand(2).getTargetFlags(); + if (TargetFlags == MaxisII::MO_ABS_HI) + lowerLongBranchADDiu(MI, OutMI, Maxis::DADDiu, MaxisMCExpr::MEK_HI); + else if (TargetFlags == MaxisII::MO_ABS_LO) + lowerLongBranchADDiu(MI, OutMI, Maxis::DADDiu, MaxisMCExpr::MEK_LO); + else + report_fatal_error("Unexpected flags for LONG_BRANCH_DADDiu"); + return true; + } +} + +void MaxisMCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const { + if (lowerLongBranch(MI, OutMI)) + return; + + OutMI.setOpcode(MI->getOpcode()); + + for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { + const MachineOperand &MO = MI->getOperand(i); + MCOperand MCOp = LowerOperand(MO); + + if (MCOp.isValid()) + OutMI.addOperand(MCOp); + } +} diff --git a/lib/Target/Maxis/MaxisMCInstLower.h b/lib/Target/Maxis/MaxisMCInstLower.h new file mode 100644 index 00000000..e51cc279 --- /dev/null +++ b/lib/Target/Maxis/MaxisMCInstLower.h @@ -0,0 +1,54 @@ +//===- MaxisMCInstLower.h - Lower MachineInstr to MCInst --------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISMCINSTLOWER_H +#define LLVM_LIB_TARGET_MAXIS_MAXISMCINSTLOWER_H + +#include "MCTargetDesc/MaxisMCExpr.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/Support/Compiler.h" + +namespace llvm { + +class MachineBasicBlock; +class MachineInstr; +class MCContext; +class MCInst; +class MCOperand; +class MaxisAsmPrinter; + +/// MaxisMCInstLower - This class is used to lower an MachineInstr into an +/// MCInst. +class LLVM_LIBRARY_VISIBILITY MaxisMCInstLower { + using MachineOperandType = MachineOperand::MachineOperandType; + + MCContext *Ctx; + MaxisAsmPrinter &AsmPrinter; + +public: + MaxisMCInstLower(MaxisAsmPrinter &asmprinter); + + void Initialize(MCContext *C); + void Lower(const MachineInstr *MI, MCInst &OutMI) const; + MCOperand LowerOperand(const MachineOperand& MO, unsigned offset = 0) const; + +private: + MCOperand LowerSymbolOperand(const MachineOperand &MO, + MachineOperandType MOTy, unsigned Offset) const; + MCOperand createSub(MachineBasicBlock *BB1, MachineBasicBlock *BB2, + MaxisMCExpr::MaxisExprKind Kind) const; + void lowerLongBranchLUi(const MachineInstr *MI, MCInst &OutMI) const; + void lowerLongBranchADDiu(const MachineInstr *MI, MCInst &OutMI, int Opcode, + MaxisMCExpr::MaxisExprKind Kind) const; + bool lowerLongBranch(const MachineInstr *MI, MCInst &OutMI) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISMCINSTLOWER_H diff --git a/lib/Target/Maxis/MaxisMSAInstrFormats.td b/lib/Target/Maxis/MaxisMSAInstrFormats.td new file mode 100644 index 00000000..f89a8c87 --- /dev/null +++ b/lib/Target/Maxis/MaxisMSAInstrFormats.td @@ -0,0 +1,455 @@ +//===- MaxisMSAInstrFormats.td - Maxis Instruction Formats ---*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +class MSAInst : MaxisInst<(outs), (ins), "", [], NoItinerary, FrmOther>, + PredicateControl, ASE_MSA { + let EncodingPredicates = [HasStdEnc]; + let Inst{31-26} = 0b011110; +} + +class MSACBranch : MSAInst { + let Inst{31-26} = 0b010001; +} + +class MSASpecial : MSAInst { + let Inst{31-26} = 0b000000; +} + +class MSAPseudo pattern, + InstrItinClass itin = IIPseudo>: + MaxisPseudo { + let Predicates = [HasMSA]; +} + +class MSA_BIT_B_FMT major, bits<6> minor>: MSAInst { + bits<5> ws; + bits<5> wd; + bits<3> m; + + let Inst{25-23} = major; + let Inst{22-19} = 0b1110; + let Inst{18-16} = m; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_BIT_H_FMT major, bits<6> minor>: MSAInst { + bits<5> ws; + bits<5> wd; + bits<4> m; + + let Inst{25-23} = major; + let Inst{22-20} = 0b110; + let Inst{19-16} = m; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_BIT_W_FMT major, bits<6> minor>: MSAInst { + bits<5> ws; + bits<5> wd; + bits<5> m; + + let Inst{25-23} = major; + let Inst{22-21} = 0b10; + let Inst{20-16} = m; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_BIT_D_FMT major, bits<6> minor>: MSAInst { + bits<5> ws; + bits<5> wd; + bits<6> m; + + let Inst{25-23} = major; + let Inst{22} = 0b0; + let Inst{21-16} = m; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_2R_FILL_FMT major, bits<2> df, bits<6> minor>: MSAInst { + bits<5> rs; + bits<5> wd; + + let Inst{25-18} = major; + let Inst{17-16} = df; + let Inst{15-11} = rs; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_2R_FILL_D_FMT major, bits<2> df, bits<6> minor>: MSAInst { + bits<5> rs; + bits<5> wd; + + let Inst{25-18} = major; + let Inst{17-16} = df; + let Inst{15-11} = rs; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_2R_FMT major, bits<2> df, bits<6> minor>: MSAInst { + bits<5> ws; + bits<5> wd; + + let Inst{25-18} = major; + let Inst{17-16} = df; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_2RF_FMT major, bits<1> df, bits<6> minor>: MSAInst { + bits<5> ws; + bits<5> wd; + + let Inst{25-17} = major; + let Inst{16} = df; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_3R_FMT major, bits<2> df, bits<6> minor>: MSAInst { + bits<5> wt; + bits<5> ws; + bits<5> wd; + + let Inst{25-23} = major; + let Inst{22-21} = df; + let Inst{20-16} = wt; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_3RF_FMT major, bits<1> df, bits<6> minor>: MSAInst { + bits<5> wt; + bits<5> ws; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21} = df; + let Inst{20-16} = wt; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_3R_INDEX_FMT major, bits<2> df, bits<6> minor>: MSAInst { + bits<5> rt; + bits<5> ws; + bits<5> wd; + + let Inst{25-23} = major; + let Inst{22-21} = df; + let Inst{20-16} = rt; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_FMT major, bits<6> minor>: MSAInst { + bits<5> ws; + bits<5> wd; + + let Inst{25-16} = major; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_CFCMSA_FMT major, bits<6> minor>: MSAInst { + bits<5> rd; + bits<5> cs; + + let Inst{25-16} = major; + let Inst{15-11} = cs; + let Inst{10-6} = rd; + let Inst{5-0} = minor; +} + +class MSA_ELM_CTCMSA_FMT major, bits<6> minor>: MSAInst { + bits<5> rs; + bits<5> cd; + + let Inst{25-16} = major; + let Inst{15-11} = rs; + let Inst{10-6} = cd; + let Inst{5-0} = minor; +} + +class MSA_ELM_B_FMT major, bits<6> minor>: MSAInst { + bits<4> n; + bits<5> ws; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21-20} = 0b00; + let Inst{19-16} = n{3-0}; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_H_FMT major, bits<6> minor>: MSAInst { + bits<4> n; + bits<5> ws; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21-19} = 0b100; + let Inst{18-16} = n{2-0}; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_W_FMT major, bits<6> minor>: MSAInst { + bits<4> n; + bits<5> ws; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21-18} = 0b1100; + let Inst{17-16} = n{1-0}; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_D_FMT major, bits<6> minor>: MSAInst { + bits<4> n; + bits<5> ws; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21-17} = 0b11100; + let Inst{16} = n{0}; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_COPY_B_FMT major, bits<6> minor>: MSAInst { + bits<4> n; + bits<5> ws; + bits<5> rd; + + let Inst{25-22} = major; + let Inst{21-20} = 0b00; + let Inst{19-16} = n{3-0}; + let Inst{15-11} = ws; + let Inst{10-6} = rd; + let Inst{5-0} = minor; +} + +class MSA_ELM_COPY_H_FMT major, bits<6> minor>: MSAInst { + bits<4> n; + bits<5> ws; + bits<5> rd; + + let Inst{25-22} = major; + let Inst{21-19} = 0b100; + let Inst{18-16} = n{2-0}; + let Inst{15-11} = ws; + let Inst{10-6} = rd; + let Inst{5-0} = minor; +} + +class MSA_ELM_COPY_W_FMT major, bits<6> minor>: MSAInst { + bits<4> n; + bits<5> ws; + bits<5> rd; + + let Inst{25-22} = major; + let Inst{21-18} = 0b1100; + let Inst{17-16} = n{1-0}; + let Inst{15-11} = ws; + let Inst{10-6} = rd; + let Inst{5-0} = minor; +} + +class MSA_ELM_COPY_D_FMT major, bits<6> minor>: MSAInst { + bits<4> n; + bits<5> ws; + bits<5> rd; + + let Inst{25-22} = major; + let Inst{21-17} = 0b11100; + let Inst{16} = n{0}; + let Inst{15-11} = ws; + let Inst{10-6} = rd; + let Inst{5-0} = minor; +} + +class MSA_ELM_INSERT_B_FMT major, bits<6> minor>: MSAInst { + bits<6> n; + bits<5> rs; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21-20} = 0b00; + let Inst{19-16} = n{3-0}; + let Inst{15-11} = rs; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_INSERT_H_FMT major, bits<6> minor>: MSAInst { + bits<6> n; + bits<5> rs; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21-19} = 0b100; + let Inst{18-16} = n{2-0}; + let Inst{15-11} = rs; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_INSERT_W_FMT major, bits<6> minor>: MSAInst { + bits<6> n; + bits<5> rs; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21-18} = 0b1100; + let Inst{17-16} = n{1-0}; + let Inst{15-11} = rs; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_ELM_INSERT_D_FMT major, bits<6> minor>: MSAInst { + bits<6> n; + bits<5> rs; + bits<5> wd; + + let Inst{25-22} = major; + let Inst{21-17} = 0b11100; + let Inst{16} = n{0}; + let Inst{15-11} = rs; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_I5_FMT major, bits<2> df, bits<6> minor>: MSAInst { + bits<5> imm; + bits<5> ws; + bits<5> wd; + + let Inst{25-23} = major; + let Inst{22-21} = df; + let Inst{20-16} = imm; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_I8_FMT major, bits<6> minor>: MSAInst { + bits<8> u8; + bits<5> ws; + bits<5> wd; + + let Inst{25-24} = major; + let Inst{23-16} = u8; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_I10_FMT major, bits<2> df, bits<6> minor>: MSAInst { + bits<10> s10; + bits<5> wd; + + let Inst{25-23} = major; + let Inst{22-21} = df; + let Inst{20-11} = s10; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_MI10_FMT df, bits<4> minor>: MSAInst { + bits<21> addr; + bits<5> wd; + + let Inst{25-16} = addr{9-0}; + let Inst{15-11} = addr{20-16}; + let Inst{10-6} = wd; + let Inst{5-2} = minor; + let Inst{1-0} = df; +} + +class MSA_VEC_FMT major, bits<6> minor>: MSAInst { + bits<5> wt; + bits<5> ws; + bits<5> wd; + + let Inst{25-21} = major; + let Inst{20-16} = wt; + let Inst{15-11} = ws; + let Inst{10-6} = wd; + let Inst{5-0} = minor; +} + +class MSA_CBRANCH_FMT major, bits<2> df>: MSACBranch { + bits<16> offset; + bits<5> wt; + + let Inst{25-23} = major; + let Inst{22-21} = df; + let Inst{20-16} = wt; + let Inst{15-0} = offset; +} + +class MSA_CBRANCH_V_FMT major>: MSACBranch { + bits<16> offset; + bits<5> wt; + + let Inst{25-21} = major; + let Inst{20-16} = wt; + let Inst{15-0} = offset; +} + +class SPECIAL_LSA_FMT minor>: MSASpecial { + bits<5> rs; + bits<5> rt; + bits<5> rd; + bits<2> sa; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-8} = 0b000; + let Inst{7-6} = sa; + let Inst{5-0} = minor; +} + +class SPECIAL_DLSA_FMT minor>: MSASpecial { + bits<5> rs; + bits<5> rt; + bits<5> rd; + bits<2> sa; + + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-8} = 0b000; + let Inst{7-6} = sa; + let Inst{5-0} = minor; +} diff --git a/lib/Target/Maxis/MaxisMSAInstrInfo.td b/lib/Target/Maxis/MaxisMSAInstrInfo.td new file mode 100644 index 00000000..88ce39c7 --- /dev/null +++ b/lib/Target/Maxis/MaxisMSAInstrInfo.td @@ -0,0 +1,4020 @@ +//===- MaxisMSAInstrInfo.td - MSA ASE instructions -*- tablegen ------------*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes Maxis MSA ASE instructions. +// +//===----------------------------------------------------------------------===// + +def SDT_MaxisVecCond : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisVec<1>]>; +def SDT_VSetCC : SDTypeProfile<1, 3, [SDTCisInt<0>, + SDTCisInt<1>, + SDTCisSameAs<1, 2>, + SDTCisVT<3, OtherVT>]>; +def SDT_VFSetCC : SDTypeProfile<1, 3, [SDTCisInt<0>, + SDTCisFP<1>, + SDTCisSameAs<1, 2>, + SDTCisVT<3, OtherVT>]>; +def SDT_VSHF : SDTypeProfile<1, 3, [SDTCisInt<0>, SDTCisVec<0>, + SDTCisInt<1>, SDTCisVec<1>, + SDTCisSameAs<0, 2>, SDTCisSameAs<2, 3>]>; +def SDT_SHF : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisVec<0>, + SDTCisVT<1, i32>, SDTCisSameAs<0, 2>]>; +def SDT_ILV : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisVec<0>, + SDTCisSameAs<0, 1>, SDTCisSameAs<1, 2>]>; +def SDT_INSVE : SDTypeProfile<1, 4, [SDTCisVec<0>, SDTCisSameAs<0, 1>, + SDTCisVT<2, i32>, SDTCisSameAs<0, 3>, + SDTCisVT<4, i32>]>; + +def MaxisVAllNonZero : SDNode<"MaxisISD::VALL_NONZERO", SDT_MaxisVecCond>; +def MaxisVAnyNonZero : SDNode<"MaxisISD::VANY_NONZERO", SDT_MaxisVecCond>; +def MaxisVAllZero : SDNode<"MaxisISD::VALL_ZERO", SDT_MaxisVecCond>; +def MaxisVAnyZero : SDNode<"MaxisISD::VANY_ZERO", SDT_MaxisVecCond>; +def MaxisVSMax : SDNode<"MaxisISD::VSMAX", SDTIntBinOp, + [SDNPCommutative, SDNPAssociative]>; +def MaxisVSMin : SDNode<"MaxisISD::VSMIN", SDTIntBinOp, + [SDNPCommutative, SDNPAssociative]>; +def MaxisVUMax : SDNode<"MaxisISD::VUMAX", SDTIntBinOp, + [SDNPCommutative, SDNPAssociative]>; +def MaxisVUMin : SDNode<"MaxisISD::VUMIN", SDTIntBinOp, + [SDNPCommutative, SDNPAssociative]>; +def MaxisVNOR : SDNode<"MaxisISD::VNOR", SDTIntBinOp, + [SDNPCommutative, SDNPAssociative]>; +def MaxisVSHF : SDNode<"MaxisISD::VSHF", SDT_VSHF>; +def MaxisSHF : SDNode<"MaxisISD::SHF", SDT_SHF>; +def MaxisILVEV : SDNode<"MaxisISD::ILVEV", SDT_ILV>; +def MaxisILVOD : SDNode<"MaxisISD::ILVOD", SDT_ILV>; +def MaxisILVL : SDNode<"MaxisISD::ILVL", SDT_ILV>; +def MaxisILVR : SDNode<"MaxisISD::ILVR", SDT_ILV>; +def MaxisPCKEV : SDNode<"MaxisISD::PCKEV", SDT_ILV>; +def MaxisPCKOD : SDNode<"MaxisISD::PCKOD", SDT_ILV>; +def MaxisINSVE : SDNode<"MaxisISD::INSVE", SDT_INSVE>; + +def vsetcc : SDNode<"ISD::SETCC", SDT_VSetCC>; +def vfsetcc : SDNode<"ISD::SETCC", SDT_VFSetCC>; + +def MaxisVExtractSExt : SDNode<"MaxisISD::VEXTRACT_SEXT_ELT", + SDTypeProfile<1, 3, [SDTCisPtrTy<2>]>, []>; +def MaxisVExtractZExt : SDNode<"MaxisISD::VEXTRACT_ZEXT_ELT", + SDTypeProfile<1, 3, [SDTCisPtrTy<2>]>, []>; + +def immZExt1Ptr : ImmLeaf(Imm);}]>; +def immZExt2Ptr : ImmLeaf(Imm);}]>; +def immZExt3Ptr : ImmLeaf(Imm);}]>; +def immZExt4Ptr : ImmLeaf(Imm);}]>; + +// Operands + +def immZExt2Lsa : ImmLeaf(Imm - 1);}]>; + +// Pattern fragments +def vextract_sext_i8 : PatFrag<(ops node:$vec, node:$idx), + (MaxisVExtractSExt node:$vec, node:$idx, i8)>; +def vextract_sext_i16 : PatFrag<(ops node:$vec, node:$idx), + (MaxisVExtractSExt node:$vec, node:$idx, i16)>; +def vextract_sext_i32 : PatFrag<(ops node:$vec, node:$idx), + (MaxisVExtractSExt node:$vec, node:$idx, i32)>; +def vextract_sext_i64 : PatFrag<(ops node:$vec, node:$idx), + (MaxisVExtractSExt node:$vec, node:$idx, i64)>; + +def vextract_zext_i8 : PatFrag<(ops node:$vec, node:$idx), + (MaxisVExtractZExt node:$vec, node:$idx, i8)>; +def vextract_zext_i16 : PatFrag<(ops node:$vec, node:$idx), + (MaxisVExtractZExt node:$vec, node:$idx, i16)>; +def vextract_zext_i32 : PatFrag<(ops node:$vec, node:$idx), + (MaxisVExtractZExt node:$vec, node:$idx, i32)>; +def vextract_zext_i64 : PatFrag<(ops node:$vec, node:$idx), + (MaxisVExtractZExt node:$vec, node:$idx, i64)>; + +def vinsert_v16i8 : PatFrag<(ops node:$vec, node:$val, node:$idx), + (v16i8 (vector_insert node:$vec, node:$val, node:$idx))>; +def vinsert_v8i16 : PatFrag<(ops node:$vec, node:$val, node:$idx), + (v8i16 (vector_insert node:$vec, node:$val, node:$idx))>; +def vinsert_v4i32 : PatFrag<(ops node:$vec, node:$val, node:$idx), + (v4i32 (vector_insert node:$vec, node:$val, node:$idx))>; +def vinsert_v2i64 : PatFrag<(ops node:$vec, node:$val, node:$idx), + (v2i64 (vector_insert node:$vec, node:$val, node:$idx))>; + +def insve_v16i8 : PatFrag<(ops node:$v1, node:$i1, node:$v2, node:$i2), + (v16i8 (MaxisINSVE node:$v1, node:$i1, node:$v2, node:$i2))>; +def insve_v8i16 : PatFrag<(ops node:$v1, node:$i1, node:$v2, node:$i2), + (v8i16 (MaxisINSVE node:$v1, node:$i1, node:$v2, node:$i2))>; +def insve_v4i32 : PatFrag<(ops node:$v1, node:$i1, node:$v2, node:$i2), + (v4i32 (MaxisINSVE node:$v1, node:$i1, node:$v2, node:$i2))>; +def insve_v2i64 : PatFrag<(ops node:$v1, node:$i1, node:$v2, node:$i2), + (v2i64 (MaxisINSVE node:$v1, node:$i1, node:$v2, node:$i2))>; + +class vfsetcc_type : + PatFrag<(ops node:$lhs, node:$rhs), + (ResTy (vfsetcc (OpTy node:$lhs), (OpTy node:$rhs), CC))>; + +// ISD::SETFALSE cannot occur +def vfsetoeq_v4f32 : vfsetcc_type; +def vfsetoeq_v2f64 : vfsetcc_type; +def vfsetoge_v4f32 : vfsetcc_type; +def vfsetoge_v2f64 : vfsetcc_type; +def vfsetogt_v4f32 : vfsetcc_type; +def vfsetogt_v2f64 : vfsetcc_type; +def vfsetole_v4f32 : vfsetcc_type; +def vfsetole_v2f64 : vfsetcc_type; +def vfsetolt_v4f32 : vfsetcc_type; +def vfsetolt_v2f64 : vfsetcc_type; +def vfsetone_v4f32 : vfsetcc_type; +def vfsetone_v2f64 : vfsetcc_type; +def vfsetord_v4f32 : vfsetcc_type; +def vfsetord_v2f64 : vfsetcc_type; +def vfsetun_v4f32 : vfsetcc_type; +def vfsetun_v2f64 : vfsetcc_type; +def vfsetueq_v4f32 : vfsetcc_type; +def vfsetueq_v2f64 : vfsetcc_type; +def vfsetuge_v4f32 : vfsetcc_type; +def vfsetuge_v2f64 : vfsetcc_type; +def vfsetugt_v4f32 : vfsetcc_type; +def vfsetugt_v2f64 : vfsetcc_type; +def vfsetule_v4f32 : vfsetcc_type; +def vfsetule_v2f64 : vfsetcc_type; +def vfsetult_v4f32 : vfsetcc_type; +def vfsetult_v2f64 : vfsetcc_type; +def vfsetune_v4f32 : vfsetcc_type; +def vfsetune_v2f64 : vfsetcc_type; +// ISD::SETTRUE cannot occur +// ISD::SETFALSE2 cannot occur +// ISD::SETTRUE2 cannot occur + +class vsetcc_type : + PatFrag<(ops node:$lhs, node:$rhs), + (ResTy (vsetcc node:$lhs, node:$rhs, CC))>; + +def vseteq_v16i8 : vsetcc_type; +def vseteq_v8i16 : vsetcc_type; +def vseteq_v4i32 : vsetcc_type; +def vseteq_v2i64 : vsetcc_type; +def vsetle_v16i8 : vsetcc_type; +def vsetle_v8i16 : vsetcc_type; +def vsetle_v4i32 : vsetcc_type; +def vsetle_v2i64 : vsetcc_type; +def vsetlt_v16i8 : vsetcc_type; +def vsetlt_v8i16 : vsetcc_type; +def vsetlt_v4i32 : vsetcc_type; +def vsetlt_v2i64 : vsetcc_type; +def vsetule_v16i8 : vsetcc_type; +def vsetule_v8i16 : vsetcc_type; +def vsetule_v4i32 : vsetcc_type; +def vsetule_v2i64 : vsetcc_type; +def vsetult_v16i8 : vsetcc_type; +def vsetult_v8i16 : vsetcc_type; +def vsetult_v4i32 : vsetcc_type; +def vsetult_v2i64 : vsetcc_type; + +def vsplati8 : PatFrag<(ops node:$e0), + (v16i8 (build_vector node:$e0, node:$e0, + node:$e0, node:$e0, + node:$e0, node:$e0, + node:$e0, node:$e0, + node:$e0, node:$e0, + node:$e0, node:$e0, + node:$e0, node:$e0, + node:$e0, node:$e0))>; +def vsplati16 : PatFrag<(ops node:$e0), + (v8i16 (build_vector node:$e0, node:$e0, + node:$e0, node:$e0, + node:$e0, node:$e0, + node:$e0, node:$e0))>; +def vsplati32 : PatFrag<(ops node:$e0), + (v4i32 (build_vector node:$e0, node:$e0, + node:$e0, node:$e0))>; +def vsplati64 : PatFrag<(ops node:$e0), + (v2i64 (build_vector node:$e0, node:$e0))>; +def vsplatf32 : PatFrag<(ops node:$e0), + (v4f32 (build_vector node:$e0, node:$e0, + node:$e0, node:$e0))>; +def vsplatf64 : PatFrag<(ops node:$e0), + (v2f64 (build_vector node:$e0, node:$e0))>; + +def vsplati8_elt : PatFrag<(ops node:$v, node:$i), + (MaxisVSHF (vsplati8 node:$i), node:$v, node:$v)>; +def vsplati16_elt : PatFrag<(ops node:$v, node:$i), + (MaxisVSHF (vsplati16 node:$i), node:$v, node:$v)>; +def vsplati32_elt : PatFrag<(ops node:$v, node:$i), + (MaxisVSHF (vsplati32 node:$i), node:$v, node:$v)>; +def vsplati64_elt : PatFrag<(ops node:$v, node:$i), + (MaxisVSHF (vsplati64 node:$i), node:$v, node:$v)>; + +class SplatPatLeaf + : PatLeaf { + Operand OpClass = opclass; +} + +class SplatComplexPattern roots = [], + list props = []> : + ComplexPattern { + Operand OpClass = opclass; +} + +def vsplati8_uimm3 : SplatComplexPattern; + +def vsplati8_uimm4 : SplatComplexPattern; + +def vsplati8_uimm5 : SplatComplexPattern; + +def vsplati8_uimm8 : SplatComplexPattern; + +def vsplati8_simm5 : SplatComplexPattern; + +def vsplati16_uimm3 : SplatComplexPattern; + +def vsplati16_uimm4 : SplatComplexPattern; + +def vsplati16_uimm5 : SplatComplexPattern; + +def vsplati16_simm5 : SplatComplexPattern; + +def vsplati32_uimm2 : SplatComplexPattern; + +def vsplati32_uimm5 : SplatComplexPattern; + +def vsplati32_simm5 : SplatComplexPattern; + +def vsplati64_uimm1 : SplatComplexPattern; + +def vsplati64_uimm5 : SplatComplexPattern; + +def vsplati64_uimm6 : SplatComplexPattern; + +def vsplati64_simm5 : SplatComplexPattern; + +// Any build_vector that is a constant splat with a value that is an exact +// power of 2 +def vsplat_uimm_pow2 : ComplexPattern; + +// Any build_vector that is a constant splat with a value that is the bitwise +// inverse of an exact power of 2 +def vsplat_uimm_inv_pow2 : ComplexPattern; + +// Any build_vector that is a constant splat with only a consecutive sequence +// of left-most bits set. +def vsplat_maskl_bits_uimm3 + : SplatComplexPattern; +def vsplat_maskl_bits_uimm4 + : SplatComplexPattern; +def vsplat_maskl_bits_uimm5 + : SplatComplexPattern; +def vsplat_maskl_bits_uimm6 + : SplatComplexPattern; + +// Any build_vector that is a constant splat with only a consecutive sequence +// of right-most bits set. +def vsplat_maskr_bits_uimm3 + : SplatComplexPattern; +def vsplat_maskr_bits_uimm4 + : SplatComplexPattern; +def vsplat_maskr_bits_uimm5 + : SplatComplexPattern; +def vsplat_maskr_bits_uimm6 + : SplatComplexPattern; + +// Any build_vector that is a constant splat with a value that equals 1 +// FIXME: These should be a ComplexPattern but we can't use them because the +// ISel generator requires the uses to have a name, but providing a name +// causes other errors ("used in pattern but not operand list") +def vsplat_imm_eq_1 : PatLeaf<(build_vector), [{ + APInt Imm; + EVT EltTy = N->getValueType(0).getVectorElementType(); + + return selectVSplat(N, Imm, EltTy.getSizeInBits()) && + Imm.getBitWidth() == EltTy.getSizeInBits() && Imm == 1; +}]>; + +def vsplati64_imm_eq_1 : PatLeaf<(bitconvert (v4i32 (build_vector))), [{ + APInt Imm; + SDNode *BV = N->getOperand(0).getNode(); + EVT EltTy = N->getValueType(0).getVectorElementType(); + + return selectVSplat(BV, Imm, EltTy.getSizeInBits()) && + Imm.getBitWidth() == EltTy.getSizeInBits() && Imm == 1; +}]>; + +def vbclr_b : PatFrag<(ops node:$ws, node:$wt), + (and node:$ws, (xor (shl vsplat_imm_eq_1, node:$wt), + immAllOnesV))>; +def vbclr_h : PatFrag<(ops node:$ws, node:$wt), + (and node:$ws, (xor (shl vsplat_imm_eq_1, node:$wt), + immAllOnesV))>; +def vbclr_w : PatFrag<(ops node:$ws, node:$wt), + (and node:$ws, (xor (shl vsplat_imm_eq_1, node:$wt), + immAllOnesV))>; +def vbclr_d : PatFrag<(ops node:$ws, node:$wt), + (and node:$ws, (xor (shl (v2i64 vsplati64_imm_eq_1), + node:$wt), + (bitconvert (v4i32 immAllOnesV))))>; + +def vbneg_b : PatFrag<(ops node:$ws, node:$wt), + (xor node:$ws, (shl vsplat_imm_eq_1, node:$wt))>; +def vbneg_h : PatFrag<(ops node:$ws, node:$wt), + (xor node:$ws, (shl vsplat_imm_eq_1, node:$wt))>; +def vbneg_w : PatFrag<(ops node:$ws, node:$wt), + (xor node:$ws, (shl vsplat_imm_eq_1, node:$wt))>; +def vbneg_d : PatFrag<(ops node:$ws, node:$wt), + (xor node:$ws, (shl (v2i64 vsplati64_imm_eq_1), + node:$wt))>; + +def vbset_b : PatFrag<(ops node:$ws, node:$wt), + (or node:$ws, (shl vsplat_imm_eq_1, node:$wt))>; +def vbset_h : PatFrag<(ops node:$ws, node:$wt), + (or node:$ws, (shl vsplat_imm_eq_1, node:$wt))>; +def vbset_w : PatFrag<(ops node:$ws, node:$wt), + (or node:$ws, (shl vsplat_imm_eq_1, node:$wt))>; +def vbset_d : PatFrag<(ops node:$ws, node:$wt), + (or node:$ws, (shl (v2i64 vsplati64_imm_eq_1), + node:$wt))>; + +def fms : PatFrag<(ops node:$wd, node:$ws, node:$wt), + (fsub node:$wd, (fmul node:$ws, node:$wt))>; + +def muladd : PatFrag<(ops node:$wd, node:$ws, node:$wt), + (add node:$wd, (mul node:$ws, node:$wt))>; + +def mulsub : PatFrag<(ops node:$wd, node:$ws, node:$wt), + (sub node:$wd, (mul node:$ws, node:$wt))>; + +def mul_fexp2 : PatFrag<(ops node:$ws, node:$wt), + (fmul node:$ws, (fexp2 node:$wt))>; + +// Instruction encoding. +class ADD_A_B_ENC : MSA_3R_FMT<0b000, 0b00, 0b010000>; +class ADD_A_H_ENC : MSA_3R_FMT<0b000, 0b01, 0b010000>; +class ADD_A_W_ENC : MSA_3R_FMT<0b000, 0b10, 0b010000>; +class ADD_A_D_ENC : MSA_3R_FMT<0b000, 0b11, 0b010000>; + +class ADDS_A_B_ENC : MSA_3R_FMT<0b001, 0b00, 0b010000>; +class ADDS_A_H_ENC : MSA_3R_FMT<0b001, 0b01, 0b010000>; +class ADDS_A_W_ENC : MSA_3R_FMT<0b001, 0b10, 0b010000>; +class ADDS_A_D_ENC : MSA_3R_FMT<0b001, 0b11, 0b010000>; + +class ADDS_S_B_ENC : MSA_3R_FMT<0b010, 0b00, 0b010000>; +class ADDS_S_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b010000>; +class ADDS_S_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b010000>; +class ADDS_S_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b010000>; + +class ADDS_U_B_ENC : MSA_3R_FMT<0b011, 0b00, 0b010000>; +class ADDS_U_H_ENC : MSA_3R_FMT<0b011, 0b01, 0b010000>; +class ADDS_U_W_ENC : MSA_3R_FMT<0b011, 0b10, 0b010000>; +class ADDS_U_D_ENC : MSA_3R_FMT<0b011, 0b11, 0b010000>; + +class ADDV_B_ENC : MSA_3R_FMT<0b000, 0b00, 0b001110>; +class ADDV_H_ENC : MSA_3R_FMT<0b000, 0b01, 0b001110>; +class ADDV_W_ENC : MSA_3R_FMT<0b000, 0b10, 0b001110>; +class ADDV_D_ENC : MSA_3R_FMT<0b000, 0b11, 0b001110>; + +class ADDVI_B_ENC : MSA_I5_FMT<0b000, 0b00, 0b000110>; +class ADDVI_H_ENC : MSA_I5_FMT<0b000, 0b01, 0b000110>; +class ADDVI_W_ENC : MSA_I5_FMT<0b000, 0b10, 0b000110>; +class ADDVI_D_ENC : MSA_I5_FMT<0b000, 0b11, 0b000110>; + +class AND_V_ENC : MSA_VEC_FMT<0b00000, 0b011110>; + +class ANDI_B_ENC : MSA_I8_FMT<0b00, 0b000000>; + +class ASUB_S_B_ENC : MSA_3R_FMT<0b100, 0b00, 0b010001>; +class ASUB_S_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b010001>; +class ASUB_S_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b010001>; +class ASUB_S_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b010001>; + +class ASUB_U_B_ENC : MSA_3R_FMT<0b101, 0b00, 0b010001>; +class ASUB_U_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b010001>; +class ASUB_U_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b010001>; +class ASUB_U_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b010001>; + +class AVE_S_B_ENC : MSA_3R_FMT<0b100, 0b00, 0b010000>; +class AVE_S_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b010000>; +class AVE_S_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b010000>; +class AVE_S_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b010000>; + +class AVE_U_B_ENC : MSA_3R_FMT<0b101, 0b00, 0b010000>; +class AVE_U_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b010000>; +class AVE_U_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b010000>; +class AVE_U_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b010000>; + +class AVER_S_B_ENC : MSA_3R_FMT<0b110, 0b00, 0b010000>; +class AVER_S_H_ENC : MSA_3R_FMT<0b110, 0b01, 0b010000>; +class AVER_S_W_ENC : MSA_3R_FMT<0b110, 0b10, 0b010000>; +class AVER_S_D_ENC : MSA_3R_FMT<0b110, 0b11, 0b010000>; + +class AVER_U_B_ENC : MSA_3R_FMT<0b111, 0b00, 0b010000>; +class AVER_U_H_ENC : MSA_3R_FMT<0b111, 0b01, 0b010000>; +class AVER_U_W_ENC : MSA_3R_FMT<0b111, 0b10, 0b010000>; +class AVER_U_D_ENC : MSA_3R_FMT<0b111, 0b11, 0b010000>; + +class BCLR_B_ENC : MSA_3R_FMT<0b011, 0b00, 0b001101>; +class BCLR_H_ENC : MSA_3R_FMT<0b011, 0b01, 0b001101>; +class BCLR_W_ENC : MSA_3R_FMT<0b011, 0b10, 0b001101>; +class BCLR_D_ENC : MSA_3R_FMT<0b011, 0b11, 0b001101>; + +class BCLRI_B_ENC : MSA_BIT_B_FMT<0b011, 0b001001>; +class BCLRI_H_ENC : MSA_BIT_H_FMT<0b011, 0b001001>; +class BCLRI_W_ENC : MSA_BIT_W_FMT<0b011, 0b001001>; +class BCLRI_D_ENC : MSA_BIT_D_FMT<0b011, 0b001001>; + +class BINSL_B_ENC : MSA_3R_FMT<0b110, 0b00, 0b001101>; +class BINSL_H_ENC : MSA_3R_FMT<0b110, 0b01, 0b001101>; +class BINSL_W_ENC : MSA_3R_FMT<0b110, 0b10, 0b001101>; +class BINSL_D_ENC : MSA_3R_FMT<0b110, 0b11, 0b001101>; + +class BINSLI_B_ENC : MSA_BIT_B_FMT<0b110, 0b001001>; +class BINSLI_H_ENC : MSA_BIT_H_FMT<0b110, 0b001001>; +class BINSLI_W_ENC : MSA_BIT_W_FMT<0b110, 0b001001>; +class BINSLI_D_ENC : MSA_BIT_D_FMT<0b110, 0b001001>; + +class BINSR_B_ENC : MSA_3R_FMT<0b111, 0b00, 0b001101>; +class BINSR_H_ENC : MSA_3R_FMT<0b111, 0b01, 0b001101>; +class BINSR_W_ENC : MSA_3R_FMT<0b111, 0b10, 0b001101>; +class BINSR_D_ENC : MSA_3R_FMT<0b111, 0b11, 0b001101>; + +class BINSRI_B_ENC : MSA_BIT_B_FMT<0b111, 0b001001>; +class BINSRI_H_ENC : MSA_BIT_H_FMT<0b111, 0b001001>; +class BINSRI_W_ENC : MSA_BIT_W_FMT<0b111, 0b001001>; +class BINSRI_D_ENC : MSA_BIT_D_FMT<0b111, 0b001001>; + +class BMNZ_V_ENC : MSA_VEC_FMT<0b00100, 0b011110>; + +class BMNZI_B_ENC : MSA_I8_FMT<0b00, 0b000001>; + +class BMZ_V_ENC : MSA_VEC_FMT<0b00101, 0b011110>; + +class BMZI_B_ENC : MSA_I8_FMT<0b01, 0b000001>; + +class BNEG_B_ENC : MSA_3R_FMT<0b101, 0b00, 0b001101>; +class BNEG_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b001101>; +class BNEG_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b001101>; +class BNEG_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b001101>; + +class BNEGI_B_ENC : MSA_BIT_B_FMT<0b101, 0b001001>; +class BNEGI_H_ENC : MSA_BIT_H_FMT<0b101, 0b001001>; +class BNEGI_W_ENC : MSA_BIT_W_FMT<0b101, 0b001001>; +class BNEGI_D_ENC : MSA_BIT_D_FMT<0b101, 0b001001>; + +class BNZ_B_ENC : MSA_CBRANCH_FMT<0b111, 0b00>; +class BNZ_H_ENC : MSA_CBRANCH_FMT<0b111, 0b01>; +class BNZ_W_ENC : MSA_CBRANCH_FMT<0b111, 0b10>; +class BNZ_D_ENC : MSA_CBRANCH_FMT<0b111, 0b11>; + +class BNZ_V_ENC : MSA_CBRANCH_V_FMT<0b01111>; + +class BSEL_V_ENC : MSA_VEC_FMT<0b00110, 0b011110>; + +class BSELI_B_ENC : MSA_I8_FMT<0b10, 0b000001>; + +class BSET_B_ENC : MSA_3R_FMT<0b100, 0b00, 0b001101>; +class BSET_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b001101>; +class BSET_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b001101>; +class BSET_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b001101>; + +class BSETI_B_ENC : MSA_BIT_B_FMT<0b100, 0b001001>; +class BSETI_H_ENC : MSA_BIT_H_FMT<0b100, 0b001001>; +class BSETI_W_ENC : MSA_BIT_W_FMT<0b100, 0b001001>; +class BSETI_D_ENC : MSA_BIT_D_FMT<0b100, 0b001001>; + +class BZ_B_ENC : MSA_CBRANCH_FMT<0b110, 0b00>; +class BZ_H_ENC : MSA_CBRANCH_FMT<0b110, 0b01>; +class BZ_W_ENC : MSA_CBRANCH_FMT<0b110, 0b10>; +class BZ_D_ENC : MSA_CBRANCH_FMT<0b110, 0b11>; + +class BZ_V_ENC : MSA_CBRANCH_V_FMT<0b01011>; + +class CEQ_B_ENC : MSA_3R_FMT<0b000, 0b00, 0b001111>; +class CEQ_H_ENC : MSA_3R_FMT<0b000, 0b01, 0b001111>; +class CEQ_W_ENC : MSA_3R_FMT<0b000, 0b10, 0b001111>; +class CEQ_D_ENC : MSA_3R_FMT<0b000, 0b11, 0b001111>; + +class CEQI_B_ENC : MSA_I5_FMT<0b000, 0b00, 0b000111>; +class CEQI_H_ENC : MSA_I5_FMT<0b000, 0b01, 0b000111>; +class CEQI_W_ENC : MSA_I5_FMT<0b000, 0b10, 0b000111>; +class CEQI_D_ENC : MSA_I5_FMT<0b000, 0b11, 0b000111>; + +class CFCMSA_ENC : MSA_ELM_CFCMSA_FMT<0b0001111110, 0b011001>; + +class CLE_S_B_ENC : MSA_3R_FMT<0b100, 0b00, 0b001111>; +class CLE_S_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b001111>; +class CLE_S_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b001111>; +class CLE_S_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b001111>; + +class CLE_U_B_ENC : MSA_3R_FMT<0b101, 0b00, 0b001111>; +class CLE_U_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b001111>; +class CLE_U_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b001111>; +class CLE_U_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b001111>; + +class CLEI_S_B_ENC : MSA_I5_FMT<0b100, 0b00, 0b000111>; +class CLEI_S_H_ENC : MSA_I5_FMT<0b100, 0b01, 0b000111>; +class CLEI_S_W_ENC : MSA_I5_FMT<0b100, 0b10, 0b000111>; +class CLEI_S_D_ENC : MSA_I5_FMT<0b100, 0b11, 0b000111>; + +class CLEI_U_B_ENC : MSA_I5_FMT<0b101, 0b00, 0b000111>; +class CLEI_U_H_ENC : MSA_I5_FMT<0b101, 0b01, 0b000111>; +class CLEI_U_W_ENC : MSA_I5_FMT<0b101, 0b10, 0b000111>; +class CLEI_U_D_ENC : MSA_I5_FMT<0b101, 0b11, 0b000111>; + +class CLT_S_B_ENC : MSA_3R_FMT<0b010, 0b00, 0b001111>; +class CLT_S_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b001111>; +class CLT_S_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b001111>; +class CLT_S_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b001111>; + +class CLT_U_B_ENC : MSA_3R_FMT<0b011, 0b00, 0b001111>; +class CLT_U_H_ENC : MSA_3R_FMT<0b011, 0b01, 0b001111>; +class CLT_U_W_ENC : MSA_3R_FMT<0b011, 0b10, 0b001111>; +class CLT_U_D_ENC : MSA_3R_FMT<0b011, 0b11, 0b001111>; + +class CLTI_S_B_ENC : MSA_I5_FMT<0b010, 0b00, 0b000111>; +class CLTI_S_H_ENC : MSA_I5_FMT<0b010, 0b01, 0b000111>; +class CLTI_S_W_ENC : MSA_I5_FMT<0b010, 0b10, 0b000111>; +class CLTI_S_D_ENC : MSA_I5_FMT<0b010, 0b11, 0b000111>; + +class CLTI_U_B_ENC : MSA_I5_FMT<0b011, 0b00, 0b000111>; +class CLTI_U_H_ENC : MSA_I5_FMT<0b011, 0b01, 0b000111>; +class CLTI_U_W_ENC : MSA_I5_FMT<0b011, 0b10, 0b000111>; +class CLTI_U_D_ENC : MSA_I5_FMT<0b011, 0b11, 0b000111>; + +class COPY_S_B_ENC : MSA_ELM_COPY_B_FMT<0b0010, 0b011001>; +class COPY_S_H_ENC : MSA_ELM_COPY_H_FMT<0b0010, 0b011001>; +class COPY_S_W_ENC : MSA_ELM_COPY_W_FMT<0b0010, 0b011001>; +class COPY_S_D_ENC : MSA_ELM_COPY_D_FMT<0b0010, 0b011001>; + +class COPY_U_B_ENC : MSA_ELM_COPY_B_FMT<0b0011, 0b011001>; +class COPY_U_H_ENC : MSA_ELM_COPY_H_FMT<0b0011, 0b011001>; +class COPY_U_W_ENC : MSA_ELM_COPY_W_FMT<0b0011, 0b011001>; + +class CTCMSA_ENC : MSA_ELM_CTCMSA_FMT<0b0000111110, 0b011001>; + +class DIV_S_B_ENC : MSA_3R_FMT<0b100, 0b00, 0b010010>; +class DIV_S_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b010010>; +class DIV_S_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b010010>; +class DIV_S_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b010010>; + +class DIV_U_B_ENC : MSA_3R_FMT<0b101, 0b00, 0b010010>; +class DIV_U_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b010010>; +class DIV_U_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b010010>; +class DIV_U_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b010010>; + +class DOTP_S_H_ENC : MSA_3R_FMT<0b000, 0b01, 0b010011>; +class DOTP_S_W_ENC : MSA_3R_FMT<0b000, 0b10, 0b010011>; +class DOTP_S_D_ENC : MSA_3R_FMT<0b000, 0b11, 0b010011>; + +class DOTP_U_H_ENC : MSA_3R_FMT<0b001, 0b01, 0b010011>; +class DOTP_U_W_ENC : MSA_3R_FMT<0b001, 0b10, 0b010011>; +class DOTP_U_D_ENC : MSA_3R_FMT<0b001, 0b11, 0b010011>; + +class DPADD_S_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b010011>; +class DPADD_S_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b010011>; +class DPADD_S_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b010011>; + +class DPADD_U_H_ENC : MSA_3R_FMT<0b011, 0b01, 0b010011>; +class DPADD_U_W_ENC : MSA_3R_FMT<0b011, 0b10, 0b010011>; +class DPADD_U_D_ENC : MSA_3R_FMT<0b011, 0b11, 0b010011>; + +class DPSUB_S_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b010011>; +class DPSUB_S_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b010011>; +class DPSUB_S_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b010011>; + +class DPSUB_U_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b010011>; +class DPSUB_U_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b010011>; +class DPSUB_U_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b010011>; + +class FADD_W_ENC : MSA_3RF_FMT<0b0000, 0b0, 0b011011>; +class FADD_D_ENC : MSA_3RF_FMT<0b0000, 0b1, 0b011011>; + +class FCAF_W_ENC : MSA_3RF_FMT<0b0000, 0b0, 0b011010>; +class FCAF_D_ENC : MSA_3RF_FMT<0b0000, 0b1, 0b011010>; + +class FCEQ_W_ENC : MSA_3RF_FMT<0b0010, 0b0, 0b011010>; +class FCEQ_D_ENC : MSA_3RF_FMT<0b0010, 0b1, 0b011010>; + +class FCLASS_W_ENC : MSA_2RF_FMT<0b110010000, 0b0, 0b011110>; +class FCLASS_D_ENC : MSA_2RF_FMT<0b110010000, 0b1, 0b011110>; + +class FCLE_W_ENC : MSA_3RF_FMT<0b0110, 0b0, 0b011010>; +class FCLE_D_ENC : MSA_3RF_FMT<0b0110, 0b1, 0b011010>; + +class FCLT_W_ENC : MSA_3RF_FMT<0b0100, 0b0, 0b011010>; +class FCLT_D_ENC : MSA_3RF_FMT<0b0100, 0b1, 0b011010>; + +class FCNE_W_ENC : MSA_3RF_FMT<0b0011, 0b0, 0b011100>; +class FCNE_D_ENC : MSA_3RF_FMT<0b0011, 0b1, 0b011100>; + +class FCOR_W_ENC : MSA_3RF_FMT<0b0001, 0b0, 0b011100>; +class FCOR_D_ENC : MSA_3RF_FMT<0b0001, 0b1, 0b011100>; + +class FCUEQ_W_ENC : MSA_3RF_FMT<0b0011, 0b0, 0b011010>; +class FCUEQ_D_ENC : MSA_3RF_FMT<0b0011, 0b1, 0b011010>; + +class FCULE_W_ENC : MSA_3RF_FMT<0b0111, 0b0, 0b011010>; +class FCULE_D_ENC : MSA_3RF_FMT<0b0111, 0b1, 0b011010>; + +class FCULT_W_ENC : MSA_3RF_FMT<0b0101, 0b0, 0b011010>; +class FCULT_D_ENC : MSA_3RF_FMT<0b0101, 0b1, 0b011010>; + +class FCUN_W_ENC : MSA_3RF_FMT<0b0001, 0b0, 0b011010>; +class FCUN_D_ENC : MSA_3RF_FMT<0b0001, 0b1, 0b011010>; + +class FCUNE_W_ENC : MSA_3RF_FMT<0b0010, 0b0, 0b011100>; +class FCUNE_D_ENC : MSA_3RF_FMT<0b0010, 0b1, 0b011100>; + +class FDIV_W_ENC : MSA_3RF_FMT<0b0011, 0b0, 0b011011>; +class FDIV_D_ENC : MSA_3RF_FMT<0b0011, 0b1, 0b011011>; + +class FEXDO_H_ENC : MSA_3RF_FMT<0b1000, 0b0, 0b011011>; +class FEXDO_W_ENC : MSA_3RF_FMT<0b1000, 0b1, 0b011011>; + +class FEXP2_W_ENC : MSA_3RF_FMT<0b0111, 0b0, 0b011011>; +class FEXP2_D_ENC : MSA_3RF_FMT<0b0111, 0b1, 0b011011>; + +class FEXUPL_W_ENC : MSA_2RF_FMT<0b110011000, 0b0, 0b011110>; +class FEXUPL_D_ENC : MSA_2RF_FMT<0b110011000, 0b1, 0b011110>; + +class FEXUPR_W_ENC : MSA_2RF_FMT<0b110011001, 0b0, 0b011110>; +class FEXUPR_D_ENC : MSA_2RF_FMT<0b110011001, 0b1, 0b011110>; + +class FFINT_S_W_ENC : MSA_2RF_FMT<0b110011110, 0b0, 0b011110>; +class FFINT_S_D_ENC : MSA_2RF_FMT<0b110011110, 0b1, 0b011110>; + +class FFINT_U_W_ENC : MSA_2RF_FMT<0b110011111, 0b0, 0b011110>; +class FFINT_U_D_ENC : MSA_2RF_FMT<0b110011111, 0b1, 0b011110>; + +class FFQL_W_ENC : MSA_2RF_FMT<0b110011010, 0b0, 0b011110>; +class FFQL_D_ENC : MSA_2RF_FMT<0b110011010, 0b1, 0b011110>; + +class FFQR_W_ENC : MSA_2RF_FMT<0b110011011, 0b0, 0b011110>; +class FFQR_D_ENC : MSA_2RF_FMT<0b110011011, 0b1, 0b011110>; + +class FILL_B_ENC : MSA_2R_FILL_FMT<0b11000000, 0b00, 0b011110>; +class FILL_H_ENC : MSA_2R_FILL_FMT<0b11000000, 0b01, 0b011110>; +class FILL_W_ENC : MSA_2R_FILL_FMT<0b11000000, 0b10, 0b011110>; +class FILL_D_ENC : MSA_2R_FILL_D_FMT<0b11000000, 0b11, 0b011110>; + +class FLOG2_W_ENC : MSA_2RF_FMT<0b110010111, 0b0, 0b011110>; +class FLOG2_D_ENC : MSA_2RF_FMT<0b110010111, 0b1, 0b011110>; + +class FMADD_W_ENC : MSA_3RF_FMT<0b0100, 0b0, 0b011011>; +class FMADD_D_ENC : MSA_3RF_FMT<0b0100, 0b1, 0b011011>; + +class FMAX_W_ENC : MSA_3RF_FMT<0b1110, 0b0, 0b011011>; +class FMAX_D_ENC : MSA_3RF_FMT<0b1110, 0b1, 0b011011>; + +class FMAX_A_W_ENC : MSA_3RF_FMT<0b1111, 0b0, 0b011011>; +class FMAX_A_D_ENC : MSA_3RF_FMT<0b1111, 0b1, 0b011011>; + +class FMIN_W_ENC : MSA_3RF_FMT<0b1100, 0b0, 0b011011>; +class FMIN_D_ENC : MSA_3RF_FMT<0b1100, 0b1, 0b011011>; + +class FMIN_A_W_ENC : MSA_3RF_FMT<0b1101, 0b0, 0b011011>; +class FMIN_A_D_ENC : MSA_3RF_FMT<0b1101, 0b1, 0b011011>; + +class FMSUB_W_ENC : MSA_3RF_FMT<0b0101, 0b0, 0b011011>; +class FMSUB_D_ENC : MSA_3RF_FMT<0b0101, 0b1, 0b011011>; + +class FMUL_W_ENC : MSA_3RF_FMT<0b0010, 0b0, 0b011011>; +class FMUL_D_ENC : MSA_3RF_FMT<0b0010, 0b1, 0b011011>; + +class FRINT_W_ENC : MSA_2RF_FMT<0b110010110, 0b0, 0b011110>; +class FRINT_D_ENC : MSA_2RF_FMT<0b110010110, 0b1, 0b011110>; + +class FRCP_W_ENC : MSA_2RF_FMT<0b110010101, 0b0, 0b011110>; +class FRCP_D_ENC : MSA_2RF_FMT<0b110010101, 0b1, 0b011110>; + +class FRSQRT_W_ENC : MSA_2RF_FMT<0b110010100, 0b0, 0b011110>; +class FRSQRT_D_ENC : MSA_2RF_FMT<0b110010100, 0b1, 0b011110>; + +class FSAF_W_ENC : MSA_3RF_FMT<0b1000, 0b0, 0b011010>; +class FSAF_D_ENC : MSA_3RF_FMT<0b1000, 0b1, 0b011010>; + +class FSEQ_W_ENC : MSA_3RF_FMT<0b1010, 0b0, 0b011010>; +class FSEQ_D_ENC : MSA_3RF_FMT<0b1010, 0b1, 0b011010>; + +class FSLE_W_ENC : MSA_3RF_FMT<0b1110, 0b0, 0b011010>; +class FSLE_D_ENC : MSA_3RF_FMT<0b1110, 0b1, 0b011010>; + +class FSLT_W_ENC : MSA_3RF_FMT<0b1100, 0b0, 0b011010>; +class FSLT_D_ENC : MSA_3RF_FMT<0b1100, 0b1, 0b011010>; + +class FSNE_W_ENC : MSA_3RF_FMT<0b1011, 0b0, 0b011100>; +class FSNE_D_ENC : MSA_3RF_FMT<0b1011, 0b1, 0b011100>; + +class FSOR_W_ENC : MSA_3RF_FMT<0b1001, 0b0, 0b011100>; +class FSOR_D_ENC : MSA_3RF_FMT<0b1001, 0b1, 0b011100>; + +class FSQRT_W_ENC : MSA_2RF_FMT<0b110010011, 0b0, 0b011110>; +class FSQRT_D_ENC : MSA_2RF_FMT<0b110010011, 0b1, 0b011110>; + +class FSUB_W_ENC : MSA_3RF_FMT<0b0001, 0b0, 0b011011>; +class FSUB_D_ENC : MSA_3RF_FMT<0b0001, 0b1, 0b011011>; + +class FSUEQ_W_ENC : MSA_3RF_FMT<0b1011, 0b0, 0b011010>; +class FSUEQ_D_ENC : MSA_3RF_FMT<0b1011, 0b1, 0b011010>; + +class FSULE_W_ENC : MSA_3RF_FMT<0b1111, 0b0, 0b011010>; +class FSULE_D_ENC : MSA_3RF_FMT<0b1111, 0b1, 0b011010>; + +class FSULT_W_ENC : MSA_3RF_FMT<0b1101, 0b0, 0b011010>; +class FSULT_D_ENC : MSA_3RF_FMT<0b1101, 0b1, 0b011010>; + +class FSUN_W_ENC : MSA_3RF_FMT<0b1001, 0b0, 0b011010>; +class FSUN_D_ENC : MSA_3RF_FMT<0b1001, 0b1, 0b011010>; + +class FSUNE_W_ENC : MSA_3RF_FMT<0b1010, 0b0, 0b011100>; +class FSUNE_D_ENC : MSA_3RF_FMT<0b1010, 0b1, 0b011100>; + +class FTINT_S_W_ENC : MSA_2RF_FMT<0b110011100, 0b0, 0b011110>; +class FTINT_S_D_ENC : MSA_2RF_FMT<0b110011100, 0b1, 0b011110>; + +class FTINT_U_W_ENC : MSA_2RF_FMT<0b110011101, 0b0, 0b011110>; +class FTINT_U_D_ENC : MSA_2RF_FMT<0b110011101, 0b1, 0b011110>; + +class FTQ_H_ENC : MSA_3RF_FMT<0b1010, 0b0, 0b011011>; +class FTQ_W_ENC : MSA_3RF_FMT<0b1010, 0b1, 0b011011>; + +class FTRUNC_S_W_ENC : MSA_2RF_FMT<0b110010001, 0b0, 0b011110>; +class FTRUNC_S_D_ENC : MSA_2RF_FMT<0b110010001, 0b1, 0b011110>; + +class FTRUNC_U_W_ENC : MSA_2RF_FMT<0b110010010, 0b0, 0b011110>; +class FTRUNC_U_D_ENC : MSA_2RF_FMT<0b110010010, 0b1, 0b011110>; + +class HADD_S_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b010101>; +class HADD_S_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b010101>; +class HADD_S_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b010101>; + +class HADD_U_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b010101>; +class HADD_U_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b010101>; +class HADD_U_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b010101>; + +class HSUB_S_H_ENC : MSA_3R_FMT<0b110, 0b01, 0b010101>; +class HSUB_S_W_ENC : MSA_3R_FMT<0b110, 0b10, 0b010101>; +class HSUB_S_D_ENC : MSA_3R_FMT<0b110, 0b11, 0b010101>; + +class HSUB_U_H_ENC : MSA_3R_FMT<0b111, 0b01, 0b010101>; +class HSUB_U_W_ENC : MSA_3R_FMT<0b111, 0b10, 0b010101>; +class HSUB_U_D_ENC : MSA_3R_FMT<0b111, 0b11, 0b010101>; + +class ILVEV_B_ENC : MSA_3R_FMT<0b110, 0b00, 0b010100>; +class ILVEV_H_ENC : MSA_3R_FMT<0b110, 0b01, 0b010100>; +class ILVEV_W_ENC : MSA_3R_FMT<0b110, 0b10, 0b010100>; +class ILVEV_D_ENC : MSA_3R_FMT<0b110, 0b11, 0b010100>; + +class ILVL_B_ENC : MSA_3R_FMT<0b100, 0b00, 0b010100>; +class ILVL_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b010100>; +class ILVL_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b010100>; +class ILVL_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b010100>; + +class ILVOD_B_ENC : MSA_3R_FMT<0b111, 0b00, 0b010100>; +class ILVOD_H_ENC : MSA_3R_FMT<0b111, 0b01, 0b010100>; +class ILVOD_W_ENC : MSA_3R_FMT<0b111, 0b10, 0b010100>; +class ILVOD_D_ENC : MSA_3R_FMT<0b111, 0b11, 0b010100>; + +class ILVR_B_ENC : MSA_3R_FMT<0b101, 0b00, 0b010100>; +class ILVR_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b010100>; +class ILVR_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b010100>; +class ILVR_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b010100>; + +class INSERT_B_ENC : MSA_ELM_INSERT_B_FMT<0b0100, 0b011001>; +class INSERT_H_ENC : MSA_ELM_INSERT_H_FMT<0b0100, 0b011001>; +class INSERT_W_ENC : MSA_ELM_INSERT_W_FMT<0b0100, 0b011001>; +class INSERT_D_ENC : MSA_ELM_INSERT_D_FMT<0b0100, 0b011001>; + +class INSVE_B_ENC : MSA_ELM_B_FMT<0b0101, 0b011001>; +class INSVE_H_ENC : MSA_ELM_H_FMT<0b0101, 0b011001>; +class INSVE_W_ENC : MSA_ELM_W_FMT<0b0101, 0b011001>; +class INSVE_D_ENC : MSA_ELM_D_FMT<0b0101, 0b011001>; + +class LD_B_ENC : MSA_MI10_FMT<0b00, 0b1000>; +class LD_H_ENC : MSA_MI10_FMT<0b01, 0b1000>; +class LD_W_ENC : MSA_MI10_FMT<0b10, 0b1000>; +class LD_D_ENC : MSA_MI10_FMT<0b11, 0b1000>; + +class LDI_B_ENC : MSA_I10_FMT<0b110, 0b00, 0b000111>; +class LDI_H_ENC : MSA_I10_FMT<0b110, 0b01, 0b000111>; +class LDI_W_ENC : MSA_I10_FMT<0b110, 0b10, 0b000111>; +class LDI_D_ENC : MSA_I10_FMT<0b110, 0b11, 0b000111>; + +class LSA_ENC : SPECIAL_LSA_FMT<0b000101>; +class DLSA_ENC : SPECIAL_DLSA_FMT<0b010101>; + +class MADD_Q_H_ENC : MSA_3RF_FMT<0b0101, 0b0, 0b011100>; +class MADD_Q_W_ENC : MSA_3RF_FMT<0b0101, 0b1, 0b011100>; + +class MADDR_Q_H_ENC : MSA_3RF_FMT<0b1101, 0b0, 0b011100>; +class MADDR_Q_W_ENC : MSA_3RF_FMT<0b1101, 0b1, 0b011100>; + +class MADDV_B_ENC : MSA_3R_FMT<0b001, 0b00, 0b010010>; +class MADDV_H_ENC : MSA_3R_FMT<0b001, 0b01, 0b010010>; +class MADDV_W_ENC : MSA_3R_FMT<0b001, 0b10, 0b010010>; +class MADDV_D_ENC : MSA_3R_FMT<0b001, 0b11, 0b010010>; + +class MAX_A_B_ENC : MSA_3R_FMT<0b110, 0b00, 0b001110>; +class MAX_A_H_ENC : MSA_3R_FMT<0b110, 0b01, 0b001110>; +class MAX_A_W_ENC : MSA_3R_FMT<0b110, 0b10, 0b001110>; +class MAX_A_D_ENC : MSA_3R_FMT<0b110, 0b11, 0b001110>; + +class MAX_S_B_ENC : MSA_3R_FMT<0b010, 0b00, 0b001110>; +class MAX_S_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b001110>; +class MAX_S_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b001110>; +class MAX_S_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b001110>; + +class MAX_U_B_ENC : MSA_3R_FMT<0b011, 0b00, 0b001110>; +class MAX_U_H_ENC : MSA_3R_FMT<0b011, 0b01, 0b001110>; +class MAX_U_W_ENC : MSA_3R_FMT<0b011, 0b10, 0b001110>; +class MAX_U_D_ENC : MSA_3R_FMT<0b011, 0b11, 0b001110>; + +class MAXI_S_B_ENC : MSA_I5_FMT<0b010, 0b00, 0b000110>; +class MAXI_S_H_ENC : MSA_I5_FMT<0b010, 0b01, 0b000110>; +class MAXI_S_W_ENC : MSA_I5_FMT<0b010, 0b10, 0b000110>; +class MAXI_S_D_ENC : MSA_I5_FMT<0b010, 0b11, 0b000110>; + +class MAXI_U_B_ENC : MSA_I5_FMT<0b011, 0b00, 0b000110>; +class MAXI_U_H_ENC : MSA_I5_FMT<0b011, 0b01, 0b000110>; +class MAXI_U_W_ENC : MSA_I5_FMT<0b011, 0b10, 0b000110>; +class MAXI_U_D_ENC : MSA_I5_FMT<0b011, 0b11, 0b000110>; + +class MIN_A_B_ENC : MSA_3R_FMT<0b111, 0b00, 0b001110>; +class MIN_A_H_ENC : MSA_3R_FMT<0b111, 0b01, 0b001110>; +class MIN_A_W_ENC : MSA_3R_FMT<0b111, 0b10, 0b001110>; +class MIN_A_D_ENC : MSA_3R_FMT<0b111, 0b11, 0b001110>; + +class MIN_S_B_ENC : MSA_3R_FMT<0b100, 0b00, 0b001110>; +class MIN_S_H_ENC : MSA_3R_FMT<0b100, 0b01, 0b001110>; +class MIN_S_W_ENC : MSA_3R_FMT<0b100, 0b10, 0b001110>; +class MIN_S_D_ENC : MSA_3R_FMT<0b100, 0b11, 0b001110>; + +class MIN_U_B_ENC : MSA_3R_FMT<0b101, 0b00, 0b001110>; +class MIN_U_H_ENC : MSA_3R_FMT<0b101, 0b01, 0b001110>; +class MIN_U_W_ENC : MSA_3R_FMT<0b101, 0b10, 0b001110>; +class MIN_U_D_ENC : MSA_3R_FMT<0b101, 0b11, 0b001110>; + +class MINI_S_B_ENC : MSA_I5_FMT<0b100, 0b00, 0b000110>; +class MINI_S_H_ENC : MSA_I5_FMT<0b100, 0b01, 0b000110>; +class MINI_S_W_ENC : MSA_I5_FMT<0b100, 0b10, 0b000110>; +class MINI_S_D_ENC : MSA_I5_FMT<0b100, 0b11, 0b000110>; + +class MINI_U_B_ENC : MSA_I5_FMT<0b101, 0b00, 0b000110>; +class MINI_U_H_ENC : MSA_I5_FMT<0b101, 0b01, 0b000110>; +class MINI_U_W_ENC : MSA_I5_FMT<0b101, 0b10, 0b000110>; +class MINI_U_D_ENC : MSA_I5_FMT<0b101, 0b11, 0b000110>; + +class MOD_S_B_ENC : MSA_3R_FMT<0b110, 0b00, 0b010010>; +class MOD_S_H_ENC : MSA_3R_FMT<0b110, 0b01, 0b010010>; +class MOD_S_W_ENC : MSA_3R_FMT<0b110, 0b10, 0b010010>; +class MOD_S_D_ENC : MSA_3R_FMT<0b110, 0b11, 0b010010>; + +class MOD_U_B_ENC : MSA_3R_FMT<0b111, 0b00, 0b010010>; +class MOD_U_H_ENC : MSA_3R_FMT<0b111, 0b01, 0b010010>; +class MOD_U_W_ENC : MSA_3R_FMT<0b111, 0b10, 0b010010>; +class MOD_U_D_ENC : MSA_3R_FMT<0b111, 0b11, 0b010010>; + +class MOVE_V_ENC : MSA_ELM_FMT<0b0010111110, 0b011001>; + +class MSUB_Q_H_ENC : MSA_3RF_FMT<0b0110, 0b0, 0b011100>; +class MSUB_Q_W_ENC : MSA_3RF_FMT<0b0110, 0b1, 0b011100>; + +class MSUBR_Q_H_ENC : MSA_3RF_FMT<0b1110, 0b0, 0b011100>; +class MSUBR_Q_W_ENC : MSA_3RF_FMT<0b1110, 0b1, 0b011100>; + +class MSUBV_B_ENC : MSA_3R_FMT<0b010, 0b00, 0b010010>; +class MSUBV_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b010010>; +class MSUBV_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b010010>; +class MSUBV_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b010010>; + +class MUL_Q_H_ENC : MSA_3RF_FMT<0b0100, 0b0, 0b011100>; +class MUL_Q_W_ENC : MSA_3RF_FMT<0b0100, 0b1, 0b011100>; + +class MULR_Q_H_ENC : MSA_3RF_FMT<0b1100, 0b0, 0b011100>; +class MULR_Q_W_ENC : MSA_3RF_FMT<0b1100, 0b1, 0b011100>; + +class MULV_B_ENC : MSA_3R_FMT<0b000, 0b00, 0b010010>; +class MULV_H_ENC : MSA_3R_FMT<0b000, 0b01, 0b010010>; +class MULV_W_ENC : MSA_3R_FMT<0b000, 0b10, 0b010010>; +class MULV_D_ENC : MSA_3R_FMT<0b000, 0b11, 0b010010>; + +class NLOC_B_ENC : MSA_2R_FMT<0b11000010, 0b00, 0b011110>; +class NLOC_H_ENC : MSA_2R_FMT<0b11000010, 0b01, 0b011110>; +class NLOC_W_ENC : MSA_2R_FMT<0b11000010, 0b10, 0b011110>; +class NLOC_D_ENC : MSA_2R_FMT<0b11000010, 0b11, 0b011110>; + +class NLZC_B_ENC : MSA_2R_FMT<0b11000011, 0b00, 0b011110>; +class NLZC_H_ENC : MSA_2R_FMT<0b11000011, 0b01, 0b011110>; +class NLZC_W_ENC : MSA_2R_FMT<0b11000011, 0b10, 0b011110>; +class NLZC_D_ENC : MSA_2R_FMT<0b11000011, 0b11, 0b011110>; + +class NOR_V_ENC : MSA_VEC_FMT<0b00010, 0b011110>; + +class NORI_B_ENC : MSA_I8_FMT<0b10, 0b000000>; + +class OR_V_ENC : MSA_VEC_FMT<0b00001, 0b011110>; + +class ORI_B_ENC : MSA_I8_FMT<0b01, 0b000000>; + +class PCKEV_B_ENC : MSA_3R_FMT<0b010, 0b00, 0b010100>; +class PCKEV_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b010100>; +class PCKEV_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b010100>; +class PCKEV_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b010100>; + +class PCKOD_B_ENC : MSA_3R_FMT<0b011, 0b00, 0b010100>; +class PCKOD_H_ENC : MSA_3R_FMT<0b011, 0b01, 0b010100>; +class PCKOD_W_ENC : MSA_3R_FMT<0b011, 0b10, 0b010100>; +class PCKOD_D_ENC : MSA_3R_FMT<0b011, 0b11, 0b010100>; + +class PCNT_B_ENC : MSA_2R_FMT<0b11000001, 0b00, 0b011110>; +class PCNT_H_ENC : MSA_2R_FMT<0b11000001, 0b01, 0b011110>; +class PCNT_W_ENC : MSA_2R_FMT<0b11000001, 0b10, 0b011110>; +class PCNT_D_ENC : MSA_2R_FMT<0b11000001, 0b11, 0b011110>; + +class SAT_S_B_ENC : MSA_BIT_B_FMT<0b000, 0b001010>; +class SAT_S_H_ENC : MSA_BIT_H_FMT<0b000, 0b001010>; +class SAT_S_W_ENC : MSA_BIT_W_FMT<0b000, 0b001010>; +class SAT_S_D_ENC : MSA_BIT_D_FMT<0b000, 0b001010>; + +class SAT_U_B_ENC : MSA_BIT_B_FMT<0b001, 0b001010>; +class SAT_U_H_ENC : MSA_BIT_H_FMT<0b001, 0b001010>; +class SAT_U_W_ENC : MSA_BIT_W_FMT<0b001, 0b001010>; +class SAT_U_D_ENC : MSA_BIT_D_FMT<0b001, 0b001010>; + +class SHF_B_ENC : MSA_I8_FMT<0b00, 0b000010>; +class SHF_H_ENC : MSA_I8_FMT<0b01, 0b000010>; +class SHF_W_ENC : MSA_I8_FMT<0b10, 0b000010>; + +class SLD_B_ENC : MSA_3R_INDEX_FMT<0b000, 0b00, 0b010100>; +class SLD_H_ENC : MSA_3R_INDEX_FMT<0b000, 0b01, 0b010100>; +class SLD_W_ENC : MSA_3R_INDEX_FMT<0b000, 0b10, 0b010100>; +class SLD_D_ENC : MSA_3R_INDEX_FMT<0b000, 0b11, 0b010100>; + +class SLDI_B_ENC : MSA_ELM_B_FMT<0b0000, 0b011001>; +class SLDI_H_ENC : MSA_ELM_H_FMT<0b0000, 0b011001>; +class SLDI_W_ENC : MSA_ELM_W_FMT<0b0000, 0b011001>; +class SLDI_D_ENC : MSA_ELM_D_FMT<0b0000, 0b011001>; + +class SLL_B_ENC : MSA_3R_FMT<0b000, 0b00, 0b001101>; +class SLL_H_ENC : MSA_3R_FMT<0b000, 0b01, 0b001101>; +class SLL_W_ENC : MSA_3R_FMT<0b000, 0b10, 0b001101>; +class SLL_D_ENC : MSA_3R_FMT<0b000, 0b11, 0b001101>; + +class SLLI_B_ENC : MSA_BIT_B_FMT<0b000, 0b001001>; +class SLLI_H_ENC : MSA_BIT_H_FMT<0b000, 0b001001>; +class SLLI_W_ENC : MSA_BIT_W_FMT<0b000, 0b001001>; +class SLLI_D_ENC : MSA_BIT_D_FMT<0b000, 0b001001>; + +class SPLAT_B_ENC : MSA_3R_INDEX_FMT<0b001, 0b00, 0b010100>; +class SPLAT_H_ENC : MSA_3R_INDEX_FMT<0b001, 0b01, 0b010100>; +class SPLAT_W_ENC : MSA_3R_INDEX_FMT<0b001, 0b10, 0b010100>; +class SPLAT_D_ENC : MSA_3R_INDEX_FMT<0b001, 0b11, 0b010100>; + +class SPLATI_B_ENC : MSA_ELM_B_FMT<0b0001, 0b011001>; +class SPLATI_H_ENC : MSA_ELM_H_FMT<0b0001, 0b011001>; +class SPLATI_W_ENC : MSA_ELM_W_FMT<0b0001, 0b011001>; +class SPLATI_D_ENC : MSA_ELM_D_FMT<0b0001, 0b011001>; + +class SRA_B_ENC : MSA_3R_FMT<0b001, 0b00, 0b001101>; +class SRA_H_ENC : MSA_3R_FMT<0b001, 0b01, 0b001101>; +class SRA_W_ENC : MSA_3R_FMT<0b001, 0b10, 0b001101>; +class SRA_D_ENC : MSA_3R_FMT<0b001, 0b11, 0b001101>; + +class SRAI_B_ENC : MSA_BIT_B_FMT<0b001, 0b001001>; +class SRAI_H_ENC : MSA_BIT_H_FMT<0b001, 0b001001>; +class SRAI_W_ENC : MSA_BIT_W_FMT<0b001, 0b001001>; +class SRAI_D_ENC : MSA_BIT_D_FMT<0b001, 0b001001>; + +class SRAR_B_ENC : MSA_3R_FMT<0b001, 0b00, 0b010101>; +class SRAR_H_ENC : MSA_3R_FMT<0b001, 0b01, 0b010101>; +class SRAR_W_ENC : MSA_3R_FMT<0b001, 0b10, 0b010101>; +class SRAR_D_ENC : MSA_3R_FMT<0b001, 0b11, 0b010101>; + +class SRARI_B_ENC : MSA_BIT_B_FMT<0b010, 0b001010>; +class SRARI_H_ENC : MSA_BIT_H_FMT<0b010, 0b001010>; +class SRARI_W_ENC : MSA_BIT_W_FMT<0b010, 0b001010>; +class SRARI_D_ENC : MSA_BIT_D_FMT<0b010, 0b001010>; + +class SRL_B_ENC : MSA_3R_FMT<0b010, 0b00, 0b001101>; +class SRL_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b001101>; +class SRL_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b001101>; +class SRL_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b001101>; + +class SRLI_B_ENC : MSA_BIT_B_FMT<0b010, 0b001001>; +class SRLI_H_ENC : MSA_BIT_H_FMT<0b010, 0b001001>; +class SRLI_W_ENC : MSA_BIT_W_FMT<0b010, 0b001001>; +class SRLI_D_ENC : MSA_BIT_D_FMT<0b010, 0b001001>; + +class SRLR_B_ENC : MSA_3R_FMT<0b010, 0b00, 0b010101>; +class SRLR_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b010101>; +class SRLR_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b010101>; +class SRLR_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b010101>; + +class SRLRI_B_ENC : MSA_BIT_B_FMT<0b011, 0b001010>; +class SRLRI_H_ENC : MSA_BIT_H_FMT<0b011, 0b001010>; +class SRLRI_W_ENC : MSA_BIT_W_FMT<0b011, 0b001010>; +class SRLRI_D_ENC : MSA_BIT_D_FMT<0b011, 0b001010>; + +class ST_B_ENC : MSA_MI10_FMT<0b00, 0b1001>; +class ST_H_ENC : MSA_MI10_FMT<0b01, 0b1001>; +class ST_W_ENC : MSA_MI10_FMT<0b10, 0b1001>; +class ST_D_ENC : MSA_MI10_FMT<0b11, 0b1001>; + +class SUBS_S_B_ENC : MSA_3R_FMT<0b000, 0b00, 0b010001>; +class SUBS_S_H_ENC : MSA_3R_FMT<0b000, 0b01, 0b010001>; +class SUBS_S_W_ENC : MSA_3R_FMT<0b000, 0b10, 0b010001>; +class SUBS_S_D_ENC : MSA_3R_FMT<0b000, 0b11, 0b010001>; + +class SUBS_U_B_ENC : MSA_3R_FMT<0b001, 0b00, 0b010001>; +class SUBS_U_H_ENC : MSA_3R_FMT<0b001, 0b01, 0b010001>; +class SUBS_U_W_ENC : MSA_3R_FMT<0b001, 0b10, 0b010001>; +class SUBS_U_D_ENC : MSA_3R_FMT<0b001, 0b11, 0b010001>; + +class SUBSUS_U_B_ENC : MSA_3R_FMT<0b010, 0b00, 0b010001>; +class SUBSUS_U_H_ENC : MSA_3R_FMT<0b010, 0b01, 0b010001>; +class SUBSUS_U_W_ENC : MSA_3R_FMT<0b010, 0b10, 0b010001>; +class SUBSUS_U_D_ENC : MSA_3R_FMT<0b010, 0b11, 0b010001>; + +class SUBSUU_S_B_ENC : MSA_3R_FMT<0b011, 0b00, 0b010001>; +class SUBSUU_S_H_ENC : MSA_3R_FMT<0b011, 0b01, 0b010001>; +class SUBSUU_S_W_ENC : MSA_3R_FMT<0b011, 0b10, 0b010001>; +class SUBSUU_S_D_ENC : MSA_3R_FMT<0b011, 0b11, 0b010001>; + +class SUBV_B_ENC : MSA_3R_FMT<0b001, 0b00, 0b001110>; +class SUBV_H_ENC : MSA_3R_FMT<0b001, 0b01, 0b001110>; +class SUBV_W_ENC : MSA_3R_FMT<0b001, 0b10, 0b001110>; +class SUBV_D_ENC : MSA_3R_FMT<0b001, 0b11, 0b001110>; + +class SUBVI_B_ENC : MSA_I5_FMT<0b001, 0b00, 0b000110>; +class SUBVI_H_ENC : MSA_I5_FMT<0b001, 0b01, 0b000110>; +class SUBVI_W_ENC : MSA_I5_FMT<0b001, 0b10, 0b000110>; +class SUBVI_D_ENC : MSA_I5_FMT<0b001, 0b11, 0b000110>; + +class VSHF_B_ENC : MSA_3R_FMT<0b000, 0b00, 0b010101>; +class VSHF_H_ENC : MSA_3R_FMT<0b000, 0b01, 0b010101>; +class VSHF_W_ENC : MSA_3R_FMT<0b000, 0b10, 0b010101>; +class VSHF_D_ENC : MSA_3R_FMT<0b000, 0b11, 0b010101>; + +class XOR_V_ENC : MSA_VEC_FMT<0b00011, 0b011110>; + +class XORI_B_ENC : MSA_I8_FMT<0b11, 0b000000>; + +// Instruction desc. +class MSA_BIT_B_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, vsplat_uimm3:$m); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $m"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, Imm:$m))]; + InstrItinClass Itinerary = itin; +} + +class MSA_BIT_H_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, vsplat_uimm4:$m); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $m"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, Imm:$m))]; + InstrItinClass Itinerary = itin; +} + +class MSA_BIT_W_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, vsplat_uimm5:$m); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $m"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, Imm:$m))]; + InstrItinClass Itinerary = itin; +} + +class MSA_BIT_D_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, vsplat_uimm6:$m); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $m"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, Imm:$m))]; + InstrItinClass Itinerary = itin; +} + +class MSA_BIT_X_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, ImmOp:$m); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $m"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, Imm:$m))]; + InstrItinClass Itinerary = itin; +} + +class MSA_BIT_BINSXI_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWD:$wd_in, ROWS:$ws, Mask.OpClass:$m); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $m"); + // Note that binsxi and vselect treat the condition operand the opposite + // way to each other. + // (vselect cond, if_set, if_clear) + // (BSEL_V cond, if_clear, if_set) + list Pattern = [(set ROWD:$wd, (vselect (Ty Mask:$m), (Ty ROWD:$ws), + ROWS:$wd_in))]; + InstrItinClass Itinerary = itin; + string Constraints = "$wd = $wd_in"; +} + +class MSA_BIT_BINSLI_DESC_BASE : + MSA_BIT_BINSXI_DESC_BASE; + +class MSA_BIT_BINSRI_DESC_BASE : + MSA_BIT_BINSXI_DESC_BASE; + +class MSA_BIT_SPLAT_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, SplatImm.OpClass:$m); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $m"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, SplatImm:$m))]; + InstrItinClass Itinerary = itin; +} + +class MSA_COPY_DESC_BASE { + dag OutOperandList = (outs ROD:$rd); + dag InOperandList = (ins ROWS:$ws, ImmOp:$n); + string AsmString = !strconcat(instr_asm, "\t$rd, $ws[$n]"); + list Pattern = [(set ROD:$rd, (OpNode (VecTy ROWS:$ws), Imm:$n))]; + InstrItinClass Itinerary = itin; +} + +class MSA_ELM_SLD_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWD:$wd_in, ROWS:$ws, ImmOp:$n); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws[$n]"); + list Pattern = [(set ROWD:$wd, (OpNode ROWD:$wd_in, ROWS:$ws, + Imm:$n))]; + string Constraints = "$wd = $wd_in"; + InstrItinClass Itinerary = itin; +} + +class MSA_COPY_PSEUDO_BASE : + MSAPseudo<(outs RCD:$wd), (ins RCWS:$ws, ImmOp:$n), + [(set RCD:$wd, (OpNode (VecTy RCWS:$ws), Imm:$n))]> { + bit usesCustomInserter = 1; +} + +class MSA_I5_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, SplatImm.OpClass:$imm); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $imm"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, SplatImm:$imm))]; + InstrItinClass Itinerary = itin; +} + +class MSA_I8_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, SplatImm.OpClass:$u8); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $u8"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, SplatImm:$u8))]; + InstrItinClass Itinerary = itin; +} + +class MSA_I8_SHF_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, uimm8:$u8); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $u8"); + list Pattern = [(set ROWD:$wd, (MaxisSHF immZExt8:$u8, ROWS:$ws))]; + InstrItinClass Itinerary = itin; +} + +class MSA_I10_LDI_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins vsplat_simm10:$s10); + string AsmString = !strconcat(instr_asm, "\t$wd, $s10"); + // LDI is matched using custom matching code in MaxisSEISelDAGToDAG.cpp + list Pattern = []; + bit hasSideEffects = 0; + InstrItinClass Itinerary = itin; +} + +class MSA_2R_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws))]; + InstrItinClass Itinerary = itin; +} + +class MSA_2R_FILL_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROS:$rs); + string AsmString = !strconcat(instr_asm, "\t$wd, $rs"); + list Pattern = [(set ROWD:$wd, (VT (OpNode ROS:$rs)))]; + InstrItinClass Itinerary = itin; +} + +class MSA_2R_FILL_PSEUDO_BASE : + MSAPseudo<(outs RCWD:$wd), (ins RCWS:$fs), + [(set RCWD:$wd, (OpNode RCWS:$fs))]> { + let usesCustomInserter = 1; +} + +class MSA_2RF_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws))]; + InstrItinClass Itinerary = itin; +} + +class MSA_3R_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, ROWT:$wt); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $wt"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, ROWT:$wt))]; + InstrItinClass Itinerary = itin; +} + +class MSA_3R_BINSX_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWD:$wd_in, ROWS:$ws, ROWT:$wt); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $wt"); + list Pattern = [(set ROWD:$wd, (OpNode ROWD:$wd_in, ROWS:$ws, + ROWT:$wt))]; + string Constraints = "$wd = $wd_in"; + InstrItinClass Itinerary = itin; +} + +class MSA_3R_SPLAT_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, GPR32Opnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws[$rt]"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, GPR32Opnd:$rt))]; + InstrItinClass Itinerary = itin; +} + +class MSA_3R_VSHF_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWD:$wd_in, ROWS:$ws, ROWT:$wt); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $wt"); + list Pattern = [(set ROWD:$wd, (MaxisVSHF ROWD:$wd_in, ROWS:$ws, + ROWT:$wt))]; + string Constraints = "$wd = $wd_in"; + InstrItinClass Itinerary = itin; +} + +class MSA_3R_SLD_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWD:$wd_in, ROWS:$ws, GPR32Opnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws[$rt]"); + list Pattern = [(set ROWD:$wd, (OpNode ROWD:$wd_in, ROWS:$ws, + GPR32Opnd:$rt))]; + InstrItinClass Itinerary = itin; + string Constraints = "$wd = $wd_in"; +} + +class MSA_3R_4R_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWD:$wd_in, ROWS:$ws, ROWT:$wt); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $wt"); + list Pattern = [(set ROWD:$wd, (OpNode ROWD:$wd_in, ROWS:$ws, + ROWT:$wt))]; + InstrItinClass Itinerary = itin; + string Constraints = "$wd = $wd_in"; +} + +class MSA_3RF_DESC_BASE : + MSA_3R_DESC_BASE; + +class MSA_3RF_4RF_DESC_BASE : + MSA_3R_4R_DESC_BASE; + +class MSA_CBRANCH_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins ROWD:$wt, brtarget:$offset); + string AsmString = !strconcat(instr_asm, "\t$wt, $offset"); + list Pattern = []; + InstrItinClass Itinerary = NoItinerary; + bit isBranch = 1; + bit isTerminator = 1; + bit hasDelaySlot = 1; + list Defs = [AT]; +} + +class MSA_INSERT_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWD:$wd_in, ROS:$rs, ImmOp:$n); + string AsmString = !strconcat(instr_asm, "\t$wd[$n], $rs"); + list Pattern = [(set ROWD:$wd, (OpNode ROWD:$wd_in, ROS:$rs, Imm:$n))]; + InstrItinClass Itinerary = itin; + string Constraints = "$wd = $wd_in"; +} + +class MSA_INSERT_PSEUDO_BASE : + MSAPseudo<(outs ROWD:$wd), (ins ROWD:$wd_in, ImmOp:$n, ROFS:$fs), + [(set ROWD:$wd, (OpNode (Ty ROWD:$wd_in), ROFS:$fs, Imm:$n))]> { + bit usesCustomInserter = 1; + string Constraints = "$wd = $wd_in"; +} + +class MSA_INSERT_VIDX_PSEUDO_BASE : + MSAPseudo<(outs ROWD:$wd), (ins ROWD:$wd_in, ROIdx:$n, ROFS:$fs), + [(set ROWD:$wd, (OpNode (Ty ROWD:$wd_in), ROFS:$fs, + ROIdx:$n))]> { + bit usesCustomInserter = 1; + string Constraints = "$wd = $wd_in"; +} + +class MSA_INSVE_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWD:$wd_in, ImmOp:$n, ROWS:$ws, uimmz:$n2); + string AsmString = !strconcat(instr_asm, "\t$wd[$n], $ws[$n2]"); + list Pattern = [(set ROWD:$wd, (OpNode ROWD:$wd_in, + Imm:$n, + ROWS:$ws, + immz:$n2))]; + InstrItinClass Itinerary = itin; + string Constraints = "$wd = $wd_in"; +} + +class MSA_VEC_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, ROWT:$wt); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws, $wt"); + list Pattern = [(set ROWD:$wd, (OpNode ROWS:$ws, ROWT:$wt))]; + InstrItinClass Itinerary = itin; +} + +class MSA_ELM_SPLAT_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins ROWS:$ws, SplatImm.OpClass:$n); + string AsmString = !strconcat(instr_asm, "\t$wd, $ws[$n]"); + list Pattern = [(set ROWD:$wd, (MaxisVSHF SplatImm:$n, ROWS:$ws, + ROWS:$ws))]; + InstrItinClass Itinerary = itin; +} + +class MSA_VEC_PSEUDO_BASE : + MSAPseudo<(outs ROWD:$wd), (ins ROWS:$ws, ROWT:$wt), + [(set ROWD:$wd, (OpNode ROWS:$ws, ROWT:$wt))]>; + +class ADD_A_B_DESC : MSA_3R_DESC_BASE<"add_a.b", int_maxis_add_a_b, MSA128BOpnd>, + IsCommutable; +class ADD_A_H_DESC : MSA_3R_DESC_BASE<"add_a.h", int_maxis_add_a_h, MSA128HOpnd>, + IsCommutable; +class ADD_A_W_DESC : MSA_3R_DESC_BASE<"add_a.w", int_maxis_add_a_w, MSA128WOpnd>, + IsCommutable; +class ADD_A_D_DESC : MSA_3R_DESC_BASE<"add_a.d", int_maxis_add_a_d, MSA128DOpnd>, + IsCommutable; + +class ADDS_A_B_DESC : MSA_3R_DESC_BASE<"adds_a.b", int_maxis_adds_a_b, + MSA128BOpnd>, IsCommutable; +class ADDS_A_H_DESC : MSA_3R_DESC_BASE<"adds_a.h", int_maxis_adds_a_h, + MSA128HOpnd>, IsCommutable; +class ADDS_A_W_DESC : MSA_3R_DESC_BASE<"adds_a.w", int_maxis_adds_a_w, + MSA128WOpnd>, IsCommutable; +class ADDS_A_D_DESC : MSA_3R_DESC_BASE<"adds_a.d", int_maxis_adds_a_d, + MSA128DOpnd>, IsCommutable; + +class ADDS_S_B_DESC : MSA_3R_DESC_BASE<"adds_s.b", int_maxis_adds_s_b, + MSA128BOpnd>, IsCommutable; +class ADDS_S_H_DESC : MSA_3R_DESC_BASE<"adds_s.h", int_maxis_adds_s_h, + MSA128HOpnd>, IsCommutable; +class ADDS_S_W_DESC : MSA_3R_DESC_BASE<"adds_s.w", int_maxis_adds_s_w, + MSA128WOpnd>, IsCommutable; +class ADDS_S_D_DESC : MSA_3R_DESC_BASE<"adds_s.d", int_maxis_adds_s_d, + MSA128DOpnd>, IsCommutable; + +class ADDS_U_B_DESC : MSA_3R_DESC_BASE<"adds_u.b", int_maxis_adds_u_b, + MSA128BOpnd>, IsCommutable; +class ADDS_U_H_DESC : MSA_3R_DESC_BASE<"adds_u.h", int_maxis_adds_u_h, + MSA128HOpnd>, IsCommutable; +class ADDS_U_W_DESC : MSA_3R_DESC_BASE<"adds_u.w", int_maxis_adds_u_w, + MSA128WOpnd>, IsCommutable; +class ADDS_U_D_DESC : MSA_3R_DESC_BASE<"adds_u.d", int_maxis_adds_u_d, + MSA128DOpnd>, IsCommutable; + +class ADDV_B_DESC : MSA_3R_DESC_BASE<"addv.b", add, MSA128BOpnd>, IsCommutable; +class ADDV_H_DESC : MSA_3R_DESC_BASE<"addv.h", add, MSA128HOpnd>, IsCommutable; +class ADDV_W_DESC : MSA_3R_DESC_BASE<"addv.w", add, MSA128WOpnd>, IsCommutable; +class ADDV_D_DESC : MSA_3R_DESC_BASE<"addv.d", add, MSA128DOpnd>, IsCommutable; + +class ADDVI_B_DESC : MSA_I5_DESC_BASE<"addvi.b", add, vsplati8_uimm5, + MSA128BOpnd>; +class ADDVI_H_DESC : MSA_I5_DESC_BASE<"addvi.h", add, vsplati16_uimm5, + MSA128HOpnd>; +class ADDVI_W_DESC : MSA_I5_DESC_BASE<"addvi.w", add, vsplati32_uimm5, + MSA128WOpnd>; +class ADDVI_D_DESC : MSA_I5_DESC_BASE<"addvi.d", add, vsplati64_uimm5, + MSA128DOpnd>; + +class AND_V_DESC : MSA_VEC_DESC_BASE<"and.v", and, MSA128BOpnd>; +class AND_V_H_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; +class AND_V_W_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; +class AND_V_D_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; + +class ANDI_B_DESC : MSA_I8_DESC_BASE<"andi.b", and, vsplati8_uimm8, + MSA128BOpnd>; + +class ASUB_S_B_DESC : MSA_3R_DESC_BASE<"asub_s.b", int_maxis_asub_s_b, + MSA128BOpnd>; +class ASUB_S_H_DESC : MSA_3R_DESC_BASE<"asub_s.h", int_maxis_asub_s_h, + MSA128HOpnd>; +class ASUB_S_W_DESC : MSA_3R_DESC_BASE<"asub_s.w", int_maxis_asub_s_w, + MSA128WOpnd>; +class ASUB_S_D_DESC : MSA_3R_DESC_BASE<"asub_s.d", int_maxis_asub_s_d, + MSA128DOpnd>; + +class ASUB_U_B_DESC : MSA_3R_DESC_BASE<"asub_u.b", int_maxis_asub_u_b, + MSA128BOpnd>; +class ASUB_U_H_DESC : MSA_3R_DESC_BASE<"asub_u.h", int_maxis_asub_u_h, + MSA128HOpnd>; +class ASUB_U_W_DESC : MSA_3R_DESC_BASE<"asub_u.w", int_maxis_asub_u_w, + MSA128WOpnd>; +class ASUB_U_D_DESC : MSA_3R_DESC_BASE<"asub_u.d", int_maxis_asub_u_d, + MSA128DOpnd>; + +class AVE_S_B_DESC : MSA_3R_DESC_BASE<"ave_s.b", int_maxis_ave_s_b, MSA128BOpnd>, + IsCommutable; +class AVE_S_H_DESC : MSA_3R_DESC_BASE<"ave_s.h", int_maxis_ave_s_h, MSA128HOpnd>, + IsCommutable; +class AVE_S_W_DESC : MSA_3R_DESC_BASE<"ave_s.w", int_maxis_ave_s_w, MSA128WOpnd>, + IsCommutable; +class AVE_S_D_DESC : MSA_3R_DESC_BASE<"ave_s.d", int_maxis_ave_s_d, MSA128DOpnd>, + IsCommutable; + +class AVE_U_B_DESC : MSA_3R_DESC_BASE<"ave_u.b", int_maxis_ave_u_b, MSA128BOpnd>, + IsCommutable; +class AVE_U_H_DESC : MSA_3R_DESC_BASE<"ave_u.h", int_maxis_ave_u_h, MSA128HOpnd>, + IsCommutable; +class AVE_U_W_DESC : MSA_3R_DESC_BASE<"ave_u.w", int_maxis_ave_u_w, MSA128WOpnd>, + IsCommutable; +class AVE_U_D_DESC : MSA_3R_DESC_BASE<"ave_u.d", int_maxis_ave_u_d, MSA128DOpnd>, + IsCommutable; + +class AVER_S_B_DESC : MSA_3R_DESC_BASE<"aver_s.b", int_maxis_aver_s_b, + MSA128BOpnd>, IsCommutable; +class AVER_S_H_DESC : MSA_3R_DESC_BASE<"aver_s.h", int_maxis_aver_s_h, + MSA128HOpnd>, IsCommutable; +class AVER_S_W_DESC : MSA_3R_DESC_BASE<"aver_s.w", int_maxis_aver_s_w, + MSA128WOpnd>, IsCommutable; +class AVER_S_D_DESC : MSA_3R_DESC_BASE<"aver_s.d", int_maxis_aver_s_d, + MSA128DOpnd>, IsCommutable; + +class AVER_U_B_DESC : MSA_3R_DESC_BASE<"aver_u.b", int_maxis_aver_u_b, + MSA128BOpnd>, IsCommutable; +class AVER_U_H_DESC : MSA_3R_DESC_BASE<"aver_u.h", int_maxis_aver_u_h, + MSA128HOpnd>, IsCommutable; +class AVER_U_W_DESC : MSA_3R_DESC_BASE<"aver_u.w", int_maxis_aver_u_w, + MSA128WOpnd>, IsCommutable; +class AVER_U_D_DESC : MSA_3R_DESC_BASE<"aver_u.d", int_maxis_aver_u_d, + MSA128DOpnd>, IsCommutable; + +class BCLR_B_DESC : MSA_3R_DESC_BASE<"bclr.b", vbclr_b, MSA128BOpnd>; +class BCLR_H_DESC : MSA_3R_DESC_BASE<"bclr.h", vbclr_h, MSA128HOpnd>; +class BCLR_W_DESC : MSA_3R_DESC_BASE<"bclr.w", vbclr_w, MSA128WOpnd>; +class BCLR_D_DESC : MSA_3R_DESC_BASE<"bclr.d", vbclr_d, MSA128DOpnd>; + +class BCLRI_B_DESC : MSA_BIT_B_DESC_BASE<"bclri.b", and, vsplat_uimm_inv_pow2, + MSA128BOpnd>; +class BCLRI_H_DESC : MSA_BIT_H_DESC_BASE<"bclri.h", and, vsplat_uimm_inv_pow2, + MSA128HOpnd>; +class BCLRI_W_DESC : MSA_BIT_W_DESC_BASE<"bclri.w", and, vsplat_uimm_inv_pow2, + MSA128WOpnd>; +class BCLRI_D_DESC : MSA_BIT_D_DESC_BASE<"bclri.d", and, vsplat_uimm_inv_pow2, + MSA128DOpnd>; + +class BINSL_B_DESC : MSA_3R_BINSX_DESC_BASE<"binsl.b", int_maxis_binsl_b, + MSA128BOpnd>; +class BINSL_H_DESC : MSA_3R_BINSX_DESC_BASE<"binsl.h", int_maxis_binsl_h, + MSA128HOpnd>; +class BINSL_W_DESC : MSA_3R_BINSX_DESC_BASE<"binsl.w", int_maxis_binsl_w, + MSA128WOpnd>; +class BINSL_D_DESC : MSA_3R_BINSX_DESC_BASE<"binsl.d", int_maxis_binsl_d, + MSA128DOpnd>; + +class BINSLI_B_DESC : MSA_BIT_BINSLI_DESC_BASE<"binsli.b", v16i8, vsplat_maskl_bits_uimm3, MSA128BOpnd>; +class BINSLI_H_DESC : MSA_BIT_BINSLI_DESC_BASE<"binsli.h", v8i16, vsplat_maskl_bits_uimm4, MSA128HOpnd>; +class BINSLI_W_DESC : MSA_BIT_BINSLI_DESC_BASE<"binsli.w", v4i32, vsplat_maskl_bits_uimm5, MSA128WOpnd>; +class BINSLI_D_DESC : MSA_BIT_BINSLI_DESC_BASE<"binsli.d", v2i64, vsplat_maskl_bits_uimm6, MSA128DOpnd>; + +class BINSR_B_DESC : MSA_3R_BINSX_DESC_BASE<"binsr.b", int_maxis_binsr_b, + MSA128BOpnd>; +class BINSR_H_DESC : MSA_3R_BINSX_DESC_BASE<"binsr.h", int_maxis_binsr_h, + MSA128HOpnd>; +class BINSR_W_DESC : MSA_3R_BINSX_DESC_BASE<"binsr.w", int_maxis_binsr_w, + MSA128WOpnd>; +class BINSR_D_DESC : MSA_3R_BINSX_DESC_BASE<"binsr.d", int_maxis_binsr_d, + MSA128DOpnd>; + +class BINSRI_B_DESC + : MSA_BIT_BINSRI_DESC_BASE<"binsri.b", v16i8, vsplat_maskr_bits_uimm3, + MSA128BOpnd>; +class BINSRI_H_DESC + : MSA_BIT_BINSRI_DESC_BASE<"binsri.h", v8i16, vsplat_maskr_bits_uimm4, + MSA128HOpnd>; +class BINSRI_W_DESC + : MSA_BIT_BINSRI_DESC_BASE<"binsri.w", v4i32, vsplat_maskr_bits_uimm5, + MSA128WOpnd>; +class BINSRI_D_DESC + : MSA_BIT_BINSRI_DESC_BASE<"binsri.d", v2i64, vsplat_maskr_bits_uimm6, + MSA128DOpnd>; + +class BMNZ_V_DESC { + dag OutOperandList = (outs MSA128BOpnd:$wd); + dag InOperandList = (ins MSA128BOpnd:$wd_in, MSA128BOpnd:$ws, + MSA128BOpnd:$wt); + string AsmString = "bmnz.v\t$wd, $ws, $wt"; + list Pattern = [(set MSA128BOpnd:$wd, (vselect MSA128BOpnd:$wt, + MSA128BOpnd:$ws, + MSA128BOpnd:$wd_in))]; + InstrItinClass Itinerary = NoItinerary; + string Constraints = "$wd = $wd_in"; +} + +class BMNZI_B_DESC { + dag OutOperandList = (outs MSA128BOpnd:$wd); + dag InOperandList = (ins MSA128BOpnd:$wd_in, MSA128BOpnd:$ws, + vsplat_uimm8:$u8); + string AsmString = "bmnzi.b\t$wd, $ws, $u8"; + list Pattern = [(set MSA128BOpnd:$wd, (vselect vsplati8_uimm8:$u8, + MSA128BOpnd:$ws, + MSA128BOpnd:$wd_in))]; + InstrItinClass Itinerary = NoItinerary; + string Constraints = "$wd = $wd_in"; +} + +class BMZ_V_DESC { + dag OutOperandList = (outs MSA128BOpnd:$wd); + dag InOperandList = (ins MSA128BOpnd:$wd_in, MSA128BOpnd:$ws, + MSA128BOpnd:$wt); + string AsmString = "bmz.v\t$wd, $ws, $wt"; + list Pattern = [(set MSA128BOpnd:$wd, (vselect MSA128BOpnd:$wt, + MSA128BOpnd:$wd_in, + MSA128BOpnd:$ws))]; + InstrItinClass Itinerary = NoItinerary; + string Constraints = "$wd = $wd_in"; +} + +class BMZI_B_DESC { + dag OutOperandList = (outs MSA128BOpnd:$wd); + dag InOperandList = (ins MSA128BOpnd:$wd_in, MSA128BOpnd:$ws, + vsplat_uimm8:$u8); + string AsmString = "bmzi.b\t$wd, $ws, $u8"; + list Pattern = [(set MSA128BOpnd:$wd, (vselect vsplati8_uimm8:$u8, + MSA128BOpnd:$wd_in, + MSA128BOpnd:$ws))]; + InstrItinClass Itinerary = NoItinerary; + string Constraints = "$wd = $wd_in"; +} + +class BNEG_B_DESC : MSA_3R_DESC_BASE<"bneg.b", vbneg_b, MSA128BOpnd>; +class BNEG_H_DESC : MSA_3R_DESC_BASE<"bneg.h", vbneg_h, MSA128HOpnd>; +class BNEG_W_DESC : MSA_3R_DESC_BASE<"bneg.w", vbneg_w, MSA128WOpnd>; +class BNEG_D_DESC : MSA_3R_DESC_BASE<"bneg.d", vbneg_d, MSA128DOpnd>; + +class BNEGI_B_DESC : MSA_BIT_B_DESC_BASE<"bnegi.b", xor, vsplat_uimm_pow2, + MSA128BOpnd>; +class BNEGI_H_DESC : MSA_BIT_H_DESC_BASE<"bnegi.h", xor, vsplat_uimm_pow2, + MSA128HOpnd>; +class BNEGI_W_DESC : MSA_BIT_W_DESC_BASE<"bnegi.w", xor, vsplat_uimm_pow2, + MSA128WOpnd>; +class BNEGI_D_DESC : MSA_BIT_D_DESC_BASE<"bnegi.d", xor, vsplat_uimm_pow2, + MSA128DOpnd>; + +class BNZ_B_DESC : MSA_CBRANCH_DESC_BASE<"bnz.b", MSA128BOpnd>; +class BNZ_H_DESC : MSA_CBRANCH_DESC_BASE<"bnz.h", MSA128HOpnd>; +class BNZ_W_DESC : MSA_CBRANCH_DESC_BASE<"bnz.w", MSA128WOpnd>; +class BNZ_D_DESC : MSA_CBRANCH_DESC_BASE<"bnz.d", MSA128DOpnd>; + +class BNZ_V_DESC : MSA_CBRANCH_DESC_BASE<"bnz.v", MSA128BOpnd>; + +class BSEL_V_DESC { + dag OutOperandList = (outs MSA128BOpnd:$wd); + dag InOperandList = (ins MSA128BOpnd:$wd_in, MSA128BOpnd:$ws, + MSA128BOpnd:$wt); + string AsmString = "bsel.v\t$wd, $ws, $wt"; + // Note that vselect and BSEL_V treat the condition operand the opposite way + // from each other. + // (vselect cond, if_set, if_clear) + // (BSEL_V cond, if_clear, if_set) + list Pattern = [(set MSA128BOpnd:$wd, + (vselect MSA128BOpnd:$wd_in, MSA128BOpnd:$wt, + MSA128BOpnd:$ws))]; + InstrItinClass Itinerary = NoItinerary; + string Constraints = "$wd = $wd_in"; +} + +class BSELI_B_DESC { + dag OutOperandList = (outs MSA128BOpnd:$wd); + dag InOperandList = (ins MSA128BOpnd:$wd_in, MSA128BOpnd:$ws, + vsplat_uimm8:$u8); + string AsmString = "bseli.b\t$wd, $ws, $u8"; + // Note that vselect and BSEL_V treat the condition operand the opposite way + // from each other. + // (vselect cond, if_set, if_clear) + // (BSEL_V cond, if_clear, if_set) + list Pattern = [(set MSA128BOpnd:$wd, (vselect MSA128BOpnd:$wd_in, + vsplati8_uimm8:$u8, + MSA128BOpnd:$ws))]; + InstrItinClass Itinerary = NoItinerary; + string Constraints = "$wd = $wd_in"; +} + +class BSET_B_DESC : MSA_3R_DESC_BASE<"bset.b", vbset_b, MSA128BOpnd>; +class BSET_H_DESC : MSA_3R_DESC_BASE<"bset.h", vbset_h, MSA128HOpnd>; +class BSET_W_DESC : MSA_3R_DESC_BASE<"bset.w", vbset_w, MSA128WOpnd>; +class BSET_D_DESC : MSA_3R_DESC_BASE<"bset.d", vbset_d, MSA128DOpnd>; + +class BSETI_B_DESC : MSA_BIT_B_DESC_BASE<"bseti.b", or, vsplat_uimm_pow2, + MSA128BOpnd>; +class BSETI_H_DESC : MSA_BIT_H_DESC_BASE<"bseti.h", or, vsplat_uimm_pow2, + MSA128HOpnd>; +class BSETI_W_DESC : MSA_BIT_W_DESC_BASE<"bseti.w", or, vsplat_uimm_pow2, + MSA128WOpnd>; +class BSETI_D_DESC : MSA_BIT_D_DESC_BASE<"bseti.d", or, vsplat_uimm_pow2, + MSA128DOpnd>; + +class BZ_B_DESC : MSA_CBRANCH_DESC_BASE<"bz.b", MSA128BOpnd>; +class BZ_H_DESC : MSA_CBRANCH_DESC_BASE<"bz.h", MSA128HOpnd>; +class BZ_W_DESC : MSA_CBRANCH_DESC_BASE<"bz.w", MSA128WOpnd>; +class BZ_D_DESC : MSA_CBRANCH_DESC_BASE<"bz.d", MSA128DOpnd>; + +class BZ_V_DESC : MSA_CBRANCH_DESC_BASE<"bz.v", MSA128BOpnd>; + +class CEQ_B_DESC : MSA_3R_DESC_BASE<"ceq.b", vseteq_v16i8, MSA128BOpnd>, + IsCommutable; +class CEQ_H_DESC : MSA_3R_DESC_BASE<"ceq.h", vseteq_v8i16, MSA128HOpnd>, + IsCommutable; +class CEQ_W_DESC : MSA_3R_DESC_BASE<"ceq.w", vseteq_v4i32, MSA128WOpnd>, + IsCommutable; +class CEQ_D_DESC : MSA_3R_DESC_BASE<"ceq.d", vseteq_v2i64, MSA128DOpnd>, + IsCommutable; + +class CEQI_B_DESC : MSA_I5_DESC_BASE<"ceqi.b", vseteq_v16i8, vsplati8_simm5, + MSA128BOpnd>; +class CEQI_H_DESC : MSA_I5_DESC_BASE<"ceqi.h", vseteq_v8i16, vsplati16_simm5, + MSA128HOpnd>; +class CEQI_W_DESC : MSA_I5_DESC_BASE<"ceqi.w", vseteq_v4i32, vsplati32_simm5, + MSA128WOpnd>; +class CEQI_D_DESC : MSA_I5_DESC_BASE<"ceqi.d", vseteq_v2i64, vsplati64_simm5, + MSA128DOpnd>; + +class CFCMSA_DESC { + dag OutOperandList = (outs GPR32Opnd:$rd); + dag InOperandList = (ins MSA128CROpnd:$cs); + string AsmString = "cfcmsa\t$rd, $cs"; + InstrItinClass Itinerary = NoItinerary; + bit hasSideEffects = 1; +} + +class CLE_S_B_DESC : MSA_3R_DESC_BASE<"cle_s.b", vsetle_v16i8, MSA128BOpnd>; +class CLE_S_H_DESC : MSA_3R_DESC_BASE<"cle_s.h", vsetle_v8i16, MSA128HOpnd>; +class CLE_S_W_DESC : MSA_3R_DESC_BASE<"cle_s.w", vsetle_v4i32, MSA128WOpnd>; +class CLE_S_D_DESC : MSA_3R_DESC_BASE<"cle_s.d", vsetle_v2i64, MSA128DOpnd>; + +class CLE_U_B_DESC : MSA_3R_DESC_BASE<"cle_u.b", vsetule_v16i8, MSA128BOpnd>; +class CLE_U_H_DESC : MSA_3R_DESC_BASE<"cle_u.h", vsetule_v8i16, MSA128HOpnd>; +class CLE_U_W_DESC : MSA_3R_DESC_BASE<"cle_u.w", vsetule_v4i32, MSA128WOpnd>; +class CLE_U_D_DESC : MSA_3R_DESC_BASE<"cle_u.d", vsetule_v2i64, MSA128DOpnd>; + +class CLEI_S_B_DESC : MSA_I5_DESC_BASE<"clei_s.b", vsetle_v16i8, + vsplati8_simm5, MSA128BOpnd>; +class CLEI_S_H_DESC : MSA_I5_DESC_BASE<"clei_s.h", vsetle_v8i16, + vsplati16_simm5, MSA128HOpnd>; +class CLEI_S_W_DESC : MSA_I5_DESC_BASE<"clei_s.w", vsetle_v4i32, + vsplati32_simm5, MSA128WOpnd>; +class CLEI_S_D_DESC : MSA_I5_DESC_BASE<"clei_s.d", vsetle_v2i64, + vsplati64_simm5, MSA128DOpnd>; + +class CLEI_U_B_DESC : MSA_I5_DESC_BASE<"clei_u.b", vsetule_v16i8, + vsplati8_uimm5, MSA128BOpnd>; +class CLEI_U_H_DESC : MSA_I5_DESC_BASE<"clei_u.h", vsetule_v8i16, + vsplati16_uimm5, MSA128HOpnd>; +class CLEI_U_W_DESC : MSA_I5_DESC_BASE<"clei_u.w", vsetule_v4i32, + vsplati32_uimm5, MSA128WOpnd>; +class CLEI_U_D_DESC : MSA_I5_DESC_BASE<"clei_u.d", vsetule_v2i64, + vsplati64_uimm5, MSA128DOpnd>; + +class CLT_S_B_DESC : MSA_3R_DESC_BASE<"clt_s.b", vsetlt_v16i8, MSA128BOpnd>; +class CLT_S_H_DESC : MSA_3R_DESC_BASE<"clt_s.h", vsetlt_v8i16, MSA128HOpnd>; +class CLT_S_W_DESC : MSA_3R_DESC_BASE<"clt_s.w", vsetlt_v4i32, MSA128WOpnd>; +class CLT_S_D_DESC : MSA_3R_DESC_BASE<"clt_s.d", vsetlt_v2i64, MSA128DOpnd>; + +class CLT_U_B_DESC : MSA_3R_DESC_BASE<"clt_u.b", vsetult_v16i8, MSA128BOpnd>; +class CLT_U_H_DESC : MSA_3R_DESC_BASE<"clt_u.h", vsetult_v8i16, MSA128HOpnd>; +class CLT_U_W_DESC : MSA_3R_DESC_BASE<"clt_u.w", vsetult_v4i32, MSA128WOpnd>; +class CLT_U_D_DESC : MSA_3R_DESC_BASE<"clt_u.d", vsetult_v2i64, MSA128DOpnd>; + +class CLTI_S_B_DESC : MSA_I5_DESC_BASE<"clti_s.b", vsetlt_v16i8, + vsplati8_simm5, MSA128BOpnd>; +class CLTI_S_H_DESC : MSA_I5_DESC_BASE<"clti_s.h", vsetlt_v8i16, + vsplati16_simm5, MSA128HOpnd>; +class CLTI_S_W_DESC : MSA_I5_DESC_BASE<"clti_s.w", vsetlt_v4i32, + vsplati32_simm5, MSA128WOpnd>; +class CLTI_S_D_DESC : MSA_I5_DESC_BASE<"clti_s.d", vsetlt_v2i64, + vsplati64_simm5, MSA128DOpnd>; + +class CLTI_U_B_DESC : MSA_I5_DESC_BASE<"clti_u.b", vsetult_v16i8, + vsplati8_uimm5, MSA128BOpnd>; +class CLTI_U_H_DESC : MSA_I5_DESC_BASE<"clti_u.h", vsetult_v8i16, + vsplati16_uimm5, MSA128HOpnd>; +class CLTI_U_W_DESC : MSA_I5_DESC_BASE<"clti_u.w", vsetult_v4i32, + vsplati32_uimm5, MSA128WOpnd>; +class CLTI_U_D_DESC : MSA_I5_DESC_BASE<"clti_u.d", vsetult_v2i64, + vsplati64_uimm5, MSA128DOpnd>; + +class COPY_S_B_DESC : MSA_COPY_DESC_BASE<"copy_s.b", vextract_sext_i8, v16i8, + uimm4_ptr, immZExt4Ptr, GPR32Opnd, + MSA128BOpnd>; +class COPY_S_H_DESC : MSA_COPY_DESC_BASE<"copy_s.h", vextract_sext_i16, v8i16, + uimm3_ptr, immZExt3Ptr, GPR32Opnd, + MSA128HOpnd>; +class COPY_S_W_DESC : MSA_COPY_DESC_BASE<"copy_s.w", vextract_sext_i32, v4i32, + uimm2_ptr, immZExt2Ptr, GPR32Opnd, + MSA128WOpnd>; +class COPY_S_D_DESC : MSA_COPY_DESC_BASE<"copy_s.d", vextract_sext_i64, v2i64, + uimm1_ptr, immZExt1Ptr, GPR64Opnd, + MSA128DOpnd>; + +class COPY_U_B_DESC : MSA_COPY_DESC_BASE<"copy_u.b", vextract_zext_i8, v16i8, + uimm4_ptr, immZExt4Ptr, GPR32Opnd, + MSA128BOpnd>; +class COPY_U_H_DESC : MSA_COPY_DESC_BASE<"copy_u.h", vextract_zext_i16, v8i16, + uimm3_ptr, immZExt3Ptr, GPR32Opnd, + MSA128HOpnd>; +class COPY_U_W_DESC : MSA_COPY_DESC_BASE<"copy_u.w", vextract_zext_i32, v4i32, + uimm2_ptr, immZExt2Ptr, GPR32Opnd, + MSA128WOpnd>; + +class COPY_FW_PSEUDO_DESC : MSA_COPY_PSEUDO_BASE; +class COPY_FD_PSEUDO_DESC : MSA_COPY_PSEUDO_BASE; + +class CTCMSA_DESC { + dag OutOperandList = (outs); + dag InOperandList = (ins MSA128CROpnd:$cd, GPR32Opnd:$rs); + string AsmString = "ctcmsa\t$cd, $rs"; + InstrItinClass Itinerary = NoItinerary; + bit hasSideEffects = 1; +} + +class DIV_S_B_DESC : MSA_3R_DESC_BASE<"div_s.b", sdiv, MSA128BOpnd>; +class DIV_S_H_DESC : MSA_3R_DESC_BASE<"div_s.h", sdiv, MSA128HOpnd>; +class DIV_S_W_DESC : MSA_3R_DESC_BASE<"div_s.w", sdiv, MSA128WOpnd>; +class DIV_S_D_DESC : MSA_3R_DESC_BASE<"div_s.d", sdiv, MSA128DOpnd>; + +class DIV_U_B_DESC : MSA_3R_DESC_BASE<"div_u.b", udiv, MSA128BOpnd>; +class DIV_U_H_DESC : MSA_3R_DESC_BASE<"div_u.h", udiv, MSA128HOpnd>; +class DIV_U_W_DESC : MSA_3R_DESC_BASE<"div_u.w", udiv, MSA128WOpnd>; +class DIV_U_D_DESC : MSA_3R_DESC_BASE<"div_u.d", udiv, MSA128DOpnd>; + +class DOTP_S_H_DESC : MSA_3R_DESC_BASE<"dotp_s.h", int_maxis_dotp_s_h, + MSA128HOpnd, MSA128BOpnd, MSA128BOpnd>, + IsCommutable; +class DOTP_S_W_DESC : MSA_3R_DESC_BASE<"dotp_s.w", int_maxis_dotp_s_w, + MSA128WOpnd, MSA128HOpnd, MSA128HOpnd>, + IsCommutable; +class DOTP_S_D_DESC : MSA_3R_DESC_BASE<"dotp_s.d", int_maxis_dotp_s_d, + MSA128DOpnd, MSA128WOpnd, MSA128WOpnd>, + IsCommutable; + +class DOTP_U_H_DESC : MSA_3R_DESC_BASE<"dotp_u.h", int_maxis_dotp_u_h, + MSA128HOpnd, MSA128BOpnd, MSA128BOpnd>, + IsCommutable; +class DOTP_U_W_DESC : MSA_3R_DESC_BASE<"dotp_u.w", int_maxis_dotp_u_w, + MSA128WOpnd, MSA128HOpnd, MSA128HOpnd>, + IsCommutable; +class DOTP_U_D_DESC : MSA_3R_DESC_BASE<"dotp_u.d", int_maxis_dotp_u_d, + MSA128DOpnd, MSA128WOpnd, MSA128WOpnd>, + IsCommutable; + +class DPADD_S_H_DESC : MSA_3R_4R_DESC_BASE<"dpadd_s.h", int_maxis_dpadd_s_h, + MSA128HOpnd, MSA128BOpnd, + MSA128BOpnd>, IsCommutable; +class DPADD_S_W_DESC : MSA_3R_4R_DESC_BASE<"dpadd_s.w", int_maxis_dpadd_s_w, + MSA128WOpnd, MSA128HOpnd, + MSA128HOpnd>, IsCommutable; +class DPADD_S_D_DESC : MSA_3R_4R_DESC_BASE<"dpadd_s.d", int_maxis_dpadd_s_d, + MSA128DOpnd, MSA128WOpnd, + MSA128WOpnd>, IsCommutable; + +class DPADD_U_H_DESC : MSA_3R_4R_DESC_BASE<"dpadd_u.h", int_maxis_dpadd_u_h, + MSA128HOpnd, MSA128BOpnd, + MSA128BOpnd>, IsCommutable; +class DPADD_U_W_DESC : MSA_3R_4R_DESC_BASE<"dpadd_u.w", int_maxis_dpadd_u_w, + MSA128WOpnd, MSA128HOpnd, + MSA128HOpnd>, IsCommutable; +class DPADD_U_D_DESC : MSA_3R_4R_DESC_BASE<"dpadd_u.d", int_maxis_dpadd_u_d, + MSA128DOpnd, MSA128WOpnd, + MSA128WOpnd>, IsCommutable; + +class DPSUB_S_H_DESC : MSA_3R_4R_DESC_BASE<"dpsub_s.h", int_maxis_dpsub_s_h, + MSA128HOpnd, MSA128BOpnd, + MSA128BOpnd>; +class DPSUB_S_W_DESC : MSA_3R_4R_DESC_BASE<"dpsub_s.w", int_maxis_dpsub_s_w, + MSA128WOpnd, MSA128HOpnd, + MSA128HOpnd>; +class DPSUB_S_D_DESC : MSA_3R_4R_DESC_BASE<"dpsub_s.d", int_maxis_dpsub_s_d, + MSA128DOpnd, MSA128WOpnd, + MSA128WOpnd>; + +class DPSUB_U_H_DESC : MSA_3R_4R_DESC_BASE<"dpsub_u.h", int_maxis_dpsub_u_h, + MSA128HOpnd, MSA128BOpnd, + MSA128BOpnd>; +class DPSUB_U_W_DESC : MSA_3R_4R_DESC_BASE<"dpsub_u.w", int_maxis_dpsub_u_w, + MSA128WOpnd, MSA128HOpnd, + MSA128HOpnd>; +class DPSUB_U_D_DESC : MSA_3R_4R_DESC_BASE<"dpsub_u.d", int_maxis_dpsub_u_d, + MSA128DOpnd, MSA128WOpnd, + MSA128WOpnd>; + +class FADD_W_DESC : MSA_3RF_DESC_BASE<"fadd.w", fadd, MSA128WOpnd>, + IsCommutable; +class FADD_D_DESC : MSA_3RF_DESC_BASE<"fadd.d", fadd, MSA128DOpnd>, + IsCommutable; + +class FCAF_W_DESC : MSA_3RF_DESC_BASE<"fcaf.w", int_maxis_fcaf_w, MSA128WOpnd>, + IsCommutable; +class FCAF_D_DESC : MSA_3RF_DESC_BASE<"fcaf.d", int_maxis_fcaf_d, MSA128DOpnd>, + IsCommutable; + +class FCEQ_W_DESC : MSA_3RF_DESC_BASE<"fceq.w", vfsetoeq_v4f32, MSA128WOpnd>, + IsCommutable; +class FCEQ_D_DESC : MSA_3RF_DESC_BASE<"fceq.d", vfsetoeq_v2f64, MSA128DOpnd>, + IsCommutable; + +class FCLASS_W_DESC : MSA_2RF_DESC_BASE<"fclass.w", int_maxis_fclass_w, + MSA128WOpnd>; +class FCLASS_D_DESC : MSA_2RF_DESC_BASE<"fclass.d", int_maxis_fclass_d, + MSA128DOpnd>; + +class FCLE_W_DESC : MSA_3RF_DESC_BASE<"fcle.w", vfsetole_v4f32, MSA128WOpnd>; +class FCLE_D_DESC : MSA_3RF_DESC_BASE<"fcle.d", vfsetole_v2f64, MSA128DOpnd>; + +class FCLT_W_DESC : MSA_3RF_DESC_BASE<"fclt.w", vfsetolt_v4f32, MSA128WOpnd>; +class FCLT_D_DESC : MSA_3RF_DESC_BASE<"fclt.d", vfsetolt_v2f64, MSA128DOpnd>; + +class FCNE_W_DESC : MSA_3RF_DESC_BASE<"fcne.w", vfsetone_v4f32, MSA128WOpnd>, + IsCommutable; +class FCNE_D_DESC : MSA_3RF_DESC_BASE<"fcne.d", vfsetone_v2f64, MSA128DOpnd>, + IsCommutable; + +class FCOR_W_DESC : MSA_3RF_DESC_BASE<"fcor.w", vfsetord_v4f32, MSA128WOpnd>, + IsCommutable; +class FCOR_D_DESC : MSA_3RF_DESC_BASE<"fcor.d", vfsetord_v2f64, MSA128DOpnd>, + IsCommutable; + +class FCUEQ_W_DESC : MSA_3RF_DESC_BASE<"fcueq.w", vfsetueq_v4f32, MSA128WOpnd>, + IsCommutable; +class FCUEQ_D_DESC : MSA_3RF_DESC_BASE<"fcueq.d", vfsetueq_v2f64, MSA128DOpnd>, + IsCommutable; + +class FCULE_W_DESC : MSA_3RF_DESC_BASE<"fcule.w", vfsetule_v4f32, MSA128WOpnd>, + IsCommutable; +class FCULE_D_DESC : MSA_3RF_DESC_BASE<"fcule.d", vfsetule_v2f64, MSA128DOpnd>, + IsCommutable; + +class FCULT_W_DESC : MSA_3RF_DESC_BASE<"fcult.w", vfsetult_v4f32, MSA128WOpnd>, + IsCommutable; +class FCULT_D_DESC : MSA_3RF_DESC_BASE<"fcult.d", vfsetult_v2f64, MSA128DOpnd>, + IsCommutable; + +class FCUN_W_DESC : MSA_3RF_DESC_BASE<"fcun.w", vfsetun_v4f32, MSA128WOpnd>, + IsCommutable; +class FCUN_D_DESC : MSA_3RF_DESC_BASE<"fcun.d", vfsetun_v2f64, MSA128DOpnd>, + IsCommutable; + +class FCUNE_W_DESC : MSA_3RF_DESC_BASE<"fcune.w", vfsetune_v4f32, MSA128WOpnd>, + IsCommutable; +class FCUNE_D_DESC : MSA_3RF_DESC_BASE<"fcune.d", vfsetune_v2f64, MSA128DOpnd>, + IsCommutable; + +class FDIV_W_DESC : MSA_3RF_DESC_BASE<"fdiv.w", fdiv, MSA128WOpnd>; +class FDIV_D_DESC : MSA_3RF_DESC_BASE<"fdiv.d", fdiv, MSA128DOpnd>; + +class FEXDO_H_DESC : MSA_3RF_DESC_BASE<"fexdo.h", int_maxis_fexdo_h, + MSA128HOpnd, MSA128WOpnd, MSA128WOpnd>; +class FEXDO_W_DESC : MSA_3RF_DESC_BASE<"fexdo.w", int_maxis_fexdo_w, + MSA128WOpnd, MSA128DOpnd, MSA128DOpnd>; + +// The fexp2.df instruction multiplies the first operand by 2 to the power of +// the second operand. We therefore need a pseudo-insn in order to invent the +// 1.0 when we only need to match ISD::FEXP2. +class FEXP2_W_DESC : MSA_3RF_DESC_BASE<"fexp2.w", mul_fexp2, MSA128WOpnd>; +class FEXP2_D_DESC : MSA_3RF_DESC_BASE<"fexp2.d", mul_fexp2, MSA128DOpnd>; +let usesCustomInserter = 1 in { + class FEXP2_W_1_PSEUDO_DESC : + MSAPseudo<(outs MSA128W:$wd), (ins MSA128W:$ws), + [(set MSA128W:$wd, (fexp2 MSA128W:$ws))]>; + class FEXP2_D_1_PSEUDO_DESC : + MSAPseudo<(outs MSA128D:$wd), (ins MSA128D:$ws), + [(set MSA128D:$wd, (fexp2 MSA128D:$ws))]>; +} + +class FEXUPL_W_DESC : MSA_2RF_DESC_BASE<"fexupl.w", int_maxis_fexupl_w, + MSA128WOpnd, MSA128HOpnd>; +class FEXUPL_D_DESC : MSA_2RF_DESC_BASE<"fexupl.d", int_maxis_fexupl_d, + MSA128DOpnd, MSA128WOpnd>; + +class FEXUPR_W_DESC : MSA_2RF_DESC_BASE<"fexupr.w", int_maxis_fexupr_w, + MSA128WOpnd, MSA128HOpnd>; +class FEXUPR_D_DESC : MSA_2RF_DESC_BASE<"fexupr.d", int_maxis_fexupr_d, + MSA128DOpnd, MSA128WOpnd>; + +class FFINT_S_W_DESC : MSA_2RF_DESC_BASE<"ffint_s.w", sint_to_fp, MSA128WOpnd>; +class FFINT_S_D_DESC : MSA_2RF_DESC_BASE<"ffint_s.d", sint_to_fp, MSA128DOpnd>; + +class FFINT_U_W_DESC : MSA_2RF_DESC_BASE<"ffint_u.w", uint_to_fp, MSA128WOpnd>; +class FFINT_U_D_DESC : MSA_2RF_DESC_BASE<"ffint_u.d", uint_to_fp, MSA128DOpnd>; + +class FFQL_W_DESC : MSA_2RF_DESC_BASE<"ffql.w", int_maxis_ffql_w, + MSA128WOpnd, MSA128HOpnd>; +class FFQL_D_DESC : MSA_2RF_DESC_BASE<"ffql.d", int_maxis_ffql_d, + MSA128DOpnd, MSA128WOpnd>; + +class FFQR_W_DESC : MSA_2RF_DESC_BASE<"ffqr.w", int_maxis_ffqr_w, + MSA128WOpnd, MSA128HOpnd>; +class FFQR_D_DESC : MSA_2RF_DESC_BASE<"ffqr.d", int_maxis_ffqr_d, + MSA128DOpnd, MSA128WOpnd>; + +class FILL_B_DESC : MSA_2R_FILL_DESC_BASE<"fill.b", v16i8, vsplati8, + MSA128BOpnd, GPR32Opnd>; +class FILL_H_DESC : MSA_2R_FILL_DESC_BASE<"fill.h", v8i16, vsplati16, + MSA128HOpnd, GPR32Opnd>; +class FILL_W_DESC : MSA_2R_FILL_DESC_BASE<"fill.w", v4i32, vsplati32, + MSA128WOpnd, GPR32Opnd>; +class FILL_D_DESC : MSA_2R_FILL_DESC_BASE<"fill.d", v2i64, vsplati64, + MSA128DOpnd, GPR64Opnd>; + +class FILL_FW_PSEUDO_DESC : MSA_2R_FILL_PSEUDO_BASE; +class FILL_FD_PSEUDO_DESC : MSA_2R_FILL_PSEUDO_BASE; + +class FLOG2_W_DESC : MSA_2RF_DESC_BASE<"flog2.w", flog2, MSA128WOpnd>; +class FLOG2_D_DESC : MSA_2RF_DESC_BASE<"flog2.d", flog2, MSA128DOpnd>; + +class FMADD_W_DESC : MSA_3RF_4RF_DESC_BASE<"fmadd.w", fma, MSA128WOpnd>; +class FMADD_D_DESC : MSA_3RF_4RF_DESC_BASE<"fmadd.d", fma, MSA128DOpnd>; + +class FMAX_W_DESC : MSA_3RF_DESC_BASE<"fmax.w", int_maxis_fmax_w, MSA128WOpnd>; +class FMAX_D_DESC : MSA_3RF_DESC_BASE<"fmax.d", int_maxis_fmax_d, MSA128DOpnd>; + +class FMAX_A_W_DESC : MSA_3RF_DESC_BASE<"fmax_a.w", int_maxis_fmax_a_w, + MSA128WOpnd>; +class FMAX_A_D_DESC : MSA_3RF_DESC_BASE<"fmax_a.d", int_maxis_fmax_a_d, + MSA128DOpnd>; + +class FMIN_W_DESC : MSA_3RF_DESC_BASE<"fmin.w", int_maxis_fmin_w, MSA128WOpnd>; +class FMIN_D_DESC : MSA_3RF_DESC_BASE<"fmin.d", int_maxis_fmin_d, MSA128DOpnd>; + +class FMIN_A_W_DESC : MSA_3RF_DESC_BASE<"fmin_a.w", int_maxis_fmin_a_w, + MSA128WOpnd>; +class FMIN_A_D_DESC : MSA_3RF_DESC_BASE<"fmin_a.d", int_maxis_fmin_a_d, + MSA128DOpnd>; + +class FMSUB_W_DESC : MSA_3RF_4RF_DESC_BASE<"fmsub.w", fms, MSA128WOpnd>; +class FMSUB_D_DESC : MSA_3RF_4RF_DESC_BASE<"fmsub.d", fms, MSA128DOpnd>; + +class FMUL_W_DESC : MSA_3RF_DESC_BASE<"fmul.w", fmul, MSA128WOpnd>; +class FMUL_D_DESC : MSA_3RF_DESC_BASE<"fmul.d", fmul, MSA128DOpnd>; + +class FRINT_W_DESC : MSA_2RF_DESC_BASE<"frint.w", frint, MSA128WOpnd>; +class FRINT_D_DESC : MSA_2RF_DESC_BASE<"frint.d", frint, MSA128DOpnd>; + +class FRCP_W_DESC : MSA_2RF_DESC_BASE<"frcp.w", int_maxis_frcp_w, MSA128WOpnd>; +class FRCP_D_DESC : MSA_2RF_DESC_BASE<"frcp.d", int_maxis_frcp_d, MSA128DOpnd>; + +class FRSQRT_W_DESC : MSA_2RF_DESC_BASE<"frsqrt.w", int_maxis_frsqrt_w, + MSA128WOpnd>; +class FRSQRT_D_DESC : MSA_2RF_DESC_BASE<"frsqrt.d", int_maxis_frsqrt_d, + MSA128DOpnd>; + +class FSAF_W_DESC : MSA_3RF_DESC_BASE<"fsaf.w", int_maxis_fsaf_w, MSA128WOpnd>; +class FSAF_D_DESC : MSA_3RF_DESC_BASE<"fsaf.d", int_maxis_fsaf_d, MSA128DOpnd>; + +class FSEQ_W_DESC : MSA_3RF_DESC_BASE<"fseq.w", int_maxis_fseq_w, MSA128WOpnd>; +class FSEQ_D_DESC : MSA_3RF_DESC_BASE<"fseq.d", int_maxis_fseq_d, MSA128DOpnd>; + +class FSLE_W_DESC : MSA_3RF_DESC_BASE<"fsle.w", int_maxis_fsle_w, MSA128WOpnd>; +class FSLE_D_DESC : MSA_3RF_DESC_BASE<"fsle.d", int_maxis_fsle_d, MSA128DOpnd>; + +class FSLT_W_DESC : MSA_3RF_DESC_BASE<"fslt.w", int_maxis_fslt_w, MSA128WOpnd>; +class FSLT_D_DESC : MSA_3RF_DESC_BASE<"fslt.d", int_maxis_fslt_d, MSA128DOpnd>; + +class FSNE_W_DESC : MSA_3RF_DESC_BASE<"fsne.w", int_maxis_fsne_w, MSA128WOpnd>; +class FSNE_D_DESC : MSA_3RF_DESC_BASE<"fsne.d", int_maxis_fsne_d, MSA128DOpnd>; + +class FSOR_W_DESC : MSA_3RF_DESC_BASE<"fsor.w", int_maxis_fsor_w, MSA128WOpnd>; +class FSOR_D_DESC : MSA_3RF_DESC_BASE<"fsor.d", int_maxis_fsor_d, MSA128DOpnd>; + +class FSQRT_W_DESC : MSA_2RF_DESC_BASE<"fsqrt.w", fsqrt, MSA128WOpnd>; +class FSQRT_D_DESC : MSA_2RF_DESC_BASE<"fsqrt.d", fsqrt, MSA128DOpnd>; + +class FSUB_W_DESC : MSA_3RF_DESC_BASE<"fsub.w", fsub, MSA128WOpnd>; +class FSUB_D_DESC : MSA_3RF_DESC_BASE<"fsub.d", fsub, MSA128DOpnd>; + +class FSUEQ_W_DESC : MSA_3RF_DESC_BASE<"fsueq.w", int_maxis_fsueq_w, + MSA128WOpnd>; +class FSUEQ_D_DESC : MSA_3RF_DESC_BASE<"fsueq.d", int_maxis_fsueq_d, + MSA128DOpnd>; + +class FSULE_W_DESC : MSA_3RF_DESC_BASE<"fsule.w", int_maxis_fsule_w, + MSA128WOpnd>; +class FSULE_D_DESC : MSA_3RF_DESC_BASE<"fsule.d", int_maxis_fsule_d, + MSA128DOpnd>; + +class FSULT_W_DESC : MSA_3RF_DESC_BASE<"fsult.w", int_maxis_fsult_w, + MSA128WOpnd>; +class FSULT_D_DESC : MSA_3RF_DESC_BASE<"fsult.d", int_maxis_fsult_d, + MSA128DOpnd>; + +class FSUN_W_DESC : MSA_3RF_DESC_BASE<"fsun.w", int_maxis_fsun_w, + MSA128WOpnd>; +class FSUN_D_DESC : MSA_3RF_DESC_BASE<"fsun.d", int_maxis_fsun_d, + MSA128DOpnd>; + +class FSUNE_W_DESC : MSA_3RF_DESC_BASE<"fsune.w", int_maxis_fsune_w, + MSA128WOpnd>; +class FSUNE_D_DESC : MSA_3RF_DESC_BASE<"fsune.d", int_maxis_fsune_d, + MSA128DOpnd>; + +class FTINT_S_W_DESC : MSA_2RF_DESC_BASE<"ftint_s.w", int_maxis_ftint_s_w, + MSA128WOpnd>; +class FTINT_S_D_DESC : MSA_2RF_DESC_BASE<"ftint_s.d", int_maxis_ftint_s_d, + MSA128DOpnd>; + +class FTINT_U_W_DESC : MSA_2RF_DESC_BASE<"ftint_u.w", int_maxis_ftint_u_w, + MSA128WOpnd>; +class FTINT_U_D_DESC : MSA_2RF_DESC_BASE<"ftint_u.d", int_maxis_ftint_u_d, + MSA128DOpnd>; + +class FTQ_H_DESC : MSA_3RF_DESC_BASE<"ftq.h", int_maxis_ftq_h, + MSA128HOpnd, MSA128WOpnd, MSA128WOpnd>; +class FTQ_W_DESC : MSA_3RF_DESC_BASE<"ftq.w", int_maxis_ftq_w, + MSA128WOpnd, MSA128DOpnd, MSA128DOpnd>; + +class FTRUNC_S_W_DESC : MSA_2RF_DESC_BASE<"ftrunc_s.w", fp_to_sint, + MSA128WOpnd>; +class FTRUNC_S_D_DESC : MSA_2RF_DESC_BASE<"ftrunc_s.d", fp_to_sint, + MSA128DOpnd>; + +class FTRUNC_U_W_DESC : MSA_2RF_DESC_BASE<"ftrunc_u.w", fp_to_uint, + MSA128WOpnd>; +class FTRUNC_U_D_DESC : MSA_2RF_DESC_BASE<"ftrunc_u.d", fp_to_uint, + MSA128DOpnd>; + +class HADD_S_H_DESC : MSA_3R_DESC_BASE<"hadd_s.h", int_maxis_hadd_s_h, + MSA128HOpnd, MSA128BOpnd, MSA128BOpnd>; +class HADD_S_W_DESC : MSA_3R_DESC_BASE<"hadd_s.w", int_maxis_hadd_s_w, + MSA128WOpnd, MSA128HOpnd, MSA128HOpnd>; +class HADD_S_D_DESC : MSA_3R_DESC_BASE<"hadd_s.d", int_maxis_hadd_s_d, + MSA128DOpnd, MSA128WOpnd, MSA128WOpnd>; + +class HADD_U_H_DESC : MSA_3R_DESC_BASE<"hadd_u.h", int_maxis_hadd_u_h, + MSA128HOpnd, MSA128BOpnd, MSA128BOpnd>; +class HADD_U_W_DESC : MSA_3R_DESC_BASE<"hadd_u.w", int_maxis_hadd_u_w, + MSA128WOpnd, MSA128HOpnd, MSA128HOpnd>; +class HADD_U_D_DESC : MSA_3R_DESC_BASE<"hadd_u.d", int_maxis_hadd_u_d, + MSA128DOpnd, MSA128WOpnd, MSA128WOpnd>; + +class HSUB_S_H_DESC : MSA_3R_DESC_BASE<"hsub_s.h", int_maxis_hsub_s_h, + MSA128HOpnd, MSA128BOpnd, MSA128BOpnd>; +class HSUB_S_W_DESC : MSA_3R_DESC_BASE<"hsub_s.w", int_maxis_hsub_s_w, + MSA128WOpnd, MSA128HOpnd, MSA128HOpnd>; +class HSUB_S_D_DESC : MSA_3R_DESC_BASE<"hsub_s.d", int_maxis_hsub_s_d, + MSA128DOpnd, MSA128WOpnd, MSA128WOpnd>; + +class HSUB_U_H_DESC : MSA_3R_DESC_BASE<"hsub_u.h", int_maxis_hsub_u_h, + MSA128HOpnd, MSA128BOpnd, MSA128BOpnd>; +class HSUB_U_W_DESC : MSA_3R_DESC_BASE<"hsub_u.w", int_maxis_hsub_u_w, + MSA128WOpnd, MSA128HOpnd, MSA128HOpnd>; +class HSUB_U_D_DESC : MSA_3R_DESC_BASE<"hsub_u.d", int_maxis_hsub_u_d, + MSA128DOpnd, MSA128WOpnd, MSA128WOpnd>; + +class ILVEV_B_DESC : MSA_3R_DESC_BASE<"ilvev.b", MaxisILVEV, MSA128BOpnd>; +class ILVEV_H_DESC : MSA_3R_DESC_BASE<"ilvev.h", MaxisILVEV, MSA128HOpnd>; +class ILVEV_W_DESC : MSA_3R_DESC_BASE<"ilvev.w", MaxisILVEV, MSA128WOpnd>; +class ILVEV_D_DESC : MSA_3R_DESC_BASE<"ilvev.d", MaxisILVEV, MSA128DOpnd>; + +class ILVL_B_DESC : MSA_3R_DESC_BASE<"ilvl.b", MaxisILVL, MSA128BOpnd>; +class ILVL_H_DESC : MSA_3R_DESC_BASE<"ilvl.h", MaxisILVL, MSA128HOpnd>; +class ILVL_W_DESC : MSA_3R_DESC_BASE<"ilvl.w", MaxisILVL, MSA128WOpnd>; +class ILVL_D_DESC : MSA_3R_DESC_BASE<"ilvl.d", MaxisILVL, MSA128DOpnd>; + +class ILVOD_B_DESC : MSA_3R_DESC_BASE<"ilvod.b", MaxisILVOD, MSA128BOpnd>; +class ILVOD_H_DESC : MSA_3R_DESC_BASE<"ilvod.h", MaxisILVOD, MSA128HOpnd>; +class ILVOD_W_DESC : MSA_3R_DESC_BASE<"ilvod.w", MaxisILVOD, MSA128WOpnd>; +class ILVOD_D_DESC : MSA_3R_DESC_BASE<"ilvod.d", MaxisILVOD, MSA128DOpnd>; + +class ILVR_B_DESC : MSA_3R_DESC_BASE<"ilvr.b", MaxisILVR, MSA128BOpnd>; +class ILVR_H_DESC : MSA_3R_DESC_BASE<"ilvr.h", MaxisILVR, MSA128HOpnd>; +class ILVR_W_DESC : MSA_3R_DESC_BASE<"ilvr.w", MaxisILVR, MSA128WOpnd>; +class ILVR_D_DESC : MSA_3R_DESC_BASE<"ilvr.d", MaxisILVR, MSA128DOpnd>; + +class INSERT_B_DESC : MSA_INSERT_DESC_BASE<"insert.b", vinsert_v16i8, uimm4, + immZExt4Ptr, MSA128BOpnd, GPR32Opnd>; +class INSERT_H_DESC : MSA_INSERT_DESC_BASE<"insert.h", vinsert_v8i16, uimm3, + immZExt3Ptr, MSA128HOpnd, GPR32Opnd>; +class INSERT_W_DESC : MSA_INSERT_DESC_BASE<"insert.w", vinsert_v4i32, uimm2, + immZExt2Ptr, MSA128WOpnd, GPR32Opnd>; +class INSERT_D_DESC : MSA_INSERT_DESC_BASE<"insert.d", vinsert_v2i64, uimm1, + immZExt1Ptr, MSA128DOpnd, GPR64Opnd>; + +class INSERT_B_VIDX_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; +class INSERT_H_VIDX_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; +class INSERT_W_VIDX_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; +class INSERT_D_VIDX_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; + +class INSERT_FW_PSEUDO_DESC : MSA_INSERT_PSEUDO_BASE; +class INSERT_FD_PSEUDO_DESC : MSA_INSERT_PSEUDO_BASE; + +class INSERT_FW_VIDX_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; +class INSERT_FD_VIDX_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; + +class INSERT_B_VIDX64_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; +class INSERT_H_VIDX64_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; +class INSERT_W_VIDX64_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; +class INSERT_D_VIDX64_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; + +class INSERT_FW_VIDX64_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; +class INSERT_FD_VIDX64_PSEUDO_DESC : + MSA_INSERT_VIDX_PSEUDO_BASE; + +class INSVE_B_DESC : MSA_INSVE_DESC_BASE<"insve.b", insve_v16i8, uimm4, immZExt4, + MSA128BOpnd>; +class INSVE_H_DESC : MSA_INSVE_DESC_BASE<"insve.h", insve_v8i16, uimm3, immZExt3, + MSA128HOpnd>; +class INSVE_W_DESC : MSA_INSVE_DESC_BASE<"insve.w", insve_v4i32, uimm2, immZExt2, + MSA128WOpnd>; +class INSVE_D_DESC : MSA_INSVE_DESC_BASE<"insve.d", insve_v2i64, uimm1, immZExt1, + MSA128DOpnd>; + +class LD_DESC_BASE { + dag OutOperandList = (outs ROWD:$wd); + dag InOperandList = (ins MemOpnd:$addr); + string AsmString = !strconcat(instr_asm, "\t$wd, $addr"); + list Pattern = [(set ROWD:$wd, (TyNode (OpNode Addr:$addr)))]; + InstrItinClass Itinerary = itin; + string DecoderMethod = "DecodeMSA128Mem"; +} + +class LD_B_DESC : LD_DESC_BASE<"ld.b", load, v16i8, MSA128BOpnd, mem_simm10>; +class LD_H_DESC : LD_DESC_BASE<"ld.h", load, v8i16, MSA128HOpnd, + mem_simm10_lsl1, addrimm10lsl1>; +class LD_W_DESC : LD_DESC_BASE<"ld.w", load, v4i32, MSA128WOpnd, + mem_simm10_lsl2, addrimm10lsl2>; +class LD_D_DESC : LD_DESC_BASE<"ld.d", load, v2i64, MSA128DOpnd, + mem_simm10_lsl3, addrimm10lsl3>; + +class LDI_B_DESC : MSA_I10_LDI_DESC_BASE<"ldi.b", MSA128BOpnd>; +class LDI_H_DESC : MSA_I10_LDI_DESC_BASE<"ldi.h", MSA128HOpnd>; +class LDI_W_DESC : MSA_I10_LDI_DESC_BASE<"ldi.w", MSA128WOpnd>; +class LDI_D_DESC : MSA_I10_LDI_DESC_BASE<"ldi.d", MSA128DOpnd>; + +class LSA_DESC_BASE { + dag OutOperandList = (outs RORD:$rd); + dag InOperandList = (ins RORD:$rs, RORD:$rt, uimm2_plus1:$sa); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt, $sa"); + list Pattern = [(set RORD:$rd, (add RORD:$rt, + (shl RORD:$rs, + immZExt2Lsa:$sa)))]; + InstrItinClass Itinerary = itin; +} + +class LSA_DESC : LSA_DESC_BASE<"lsa", GPR32Opnd, II_LSA>; +class DLSA_DESC : LSA_DESC_BASE<"dlsa", GPR64Opnd, II_DLSA>; + +class MADD_Q_H_DESC : MSA_3RF_4RF_DESC_BASE<"madd_q.h", int_maxis_madd_q_h, + MSA128HOpnd>; +class MADD_Q_W_DESC : MSA_3RF_4RF_DESC_BASE<"madd_q.w", int_maxis_madd_q_w, + MSA128WOpnd>; + +class MADDR_Q_H_DESC : MSA_3RF_4RF_DESC_BASE<"maddr_q.h", int_maxis_maddr_q_h, + MSA128HOpnd>; +class MADDR_Q_W_DESC : MSA_3RF_4RF_DESC_BASE<"maddr_q.w", int_maxis_maddr_q_w, + MSA128WOpnd>; + +class MADDV_B_DESC : MSA_3R_4R_DESC_BASE<"maddv.b", muladd, MSA128BOpnd>; +class MADDV_H_DESC : MSA_3R_4R_DESC_BASE<"maddv.h", muladd, MSA128HOpnd>; +class MADDV_W_DESC : MSA_3R_4R_DESC_BASE<"maddv.w", muladd, MSA128WOpnd>; +class MADDV_D_DESC : MSA_3R_4R_DESC_BASE<"maddv.d", muladd, MSA128DOpnd>; + +class MAX_A_B_DESC : MSA_3R_DESC_BASE<"max_a.b", int_maxis_max_a_b, MSA128BOpnd>; +class MAX_A_H_DESC : MSA_3R_DESC_BASE<"max_a.h", int_maxis_max_a_h, MSA128HOpnd>; +class MAX_A_W_DESC : MSA_3R_DESC_BASE<"max_a.w", int_maxis_max_a_w, MSA128WOpnd>; +class MAX_A_D_DESC : MSA_3R_DESC_BASE<"max_a.d", int_maxis_max_a_d, MSA128DOpnd>; + +class MAX_S_B_DESC : MSA_3R_DESC_BASE<"max_s.b", MaxisVSMax, MSA128BOpnd>; +class MAX_S_H_DESC : MSA_3R_DESC_BASE<"max_s.h", MaxisVSMax, MSA128HOpnd>; +class MAX_S_W_DESC : MSA_3R_DESC_BASE<"max_s.w", MaxisVSMax, MSA128WOpnd>; +class MAX_S_D_DESC : MSA_3R_DESC_BASE<"max_s.d", MaxisVSMax, MSA128DOpnd>; + +class MAX_U_B_DESC : MSA_3R_DESC_BASE<"max_u.b", MaxisVUMax, MSA128BOpnd>; +class MAX_U_H_DESC : MSA_3R_DESC_BASE<"max_u.h", MaxisVUMax, MSA128HOpnd>; +class MAX_U_W_DESC : MSA_3R_DESC_BASE<"max_u.w", MaxisVUMax, MSA128WOpnd>; +class MAX_U_D_DESC : MSA_3R_DESC_BASE<"max_u.d", MaxisVUMax, MSA128DOpnd>; + +class MAXI_S_B_DESC : MSA_I5_DESC_BASE<"maxi_s.b", MaxisVSMax, vsplati8_simm5, + MSA128BOpnd>; +class MAXI_S_H_DESC : MSA_I5_DESC_BASE<"maxi_s.h", MaxisVSMax, vsplati16_simm5, + MSA128HOpnd>; +class MAXI_S_W_DESC : MSA_I5_DESC_BASE<"maxi_s.w", MaxisVSMax, vsplati32_simm5, + MSA128WOpnd>; +class MAXI_S_D_DESC : MSA_I5_DESC_BASE<"maxi_s.d", MaxisVSMax, vsplati64_simm5, + MSA128DOpnd>; + +class MAXI_U_B_DESC : MSA_I5_DESC_BASE<"maxi_u.b", MaxisVUMax, vsplati8_uimm5, + MSA128BOpnd>; +class MAXI_U_H_DESC : MSA_I5_DESC_BASE<"maxi_u.h", MaxisVUMax, vsplati16_uimm5, + MSA128HOpnd>; +class MAXI_U_W_DESC : MSA_I5_DESC_BASE<"maxi_u.w", MaxisVUMax, vsplati32_uimm5, + MSA128WOpnd>; +class MAXI_U_D_DESC : MSA_I5_DESC_BASE<"maxi_u.d", MaxisVUMax, vsplati64_uimm5, + MSA128DOpnd>; + +class MIN_A_B_DESC : MSA_3R_DESC_BASE<"min_a.b", int_maxis_min_a_b, MSA128BOpnd>; +class MIN_A_H_DESC : MSA_3R_DESC_BASE<"min_a.h", int_maxis_min_a_h, MSA128HOpnd>; +class MIN_A_W_DESC : MSA_3R_DESC_BASE<"min_a.w", int_maxis_min_a_w, MSA128WOpnd>; +class MIN_A_D_DESC : MSA_3R_DESC_BASE<"min_a.d", int_maxis_min_a_d, MSA128DOpnd>; + +class MIN_S_B_DESC : MSA_3R_DESC_BASE<"min_s.b", MaxisVSMin, MSA128BOpnd>; +class MIN_S_H_DESC : MSA_3R_DESC_BASE<"min_s.h", MaxisVSMin, MSA128HOpnd>; +class MIN_S_W_DESC : MSA_3R_DESC_BASE<"min_s.w", MaxisVSMin, MSA128WOpnd>; +class MIN_S_D_DESC : MSA_3R_DESC_BASE<"min_s.d", MaxisVSMin, MSA128DOpnd>; + +class MIN_U_B_DESC : MSA_3R_DESC_BASE<"min_u.b", MaxisVUMin, MSA128BOpnd>; +class MIN_U_H_DESC : MSA_3R_DESC_BASE<"min_u.h", MaxisVUMin, MSA128HOpnd>; +class MIN_U_W_DESC : MSA_3R_DESC_BASE<"min_u.w", MaxisVUMin, MSA128WOpnd>; +class MIN_U_D_DESC : MSA_3R_DESC_BASE<"min_u.d", MaxisVUMin, MSA128DOpnd>; + +class MINI_S_B_DESC : MSA_I5_DESC_BASE<"mini_s.b", MaxisVSMin, vsplati8_simm5, + MSA128BOpnd>; +class MINI_S_H_DESC : MSA_I5_DESC_BASE<"mini_s.h", MaxisVSMin, vsplati16_simm5, + MSA128HOpnd>; +class MINI_S_W_DESC : MSA_I5_DESC_BASE<"mini_s.w", MaxisVSMin, vsplati32_simm5, + MSA128WOpnd>; +class MINI_S_D_DESC : MSA_I5_DESC_BASE<"mini_s.d", MaxisVSMin, vsplati64_simm5, + MSA128DOpnd>; + +class MINI_U_B_DESC : MSA_I5_DESC_BASE<"mini_u.b", MaxisVUMin, vsplati8_uimm5, + MSA128BOpnd>; +class MINI_U_H_DESC : MSA_I5_DESC_BASE<"mini_u.h", MaxisVUMin, vsplati16_uimm5, + MSA128HOpnd>; +class MINI_U_W_DESC : MSA_I5_DESC_BASE<"mini_u.w", MaxisVUMin, vsplati32_uimm5, + MSA128WOpnd>; +class MINI_U_D_DESC : MSA_I5_DESC_BASE<"mini_u.d", MaxisVUMin, vsplati64_uimm5, + MSA128DOpnd>; + +class MOD_S_B_DESC : MSA_3R_DESC_BASE<"mod_s.b", srem, MSA128BOpnd>; +class MOD_S_H_DESC : MSA_3R_DESC_BASE<"mod_s.h", srem, MSA128HOpnd>; +class MOD_S_W_DESC : MSA_3R_DESC_BASE<"mod_s.w", srem, MSA128WOpnd>; +class MOD_S_D_DESC : MSA_3R_DESC_BASE<"mod_s.d", srem, MSA128DOpnd>; + +class MOD_U_B_DESC : MSA_3R_DESC_BASE<"mod_u.b", urem, MSA128BOpnd>; +class MOD_U_H_DESC : MSA_3R_DESC_BASE<"mod_u.h", urem, MSA128HOpnd>; +class MOD_U_W_DESC : MSA_3R_DESC_BASE<"mod_u.w", urem, MSA128WOpnd>; +class MOD_U_D_DESC : MSA_3R_DESC_BASE<"mod_u.d", urem, MSA128DOpnd>; + +class MOVE_V_DESC { + dag OutOperandList = (outs MSA128BOpnd:$wd); + dag InOperandList = (ins MSA128BOpnd:$ws); + string AsmString = "move.v\t$wd, $ws"; + list Pattern = []; + InstrItinClass Itinerary = NoItinerary; +} + +class MSUB_Q_H_DESC : MSA_3RF_4RF_DESC_BASE<"msub_q.h", int_maxis_msub_q_h, + MSA128HOpnd>; +class MSUB_Q_W_DESC : MSA_3RF_4RF_DESC_BASE<"msub_q.w", int_maxis_msub_q_w, + MSA128WOpnd>; + +class MSUBR_Q_H_DESC : MSA_3RF_4RF_DESC_BASE<"msubr_q.h", int_maxis_msubr_q_h, + MSA128HOpnd>; +class MSUBR_Q_W_DESC : MSA_3RF_4RF_DESC_BASE<"msubr_q.w", int_maxis_msubr_q_w, + MSA128WOpnd>; + +class MSUBV_B_DESC : MSA_3R_4R_DESC_BASE<"msubv.b", mulsub, MSA128BOpnd>; +class MSUBV_H_DESC : MSA_3R_4R_DESC_BASE<"msubv.h", mulsub, MSA128HOpnd>; +class MSUBV_W_DESC : MSA_3R_4R_DESC_BASE<"msubv.w", mulsub, MSA128WOpnd>; +class MSUBV_D_DESC : MSA_3R_4R_DESC_BASE<"msubv.d", mulsub, MSA128DOpnd>; + +class MUL_Q_H_DESC : MSA_3RF_DESC_BASE<"mul_q.h", int_maxis_mul_q_h, + MSA128HOpnd>; +class MUL_Q_W_DESC : MSA_3RF_DESC_BASE<"mul_q.w", int_maxis_mul_q_w, + MSA128WOpnd>; + +class MULR_Q_H_DESC : MSA_3RF_DESC_BASE<"mulr_q.h", int_maxis_mulr_q_h, + MSA128HOpnd>; +class MULR_Q_W_DESC : MSA_3RF_DESC_BASE<"mulr_q.w", int_maxis_mulr_q_w, + MSA128WOpnd>; + +class MULV_B_DESC : MSA_3R_DESC_BASE<"mulv.b", mul, MSA128BOpnd>; +class MULV_H_DESC : MSA_3R_DESC_BASE<"mulv.h", mul, MSA128HOpnd>; +class MULV_W_DESC : MSA_3R_DESC_BASE<"mulv.w", mul, MSA128WOpnd>; +class MULV_D_DESC : MSA_3R_DESC_BASE<"mulv.d", mul, MSA128DOpnd>; + +class NLOC_B_DESC : MSA_2R_DESC_BASE<"nloc.b", int_maxis_nloc_b, MSA128BOpnd>; +class NLOC_H_DESC : MSA_2R_DESC_BASE<"nloc.h", int_maxis_nloc_h, MSA128HOpnd>; +class NLOC_W_DESC : MSA_2R_DESC_BASE<"nloc.w", int_maxis_nloc_w, MSA128WOpnd>; +class NLOC_D_DESC : MSA_2R_DESC_BASE<"nloc.d", int_maxis_nloc_d, MSA128DOpnd>; + +class NLZC_B_DESC : MSA_2R_DESC_BASE<"nlzc.b", ctlz, MSA128BOpnd>; +class NLZC_H_DESC : MSA_2R_DESC_BASE<"nlzc.h", ctlz, MSA128HOpnd>; +class NLZC_W_DESC : MSA_2R_DESC_BASE<"nlzc.w", ctlz, MSA128WOpnd>; +class NLZC_D_DESC : MSA_2R_DESC_BASE<"nlzc.d", ctlz, MSA128DOpnd>; + +class NOR_V_DESC : MSA_VEC_DESC_BASE<"nor.v", MaxisVNOR, MSA128BOpnd>; +class NOR_V_H_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; +class NOR_V_W_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; +class NOR_V_D_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; + +class NORI_B_DESC : MSA_I8_DESC_BASE<"nori.b", MaxisVNOR, vsplati8_uimm8, + MSA128BOpnd>; + +class OR_V_DESC : MSA_VEC_DESC_BASE<"or.v", or, MSA128BOpnd>; +class OR_V_H_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; +class OR_V_W_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; +class OR_V_D_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; + +class ORI_B_DESC : MSA_I8_DESC_BASE<"ori.b", or, vsplati8_uimm8, MSA128BOpnd>; + +class PCKEV_B_DESC : MSA_3R_DESC_BASE<"pckev.b", MaxisPCKEV, MSA128BOpnd>; +class PCKEV_H_DESC : MSA_3R_DESC_BASE<"pckev.h", MaxisPCKEV, MSA128HOpnd>; +class PCKEV_W_DESC : MSA_3R_DESC_BASE<"pckev.w", MaxisPCKEV, MSA128WOpnd>; +class PCKEV_D_DESC : MSA_3R_DESC_BASE<"pckev.d", MaxisPCKEV, MSA128DOpnd>; + +class PCKOD_B_DESC : MSA_3R_DESC_BASE<"pckod.b", MaxisPCKOD, MSA128BOpnd>; +class PCKOD_H_DESC : MSA_3R_DESC_BASE<"pckod.h", MaxisPCKOD, MSA128HOpnd>; +class PCKOD_W_DESC : MSA_3R_DESC_BASE<"pckod.w", MaxisPCKOD, MSA128WOpnd>; +class PCKOD_D_DESC : MSA_3R_DESC_BASE<"pckod.d", MaxisPCKOD, MSA128DOpnd>; + +class PCNT_B_DESC : MSA_2R_DESC_BASE<"pcnt.b", ctpop, MSA128BOpnd>; +class PCNT_H_DESC : MSA_2R_DESC_BASE<"pcnt.h", ctpop, MSA128HOpnd>; +class PCNT_W_DESC : MSA_2R_DESC_BASE<"pcnt.w", ctpop, MSA128WOpnd>; +class PCNT_D_DESC : MSA_2R_DESC_BASE<"pcnt.d", ctpop, MSA128DOpnd>; + +class SAT_S_B_DESC : MSA_BIT_X_DESC_BASE<"sat_s.b", int_maxis_sat_s_b, uimm3, + immZExt3, MSA128BOpnd>; +class SAT_S_H_DESC : MSA_BIT_X_DESC_BASE<"sat_s.h", int_maxis_sat_s_h, uimm4, + immZExt4, MSA128HOpnd>; +class SAT_S_W_DESC : MSA_BIT_X_DESC_BASE<"sat_s.w", int_maxis_sat_s_w, uimm5, + immZExt5, MSA128WOpnd>; +class SAT_S_D_DESC : MSA_BIT_X_DESC_BASE<"sat_s.d", int_maxis_sat_s_d, uimm6, + immZExt6, MSA128DOpnd>; + +class SAT_U_B_DESC : MSA_BIT_X_DESC_BASE<"sat_u.b", int_maxis_sat_u_b, uimm3, + immZExt3, MSA128BOpnd>; +class SAT_U_H_DESC : MSA_BIT_X_DESC_BASE<"sat_u.h", int_maxis_sat_u_h, uimm4, + immZExt4, MSA128HOpnd>; +class SAT_U_W_DESC : MSA_BIT_X_DESC_BASE<"sat_u.w", int_maxis_sat_u_w, uimm5, + immZExt5, MSA128WOpnd>; +class SAT_U_D_DESC : MSA_BIT_X_DESC_BASE<"sat_u.d", int_maxis_sat_u_d, uimm6, + immZExt6, MSA128DOpnd>; + +class SHF_B_DESC : MSA_I8_SHF_DESC_BASE<"shf.b", MSA128BOpnd>; +class SHF_H_DESC : MSA_I8_SHF_DESC_BASE<"shf.h", MSA128HOpnd>; +class SHF_W_DESC : MSA_I8_SHF_DESC_BASE<"shf.w", MSA128WOpnd>; + +class SLD_B_DESC : MSA_3R_SLD_DESC_BASE<"sld.b", int_maxis_sld_b, MSA128BOpnd>; +class SLD_H_DESC : MSA_3R_SLD_DESC_BASE<"sld.h", int_maxis_sld_h, MSA128HOpnd>; +class SLD_W_DESC : MSA_3R_SLD_DESC_BASE<"sld.w", int_maxis_sld_w, MSA128WOpnd>; +class SLD_D_DESC : MSA_3R_SLD_DESC_BASE<"sld.d", int_maxis_sld_d, MSA128DOpnd>; + +class SLDI_B_DESC : MSA_ELM_SLD_DESC_BASE<"sldi.b", int_maxis_sldi_b, + MSA128BOpnd, MSA128BOpnd, uimm4, + immZExt4>; +class SLDI_H_DESC : MSA_ELM_SLD_DESC_BASE<"sldi.h", int_maxis_sldi_h, + MSA128HOpnd, MSA128HOpnd, uimm3, + immZExt3>; +class SLDI_W_DESC : MSA_ELM_SLD_DESC_BASE<"sldi.w", int_maxis_sldi_w, + MSA128WOpnd, MSA128WOpnd, uimm2, + immZExt2>; +class SLDI_D_DESC : MSA_ELM_SLD_DESC_BASE<"sldi.d", int_maxis_sldi_d, + MSA128DOpnd, MSA128DOpnd, uimm1, + immZExt1>; + +class SLL_B_DESC : MSA_3R_DESC_BASE<"sll.b", shl, MSA128BOpnd>; +class SLL_H_DESC : MSA_3R_DESC_BASE<"sll.h", shl, MSA128HOpnd>; +class SLL_W_DESC : MSA_3R_DESC_BASE<"sll.w", shl, MSA128WOpnd>; +class SLL_D_DESC : MSA_3R_DESC_BASE<"sll.d", shl, MSA128DOpnd>; + +class SLLI_B_DESC : MSA_BIT_SPLAT_DESC_BASE<"slli.b", shl, vsplati8_uimm3, + MSA128BOpnd>; +class SLLI_H_DESC : MSA_BIT_SPLAT_DESC_BASE<"slli.h", shl, vsplati16_uimm4, + MSA128HOpnd>; +class SLLI_W_DESC : MSA_BIT_SPLAT_DESC_BASE<"slli.w", shl, vsplati32_uimm5, + MSA128WOpnd>; +class SLLI_D_DESC : MSA_BIT_SPLAT_DESC_BASE<"slli.d", shl, vsplati64_uimm6, + MSA128DOpnd>; + +class SPLAT_B_DESC : MSA_3R_SPLAT_DESC_BASE<"splat.b", vsplati8_elt, + MSA128BOpnd>; +class SPLAT_H_DESC : MSA_3R_SPLAT_DESC_BASE<"splat.h", vsplati16_elt, + MSA128HOpnd>; +class SPLAT_W_DESC : MSA_3R_SPLAT_DESC_BASE<"splat.w", vsplati32_elt, + MSA128WOpnd>; +class SPLAT_D_DESC : MSA_3R_SPLAT_DESC_BASE<"splat.d", vsplati64_elt, + MSA128DOpnd>; + +class SPLATI_B_DESC : MSA_ELM_SPLAT_DESC_BASE<"splati.b", vsplati8_uimm4, + MSA128BOpnd>; +class SPLATI_H_DESC : MSA_ELM_SPLAT_DESC_BASE<"splati.h", vsplati16_uimm3, + MSA128HOpnd>; +class SPLATI_W_DESC : MSA_ELM_SPLAT_DESC_BASE<"splati.w", vsplati32_uimm2, + MSA128WOpnd>; +class SPLATI_D_DESC : MSA_ELM_SPLAT_DESC_BASE<"splati.d", vsplati64_uimm1, + MSA128DOpnd>; + +class SRA_B_DESC : MSA_3R_DESC_BASE<"sra.b", sra, MSA128BOpnd>; +class SRA_H_DESC : MSA_3R_DESC_BASE<"sra.h", sra, MSA128HOpnd>; +class SRA_W_DESC : MSA_3R_DESC_BASE<"sra.w", sra, MSA128WOpnd>; +class SRA_D_DESC : MSA_3R_DESC_BASE<"sra.d", sra, MSA128DOpnd>; + +class SRAI_B_DESC : MSA_BIT_SPLAT_DESC_BASE<"srai.b", sra, vsplati8_uimm3, + MSA128BOpnd>; +class SRAI_H_DESC : MSA_BIT_SPLAT_DESC_BASE<"srai.h", sra, vsplati16_uimm4, + MSA128HOpnd>; +class SRAI_W_DESC : MSA_BIT_SPLAT_DESC_BASE<"srai.w", sra, vsplati32_uimm5, + MSA128WOpnd>; +class SRAI_D_DESC : MSA_BIT_SPLAT_DESC_BASE<"srai.d", sra, vsplati64_uimm6, + MSA128DOpnd>; + +class SRAR_B_DESC : MSA_3R_DESC_BASE<"srar.b", int_maxis_srar_b, MSA128BOpnd>; +class SRAR_H_DESC : MSA_3R_DESC_BASE<"srar.h", int_maxis_srar_h, MSA128HOpnd>; +class SRAR_W_DESC : MSA_3R_DESC_BASE<"srar.w", int_maxis_srar_w, MSA128WOpnd>; +class SRAR_D_DESC : MSA_3R_DESC_BASE<"srar.d", int_maxis_srar_d, MSA128DOpnd>; + +class SRARI_B_DESC : MSA_BIT_X_DESC_BASE<"srari.b", int_maxis_srari_b, uimm3, + immZExt3, MSA128BOpnd>; +class SRARI_H_DESC : MSA_BIT_X_DESC_BASE<"srari.h", int_maxis_srari_h, uimm4, + immZExt4, MSA128HOpnd>; +class SRARI_W_DESC : MSA_BIT_X_DESC_BASE<"srari.w", int_maxis_srari_w, uimm5, + immZExt5, MSA128WOpnd>; +class SRARI_D_DESC : MSA_BIT_X_DESC_BASE<"srari.d", int_maxis_srari_d, uimm6, + immZExt6, MSA128DOpnd>; + +class SRL_B_DESC : MSA_3R_DESC_BASE<"srl.b", srl, MSA128BOpnd>; +class SRL_H_DESC : MSA_3R_DESC_BASE<"srl.h", srl, MSA128HOpnd>; +class SRL_W_DESC : MSA_3R_DESC_BASE<"srl.w", srl, MSA128WOpnd>; +class SRL_D_DESC : MSA_3R_DESC_BASE<"srl.d", srl, MSA128DOpnd>; + +class SRLI_B_DESC : MSA_BIT_SPLAT_DESC_BASE<"srli.b", srl, vsplati8_uimm3, + MSA128BOpnd>; +class SRLI_H_DESC : MSA_BIT_SPLAT_DESC_BASE<"srli.h", srl, vsplati16_uimm4, + MSA128HOpnd>; +class SRLI_W_DESC : MSA_BIT_SPLAT_DESC_BASE<"srli.w", srl, vsplati32_uimm5, + MSA128WOpnd>; +class SRLI_D_DESC : MSA_BIT_SPLAT_DESC_BASE<"srli.d", srl, vsplati64_uimm6, + MSA128DOpnd>; + +class SRLR_B_DESC : MSA_3R_DESC_BASE<"srlr.b", int_maxis_srlr_b, MSA128BOpnd>; +class SRLR_H_DESC : MSA_3R_DESC_BASE<"srlr.h", int_maxis_srlr_h, MSA128HOpnd>; +class SRLR_W_DESC : MSA_3R_DESC_BASE<"srlr.w", int_maxis_srlr_w, MSA128WOpnd>; +class SRLR_D_DESC : MSA_3R_DESC_BASE<"srlr.d", int_maxis_srlr_d, MSA128DOpnd>; + +class SRLRI_B_DESC : MSA_BIT_X_DESC_BASE<"srlri.b", int_maxis_srlri_b, uimm3, + immZExt3, MSA128BOpnd>; +class SRLRI_H_DESC : MSA_BIT_X_DESC_BASE<"srlri.h", int_maxis_srlri_h, uimm4, + immZExt4, MSA128HOpnd>; +class SRLRI_W_DESC : MSA_BIT_X_DESC_BASE<"srlri.w", int_maxis_srlri_w, uimm5, + immZExt5, MSA128WOpnd>; +class SRLRI_D_DESC : MSA_BIT_X_DESC_BASE<"srlri.d", int_maxis_srlri_d, uimm6, + immZExt6, MSA128DOpnd>; + +class ST_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins ROWD:$wd, MemOpnd:$addr); + string AsmString = !strconcat(instr_asm, "\t$wd, $addr"); + list Pattern = [(OpNode (TyNode ROWD:$wd), Addr:$addr)]; + InstrItinClass Itinerary = itin; + string DecoderMethod = "DecodeMSA128Mem"; +} + +class ST_B_DESC : ST_DESC_BASE<"st.b", store, v16i8, MSA128BOpnd, mem_simm10>; +class ST_H_DESC : ST_DESC_BASE<"st.h", store, v8i16, MSA128HOpnd, + mem_simm10_lsl1, addrimm10lsl1>; +class ST_W_DESC : ST_DESC_BASE<"st.w", store, v4i32, MSA128WOpnd, + mem_simm10_lsl2, addrimm10lsl2>; +class ST_D_DESC : ST_DESC_BASE<"st.d", store, v2i64, MSA128DOpnd, + mem_simm10_lsl3, addrimm10lsl3>; + +class SUBS_S_B_DESC : MSA_3R_DESC_BASE<"subs_s.b", int_maxis_subs_s_b, + MSA128BOpnd>; +class SUBS_S_H_DESC : MSA_3R_DESC_BASE<"subs_s.h", int_maxis_subs_s_h, + MSA128HOpnd>; +class SUBS_S_W_DESC : MSA_3R_DESC_BASE<"subs_s.w", int_maxis_subs_s_w, + MSA128WOpnd>; +class SUBS_S_D_DESC : MSA_3R_DESC_BASE<"subs_s.d", int_maxis_subs_s_d, + MSA128DOpnd>; + +class SUBS_U_B_DESC : MSA_3R_DESC_BASE<"subs_u.b", int_maxis_subs_u_b, + MSA128BOpnd>; +class SUBS_U_H_DESC : MSA_3R_DESC_BASE<"subs_u.h", int_maxis_subs_u_h, + MSA128HOpnd>; +class SUBS_U_W_DESC : MSA_3R_DESC_BASE<"subs_u.w", int_maxis_subs_u_w, + MSA128WOpnd>; +class SUBS_U_D_DESC : MSA_3R_DESC_BASE<"subs_u.d", int_maxis_subs_u_d, + MSA128DOpnd>; + +class SUBSUS_U_B_DESC : MSA_3R_DESC_BASE<"subsus_u.b", int_maxis_subsus_u_b, + MSA128BOpnd>; +class SUBSUS_U_H_DESC : MSA_3R_DESC_BASE<"subsus_u.h", int_maxis_subsus_u_h, + MSA128HOpnd>; +class SUBSUS_U_W_DESC : MSA_3R_DESC_BASE<"subsus_u.w", int_maxis_subsus_u_w, + MSA128WOpnd>; +class SUBSUS_U_D_DESC : MSA_3R_DESC_BASE<"subsus_u.d", int_maxis_subsus_u_d, + MSA128DOpnd>; + +class SUBSUU_S_B_DESC : MSA_3R_DESC_BASE<"subsuu_s.b", int_maxis_subsuu_s_b, + MSA128BOpnd>; +class SUBSUU_S_H_DESC : MSA_3R_DESC_BASE<"subsuu_s.h", int_maxis_subsuu_s_h, + MSA128HOpnd>; +class SUBSUU_S_W_DESC : MSA_3R_DESC_BASE<"subsuu_s.w", int_maxis_subsuu_s_w, + MSA128WOpnd>; +class SUBSUU_S_D_DESC : MSA_3R_DESC_BASE<"subsuu_s.d", int_maxis_subsuu_s_d, + MSA128DOpnd>; + +class SUBV_B_DESC : MSA_3R_DESC_BASE<"subv.b", sub, MSA128BOpnd>; +class SUBV_H_DESC : MSA_3R_DESC_BASE<"subv.h", sub, MSA128HOpnd>; +class SUBV_W_DESC : MSA_3R_DESC_BASE<"subv.w", sub, MSA128WOpnd>; +class SUBV_D_DESC : MSA_3R_DESC_BASE<"subv.d", sub, MSA128DOpnd>; + +class SUBVI_B_DESC : MSA_I5_DESC_BASE<"subvi.b", sub, vsplati8_uimm5, + MSA128BOpnd>; +class SUBVI_H_DESC : MSA_I5_DESC_BASE<"subvi.h", sub, vsplati16_uimm5, + MSA128HOpnd>; +class SUBVI_W_DESC : MSA_I5_DESC_BASE<"subvi.w", sub, vsplati32_uimm5, + MSA128WOpnd>; +class SUBVI_D_DESC : MSA_I5_DESC_BASE<"subvi.d", sub, vsplati64_uimm5, + MSA128DOpnd>; + +class VSHF_B_DESC : MSA_3R_VSHF_DESC_BASE<"vshf.b", MSA128BOpnd>; +class VSHF_H_DESC : MSA_3R_VSHF_DESC_BASE<"vshf.h", MSA128HOpnd>; +class VSHF_W_DESC : MSA_3R_VSHF_DESC_BASE<"vshf.w", MSA128WOpnd>; +class VSHF_D_DESC : MSA_3R_VSHF_DESC_BASE<"vshf.d", MSA128DOpnd>; + +class XOR_V_DESC : MSA_VEC_DESC_BASE<"xor.v", xor, MSA128BOpnd>; +class XOR_V_H_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; +class XOR_V_W_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; +class XOR_V_D_PSEUDO_DESC : MSA_VEC_PSEUDO_BASE; + +class XORI_B_DESC : MSA_I8_DESC_BASE<"xori.b", xor, vsplati8_uimm8, + MSA128BOpnd>; + +// Instruction defs. +def ADD_A_B : ADD_A_B_ENC, ADD_A_B_DESC; +def ADD_A_H : ADD_A_H_ENC, ADD_A_H_DESC; +def ADD_A_W : ADD_A_W_ENC, ADD_A_W_DESC; +def ADD_A_D : ADD_A_D_ENC, ADD_A_D_DESC; + +def ADDS_A_B : ADDS_A_B_ENC, ADDS_A_B_DESC; +def ADDS_A_H : ADDS_A_H_ENC, ADDS_A_H_DESC; +def ADDS_A_W : ADDS_A_W_ENC, ADDS_A_W_DESC; +def ADDS_A_D : ADDS_A_D_ENC, ADDS_A_D_DESC; + +def ADDS_S_B : ADDS_S_B_ENC, ADDS_S_B_DESC; +def ADDS_S_H : ADDS_S_H_ENC, ADDS_S_H_DESC; +def ADDS_S_W : ADDS_S_W_ENC, ADDS_S_W_DESC; +def ADDS_S_D : ADDS_S_D_ENC, ADDS_S_D_DESC; + +def ADDS_U_B : ADDS_U_B_ENC, ADDS_U_B_DESC; +def ADDS_U_H : ADDS_U_H_ENC, ADDS_U_H_DESC; +def ADDS_U_W : ADDS_U_W_ENC, ADDS_U_W_DESC; +def ADDS_U_D : ADDS_U_D_ENC, ADDS_U_D_DESC; + +def ADDV_B : ADDV_B_ENC, ADDV_B_DESC; +def ADDV_H : ADDV_H_ENC, ADDV_H_DESC; +def ADDV_W : ADDV_W_ENC, ADDV_W_DESC; +def ADDV_D : ADDV_D_ENC, ADDV_D_DESC; + +def ADDVI_B : ADDVI_B_ENC, ADDVI_B_DESC; +def ADDVI_H : ADDVI_H_ENC, ADDVI_H_DESC; +def ADDVI_W : ADDVI_W_ENC, ADDVI_W_DESC; +def ADDVI_D : ADDVI_D_ENC, ADDVI_D_DESC; + +def AND_V : AND_V_ENC, AND_V_DESC; +def AND_V_H_PSEUDO : AND_V_H_PSEUDO_DESC, + PseudoInstExpansion<(AND_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; +def AND_V_W_PSEUDO : AND_V_W_PSEUDO_DESC, + PseudoInstExpansion<(AND_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; +def AND_V_D_PSEUDO : AND_V_D_PSEUDO_DESC, + PseudoInstExpansion<(AND_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; + +def ANDI_B : ANDI_B_ENC, ANDI_B_DESC; + +def ASUB_S_B : ASUB_S_B_ENC, ASUB_S_B_DESC; +def ASUB_S_H : ASUB_S_H_ENC, ASUB_S_H_DESC; +def ASUB_S_W : ASUB_S_W_ENC, ASUB_S_W_DESC; +def ASUB_S_D : ASUB_S_D_ENC, ASUB_S_D_DESC; + +def ASUB_U_B : ASUB_U_B_ENC, ASUB_U_B_DESC; +def ASUB_U_H : ASUB_U_H_ENC, ASUB_U_H_DESC; +def ASUB_U_W : ASUB_U_W_ENC, ASUB_U_W_DESC; +def ASUB_U_D : ASUB_U_D_ENC, ASUB_U_D_DESC; + +def AVE_S_B : AVE_S_B_ENC, AVE_S_B_DESC; +def AVE_S_H : AVE_S_H_ENC, AVE_S_H_DESC; +def AVE_S_W : AVE_S_W_ENC, AVE_S_W_DESC; +def AVE_S_D : AVE_S_D_ENC, AVE_S_D_DESC; + +def AVE_U_B : AVE_U_B_ENC, AVE_U_B_DESC; +def AVE_U_H : AVE_U_H_ENC, AVE_U_H_DESC; +def AVE_U_W : AVE_U_W_ENC, AVE_U_W_DESC; +def AVE_U_D : AVE_U_D_ENC, AVE_U_D_DESC; + +def AVER_S_B : AVER_S_B_ENC, AVER_S_B_DESC; +def AVER_S_H : AVER_S_H_ENC, AVER_S_H_DESC; +def AVER_S_W : AVER_S_W_ENC, AVER_S_W_DESC; +def AVER_S_D : AVER_S_D_ENC, AVER_S_D_DESC; + +def AVER_U_B : AVER_U_B_ENC, AVER_U_B_DESC; +def AVER_U_H : AVER_U_H_ENC, AVER_U_H_DESC; +def AVER_U_W : AVER_U_W_ENC, AVER_U_W_DESC; +def AVER_U_D : AVER_U_D_ENC, AVER_U_D_DESC; + +def BCLR_B : BCLR_B_ENC, BCLR_B_DESC; +def BCLR_H : BCLR_H_ENC, BCLR_H_DESC; +def BCLR_W : BCLR_W_ENC, BCLR_W_DESC; +def BCLR_D : BCLR_D_ENC, BCLR_D_DESC; + +def BCLRI_B : BCLRI_B_ENC, BCLRI_B_DESC; +def BCLRI_H : BCLRI_H_ENC, BCLRI_H_DESC; +def BCLRI_W : BCLRI_W_ENC, BCLRI_W_DESC; +def BCLRI_D : BCLRI_D_ENC, BCLRI_D_DESC; + +def BINSL_B : BINSL_B_ENC, BINSL_B_DESC; +def BINSL_H : BINSL_H_ENC, BINSL_H_DESC; +def BINSL_W : BINSL_W_ENC, BINSL_W_DESC; +def BINSL_D : BINSL_D_ENC, BINSL_D_DESC; + +def BINSLI_B : BINSLI_B_ENC, BINSLI_B_DESC; +def BINSLI_H : BINSLI_H_ENC, BINSLI_H_DESC; +def BINSLI_W : BINSLI_W_ENC, BINSLI_W_DESC; +def BINSLI_D : BINSLI_D_ENC, BINSLI_D_DESC; + +def BINSR_B : BINSR_B_ENC, BINSR_B_DESC; +def BINSR_H : BINSR_H_ENC, BINSR_H_DESC; +def BINSR_W : BINSR_W_ENC, BINSR_W_DESC; +def BINSR_D : BINSR_D_ENC, BINSR_D_DESC; + +def BINSRI_B : BINSRI_B_ENC, BINSRI_B_DESC; +def BINSRI_H : BINSRI_H_ENC, BINSRI_H_DESC; +def BINSRI_W : BINSRI_W_ENC, BINSRI_W_DESC; +def BINSRI_D : BINSRI_D_ENC, BINSRI_D_DESC; + +def BMNZ_V : BMNZ_V_ENC, BMNZ_V_DESC; + +def BMNZI_B : BMNZI_B_ENC, BMNZI_B_DESC; + +def BMZ_V : BMZ_V_ENC, BMZ_V_DESC; + +def BMZI_B : BMZI_B_ENC, BMZI_B_DESC; + +def BNEG_B : BNEG_B_ENC, BNEG_B_DESC; +def BNEG_H : BNEG_H_ENC, BNEG_H_DESC; +def BNEG_W : BNEG_W_ENC, BNEG_W_DESC; +def BNEG_D : BNEG_D_ENC, BNEG_D_DESC; + +def BNEGI_B : BNEGI_B_ENC, BNEGI_B_DESC; +def BNEGI_H : BNEGI_H_ENC, BNEGI_H_DESC; +def BNEGI_W : BNEGI_W_ENC, BNEGI_W_DESC; +def BNEGI_D : BNEGI_D_ENC, BNEGI_D_DESC; + +def BNZ_B : BNZ_B_ENC, BNZ_B_DESC; +def BNZ_H : BNZ_H_ENC, BNZ_H_DESC; +def BNZ_W : BNZ_W_ENC, BNZ_W_DESC; +def BNZ_D : BNZ_D_ENC, BNZ_D_DESC; + +def BNZ_V : BNZ_V_ENC, BNZ_V_DESC; + +def BSEL_V : BSEL_V_ENC, BSEL_V_DESC; + +class MSA_BSEL_PSEUDO_BASE : + MSAPseudo<(outs RO:$wd), (ins RO:$wd_in, RO:$ws, RO:$wt), + [(set RO:$wd, (Ty (vselect RO:$wd_in, RO:$wt, RO:$ws)))]>, + // Note that vselect and BSEL_V treat the condition operand the opposite way + // from each other. + // (vselect cond, if_set, if_clear) + // (BSEL_V cond, if_clear, if_set) + PseudoInstExpansion<(BSEL_V MSA128BOpnd:$wd, MSA128BOpnd:$wd_in, + MSA128BOpnd:$ws, MSA128BOpnd:$wt)> { + let Constraints = "$wd_in = $wd"; +} + +def BSEL_H_PSEUDO : MSA_BSEL_PSEUDO_BASE; +def BSEL_W_PSEUDO : MSA_BSEL_PSEUDO_BASE; +def BSEL_D_PSEUDO : MSA_BSEL_PSEUDO_BASE; +def BSEL_FW_PSEUDO : MSA_BSEL_PSEUDO_BASE; +def BSEL_FD_PSEUDO : MSA_BSEL_PSEUDO_BASE; + +def BSELI_B : BSELI_B_ENC, BSELI_B_DESC; + +def BSET_B : BSET_B_ENC, BSET_B_DESC; +def BSET_H : BSET_H_ENC, BSET_H_DESC; +def BSET_W : BSET_W_ENC, BSET_W_DESC; +def BSET_D : BSET_D_ENC, BSET_D_DESC; + +def BSETI_B : BSETI_B_ENC, BSETI_B_DESC; +def BSETI_H : BSETI_H_ENC, BSETI_H_DESC; +def BSETI_W : BSETI_W_ENC, BSETI_W_DESC; +def BSETI_D : BSETI_D_ENC, BSETI_D_DESC; + +def BZ_B : BZ_B_ENC, BZ_B_DESC; +def BZ_H : BZ_H_ENC, BZ_H_DESC; +def BZ_W : BZ_W_ENC, BZ_W_DESC; +def BZ_D : BZ_D_ENC, BZ_D_DESC; + +def BZ_V : BZ_V_ENC, BZ_V_DESC; + +def CEQ_B : CEQ_B_ENC, CEQ_B_DESC; +def CEQ_H : CEQ_H_ENC, CEQ_H_DESC; +def CEQ_W : CEQ_W_ENC, CEQ_W_DESC; +def CEQ_D : CEQ_D_ENC, CEQ_D_DESC; + +def CEQI_B : CEQI_B_ENC, CEQI_B_DESC; +def CEQI_H : CEQI_H_ENC, CEQI_H_DESC; +def CEQI_W : CEQI_W_ENC, CEQI_W_DESC; +def CEQI_D : CEQI_D_ENC, CEQI_D_DESC; + +def CFCMSA : CFCMSA_ENC, CFCMSA_DESC; + +def CLE_S_B : CLE_S_B_ENC, CLE_S_B_DESC; +def CLE_S_H : CLE_S_H_ENC, CLE_S_H_DESC; +def CLE_S_W : CLE_S_W_ENC, CLE_S_W_DESC; +def CLE_S_D : CLE_S_D_ENC, CLE_S_D_DESC; + +def CLE_U_B : CLE_U_B_ENC, CLE_U_B_DESC; +def CLE_U_H : CLE_U_H_ENC, CLE_U_H_DESC; +def CLE_U_W : CLE_U_W_ENC, CLE_U_W_DESC; +def CLE_U_D : CLE_U_D_ENC, CLE_U_D_DESC; + +def CLEI_S_B : CLEI_S_B_ENC, CLEI_S_B_DESC; +def CLEI_S_H : CLEI_S_H_ENC, CLEI_S_H_DESC; +def CLEI_S_W : CLEI_S_W_ENC, CLEI_S_W_DESC; +def CLEI_S_D : CLEI_S_D_ENC, CLEI_S_D_DESC; + +def CLEI_U_B : CLEI_U_B_ENC, CLEI_U_B_DESC; +def CLEI_U_H : CLEI_U_H_ENC, CLEI_U_H_DESC; +def CLEI_U_W : CLEI_U_W_ENC, CLEI_U_W_DESC; +def CLEI_U_D : CLEI_U_D_ENC, CLEI_U_D_DESC; + +def CLT_S_B : CLT_S_B_ENC, CLT_S_B_DESC; +def CLT_S_H : CLT_S_H_ENC, CLT_S_H_DESC; +def CLT_S_W : CLT_S_W_ENC, CLT_S_W_DESC; +def CLT_S_D : CLT_S_D_ENC, CLT_S_D_DESC; + +def CLT_U_B : CLT_U_B_ENC, CLT_U_B_DESC; +def CLT_U_H : CLT_U_H_ENC, CLT_U_H_DESC; +def CLT_U_W : CLT_U_W_ENC, CLT_U_W_DESC; +def CLT_U_D : CLT_U_D_ENC, CLT_U_D_DESC; + +def CLTI_S_B : CLTI_S_B_ENC, CLTI_S_B_DESC; +def CLTI_S_H : CLTI_S_H_ENC, CLTI_S_H_DESC; +def CLTI_S_W : CLTI_S_W_ENC, CLTI_S_W_DESC; +def CLTI_S_D : CLTI_S_D_ENC, CLTI_S_D_DESC; + +def CLTI_U_B : CLTI_U_B_ENC, CLTI_U_B_DESC; +def CLTI_U_H : CLTI_U_H_ENC, CLTI_U_H_DESC; +def CLTI_U_W : CLTI_U_W_ENC, CLTI_U_W_DESC; +def CLTI_U_D : CLTI_U_D_ENC, CLTI_U_D_DESC; + +def COPY_S_B : COPY_S_B_ENC, COPY_S_B_DESC; +def COPY_S_H : COPY_S_H_ENC, COPY_S_H_DESC; +def COPY_S_W : COPY_S_W_ENC, COPY_S_W_DESC; +def COPY_S_D : COPY_S_D_ENC, COPY_S_D_DESC, ASE_MSA64; + +def COPY_U_B : COPY_U_B_ENC, COPY_U_B_DESC; +def COPY_U_H : COPY_U_H_ENC, COPY_U_H_DESC; +def COPY_U_W : COPY_U_W_ENC, COPY_U_W_DESC, ASE_MSA64; + +def COPY_FW_PSEUDO : COPY_FW_PSEUDO_DESC; +def COPY_FD_PSEUDO : COPY_FD_PSEUDO_DESC; + +def CTCMSA : CTCMSA_ENC, CTCMSA_DESC; + +def DIV_S_B : DIV_S_B_ENC, DIV_S_B_DESC; +def DIV_S_H : DIV_S_H_ENC, DIV_S_H_DESC; +def DIV_S_W : DIV_S_W_ENC, DIV_S_W_DESC; +def DIV_S_D : DIV_S_D_ENC, DIV_S_D_DESC; + +def DIV_U_B : DIV_U_B_ENC, DIV_U_B_DESC; +def DIV_U_H : DIV_U_H_ENC, DIV_U_H_DESC; +def DIV_U_W : DIV_U_W_ENC, DIV_U_W_DESC; +def DIV_U_D : DIV_U_D_ENC, DIV_U_D_DESC; + +def DOTP_S_H : DOTP_S_H_ENC, DOTP_S_H_DESC; +def DOTP_S_W : DOTP_S_W_ENC, DOTP_S_W_DESC; +def DOTP_S_D : DOTP_S_D_ENC, DOTP_S_D_DESC; + +def DOTP_U_H : DOTP_U_H_ENC, DOTP_U_H_DESC; +def DOTP_U_W : DOTP_U_W_ENC, DOTP_U_W_DESC; +def DOTP_U_D : DOTP_U_D_ENC, DOTP_U_D_DESC; + +def DPADD_S_H : DPADD_S_H_ENC, DPADD_S_H_DESC; +def DPADD_S_W : DPADD_S_W_ENC, DPADD_S_W_DESC; +def DPADD_S_D : DPADD_S_D_ENC, DPADD_S_D_DESC; + +def DPADD_U_H : DPADD_U_H_ENC, DPADD_U_H_DESC; +def DPADD_U_W : DPADD_U_W_ENC, DPADD_U_W_DESC; +def DPADD_U_D : DPADD_U_D_ENC, DPADD_U_D_DESC; + +def DPSUB_S_H : DPSUB_S_H_ENC, DPSUB_S_H_DESC; +def DPSUB_S_W : DPSUB_S_W_ENC, DPSUB_S_W_DESC; +def DPSUB_S_D : DPSUB_S_D_ENC, DPSUB_S_D_DESC; + +def DPSUB_U_H : DPSUB_U_H_ENC, DPSUB_U_H_DESC; +def DPSUB_U_W : DPSUB_U_W_ENC, DPSUB_U_W_DESC; +def DPSUB_U_D : DPSUB_U_D_ENC, DPSUB_U_D_DESC; + +def FADD_W : FADD_W_ENC, FADD_W_DESC; +def FADD_D : FADD_D_ENC, FADD_D_DESC; + +def FCAF_W : FCAF_W_ENC, FCAF_W_DESC; +def FCAF_D : FCAF_D_ENC, FCAF_D_DESC; + +def FCEQ_W : FCEQ_W_ENC, FCEQ_W_DESC; +def FCEQ_D : FCEQ_D_ENC, FCEQ_D_DESC; + +def FCLE_W : FCLE_W_ENC, FCLE_W_DESC; +def FCLE_D : FCLE_D_ENC, FCLE_D_DESC; + +def FCLT_W : FCLT_W_ENC, FCLT_W_DESC; +def FCLT_D : FCLT_D_ENC, FCLT_D_DESC; + +def FCLASS_W : FCLASS_W_ENC, FCLASS_W_DESC; +def FCLASS_D : FCLASS_D_ENC, FCLASS_D_DESC; + +def FCNE_W : FCNE_W_ENC, FCNE_W_DESC; +def FCNE_D : FCNE_D_ENC, FCNE_D_DESC; + +def FCOR_W : FCOR_W_ENC, FCOR_W_DESC; +def FCOR_D : FCOR_D_ENC, FCOR_D_DESC; + +def FCUEQ_W : FCUEQ_W_ENC, FCUEQ_W_DESC; +def FCUEQ_D : FCUEQ_D_ENC, FCUEQ_D_DESC; + +def FCULE_W : FCULE_W_ENC, FCULE_W_DESC; +def FCULE_D : FCULE_D_ENC, FCULE_D_DESC; + +def FCULT_W : FCULT_W_ENC, FCULT_W_DESC; +def FCULT_D : FCULT_D_ENC, FCULT_D_DESC; + +def FCUN_W : FCUN_W_ENC, FCUN_W_DESC; +def FCUN_D : FCUN_D_ENC, FCUN_D_DESC; + +def FCUNE_W : FCUNE_W_ENC, FCUNE_W_DESC; +def FCUNE_D : FCUNE_D_ENC, FCUNE_D_DESC; + +def FDIV_W : FDIV_W_ENC, FDIV_W_DESC; +def FDIV_D : FDIV_D_ENC, FDIV_D_DESC; + +def FEXDO_H : FEXDO_H_ENC, FEXDO_H_DESC; +def FEXDO_W : FEXDO_W_ENC, FEXDO_W_DESC; + +def FEXP2_W : FEXP2_W_ENC, FEXP2_W_DESC; +def FEXP2_D : FEXP2_D_ENC, FEXP2_D_DESC; +def FEXP2_W_1_PSEUDO : FEXP2_W_1_PSEUDO_DESC; +def FEXP2_D_1_PSEUDO : FEXP2_D_1_PSEUDO_DESC; + +def FEXUPL_W : FEXUPL_W_ENC, FEXUPL_W_DESC; +def FEXUPL_D : FEXUPL_D_ENC, FEXUPL_D_DESC; + +def FEXUPR_W : FEXUPR_W_ENC, FEXUPR_W_DESC; +def FEXUPR_D : FEXUPR_D_ENC, FEXUPR_D_DESC; + +def FFINT_S_W : FFINT_S_W_ENC, FFINT_S_W_DESC; +def FFINT_S_D : FFINT_S_D_ENC, FFINT_S_D_DESC; + +def FFINT_U_W : FFINT_U_W_ENC, FFINT_U_W_DESC; +def FFINT_U_D : FFINT_U_D_ENC, FFINT_U_D_DESC; + +def FFQL_W : FFQL_W_ENC, FFQL_W_DESC; +def FFQL_D : FFQL_D_ENC, FFQL_D_DESC; + +def FFQR_W : FFQR_W_ENC, FFQR_W_DESC; +def FFQR_D : FFQR_D_ENC, FFQR_D_DESC; + +def FILL_B : FILL_B_ENC, FILL_B_DESC; +def FILL_H : FILL_H_ENC, FILL_H_DESC; +def FILL_W : FILL_W_ENC, FILL_W_DESC; +def FILL_D : FILL_D_ENC, FILL_D_DESC, ASE_MSA64; +def FILL_FW_PSEUDO : FILL_FW_PSEUDO_DESC; +def FILL_FD_PSEUDO : FILL_FD_PSEUDO_DESC; + +def FLOG2_W : FLOG2_W_ENC, FLOG2_W_DESC; +def FLOG2_D : FLOG2_D_ENC, FLOG2_D_DESC; + +def FMADD_W : FMADD_W_ENC, FMADD_W_DESC; +def FMADD_D : FMADD_D_ENC, FMADD_D_DESC; + +def FMAX_W : FMAX_W_ENC, FMAX_W_DESC; +def FMAX_D : FMAX_D_ENC, FMAX_D_DESC; + +def FMAX_A_W : FMAX_A_W_ENC, FMAX_A_W_DESC; +def FMAX_A_D : FMAX_A_D_ENC, FMAX_A_D_DESC; + +def FMIN_W : FMIN_W_ENC, FMIN_W_DESC; +def FMIN_D : FMIN_D_ENC, FMIN_D_DESC; + +def FMIN_A_W : FMIN_A_W_ENC, FMIN_A_W_DESC; +def FMIN_A_D : FMIN_A_D_ENC, FMIN_A_D_DESC; + +def FMSUB_W : FMSUB_W_ENC, FMSUB_W_DESC; +def FMSUB_D : FMSUB_D_ENC, FMSUB_D_DESC; + +def FMUL_W : FMUL_W_ENC, FMUL_W_DESC; +def FMUL_D : FMUL_D_ENC, FMUL_D_DESC; + +def FRINT_W : FRINT_W_ENC, FRINT_W_DESC; +def FRINT_D : FRINT_D_ENC, FRINT_D_DESC; + +def FRCP_W : FRCP_W_ENC, FRCP_W_DESC; +def FRCP_D : FRCP_D_ENC, FRCP_D_DESC; + +def FRSQRT_W : FRSQRT_W_ENC, FRSQRT_W_DESC; +def FRSQRT_D : FRSQRT_D_ENC, FRSQRT_D_DESC; + +def FSAF_W : FSAF_W_ENC, FSAF_W_DESC; +def FSAF_D : FSAF_D_ENC, FSAF_D_DESC; + +def FSEQ_W : FSEQ_W_ENC, FSEQ_W_DESC; +def FSEQ_D : FSEQ_D_ENC, FSEQ_D_DESC; + +def FSLE_W : FSLE_W_ENC, FSLE_W_DESC; +def FSLE_D : FSLE_D_ENC, FSLE_D_DESC; + +def FSLT_W : FSLT_W_ENC, FSLT_W_DESC; +def FSLT_D : FSLT_D_ENC, FSLT_D_DESC; + +def FSNE_W : FSNE_W_ENC, FSNE_W_DESC; +def FSNE_D : FSNE_D_ENC, FSNE_D_DESC; + +def FSOR_W : FSOR_W_ENC, FSOR_W_DESC; +def FSOR_D : FSOR_D_ENC, FSOR_D_DESC; + +def FSQRT_W : FSQRT_W_ENC, FSQRT_W_DESC; +def FSQRT_D : FSQRT_D_ENC, FSQRT_D_DESC; + +def FSUB_W : FSUB_W_ENC, FSUB_W_DESC; +def FSUB_D : FSUB_D_ENC, FSUB_D_DESC; + +def FSUEQ_W : FSUEQ_W_ENC, FSUEQ_W_DESC; +def FSUEQ_D : FSUEQ_D_ENC, FSUEQ_D_DESC; + +def FSULE_W : FSULE_W_ENC, FSULE_W_DESC; +def FSULE_D : FSULE_D_ENC, FSULE_D_DESC; + +def FSULT_W : FSULT_W_ENC, FSULT_W_DESC; +def FSULT_D : FSULT_D_ENC, FSULT_D_DESC; + +def FSUN_W : FSUN_W_ENC, FSUN_W_DESC; +def FSUN_D : FSUN_D_ENC, FSUN_D_DESC; + +def FSUNE_W : FSUNE_W_ENC, FSUNE_W_DESC; +def FSUNE_D : FSUNE_D_ENC, FSUNE_D_DESC; + +def FTINT_S_W : FTINT_S_W_ENC, FTINT_S_W_DESC; +def FTINT_S_D : FTINT_S_D_ENC, FTINT_S_D_DESC; + +def FTINT_U_W : FTINT_U_W_ENC, FTINT_U_W_DESC; +def FTINT_U_D : FTINT_U_D_ENC, FTINT_U_D_DESC; + +def FTQ_H : FTQ_H_ENC, FTQ_H_DESC; +def FTQ_W : FTQ_W_ENC, FTQ_W_DESC; + +def FTRUNC_S_W : FTRUNC_S_W_ENC, FTRUNC_S_W_DESC; +def FTRUNC_S_D : FTRUNC_S_D_ENC, FTRUNC_S_D_DESC; + +def FTRUNC_U_W : FTRUNC_U_W_ENC, FTRUNC_U_W_DESC; +def FTRUNC_U_D : FTRUNC_U_D_ENC, FTRUNC_U_D_DESC; + +def HADD_S_H : HADD_S_H_ENC, HADD_S_H_DESC; +def HADD_S_W : HADD_S_W_ENC, HADD_S_W_DESC; +def HADD_S_D : HADD_S_D_ENC, HADD_S_D_DESC; + +def HADD_U_H : HADD_U_H_ENC, HADD_U_H_DESC; +def HADD_U_W : HADD_U_W_ENC, HADD_U_W_DESC; +def HADD_U_D : HADD_U_D_ENC, HADD_U_D_DESC; + +def HSUB_S_H : HSUB_S_H_ENC, HSUB_S_H_DESC; +def HSUB_S_W : HSUB_S_W_ENC, HSUB_S_W_DESC; +def HSUB_S_D : HSUB_S_D_ENC, HSUB_S_D_DESC; + +def HSUB_U_H : HSUB_U_H_ENC, HSUB_U_H_DESC; +def HSUB_U_W : HSUB_U_W_ENC, HSUB_U_W_DESC; +def HSUB_U_D : HSUB_U_D_ENC, HSUB_U_D_DESC; + +def ILVEV_B : ILVEV_B_ENC, ILVEV_B_DESC; +def ILVEV_H : ILVEV_H_ENC, ILVEV_H_DESC; +def ILVEV_W : ILVEV_W_ENC, ILVEV_W_DESC; +def ILVEV_D : ILVEV_D_ENC, ILVEV_D_DESC; + +def ILVL_B : ILVL_B_ENC, ILVL_B_DESC; +def ILVL_H : ILVL_H_ENC, ILVL_H_DESC; +def ILVL_W : ILVL_W_ENC, ILVL_W_DESC; +def ILVL_D : ILVL_D_ENC, ILVL_D_DESC; + +def ILVOD_B : ILVOD_B_ENC, ILVOD_B_DESC; +def ILVOD_H : ILVOD_H_ENC, ILVOD_H_DESC; +def ILVOD_W : ILVOD_W_ENC, ILVOD_W_DESC; +def ILVOD_D : ILVOD_D_ENC, ILVOD_D_DESC; + +def ILVR_B : ILVR_B_ENC, ILVR_B_DESC; +def ILVR_H : ILVR_H_ENC, ILVR_H_DESC; +def ILVR_W : ILVR_W_ENC, ILVR_W_DESC; +def ILVR_D : ILVR_D_ENC, ILVR_D_DESC; + +def INSERT_B : INSERT_B_ENC, INSERT_B_DESC; +def INSERT_H : INSERT_H_ENC, INSERT_H_DESC; +def INSERT_W : INSERT_W_ENC, INSERT_W_DESC; +def INSERT_D : INSERT_D_ENC, INSERT_D_DESC, ASE_MSA64; + +// INSERT_FW_PSEUDO defined after INSVE_W +// INSERT_FD_PSEUDO defined after INSVE_D + +// There is a fourth operand that is not present in the encoding. Use a +// custom decoder to get a chance to add it. +let DecoderMethod = "DecodeINSVE_DF" in { + def INSVE_B : INSVE_B_ENC, INSVE_B_DESC; + def INSVE_H : INSVE_H_ENC, INSVE_H_DESC; + def INSVE_W : INSVE_W_ENC, INSVE_W_DESC; + def INSVE_D : INSVE_D_ENC, INSVE_D_DESC; +} + +def INSERT_FW_PSEUDO : INSERT_FW_PSEUDO_DESC; +def INSERT_FD_PSEUDO : INSERT_FD_PSEUDO_DESC; + +def INSERT_B_VIDX_PSEUDO : INSERT_B_VIDX_PSEUDO_DESC; +def INSERT_H_VIDX_PSEUDO : INSERT_H_VIDX_PSEUDO_DESC; +def INSERT_W_VIDX_PSEUDO : INSERT_W_VIDX_PSEUDO_DESC; +def INSERT_D_VIDX_PSEUDO : INSERT_D_VIDX_PSEUDO_DESC; +def INSERT_FW_VIDX_PSEUDO : INSERT_FW_VIDX_PSEUDO_DESC; +def INSERT_FD_VIDX_PSEUDO : INSERT_FD_VIDX_PSEUDO_DESC; + +def INSERT_B_VIDX64_PSEUDO : INSERT_B_VIDX64_PSEUDO_DESC; +def INSERT_H_VIDX64_PSEUDO : INSERT_H_VIDX64_PSEUDO_DESC; +def INSERT_W_VIDX64_PSEUDO : INSERT_W_VIDX64_PSEUDO_DESC; +def INSERT_D_VIDX64_PSEUDO : INSERT_D_VIDX64_PSEUDO_DESC; +def INSERT_FW_VIDX64_PSEUDO : INSERT_FW_VIDX64_PSEUDO_DESC; +def INSERT_FD_VIDX64_PSEUDO : INSERT_FD_VIDX64_PSEUDO_DESC; + +def LD_B: LD_B_ENC, LD_B_DESC; +def LD_H: LD_H_ENC, LD_H_DESC; +def LD_W: LD_W_ENC, LD_W_DESC; +def LD_D: LD_D_ENC, LD_D_DESC; + +def LDI_B : LDI_B_ENC, LDI_B_DESC; +def LDI_H : LDI_H_ENC, LDI_H_DESC; +def LDI_W : LDI_W_ENC, LDI_W_DESC; +def LDI_D : LDI_D_ENC, LDI_D_DESC; + +def LSA : LSA_ENC, LSA_DESC; +def DLSA : DLSA_ENC, DLSA_DESC, ASE_MSA64; + +def MADD_Q_H : MADD_Q_H_ENC, MADD_Q_H_DESC; +def MADD_Q_W : MADD_Q_W_ENC, MADD_Q_W_DESC; + +def MADDR_Q_H : MADDR_Q_H_ENC, MADDR_Q_H_DESC; +def MADDR_Q_W : MADDR_Q_W_ENC, MADDR_Q_W_DESC; + +def MADDV_B : MADDV_B_ENC, MADDV_B_DESC; +def MADDV_H : MADDV_H_ENC, MADDV_H_DESC; +def MADDV_W : MADDV_W_ENC, MADDV_W_DESC; +def MADDV_D : MADDV_D_ENC, MADDV_D_DESC; + +def MAX_A_B : MAX_A_B_ENC, MAX_A_B_DESC; +def MAX_A_H : MAX_A_H_ENC, MAX_A_H_DESC; +def MAX_A_W : MAX_A_W_ENC, MAX_A_W_DESC; +def MAX_A_D : MAX_A_D_ENC, MAX_A_D_DESC; + +def MAX_S_B : MAX_S_B_ENC, MAX_S_B_DESC; +def MAX_S_H : MAX_S_H_ENC, MAX_S_H_DESC; +def MAX_S_W : MAX_S_W_ENC, MAX_S_W_DESC; +def MAX_S_D : MAX_S_D_ENC, MAX_S_D_DESC; + +def MAX_U_B : MAX_U_B_ENC, MAX_U_B_DESC; +def MAX_U_H : MAX_U_H_ENC, MAX_U_H_DESC; +def MAX_U_W : MAX_U_W_ENC, MAX_U_W_DESC; +def MAX_U_D : MAX_U_D_ENC, MAX_U_D_DESC; + +def MAXI_S_B : MAXI_S_B_ENC, MAXI_S_B_DESC; +def MAXI_S_H : MAXI_S_H_ENC, MAXI_S_H_DESC; +def MAXI_S_W : MAXI_S_W_ENC, MAXI_S_W_DESC; +def MAXI_S_D : MAXI_S_D_ENC, MAXI_S_D_DESC; + +def MAXI_U_B : MAXI_U_B_ENC, MAXI_U_B_DESC; +def MAXI_U_H : MAXI_U_H_ENC, MAXI_U_H_DESC; +def MAXI_U_W : MAXI_U_W_ENC, MAXI_U_W_DESC; +def MAXI_U_D : MAXI_U_D_ENC, MAXI_U_D_DESC; + +def MIN_A_B : MIN_A_B_ENC, MIN_A_B_DESC; +def MIN_A_H : MIN_A_H_ENC, MIN_A_H_DESC; +def MIN_A_W : MIN_A_W_ENC, MIN_A_W_DESC; +def MIN_A_D : MIN_A_D_ENC, MIN_A_D_DESC; + +def MIN_S_B : MIN_S_B_ENC, MIN_S_B_DESC; +def MIN_S_H : MIN_S_H_ENC, MIN_S_H_DESC; +def MIN_S_W : MIN_S_W_ENC, MIN_S_W_DESC; +def MIN_S_D : MIN_S_D_ENC, MIN_S_D_DESC; + +def MIN_U_B : MIN_U_B_ENC, MIN_U_B_DESC; +def MIN_U_H : MIN_U_H_ENC, MIN_U_H_DESC; +def MIN_U_W : MIN_U_W_ENC, MIN_U_W_DESC; +def MIN_U_D : MIN_U_D_ENC, MIN_U_D_DESC; + +def MINI_S_B : MINI_S_B_ENC, MINI_S_B_DESC; +def MINI_S_H : MINI_S_H_ENC, MINI_S_H_DESC; +def MINI_S_W : MINI_S_W_ENC, MINI_S_W_DESC; +def MINI_S_D : MINI_S_D_ENC, MINI_S_D_DESC; + +def MINI_U_B : MINI_U_B_ENC, MINI_U_B_DESC; +def MINI_U_H : MINI_U_H_ENC, MINI_U_H_DESC; +def MINI_U_W : MINI_U_W_ENC, MINI_U_W_DESC; +def MINI_U_D : MINI_U_D_ENC, MINI_U_D_DESC; + +def MOD_S_B : MOD_S_B_ENC, MOD_S_B_DESC; +def MOD_S_H : MOD_S_H_ENC, MOD_S_H_DESC; +def MOD_S_W : MOD_S_W_ENC, MOD_S_W_DESC; +def MOD_S_D : MOD_S_D_ENC, MOD_S_D_DESC; + +def MOD_U_B : MOD_U_B_ENC, MOD_U_B_DESC; +def MOD_U_H : MOD_U_H_ENC, MOD_U_H_DESC; +def MOD_U_W : MOD_U_W_ENC, MOD_U_W_DESC; +def MOD_U_D : MOD_U_D_ENC, MOD_U_D_DESC; + +def MOVE_V : MOVE_V_ENC, MOVE_V_DESC; + +def MSUB_Q_H : MSUB_Q_H_ENC, MSUB_Q_H_DESC; +def MSUB_Q_W : MSUB_Q_W_ENC, MSUB_Q_W_DESC; + +def MSUBR_Q_H : MSUBR_Q_H_ENC, MSUBR_Q_H_DESC; +def MSUBR_Q_W : MSUBR_Q_W_ENC, MSUBR_Q_W_DESC; + +def MSUBV_B : MSUBV_B_ENC, MSUBV_B_DESC; +def MSUBV_H : MSUBV_H_ENC, MSUBV_H_DESC; +def MSUBV_W : MSUBV_W_ENC, MSUBV_W_DESC; +def MSUBV_D : MSUBV_D_ENC, MSUBV_D_DESC; + +def MUL_Q_H : MUL_Q_H_ENC, MUL_Q_H_DESC; +def MUL_Q_W : MUL_Q_W_ENC, MUL_Q_W_DESC; + +def MULR_Q_H : MULR_Q_H_ENC, MULR_Q_H_DESC; +def MULR_Q_W : MULR_Q_W_ENC, MULR_Q_W_DESC; + +def MULV_B : MULV_B_ENC, MULV_B_DESC; +def MULV_H : MULV_H_ENC, MULV_H_DESC; +def MULV_W : MULV_W_ENC, MULV_W_DESC; +def MULV_D : MULV_D_ENC, MULV_D_DESC; + +def NLOC_B : NLOC_B_ENC, NLOC_B_DESC; +def NLOC_H : NLOC_H_ENC, NLOC_H_DESC; +def NLOC_W : NLOC_W_ENC, NLOC_W_DESC; +def NLOC_D : NLOC_D_ENC, NLOC_D_DESC; + +def NLZC_B : NLZC_B_ENC, NLZC_B_DESC; +def NLZC_H : NLZC_H_ENC, NLZC_H_DESC; +def NLZC_W : NLZC_W_ENC, NLZC_W_DESC; +def NLZC_D : NLZC_D_ENC, NLZC_D_DESC; + +def NOR_V : NOR_V_ENC, NOR_V_DESC; +def NOR_V_H_PSEUDO : NOR_V_H_PSEUDO_DESC, + PseudoInstExpansion<(NOR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; +def NOR_V_W_PSEUDO : NOR_V_W_PSEUDO_DESC, + PseudoInstExpansion<(NOR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; +def NOR_V_D_PSEUDO : NOR_V_D_PSEUDO_DESC, + PseudoInstExpansion<(NOR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; + +def NORI_B : NORI_B_ENC, NORI_B_DESC; + +def OR_V : OR_V_ENC, OR_V_DESC; +def OR_V_H_PSEUDO : OR_V_H_PSEUDO_DESC, + PseudoInstExpansion<(OR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; +def OR_V_W_PSEUDO : OR_V_W_PSEUDO_DESC, + PseudoInstExpansion<(OR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; +def OR_V_D_PSEUDO : OR_V_D_PSEUDO_DESC, + PseudoInstExpansion<(OR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; + +def ORI_B : ORI_B_ENC, ORI_B_DESC; + +def PCKEV_B : PCKEV_B_ENC, PCKEV_B_DESC; +def PCKEV_H : PCKEV_H_ENC, PCKEV_H_DESC; +def PCKEV_W : PCKEV_W_ENC, PCKEV_W_DESC; +def PCKEV_D : PCKEV_D_ENC, PCKEV_D_DESC; + +def PCKOD_B : PCKOD_B_ENC, PCKOD_B_DESC; +def PCKOD_H : PCKOD_H_ENC, PCKOD_H_DESC; +def PCKOD_W : PCKOD_W_ENC, PCKOD_W_DESC; +def PCKOD_D : PCKOD_D_ENC, PCKOD_D_DESC; + +def PCNT_B : PCNT_B_ENC, PCNT_B_DESC; +def PCNT_H : PCNT_H_ENC, PCNT_H_DESC; +def PCNT_W : PCNT_W_ENC, PCNT_W_DESC; +def PCNT_D : PCNT_D_ENC, PCNT_D_DESC; + +def SAT_S_B : SAT_S_B_ENC, SAT_S_B_DESC; +def SAT_S_H : SAT_S_H_ENC, SAT_S_H_DESC; +def SAT_S_W : SAT_S_W_ENC, SAT_S_W_DESC; +def SAT_S_D : SAT_S_D_ENC, SAT_S_D_DESC; + +def SAT_U_B : SAT_U_B_ENC, SAT_U_B_DESC; +def SAT_U_H : SAT_U_H_ENC, SAT_U_H_DESC; +def SAT_U_W : SAT_U_W_ENC, SAT_U_W_DESC; +def SAT_U_D : SAT_U_D_ENC, SAT_U_D_DESC; + +def SHF_B : SHF_B_ENC, SHF_B_DESC; +def SHF_H : SHF_H_ENC, SHF_H_DESC; +def SHF_W : SHF_W_ENC, SHF_W_DESC; + +def SLD_B : SLD_B_ENC, SLD_B_DESC; +def SLD_H : SLD_H_ENC, SLD_H_DESC; +def SLD_W : SLD_W_ENC, SLD_W_DESC; +def SLD_D : SLD_D_ENC, SLD_D_DESC; + +def SLDI_B : SLDI_B_ENC, SLDI_B_DESC; +def SLDI_H : SLDI_H_ENC, SLDI_H_DESC; +def SLDI_W : SLDI_W_ENC, SLDI_W_DESC; +def SLDI_D : SLDI_D_ENC, SLDI_D_DESC; + +def SLL_B : SLL_B_ENC, SLL_B_DESC; +def SLL_H : SLL_H_ENC, SLL_H_DESC; +def SLL_W : SLL_W_ENC, SLL_W_DESC; +def SLL_D : SLL_D_ENC, SLL_D_DESC; + +def SLLI_B : SLLI_B_ENC, SLLI_B_DESC; +def SLLI_H : SLLI_H_ENC, SLLI_H_DESC; +def SLLI_W : SLLI_W_ENC, SLLI_W_DESC; +def SLLI_D : SLLI_D_ENC, SLLI_D_DESC; + +def SPLAT_B : SPLAT_B_ENC, SPLAT_B_DESC; +def SPLAT_H : SPLAT_H_ENC, SPLAT_H_DESC; +def SPLAT_W : SPLAT_W_ENC, SPLAT_W_DESC; +def SPLAT_D : SPLAT_D_ENC, SPLAT_D_DESC; + +def SPLATI_B : SPLATI_B_ENC, SPLATI_B_DESC; +def SPLATI_H : SPLATI_H_ENC, SPLATI_H_DESC; +def SPLATI_W : SPLATI_W_ENC, SPLATI_W_DESC; +def SPLATI_D : SPLATI_D_ENC, SPLATI_D_DESC; + +def SRA_B : SRA_B_ENC, SRA_B_DESC; +def SRA_H : SRA_H_ENC, SRA_H_DESC; +def SRA_W : SRA_W_ENC, SRA_W_DESC; +def SRA_D : SRA_D_ENC, SRA_D_DESC; + +def SRAI_B : SRAI_B_ENC, SRAI_B_DESC; +def SRAI_H : SRAI_H_ENC, SRAI_H_DESC; +def SRAI_W : SRAI_W_ENC, SRAI_W_DESC; +def SRAI_D : SRAI_D_ENC, SRAI_D_DESC; + +def SRAR_B : SRAR_B_ENC, SRAR_B_DESC; +def SRAR_H : SRAR_H_ENC, SRAR_H_DESC; +def SRAR_W : SRAR_W_ENC, SRAR_W_DESC; +def SRAR_D : SRAR_D_ENC, SRAR_D_DESC; + +def SRARI_B : SRARI_B_ENC, SRARI_B_DESC; +def SRARI_H : SRARI_H_ENC, SRARI_H_DESC; +def SRARI_W : SRARI_W_ENC, SRARI_W_DESC; +def SRARI_D : SRARI_D_ENC, SRARI_D_DESC; + +def SRL_B : SRL_B_ENC, SRL_B_DESC; +def SRL_H : SRL_H_ENC, SRL_H_DESC; +def SRL_W : SRL_W_ENC, SRL_W_DESC; +def SRL_D : SRL_D_ENC, SRL_D_DESC; + +def SRLI_B : SRLI_B_ENC, SRLI_B_DESC; +def SRLI_H : SRLI_H_ENC, SRLI_H_DESC; +def SRLI_W : SRLI_W_ENC, SRLI_W_DESC; +def SRLI_D : SRLI_D_ENC, SRLI_D_DESC; + +def SRLR_B : SRLR_B_ENC, SRLR_B_DESC; +def SRLR_H : SRLR_H_ENC, SRLR_H_DESC; +def SRLR_W : SRLR_W_ENC, SRLR_W_DESC; +def SRLR_D : SRLR_D_ENC, SRLR_D_DESC; + +def SRLRI_B : SRLRI_B_ENC, SRLRI_B_DESC; +def SRLRI_H : SRLRI_H_ENC, SRLRI_H_DESC; +def SRLRI_W : SRLRI_W_ENC, SRLRI_W_DESC; +def SRLRI_D : SRLRI_D_ENC, SRLRI_D_DESC; + +def ST_B: ST_B_ENC, ST_B_DESC; +def ST_H: ST_H_ENC, ST_H_DESC; +def ST_W: ST_W_ENC, ST_W_DESC; +def ST_D: ST_D_ENC, ST_D_DESC; + +def SUBS_S_B : SUBS_S_B_ENC, SUBS_S_B_DESC; +def SUBS_S_H : SUBS_S_H_ENC, SUBS_S_H_DESC; +def SUBS_S_W : SUBS_S_W_ENC, SUBS_S_W_DESC; +def SUBS_S_D : SUBS_S_D_ENC, SUBS_S_D_DESC; + +def SUBS_U_B : SUBS_U_B_ENC, SUBS_U_B_DESC; +def SUBS_U_H : SUBS_U_H_ENC, SUBS_U_H_DESC; +def SUBS_U_W : SUBS_U_W_ENC, SUBS_U_W_DESC; +def SUBS_U_D : SUBS_U_D_ENC, SUBS_U_D_DESC; + +def SUBSUS_U_B : SUBSUS_U_B_ENC, SUBSUS_U_B_DESC; +def SUBSUS_U_H : SUBSUS_U_H_ENC, SUBSUS_U_H_DESC; +def SUBSUS_U_W : SUBSUS_U_W_ENC, SUBSUS_U_W_DESC; +def SUBSUS_U_D : SUBSUS_U_D_ENC, SUBSUS_U_D_DESC; + +def SUBSUU_S_B : SUBSUU_S_B_ENC, SUBSUU_S_B_DESC; +def SUBSUU_S_H : SUBSUU_S_H_ENC, SUBSUU_S_H_DESC; +def SUBSUU_S_W : SUBSUU_S_W_ENC, SUBSUU_S_W_DESC; +def SUBSUU_S_D : SUBSUU_S_D_ENC, SUBSUU_S_D_DESC; + +def SUBV_B : SUBV_B_ENC, SUBV_B_DESC; +def SUBV_H : SUBV_H_ENC, SUBV_H_DESC; +def SUBV_W : SUBV_W_ENC, SUBV_W_DESC; +def SUBV_D : SUBV_D_ENC, SUBV_D_DESC; + +def SUBVI_B : SUBVI_B_ENC, SUBVI_B_DESC; +def SUBVI_H : SUBVI_H_ENC, SUBVI_H_DESC; +def SUBVI_W : SUBVI_W_ENC, SUBVI_W_DESC; +def SUBVI_D : SUBVI_D_ENC, SUBVI_D_DESC; + +def VSHF_B : VSHF_B_ENC, VSHF_B_DESC; +def VSHF_H : VSHF_H_ENC, VSHF_H_DESC; +def VSHF_W : VSHF_W_ENC, VSHF_W_DESC; +def VSHF_D : VSHF_D_ENC, VSHF_D_DESC; + +def XOR_V : XOR_V_ENC, XOR_V_DESC; +def XOR_V_H_PSEUDO : XOR_V_H_PSEUDO_DESC, + PseudoInstExpansion<(XOR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; +def XOR_V_W_PSEUDO : XOR_V_W_PSEUDO_DESC, + PseudoInstExpansion<(XOR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; +def XOR_V_D_PSEUDO : XOR_V_D_PSEUDO_DESC, + PseudoInstExpansion<(XOR_V MSA128BOpnd:$wd, + MSA128BOpnd:$ws, + MSA128BOpnd:$wt)>; + +def XORI_B : XORI_B_ENC, XORI_B_DESC; + +// Patterns. +class MSAPat pred = [HasMSA]> : + Pat, Requires; + +def : MSAPat<(extractelt (v4i32 MSA128W:$ws), immZExt4:$idx), + (COPY_S_W MSA128W:$ws, immZExt4:$idx)>; + +def : MSAPat<(v8f16 (load addrimm10lsl1:$addr)), (LD_H addrimm10lsl1:$addr)>; +def : MSAPat<(v4f32 (load addrimm10lsl2:$addr)), (LD_W addrimm10lsl2:$addr)>; +def : MSAPat<(v2f64 (load addrimm10lsl3:$addr)), (LD_D addrimm10lsl3:$addr)>; + +def ST_FH : MSAPat<(store (v8f16 MSA128H:$ws), addrimm10lsl1:$addr), + (ST_H MSA128H:$ws, addrimm10lsl1:$addr)>; +def ST_FW : MSAPat<(store (v4f32 MSA128W:$ws), addrimm10lsl2:$addr), + (ST_W MSA128W:$ws, addrimm10lsl2:$addr)>; +def ST_FD : MSAPat<(store (v2f64 MSA128D:$ws), addrimm10lsl3:$addr), + (ST_D MSA128D:$ws, addrimm10lsl3:$addr)>; + +class MSA_FABS_PSEUDO_DESC_BASE : + MSAPseudo<(outs ROWD:$wd), + (ins ROWS:$ws), + [(set ROWD:$wd, (fabs ROWS:$ws))]> { + InstrItinClass Itinerary = itin; +} +def FABS_W : MSA_FABS_PSEUDO_DESC_BASE, + PseudoInstExpansion<(FMAX_A_W MSA128WOpnd:$wd, MSA128WOpnd:$ws, + MSA128WOpnd:$ws)>; +def FABS_D : MSA_FABS_PSEUDO_DESC_BASE, + PseudoInstExpansion<(FMAX_A_D MSA128DOpnd:$wd, MSA128DOpnd:$ws, + MSA128DOpnd:$ws)>; + +class MSABitconvertPat preds = [HasMSA]> : + MSAPat<(DstVT (bitconvert SrcVT:$src)), + (COPY_TO_REGCLASS SrcVT:$src, DstRC), preds>; + +// These are endian-independent because the element size doesnt change +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; + +// Little endian bitcasts are always no-ops +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; + +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; + +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; + +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; + +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; + +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; +def : MSABitconvertPat; + +// Big endian bitcasts expand to shuffle instructions. +// This is because bitcast is defined to be a store/load sequence and the +// vector store/load instructions are mixed-endian with respect to the vector +// as a whole (little endian with respect to element order, but big endian +// elements). + +class MSABitconvertReverseQuartersPat : + MSAPat<(DstVT (bitconvert SrcVT:$src)), + (COPY_TO_REGCLASS (Insn (COPY_TO_REGCLASS SrcVT:$src, ViaRC), 27), + DstRC), + [HasMSA, IsBE]>; + +class MSABitconvertReverseHalvesPat : + MSAPat<(DstVT (bitconvert SrcVT:$src)), + (COPY_TO_REGCLASS (Insn (COPY_TO_REGCLASS SrcVT:$src, ViaRC), 177), + DstRC), + [HasMSA, IsBE]>; + +class MSABitconvertReverseBInHPat : + MSABitconvertReverseHalvesPat; + +class MSABitconvertReverseBInWPat : + MSABitconvertReverseQuartersPat; + +class MSABitconvertReverseBInDPat : + MSAPat<(DstVT (bitconvert SrcVT:$src)), + (COPY_TO_REGCLASS + (SHF_W + (COPY_TO_REGCLASS + (SHF_B (COPY_TO_REGCLASS SrcVT:$src, MSA128B), 27), + MSA128W), 177), + DstRC), + [HasMSA, IsBE]>; + +class MSABitconvertReverseHInWPat : + MSABitconvertReverseHalvesPat; + +class MSABitconvertReverseHInDPat : + MSABitconvertReverseQuartersPat; + +class MSABitconvertReverseWInDPat : + MSABitconvertReverseHalvesPat; + +def : MSABitconvertReverseBInHPat; +def : MSABitconvertReverseBInHPat; +def : MSABitconvertReverseBInWPat; +def : MSABitconvertReverseBInWPat; +def : MSABitconvertReverseBInDPat; +def : MSABitconvertReverseBInDPat; + +def : MSABitconvertReverseBInHPat; +def : MSABitconvertReverseHInWPat; +def : MSABitconvertReverseHInWPat; +def : MSABitconvertReverseHInDPat; +def : MSABitconvertReverseHInDPat; + +def : MSABitconvertReverseBInHPat; +def : MSABitconvertReverseHInWPat; +def : MSABitconvertReverseHInWPat; +def : MSABitconvertReverseHInDPat; +def : MSABitconvertReverseHInDPat; + +def : MSABitconvertReverseBInWPat; +def : MSABitconvertReverseHInWPat; +def : MSABitconvertReverseHInWPat; +def : MSABitconvertReverseWInDPat; +def : MSABitconvertReverseWInDPat; + +def : MSABitconvertReverseBInWPat; +def : MSABitconvertReverseHInWPat; +def : MSABitconvertReverseHInWPat; +def : MSABitconvertReverseWInDPat; +def : MSABitconvertReverseWInDPat; + +def : MSABitconvertReverseBInDPat; +def : MSABitconvertReverseHInDPat; +def : MSABitconvertReverseHInDPat; +def : MSABitconvertReverseWInDPat; +def : MSABitconvertReverseWInDPat; + +def : MSABitconvertReverseBInDPat; +def : MSABitconvertReverseHInDPat; +def : MSABitconvertReverseHInDPat; +def : MSABitconvertReverseWInDPat; +def : MSABitconvertReverseWInDPat; + +// Pseudos used to implement BNZ.df, and BZ.df + +class MSA_CBRANCH_PSEUDO_DESC_BASE : + MaxisPseudo<(outs GPR32:$dst), + (ins RCWS:$ws), + [(set GPR32:$dst, (OpNode (TyNode RCWS:$ws)))]> { + bit usesCustomInserter = 1; +} + +def SNZ_B_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; +def SNZ_H_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; +def SNZ_W_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; +def SNZ_D_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; +def SNZ_V_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; + +def SZ_B_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; +def SZ_H_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; +def SZ_W_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; +def SZ_D_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; +def SZ_V_PSEUDO : MSA_CBRANCH_PSEUDO_DESC_BASE; + +// Pseudoes used to implement transparent fp16 support. + +let Predicates = [HasMSA] in { + def ST_F16 : MaxisPseudo<(outs), (ins MSA128F16:$ws, mem_simm10:$addr), + [(store (f16 MSA128F16:$ws), (addrimm10:$addr))]> { + let usesCustomInserter = 1; + } + + def LD_F16 : MaxisPseudo<(outs MSA128F16:$ws), (ins mem_simm10:$addr), + [(set MSA128F16:$ws, (f16 (load addrimm10:$addr)))]> { + let usesCustomInserter = 1; + } + + def MSA_FP_EXTEND_W_PSEUDO : MaxisPseudo<(outs FGR32Opnd:$fd), + (ins MSA128F16:$ws), + [(set FGR32Opnd:$fd, + (f32 (fpextend MSA128F16:$ws)))]> { + let usesCustomInserter = 1; + } + + def MSA_FP_ROUND_W_PSEUDO : MaxisPseudo<(outs MSA128F16:$wd), + (ins FGR32Opnd:$fs), + [(set MSA128F16:$wd, + (f16 (fpround FGR32Opnd:$fs)))]> { + let usesCustomInserter = 1; + } + + def MSA_FP_EXTEND_D_PSEUDO : MaxisPseudo<(outs FGR64Opnd:$fd), + (ins MSA128F16:$ws), + [(set FGR64Opnd:$fd, + (f64 (fpextend MSA128F16:$ws)))]> { + let usesCustomInserter = 1; + } + + def MSA_FP_ROUND_D_PSEUDO : MaxisPseudo<(outs MSA128F16:$wd), + (ins FGR64Opnd:$fs), + [(set MSA128F16:$wd, + (f16 (fpround FGR64Opnd:$fs)))]> { + let usesCustomInserter = 1; + } + + def : MaxisPat<(MaxisTruncIntFP MSA128F16:$ws), + (TRUNC_W_D64 (MSA_FP_EXTEND_D_PSEUDO MSA128F16:$ws))>; + + def : MaxisPat<(MaxisFPCmp MSA128F16:$ws, MSA128F16:$wt, imm:$cond), + (FCMP_S32 (MSA_FP_EXTEND_W_PSEUDO MSA128F16:$ws), + (MSA_FP_EXTEND_W_PSEUDO MSA128F16:$wt), imm:$cond)>, + ISA_MAXIS1_NOT_32R6_64R6; +} + +def vsplati64_imm_eq_63 : PatLeaf<(bitconvert (v4i32 (build_vector))), [{ + APInt Imm; + SDNode *BV = N->getOperand(0).getNode(); + EVT EltTy = N->getValueType(0).getVectorElementType(); + + return selectVSplat(BV, Imm, EltTy.getSizeInBits()) && + Imm.getBitWidth() == EltTy.getSizeInBits() && Imm == 63; +}]>; + +def immi32Cst7 : ImmLeaf(Imm) && Imm == 7;}]>; +def immi32Cst15 : ImmLeaf(Imm) && Imm == 15;}]>; +def immi32Cst31 : ImmLeaf(Imm) && Imm == 31;}]>; + +def vsplati8imm7 : PatFrag<(ops node:$wt), + (and node:$wt, (vsplati8 immi32Cst7))>; +def vsplati16imm15 : PatFrag<(ops node:$wt), + (and node:$wt, (vsplati16 immi32Cst15))>; +def vsplati32imm31 : PatFrag<(ops node:$wt), + (and node:$wt, (vsplati32 immi32Cst31))>; +def vsplati64imm63 : PatFrag<(ops node:$wt), + (and node:$wt, vsplati64_imm_eq_63)>; + +class MSAShiftPat : + MSAPat<(VT (Node VT:$ws, (VT (and VT:$wt, Vec)))), + (VT (Insn VT:$ws, VT:$wt))>; + +class MSABitPat : + MSAPat<(VT (Node VT:$ws, (shl vsplat_imm_eq_1, (Frag VT:$wt)))), + (VT (Insn VT:$ws, VT:$wt))>; + +multiclass MSAShiftPats { + def : MSAShiftPat(Insn#_B), + (vsplati8 immi32Cst7)>; + def : MSAShiftPat(Insn#_H), + (vsplati16 immi32Cst15)>; + def : MSAShiftPat(Insn#_W), + (vsplati32 immi32Cst31)>; + def : MSAPat<(v2i64 (Node v2i64:$ws, (v2i64 (and v2i64:$wt, + vsplati64_imm_eq_63)))), + (v2i64 (!cast(Insn#_D) v2i64:$ws, v2i64:$wt))>; +} + +multiclass MSABitPats { + def : MSABitPat(Insn#_B), vsplati8imm7>; + def : MSABitPat(Insn#_H), vsplati16imm15>; + def : MSABitPat(Insn#_W), vsplati32imm31>; + def : MSAPat<(Node v2i64:$ws, (shl (v2i64 vsplati64_imm_eq_1), + (vsplati64imm63 v2i64:$wt))), + (v2i64 (!cast(Insn#_D) v2i64:$ws, v2i64:$wt))>; +} + +defm : MSAShiftPats; +defm : MSAShiftPats; +defm : MSAShiftPats; +defm : MSABitPats; +defm : MSABitPats; + +def : MSAPat<(and v16i8:$ws, (xor (shl vsplat_imm_eq_1, + (vsplati8imm7 v16i8:$wt)), + immAllOnesV)), + (v16i8 (BCLR_B v16i8:$ws, v16i8:$wt))>; +def : MSAPat<(and v8i16:$ws, (xor (shl vsplat_imm_eq_1, + (vsplati16imm15 v8i16:$wt)), + immAllOnesV)), + (v8i16 (BCLR_H v8i16:$ws, v8i16:$wt))>; +def : MSAPat<(and v4i32:$ws, (xor (shl vsplat_imm_eq_1, + (vsplati32imm31 v4i32:$wt)), + immAllOnesV)), + (v4i32 (BCLR_W v4i32:$ws, v4i32:$wt))>; +def : MSAPat<(and v2i64:$ws, (xor (shl (v2i64 vsplati64_imm_eq_1), + (vsplati64imm63 v2i64:$wt)), + (bitconvert (v4i32 immAllOnesV)))), + (v2i64 (BCLR_D v2i64:$ws, v2i64:$wt))>; + +// Vector extraction with fixed index. +// +// Extracting 32-bit values on MSA32 should always use COPY_S_W rather than +// COPY_U_W, even for the zero-extended case. This is because our forward +// compatibility strategy is to consider registers to be infinitely +// sign-extended so that a MAXIS64 can execute MAXIS32 code without getting +// different register values. +def : MSAPat<(vextract_zext_i32 (v4i32 MSA128W:$ws), immZExt2Ptr:$idx), + (COPY_S_W MSA128W:$ws, immZExt2:$idx)>, ASE_MSA_NOT_MSA64; +def : MSAPat<(vextract_zext_i32 (v4f32 MSA128W:$ws), immZExt2Ptr:$idx), + (COPY_S_W MSA128W:$ws, immZExt2:$idx)>, ASE_MSA_NOT_MSA64; + +// Extracting 64-bit values on MSA64 should always use COPY_S_D rather than +// COPY_U_D, even for the zero-extended case. This is because our forward +// compatibility strategy is to consider registers to be infinitely +// sign-extended so that a hypothetical MAXIS128 would be able to execute MAXIS64 +// code without getting different register values. +def : MSAPat<(vextract_zext_i64 (v2i64 MSA128D:$ws), immZExt1Ptr:$idx), + (COPY_S_D MSA128D:$ws, immZExt1:$idx)>, ASE_MSA64; +def : MSAPat<(vextract_zext_i64 (v2f64 MSA128D:$ws), immZExt1Ptr:$idx), + (COPY_S_D MSA128D:$ws, immZExt1:$idx)>, ASE_MSA64; + +// Vector extraction with variable index +def : MSAPat<(i32 (vextract_sext_i8 v16i8:$ws, i32:$idx)), + (SRA (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG (SPLAT_B v16i8:$ws, + i32:$idx), + sub_lo)), + GPR32), (i32 24))>; +def : MSAPat<(i32 (vextract_sext_i16 v8i16:$ws, i32:$idx)), + (SRA (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG (SPLAT_H v8i16:$ws, + i32:$idx), + sub_lo)), + GPR32), (i32 16))>; +def : MSAPat<(i32 (vextract_sext_i32 v4i32:$ws, i32:$idx)), + (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG (SPLAT_W v4i32:$ws, + i32:$idx), + sub_lo)), + GPR32)>; +def : MSAPat<(i64 (vextract_sext_i64 v2i64:$ws, i32:$idx)), + (COPY_TO_REGCLASS (i64 (EXTRACT_SUBREG (SPLAT_D v2i64:$ws, + i32:$idx), + sub_64)), + GPR64), [HasMSA, IsGP64bit]>; + +def : MSAPat<(i32 (vextract_zext_i8 v16i8:$ws, i32:$idx)), + (SRL (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG (SPLAT_B v16i8:$ws, + i32:$idx), + sub_lo)), + GPR32), (i32 24))>; +def : MSAPat<(i32 (vextract_zext_i16 v8i16:$ws, i32:$idx)), + (SRL (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG (SPLAT_H v8i16:$ws, + i32:$idx), + sub_lo)), + GPR32), (i32 16))>; +def : MSAPat<(i32 (vextract_zext_i32 v4i32:$ws, i32:$idx)), + (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG (SPLAT_W v4i32:$ws, + i32:$idx), + sub_lo)), + GPR32)>; +def : MSAPat<(i64 (vextract_zext_i64 v2i64:$ws, i32:$idx)), + (COPY_TO_REGCLASS (i64 (EXTRACT_SUBREG (SPLAT_D v2i64:$ws, + i32:$idx), + sub_64)), + GPR64), [HasMSA, IsGP64bit]>; + +def : MSAPat<(f32 (vector_extract v4f32:$ws, i32:$idx)), + (f32 (EXTRACT_SUBREG (SPLAT_W v4f32:$ws, + i32:$idx), + sub_lo))>; +def : MSAPat<(f64 (vector_extract v2f64:$ws, i32:$idx)), + (f64 (EXTRACT_SUBREG (SPLAT_D v2f64:$ws, + i32:$idx), + sub_64))>; + +// Vector extraction with variable index (N64 ABI) +def : MSAPat< + (i32 (vextract_sext_i8 v16i8:$ws, i64:$idx)), + (SRA (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG + (SPLAT_B v16i8:$ws, + (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_lo)), + GPR32), + (i32 24))>; +def : MSAPat< + (i32 (vextract_sext_i16 v8i16:$ws, i64:$idx)), + (SRA (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG + (SPLAT_H v8i16:$ws, + (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_lo)), + GPR32), + (i32 16))>; +def : MSAPat< + (i32 (vextract_sext_i32 v4i32:$ws, i64:$idx)), + (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG + (SPLAT_W v4i32:$ws, + (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_lo)), + GPR32)>; +def : MSAPat< + (i64 (vextract_sext_i64 v2i64:$ws, i64:$idx)), + (COPY_TO_REGCLASS + (i64 (EXTRACT_SUBREG + (SPLAT_D v2i64:$ws, + (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_64)), + GPR64), [HasMSA, IsGP64bit]>; + +def : MSAPat< + (i32 (vextract_zext_i8 v16i8:$ws, i64:$idx)), + (SRL (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG + (SPLAT_B v16i8:$ws, + (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_lo)), + GPR32), + (i32 24))>; +def : MSAPat< + (i32 (vextract_zext_i16 v8i16:$ws, i64:$idx)), + (SRL (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG + (SPLAT_H v8i16:$ws, + (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_lo)), + GPR32), + (i32 16))>; +def : MSAPat< + (i32 (vextract_zext_i32 v4i32:$ws, i64:$idx)), + (COPY_TO_REGCLASS + (i32 (EXTRACT_SUBREG + (SPLAT_W v4i32:$ws, + (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_lo)), + GPR32)>; +def : MSAPat< + (i64 (vextract_zext_i64 v2i64:$ws, i64:$idx)), + (COPY_TO_REGCLASS + (i64 (EXTRACT_SUBREG + (SPLAT_D v2i64:$ws, + (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_64)), + GPR64), + [HasMSA, IsGP64bit]>; + +def : MSAPat< + (f32 (vector_extract v4f32:$ws, i64:$idx)), + (f32 (EXTRACT_SUBREG + (SPLAT_W v4f32:$ws, + (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_lo))>; +def : MSAPat< + (f64 (vector_extract v2f64:$ws, i64:$idx)), + (f64 (EXTRACT_SUBREG + (SPLAT_D v2f64:$ws, + (COPY_TO_REGCLASS (i32 (EXTRACT_SUBREG i64:$idx, sub_32)), GPR32)), + sub_64))>; diff --git a/lib/Target/Maxis/MaxisMTInstrFormats.td b/lib/Target/Maxis/MaxisMTInstrFormats.td new file mode 100644 index 00000000..c84b1000 --- /dev/null +++ b/lib/Target/Maxis/MaxisMTInstrFormats.td @@ -0,0 +1,99 @@ +//===-- MaxisMTInstrFormats.td - Maxis Instruction Formats ---*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Describe the MAXIS MT instructions format +// +// opcode - operation code. +// rt - destination register +// +//===----------------------------------------------------------------------===// + +class MaxisMTInst : MaxisInst<(outs), (ins), "", [], NoItinerary, FrmOther>, + PredicateControl { + let DecoderNamespace = "Maxis"; + let EncodingPredicates = [HasStdEnc]; +} + +class OPCODE1 Val> { + bits<1> Value = Val; +} + +def OPCODE_SC_D : OPCODE1<0b0>; +def OPCODE_SC_E : OPCODE1<0b1>; + +class FIELD5 Val> { + bits<5> Value = Val; +} + +def FIELD5_1_DMT_EMT : FIELD5<0b00001>; +def FIELD5_2_DMT_EMT : FIELD5<0b01111>; +def FIELD5_1_2_DVPE_EVPE : FIELD5<0b00000>; +def FIELD5_MFTR : FIELD5<0b01000>; +def FIELD5_MTTR : FIELD5<0b01100>; + +class COP0_MFMC0_MT : MaxisMTInst { + bits<32> Inst; + + bits<5> rt; + let Inst{31-26} = 0b010000; // COP0 + let Inst{25-21} = 0b01011; // MFMC0 + let Inst{20-16} = rt; + let Inst{15-11} = Op1.Value; + let Inst{10-6} = Op2.Value; + let Inst{5} = sc.Value; + let Inst{4-3} = 0b00; + let Inst{2-0} = 0b001; +} + +class COP0_MFTTR_MT : MaxisMTInst { + bits<32> Inst; + + bits<5> rt; + bits<5> rd; + bits<1> u; + bits<1> h; + bits<3> sel; + let Inst{31-26} = 0b010000; // COP0 + let Inst{25-21} = Op.Value; // MFMC0 + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = 0b00000; // rx - currently unsupported. + let Inst{5} = u; + let Inst{4} = h; + let Inst{3} = 0b0; + let Inst{2-0} = sel; +} + +class SPECIAL3_MT_FORK : MaxisMTInst { + bits<32> Inst; + + bits<5> rs; + bits<5> rt; + bits<5> rd; + let Inst{31-26} = 0b011111; // SPECIAL3 + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-6} = 0b00000; + let Inst{5-0} = 0b001000; // FORK +} + +class SPECIAL3_MT_YIELD : MaxisMTInst { + bits<32> Inst; + + bits<5> rs; + bits<5> rd; + let Inst{31-26} = 0b011111; // SPECIAL3 + let Inst{25-21} = rs; + let Inst{20-16} = 0b00000; + let Inst{15-11} = rd; + let Inst{10-6} = 0b00000; + let Inst{5-0} = 0b001001; // FORK +} diff --git a/lib/Target/Maxis/MaxisMTInstrInfo.td b/lib/Target/Maxis/MaxisMTInstrInfo.td new file mode 100644 index 00000000..b5c7defd --- /dev/null +++ b/lib/Target/Maxis/MaxisMTInstrInfo.td @@ -0,0 +1,208 @@ +//===-- MaxisMTInstrInfo.td - Maxis MT Instruction Infos -----*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the MAXIS MT ASE as defined by MD00378 1.12. +// +// TODO: Add support for the microMAXIS encodings for the MT ASE and add the +// instruction mappings. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// MAXIS MT Instruction Encodings +//===----------------------------------------------------------------------===// + +class DMT_ENC : COP0_MFMC0_MT; + +class EMT_ENC : COP0_MFMC0_MT; + +class DVPE_ENC : COP0_MFMC0_MT; + +class EVPE_ENC : COP0_MFMC0_MT; + +class FORK_ENC : SPECIAL3_MT_FORK; + +class YIELD_ENC : SPECIAL3_MT_YIELD; + +class MFTR_ENC : COP0_MFTTR_MT; + +class MTTR_ENC : COP0_MFTTR_MT; + +//===----------------------------------------------------------------------===// +// MAXIS MT Instruction Descriptions +//===----------------------------------------------------------------------===// + +class MT_1R_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins); + string AsmString = !strconcat(instr_asm, "\t$rt"); + list Pattern = []; + InstrItinClass Itinerary = Itin; +} + +class MFTR_DESC { + dag OutOperandList = (outs GPR32Opnd:$rd); + dag InOperandList = (ins GPR32Opnd:$rt, uimm1:$u, uimm3:$sel, uimm1:$h); + string AsmString = "mftr\t$rd, $rt, $u, $sel, $h"; + list Pattern = []; + InstrItinClass Itinerary = II_MFTR; +} + +class MTTR_DESC { + dag OutOperandList = (outs GPR32Opnd:$rd); + dag InOperandList = (ins GPR32Opnd:$rt, uimm1:$u, uimm3:$sel, uimm1:$h); + string AsmString = "mttr\t$rt, $rd, $u, $sel, $h"; + list Pattern = []; + InstrItinClass Itinerary = II_MTTR; +} + +class FORK_DESC { + dag OutOperandList = (outs GPR32Opnd:$rs, GPR32Opnd:$rd); + dag InOperandList = (ins GPR32Opnd:$rt); + string AsmString = "fork\t$rd, $rs, $rt"; + list Pattern = []; + InstrItinClass Itinerary = II_FORK; +} + +class YIELD_DESC { + dag OutOperandList = (outs GPR32Opnd:$rd); + dag InOperandList = (ins GPR32Opnd:$rs); + string AsmString = "yield\t$rd, $rs"; + list Pattern = []; + InstrItinClass Itinerary = II_YIELD; +} + +class DMT_DESC : MT_1R_DESC_BASE<"dmt", II_DMT>; + +class EMT_DESC : MT_1R_DESC_BASE<"emt", II_EMT>; + +class DVPE_DESC : MT_1R_DESC_BASE<"dvpe", II_DVPE>; + +class EVPE_DESC : MT_1R_DESC_BASE<"evpe", II_EVPE>; + +//===----------------------------------------------------------------------===// +// MAXIS MT Instruction Definitions +//===----------------------------------------------------------------------===// +let hasSideEffects = 1, isNotDuplicable = 1, + AdditionalPredicates = [NotInMicroMaxis] in { + def DMT : DMT_ENC, DMT_DESC, ASE_MT; + + def EMT : EMT_ENC, EMT_DESC, ASE_MT; + + def DVPE : DVPE_ENC, DVPE_DESC, ASE_MT; + + def EVPE : EVPE_ENC, EVPE_DESC, ASE_MT; + + def FORK : FORK_ENC, FORK_DESC, ASE_MT; + + def YIELD : YIELD_ENC, YIELD_DESC, ASE_MT; + + def MFTR : MFTR_ENC, MFTR_DESC, ASE_MT; + + def MTTR : MTTR_ENC, MTTR_DESC, ASE_MT; +} + +//===----------------------------------------------------------------------===// +// MAXIS MT Pseudo Instructions - used to support mtfr & mttr aliases. +//===----------------------------------------------------------------------===// +def MFTC0 : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), (ins COP0Opnd:$rt, + uimm3:$sel), + "mftc0 $rd, $rt, $sel">, ASE_MT; + +def MFTGPR : MaxisAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rt, + uimm3:$sel), + "mftgpr $rd, $rt">, ASE_MT; + +def MFTLO : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins ACC64DSPOpnd:$ac), + "mftlo $rt, $ac">, ASE_MT; + +def MFTHI : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins ACC64DSPOpnd:$ac), + "mfthi $rt, $ac">, ASE_MT; + +def MFTACX : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins ACC64DSPOpnd:$ac), + "mftacx $rt, $ac">, ASE_MT; + +def MFTDSP : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins), + "mftdsp $rt">, ASE_MT; + +def MFTC1 : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins FGR32Opnd:$ft), + "mftc1 $rt, $ft">, ASE_MT; + +def MFTHC1 : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins FGR32Opnd:$ft), + "mfthc1 $rt, $ft">, ASE_MT; + +def CFTC1 : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins FGRCCOpnd:$ft), + "cftc1 $rt, $ft">, ASE_MT; + + +def MTTC0 : MaxisAsmPseudoInst<(outs COP0Opnd:$rd), (ins GPR32Opnd:$rt, + uimm3:$sel), + "mttc0 $rt, $rd, $sel">, ASE_MT; + +def MTTGPR : MaxisAsmPseudoInst<(outs GPR32Opnd:$rt), (ins GPR32Opnd:$rd), + "mttgpr $rd, $rt">, ASE_MT; + +def MTTLO : MaxisAsmPseudoInst<(outs ACC64DSPOpnd:$ac), (ins GPR32Opnd:$rt), + "mttlo $rt, $ac">, ASE_MT; + +def MTTHI : MaxisAsmPseudoInst<(outs ACC64DSPOpnd:$ac), (ins GPR32Opnd:$rt), + "mtthi $rt, $ac">, ASE_MT; + +def MTTACX : MaxisAsmPseudoInst<(outs ACC64DSPOpnd:$ac), (ins GPR32Opnd:$rt), + "mttacx $rt, $ac">, ASE_MT; + +def MTTDSP : MaxisAsmPseudoInst<(outs), (ins GPR32Opnd:$rt), + "mttdsp $rt">, ASE_MT; + +def MTTC1 : MaxisAsmPseudoInst<(outs FGR32Opnd:$ft), (ins GPR32Opnd:$rt), + "mttc1 $rt, $ft">, ASE_MT; + +def MTTHC1 : MaxisAsmPseudoInst<(outs FGR32Opnd:$ft), (ins GPR32Opnd:$rt), + "mtthc1 $rt, $ft">, ASE_MT; + +def CTTC1 : MaxisAsmPseudoInst<(outs FGRCCOpnd:$ft), (ins GPR32Opnd:$rt), + "cttc1 $rt, $ft">, ASE_MT; + +//===----------------------------------------------------------------------===// +// MAXIS MT Instruction Definitions +//===----------------------------------------------------------------------===// + +let AdditionalPredicates = [NotInMicroMaxis] in { + def : MaxisInstAlias<"dmt", (DMT ZERO), 1>, ASE_MT; + + def : MaxisInstAlias<"emt", (EMT ZERO), 1>, ASE_MT; + + def : MaxisInstAlias<"dvpe", (DVPE ZERO), 1>, ASE_MT; + + def : MaxisInstAlias<"evpe", (EVPE ZERO), 1>, ASE_MT; + + def : MaxisInstAlias<"yield $rs", (YIELD ZERO, GPR32Opnd:$rs), 1>, ASE_MT; + + def : MaxisInstAlias<"mftc0 $rd, $rt", (MFTC0 GPR32Opnd:$rd, COP0Opnd:$rt, 0), + 1>, ASE_MT; + + def : MaxisInstAlias<"mftlo $rt", (MFTLO GPR32Opnd:$rt, AC0), 1>, ASE_MT; + + def : MaxisInstAlias<"mfthi $rt", (MFTHI GPR32Opnd:$rt, AC0), 1>, ASE_MT; + + def : MaxisInstAlias<"mftacx $rt", (MFTACX GPR32Opnd:$rt, AC0), 1>, ASE_MT; + + def : MaxisInstAlias<"mttc0 $rd, $rt", (MTTC0 COP0Opnd:$rt, GPR32Opnd:$rd, 0), + 1>, ASE_MT; + + def : MaxisInstAlias<"mttlo $rt", (MTTLO AC0, GPR32Opnd:$rt), 1>, ASE_MT; + + def : MaxisInstAlias<"mtthi $rt", (MTTHI AC0, GPR32Opnd:$rt), 1>, ASE_MT; + + def : MaxisInstAlias<"mttacx $rt", (MTTACX AC0, GPR32Opnd:$rt), 1>, ASE_MT; +} diff --git a/lib/Target/Maxis/MaxisMachineFunction.cpp b/lib/Target/Maxis/MaxisMachineFunction.cpp new file mode 100644 index 00000000..f84fb4d5 --- /dev/null +++ b/lib/Target/Maxis/MaxisMachineFunction.cpp @@ -0,0 +1,104 @@ +//===-- MaxisMachineFunctionInfo.cpp - Private data used for Maxis ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisMachineFunction.h" +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/PseudoSourceValue.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/Support/CommandLine.h" + +using namespace llvm; + +static cl::opt +FixGlobalBaseReg("maxis-fix-global-base-reg", cl::Hidden, cl::init(true), + cl::desc("Always use $gp as the global base register.")); + +MaxisFunctionInfo::~MaxisFunctionInfo() = default; + +bool MaxisFunctionInfo::globalBaseRegSet() const { + return GlobalBaseReg; +} + +unsigned MaxisFunctionInfo::getGlobalBaseReg() { + // Return if it has already been initialized. + if (GlobalBaseReg) + return GlobalBaseReg; + + MaxisSubtarget const &STI = + static_cast(MF.getSubtarget()); + + const TargetRegisterClass *RC = + STI.inMaxis16Mode() + ? &Maxis::CPU16RegsRegClass + : STI.inMicroMaxisMode() + ? &Maxis::GPRMM16RegClass + : static_cast(MF.getTarget()) + .getABI() + .IsN64() + ? &Maxis::GPR64RegClass + : &Maxis::GPR32RegClass; + return GlobalBaseReg = MF.getRegInfo().createVirtualRegister(RC); +} + +void MaxisFunctionInfo::createEhDataRegsFI() { + const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo(); + for (int I = 0; I < 4; ++I) { + const TargetRegisterClass &RC = + static_cast(MF.getTarget()).getABI().IsN64() + ? Maxis::GPR64RegClass + : Maxis::GPR32RegClass; + + EhDataRegFI[I] = MF.getFrameInfo().CreateStackObject(TRI.getSpillSize(RC), + TRI.getSpillAlignment(RC), false); + } +} + +void MaxisFunctionInfo::createISRRegFI() { + // ISRs require spill slots for Status & ErrorPC Coprocessor 0 registers. + // The current implementation only supports Maxis32r2+ not Maxis64rX. Status + // is always 32 bits, ErrorPC is 32 or 64 bits dependent on architecture, + // however Maxis32r2+ is the supported architecture. + const TargetRegisterClass &RC = Maxis::GPR32RegClass; + const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo(); + + for (int I = 0; I < 2; ++I) + ISRDataRegFI[I] = MF.getFrameInfo().CreateStackObject( + TRI.getSpillSize(RC), TRI.getSpillAlignment(RC), false); +} + +bool MaxisFunctionInfo::isEhDataRegFI(int FI) const { + return CallsEhReturn && (FI == EhDataRegFI[0] || FI == EhDataRegFI[1] + || FI == EhDataRegFI[2] || FI == EhDataRegFI[3]); +} + +bool MaxisFunctionInfo::isISRRegFI(int FI) const { + return IsISR && (FI == ISRDataRegFI[0] || FI == ISRDataRegFI[1]); +} +MachinePointerInfo MaxisFunctionInfo::callPtrInfo(const char *ES) { + return MachinePointerInfo(MF.getPSVManager().getExternalSymbolCallEntry(ES)); +} + +MachinePointerInfo MaxisFunctionInfo::callPtrInfo(const GlobalValue *GV) { + return MachinePointerInfo(MF.getPSVManager().getGlobalValueCallEntry(GV)); +} + +int MaxisFunctionInfo::getMoveF64ViaSpillFI(const TargetRegisterClass *RC) { + const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo(); + if (MoveF64ViaSpillFI == -1) { + MoveF64ViaSpillFI = MF.getFrameInfo().CreateStackObject( + TRI.getSpillSize(*RC), TRI.getSpillAlignment(*RC), false); + } + return MoveF64ViaSpillFI; +} + +void MaxisFunctionInfo::anchor() {} diff --git a/lib/Target/Maxis/MaxisMachineFunction.h b/lib/Target/Maxis/MaxisMachineFunction.h new file mode 100644 index 00000000..2b72d092 --- /dev/null +++ b/lib/Target/Maxis/MaxisMachineFunction.h @@ -0,0 +1,126 @@ +//===- MaxisMachineFunctionInfo.h - Private data used for Maxis ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the Maxis specific subclass of MachineFunctionInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISMACHINEFUNCTION_H +#define LLVM_LIB_TARGET_MAXIS_MAXISMACHINEFUNCTION_H + +#include "Maxis16HardFloatInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include + +namespace llvm { + +/// MaxisFunctionInfo - This class is derived from MachineFunction private +/// Maxis target-specific information for each MachineFunction. +class MaxisFunctionInfo : public MachineFunctionInfo { +public: + MaxisFunctionInfo(MachineFunction &MF) : MF(MF) {} + + ~MaxisFunctionInfo() override; + + unsigned getSRetReturnReg() const { return SRetReturnReg; } + void setSRetReturnReg(unsigned Reg) { SRetReturnReg = Reg; } + + bool globalBaseRegSet() const; + unsigned getGlobalBaseReg(); + + int getVarArgsFrameIndex() const { return VarArgsFrameIndex; } + void setVarArgsFrameIndex(int Index) { VarArgsFrameIndex = Index; } + + bool hasByvalArg() const { return HasByvalArg; } + void setFormalArgInfo(unsigned Size, bool HasByval) { + IncomingArgSize = Size; + HasByvalArg = HasByval; + } + + unsigned getIncomingArgSize() const { return IncomingArgSize; } + + bool callsEhReturn() const { return CallsEhReturn; } + void setCallsEhReturn() { CallsEhReturn = true; } + + void createEhDataRegsFI(); + int getEhDataRegFI(unsigned Reg) const { return EhDataRegFI[Reg]; } + bool isEhDataRegFI(int FI) const; + + /// Create a MachinePointerInfo that has an ExternalSymbolPseudoSourceValue + /// object representing a GOT entry for an external function. + MachinePointerInfo callPtrInfo(const char *ES); + + // Functions with the "interrupt" attribute require special prologues, + // epilogues and additional spill slots. + bool isISR() const { return IsISR; } + void setISR() { IsISR = true; } + void createISRRegFI(); + int getISRRegFI(unsigned Reg) const { return ISRDataRegFI[Reg]; } + bool isISRRegFI(int FI) const; + + /// Create a MachinePointerInfo that has a GlobalValuePseudoSourceValue object + /// representing a GOT entry for a global function. + MachinePointerInfo callPtrInfo(const GlobalValue *GV); + + void setSaveS2() { SaveS2 = true; } + bool hasSaveS2() const { return SaveS2; } + + int getMoveF64ViaSpillFI(const TargetRegisterClass *RC); + + std::map + StubsNeeded; + +private: + virtual void anchor(); + + MachineFunction& MF; + + /// SRetReturnReg - Some subtargets require that sret lowering includes + /// returning the value of the returned struct in a register. This field + /// holds the virtual register into which the sret argument is passed. + unsigned SRetReturnReg = 0; + + /// GlobalBaseReg - 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; + + /// VarArgsFrameIndex - FrameIndex for start of varargs area. + int VarArgsFrameIndex = 0; + + /// True if function has a byval argument. + bool HasByvalArg; + + /// Size of incoming argument area. + unsigned IncomingArgSize; + + /// CallsEhReturn - Whether the function calls llvm.eh.return. + bool CallsEhReturn = false; + + /// Frame objects for spilling eh data registers. + int EhDataRegFI[4]; + + /// ISR - Whether the function is an Interrupt Service Routine. + bool IsISR = false; + + /// Frame objects for spilling C0_STATUS, C0_EPC + int ISRDataRegFI[2]; + + // saveS2 + bool SaveS2 = false; + + /// FrameIndex for expanding BuildPairF64 nodes to spill and reload when the + /// O32 FPXX ABI is enabled. -1 is used to denote invalid index. + int MoveF64ViaSpillFI = -1; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISMACHINEFUNCTION_H diff --git a/lib/Target/Maxis/MaxisModuleISelDAGToDAG.cpp b/lib/Target/Maxis/MaxisModuleISelDAGToDAG.cpp new file mode 100644 index 00000000..88bebfd6 --- /dev/null +++ b/lib/Target/Maxis/MaxisModuleISelDAGToDAG.cpp @@ -0,0 +1,54 @@ +//===----------------------------------------------------------------------===// +// Instruction Selector Subtarget Control +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// This file defines a pass used to change the subtarget for the +// Maxis Instruction selector. +// +//===----------------------------------------------------------------------===// + +#include "Maxis.h" +#include "MaxisTargetMachine.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "maxis-isel" + +namespace { + class MaxisModuleDAGToDAGISel : public MachineFunctionPass { + public: + static char ID; + + MaxisModuleDAGToDAGISel() : MachineFunctionPass(ID) {} + + // Pass Name + StringRef getPassName() const override { + return "MAXIS DAG->DAG Pattern Instruction Selection"; + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; + }; + + char MaxisModuleDAGToDAGISel::ID = 0; +} + +bool MaxisModuleDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { + DEBUG(errs() << "In MaxisModuleDAGToDAGISel::runMachineFunction\n"); + auto &TPC = getAnalysis(); + auto &TM = TPC.getTM(); + TM.resetSubtarget(&MF); + return false; +} + +llvm::FunctionPass *llvm::createMaxisModuleISelDagPass() { + return new MaxisModuleDAGToDAGISel(); +} diff --git a/lib/Target/Maxis/MaxisOptimizePICCall.cpp b/lib/Target/Maxis/MaxisOptimizePICCall.cpp new file mode 100644 index 00000000..6e208de0 --- /dev/null +++ b/lib/Target/Maxis/MaxisOptimizePICCall.cpp @@ -0,0 +1,323 @@ +//===- MaxisOptimizePICCall.cpp - Optimize PIC Calls -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass eliminates unnecessary instructions that set up $gp and replace +// instructions that load target function addresses with copy instructions. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "Maxis.h" +#include "MaxisRegisterInfo.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/ScopedHashTable.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetOpcodes.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/RecyclingAllocator.h" +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "optimize-maxis-pic-call" + +static cl::opt LoadTargetFromGOT("maxis-load-target-from-got", + cl::init(true), + cl::desc("Load target address from GOT"), + cl::Hidden); + +static cl::opt EraseGPOpnd("maxis-erase-gp-opnd", + cl::init(true), cl::desc("Erase GP Operand"), + cl::Hidden); + +namespace { + +using ValueType = PointerUnion; +using CntRegP = std::pair; +using AllocatorTy = RecyclingAllocator>; +using ScopedHTType = ScopedHashTable, AllocatorTy>; + +class MBBInfo { +public: + MBBInfo(MachineDomTreeNode *N); + + const MachineDomTreeNode *getNode() const; + bool isVisited() const; + void preVisit(ScopedHTType &ScopedHT); + void postVisit(); + +private: + MachineDomTreeNode *Node; + ScopedHTType::ScopeTy *HTScope; +}; + +class OptimizePICCall : public MachineFunctionPass { +public: + OptimizePICCall() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "Maxis OptimizePICCall"; } + + bool runOnMachineFunction(MachineFunction &F) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.addRequired(); + MachineFunctionPass::getAnalysisUsage(AU); + } + +private: + /// \brief Visit MBB. + bool visitNode(MBBInfo &MBBI); + + /// \brief Test if MI jumps to a function via a register. + /// + /// Also, return the virtual register containing the target function's address + /// and the underlying object in Reg and Val respectively, if the function's + /// address can be resolved lazily. + bool isCallViaRegister(MachineInstr &MI, unsigned &Reg, + ValueType &Val) const; + + /// \brief Return the number of instructions that dominate the current + /// instruction and load the function address from object Entry. + unsigned getCount(ValueType Entry); + + /// \brief Return the destination virtual register of the last instruction + /// that loads from object Entry. + unsigned getReg(ValueType Entry); + + /// \brief Update ScopedHT. + void incCntAndSetReg(ValueType Entry, unsigned Reg); + + ScopedHTType ScopedHT; + + static char ID; +}; + +} // end of anonymous namespace + +char OptimizePICCall::ID = 0; + +/// Return the first MachineOperand of MI if it is a used virtual register. +static MachineOperand *getCallTargetRegOpnd(MachineInstr &MI) { + if (MI.getNumOperands() == 0) + return nullptr; + + MachineOperand &MO = MI.getOperand(0); + + if (!MO.isReg() || !MO.isUse() || + !TargetRegisterInfo::isVirtualRegister(MO.getReg())) + return nullptr; + + return &MO; +} + +/// Return type of register Reg. +static MVT::SimpleValueType getRegTy(unsigned Reg, MachineFunction &MF) { + const TargetRegisterInfo &TRI = *MF.getSubtarget().getRegisterInfo(); + const TargetRegisterClass *RC = MF.getRegInfo().getRegClass(Reg); + assert(TRI.legalclasstypes_end(*RC) - TRI.legalclasstypes_begin(*RC) == 1); + return *TRI.legalclasstypes_begin(*RC); +} + +/// Do the following transformation: +/// +/// jalr $vreg +/// => +/// copy $t9, $vreg +/// jalr $t9 +static void setCallTargetReg(MachineBasicBlock *MBB, + MachineBasicBlock::iterator I) { + MachineFunction &MF = *MBB->getParent(); + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + unsigned SrcReg = I->getOperand(0).getReg(); + unsigned DstReg = getRegTy(SrcReg, MF) == MVT::i32 ? Maxis::T9 : Maxis::T9_64; + BuildMI(*MBB, I, I->getDebugLoc(), TII.get(TargetOpcode::COPY), DstReg) + .addReg(SrcReg); + I->getOperand(0).setReg(DstReg); +} + +/// Search MI's operands for register GP and erase it. +static void eraseGPOpnd(MachineInstr &MI) { + if (!EraseGPOpnd) + return; + + MachineFunction &MF = *MI.getParent()->getParent(); + MVT::SimpleValueType Ty = getRegTy(MI.getOperand(0).getReg(), MF); + unsigned Reg = Ty == MVT::i32 ? Maxis::GP : Maxis::GP_64; + + for (unsigned I = 0; I < MI.getNumOperands(); ++I) { + MachineOperand &MO = MI.getOperand(I); + if (MO.isReg() && MO.getReg() == Reg) { + MI.RemoveOperand(I); + return; + } + } + + llvm_unreachable(nullptr); +} + +MBBInfo::MBBInfo(MachineDomTreeNode *N) : Node(N), HTScope(nullptr) {} + +const MachineDomTreeNode *MBBInfo::getNode() const { return Node; } + +bool MBBInfo::isVisited() const { return HTScope; } + +void MBBInfo::preVisit(ScopedHTType &ScopedHT) { + HTScope = new ScopedHTType::ScopeTy(ScopedHT); +} + +void MBBInfo::postVisit() { + delete HTScope; +} + +// OptimizePICCall methods. +bool OptimizePICCall::runOnMachineFunction(MachineFunction &F) { + if (static_cast(F.getSubtarget()).inMaxis16Mode()) + return false; + + // Do a pre-order traversal of the dominator tree. + MachineDominatorTree *MDT = &getAnalysis(); + bool Changed = false; + + SmallVector WorkList(1, MBBInfo(MDT->getRootNode())); + + while (!WorkList.empty()) { + MBBInfo &MBBI = WorkList.back(); + + // If this MBB has already been visited, destroy the scope for the MBB and + // pop it from the work list. + if (MBBI.isVisited()) { + MBBI.postVisit(); + WorkList.pop_back(); + continue; + } + + // Visit the MBB and add its children to the work list. + MBBI.preVisit(ScopedHT); + Changed |= visitNode(MBBI); + const MachineDomTreeNode *Node = MBBI.getNode(); + const std::vector &Children = Node->getChildren(); + WorkList.append(Children.begin(), Children.end()); + } + + return Changed; +} + +bool OptimizePICCall::visitNode(MBBInfo &MBBI) { + bool Changed = false; + MachineBasicBlock *MBB = MBBI.getNode()->getBlock(); + + for (MachineBasicBlock::iterator I = MBB->begin(), E = MBB->end(); I != E; + ++I) { + unsigned Reg; + ValueType Entry; + + // Skip instructions that are not call instructions via registers. + if (!isCallViaRegister(*I, Reg, Entry)) + continue; + + Changed = true; + unsigned N = getCount(Entry); + + if (N != 0) { + // If a function has been called more than twice, we do not have to emit a + // load instruction to get the function address from the GOT, but can + // instead reuse the address that has been loaded before. + if (N >= 2 && !LoadTargetFromGOT) + getCallTargetRegOpnd(*I)->setReg(getReg(Entry)); + + // Erase the $gp operand if this isn't the first time a function has + // been called. $gp needs to be set up only if the function call can go + // through a lazy binding stub. + eraseGPOpnd(*I); + } + + if (Entry) + incCntAndSetReg(Entry, Reg); + + setCallTargetReg(MBB, I); + } + + return Changed; +} + +bool OptimizePICCall::isCallViaRegister(MachineInstr &MI, unsigned &Reg, + ValueType &Val) const { + if (!MI.isCall()) + return false; + + MachineOperand *MO = getCallTargetRegOpnd(MI); + + // Return if MI is not a function call via a register. + if (!MO) + return false; + + // Get the instruction that loads the function address from the GOT. + Reg = MO->getReg(); + Val = nullptr; + MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo(); + MachineInstr *DefMI = MRI.getVRegDef(Reg); + + assert(DefMI); + + // See if DefMI is an instruction that loads from a GOT entry that holds the + // address of a lazy binding stub. + if (!DefMI->mayLoad() || DefMI->getNumOperands() < 3) + return true; + + unsigned Flags = DefMI->getOperand(2).getTargetFlags(); + + if (Flags != MaxisII::MO_GOT_CALL && Flags != MaxisII::MO_CALL_LO16) + return true; + + // Return the underlying object for the GOT entry in Val. + assert(DefMI->hasOneMemOperand()); + Val = (*DefMI->memoperands_begin())->getValue(); + if (!Val) + Val = (*DefMI->memoperands_begin())->getPseudoValue(); + return true; +} + +unsigned OptimizePICCall::getCount(ValueType Entry) { + return ScopedHT.lookup(Entry).first; +} + +unsigned OptimizePICCall::getReg(ValueType Entry) { + unsigned Reg = ScopedHT.lookup(Entry).second; + assert(Reg); + return Reg; +} + +void OptimizePICCall::incCntAndSetReg(ValueType Entry, unsigned Reg) { + CntRegP P = ScopedHT.lookup(Entry); + ScopedHT.insert(Entry, std::make_pair(P.first + 1, Reg)); +} + +/// Return an OptimizeCall object. +FunctionPass *llvm::createMaxisOptimizePICCallPass() { + return new OptimizePICCall(); +} diff --git a/lib/Target/Maxis/MaxisOptionRecord.h b/lib/Target/Maxis/MaxisOptionRecord.h new file mode 100644 index 00000000..f3e261e9 --- /dev/null +++ b/lib/Target/Maxis/MaxisOptionRecord.h @@ -0,0 +1,83 @@ +//===- MaxisOptionRecord.h - Abstraction for storing information -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// MaxisOptionRecord - Abstraction for storing arbitrary information in +// ELF files. Arbitrary information (e.g. register usage) can be stored in Maxis +// specific ELF sections like .Maxis.options. Specific records should subclass +// MaxisOptionRecord and provide an implementation to EmitMaxisOptionRecord which +// basically just dumps the information into an ELF section. More information +// about .Maxis.option can be found in the SysV ABI and the 64-bit ELF Object +// specification. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISOPTIONRECORD_H +#define LLVM_LIB_TARGET_MAXIS_MAXISOPTIONRECORD_H + +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCRegisterInfo.h" +#include + +namespace llvm { + +class MaxisELFStreamer; + +class MaxisOptionRecord { +public: + virtual ~MaxisOptionRecord() = default; + + virtual void EmitMaxisOptionRecord() = 0; +}; + +class MaxisRegInfoRecord : public MaxisOptionRecord { +public: + MaxisRegInfoRecord(MaxisELFStreamer *S, MCContext &Context) + : Streamer(S), Context(Context) { + ri_gprmask = 0; + ri_cprmask[0] = ri_cprmask[1] = ri_cprmask[2] = ri_cprmask[3] = 0; + ri_gp_value = 0; + + const MCRegisterInfo *TRI = Context.getRegisterInfo(); + GPR32RegClass = &(TRI->getRegClass(Maxis::GPR32RegClassID)); + GPR64RegClass = &(TRI->getRegClass(Maxis::GPR64RegClassID)); + FGR32RegClass = &(TRI->getRegClass(Maxis::FGR32RegClassID)); + FGR64RegClass = &(TRI->getRegClass(Maxis::FGR64RegClassID)); + AFGR64RegClass = &(TRI->getRegClass(Maxis::AFGR64RegClassID)); + MSA128BRegClass = &(TRI->getRegClass(Maxis::MSA128BRegClassID)); + COP0RegClass = &(TRI->getRegClass(Maxis::COP0RegClassID)); + COP2RegClass = &(TRI->getRegClass(Maxis::COP2RegClassID)); + COP3RegClass = &(TRI->getRegClass(Maxis::COP3RegClassID)); + } + + ~MaxisRegInfoRecord() override = default; + + void EmitMaxisOptionRecord() override; + void SetPhysRegUsed(unsigned Reg, const MCRegisterInfo *MCRegInfo); + +private: + MaxisELFStreamer *Streamer; + MCContext &Context; + const MCRegisterClass *GPR32RegClass; + const MCRegisterClass *GPR64RegClass; + const MCRegisterClass *FGR32RegClass; + const MCRegisterClass *FGR64RegClass; + const MCRegisterClass *AFGR64RegClass; + const MCRegisterClass *MSA128BRegClass; + const MCRegisterClass *COP0RegClass; + const MCRegisterClass *COP2RegClass; + const MCRegisterClass *COP3RegClass; + uint32_t ri_gprmask; + uint32_t ri_cprmask[4]; + int64_t ri_gp_value; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISOPTIONRECORD_H diff --git a/lib/Target/Maxis/MaxisOs16.cpp b/lib/Target/Maxis/MaxisOs16.cpp new file mode 100644 index 00000000..9e698719 --- /dev/null +++ b/lib/Target/Maxis/MaxisOs16.cpp @@ -0,0 +1,158 @@ +//===---- MaxisOs16.cpp for Maxis Option -Os16 --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines an optimization phase for the MAXIS target. +// +//===----------------------------------------------------------------------===// + +#include "Maxis.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "maxis-os16" + +static cl::opt Maxis32FunctionMask( + "maxis32-function-mask", + cl::init(""), + cl::desc("Force function to be maxis32"), + cl::Hidden); + +namespace { + class MaxisOs16 : public ModulePass { + public: + static char ID; + + MaxisOs16() : ModulePass(ID) {} + + StringRef getPassName() const override { return "MAXIS Os16 Optimization"; } + + bool runOnModule(Module &M) override; + }; + + char MaxisOs16::ID = 0; +} + +// Figure out if we need float point based on the function signature. +// We need to move variables in and/or out of floating point +// registers because of the ABI +// +static bool needsFPFromSig(Function &F) { + Type* RetType = F.getReturnType(); + switch (RetType->getTypeID()) { + case Type::FloatTyID: + case Type::DoubleTyID: + return true; + default: + ; + } + if (F.arg_size() >=1) { + Argument &Arg = *F.arg_begin(); + switch (Arg.getType()->getTypeID()) { + case Type::FloatTyID: + case Type::DoubleTyID: + return true; + default: + ; + } + } + return false; +} + +// Figure out if the function will need floating point operations +// +static bool needsFP(Function &F) { + if (needsFPFromSig(F)) + return true; + for (Function::const_iterator BB = F.begin(), E = F.end(); BB != E; ++BB) + for (BasicBlock::const_iterator I = BB->begin(), E = BB->end(); + I != E; ++I) { + const Instruction &Inst = *I; + switch (Inst.getOpcode()) { + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FMul: + case Instruction::FDiv: + case Instruction::FRem: + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::UIToFP: + case Instruction::SIToFP: + case Instruction::FPTrunc: + case Instruction::FPExt: + case Instruction::FCmp: + return true; + default: + ; + } + if (const CallInst *CI = dyn_cast(I)) { + DEBUG(dbgs() << "Working on call" << "\n"); + Function &F_ = *CI->getCalledFunction(); + if (needsFPFromSig(F_)) + return true; + } + } + return false; +} + + +bool MaxisOs16::runOnModule(Module &M) { + bool usingMask = Maxis32FunctionMask.length() > 0; + bool doneUsingMask = false; // this will make it stop repeating + + DEBUG(dbgs() << "Run on Module MaxisOs16 \n" << Maxis32FunctionMask << "\n"); + if (usingMask) + DEBUG(dbgs() << "using mask \n" << Maxis32FunctionMask << "\n"); + + unsigned int functionIndex = 0; + bool modified = false; + + for (auto &F : M) { + if (F.isDeclaration()) + continue; + + DEBUG(dbgs() << "Working on " << F.getName() << "\n"); + if (usingMask) { + if (!doneUsingMask) { + if (functionIndex == Maxis32FunctionMask.length()) + functionIndex = 0; + switch (Maxis32FunctionMask[functionIndex]) { + case '1': + DEBUG(dbgs() << "mask forced maxis32: " << F.getName() << "\n"); + F.addFnAttr("nomaxis16"); + break; + case '.': + doneUsingMask = true; + break; + default: + break; + } + functionIndex++; + } + } + else { + if (needsFP(F)) { + DEBUG(dbgs() << "os16 forced maxis32: " << F.getName() << "\n"); + F.addFnAttr("nomaxis16"); + } + else { + DEBUG(dbgs() << "os16 forced maxis16: " << F.getName() << "\n"); + F.addFnAttr("maxis16"); + } + } + } + + return modified; +} + +ModulePass *llvm::createMaxisOs16Pass() { return new MaxisOs16(); } diff --git a/lib/Target/Maxis/MaxisRegisterInfo.cpp b/lib/Target/Maxis/MaxisRegisterInfo.cpp new file mode 100644 index 00000000..1ff830f5 --- /dev/null +++ b/lib/Target/Maxis/MaxisRegisterInfo.cpp @@ -0,0 +1,340 @@ +//===- MaxisRegisterInfo.cpp - MAXIS Register Information -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the MAXIS implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "MaxisRegisterInfo.h" +#include "MCTargetDesc/MaxisABIInfo.h" +#include "Maxis.h" +#include "MaxisMachineFunction.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Function.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-reg-info" + +#define GET_REGINFO_TARGET_DESC +#include "MaxisGenRegisterInfo.inc" + +MaxisRegisterInfo::MaxisRegisterInfo() : MaxisGenRegisterInfo(Maxis::RA) {} + +unsigned MaxisRegisterInfo::getPICCallReg() { return Maxis::T9; } + +const TargetRegisterClass * +MaxisRegisterInfo::getPointerRegClass(const MachineFunction &MF, + unsigned Kind) const { + MaxisABIInfo ABI = MF.getSubtarget().getABI(); + MaxisPtrClass PtrClassKind = static_cast(Kind); + + switch (PtrClassKind) { + case MaxisPtrClass::Default: + return ABI.ArePtrs64bit() ? &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + case MaxisPtrClass::GPR16MM: + return &Maxis::GPRMM16RegClass; + case MaxisPtrClass::StackPointer: + return ABI.ArePtrs64bit() ? &Maxis::SP64RegClass : &Maxis::SP32RegClass; + case MaxisPtrClass::GlobalPointer: + return ABI.ArePtrs64bit() ? &Maxis::GP64RegClass : &Maxis::GP32RegClass; + } + + llvm_unreachable("Unknown pointer kind"); +} + +unsigned +MaxisRegisterInfo::getRegPressureLimit(const TargetRegisterClass *RC, + MachineFunction &MF) const { + switch (RC->getID()) { + default: + return 0; + case Maxis::GPR32RegClassID: + case Maxis::GPR64RegClassID: + case Maxis::DSPRRegClassID: { + const TargetFrameLowering *TFI = MF.getSubtarget().getFrameLowering(); + return 28 - TFI->hasFP(MF); + } + case Maxis::FGR32RegClassID: + return 32; + case Maxis::AFGR64RegClassID: + return 16; + case Maxis::FGR64RegClassID: + return 32; + } +} + +//===----------------------------------------------------------------------===// +// Callee Saved Registers methods +//===----------------------------------------------------------------------===// + +/// Maxis Callee Saved Registers +const MCPhysReg * +MaxisRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + const MaxisSubtarget &Subtarget = MF->getSubtarget(); + const Function &F = MF->getFunction(); + if (F.hasFnAttribute("interrupt")) { + if (Subtarget.hasMaxis64()) + return Subtarget.hasMaxis64r6() ? CSR_Interrupt_64R6_SaveList + : CSR_Interrupt_64_SaveList; + else + return Subtarget.hasMaxis32r6() ? CSR_Interrupt_32R6_SaveList + : CSR_Interrupt_32_SaveList; + } + + if (Subtarget.isSingleFloat()) + return CSR_SingleFloatOnly_SaveList; + + if (Subtarget.isABI_N64()) + return CSR_N64_SaveList; + + if (Subtarget.isABI_N32()) + return CSR_N32_SaveList; + + if (Subtarget.isFP64bit()) + return CSR_O32_FP64_SaveList; + + if (Subtarget.isFPXX()) + return CSR_O32_FPXX_SaveList; + + return CSR_O32_SaveList; +} + +const uint32_t * +MaxisRegisterInfo::getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID) const { + const MaxisSubtarget &Subtarget = MF.getSubtarget(); + if (Subtarget.isSingleFloat()) + return CSR_SingleFloatOnly_RegMask; + + if (Subtarget.isABI_N64()) + return CSR_N64_RegMask; + + if (Subtarget.isABI_N32()) + return CSR_N32_RegMask; + + if (Subtarget.isFP64bit()) + return CSR_O32_FP64_RegMask; + + if (Subtarget.isFPXX()) + return CSR_O32_FPXX_RegMask; + + return CSR_O32_RegMask; +} + +const uint32_t *MaxisRegisterInfo::getMaxis16RetHelperMask() { + return CSR_Maxis16RetHelper_RegMask; +} + +BitVector MaxisRegisterInfo:: +getReservedRegs(const MachineFunction &MF) const { + static const MCPhysReg ReservedGPR32[] = { + Maxis::ZERO, Maxis::K0, Maxis::K1, Maxis::SP + }; + + static const MCPhysReg ReservedGPR64[] = { + Maxis::ZERO_64, Maxis::K0_64, Maxis::K1_64, Maxis::SP_64 + }; + + BitVector Reserved(getNumRegs()); + const MaxisSubtarget &Subtarget = MF.getSubtarget(); + + using RegIter = TargetRegisterClass::const_iterator; + + for (unsigned I = 0; I < array_lengthof(ReservedGPR32); ++I) + Reserved.set(ReservedGPR32[I]); + + // Reserve registers for the NaCl sandbox. + if (Subtarget.isTargetNaCl()) { + Reserved.set(Maxis::T6); // Reserved for control flow mask. + Reserved.set(Maxis::T7); // Reserved for memory access mask. + Reserved.set(Maxis::T8); // Reserved for thread pointer. + } + + for (unsigned I = 0; I < array_lengthof(ReservedGPR64); ++I) + Reserved.set(ReservedGPR64[I]); + + // For mno-abicalls, GP is a program invariant! + if (!Subtarget.isABICalls()) { + Reserved.set(Maxis::GP); + Reserved.set(Maxis::GP_64); + } + + if (Subtarget.isFP64bit()) { + // Reserve all registers in AFGR64. + for (RegIter Reg = Maxis::AFGR64RegClass.begin(), + EReg = Maxis::AFGR64RegClass.end(); Reg != EReg; ++Reg) + Reserved.set(*Reg); + } else { + // Reserve all registers in FGR64. + for (RegIter Reg = Maxis::FGR64RegClass.begin(), + EReg = Maxis::FGR64RegClass.end(); Reg != EReg; ++Reg) + Reserved.set(*Reg); + } + // Reserve FP if this function should have a dedicated frame pointer register. + if (Subtarget.getFrameLowering()->hasFP(MF)) { + if (Subtarget.inMaxis16Mode()) + Reserved.set(Maxis::S0); + else { + Reserved.set(Maxis::FP); + Reserved.set(Maxis::FP_64); + + // Reserve the base register if we need to both realign the stack and + // allocate variable-sized objects at runtime. This should test the + // same conditions as MaxisFrameLowering::hasBP(). + if (needsStackRealignment(MF) && + MF.getFrameInfo().hasVarSizedObjects()) { + Reserved.set(Maxis::S7); + Reserved.set(Maxis::S7_64); + } + } + } + + // Reserve hardware registers. + Reserved.set(Maxis::HWR29); + + // Reserve DSP control register. + Reserved.set(Maxis::DSPPos); + Reserved.set(Maxis::DSPSCount); + Reserved.set(Maxis::DSPCarry); + Reserved.set(Maxis::DSPEFI); + Reserved.set(Maxis::DSPOutFlag); + + // Reserve MSA control registers. + Reserved.set(Maxis::MSAIR); + Reserved.set(Maxis::MSACSR); + Reserved.set(Maxis::MSAAccess); + Reserved.set(Maxis::MSASave); + Reserved.set(Maxis::MSAModify); + Reserved.set(Maxis::MSARequest); + Reserved.set(Maxis::MSAMap); + Reserved.set(Maxis::MSAUnmap); + + // Reserve RA if in maxis16 mode. + if (Subtarget.inMaxis16Mode()) { + const MaxisFunctionInfo *MaxisFI = MF.getInfo(); + Reserved.set(Maxis::RA); + Reserved.set(Maxis::RA_64); + Reserved.set(Maxis::T0); + Reserved.set(Maxis::T1); + if (MF.getFunction().hasFnAttribute("saveS2") || MaxisFI->hasSaveS2()) + Reserved.set(Maxis::S2); + } + + // Reserve GP if small section is used. + if (Subtarget.useSmallSection()) { + Reserved.set(Maxis::GP); + Reserved.set(Maxis::GP_64); + } + + if (Subtarget.isABI_O32() && !Subtarget.useOddSPReg()) { + for (const auto &Reg : Maxis::OddSPRegClass) + Reserved.set(Reg); + } + + return Reserved; +} + +bool +MaxisRegisterInfo::requiresRegisterScavenging(const MachineFunction &MF) const { + return true; +} + +bool +MaxisRegisterInfo::trackLivenessAfterRegAlloc(const MachineFunction &MF) const { + return true; +} + +// FrameIndex represent objects inside a abstract stack. +// We must replace FrameIndex with an stack/frame pointer +// direct reference. +void MaxisRegisterInfo:: +eliminateFrameIndex(MachineBasicBlock::iterator II, int SPAdj, + unsigned FIOperandNum, RegScavenger *RS) const { + MachineInstr &MI = *II; + MachineFunction &MF = *MI.getParent()->getParent(); + + DEBUG(errs() << "\nFunction : " << MF.getName() << "\n"; + errs() << "<--------->\n" << MI); + + int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); + uint64_t stackSize = MF.getFrameInfo().getStackSize(); + int64_t spOffset = MF.getFrameInfo().getObjectOffset(FrameIndex); + + DEBUG(errs() << "FrameIndex : " << FrameIndex << "\n" + << "spOffset : " << spOffset << "\n" + << "stackSize : " << stackSize << "\n" + << "alignment : " + << MF.getFrameInfo().getObjectAlignment(FrameIndex) << "\n"); + + eliminateFI(MI, FIOperandNum, FrameIndex, stackSize, spOffset); +} + +unsigned MaxisRegisterInfo:: +getFrameRegister(const MachineFunction &MF) const { + const MaxisSubtarget &Subtarget = MF.getSubtarget(); + const TargetFrameLowering *TFI = Subtarget.getFrameLowering(); + bool IsN64 = + static_cast(MF.getTarget()).getABI().IsN64(); + + if (Subtarget.inMaxis16Mode()) + return TFI->hasFP(MF) ? Maxis::S0 : Maxis::SP; + else + return TFI->hasFP(MF) ? (IsN64 ? Maxis::FP_64 : Maxis::FP) : + (IsN64 ? Maxis::SP_64 : Maxis::SP); +} + +bool MaxisRegisterInfo::canRealignStack(const MachineFunction &MF) const { + // Avoid realigning functions that explicitly do not want to be realigned. + // Normally, we should report an error when a function should be dynamically + // realigned but also has the attribute no-realign-stack. Unfortunately, + // with this attribute, MachineFrameInfo clamps each new object's alignment + // to that of the stack's alignment as specified by the ABI. As a result, + // the information of whether we have objects with larger alignment + // requirement than the stack's alignment is already lost at this point. + if (!TargetRegisterInfo::canRealignStack(MF)) + return false; + + const MaxisSubtarget &Subtarget = MF.getSubtarget(); + unsigned FP = Subtarget.isGP32bit() ? Maxis::FP : Maxis::FP_64; + unsigned BP = Subtarget.isGP32bit() ? Maxis::S7 : Maxis::S7_64; + + // Support dynamic stack realignment only for targets with standard encoding. + if (!Subtarget.hasStandardEncoding()) + return false; + + // We can't perform dynamic stack realignment if we can't reserve the + // frame pointer register. + if (!MF.getRegInfo().canReserveReg(FP)) + return false; + + // We can realign the stack if we know the maximum call frame size and we + // don't have variable sized objects. + if (Subtarget.getFrameLowering()->hasReservedCallFrame(MF)) + return true; + + // We have to reserve the base pointer register in the presence of variable + // sized objects. + return MF.getRegInfo().canReserveReg(BP); +} diff --git a/lib/Target/Maxis/MaxisRegisterInfo.h b/lib/Target/Maxis/MaxisRegisterInfo.h new file mode 100644 index 00000000..144f67b3 --- /dev/null +++ b/lib/Target/Maxis/MaxisRegisterInfo.h @@ -0,0 +1,86 @@ +//===- MaxisRegisterInfo.h - Maxis Register Information Impl ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISREGISTERINFO_H +#define LLVM_LIB_TARGET_MAXIS_MAXISREGISTERINFO_H + +#include "Maxis.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include + +#define GET_REGINFO_HEADER +#include "MaxisGenRegisterInfo.inc" + +namespace llvm { + +class TargetRegisterClass; + +class MaxisRegisterInfo : public MaxisGenRegisterInfo { +public: + enum class MaxisPtrClass { + /// The default register class for integer values. + Default = 0, + /// The subset of registers permitted in certain microMAXIS instructions + /// such as lw16. + GPR16MM = 1, + /// The stack pointer only. + StackPointer = 2, + /// The global pointer only. + GlobalPointer = 3, + }; + + MaxisRegisterInfo(); + + /// Get PIC indirect call register + static unsigned getPICCallReg(); + + /// Code Generation virtual methods... + const TargetRegisterClass *getPointerRegClass(const MachineFunction &MF, + unsigned Kind) const override; + + unsigned getRegPressureLimit(const TargetRegisterClass *RC, + MachineFunction &MF) const override; + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + const uint32_t *getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID) const override; + static const uint32_t *getMaxis16RetHelperMask(); + + BitVector getReservedRegs(const MachineFunction &MF) const override; + + bool requiresRegisterScavenging(const MachineFunction &MF) const override; + + bool trackLivenessAfterRegAlloc(const MachineFunction &MF) const override; + + /// Stack Frame Processing Methods + void eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + // Stack realignment queries. + bool canRealignStack(const MachineFunction &MF) const override; + + /// Debug information queries. + unsigned getFrameRegister(const MachineFunction &MF) const override; + + /// \brief Return GPR register class. + virtual const TargetRegisterClass *intRegClass(unsigned Size) const = 0; + +private: + virtual void eliminateFI(MachineBasicBlock::iterator II, unsigned OpNo, + int FrameIndex, uint64_t StackSize, + int64_t SPOffset) const = 0; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISREGISTERINFO_H diff --git a/lib/Target/Maxis/MaxisRegisterInfo.td b/lib/Target/Maxis/MaxisRegisterInfo.td new file mode 100644 index 00000000..b3225d84 --- /dev/null +++ b/lib/Target/Maxis/MaxisRegisterInfo.td @@ -0,0 +1,732 @@ +//===-- MaxisRegisterInfo.td - Maxis Register defs -----------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Declarations that describe the MAXIS register file +//===----------------------------------------------------------------------===// +let Namespace = "Maxis" in { +def sub_32 : SubRegIndex<32>; +def sub_64 : SubRegIndex<64>; +def sub_lo : SubRegIndex<32>; +def sub_hi : SubRegIndex<32, 32>; +def sub_dsp16_19 : SubRegIndex<4, 16>; +def sub_dsp20 : SubRegIndex<1, 20>; +def sub_dsp21 : SubRegIndex<1, 21>; +def sub_dsp22 : SubRegIndex<1, 22>; +def sub_dsp23 : SubRegIndex<1, 23>; +} + +class Unallocatable { + bit isAllocatable = 0; +} + +// We have banks of 32 registers each. +class MaxisReg Enc, string n> : Register { + let HWEncoding = Enc; + let Namespace = "Maxis"; +} + +class MaxisRegWithSubRegs Enc, string n, list subregs> + : RegisterWithSubRegs { + let HWEncoding = Enc; + let Namespace = "Maxis"; +} + +// Maxis CPU Registers. +class MaxisGPRReg Enc, string n> : MaxisReg; + +// Maxis 64-bit CPU Registers +class Maxis64GPRReg Enc, string n, list subregs> + : MaxisRegWithSubRegs { + let SubRegIndices = [sub_32]; +} + +// Maxis 32-bit FPU Registers +class FPR Enc, string n> : MaxisReg; + +// Maxis 64-bit (aliased) FPU Registers +class AFPR Enc, string n, list subregs> + : MaxisRegWithSubRegs { + let SubRegIndices = [sub_lo, sub_hi]; + let CoveredBySubRegs = 1; +} + +class AFPR64 Enc, string n, list subregs> + : MaxisRegWithSubRegs { + let SubRegIndices = [sub_lo, sub_hi]; + let CoveredBySubRegs = 1; +} + +// Maxis 128-bit (aliased) MSA Registers +class AFPR128 Enc, string n, list subregs> + : MaxisRegWithSubRegs { + let SubRegIndices = [sub_64]; +} + +// Accumulator Registers +class ACCReg Enc, string n, list subregs> + : MaxisRegWithSubRegs { + let SubRegIndices = [sub_lo, sub_hi]; + let CoveredBySubRegs = 1; +} + +// Maxis Hardware Registers +class HWR Enc, string n> : MaxisReg; + +//===----------------------------------------------------------------------===// +// Registers +//===----------------------------------------------------------------------===// + +let Namespace = "Maxis" in { + // General Purpose Registers + def ZERO : MaxisGPRReg< 0, "zero">, DwarfRegNum<[0]>; + def AT : MaxisGPRReg< 1, "1">, DwarfRegNum<[1]>; + def V0 : MaxisGPRReg< 2, "2">, DwarfRegNum<[2]>; + def V1 : MaxisGPRReg< 3, "3">, DwarfRegNum<[3]>; + def A0 : MaxisGPRReg< 4, "4">, DwarfRegNum<[4]>; + def A1 : MaxisGPRReg< 5, "5">, DwarfRegNum<[5]>; + def A2 : MaxisGPRReg< 6, "6">, DwarfRegNum<[6]>; + def A3 : MaxisGPRReg< 7, "7">, DwarfRegNum<[7]>; + def T0 : MaxisGPRReg< 8, "8">, DwarfRegNum<[8]>; + def T1 : MaxisGPRReg< 9, "9">, DwarfRegNum<[9]>; + def T2 : MaxisGPRReg< 10, "10">, DwarfRegNum<[10]>; + def T3 : MaxisGPRReg< 11, "11">, DwarfRegNum<[11]>; + def T4 : MaxisGPRReg< 12, "12">, DwarfRegNum<[12]>; + def T5 : MaxisGPRReg< 13, "13">, DwarfRegNum<[13]>; + def T6 : MaxisGPRReg< 14, "14">, DwarfRegNum<[14]>; + def T7 : MaxisGPRReg< 15, "15">, DwarfRegNum<[15]>; + def S0 : MaxisGPRReg< 16, "16">, DwarfRegNum<[16]>; + def S1 : MaxisGPRReg< 17, "17">, DwarfRegNum<[17]>; + def S2 : MaxisGPRReg< 18, "18">, DwarfRegNum<[18]>; + def S3 : MaxisGPRReg< 19, "19">, DwarfRegNum<[19]>; + def S4 : MaxisGPRReg< 20, "20">, DwarfRegNum<[20]>; + def S5 : MaxisGPRReg< 21, "21">, DwarfRegNum<[21]>; + def S6 : MaxisGPRReg< 22, "22">, DwarfRegNum<[22]>; + def S7 : MaxisGPRReg< 23, "23">, DwarfRegNum<[23]>; + def T8 : MaxisGPRReg< 24, "24">, DwarfRegNum<[24]>; + def T9 : MaxisGPRReg< 25, "25">, DwarfRegNum<[25]>; + def K0 : MaxisGPRReg< 26, "26">, DwarfRegNum<[26]>; + def K1 : MaxisGPRReg< 27, "27">, DwarfRegNum<[27]>; + def GP : MaxisGPRReg< 28, "gp">, DwarfRegNum<[28]>; + def SP : MaxisGPRReg< 29, "sp">, DwarfRegNum<[29]>; + def FP : MaxisGPRReg< 30, "fp">, DwarfRegNum<[30]>; + def RA : MaxisGPRReg< 31, "ra">, DwarfRegNum<[31]>; + + // General Purpose 64-bit Registers + def ZERO_64 : Maxis64GPRReg< 0, "zero", [ZERO]>, DwarfRegNum<[0]>; + def AT_64 : Maxis64GPRReg< 1, "1", [AT]>, DwarfRegNum<[1]>; + def V0_64 : Maxis64GPRReg< 2, "2", [V0]>, DwarfRegNum<[2]>; + def V1_64 : Maxis64GPRReg< 3, "3", [V1]>, DwarfRegNum<[3]>; + def A0_64 : Maxis64GPRReg< 4, "4", [A0]>, DwarfRegNum<[4]>; + def A1_64 : Maxis64GPRReg< 5, "5", [A1]>, DwarfRegNum<[5]>; + def A2_64 : Maxis64GPRReg< 6, "6", [A2]>, DwarfRegNum<[6]>; + def A3_64 : Maxis64GPRReg< 7, "7", [A3]>, DwarfRegNum<[7]>; + def T0_64 : Maxis64GPRReg< 8, "8", [T0]>, DwarfRegNum<[8]>; + def T1_64 : Maxis64GPRReg< 9, "9", [T1]>, DwarfRegNum<[9]>; + def T2_64 : Maxis64GPRReg< 10, "10", [T2]>, DwarfRegNum<[10]>; + def T3_64 : Maxis64GPRReg< 11, "11", [T3]>, DwarfRegNum<[11]>; + def T4_64 : Maxis64GPRReg< 12, "12", [T4]>, DwarfRegNum<[12]>; + def T5_64 : Maxis64GPRReg< 13, "13", [T5]>, DwarfRegNum<[13]>; + def T6_64 : Maxis64GPRReg< 14, "14", [T6]>, DwarfRegNum<[14]>; + def T7_64 : Maxis64GPRReg< 15, "15", [T7]>, DwarfRegNum<[15]>; + def S0_64 : Maxis64GPRReg< 16, "16", [S0]>, DwarfRegNum<[16]>; + def S1_64 : Maxis64GPRReg< 17, "17", [S1]>, DwarfRegNum<[17]>; + def S2_64 : Maxis64GPRReg< 18, "18", [S2]>, DwarfRegNum<[18]>; + def S3_64 : Maxis64GPRReg< 19, "19", [S3]>, DwarfRegNum<[19]>; + def S4_64 : Maxis64GPRReg< 20, "20", [S4]>, DwarfRegNum<[20]>; + def S5_64 : Maxis64GPRReg< 21, "21", [S5]>, DwarfRegNum<[21]>; + def S6_64 : Maxis64GPRReg< 22, "22", [S6]>, DwarfRegNum<[22]>; + def S7_64 : Maxis64GPRReg< 23, "23", [S7]>, DwarfRegNum<[23]>; + def T8_64 : Maxis64GPRReg< 24, "24", [T8]>, DwarfRegNum<[24]>; + def T9_64 : Maxis64GPRReg< 25, "25", [T9]>, DwarfRegNum<[25]>; + def K0_64 : Maxis64GPRReg< 26, "26", [K0]>, DwarfRegNum<[26]>; + def K1_64 : Maxis64GPRReg< 27, "27", [K1]>, DwarfRegNum<[27]>; + def GP_64 : Maxis64GPRReg< 28, "gp", [GP]>, DwarfRegNum<[28]>; + def SP_64 : Maxis64GPRReg< 29, "sp", [SP]>, DwarfRegNum<[29]>; + def FP_64 : Maxis64GPRReg< 30, "fp", [FP]>, DwarfRegNum<[30]>; + def RA_64 : Maxis64GPRReg< 31, "ra", [RA]>, DwarfRegNum<[31]>; + + /// Maxis Single point precision FPU Registers + foreach I = 0-31 in + def F#I : FPR, DwarfRegNum<[!add(I, 32)]>; + + // Higher half of 64-bit FP registers. + foreach I = 0-31 in + def F_HI#I : FPR, DwarfRegNum<[!add(I, 32)]>; + + /// Maxis Double point precision FPU Registers (aliased + /// with the single precision to hold 64 bit values) + foreach I = 0-15 in + def D#I : AFPR("F"#!shl(I, 1)), + !cast("F"#!add(!shl(I, 1), 1))]>; + + /// Maxis Double point precision FPU Registers in MFP64 mode. + foreach I = 0-31 in + def D#I#_64 : AFPR64("F"#I), !cast("F_HI"#I)]>, + DwarfRegNum<[!add(I, 32)]>; + + /// Maxis MSA registers + /// MSA and FPU cannot both be present unless the FPU has 64-bit registers + foreach I = 0-31 in + def W#I : AFPR128("D"#I#"_64")]>, + DwarfRegNum<[!add(I, 32)]>; + + // Hi/Lo registers + def HI0 : MaxisReg<0, "ac0">, DwarfRegNum<[64]>; + def HI1 : MaxisReg<1, "ac1">, DwarfRegNum<[176]>; + def HI2 : MaxisReg<2, "ac2">, DwarfRegNum<[178]>; + def HI3 : MaxisReg<3, "ac3">, DwarfRegNum<[180]>; + def LO0 : MaxisReg<0, "ac0">, DwarfRegNum<[65]>; + def LO1 : MaxisReg<1, "ac1">, DwarfRegNum<[177]>; + def LO2 : MaxisReg<2, "ac2">, DwarfRegNum<[179]>; + def LO3 : MaxisReg<3, "ac3">, DwarfRegNum<[181]>; + + let SubRegIndices = [sub_32] in { + def HI0_64 : RegisterWithSubRegs<"hi", [HI0]>; + def LO0_64 : RegisterWithSubRegs<"lo", [LO0]>; + } + + // FP control registers. + foreach I = 0-31 in + def FCR#I : MaxisReg<#I, ""#I>; + + // FP condition code registers. + foreach I = 0-7 in + def FCC#I : MaxisReg<#I, "fcc"#I>; + + // COP0 registers. + foreach I = 0-31 in + def COP0#I : MaxisReg<#I, ""#I>; + + // COP2 registers. + foreach I = 0-31 in + def COP2#I : MaxisReg<#I, ""#I>; + + // COP3 registers. + foreach I = 0-31 in + def COP3#I : MaxisReg<#I, ""#I>; + + // PC register + def PC : Register<"pc">; + + // Hardware registers + def HWR0 : MaxisReg<0, "hwr_cpunum">; + def HWR1 : MaxisReg<1, "hwr_synci_step">; + def HWR2 : MaxisReg<2, "hwr_cc">; + def HWR3 : MaxisReg<3, "hwr_ccres">; + + foreach I = 4-31 in + def HWR#I : MaxisReg<#I, ""#I>; + + // Accum registers + foreach I = 0-3 in + def AC#I : ACCReg<#I, "ac"#I, + [!cast("LO"#I), !cast("HI"#I)]>; + + def AC0_64 : ACCReg<0, "ac0", [LO0_64, HI0_64]>; + + // DSP-ASE control register fields. + def DSPPos : Register<"">; + def DSPSCount : Register<"">; + def DSPCarry : Register<"">; + def DSPEFI : Register<"">; + def DSPOutFlag16_19 : Register<"">; + def DSPOutFlag20 : Register<"">; + def DSPOutFlag21 : Register<"">; + def DSPOutFlag22 : Register<"">; + def DSPOutFlag23 : Register<"">; + def DSPCCond : Register<"">; + + let SubRegIndices = [sub_dsp16_19, sub_dsp20, sub_dsp21, sub_dsp22, + sub_dsp23] in + def DSPOutFlag : RegisterWithSubRegs<"", [DSPOutFlag16_19, DSPOutFlag20, + DSPOutFlag21, DSPOutFlag22, + DSPOutFlag23]>; + + // MSA-ASE control registers. + def MSAIR : MaxisReg<0, "0">; + def MSACSR : MaxisReg<1, "1">; + def MSAAccess : MaxisReg<2, "2">; + def MSASave : MaxisReg<3, "3">; + def MSAModify : MaxisReg<4, "4">; + def MSARequest : MaxisReg<5, "5">; + def MSAMap : MaxisReg<6, "6">; + def MSAUnmap : MaxisReg<7, "7">; + + // Octeon multiplier and product registers + def MPL0 : MaxisReg<0, "mpl0">; + def MPL1 : MaxisReg<1, "mpl1">; + def MPL2 : MaxisReg<2, "mpl2">; + def P0 : MaxisReg<0, "p0">; + def P1 : MaxisReg<1, "p1">; + def P2 : MaxisReg<2, "p2">; + +} + +//===----------------------------------------------------------------------===// +// Register Classes +//===----------------------------------------------------------------------===// + +class GPR32Class regTypes> : + RegisterClass<"Maxis", regTypes, 32, (add + // Reserved + ZERO, AT, + // Return Values and Arguments + V0, V1, A0, A1, A2, A3, + // Not preserved across procedure calls + T0, T1, T2, T3, T4, T5, T6, T7, + // Callee save + S0, S1, S2, S3, S4, S5, S6, S7, + // Not preserved across procedure calls + T8, T9, + // Reserved + K0, K1, GP, SP, FP, RA)>; + +def GPR32 : GPR32Class<[i32]>; + +def GPR32ZERO : RegisterClass<"Maxis", [i32], 32, (add + // Reserved + ZERO)>; + +def GPR32NONZERO : RegisterClass<"Maxis", [i32], 32, (add + // Reserved + AT, + // Return Values and Arguments + V0, V1, A0, A1, A2, A3, + // Not preserved across procedure calls + T0, T1, T2, T3, T4, T5, T6, T7, + // Callee save + S0, S1, S2, S3, S4, S5, S6, S7, + // Not preserved across procedure calls + T8, T9, + // Reserved + K0, K1, GP, SP, FP, RA)>; + +def DSPR : GPR32Class<[v4i8, v2i16]>; + +def GPRMM16 : RegisterClass<"Maxis", [i32], 32, (add + // Callee save + S0, S1, + // Return Values and Arguments + V0, V1, A0, A1, A2, A3)>; + +def GPRMM16Zero : RegisterClass<"Maxis", [i32], 32, (add + // Reserved + ZERO, + // Callee save + S1, + // Return Values and Arguments + V0, V1, A0, A1, A2, A3)>; + +def GPRMM16MoveP : RegisterClass<"Maxis", [i32], 32, (add + // Reserved + ZERO, + // Callee save + S1, + // Return Values and Arguments + V0, V1, + // Callee save + S0, S2, S3, S4)>; + +def GPR64 : RegisterClass<"Maxis", [i64], 64, (add + // Reserved + ZERO_64, AT_64, + // Return Values and Arguments + V0_64, V1_64, A0_64, A1_64, A2_64, A3_64, + // Not preserved across procedure calls + T0_64, T1_64, T2_64, T3_64, T4_64, T5_64, T6_64, T7_64, + // Callee save + S0_64, S1_64, S2_64, S3_64, S4_64, S5_64, S6_64, S7_64, + // Not preserved across procedure calls + T8_64, T9_64, + // Reserved + K0_64, K1_64, GP_64, SP_64, FP_64, RA_64)>; + +def CPU16Regs : RegisterClass<"Maxis", [i32], 32, (add + // Return Values and Arguments + V0, V1, A0, A1, A2, A3, + // Callee save + S0, S1)>; + +def CPU16RegsPlusSP : RegisterClass<"Maxis", [i32], 32, (add + // Return Values and Arguments + V0, V1, A0, A1, A2, A3, + // Callee save + S0, S1, + SP)>; + +def CPURAReg : RegisterClass<"Maxis", [i32], 32, (add RA)>, Unallocatable; + +def CPUSPReg : RegisterClass<"Maxis", [i32], 32, (add SP)>, Unallocatable; + +// 64bit fp: +// * FGR64 - 32 64-bit registers +// * AFGR64 - 16 32-bit even registers (32-bit FP Mode) +// +// 32bit fp: +// * FGR32 - 16 32-bit even registers +// * FGR32 - 32 32-bit registers (single float only mode) +def FGR32 : RegisterClass<"Maxis", [f32], 32, (sequence "F%u", 0, 31)>; + +def FGRH32 : RegisterClass<"Maxis", [f32], 32, (sequence "F_HI%u", 0, 31)>, + Unallocatable; + +def AFGR64 : RegisterClass<"Maxis", [f64], 64, (add + // Return Values and Arguments + D0, D1, + // Not preserved across procedure calls + D2, D3, D4, D5, + // Return Values and Arguments + D6, D7, + // Not preserved across procedure calls + D8, D9, + // Callee save + D10, D11, D12, D13, D14, D15)>; + +def FGR64 : RegisterClass<"Maxis", [f64], 64, (sequence "D%u_64", 0, 31)>; + +// Used to reserve odd registers when given -mattr=+nooddspreg +// FIXME: Remove double precision registers from this set. +def OddSP : RegisterClass<"Maxis", [f32], 32, + (add (decimate (sequence "F%u", 1, 31), 2), + (decimate (sequence "F_HI%u", 1, 31), 2), + (decimate (sequence "D%u", 1, 15), 2), + (decimate (sequence "D%u_64", 1, 31), 2))>, + Unallocatable; + +// FP control registers. +def CCR : RegisterClass<"Maxis", [i32], 32, (sequence "FCR%u", 0, 31)>, + Unallocatable; + +// FP condition code registers. +def FCC : RegisterClass<"Maxis", [i32], 32, (sequence "FCC%u", 0, 7)>, + Unallocatable; + +// MAXIS32r6/MAXIS64r6 store FPU condition codes in normal FGR registers. +// This class allows us to represent this in codegen patterns. +def FGRCC : RegisterClass<"Maxis", [i32], 32, (sequence "F%u", 0, 31)>; + +def MSA128F16 : RegisterClass<"Maxis", [f16], 128, (sequence "W%u", 0, 31)>; + +def MSA128B: RegisterClass<"Maxis", [v16i8], 128, + (sequence "W%u", 0, 31)>; +def MSA128H: RegisterClass<"Maxis", [v8i16, v8f16], 128, + (sequence "W%u", 0, 31)>; +def MSA128W: RegisterClass<"Maxis", [v4i32, v4f32], 128, + (sequence "W%u", 0, 31)>; +def MSA128D: RegisterClass<"Maxis", [v2i64, v2f64], 128, + (sequence "W%u", 0, 31)>; +def MSA128WEvens: RegisterClass<"Maxis", [v4i32, v4f32], 128, + (decimate (sequence "W%u", 0, 31), 2)>; + +def MSACtrl: RegisterClass<"Maxis", [i32], 32, (add + MSAIR, MSACSR, MSAAccess, MSASave, MSAModify, MSARequest, MSAMap, MSAUnmap)>; + +// Hi/Lo Registers +def LO32 : RegisterClass<"Maxis", [i32], 32, (add LO0)>; +def HI32 : RegisterClass<"Maxis", [i32], 32, (add HI0)>; +def LO32DSP : RegisterClass<"Maxis", [i32], 32, (sequence "LO%u", 0, 3)>; +def HI32DSP : RegisterClass<"Maxis", [i32], 32, (sequence "HI%u", 0, 3)>; +def LO64 : RegisterClass<"Maxis", [i64], 64, (add LO0_64)>; +def HI64 : RegisterClass<"Maxis", [i64], 64, (add HI0_64)>; + +// Hardware registers +def HWRegs : RegisterClass<"Maxis", [i32], 32, (sequence "HWR%u", 0, 31)>, + Unallocatable; + +// Accumulator Registers +def ACC64 : RegisterClass<"Maxis", [untyped], 64, (add AC0)> { + let Size = 64; +} + +def ACC128 : RegisterClass<"Maxis", [untyped], 128, (add AC0_64)> { + let Size = 128; +} + +def ACC64DSP : RegisterClass<"Maxis", [untyped], 64, (sequence "AC%u", 0, 3)> { + let Size = 64; +} + +def DSPCC : RegisterClass<"Maxis", [v4i8, v2i16], 32, (add DSPCCond)>; + +// Coprocessor 0 registers. +def COP0 : RegisterClass<"Maxis", [i32], 32, (sequence "COP0%u", 0, 31)>, + Unallocatable; + +// Coprocessor 2 registers. +def COP2 : RegisterClass<"Maxis", [i32], 32, (sequence "COP2%u", 0, 31)>, + Unallocatable; + +// Coprocessor 3 registers. +def COP3 : RegisterClass<"Maxis", [i32], 32, (sequence "COP3%u", 0, 31)>, + Unallocatable; + +// Stack pointer and global pointer classes for instructions that are limited +// to a single register such as lwgp/lwsp in microMAXIS. +def SP32 : RegisterClass<"Maxis", [i32], 32, (add SP)>, Unallocatable; +def SP64 : RegisterClass<"Maxis", [i64], 64, (add SP_64)>, Unallocatable; +def GP32 : RegisterClass<"Maxis", [i32], 32, (add GP)>, Unallocatable; +def GP64 : RegisterClass<"Maxis", [i64], 64, (add GP_64)>, Unallocatable; + +// Octeon multiplier and product registers +def OCTEON_MPL : RegisterClass<"Maxis", [i64], 64, (add MPL0, MPL1, MPL2)>, + Unallocatable; +def OCTEON_P : RegisterClass<"Maxis", [i64], 64, (add P0, P1, P2)>, + Unallocatable; + +// Register Operands. + +class MaxisAsmRegOperand : AsmOperandClass { + let ParserMethod = "parseAnyRegister"; +} + +def GPR64AsmOperand : MaxisAsmRegOperand { + let Name = "GPR64AsmReg"; + let PredicateMethod = "isGPRAsmReg"; +} + +def GPR32ZeroAsmOperand : MaxisAsmRegOperand { + let Name = "GPR32ZeroAsmReg"; + let PredicateMethod = "isGPRZeroAsmReg"; +} + +def GPR32NonZeroAsmOperand : MaxisAsmRegOperand { + let Name = "GPR32NonZeroAsmReg"; + let PredicateMethod = "isGPRNonZeroAsmReg"; +} + +def GPR32AsmOperand : MaxisAsmRegOperand { + let Name = "GPR32AsmReg"; + let PredicateMethod = "isGPRAsmReg"; +} + +def GPRMM16AsmOperand : MaxisAsmRegOperand { + let Name = "GPRMM16AsmReg"; + let PredicateMethod = "isMM16AsmReg"; +} + +def GPRMM16AsmOperandZero : MaxisAsmRegOperand { + let Name = "GPRMM16AsmRegZero"; + let PredicateMethod = "isMM16AsmRegZero"; +} + +def GPRMM16AsmOperandMoveP : MaxisAsmRegOperand { + let Name = "GPRMM16AsmRegMoveP"; + let PredicateMethod = "isMM16AsmRegMoveP"; +} + +def ACC64DSPAsmOperand : MaxisAsmRegOperand { + let Name = "ACC64DSPAsmReg"; + let PredicateMethod = "isACCAsmReg"; +} + +def HI32DSPAsmOperand : MaxisAsmRegOperand { + let Name = "HI32DSPAsmReg"; + let PredicateMethod = "isACCAsmReg"; +} + +def LO32DSPAsmOperand : MaxisAsmRegOperand { + let Name = "LO32DSPAsmReg"; + let PredicateMethod = "isACCAsmReg"; +} + +def CCRAsmOperand : MaxisAsmRegOperand { + let Name = "CCRAsmReg"; +} + +def AFGR64AsmOperand : MaxisAsmRegOperand { + let Name = "AFGR64AsmReg"; + let PredicateMethod = "isFGRAsmReg"; +} + +def StrictlyAFGR64AsmOperand : MaxisAsmRegOperand { + let Name = "StrictlyAFGR64AsmReg"; + let PredicateMethod = "isStrictlyFGRAsmReg"; +} + +def FGR64AsmOperand : MaxisAsmRegOperand { + let Name = "FGR64AsmReg"; + let PredicateMethod = "isFGRAsmReg"; +} + +def StrictlyFGR64AsmOperand : MaxisAsmRegOperand { + let Name = "StrictlyFGR64AsmReg"; + let PredicateMethod = "isStrictlyFGRAsmReg"; +} + +def FGR32AsmOperand : MaxisAsmRegOperand { + let Name = "FGR32AsmReg"; + let PredicateMethod = "isFGRAsmReg"; +} + +def StrictlyFGR32AsmOperand : MaxisAsmRegOperand { + let Name = "StrictlyFGR32AsmReg"; + let PredicateMethod = "isStrictlyFGRAsmReg"; +} + +def FGRH32AsmOperand : MaxisAsmRegOperand { + let Name = "FGRH32AsmReg"; + let PredicateMethod = "isFGRAsmReg"; +} + +def FCCRegsAsmOperand : MaxisAsmRegOperand { + let Name = "FCCAsmReg"; +} + +def MSA128AsmOperand : MaxisAsmRegOperand { + let Name = "MSA128AsmReg"; +} + +def MSACtrlAsmOperand : MaxisAsmRegOperand { + let Name = "MSACtrlAsmReg"; +} + +def GPR32ZeroOpnd : RegisterOperand { + let ParserMatchClass = GPR32ZeroAsmOperand; +} + +def GPR32NonZeroOpnd : RegisterOperand { + let ParserMatchClass = GPR32NonZeroAsmOperand; +} + +def GPR32Opnd : RegisterOperand { + let ParserMatchClass = GPR32AsmOperand; +} + +def GPRMM16Opnd : RegisterOperand { + let ParserMatchClass = GPRMM16AsmOperand; +} + +def GPRMM16OpndZero : RegisterOperand { + let ParserMatchClass = GPRMM16AsmOperandZero; +} + +def GPRMM16OpndMoveP : RegisterOperand { + let ParserMatchClass = GPRMM16AsmOperandMoveP; + let EncoderMethod = "getMovePRegSingleOpValue"; +} + +def GPR64Opnd : RegisterOperand { + let ParserMatchClass = GPR64AsmOperand; +} + +def DSPROpnd : RegisterOperand { + let ParserMatchClass = GPR32AsmOperand; +} + +def CCROpnd : RegisterOperand { + let ParserMatchClass = CCRAsmOperand; +} + +def HWRegsAsmOperand : MaxisAsmRegOperand { + let Name = "HWRegsAsmReg"; +} + +def COP0AsmOperand : MaxisAsmRegOperand { + let Name = "COP0AsmReg"; +} + +def COP2AsmOperand : MaxisAsmRegOperand { + let Name = "COP2AsmReg"; +} + +def COP3AsmOperand : MaxisAsmRegOperand { + let Name = "COP3AsmReg"; +} + +def HWRegsOpnd : RegisterOperand { + let ParserMatchClass = HWRegsAsmOperand; +} + +def AFGR64Opnd : RegisterOperand { + let ParserMatchClass = AFGR64AsmOperand; +} + +def StrictlyAFGR64Opnd : RegisterOperand { + let ParserMatchClass = StrictlyAFGR64AsmOperand; +} + +def FGR64Opnd : RegisterOperand { + let ParserMatchClass = FGR64AsmOperand; +} + +def StrictlyFGR64Opnd : RegisterOperand { + let ParserMatchClass = StrictlyFGR64AsmOperand; +} + +def FGR32Opnd : RegisterOperand { + let ParserMatchClass = FGR32AsmOperand; +} + +def StrictlyFGR32Opnd : RegisterOperand { + let ParserMatchClass = StrictlyFGR32AsmOperand; +} + +def FGRCCOpnd : RegisterOperand { + // The assembler doesn't use register classes so we can re-use + // FGR32AsmOperand. + let ParserMatchClass = FGR32AsmOperand; +} + +def FGRH32Opnd : RegisterOperand { + let ParserMatchClass = FGRH32AsmOperand; +} + +def FCCRegsOpnd : RegisterOperand { + let ParserMatchClass = FCCRegsAsmOperand; +} + +def LO32DSPOpnd : RegisterOperand { + let ParserMatchClass = LO32DSPAsmOperand; +} + +def HI32DSPOpnd : RegisterOperand { + let ParserMatchClass = HI32DSPAsmOperand; +} + +def ACC64DSPOpnd : RegisterOperand { + let ParserMatchClass = ACC64DSPAsmOperand; +} + +def COP0Opnd : RegisterOperand { + let ParserMatchClass = COP0AsmOperand; +} + +def COP2Opnd : RegisterOperand { + let ParserMatchClass = COP2AsmOperand; +} + +def COP3Opnd : RegisterOperand { + let ParserMatchClass = COP3AsmOperand; +} + +def MSA128F16Opnd : RegisterOperand { + let ParserMatchClass = MSA128AsmOperand; +} + +def MSA128BOpnd : RegisterOperand { + let ParserMatchClass = MSA128AsmOperand; +} + +def MSA128HOpnd : RegisterOperand { + let ParserMatchClass = MSA128AsmOperand; +} + +def MSA128WOpnd : RegisterOperand { + let ParserMatchClass = MSA128AsmOperand; +} + +def MSA128DOpnd : RegisterOperand { + let ParserMatchClass = MSA128AsmOperand; +} + +def MSA128CROpnd : RegisterOperand { + let ParserMatchClass = MSACtrlAsmOperand; +} diff --git a/lib/Target/Maxis/MaxisSEFrameLowering.cpp b/lib/Target/Maxis/MaxisSEFrameLowering.cpp new file mode 100644 index 00000000..9f6e7020 --- /dev/null +++ b/lib/Target/Maxis/MaxisSEFrameLowering.cpp @@ -0,0 +1,915 @@ +//===- MaxisSEFrameLowering.cpp - Maxis32/64 Frame Information --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis32/64 implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "MaxisSEFrameLowering.h" +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MaxisMachineFunction.h" +#include "MaxisRegisterInfo.h" +#include "MaxisSEInstrInfo.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/Function.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MachineLocation.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include +#include +#include +#include + +using namespace llvm; + +static std::pair getMFHiLoOpc(unsigned Src) { + if (Maxis::ACC64RegClass.contains(Src)) + return std::make_pair((unsigned)Maxis::PseudoMFHI, + (unsigned)Maxis::PseudoMFLO); + + if (Maxis::ACC64DSPRegClass.contains(Src)) + return std::make_pair((unsigned)Maxis::MFHI_DSP, (unsigned)Maxis::MFLO_DSP); + + if (Maxis::ACC128RegClass.contains(Src)) + return std::make_pair((unsigned)Maxis::PseudoMFHI64, + (unsigned)Maxis::PseudoMFLO64); + + return std::make_pair(0, 0); +} + +namespace { + +/// Helper class to expand pseudos. +class ExpandPseudo { +public: + ExpandPseudo(MachineFunction &MF); + bool expand(); + +private: + using Iter = MachineBasicBlock::iterator; + + bool expandInstr(MachineBasicBlock &MBB, Iter I); + void expandLoadCCond(MachineBasicBlock &MBB, Iter I); + void expandStoreCCond(MachineBasicBlock &MBB, Iter I); + void expandLoadACC(MachineBasicBlock &MBB, Iter I, unsigned RegSize); + void expandStoreACC(MachineBasicBlock &MBB, Iter I, unsigned MFHiOpc, + unsigned MFLoOpc, unsigned RegSize); + bool expandCopy(MachineBasicBlock &MBB, Iter I); + bool expandCopyACC(MachineBasicBlock &MBB, Iter I, unsigned MFHiOpc, + unsigned MFLoOpc); + bool expandBuildPairF64(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, bool FP64) const; + bool expandExtractElementF64(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, bool FP64) const; + + MachineFunction &MF; + MachineRegisterInfo &MRI; + const MaxisSubtarget &Subtarget; + const MaxisSEInstrInfo &TII; + const MaxisRegisterInfo &RegInfo; +}; + +} // end anonymous namespace + +ExpandPseudo::ExpandPseudo(MachineFunction &MF_) + : MF(MF_), MRI(MF.getRegInfo()), + Subtarget(static_cast(MF.getSubtarget())), + TII(*static_cast(Subtarget.getInstrInfo())), + RegInfo(*Subtarget.getRegisterInfo()) {} + +bool ExpandPseudo::expand() { + bool Expanded = false; + + for (auto &MBB : MF) { + for (Iter I = MBB.begin(), End = MBB.end(); I != End;) + Expanded |= expandInstr(MBB, I++); + } + + return Expanded; +} + +bool ExpandPseudo::expandInstr(MachineBasicBlock &MBB, Iter I) { + switch(I->getOpcode()) { + case Maxis::LOAD_CCOND_DSP: + expandLoadCCond(MBB, I); + break; + case Maxis::STORE_CCOND_DSP: + expandStoreCCond(MBB, I); + break; + case Maxis::LOAD_ACC64: + case Maxis::LOAD_ACC64DSP: + expandLoadACC(MBB, I, 4); + break; + case Maxis::LOAD_ACC128: + expandLoadACC(MBB, I, 8); + break; + case Maxis::STORE_ACC64: + expandStoreACC(MBB, I, Maxis::PseudoMFHI, Maxis::PseudoMFLO, 4); + break; + case Maxis::STORE_ACC64DSP: + expandStoreACC(MBB, I, Maxis::MFHI_DSP, Maxis::MFLO_DSP, 4); + break; + case Maxis::STORE_ACC128: + expandStoreACC(MBB, I, Maxis::PseudoMFHI64, Maxis::PseudoMFLO64, 8); + break; + case Maxis::BuildPairF64: + if (expandBuildPairF64(MBB, I, false)) + MBB.erase(I); + return false; + case Maxis::BuildPairF64_64: + if (expandBuildPairF64(MBB, I, true)) + MBB.erase(I); + return false; + case Maxis::ExtractElementF64: + if (expandExtractElementF64(MBB, I, false)) + MBB.erase(I); + return false; + case Maxis::ExtractElementF64_64: + if (expandExtractElementF64(MBB, I, true)) + MBB.erase(I); + return false; + case TargetOpcode::COPY: + if (!expandCopy(MBB, I)) + return false; + break; + default: + return false; + } + + MBB.erase(I); + return true; +} + +void ExpandPseudo::expandLoadCCond(MachineBasicBlock &MBB, Iter I) { + // load $vr, FI + // copy ccond, $vr + + assert(I->getOperand(0).isReg() && I->getOperand(1).isFI()); + + const TargetRegisterClass *RC = RegInfo.intRegClass(4); + unsigned VR = MRI.createVirtualRegister(RC); + unsigned Dst = I->getOperand(0).getReg(), FI = I->getOperand(1).getIndex(); + + TII.loadRegFromStack(MBB, I, VR, FI, RC, &RegInfo, 0); + BuildMI(MBB, I, I->getDebugLoc(), TII.get(TargetOpcode::COPY), Dst) + .addReg(VR, RegState::Kill); +} + +void ExpandPseudo::expandStoreCCond(MachineBasicBlock &MBB, Iter I) { + // copy $vr, ccond + // store $vr, FI + + assert(I->getOperand(0).isReg() && I->getOperand(1).isFI()); + + const TargetRegisterClass *RC = RegInfo.intRegClass(4); + unsigned VR = MRI.createVirtualRegister(RC); + unsigned Src = I->getOperand(0).getReg(), FI = I->getOperand(1).getIndex(); + + BuildMI(MBB, I, I->getDebugLoc(), TII.get(TargetOpcode::COPY), VR) + .addReg(Src, getKillRegState(I->getOperand(0).isKill())); + TII.storeRegToStack(MBB, I, VR, true, FI, RC, &RegInfo, 0); +} + +void ExpandPseudo::expandLoadACC(MachineBasicBlock &MBB, Iter I, + unsigned RegSize) { + // load $vr0, FI + // copy lo, $vr0 + // load $vr1, FI + 4 + // copy hi, $vr1 + + assert(I->getOperand(0).isReg() && I->getOperand(1).isFI()); + + const TargetRegisterClass *RC = RegInfo.intRegClass(RegSize); + unsigned VR0 = MRI.createVirtualRegister(RC); + unsigned VR1 = MRI.createVirtualRegister(RC); + unsigned Dst = I->getOperand(0).getReg(), FI = I->getOperand(1).getIndex(); + unsigned Lo = RegInfo.getSubReg(Dst, Maxis::sub_lo); + unsigned Hi = RegInfo.getSubReg(Dst, Maxis::sub_hi); + DebugLoc DL = I->getDebugLoc(); + const MCInstrDesc &Desc = TII.get(TargetOpcode::COPY); + + TII.loadRegFromStack(MBB, I, VR0, FI, RC, &RegInfo, 0); + BuildMI(MBB, I, DL, Desc, Lo).addReg(VR0, RegState::Kill); + TII.loadRegFromStack(MBB, I, VR1, FI, RC, &RegInfo, RegSize); + BuildMI(MBB, I, DL, Desc, Hi).addReg(VR1, RegState::Kill); +} + +void ExpandPseudo::expandStoreACC(MachineBasicBlock &MBB, Iter I, + unsigned MFHiOpc, unsigned MFLoOpc, + unsigned RegSize) { + // mflo $vr0, src + // store $vr0, FI + // mfhi $vr1, src + // store $vr1, FI + 4 + + assert(I->getOperand(0).isReg() && I->getOperand(1).isFI()); + + const TargetRegisterClass *RC = RegInfo.intRegClass(RegSize); + unsigned VR0 = MRI.createVirtualRegister(RC); + unsigned VR1 = MRI.createVirtualRegister(RC); + unsigned Src = I->getOperand(0).getReg(), FI = I->getOperand(1).getIndex(); + unsigned SrcKill = getKillRegState(I->getOperand(0).isKill()); + DebugLoc DL = I->getDebugLoc(); + + BuildMI(MBB, I, DL, TII.get(MFLoOpc), VR0).addReg(Src); + TII.storeRegToStack(MBB, I, VR0, true, FI, RC, &RegInfo, 0); + BuildMI(MBB, I, DL, TII.get(MFHiOpc), VR1).addReg(Src, SrcKill); + TII.storeRegToStack(MBB, I, VR1, true, FI, RC, &RegInfo, RegSize); +} + +bool ExpandPseudo::expandCopy(MachineBasicBlock &MBB, Iter I) { + unsigned Src = I->getOperand(1).getReg(); + std::pair Opcodes = getMFHiLoOpc(Src); + + if (!Opcodes.first) + return false; + + return expandCopyACC(MBB, I, Opcodes.first, Opcodes.second); +} + +bool ExpandPseudo::expandCopyACC(MachineBasicBlock &MBB, Iter I, + unsigned MFHiOpc, unsigned MFLoOpc) { + // mflo $vr0, src + // copy dst_lo, $vr0 + // mfhi $vr1, src + // copy dst_hi, $vr1 + + unsigned Dst = I->getOperand(0).getReg(), Src = I->getOperand(1).getReg(); + const TargetRegisterClass *DstRC = RegInfo.getMinimalPhysRegClass(Dst); + unsigned VRegSize = RegInfo.getRegSizeInBits(*DstRC) / 16; + const TargetRegisterClass *RC = RegInfo.intRegClass(VRegSize); + unsigned VR0 = MRI.createVirtualRegister(RC); + unsigned VR1 = MRI.createVirtualRegister(RC); + unsigned SrcKill = getKillRegState(I->getOperand(1).isKill()); + unsigned DstLo = RegInfo.getSubReg(Dst, Maxis::sub_lo); + unsigned DstHi = RegInfo.getSubReg(Dst, Maxis::sub_hi); + DebugLoc DL = I->getDebugLoc(); + + BuildMI(MBB, I, DL, TII.get(MFLoOpc), VR0).addReg(Src); + BuildMI(MBB, I, DL, TII.get(TargetOpcode::COPY), DstLo) + .addReg(VR0, RegState::Kill); + BuildMI(MBB, I, DL, TII.get(MFHiOpc), VR1).addReg(Src, SrcKill); + BuildMI(MBB, I, DL, TII.get(TargetOpcode::COPY), DstHi) + .addReg(VR1, RegState::Kill); + return true; +} + +/// This method expands the same instruction that MaxisSEInstrInfo:: +/// expandBuildPairF64 does, for the case when ABI is fpxx and mthc1 is not +/// available and the case where the ABI is FP64A. It is implemented here +/// because frame indexes are eliminated before MaxisSEInstrInfo:: +/// expandBuildPairF64 is called. +bool ExpandPseudo::expandBuildPairF64(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + bool FP64) const { + // For fpxx and when mthc1 is not available, use: + // spill + reload via ldc1 + // + // The case where dmtc1 is available doesn't need to be handled here + // because it never creates a BuildPairF64 node. + // + // The FP64A ABI (fp64 with nooddspreg) must also use a spill/reload sequence + // for odd-numbered double precision values (because the lower 32-bits is + // transferred with mtc1 which is redirected to the upper half of the even + // register). Unfortunately, we have to make this decision before register + // allocation so for now we use a spill/reload sequence for all + // double-precision values in regardless of being an odd/even register. + if ((Subtarget.isABI_FPXX() && !Subtarget.hasMTHC1()) || + (FP64 && !Subtarget.useOddSPReg())) { + unsigned DstReg = I->getOperand(0).getReg(); + unsigned LoReg = I->getOperand(1).getReg(); + unsigned HiReg = I->getOperand(2).getReg(); + + // It should be impossible to have FGR64 on MAXIS-II or MAXIS32r1 (which are + // the cases where mthc1 is not available). 64-bit architectures and + // MAXIS32r2 or later can use FGR64 though. + assert(Subtarget.isGP64bit() || Subtarget.hasMTHC1() || + !Subtarget.isFP64bit()); + + const TargetRegisterClass *RC = &Maxis::GPR32RegClass; + const TargetRegisterClass *RC2 = + FP64 ? &Maxis::FGR64RegClass : &Maxis::AFGR64RegClass; + + // We re-use the same spill slot each time so that the stack frame doesn't + // grow too much in functions with a large number of moves. + int FI = MF.getInfo()->getMoveF64ViaSpillFI(RC2); + if (!Subtarget.isLittle()) + std::swap(LoReg, HiReg); + TII.storeRegToStack(MBB, I, LoReg, I->getOperand(1).isKill(), FI, RC, + &RegInfo, 0); + TII.storeRegToStack(MBB, I, HiReg, I->getOperand(2).isKill(), FI, RC, + &RegInfo, 4); + TII.loadRegFromStack(MBB, I, DstReg, FI, RC2, &RegInfo, 0); + return true; + } + + return false; +} + +/// This method expands the same instruction that MaxisSEInstrInfo:: +/// expandExtractElementF64 does, for the case when ABI is fpxx and mfhc1 is not +/// available and the case where the ABI is FP64A. It is implemented here +/// because frame indexes are eliminated before MaxisSEInstrInfo:: +/// expandExtractElementF64 is called. +bool ExpandPseudo::expandExtractElementF64(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + bool FP64) const { + const MachineOperand &Op1 = I->getOperand(1); + const MachineOperand &Op2 = I->getOperand(2); + + if ((Op1.isReg() && Op1.isUndef()) || (Op2.isReg() && Op2.isUndef())) { + unsigned DstReg = I->getOperand(0).getReg(); + BuildMI(MBB, I, I->getDebugLoc(), TII.get(Maxis::IMPLICIT_DEF), DstReg); + return true; + } + + // For fpxx and when mfhc1 is not available, use: + // spill + reload via ldc1 + // + // The case where dmfc1 is available doesn't need to be handled here + // because it never creates a ExtractElementF64 node. + // + // The FP64A ABI (fp64 with nooddspreg) must also use a spill/reload sequence + // for odd-numbered double precision values (because the lower 32-bits is + // transferred with mfc1 which is redirected to the upper half of the even + // register). Unfortunately, we have to make this decision before register + // allocation so for now we use a spill/reload sequence for all + // double-precision values in regardless of being an odd/even register. + + if ((Subtarget.isABI_FPXX() && !Subtarget.hasMTHC1()) || + (FP64 && !Subtarget.useOddSPReg())) { + unsigned DstReg = I->getOperand(0).getReg(); + unsigned SrcReg = Op1.getReg(); + unsigned N = Op2.getImm(); + int64_t Offset = 4 * (Subtarget.isLittle() ? N : (1 - N)); + + // It should be impossible to have FGR64 on MAXIS-II or MAXIS32r1 (which are + // the cases where mfhc1 is not available). 64-bit architectures and + // MAXIS32r2 or later can use FGR64 though. + assert(Subtarget.isGP64bit() || Subtarget.hasMTHC1() || + !Subtarget.isFP64bit()); + + const TargetRegisterClass *RC = + FP64 ? &Maxis::FGR64RegClass : &Maxis::AFGR64RegClass; + const TargetRegisterClass *RC2 = &Maxis::GPR32RegClass; + + // We re-use the same spill slot each time so that the stack frame doesn't + // grow too much in functions with a large number of moves. + int FI = MF.getInfo()->getMoveF64ViaSpillFI(RC); + TII.storeRegToStack(MBB, I, SrcReg, Op1.isKill(), FI, RC, &RegInfo, 0); + TII.loadRegFromStack(MBB, I, DstReg, FI, RC2, &RegInfo, Offset); + return true; + } + + return false; +} + +MaxisSEFrameLowering::MaxisSEFrameLowering(const MaxisSubtarget &STI) + : MaxisFrameLowering(STI, STI.getStackAlignment()) {} + +void MaxisSEFrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + assert(&MF.front() == &MBB && "Shrink-wrapping not yet supported"); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + const MaxisSEInstrInfo &TII = + *static_cast(STI.getInstrInfo()); + const MaxisRegisterInfo &RegInfo = + *static_cast(STI.getRegisterInfo()); + + MachineBasicBlock::iterator MBBI = MBB.begin(); + DebugLoc dl; + MaxisABIInfo ABI = STI.getABI(); + unsigned SP = ABI.GetStackPtr(); + unsigned FP = ABI.GetFramePtr(); + unsigned ZERO = ABI.GetNullPtr(); + unsigned MOVE = ABI.GetGPRMoveOp(); + unsigned ADDiu = ABI.GetPtrAddiuOp(); + unsigned AND = ABI.IsN64() ? Maxis::AND64 : Maxis::AND; + + const TargetRegisterClass *RC = ABI.ArePtrs64bit() ? + &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + + // First, compute final stack size. + uint64_t StackSize = MFI.getStackSize(); + + // No need to allocate space on the stack. + if (StackSize == 0 && !MFI.adjustsStack()) return; + + MachineModuleInfo &MMI = MF.getMMI(); + const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo(); + + // Adjust stack. + TII.adjustStackPtr(SP, -StackSize, MBB, MBBI); + + // emit ".cfi_def_cfa_offset StackSize" + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createDefCfaOffset(nullptr, -StackSize)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + + if (MF.getFunction().hasFnAttribute("interrupt")) + emitInterruptPrologueStub(MF, MBB); + + const std::vector &CSI = MFI.getCalleeSavedInfo(); + + if (!CSI.empty()) { + // Find the instruction past the last instruction that saves a callee-saved + // register to the stack. + for (unsigned i = 0; i < CSI.size(); ++i) + ++MBBI; + + // Iterate over list of callee-saved registers and emit .cfi_offset + // directives. + for (std::vector::const_iterator I = CSI.begin(), + E = CSI.end(); I != E; ++I) { + int64_t Offset = MFI.getObjectOffset(I->getFrameIdx()); + unsigned Reg = I->getReg(); + + // If Reg is a double precision register, emit two cfa_offsets, + // one for each of the paired single precision registers. + if (Maxis::AFGR64RegClass.contains(Reg)) { + unsigned Reg0 = + MRI->getDwarfRegNum(RegInfo.getSubReg(Reg, Maxis::sub_lo), true); + unsigned Reg1 = + MRI->getDwarfRegNum(RegInfo.getSubReg(Reg, Maxis::sub_hi), true); + + if (!STI.isLittle()) + std::swap(Reg0, Reg1); + + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createOffset(nullptr, Reg0, Offset)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + + CFIIndex = MF.addFrameInst( + MCCFIInstruction::createOffset(nullptr, Reg1, Offset + 4)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } else if (Maxis::FGR64RegClass.contains(Reg)) { + unsigned Reg0 = MRI->getDwarfRegNum(Reg, true); + unsigned Reg1 = MRI->getDwarfRegNum(Reg, true) + 1; + + if (!STI.isLittle()) + std::swap(Reg0, Reg1); + + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createOffset(nullptr, Reg0, Offset)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + + CFIIndex = MF.addFrameInst( + MCCFIInstruction::createOffset(nullptr, Reg1, Offset + 4)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } else { + // Reg is either in GPR32 or FGR32. + unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createOffset( + nullptr, MRI->getDwarfRegNum(Reg, true), Offset)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } + } + } + + if (MaxisFI->callsEhReturn()) { + // Insert instructions that spill eh data registers. + for (int I = 0; I < 4; ++I) { + if (!MBB.isLiveIn(ABI.GetEhDataReg(I))) + MBB.addLiveIn(ABI.GetEhDataReg(I)); + TII.storeRegToStackSlot(MBB, MBBI, ABI.GetEhDataReg(I), false, + MaxisFI->getEhDataRegFI(I), RC, &RegInfo); + } + + // Emit .cfi_offset directives for eh data registers. + for (int I = 0; I < 4; ++I) { + int64_t Offset = MFI.getObjectOffset(MaxisFI->getEhDataRegFI(I)); + unsigned Reg = MRI->getDwarfRegNum(ABI.GetEhDataReg(I), true); + unsigned CFIIndex = MF.addFrameInst( + MCCFIInstruction::createOffset(nullptr, Reg, Offset)); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + } + } + + // if framepointer enabled, set it to point to the stack pointer. + if (hasFP(MF)) { + // Insert instruction "move $fp, $sp" at this location. + BuildMI(MBB, MBBI, dl, TII.get(MOVE), FP).addReg(SP).addReg(ZERO) + .setMIFlag(MachineInstr::FrameSetup); + + // emit ".cfi_def_cfa_register $fp" + unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::createDefCfaRegister( + nullptr, MRI->getDwarfRegNum(FP, true))); + BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + + if (RegInfo.needsStackRealignment(MF)) { + // addiu $Reg, $zero, -MaxAlignment + // andi $sp, $sp, $Reg + unsigned VR = MF.getRegInfo().createVirtualRegister(RC); + assert(isInt<16>(MFI.getMaxAlignment()) && + "Function's alignment size requirement is not supported."); + int MaxAlign = -(int)MFI.getMaxAlignment(); + + BuildMI(MBB, MBBI, dl, TII.get(ADDiu), VR).addReg(ZERO) .addImm(MaxAlign); + BuildMI(MBB, MBBI, dl, TII.get(AND), SP).addReg(SP).addReg(VR); + + if (hasBP(MF)) { + // move $s7, $sp + unsigned BP = STI.isABI_N64() ? Maxis::S7_64 : Maxis::S7; + BuildMI(MBB, MBBI, dl, TII.get(MOVE), BP) + .addReg(SP) + .addReg(ZERO); + } + } + } +} + +void MaxisSEFrameLowering::emitInterruptPrologueStub( + MachineFunction &MF, MachineBasicBlock &MBB) const { + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + MachineBasicBlock::iterator MBBI = MBB.begin(); + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + + // Report an error the target doesn't support Maxis32r2 or later. + // The epilogue relies on the use of the "ehb" to clear execution + // hazards. Pre R2 Maxis relies on an implementation defined number + // of "ssnop"s to clear the execution hazard. Support for ssnop hazard + // clearing is not provided so reject that configuration. + if (!STI.hasMaxis32r2()) + report_fatal_error( + "\"interrupt\" attribute is not supported on pre-MAXIS32R2 or " + "MAXIS16 targets."); + + // The GP register contains the "user" value, so we cannot perform + // any gp relative loads until we restore the "kernel" or "system" gp + // value. Until support is written we shall only accept the static + // relocation model. + if ((STI.getRelocationModel() != Reloc::Static)) + report_fatal_error("\"interrupt\" attribute is only supported for the " + "static relocation model on MAXIS at the present time."); + + if (!STI.isABI_O32() || STI.hasMaxis64()) + report_fatal_error("\"interrupt\" attribute is only supported for the " + "O32 ABI on MAXIS32R2+ at the present time."); + + // Perform ISR handling like GCC + StringRef IntKind = + MF.getFunction().getFnAttribute("interrupt").getValueAsString(); + const TargetRegisterClass *PtrRC = &Maxis::GPR32RegClass; + + // EIC interrupt handling needs to read the Cause register to disable + // interrupts. + if (IntKind == "eic") { + // Coprocessor registers are always live per se. + MBB.addLiveIn(Maxis::COP013); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::MFC0), Maxis::K0) + .addReg(Maxis::COP013) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::EXT), Maxis::K0) + .addReg(Maxis::K0) + .addImm(10) + .addImm(6) + .setMIFlag(MachineInstr::FrameSetup); + } + + // Fetch and spill EPC + MBB.addLiveIn(Maxis::COP014); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::MFC0), Maxis::K1) + .addReg(Maxis::COP014) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + + STI.getInstrInfo()->storeRegToStack(MBB, MBBI, Maxis::K1, false, + MaxisFI->getISRRegFI(0), PtrRC, + STI.getRegisterInfo(), 0); + + // Fetch and Spill Status + MBB.addLiveIn(Maxis::COP012); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::MFC0), Maxis::K1) + .addReg(Maxis::COP012) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + + STI.getInstrInfo()->storeRegToStack(MBB, MBBI, Maxis::K1, false, + MaxisFI->getISRRegFI(1), PtrRC, + STI.getRegisterInfo(), 0); + + // Build the configuration for disabling lower priority interrupts. Non EIC + // interrupts need to be masked off with zero, EIC from the Cause register. + unsigned InsPosition = 8; + unsigned InsSize = 0; + unsigned SrcReg = Maxis::ZERO; + + // If the interrupt we're tied to is the EIC, switch the source for the + // masking off interrupts to the cause register. + if (IntKind == "eic") { + SrcReg = Maxis::K0; + InsPosition = 10; + InsSize = 6; + } else + InsSize = StringSwitch(IntKind) + .Case("sw0", 1) + .Case("sw1", 2) + .Case("hw0", 3) + .Case("hw1", 4) + .Case("hw2", 5) + .Case("hw3", 6) + .Case("hw4", 7) + .Case("hw5", 8) + .Default(0); + assert(InsSize != 0 && "Unknown interrupt type!"); + + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::INS), Maxis::K1) + .addReg(SrcReg) + .addImm(InsPosition) + .addImm(InsSize) + .addReg(Maxis::K1) + .setMIFlag(MachineInstr::FrameSetup); + + // Mask off KSU, ERL, EXL + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::INS), Maxis::K1) + .addReg(Maxis::ZERO) + .addImm(1) + .addImm(4) + .addReg(Maxis::K1) + .setMIFlag(MachineInstr::FrameSetup); + + // Disable the FPU as we are not spilling those register sets. + if (!STI.useSoftFloat()) + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::INS), Maxis::K1) + .addReg(Maxis::ZERO) + .addImm(29) + .addImm(1) + .addReg(Maxis::K1) + .setMIFlag(MachineInstr::FrameSetup); + + // Set the new status + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::MTC0), Maxis::COP012) + .addReg(Maxis::K1) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); +} + +void MaxisSEFrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + const MaxisSEInstrInfo &TII = + *static_cast(STI.getInstrInfo()); + const MaxisRegisterInfo &RegInfo = + *static_cast(STI.getRegisterInfo()); + + DebugLoc DL = MBBI->getDebugLoc(); + MaxisABIInfo ABI = STI.getABI(); + unsigned SP = ABI.GetStackPtr(); + unsigned FP = ABI.GetFramePtr(); + unsigned ZERO = ABI.GetNullPtr(); + unsigned MOVE = ABI.GetGPRMoveOp(); + + // if framepointer enabled, restore the stack pointer. + if (hasFP(MF)) { + // Find the first instruction that restores a callee-saved register. + MachineBasicBlock::iterator I = MBBI; + + for (unsigned i = 0; i < MFI.getCalleeSavedInfo().size(); ++i) + --I; + + // Insert instruction "move $sp, $fp" at this location. + BuildMI(MBB, I, DL, TII.get(MOVE), SP).addReg(FP).addReg(ZERO); + } + + if (MaxisFI->callsEhReturn()) { + const TargetRegisterClass *RC = + ABI.ArePtrs64bit() ? &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + + // Find first instruction that restores a callee-saved register. + MachineBasicBlock::iterator I = MBBI; + for (unsigned i = 0; i < MFI.getCalleeSavedInfo().size(); ++i) + --I; + + // Insert instructions that restore eh data registers. + for (int J = 0; J < 4; ++J) { + TII.loadRegFromStackSlot(MBB, I, ABI.GetEhDataReg(J), + MaxisFI->getEhDataRegFI(J), RC, &RegInfo); + } + } + + if (MF.getFunction().hasFnAttribute("interrupt")) + emitInterruptEpilogueStub(MF, MBB); + + // Get the number of bytes from FrameInfo + uint64_t StackSize = MFI.getStackSize(); + + if (!StackSize) + return; + + // Adjust stack. + TII.adjustStackPtr(SP, StackSize, MBB, MBBI); +} + +void MaxisSEFrameLowering::emitInterruptEpilogueStub( + MachineFunction &MF, MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + + // Perform ISR handling like GCC + const TargetRegisterClass *PtrRC = &Maxis::GPR32RegClass; + + // Disable Interrupts. + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::DI), Maxis::ZERO); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::EHB)); + + // Restore EPC + STI.getInstrInfo()->loadRegFromStackSlot(MBB, MBBI, Maxis::K1, + MaxisFI->getISRRegFI(0), PtrRC, + STI.getRegisterInfo()); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::MTC0), Maxis::COP014) + .addReg(Maxis::K1) + .addImm(0); + + // Restore Status + STI.getInstrInfo()->loadRegFromStackSlot(MBB, MBBI, Maxis::K1, + MaxisFI->getISRRegFI(1), PtrRC, + STI.getRegisterInfo()); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Maxis::MTC0), Maxis::COP012) + .addReg(Maxis::K1) + .addImm(0); +} + +int MaxisSEFrameLowering::getFrameIndexReference(const MachineFunction &MF, + int FI, + unsigned &FrameReg) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + MaxisABIInfo ABI = STI.getABI(); + + if (MFI.isFixedObjectIndex(FI)) + FrameReg = hasFP(MF) ? ABI.GetFramePtr() : ABI.GetStackPtr(); + else + FrameReg = hasBP(MF) ? ABI.GetBasePtr() : ABI.GetStackPtr(); + + return MFI.getObjectOffset(FI) + MFI.getStackSize() - + getOffsetOfLocalArea() + MFI.getOffsetAdjustment(); +} + +bool MaxisSEFrameLowering:: +spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const { + MachineFunction *MF = MBB.getParent(); + MachineBasicBlock *EntryBlock = &MF->front(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + // Add the callee-saved register as live-in. Do not add if the register is + // RA and return address is taken, because it has already been added in + // method MaxisTargetLowering::lowerRETURNADDR. + // It's killed at the spill, unless the register is RA and return address + // is taken. + unsigned Reg = CSI[i].getReg(); + bool IsRAAndRetAddrIsTaken = (Reg == Maxis::RA || Reg == Maxis::RA_64) + && MF->getFrameInfo().isReturnAddressTaken(); + if (!IsRAAndRetAddrIsTaken) + EntryBlock->addLiveIn(Reg); + + // ISRs require HI/LO to be spilled into kernel registers to be then + // spilled to the stack frame. + bool IsLOHI = (Reg == Maxis::LO0 || Reg == Maxis::LO0_64 || + Reg == Maxis::HI0 || Reg == Maxis::HI0_64); + const Function &Func = MBB.getParent()->getFunction(); + if (IsLOHI && Func.hasFnAttribute("interrupt")) { + DebugLoc DL = MI->getDebugLoc(); + + unsigned Op = 0; + if (!STI.getABI().ArePtrs64bit()) { + Op = (Reg == Maxis::HI0) ? Maxis::MFHI : Maxis::MFLO; + Reg = Maxis::K0; + } else { + Op = (Reg == Maxis::HI0) ? Maxis::MFHI64 : Maxis::MFLO64; + Reg = Maxis::K0_64; + } + BuildMI(MBB, MI, DL, TII.get(Op), Maxis::K0) + .setMIFlag(MachineInstr::FrameSetup); + } + + // Insert the spill to the stack frame. + bool IsKill = !IsRAAndRetAddrIsTaken; + const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); + TII.storeRegToStackSlot(*EntryBlock, MI, Reg, IsKill, + CSI[i].getFrameIdx(), RC, TRI); + } + + return true; +} + +bool +MaxisSEFrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { + const MachineFrameInfo &MFI = MF.getFrameInfo(); + // Reserve call frame if the size of the maximum call frame fits into 16-bit + // immediate field and there are no variable sized objects on the stack. + // Make sure the second register scavenger spill slot can be accessed with one + // instruction. + return isInt<16>(MFI.getMaxCallFrameSize() + getStackAlignment()) && + !MFI.hasVarSizedObjects(); +} + +/// Mark \p Reg and all registers aliasing it in the bitset. +static void setAliasRegs(MachineFunction &MF, BitVector &SavedRegs, + unsigned Reg) { + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + for (MCRegAliasIterator AI(Reg, TRI, true); AI.isValid(); ++AI) + SavedRegs.set(*AI); +} + +void MaxisSEFrameLowering::determineCalleeSaves(MachineFunction &MF, + BitVector &SavedRegs, + RegScavenger *RS) const { + TargetFrameLowering::determineCalleeSaves(MF, SavedRegs, RS); + const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + MaxisABIInfo ABI = STI.getABI(); + unsigned FP = ABI.GetFramePtr(); + unsigned BP = ABI.IsN64() ? Maxis::S7_64 : Maxis::S7; + + // Mark $fp as used if function has dedicated frame pointer. + if (hasFP(MF)) + setAliasRegs(MF, SavedRegs, FP); + // Mark $s7 as used if function has dedicated base pointer. + if (hasBP(MF)) + setAliasRegs(MF, SavedRegs, BP); + + // Create spill slots for eh data registers if function calls eh_return. + if (MaxisFI->callsEhReturn()) + MaxisFI->createEhDataRegsFI(); + + // Create spill slots for Coprocessor 0 registers if function is an ISR. + if (MaxisFI->isISR()) + MaxisFI->createISRRegFI(); + + // Expand pseudo instructions which load, store or copy accumulators. + // Add an emergency spill slot if a pseudo was expanded. + if (ExpandPseudo(MF).expand()) { + // The spill slot should be half the size of the accumulator. If target is + // maxis64, it should be 64-bit, otherwise it should be 32-bt. + const TargetRegisterClass &RC = STI.hasMaxis64() ? + Maxis::GPR64RegClass : Maxis::GPR32RegClass; + int FI = MF.getFrameInfo().CreateStackObject(TRI->getSpillSize(RC), + TRI->getSpillAlignment(RC), + false); + RS->addScavengingFrameIndex(FI); + } + + // Set scavenging frame index if necessary. + uint64_t MaxSPOffset = estimateStackSize(MF); + + // MSA has a minimum offset of 10 bits signed. If there is a variable + // sized object on the stack, the estimation cannot account for it. + if (isIntN(STI.hasMSA() ? 10 : 16, MaxSPOffset) && + !MF.getFrameInfo().hasVarSizedObjects()) + return; + + const TargetRegisterClass &RC = + ABI.ArePtrs64bit() ? Maxis::GPR64RegClass : Maxis::GPR32RegClass; + int FI = MF.getFrameInfo().CreateStackObject(TRI->getSpillSize(RC), + TRI->getSpillAlignment(RC), + false); + RS->addScavengingFrameIndex(FI); +} + +const MaxisFrameLowering * +llvm::createMaxisSEFrameLowering(const MaxisSubtarget &ST) { + return new MaxisSEFrameLowering(ST); +} diff --git a/lib/Target/Maxis/MaxisSEFrameLowering.h b/lib/Target/Maxis/MaxisSEFrameLowering.h new file mode 100644 index 00000000..d1c9a00a --- /dev/null +++ b/lib/Target/Maxis/MaxisSEFrameLowering.h @@ -0,0 +1,54 @@ +//===- MaxisSEFrameLowering.h - Maxis32/64 frame lowering ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISSEFRAMELOWERING_H +#define LLVM_LIB_TARGET_MAXIS_MAXISSEFRAMELOWERING_H + +#include "MaxisFrameLowering.h" +#include + +namespace llvm { + +class MachineBasicBlock; +class MachineFunction; +class MaxisSubtarget; + +class MaxisSEFrameLowering : public MaxisFrameLowering { +public: + explicit MaxisSEFrameLowering(const MaxisSubtarget &STI); + + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into + /// the function. + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + int getFrameIndexReference(const MachineFunction &MF, int FI, + unsigned &FrameReg) const override; + + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const std::vector &CSI, + const TargetRegisterInfo *TRI) const override; + + bool hasReservedCallFrame(const MachineFunction &MF) const override; + + void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, + RegScavenger *RS) const override; + unsigned ehDataReg(unsigned I) const; + +private: + void emitInterruptEpilogueStub(MachineFunction &MF, + MachineBasicBlock &MBB) const; + void emitInterruptPrologueStub(MachineFunction &MF, + MachineBasicBlock &MBB) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISSEFRAMELOWERING_H diff --git a/lib/Target/Maxis/MaxisSEISelDAGToDAG.cpp b/lib/Target/Maxis/MaxisSEISelDAGToDAG.cpp new file mode 100644 index 00000000..bcda6a38 --- /dev/null +++ b/lib/Target/Maxis/MaxisSEISelDAGToDAG.cpp @@ -0,0 +1,1366 @@ +//===-- MaxisSEISelDAGToDAG.cpp - A Dag to Dag Inst Selector for MaxisSE ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Subclass of MaxisDAGToDAGISel specialized for maxis32/64. +// +//===----------------------------------------------------------------------===// + +#include "MaxisSEISelDAGToDAG.h" +#include "MCTargetDesc/MaxisBaseInfo.h" +#include "Maxis.h" +#include "MaxisAnalyzeImmediate.h" +#include "MaxisMachineFunction.h" +#include "MaxisRegisterInfo.h" +#include "llvm/CodeGen/MachineConstantPool.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/Dominators.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 "maxis-isel" + +bool MaxisSEDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) { + Subtarget = &static_cast(MF.getSubtarget()); + if (Subtarget->inMaxis16Mode()) + return false; + return MaxisDAGToDAGISel::runOnMachineFunction(MF); +} + +void MaxisSEDAGToDAGISel::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + SelectionDAGISel::getAnalysisUsage(AU); +} + +void MaxisSEDAGToDAGISel::addDSPCtrlRegOperands(bool IsDef, MachineInstr &MI, + MachineFunction &MF) { + MachineInstrBuilder MIB(MF, &MI); + unsigned Mask = MI.getOperand(1).getImm(); + unsigned Flag = + IsDef ? RegState::ImplicitDefine : RegState::Implicit | RegState::Undef; + + if (Mask & 1) + MIB.addReg(Maxis::DSPPos, Flag); + + if (Mask & 2) + MIB.addReg(Maxis::DSPSCount, Flag); + + if (Mask & 4) + MIB.addReg(Maxis::DSPCarry, Flag); + + if (Mask & 8) + MIB.addReg(Maxis::DSPOutFlag, Flag); + + if (Mask & 16) + MIB.addReg(Maxis::DSPCCond, Flag); + + if (Mask & 32) + MIB.addReg(Maxis::DSPEFI, Flag); +} + +unsigned MaxisSEDAGToDAGISel::getMSACtrlReg(const SDValue RegIdx) const { + switch (cast(RegIdx)->getZExtValue()) { + default: + llvm_unreachable("Could not map int to register"); + case 0: return Maxis::MSAIR; + case 1: return Maxis::MSACSR; + case 2: return Maxis::MSAAccess; + case 3: return Maxis::MSASave; + case 4: return Maxis::MSAModify; + case 5: return Maxis::MSARequest; + case 6: return Maxis::MSAMap; + case 7: return Maxis::MSAUnmap; + } +} + +bool MaxisSEDAGToDAGISel::replaceUsesWithZeroReg(MachineRegisterInfo *MRI, + const MachineInstr& MI) { + unsigned DstReg = 0, ZeroReg = 0; + + // Check if MI is "addiu $dst, $zero, 0" or "daddiu $dst, $zero, 0". + if ((MI.getOpcode() == Maxis::ADDiu) && + (MI.getOperand(1).getReg() == Maxis::ZERO) && + (MI.getOperand(2).isImm()) && + (MI.getOperand(2).getImm() == 0)) { + DstReg = MI.getOperand(0).getReg(); + ZeroReg = Maxis::ZERO; + } else if ((MI.getOpcode() == Maxis::DADDiu) && + (MI.getOperand(1).getReg() == Maxis::ZERO_64) && + (MI.getOperand(2).isImm()) && + (MI.getOperand(2).getImm() == 0)) { + DstReg = MI.getOperand(0).getReg(); + ZeroReg = Maxis::ZERO_64; + } + + if (!DstReg) + return false; + + // Replace uses with ZeroReg. + for (MachineRegisterInfo::use_iterator U = MRI->use_begin(DstReg), + E = MRI->use_end(); U != E;) { + MachineOperand &MO = *U; + unsigned OpNo = U.getOperandNo(); + MachineInstr *MI = MO.getParent(); + ++U; + + // Do not replace if it is a phi's operand or is tied to def operand. + if (MI->isPHI() || MI->isRegTiedToDefOperand(OpNo) || MI->isPseudo()) + continue; + + // Also, we have to check that the register class of the operand + // contains the zero register. + if (!MRI->getRegClass(MO.getReg())->contains(ZeroReg)) + continue; + + MO.setReg(ZeroReg); + } + + return true; +} + +void MaxisSEDAGToDAGISel::initGlobalBaseReg(MachineFunction &MF) { + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + if (!MaxisFI->globalBaseRegSet()) + return; + + MachineBasicBlock &MBB = MF.front(); + MachineBasicBlock::iterator I = MBB.begin(); + MachineRegisterInfo &RegInfo = MF.getRegInfo(); + const TargetInstrInfo &TII = *Subtarget->getInstrInfo(); + DebugLoc DL; + unsigned V0, V1, GlobalBaseReg = MaxisFI->getGlobalBaseReg(); + const TargetRegisterClass *RC; + const MaxisABIInfo &ABI = static_cast(TM).getABI(); + RC = (ABI.IsN64()) ? &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + + V0 = RegInfo.createVirtualRegister(RC); + V1 = RegInfo.createVirtualRegister(RC); + + if (ABI.IsN64()) { + MF.getRegInfo().addLiveIn(Maxis::T9_64); + MBB.addLiveIn(Maxis::T9_64); + + // lui $v0, %hi(%neg(%gp_rel(fname))) + // daddu $v1, $v0, $t9 + // daddiu $globalbasereg, $v1, %lo(%neg(%gp_rel(fname))) + const GlobalValue *FName = &MF.getFunction(); + BuildMI(MBB, I, DL, TII.get(Maxis::LUi64), V0) + .addGlobalAddress(FName, 0, MaxisII::MO_GPOFF_HI); + BuildMI(MBB, I, DL, TII.get(Maxis::DADDu), V1).addReg(V0) + .addReg(Maxis::T9_64); + BuildMI(MBB, I, DL, TII.get(Maxis::DADDiu), GlobalBaseReg).addReg(V1) + .addGlobalAddress(FName, 0, MaxisII::MO_GPOFF_LO); + return; + } + + if (!MF.getTarget().isPositionIndependent()) { + // Set global register to __gnu_local_gp. + // + // lui $v0, %hi(__gnu_local_gp) + // addiu $globalbasereg, $v0, %lo(__gnu_local_gp) + BuildMI(MBB, I, DL, TII.get(Maxis::LUi), V0) + .addExternalSymbol("__gnu_local_gp", MaxisII::MO_ABS_HI); + BuildMI(MBB, I, DL, TII.get(Maxis::ADDiu), GlobalBaseReg).addReg(V0) + .addExternalSymbol("__gnu_local_gp", MaxisII::MO_ABS_LO); + return; + } + + MF.getRegInfo().addLiveIn(Maxis::T9); + MBB.addLiveIn(Maxis::T9); + + if (ABI.IsN32()) { + // lui $v0, %hi(%neg(%gp_rel(fname))) + // addu $v1, $v0, $t9 + // addiu $globalbasereg, $v1, %lo(%neg(%gp_rel(fname))) + const GlobalValue *FName = &MF.getFunction(); + BuildMI(MBB, I, DL, TII.get(Maxis::LUi), V0) + .addGlobalAddress(FName, 0, MaxisII::MO_GPOFF_HI); + BuildMI(MBB, I, DL, TII.get(Maxis::ADDu), V1).addReg(V0).addReg(Maxis::T9); + BuildMI(MBB, I, DL, TII.get(Maxis::ADDiu), GlobalBaseReg).addReg(V1) + .addGlobalAddress(FName, 0, MaxisII::MO_GPOFF_LO); + return; + } + + assert(ABI.IsO32()); + + // For O32 ABI, the following instruction sequence is emitted to initialize + // the global base register: + // + // 0. lui $2, %hi(_gp_disp) + // 1. addiu $2, $2, %lo(_gp_disp) + // 2. addu $globalbasereg, $2, $t9 + // + // We emit only the last instruction here. + // + // GNU linker requires that the first two instructions appear at the beginning + // of a function and no instructions be inserted before or between them. + // The two instructions are emitted during lowering to MC layer in order to + // avoid any reordering. + // + // Register $2 (Maxis::V0) is added to the list of live-in registers to ensure + // the value instruction 1 (addiu) defines is valid when instruction 2 (addu) + // reads it. + MF.getRegInfo().addLiveIn(Maxis::V0); + MBB.addLiveIn(Maxis::V0); + BuildMI(MBB, I, DL, TII.get(Maxis::ADDu), GlobalBaseReg) + .addReg(Maxis::V0).addReg(Maxis::T9); +} + +void MaxisSEDAGToDAGISel::processFunctionAfterISel(MachineFunction &MF) { + initGlobalBaseReg(MF); + + MachineRegisterInfo *MRI = &MF.getRegInfo(); + + for (auto &MBB: MF) { + for (auto &MI: MBB) { + switch (MI.getOpcode()) { + case Maxis::RDDSP: + addDSPCtrlRegOperands(false, MI, MF); + break; + case Maxis::WRDSP: + addDSPCtrlRegOperands(true, MI, MF); + break; + default: + replaceUsesWithZeroReg(MRI, MI); + } + } + } +} + +void MaxisSEDAGToDAGISel::selectAddE(SDNode *Node, const SDLoc &DL) const { + SDValue InFlag = Node->getOperand(2); + unsigned Opc = InFlag.getOpcode(); + SDValue LHS = Node->getOperand(0), RHS = Node->getOperand(1); + EVT VT = LHS.getValueType(); + + // In the base case, we can rely on the carry bit from the addsc + // instruction. + if (Opc == ISD::ADDC) { + SDValue Ops[3] = {LHS, RHS, InFlag}; + CurDAG->SelectNodeTo(Node, Maxis::ADDWC, VT, MVT::Glue, Ops); + return; + } + + assert(Opc == ISD::ADDE && "ISD::ADDE not in a chain of ADDE nodes!"); + + // The more complex case is when there is a chain of ISD::ADDE nodes like: + // (adde (adde (adde (addc a b) c) d) e). + // + // The addwc instruction does not write to the carry bit, instead it writes + // to bit 20 of the dsp control register. To match this series of nodes, each + // intermediate adde node must be expanded to write the carry bit before the + // addition. + + // Start by reading the overflow field for addsc and moving the value to the + // carry field. The usage of 1 here with MaxisISD::RDDSP / Maxis::WRDSP + // corresponds to reading/writing the entire control register to/from a GPR. + + SDValue CstOne = CurDAG->getTargetConstant(1, DL, MVT::i32); + + SDValue OuFlag = CurDAG->getTargetConstant(20, DL, MVT::i32); + + SDNode *DSPCtrlField = + CurDAG->getMachineNode(Maxis::RDDSP, DL, MVT::i32, MVT::Glue, CstOne, InFlag); + + SDNode *Carry = CurDAG->getMachineNode( + Maxis::EXT, DL, MVT::i32, SDValue(DSPCtrlField, 0), OuFlag, CstOne); + + SDValue Ops[4] = {SDValue(DSPCtrlField, 0), + CurDAG->getTargetConstant(6, DL, MVT::i32), CstOne, + SDValue(Carry, 0)}; + SDNode *DSPCFWithCarry = CurDAG->getMachineNode(Maxis::INS, DL, MVT::i32, Ops); + + // My reading of the the MAXIS DSP 3.01 specification isn't as clear as I + // would like about whether bit 20 always gets overwritten by addwc. + // Hence take an extremely conservative view and presume it's sticky. We + // therefore need to clear it. + + SDValue Zero = CurDAG->getRegister(Maxis::ZERO, MVT::i32); + + SDValue InsOps[4] = {Zero, OuFlag, CstOne, SDValue(DSPCFWithCarry, 0)}; + SDNode *DSPCtrlFinal = CurDAG->getMachineNode(Maxis::INS, DL, MVT::i32, InsOps); + + SDNode *WrDSP = CurDAG->getMachineNode(Maxis::WRDSP, DL, MVT::Glue, + SDValue(DSPCtrlFinal, 0), CstOne); + + SDValue Operands[3] = {LHS, RHS, SDValue(WrDSP, 0)}; + CurDAG->SelectNodeTo(Node, Maxis::ADDWC, VT, MVT::Glue, Operands); +} + +/// Match frameindex +bool MaxisSEDAGToDAGISel::selectAddrFrameIndex(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (FrameIndexSDNode *FIN = dyn_cast(Addr)) { + EVT ValTy = Addr.getValueType(); + + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy); + Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), ValTy); + return true; + } + return false; +} + +/// Match frameindex+offset and frameindex|offset +bool MaxisSEDAGToDAGISel::selectAddrFrameIndexOffset( + SDValue Addr, SDValue &Base, SDValue &Offset, unsigned OffsetBits, + unsigned ShiftAmount = 0) const { + if (CurDAG->isBaseWithConstantOffset(Addr)) { + ConstantSDNode *CN = dyn_cast(Addr.getOperand(1)); + if (isIntN(OffsetBits + ShiftAmount, CN->getSExtValue())) { + EVT ValTy = Addr.getValueType(); + + // If the first operand is a FI, get the TargetFI Node + if (FrameIndexSDNode *FIN = + dyn_cast(Addr.getOperand(0))) + Base = CurDAG->getTargetFrameIndex(FIN->getIndex(), ValTy); + else { + Base = Addr.getOperand(0); + // If base is a FI, additional offset calculation is done in + // eliminateFrameIndex, otherwise we need to check the alignment + if (OffsetToAlignment(CN->getZExtValue(), 1ull << ShiftAmount) != 0) + return false; + } + + Offset = CurDAG->getTargetConstant(CN->getZExtValue(), SDLoc(Addr), + ValTy); + return true; + } + } + return false; +} + +/// ComplexPattern used on MaxisInstrInfo +/// Used on Maxis Load/Store instructions +bool MaxisSEDAGToDAGISel::selectAddrRegImm(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + // if Address is FI, get the TargetFrameIndex. + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + // on PIC code Load GA + if (Addr.getOpcode() == MaxisISD::Wrapper) { + Base = Addr.getOperand(0); + Offset = Addr.getOperand(1); + return true; + } + + if (!TM.isPositionIndependent()) { + if ((Addr.getOpcode() == ISD::TargetExternalSymbol || + Addr.getOpcode() == ISD::TargetGlobalAddress)) + return false; + } + + // Addresses of the form FI+const or FI|const + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 16)) + return true; + + // Operand is a result from an ADD. + if (Addr.getOpcode() == ISD::ADD) { + // When loading from constant pools, load the lower address part in + // the instruction itself. Example, instead of: + // lui $2, %hi($CPI1_0) + // addiu $2, $2, %lo($CPI1_0) + // lwc1 $f0, 0($2) + // Generate: + // lui $2, %hi($CPI1_0) + // lwc1 $f0, %lo($CPI1_0)($2) + if (Addr.getOperand(1).getOpcode() == MaxisISD::Lo || + Addr.getOperand(1).getOpcode() == MaxisISD::GPRel) { + SDValue Opnd0 = Addr.getOperand(1).getOperand(0); + if (isa(Opnd0) || isa(Opnd0) || + isa(Opnd0)) { + Base = Addr.getOperand(0); + Offset = Opnd0; + return true; + } + } + } + + return false; +} + +/// ComplexPattern used on MaxisInstrInfo +/// Used on Maxis Load/Store instructions +bool MaxisSEDAGToDAGISel::selectAddrDefault(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + Base = Addr; + Offset = CurDAG->getTargetConstant(0, SDLoc(Addr), Addr.getValueType()); + return true; +} + +bool MaxisSEDAGToDAGISel::selectIntAddr(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + return selectAddrRegImm(Addr, Base, Offset) || + selectAddrDefault(Addr, Base, Offset); +} + +bool MaxisSEDAGToDAGISel::selectAddrRegImm9(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 9)) + return true; + + return false; +} + +/// Used on microMAXIS LWC2, LDC2, SWC2 and SDC2 instructions (11-bit offset) +bool MaxisSEDAGToDAGISel::selectAddrRegImm11(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 11)) + return true; + + return false; +} + +/// Used on microMAXIS Load/Store unaligned instructions (12-bit offset) +bool MaxisSEDAGToDAGISel::selectAddrRegImm12(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 12)) + return true; + + return false; +} + +bool MaxisSEDAGToDAGISel::selectAddrRegImm16(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 16)) + return true; + + return false; +} + +bool MaxisSEDAGToDAGISel::selectIntAddr11MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + return selectAddrRegImm11(Addr, Base, Offset) || + selectAddrDefault(Addr, Base, Offset); +} + +bool MaxisSEDAGToDAGISel::selectIntAddr12MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + return selectAddrRegImm12(Addr, Base, Offset) || + selectAddrDefault(Addr, Base, Offset); +} + +bool MaxisSEDAGToDAGISel::selectIntAddr16MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + return selectAddrRegImm16(Addr, Base, Offset) || + selectAddrDefault(Addr, Base, Offset); +} + +bool MaxisSEDAGToDAGISel::selectIntAddrLSL2MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 7)) { + if (isa(Base)) + return false; + + if (ConstantSDNode *CN = dyn_cast(Offset)) { + unsigned CnstOff = CN->getZExtValue(); + return (CnstOff == (CnstOff & 0x3c)); + } + + return false; + } + + // For all other cases where "lw" would be selected, don't select "lw16" + // because it would result in additional instructions to prepare operands. + if (selectAddrRegImm(Addr, Base, Offset)) + return false; + + return selectAddrDefault(Addr, Base, Offset); +} + +bool MaxisSEDAGToDAGISel::selectIntAddrSImm10(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 10)) + return true; + + return selectAddrDefault(Addr, Base, Offset); +} + +bool MaxisSEDAGToDAGISel::selectIntAddrSImm10Lsl1(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 10, 1)) + return true; + + return selectAddrDefault(Addr, Base, Offset); +} + +bool MaxisSEDAGToDAGISel::selectIntAddrSImm10Lsl2(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 10, 2)) + return true; + + return selectAddrDefault(Addr, Base, Offset); +} + +bool MaxisSEDAGToDAGISel::selectIntAddrSImm10Lsl3(SDValue Addr, SDValue &Base, + SDValue &Offset) const { + if (selectAddrFrameIndex(Addr, Base, Offset)) + return true; + + if (selectAddrFrameIndexOffset(Addr, Base, Offset, 10, 3)) + return true; + + return selectAddrDefault(Addr, Base, Offset); +} + +// Select constant vector splats. +// +// Returns true and sets Imm if: +// * MSA is enabled +// * N is a ISD::BUILD_VECTOR representing a constant splat +bool MaxisSEDAGToDAGISel::selectVSplat(SDNode *N, APInt &Imm, + unsigned MinSizeInBits) const { + if (!Subtarget->hasMSA()) + return false; + + BuildVectorSDNode *Node = dyn_cast(N); + + if (!Node) + return false; + + APInt SplatValue, SplatUndef; + unsigned SplatBitSize; + bool HasAnyUndefs; + + if (!Node->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, HasAnyUndefs, + MinSizeInBits, !Subtarget->isLittle())) + return false; + + Imm = SplatValue; + + return true; +} + +// Select constant vector splats. +// +// In addition to the requirements of selectVSplat(), this function returns +// true and sets Imm if: +// * The splat value is the same width as the elements of the vector +// * The splat value fits in an integer with the specified signed-ness and +// width. +// +// This function looks through ISD::BITCAST nodes. +// TODO: This might not be appropriate for big-endian MSA since BITCAST is +// sometimes a shuffle in big-endian mode. +// +// It's worth noting that this function is not used as part of the selection +// of ldi.[bhwd] since it does not permit using the wrong-typed ldi.[bhwd] +// instruction to achieve the desired bit pattern. ldi.[bhwd] is selected in +// MaxisSEDAGToDAGISel::selectNode. +bool MaxisSEDAGToDAGISel:: +selectVSplatCommon(SDValue N, SDValue &Imm, bool Signed, + unsigned ImmBitSize) const { + APInt ImmValue; + EVT EltTy = N->getValueType(0).getVectorElementType(); + + if (N->getOpcode() == ISD::BITCAST) + N = N->getOperand(0); + + if (selectVSplat(N.getNode(), ImmValue, EltTy.getSizeInBits()) && + ImmValue.getBitWidth() == EltTy.getSizeInBits()) { + + if (( Signed && ImmValue.isSignedIntN(ImmBitSize)) || + (!Signed && ImmValue.isIntN(ImmBitSize))) { + Imm = CurDAG->getTargetConstant(ImmValue, SDLoc(N), EltTy); + return true; + } + } + + return false; +} + +// Select constant vector splats. +bool MaxisSEDAGToDAGISel:: +selectVSplatUimm1(SDValue N, SDValue &Imm) const { + return selectVSplatCommon(N, Imm, false, 1); +} + +bool MaxisSEDAGToDAGISel:: +selectVSplatUimm2(SDValue N, SDValue &Imm) const { + return selectVSplatCommon(N, Imm, false, 2); +} + +bool MaxisSEDAGToDAGISel:: +selectVSplatUimm3(SDValue N, SDValue &Imm) const { + return selectVSplatCommon(N, Imm, false, 3); +} + +// Select constant vector splats. +bool MaxisSEDAGToDAGISel:: +selectVSplatUimm4(SDValue N, SDValue &Imm) const { + return selectVSplatCommon(N, Imm, false, 4); +} + +// Select constant vector splats. +bool MaxisSEDAGToDAGISel:: +selectVSplatUimm5(SDValue N, SDValue &Imm) const { + return selectVSplatCommon(N, Imm, false, 5); +} + +// Select constant vector splats. +bool MaxisSEDAGToDAGISel:: +selectVSplatUimm6(SDValue N, SDValue &Imm) const { + return selectVSplatCommon(N, Imm, false, 6); +} + +// Select constant vector splats. +bool MaxisSEDAGToDAGISel:: +selectVSplatUimm8(SDValue N, SDValue &Imm) const { + return selectVSplatCommon(N, Imm, false, 8); +} + +// Select constant vector splats. +bool MaxisSEDAGToDAGISel:: +selectVSplatSimm5(SDValue N, SDValue &Imm) const { + return selectVSplatCommon(N, Imm, true, 5); +} + +// Select constant vector splats whose value is a power of 2. +// +// In addition to the requirements of selectVSplat(), this function returns +// true and sets Imm if: +// * The splat value is the same width as the elements of the vector +// * The splat value is a power of two. +// +// This function looks through ISD::BITCAST nodes. +// TODO: This might not be appropriate for big-endian MSA since BITCAST is +// sometimes a shuffle in big-endian mode. +bool MaxisSEDAGToDAGISel::selectVSplatUimmPow2(SDValue N, SDValue &Imm) const { + APInt ImmValue; + EVT EltTy = N->getValueType(0).getVectorElementType(); + + if (N->getOpcode() == ISD::BITCAST) + N = N->getOperand(0); + + if (selectVSplat(N.getNode(), ImmValue, EltTy.getSizeInBits()) && + ImmValue.getBitWidth() == EltTy.getSizeInBits()) { + int32_t Log2 = ImmValue.exactLogBase2(); + + if (Log2 != -1) { + Imm = CurDAG->getTargetConstant(Log2, SDLoc(N), EltTy); + return true; + } + } + + return false; +} + +// Select constant vector splats whose value only has a consecutive sequence +// of left-most bits set (e.g. 0b11...1100...00). +// +// In addition to the requirements of selectVSplat(), this function returns +// true and sets Imm if: +// * The splat value is the same width as the elements of the vector +// * The splat value is a consecutive sequence of left-most bits. +// +// This function looks through ISD::BITCAST nodes. +// TODO: This might not be appropriate for big-endian MSA since BITCAST is +// sometimes a shuffle in big-endian mode. +bool MaxisSEDAGToDAGISel::selectVSplatMaskL(SDValue N, SDValue &Imm) const { + APInt ImmValue; + EVT EltTy = N->getValueType(0).getVectorElementType(); + + if (N->getOpcode() == ISD::BITCAST) + N = N->getOperand(0); + + if (selectVSplat(N.getNode(), ImmValue, EltTy.getSizeInBits()) && + ImmValue.getBitWidth() == EltTy.getSizeInBits()) { + // Extract the run of set bits starting with bit zero from the bitwise + // inverse of ImmValue, and test that the inverse of this is the same + // as the original value. + if (ImmValue == ~(~ImmValue & ~(~ImmValue + 1))) { + + Imm = CurDAG->getTargetConstant(ImmValue.countPopulation() - 1, SDLoc(N), + EltTy); + return true; + } + } + + return false; +} + +// Select constant vector splats whose value only has a consecutive sequence +// of right-most bits set (e.g. 0b00...0011...11). +// +// In addition to the requirements of selectVSplat(), this function returns +// true and sets Imm if: +// * The splat value is the same width as the elements of the vector +// * The splat value is a consecutive sequence of right-most bits. +// +// This function looks through ISD::BITCAST nodes. +// TODO: This might not be appropriate for big-endian MSA since BITCAST is +// sometimes a shuffle in big-endian mode. +bool MaxisSEDAGToDAGISel::selectVSplatMaskR(SDValue N, SDValue &Imm) const { + APInt ImmValue; + EVT EltTy = N->getValueType(0).getVectorElementType(); + + if (N->getOpcode() == ISD::BITCAST) + N = N->getOperand(0); + + if (selectVSplat(N.getNode(), ImmValue, EltTy.getSizeInBits()) && + ImmValue.getBitWidth() == EltTy.getSizeInBits()) { + // Extract the run of set bits starting with bit zero, and test that the + // result is the same as the original value + if (ImmValue == (ImmValue & ~(ImmValue + 1))) { + Imm = CurDAG->getTargetConstant(ImmValue.countPopulation() - 1, SDLoc(N), + EltTy); + return true; + } + } + + return false; +} + +bool MaxisSEDAGToDAGISel::selectVSplatUimmInvPow2(SDValue N, + SDValue &Imm) const { + APInt ImmValue; + EVT EltTy = N->getValueType(0).getVectorElementType(); + + if (N->getOpcode() == ISD::BITCAST) + N = N->getOperand(0); + + if (selectVSplat(N.getNode(), ImmValue, EltTy.getSizeInBits()) && + ImmValue.getBitWidth() == EltTy.getSizeInBits()) { + int32_t Log2 = (~ImmValue).exactLogBase2(); + + if (Log2 != -1) { + Imm = CurDAG->getTargetConstant(Log2, SDLoc(N), EltTy); + return true; + } + } + + return false; +} + +bool MaxisSEDAGToDAGISel::trySelect(SDNode *Node) { + unsigned Opcode = Node->getOpcode(); + SDLoc DL(Node); + + /// + // Instruction Selection not handled by the auto-generated + // tablegen selection should be handled here. + /// + switch(Opcode) { + default: break; + + case ISD::ADDE: { + selectAddE(Node, DL); + return true; + } + + case ISD::ConstantFP: { + ConstantFPSDNode *CN = dyn_cast(Node); + if (Node->getValueType(0) == MVT::f64 && CN->isExactlyValue(+0.0)) { + if (Subtarget->isGP64bit()) { + SDValue Zero = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), DL, + Maxis::ZERO_64, MVT::i64); + ReplaceNode(Node, + CurDAG->getMachineNode(Maxis::DMTC1, DL, MVT::f64, Zero)); + } else if (Subtarget->isFP64bit()) { + SDValue Zero = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), DL, + Maxis::ZERO, MVT::i32); + ReplaceNode(Node, CurDAG->getMachineNode(Maxis::BuildPairF64_64, DL, + MVT::f64, Zero, Zero)); + } else { + SDValue Zero = CurDAG->getCopyFromReg(CurDAG->getEntryNode(), DL, + Maxis::ZERO, MVT::i32); + ReplaceNode(Node, CurDAG->getMachineNode(Maxis::BuildPairF64, DL, + MVT::f64, Zero, Zero)); + } + return true; + } + break; + } + + case ISD::Constant: { + const ConstantSDNode *CN = dyn_cast(Node); + int64_t Imm = CN->getSExtValue(); + unsigned Size = CN->getValueSizeInBits(0); + + if (isInt<32>(Imm)) + break; + + MaxisAnalyzeImmediate AnalyzeImm; + + const MaxisAnalyzeImmediate::InstSeq &Seq = + AnalyzeImm.Analyze(Imm, Size, false); + + MaxisAnalyzeImmediate::InstSeq::const_iterator Inst = Seq.begin(); + SDLoc DL(CN); + SDNode *RegOpnd; + SDValue ImmOpnd = CurDAG->getTargetConstant(SignExtend64<16>(Inst->ImmOpnd), + DL, MVT::i64); + + // The first instruction can be a LUi which is different from other + // instructions (ADDiu, ORI and SLL) in that it does not have a register + // operand. + if (Inst->Opc == Maxis::LUi64) + RegOpnd = CurDAG->getMachineNode(Inst->Opc, DL, MVT::i64, ImmOpnd); + else + RegOpnd = + CurDAG->getMachineNode(Inst->Opc, DL, MVT::i64, + CurDAG->getRegister(Maxis::ZERO_64, MVT::i64), + ImmOpnd); + + // The remaining instructions in the sequence are handled here. + for (++Inst; Inst != Seq.end(); ++Inst) { + ImmOpnd = CurDAG->getTargetConstant(SignExtend64<16>(Inst->ImmOpnd), DL, + MVT::i64); + RegOpnd = CurDAG->getMachineNode(Inst->Opc, DL, MVT::i64, + SDValue(RegOpnd, 0), ImmOpnd); + } + + ReplaceNode(Node, RegOpnd); + return true; + } + + case ISD::INTRINSIC_W_CHAIN: { + switch (cast(Node->getOperand(1))->getZExtValue()) { + default: + break; + + case Intrinsic::maxis_cfcmsa: { + SDValue ChainIn = Node->getOperand(0); + SDValue RegIdx = Node->getOperand(2); + SDValue Reg = CurDAG->getCopyFromReg(ChainIn, DL, + getMSACtrlReg(RegIdx), MVT::i32); + ReplaceNode(Node, Reg.getNode()); + return true; + } + } + break; + } + + case ISD::INTRINSIC_WO_CHAIN: { + switch (cast(Node->getOperand(0))->getZExtValue()) { + default: + break; + + case Intrinsic::maxis_move_v: + // Like an assignment but will always produce a move.v even if + // unnecessary. + ReplaceNode(Node, CurDAG->getMachineNode(Maxis::MOVE_V, DL, + Node->getValueType(0), + Node->getOperand(1))); + return true; + } + break; + } + + case ISD::INTRINSIC_VOID: { + switch (cast(Node->getOperand(1))->getZExtValue()) { + default: + break; + + case Intrinsic::maxis_ctcmsa: { + SDValue ChainIn = Node->getOperand(0); + SDValue RegIdx = Node->getOperand(2); + SDValue Value = Node->getOperand(3); + SDValue ChainOut = CurDAG->getCopyToReg(ChainIn, DL, + getMSACtrlReg(RegIdx), Value); + ReplaceNode(Node, ChainOut.getNode()); + return true; + } + } + break; + } + + // Manually match MaxisISD::Ins nodes to get the correct instruction. It has + // to be done in this fashion so that we respect the differences between + // dins and dinsm, as the difference is that the size operand has the range + // 0 < size <= 32 for dins while dinsm has the range 2 <= size <= 64 which + // means SelectionDAGISel would have to test all the operands at once to + // match the instruction. + case MaxisISD::Ins: { + + // Sanity checking for the node operands. + if (Node->getValueType(0) != MVT::i32 && Node->getValueType(0) != MVT::i64) + return false; + + if (Node->getNumOperands() != 4) + return false; + + if (Node->getOperand(1)->getOpcode() != ISD::Constant || + Node->getOperand(2)->getOpcode() != ISD::Constant) + return false; + + MVT ResTy = Node->getSimpleValueType(0); + uint64_t Pos = Node->getConstantOperandVal(1); + uint64_t Size = Node->getConstantOperandVal(2); + + // Size has to be >0 for 'ins', 'dins' and 'dinsu'. + if (!Size) + return false; + + if (Pos + Size > 64) + return false; + + if (ResTy != MVT::i32 && ResTy != MVT::i64) + return false; + + unsigned Opcode = 0; + if (ResTy == MVT::i32) { + if (Pos + Size <= 32) + Opcode = Maxis::INS; + } else { + if (Pos + Size <= 32) + Opcode = Maxis::DINS; + else if (Pos < 32 && 1 < Size) + Opcode = Maxis::DINSM; + else + Opcode = Maxis::DINSU; + } + + if (Opcode) { + SDValue Ops[4] = { + Node->getOperand(0), CurDAG->getTargetConstant(Pos, DL, MVT::i32), + CurDAG->getTargetConstant(Size, DL, MVT::i32), Node->getOperand(3)}; + + ReplaceNode(Node, CurDAG->getMachineNode(Opcode, DL, ResTy, Ops)); + return true; + } + + return false; + } + + case MaxisISD::ThreadPointer: { + EVT PtrVT = getTargetLowering()->getPointerTy(CurDAG->getDataLayout()); + unsigned RdhwrOpc, DestReg; + + if (PtrVT == MVT::i32) { + RdhwrOpc = Maxis::RDHWR; + DestReg = Maxis::V1; + } else { + RdhwrOpc = Maxis::RDHWR64; + DestReg = Maxis::V1_64; + } + + SDNode *Rdhwr = + CurDAG->getMachineNode(RdhwrOpc, DL, + Node->getValueType(0), + CurDAG->getRegister(Maxis::HWR29, MVT::i32)); + SDValue Chain = CurDAG->getCopyToReg(CurDAG->getEntryNode(), DL, DestReg, + SDValue(Rdhwr, 0)); + SDValue ResNode = CurDAG->getCopyFromReg(Chain, DL, DestReg, PtrVT); + ReplaceNode(Node, ResNode.getNode()); + return true; + } + + case ISD::BUILD_VECTOR: { + // Select appropriate ldi.[bhwd] instructions for constant splats of + // 128-bit when MSA is enabled. Fixup any register class mismatches that + // occur as a result. + // + // This allows the compiler to use a wider range of immediates than would + // otherwise be allowed. If, for example, v4i32 could only use ldi.h then + // it would not be possible to load { 0x01010101, 0x01010101, 0x01010101, + // 0x01010101 } without using a constant pool. This would be sub-optimal + // when // 'ldi.b wd, 1' is capable of producing that bit-pattern in the + // same set/ of registers. Similarly, ldi.h isn't capable of producing { + // 0x00000000, 0x00000001, 0x00000000, 0x00000001 } but 'ldi.d wd, 1' can. + + const MaxisABIInfo &ABI = + static_cast(TM).getABI(); + + BuildVectorSDNode *BVN = cast(Node); + APInt SplatValue, SplatUndef; + unsigned SplatBitSize; + bool HasAnyUndefs; + unsigned LdiOp; + EVT ResVecTy = BVN->getValueType(0); + EVT ViaVecTy; + + if (!Subtarget->hasMSA() || !BVN->getValueType(0).is128BitVector()) + return false; + + if (!BVN->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, + HasAnyUndefs, 8, + !Subtarget->isLittle())) + return false; + + switch (SplatBitSize) { + default: + return false; + case 8: + LdiOp = Maxis::LDI_B; + ViaVecTy = MVT::v16i8; + break; + case 16: + LdiOp = Maxis::LDI_H; + ViaVecTy = MVT::v8i16; + break; + case 32: + LdiOp = Maxis::LDI_W; + ViaVecTy = MVT::v4i32; + break; + case 64: + LdiOp = Maxis::LDI_D; + ViaVecTy = MVT::v2i64; + break; + } + + SDNode *Res; + + // If we have a signed 10 bit integer, we can splat it directly. + // + // If we have something bigger we can synthesize the value into a GPR and + // splat from there. + if (SplatValue.isSignedIntN(10)) { + SDValue Imm = CurDAG->getTargetConstant(SplatValue, DL, + ViaVecTy.getVectorElementType()); + + Res = CurDAG->getMachineNode(LdiOp, DL, ViaVecTy, Imm); + } else if (SplatValue.isSignedIntN(16) && + ((ABI.IsO32() && SplatBitSize < 64) || + (ABI.IsN32() || ABI.IsN64()))) { + // Only handle signed 16 bit values when the element size is GPR width. + // MAXIS64 can handle all the cases but MAXIS32 would need to handle + // negative cases specifically here. Instead, handle those cases as + // 64bit values. + + bool Is32BitSplat = ABI.IsO32() || SplatBitSize < 64; + const unsigned ADDiuOp = Is32BitSplat ? Maxis::ADDiu : Maxis::DADDiu; + const MVT SplatMVT = Is32BitSplat ? MVT::i32 : MVT::i64; + SDValue ZeroVal = CurDAG->getRegister( + Is32BitSplat ? Maxis::ZERO : Maxis::ZERO_64, SplatMVT); + + const unsigned FILLOp = + SplatBitSize == 16 + ? Maxis::FILL_H + : (SplatBitSize == 32 ? Maxis::FILL_W + : (SplatBitSize == 64 ? Maxis::FILL_D : 0)); + + assert(FILLOp != 0 && "Unknown FILL Op for splat synthesis!"); + assert((!ABI.IsO32() || (FILLOp != Maxis::FILL_D)) && + "Attempting to use fill.d on MAXIS32!"); + + const unsigned Lo = SplatValue.getLoBits(16).getZExtValue(); + SDValue LoVal = CurDAG->getTargetConstant(Lo, DL, SplatMVT); + + Res = CurDAG->getMachineNode(ADDiuOp, DL, SplatMVT, ZeroVal, LoVal); + Res = CurDAG->getMachineNode(FILLOp, DL, ViaVecTy, SDValue(Res, 0)); + + } else if (SplatValue.isSignedIntN(32) && SplatBitSize == 32) { + // Only handle the cases where the splat size agrees with the size + // of the SplatValue here. + const unsigned Lo = SplatValue.getLoBits(16).getZExtValue(); + const unsigned Hi = SplatValue.lshr(16).getLoBits(16).getZExtValue(); + SDValue ZeroVal = CurDAG->getRegister(Maxis::ZERO, MVT::i32); + + SDValue LoVal = CurDAG->getTargetConstant(Lo, DL, MVT::i32); + SDValue HiVal = CurDAG->getTargetConstant(Hi, DL, MVT::i32); + + if (Hi) + Res = CurDAG->getMachineNode(Maxis::LUi, DL, MVT::i32, HiVal); + + if (Lo) + Res = CurDAG->getMachineNode(Maxis::ORi, DL, MVT::i32, + Hi ? SDValue(Res, 0) : ZeroVal, LoVal); + + assert((Hi || Lo) && "Zero case reached 32 bit case splat synthesis!"); + Res = CurDAG->getMachineNode(Maxis::FILL_W, DL, MVT::v4i32, SDValue(Res, 0)); + + } else if (SplatValue.isSignedIntN(32) && SplatBitSize == 64 && + (ABI.IsN32() || ABI.IsN64())) { + // N32 and N64 can perform some tricks that O32 can't for signed 32 bit + // integers due to having 64bit registers. lui will cause the necessary + // zero/sign extension. + const unsigned Lo = SplatValue.getLoBits(16).getZExtValue(); + const unsigned Hi = SplatValue.lshr(16).getLoBits(16).getZExtValue(); + SDValue ZeroVal = CurDAG->getRegister(Maxis::ZERO, MVT::i32); + + SDValue LoVal = CurDAG->getTargetConstant(Lo, DL, MVT::i32); + SDValue HiVal = CurDAG->getTargetConstant(Hi, DL, MVT::i32); + + if (Hi) + Res = CurDAG->getMachineNode(Maxis::LUi, DL, MVT::i32, HiVal); + + if (Lo) + Res = CurDAG->getMachineNode(Maxis::ORi, DL, MVT::i32, + Hi ? SDValue(Res, 0) : ZeroVal, LoVal); + + Res = CurDAG->getMachineNode( + Maxis::SUBREG_TO_REG, DL, MVT::i64, + CurDAG->getTargetConstant(((Hi >> 15) & 0x1), DL, MVT::i64), + SDValue(Res, 0), + CurDAG->getTargetConstant(Maxis::sub_32, DL, MVT::i64)); + + Res = + CurDAG->getMachineNode(Maxis::FILL_D, DL, MVT::v2i64, SDValue(Res, 0)); + + } else if (SplatValue.isSignedIntN(64)) { + // If we have a 64 bit Splat value, we perform a similar sequence to the + // above: + // + // MAXIS32: MAXIS64: + // lui $res, %highest(val) lui $res, %highest(val) + // ori $res, $res, %higher(val) ori $res, $res, %higher(val) + // lui $res2, %hi(val) lui $res2, %hi(val) + // ori $res2, %res2, %lo(val) ori $res2, %res2, %lo(val) + // $res3 = fill $res2 dinsu $res, $res2, 0, 32 + // $res4 = insert.w $res3[1], $res fill.d $res + // splat.d $res4, 0 + // + // The ability to use dinsu is guaranteed as MSA requires MAXISR5. This saves + // having to materialize the value by shifts and ors. + // + // FIXME: Implement the preferred sequence for MAXIS64R6: + // + // MAXIS64R6: + // ori $res, $zero, %lo(val) + // daui $res, $res, %hi(val) + // dahi $res, $res, %higher(val) + // dati $res, $res, %highest(cal) + // fill.d $res + // + + const unsigned Lo = SplatValue.getLoBits(16).getZExtValue(); + const unsigned Hi = SplatValue.lshr(16).getLoBits(16).getZExtValue(); + const unsigned Higher = SplatValue.lshr(32).getLoBits(16).getZExtValue(); + const unsigned Highest = SplatValue.lshr(48).getLoBits(16).getZExtValue(); + + SDValue LoVal = CurDAG->getTargetConstant(Lo, DL, MVT::i32); + SDValue HiVal = CurDAG->getTargetConstant(Hi, DL, MVT::i32); + SDValue HigherVal = CurDAG->getTargetConstant(Higher, DL, MVT::i32); + SDValue HighestVal = CurDAG->getTargetConstant(Highest, DL, MVT::i32); + SDValue ZeroVal = CurDAG->getRegister(Maxis::ZERO, MVT::i32); + + // Independent of whether we're targeting MAXIS64 or not, the basic + // operations are the same. Also, directly use the $zero register if + // the 16 bit chunk is zero. + // + // For optimization purposes we always synthesize the splat value as + // an i32 value, then if we're targetting MAXIS64, use SUBREG_TO_REG + // just before combining the values with dinsu to produce an i64. This + // enables SelectionDAG to aggressively share components of splat values + // where possible. + // + // FIXME: This is the general constant synthesis problem. This code + // should be factored out into a class shared between all the + // classes that need it. Specifically, for a splat size of 64 + // bits that's a negative number we can do better than LUi/ORi + // for the upper 32bits. + + if (Hi) + Res = CurDAG->getMachineNode(Maxis::LUi, DL, MVT::i32, HiVal); + + if (Lo) + Res = CurDAG->getMachineNode(Maxis::ORi, DL, MVT::i32, + Hi ? SDValue(Res, 0) : ZeroVal, LoVal); + + SDNode *HiRes; + if (Highest) + HiRes = CurDAG->getMachineNode(Maxis::LUi, DL, MVT::i32, HighestVal); + + if (Higher) + HiRes = CurDAG->getMachineNode(Maxis::ORi, DL, MVT::i32, + Highest ? SDValue(HiRes, 0) : ZeroVal, + HigherVal); + + + if (ABI.IsO32()) { + Res = CurDAG->getMachineNode(Maxis::FILL_W, DL, MVT::v4i32, + (Hi || Lo) ? SDValue(Res, 0) : ZeroVal); + + Res = CurDAG->getMachineNode( + Maxis::INSERT_W, DL, MVT::v4i32, SDValue(Res, 0), + (Highest || Higher) ? SDValue(HiRes, 0) : ZeroVal, + CurDAG->getTargetConstant(1, DL, MVT::i32)); + + const TargetLowering *TLI = getTargetLowering(); + const TargetRegisterClass *RC = + TLI->getRegClassFor(ViaVecTy.getSimpleVT()); + + Res = CurDAG->getMachineNode( + Maxis::COPY_TO_REGCLASS, DL, ViaVecTy, SDValue(Res, 0), + CurDAG->getTargetConstant(RC->getID(), DL, MVT::i32)); + + Res = CurDAG->getMachineNode( + Maxis::SPLATI_D, DL, MVT::v2i64, SDValue(Res, 0), + CurDAG->getTargetConstant(0, DL, MVT::i32)); + } else if (ABI.IsN64() || ABI.IsN32()) { + + SDValue Zero64Val = CurDAG->getRegister(Maxis::ZERO_64, MVT::i64); + const bool HiResNonZero = Highest || Higher; + const bool ResNonZero = Hi || Lo; + + if (HiResNonZero) + HiRes = CurDAG->getMachineNode( + Maxis::SUBREG_TO_REG, DL, MVT::i64, + CurDAG->getTargetConstant(((Highest >> 15) & 0x1), DL, MVT::i64), + SDValue(HiRes, 0), + CurDAG->getTargetConstant(Maxis::sub_32, DL, MVT::i64)); + + if (ResNonZero) + Res = CurDAG->getMachineNode( + Maxis::SUBREG_TO_REG, DL, MVT::i64, + CurDAG->getTargetConstant(((Hi >> 15) & 0x1), DL, MVT::i64), + SDValue(Res, 0), + CurDAG->getTargetConstant(Maxis::sub_32, DL, MVT::i64)); + + // We have 3 cases: + // The HiRes is nonzero but Res is $zero => dsll32 HiRes, 0 + // The Res is nonzero but HiRes is $zero => dinsu Res, $zero, 32, 32 + // Both are non zero => dinsu Res, HiRes, 32, 32 + // + // The obvious "missing" case is when both are zero, but that case is + // handled by the ldi case. + if (ResNonZero) { + IntegerType *Int32Ty = + IntegerType::get(MF->getFunction().getContext(), 32); + const ConstantInt *Const32 = ConstantInt::get(Int32Ty, 32); + SDValue Ops[4] = {HiResNonZero ? SDValue(HiRes, 0) : Zero64Val, + CurDAG->getConstant(*Const32, DL, MVT::i32), + CurDAG->getConstant(*Const32, DL, MVT::i32), + SDValue(Res, 0)}; + + Res = CurDAG->getMachineNode(Maxis::DINSU, DL, MVT::i64, Ops); + } else if (HiResNonZero) { + Res = CurDAG->getMachineNode( + Maxis::DSLL32, DL, MVT::i64, SDValue(HiRes, 0), + CurDAG->getTargetConstant(0, DL, MVT::i32)); + } else + llvm_unreachable( + "Zero splat value handled by non-zero 64bit splat synthesis!"); + + Res = CurDAG->getMachineNode(Maxis::FILL_D, DL, MVT::v2i64, SDValue(Res, 0)); + } else + llvm_unreachable("Unknown ABI in MaxisISelDAGToDAG!"); + + } else + return false; + + if (ResVecTy != ViaVecTy) { + // If LdiOp is writing to a different register class to ResVecTy, then + // fix it up here. This COPY_TO_REGCLASS should never cause a move.v + // since the source and destination register sets contain the same + // registers. + const TargetLowering *TLI = getTargetLowering(); + MVT ResVecTySimple = ResVecTy.getSimpleVT(); + const TargetRegisterClass *RC = TLI->getRegClassFor(ResVecTySimple); + Res = CurDAG->getMachineNode(Maxis::COPY_TO_REGCLASS, DL, + ResVecTy, SDValue(Res, 0), + CurDAG->getTargetConstant(RC->getID(), DL, + MVT::i32)); + } + + ReplaceNode(Node, Res); + return true; + } + + } + + return false; +} + +bool MaxisSEDAGToDAGISel:: +SelectInlineAsmMemoryOperand(const SDValue &Op, unsigned ConstraintID, + std::vector &OutOps) { + SDValue Base, Offset; + + switch(ConstraintID) { + default: + llvm_unreachable("Unexpected asm memory constraint"); + // All memory constraints can at least accept raw pointers. + case InlineAsm::Constraint_i: + OutOps.push_back(Op); + OutOps.push_back(CurDAG->getTargetConstant(0, SDLoc(Op), MVT::i32)); + return false; + case InlineAsm::Constraint_m: + if (selectAddrRegImm16(Op, Base, Offset)) { + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + OutOps.push_back(Op); + OutOps.push_back(CurDAG->getTargetConstant(0, SDLoc(Op), MVT::i32)); + return false; + case InlineAsm::Constraint_R: + // The 'R' constraint is supposed to be much more complicated than this. + // However, it's becoming less useful due to architectural changes and + // ought to be replaced by other constraints such as 'ZC'. + // For now, support 9-bit signed offsets which is supportable by all + // subtargets for all instructions. + if (selectAddrRegImm9(Op, Base, Offset)) { + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + OutOps.push_back(Op); + OutOps.push_back(CurDAG->getTargetConstant(0, SDLoc(Op), MVT::i32)); + return false; + case InlineAsm::Constraint_ZC: + // ZC matches whatever the pref, ll, and sc instructions can handle for the + // given subtarget. + if (Subtarget->inMicroMaxisMode()) { + // On microMAXIS, they can handle 12-bit offsets. + if (selectAddrRegImm12(Op, Base, Offset)) { + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + } else if (Subtarget->hasMaxis32r6()) { + // On MAXIS32r6/MAXIS64r6, they can only handle 9-bit offsets. + if (selectAddrRegImm9(Op, Base, Offset)) { + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + } else if (selectAddrRegImm16(Op, Base, Offset)) { + // Prior to MAXIS32r6/MAXIS64r6, they can handle 16-bit offsets. + OutOps.push_back(Base); + OutOps.push_back(Offset); + return false; + } + // In all cases, 0-bit offsets are acceptable. + OutOps.push_back(Op); + OutOps.push_back(CurDAG->getTargetConstant(0, SDLoc(Op), MVT::i32)); + return false; + } + return true; +} + +FunctionPass *llvm::createMaxisSEISelDag(MaxisTargetMachine &TM, + CodeGenOpt::Level OptLevel) { + return new MaxisSEDAGToDAGISel(TM, OptLevel); +} diff --git a/lib/Target/Maxis/MaxisSEISelDAGToDAG.h b/lib/Target/Maxis/MaxisSEISelDAGToDAG.h new file mode 100644 index 00000000..e17aebec --- /dev/null +++ b/lib/Target/Maxis/MaxisSEISelDAGToDAG.h @@ -0,0 +1,147 @@ +//===-- MaxisSEISelDAGToDAG.h - A Dag to Dag Inst Selector for MaxisSE -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Subclass of MaxisDAGToDAGISel specialized for maxis32/64. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISSEISELDAGTODAG_H +#define LLVM_LIB_TARGET_MAXIS_MAXISSEISELDAGTODAG_H + +#include "MaxisISelDAGToDAG.h" + +namespace llvm { + +class MaxisSEDAGToDAGISel : public MaxisDAGToDAGISel { + +public: + explicit MaxisSEDAGToDAGISel(MaxisTargetMachine &TM, CodeGenOpt::Level OL) + : MaxisDAGToDAGISel(TM, OL) {} + +private: + + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override; + + void addDSPCtrlRegOperands(bool IsDef, MachineInstr &MI, + MachineFunction &MF); + + unsigned getMSACtrlReg(const SDValue RegIdx) const; + + bool replaceUsesWithZeroReg(MachineRegisterInfo *MRI, const MachineInstr&); + + std::pair selectMULT(SDNode *N, unsigned Opc, + const SDLoc &dl, EVT Ty, bool HasLo, + bool HasHi); + + void selectAddE(SDNode *Node, const SDLoc &DL) const; + + bool selectAddrFrameIndex(SDValue Addr, SDValue &Base, SDValue &Offset) const; + bool selectAddrFrameIndexOffset(SDValue Addr, SDValue &Base, SDValue &Offset, + unsigned OffsetBits, + unsigned ShiftAmount) const; + + bool selectAddrRegImm(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectAddrDefault(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectIntAddr(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectAddrRegImm9(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + bool selectAddrRegImm11(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + bool selectAddrRegImm12(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + bool selectAddrRegImm16(SDValue Addr, SDValue &Base, + SDValue &Offset) const; + + bool selectIntAddr11MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectIntAddr12MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectIntAddr16MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectIntAddrLSL2MM(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectIntAddrSImm10(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectIntAddrSImm10Lsl1(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectIntAddrSImm10Lsl2(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + bool selectIntAddrSImm10Lsl3(SDValue Addr, SDValue &Base, + SDValue &Offset) const override; + + /// \brief Select constant vector splats. + bool selectVSplat(SDNode *N, APInt &Imm, + unsigned MinSizeInBits) const override; + /// \brief Select constant vector splats whose value fits in a given integer. + bool selectVSplatCommon(SDValue N, SDValue &Imm, bool Signed, + unsigned ImmBitSize) const; + /// \brief Select constant vector splats whose value fits in a uimm1. + bool selectVSplatUimm1(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value fits in a uimm2. + bool selectVSplatUimm2(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value fits in a uimm3. + bool selectVSplatUimm3(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value fits in a uimm4. + bool selectVSplatUimm4(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value fits in a uimm5. + bool selectVSplatUimm5(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value fits in a uimm6. + bool selectVSplatUimm6(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value fits in a uimm8. + bool selectVSplatUimm8(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value fits in a simm5. + bool selectVSplatSimm5(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value is a power of 2. + bool selectVSplatUimmPow2(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value is the inverse of a + /// power of 2. + bool selectVSplatUimmInvPow2(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value is a run of set bits + /// ending at the most significant bit + bool selectVSplatMaskL(SDValue N, SDValue &Imm) const override; + /// \brief Select constant vector splats whose value is a run of set bits + /// starting at bit zero. + bool selectVSplatMaskR(SDValue N, SDValue &Imm) const override; + + bool trySelect(SDNode *Node) override; + + void processFunctionAfterISel(MachineFunction &MF) override; + + // Insert instructions to initialize the global base register in the + // first MBB of the function. + void initGlobalBaseReg(MachineFunction &MF); + + bool SelectInlineAsmMemoryOperand(const SDValue &Op, + unsigned ConstraintID, + std::vector &OutOps) override; +}; + +FunctionPass *createMaxisSEISelDag(MaxisTargetMachine &TM, + CodeGenOpt::Level OptLevel); +} + +#endif diff --git a/lib/Target/Maxis/MaxisSEISelLowering.cpp b/lib/Target/Maxis/MaxisSEISelLowering.cpp new file mode 100644 index 00000000..1799a4f1 --- /dev/null +++ b/lib/Target/Maxis/MaxisSEISelLowering.cpp @@ -0,0 +1,3796 @@ +//===- MaxisSEISelLowering.cpp - MaxisSE DAG Lowering Interface -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Subclass of MaxisTargetLowering specialized for maxis32/64. +// +//===----------------------------------------------------------------------===// + +#include "MaxisSEISelLowering.h" +#include "MaxisMachineFunction.h" +#include "MaxisRegisterInfo.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/CodeGen/ISDOpcodes.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstr.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/CodeGen/SelectionDAG.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/CodeGen/ValueTypes.h" +#include "llvm/IR/DebugLoc.h" +#include "llvm/IR/Intrinsics.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis-isel" + +static cl::opt +UseMaxisTailCalls("maxis-tail-calls", cl::Hidden, + cl::desc("MAXIS: permit tail calls."), cl::init(false)); + +static cl::opt NoDPLoadStore("mno-ldc1-sdc1", cl::init(false), + cl::desc("Expand double precision loads and " + "stores to their single precision " + "counterparts")); + +MaxisSETargetLowering::MaxisSETargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI) + : MaxisTargetLowering(TM, STI) { + // Set up the register classes + addRegisterClass(MVT::i32, &Maxis::GPR32RegClass); + + if (Subtarget.isGP64bit()) + addRegisterClass(MVT::i64, &Maxis::GPR64RegClass); + + if (Subtarget.hasDSP() || Subtarget.hasMSA()) { + // Expand all truncating stores and extending loads. + for (MVT VT0 : MVT::vector_valuetypes()) { + for (MVT VT1 : MVT::vector_valuetypes()) { + setTruncStoreAction(VT0, VT1, Expand); + setLoadExtAction(ISD::SEXTLOAD, VT0, VT1, Expand); + setLoadExtAction(ISD::ZEXTLOAD, VT0, VT1, Expand); + setLoadExtAction(ISD::EXTLOAD, VT0, VT1, Expand); + } + } + } + + if (Subtarget.hasDSP()) { + MVT::SimpleValueType VecTys[2] = {MVT::v2i16, MVT::v4i8}; + + for (unsigned i = 0; i < array_lengthof(VecTys); ++i) { + addRegisterClass(VecTys[i], &Maxis::DSPRRegClass); + + // Expand all builtin opcodes. + for (unsigned Opc = 0; Opc < ISD::BUILTIN_OP_END; ++Opc) + setOperationAction(Opc, VecTys[i], Expand); + + setOperationAction(ISD::ADD, VecTys[i], Legal); + setOperationAction(ISD::SUB, VecTys[i], Legal); + setOperationAction(ISD::LOAD, VecTys[i], Legal); + setOperationAction(ISD::STORE, VecTys[i], Legal); + setOperationAction(ISD::BITCAST, VecTys[i], Legal); + } + + setTargetDAGCombine(ISD::SHL); + setTargetDAGCombine(ISD::SRA); + setTargetDAGCombine(ISD::SRL); + setTargetDAGCombine(ISD::SETCC); + setTargetDAGCombine(ISD::VSELECT); + } + + if (Subtarget.hasDSPR2()) + setOperationAction(ISD::MUL, MVT::v2i16, Legal); + + if (Subtarget.hasMSA()) { + addMSAIntType(MVT::v16i8, &Maxis::MSA128BRegClass); + addMSAIntType(MVT::v8i16, &Maxis::MSA128HRegClass); + addMSAIntType(MVT::v4i32, &Maxis::MSA128WRegClass); + addMSAIntType(MVT::v2i64, &Maxis::MSA128DRegClass); + addMSAFloatType(MVT::v8f16, &Maxis::MSA128HRegClass); + addMSAFloatType(MVT::v4f32, &Maxis::MSA128WRegClass); + addMSAFloatType(MVT::v2f64, &Maxis::MSA128DRegClass); + + // f16 is a storage-only type, always promote it to f32. + addRegisterClass(MVT::f16, &Maxis::MSA128HRegClass); + setOperationAction(ISD::SETCC, MVT::f16, Promote); + setOperationAction(ISD::BR_CC, MVT::f16, Promote); + setOperationAction(ISD::SELECT_CC, MVT::f16, Promote); + setOperationAction(ISD::SELECT, MVT::f16, Promote); + setOperationAction(ISD::FADD, MVT::f16, Promote); + setOperationAction(ISD::FSUB, MVT::f16, Promote); + setOperationAction(ISD::FMUL, MVT::f16, Promote); + setOperationAction(ISD::FDIV, MVT::f16, Promote); + setOperationAction(ISD::FREM, MVT::f16, Promote); + setOperationAction(ISD::FMA, MVT::f16, Promote); + setOperationAction(ISD::FNEG, MVT::f16, Promote); + setOperationAction(ISD::FABS, MVT::f16, Promote); + setOperationAction(ISD::FCEIL, MVT::f16, Promote); + setOperationAction(ISD::FCOPYSIGN, MVT::f16, Promote); + setOperationAction(ISD::FCOS, MVT::f16, Promote); + setOperationAction(ISD::FP_EXTEND, MVT::f16, Promote); + setOperationAction(ISD::FFLOOR, MVT::f16, Promote); + setOperationAction(ISD::FNEARBYINT, MVT::f16, Promote); + setOperationAction(ISD::FPOW, MVT::f16, Promote); + setOperationAction(ISD::FPOWI, MVT::f16, Promote); + setOperationAction(ISD::FRINT, MVT::f16, Promote); + setOperationAction(ISD::FSIN, MVT::f16, Promote); + setOperationAction(ISD::FSINCOS, MVT::f16, Promote); + setOperationAction(ISD::FSQRT, MVT::f16, Promote); + setOperationAction(ISD::FEXP, MVT::f16, Promote); + setOperationAction(ISD::FEXP2, MVT::f16, Promote); + setOperationAction(ISD::FLOG, MVT::f16, Promote); + setOperationAction(ISD::FLOG2, MVT::f16, Promote); + setOperationAction(ISD::FLOG10, MVT::f16, Promote); + setOperationAction(ISD::FROUND, MVT::f16, Promote); + setOperationAction(ISD::FTRUNC, MVT::f16, Promote); + setOperationAction(ISD::FMINNUM, MVT::f16, Promote); + setOperationAction(ISD::FMAXNUM, MVT::f16, Promote); + setOperationAction(ISD::FMINNAN, MVT::f16, Promote); + setOperationAction(ISD::FMAXNAN, MVT::f16, Promote); + + setTargetDAGCombine(ISD::AND); + setTargetDAGCombine(ISD::OR); + setTargetDAGCombine(ISD::SRA); + setTargetDAGCombine(ISD::VSELECT); + setTargetDAGCombine(ISD::XOR); + } + + if (!Subtarget.useSoftFloat()) { + addRegisterClass(MVT::f32, &Maxis::FGR32RegClass); + + // When dealing with single precision only, use libcalls + if (!Subtarget.isSingleFloat()) { + if (Subtarget.isFP64bit()) + addRegisterClass(MVT::f64, &Maxis::FGR64RegClass); + else + addRegisterClass(MVT::f64, &Maxis::AFGR64RegClass); + } + } + + setOperationAction(ISD::SMUL_LOHI, MVT::i32, Custom); + setOperationAction(ISD::UMUL_LOHI, MVT::i32, Custom); + setOperationAction(ISD::MULHS, MVT::i32, Custom); + setOperationAction(ISD::MULHU, MVT::i32, Custom); + + if (Subtarget.hasCnMaxis()) + setOperationAction(ISD::MUL, MVT::i64, Legal); + else if (Subtarget.isGP64bit()) + setOperationAction(ISD::MUL, MVT::i64, Custom); + + if (Subtarget.isGP64bit()) { + setOperationAction(ISD::SMUL_LOHI, MVT::i64, Custom); + setOperationAction(ISD::UMUL_LOHI, MVT::i64, Custom); + setOperationAction(ISD::MULHS, MVT::i64, Custom); + setOperationAction(ISD::MULHU, MVT::i64, Custom); + setOperationAction(ISD::SDIVREM, MVT::i64, Custom); + setOperationAction(ISD::UDIVREM, MVT::i64, Custom); + } + + setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::i64, Custom); + setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::i64, Custom); + + setOperationAction(ISD::SDIVREM, MVT::i32, Custom); + setOperationAction(ISD::UDIVREM, MVT::i32, Custom); + setOperationAction(ISD::ATOMIC_FENCE, MVT::Other, Custom); + setOperationAction(ISD::LOAD, MVT::i32, Custom); + setOperationAction(ISD::STORE, MVT::i32, Custom); + + setTargetDAGCombine(ISD::MUL); + + setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom); + setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom); + setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom); + + if (NoDPLoadStore) { + setOperationAction(ISD::LOAD, MVT::f64, Custom); + setOperationAction(ISD::STORE, MVT::f64, Custom); + } + + if (Subtarget.hasMaxis32r6()) { + // MAXIS32r6 replaces the accumulator-based multiplies with a three register + // instruction + setOperationAction(ISD::SMUL_LOHI, MVT::i32, Expand); + setOperationAction(ISD::UMUL_LOHI, MVT::i32, Expand); + setOperationAction(ISD::MUL, MVT::i32, Legal); + setOperationAction(ISD::MULHS, MVT::i32, Legal); + setOperationAction(ISD::MULHU, MVT::i32, Legal); + + // MAXIS32r6 replaces the accumulator-based division/remainder with separate + // three register division and remainder instructions. + setOperationAction(ISD::SDIVREM, MVT::i32, Expand); + setOperationAction(ISD::UDIVREM, MVT::i32, Expand); + setOperationAction(ISD::SDIV, MVT::i32, Legal); + setOperationAction(ISD::UDIV, MVT::i32, Legal); + setOperationAction(ISD::SREM, MVT::i32, Legal); + setOperationAction(ISD::UREM, MVT::i32, Legal); + + // MAXIS32r6 replaces conditional moves with an equivalent that removes the + // need for three GPR read ports. + setOperationAction(ISD::SETCC, MVT::i32, Legal); + setOperationAction(ISD::SELECT, MVT::i32, Legal); + setOperationAction(ISD::SELECT_CC, MVT::i32, Expand); + + setOperationAction(ISD::SETCC, MVT::f32, Legal); + setOperationAction(ISD::SELECT, MVT::f32, Legal); + setOperationAction(ISD::SELECT_CC, MVT::f32, Expand); + + assert(Subtarget.isFP64bit() && "FR=1 is required for MAXIS32r6"); + setOperationAction(ISD::SETCC, MVT::f64, Legal); + setOperationAction(ISD::SELECT, MVT::f64, Custom); + setOperationAction(ISD::SELECT_CC, MVT::f64, Expand); + + setOperationAction(ISD::BRCOND, MVT::Other, Legal); + + // Floating point > and >= are supported via < and <= + setCondCodeAction(ISD::SETOGE, MVT::f32, Expand); + setCondCodeAction(ISD::SETOGT, MVT::f32, Expand); + setCondCodeAction(ISD::SETUGE, MVT::f32, Expand); + setCondCodeAction(ISD::SETUGT, MVT::f32, Expand); + + setCondCodeAction(ISD::SETOGE, MVT::f64, Expand); + setCondCodeAction(ISD::SETOGT, MVT::f64, Expand); + setCondCodeAction(ISD::SETUGE, MVT::f64, Expand); + setCondCodeAction(ISD::SETUGT, MVT::f64, Expand); + } + + if (Subtarget.hasMaxis64r6()) { + // MAXIS64r6 replaces the accumulator-based multiplies with a three register + // instruction + setOperationAction(ISD::SMUL_LOHI, MVT::i64, Expand); + setOperationAction(ISD::UMUL_LOHI, MVT::i64, Expand); + setOperationAction(ISD::MUL, MVT::i64, Legal); + setOperationAction(ISD::MULHS, MVT::i64, Legal); + setOperationAction(ISD::MULHU, MVT::i64, Legal); + + // MAXIS32r6 replaces the accumulator-based division/remainder with separate + // three register division and remainder instructions. + setOperationAction(ISD::SDIVREM, MVT::i64, Expand); + setOperationAction(ISD::UDIVREM, MVT::i64, Expand); + setOperationAction(ISD::SDIV, MVT::i64, Legal); + setOperationAction(ISD::UDIV, MVT::i64, Legal); + setOperationAction(ISD::SREM, MVT::i64, Legal); + setOperationAction(ISD::UREM, MVT::i64, Legal); + + // MAXIS64r6 replaces conditional moves with an equivalent that removes the + // need for three GPR read ports. + setOperationAction(ISD::SETCC, MVT::i64, Legal); + setOperationAction(ISD::SELECT, MVT::i64, Legal); + setOperationAction(ISD::SELECT_CC, MVT::i64, Expand); + } + + computeRegisterProperties(Subtarget.getRegisterInfo()); +} + +const MaxisTargetLowering * +llvm::createMaxisSETargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI) { + return new MaxisSETargetLowering(TM, STI); +} + +const TargetRegisterClass * +MaxisSETargetLowering::getRepRegClassFor(MVT VT) const { + if (VT == MVT::Untyped) + return Subtarget.hasDSP() ? &Maxis::ACC64DSPRegClass : &Maxis::ACC64RegClass; + + return TargetLowering::getRepRegClassFor(VT); +} + +// Enable MSA support for the given integer type and Register class. +void MaxisSETargetLowering:: +addMSAIntType(MVT::SimpleValueType Ty, const TargetRegisterClass *RC) { + addRegisterClass(Ty, RC); + + // Expand all builtin opcodes. + for (unsigned Opc = 0; Opc < ISD::BUILTIN_OP_END; ++Opc) + setOperationAction(Opc, Ty, Expand); + + setOperationAction(ISD::BITCAST, Ty, Legal); + setOperationAction(ISD::LOAD, Ty, Legal); + setOperationAction(ISD::STORE, Ty, Legal); + setOperationAction(ISD::EXTRACT_VECTOR_ELT, Ty, Custom); + setOperationAction(ISD::INSERT_VECTOR_ELT, Ty, Legal); + setOperationAction(ISD::BUILD_VECTOR, Ty, Custom); + + setOperationAction(ISD::ADD, Ty, Legal); + setOperationAction(ISD::AND, Ty, Legal); + setOperationAction(ISD::CTLZ, Ty, Legal); + setOperationAction(ISD::CTPOP, Ty, Legal); + setOperationAction(ISD::MUL, Ty, Legal); + setOperationAction(ISD::OR, Ty, Legal); + setOperationAction(ISD::SDIV, Ty, Legal); + setOperationAction(ISD::SREM, Ty, Legal); + setOperationAction(ISD::SHL, Ty, Legal); + setOperationAction(ISD::SRA, Ty, Legal); + setOperationAction(ISD::SRL, Ty, Legal); + setOperationAction(ISD::SUB, Ty, Legal); + setOperationAction(ISD::UDIV, Ty, Legal); + setOperationAction(ISD::UREM, Ty, Legal); + setOperationAction(ISD::VECTOR_SHUFFLE, Ty, Custom); + setOperationAction(ISD::VSELECT, Ty, Legal); + setOperationAction(ISD::XOR, Ty, Legal); + + if (Ty == MVT::v4i32 || Ty == MVT::v2i64) { + setOperationAction(ISD::FP_TO_SINT, Ty, Legal); + setOperationAction(ISD::FP_TO_UINT, Ty, Legal); + setOperationAction(ISD::SINT_TO_FP, Ty, Legal); + setOperationAction(ISD::UINT_TO_FP, Ty, Legal); + } + + setOperationAction(ISD::SETCC, Ty, Legal); + setCondCodeAction(ISD::SETNE, Ty, Expand); + setCondCodeAction(ISD::SETGE, Ty, Expand); + setCondCodeAction(ISD::SETGT, Ty, Expand); + setCondCodeAction(ISD::SETUGE, Ty, Expand); + setCondCodeAction(ISD::SETUGT, Ty, Expand); +} + +// Enable MSA support for the given floating-point type and Register class. +void MaxisSETargetLowering:: +addMSAFloatType(MVT::SimpleValueType Ty, const TargetRegisterClass *RC) { + addRegisterClass(Ty, RC); + + // Expand all builtin opcodes. + for (unsigned Opc = 0; Opc < ISD::BUILTIN_OP_END; ++Opc) + setOperationAction(Opc, Ty, Expand); + + setOperationAction(ISD::LOAD, Ty, Legal); + setOperationAction(ISD::STORE, Ty, Legal); + setOperationAction(ISD::BITCAST, Ty, Legal); + setOperationAction(ISD::EXTRACT_VECTOR_ELT, Ty, Legal); + setOperationAction(ISD::INSERT_VECTOR_ELT, Ty, Legal); + setOperationAction(ISD::BUILD_VECTOR, Ty, Custom); + + if (Ty != MVT::v8f16) { + setOperationAction(ISD::FABS, Ty, Legal); + setOperationAction(ISD::FADD, Ty, Legal); + setOperationAction(ISD::FDIV, Ty, Legal); + setOperationAction(ISD::FEXP2, Ty, Legal); + setOperationAction(ISD::FLOG2, Ty, Legal); + setOperationAction(ISD::FMA, Ty, Legal); + setOperationAction(ISD::FMUL, Ty, Legal); + setOperationAction(ISD::FRINT, Ty, Legal); + setOperationAction(ISD::FSQRT, Ty, Legal); + setOperationAction(ISD::FSUB, Ty, Legal); + setOperationAction(ISD::VSELECT, Ty, Legal); + + setOperationAction(ISD::SETCC, Ty, Legal); + setCondCodeAction(ISD::SETOGE, Ty, Expand); + setCondCodeAction(ISD::SETOGT, Ty, Expand); + setCondCodeAction(ISD::SETUGE, Ty, Expand); + setCondCodeAction(ISD::SETUGT, Ty, Expand); + setCondCodeAction(ISD::SETGE, Ty, Expand); + setCondCodeAction(ISD::SETGT, Ty, Expand); + } +} + +SDValue MaxisSETargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const { + if(!Subtarget.hasMaxis32r6()) + return MaxisTargetLowering::LowerOperation(Op, DAG); + + EVT ResTy = Op->getValueType(0); + SDLoc DL(Op); + + // Although MTC1_D64 takes an i32 and writes an f64, the upper 32 bits of the + // floating point register are undefined. Not really an issue as sel.d, which + // is produced from an FSELECT node, only looks at bit 0. + SDValue Tmp = DAG.getNode(MaxisISD::MTC1_D64, DL, MVT::f64, Op->getOperand(0)); + return DAG.getNode(MaxisISD::FSELECT, DL, ResTy, Tmp, Op->getOperand(1), + Op->getOperand(2)); +} + +bool +MaxisSETargetLowering::allowsMisalignedMemoryAccesses(EVT VT, + unsigned, + unsigned, + bool *Fast) const { + MVT::SimpleValueType SVT = VT.getSimpleVT().SimpleTy; + + if (Subtarget.systemSupportsUnalignedAccess()) { + // MAXIS32r6/MAXIS64r6 is required to support unaligned access. It's + // implementation defined whether this is handled by hardware, software, or + // a hybrid of the two but it's expected that most implementations will + // handle the majority of cases in hardware. + if (Fast) + *Fast = true; + return true; + } + + switch (SVT) { + case MVT::i64: + case MVT::i32: + if (Fast) + *Fast = true; + return true; + default: + return false; + } +} + +SDValue MaxisSETargetLowering::LowerOperation(SDValue Op, + SelectionDAG &DAG) const { + switch(Op.getOpcode()) { + case ISD::LOAD: return lowerLOAD(Op, DAG); + case ISD::STORE: return lowerSTORE(Op, DAG); + case ISD::SMUL_LOHI: return lowerMulDiv(Op, MaxisISD::Mult, true, true, DAG); + case ISD::UMUL_LOHI: return lowerMulDiv(Op, MaxisISD::Multu, true, true, DAG); + case ISD::MULHS: return lowerMulDiv(Op, MaxisISD::Mult, false, true, DAG); + case ISD::MULHU: return lowerMulDiv(Op, MaxisISD::Multu, false, true, DAG); + case ISD::MUL: return lowerMulDiv(Op, MaxisISD::Mult, true, false, DAG); + case ISD::SDIVREM: return lowerMulDiv(Op, MaxisISD::DivRem, true, true, DAG); + case ISD::UDIVREM: return lowerMulDiv(Op, MaxisISD::DivRemU, true, true, + DAG); + case ISD::INTRINSIC_WO_CHAIN: return lowerINTRINSIC_WO_CHAIN(Op, DAG); + case ISD::INTRINSIC_W_CHAIN: return lowerINTRINSIC_W_CHAIN(Op, DAG); + case ISD::INTRINSIC_VOID: return lowerINTRINSIC_VOID(Op, DAG); + case ISD::EXTRACT_VECTOR_ELT: return lowerEXTRACT_VECTOR_ELT(Op, DAG); + case ISD::BUILD_VECTOR: return lowerBUILD_VECTOR(Op, DAG); + case ISD::VECTOR_SHUFFLE: return lowerVECTOR_SHUFFLE(Op, DAG); + case ISD::SELECT: return lowerSELECT(Op, DAG); + } + + return MaxisTargetLowering::LowerOperation(Op, DAG); +} + +// Fold zero extensions into MaxisISD::VEXTRACT_[SZ]EXT_ELT +// +// Performs the following transformations: +// - Changes MaxisISD::VEXTRACT_[SZ]EXT_ELT to zero extension if its +// sign/zero-extension is completely overwritten by the new one performed by +// the ISD::AND. +// - Removes redundant zero extensions performed by an ISD::AND. +static SDValue performANDCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + if (!Subtarget.hasMSA()) + return SDValue(); + + SDValue Op0 = N->getOperand(0); + SDValue Op1 = N->getOperand(1); + unsigned Op0Opcode = Op0->getOpcode(); + + // (and (MaxisVExtract[SZ]Ext $a, $b, $c), imm:$d) + // where $d + 1 == 2^n and n == 32 + // or $d + 1 == 2^n and n <= 32 and ZExt + // -> (MaxisVExtractZExt $a, $b, $c) + if (Op0Opcode == MaxisISD::VEXTRACT_SEXT_ELT || + Op0Opcode == MaxisISD::VEXTRACT_ZEXT_ELT) { + ConstantSDNode *Mask = dyn_cast(Op1); + + if (!Mask) + return SDValue(); + + int32_t Log2IfPositive = (Mask->getAPIntValue() + 1).exactLogBase2(); + + if (Log2IfPositive <= 0) + return SDValue(); // Mask+1 is not a power of 2 + + SDValue Op0Op2 = Op0->getOperand(2); + EVT ExtendTy = cast(Op0Op2)->getVT(); + unsigned ExtendTySize = ExtendTy.getSizeInBits(); + unsigned Log2 = Log2IfPositive; + + if ((Op0Opcode == MaxisISD::VEXTRACT_ZEXT_ELT && Log2 >= ExtendTySize) || + Log2 == ExtendTySize) { + SDValue Ops[] = { Op0->getOperand(0), Op0->getOperand(1), Op0Op2 }; + return DAG.getNode(MaxisISD::VEXTRACT_ZEXT_ELT, SDLoc(Op0), + Op0->getVTList(), + makeArrayRef(Ops, Op0->getNumOperands())); + } + } + + return SDValue(); +} + +// Determine if the specified node is a constant vector splat. +// +// Returns true and sets Imm if: +// * N is a ISD::BUILD_VECTOR representing a constant splat +// +// This function is quite similar to MaxisSEDAGToDAGISel::selectVSplat. The +// differences are that it assumes the MSA has already been checked and the +// arbitrary requirement for a maximum of 32-bit integers isn't applied (and +// must not be in order for binsri.d to be selectable). +static bool isVSplat(SDValue N, APInt &Imm, bool IsLittleEndian) { + BuildVectorSDNode *Node = dyn_cast(N.getNode()); + + if (!Node) + return false; + + APInt SplatValue, SplatUndef; + unsigned SplatBitSize; + bool HasAnyUndefs; + + if (!Node->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, HasAnyUndefs, + 8, !IsLittleEndian)) + return false; + + Imm = SplatValue; + + return true; +} + +// Test whether the given node is an all-ones build_vector. +static bool isVectorAllOnes(SDValue N) { + // Look through bitcasts. Endianness doesn't matter because we are looking + // for an all-ones value. + if (N->getOpcode() == ISD::BITCAST) + N = N->getOperand(0); + + BuildVectorSDNode *BVN = dyn_cast(N); + + if (!BVN) + return false; + + APInt SplatValue, SplatUndef; + unsigned SplatBitSize; + bool HasAnyUndefs; + + // Endianness doesn't matter in this context because we are looking for + // an all-ones value. + if (BVN->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, HasAnyUndefs)) + return SplatValue.isAllOnesValue(); + + return false; +} + +// Test whether N is the bitwise inverse of OfNode. +static bool isBitwiseInverse(SDValue N, SDValue OfNode) { + if (N->getOpcode() != ISD::XOR) + return false; + + if (isVectorAllOnes(N->getOperand(0))) + return N->getOperand(1) == OfNode; + + if (isVectorAllOnes(N->getOperand(1))) + return N->getOperand(0) == OfNode; + + return false; +} + +// Perform combines where ISD::OR is the root node. +// +// Performs the following transformations: +// - (or (and $a, $mask), (and $b, $inv_mask)) => (vselect $mask, $a, $b) +// where $inv_mask is the bitwise inverse of $mask and the 'or' has a 128-bit +// vector type. +static SDValue performORCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + if (!Subtarget.hasMSA()) + return SDValue(); + + EVT Ty = N->getValueType(0); + + if (!Ty.is128BitVector()) + return SDValue(); + + SDValue Op0 = N->getOperand(0); + SDValue Op1 = N->getOperand(1); + + if (Op0->getOpcode() == ISD::AND && Op1->getOpcode() == ISD::AND) { + SDValue Op0Op0 = Op0->getOperand(0); + SDValue Op0Op1 = Op0->getOperand(1); + SDValue Op1Op0 = Op1->getOperand(0); + SDValue Op1Op1 = Op1->getOperand(1); + bool IsLittleEndian = !Subtarget.isLittle(); + + SDValue IfSet, IfClr, Cond; + bool IsConstantMask = false; + APInt Mask, InvMask; + + // If Op0Op0 is an appropriate mask, try to find it's inverse in either + // Op1Op0, or Op1Op1. Keep track of the Cond, IfSet, and IfClr nodes, while + // looking. + // IfClr will be set if we find a valid match. + if (isVSplat(Op0Op0, Mask, IsLittleEndian)) { + Cond = Op0Op0; + IfSet = Op0Op1; + + if (isVSplat(Op1Op0, InvMask, IsLittleEndian) && + Mask.getBitWidth() == InvMask.getBitWidth() && Mask == ~InvMask) + IfClr = Op1Op1; + else if (isVSplat(Op1Op1, InvMask, IsLittleEndian) && + Mask.getBitWidth() == InvMask.getBitWidth() && Mask == ~InvMask) + IfClr = Op1Op0; + + IsConstantMask = true; + } + + // If IfClr is not yet set, and Op0Op1 is an appropriate mask, try the same + // thing again using this mask. + // IfClr will be set if we find a valid match. + if (!IfClr.getNode() && isVSplat(Op0Op1, Mask, IsLittleEndian)) { + Cond = Op0Op1; + IfSet = Op0Op0; + + if (isVSplat(Op1Op0, InvMask, IsLittleEndian) && + Mask.getBitWidth() == InvMask.getBitWidth() && Mask == ~InvMask) + IfClr = Op1Op1; + else if (isVSplat(Op1Op1, InvMask, IsLittleEndian) && + Mask.getBitWidth() == InvMask.getBitWidth() && Mask == ~InvMask) + IfClr = Op1Op0; + + IsConstantMask = true; + } + + // If IfClr is not yet set, try looking for a non-constant match. + // IfClr will be set if we find a valid match amongst the eight + // possibilities. + if (!IfClr.getNode()) { + if (isBitwiseInverse(Op0Op0, Op1Op0)) { + Cond = Op1Op0; + IfSet = Op1Op1; + IfClr = Op0Op1; + } else if (isBitwiseInverse(Op0Op1, Op1Op0)) { + Cond = Op1Op0; + IfSet = Op1Op1; + IfClr = Op0Op0; + } else if (isBitwiseInverse(Op0Op0, Op1Op1)) { + Cond = Op1Op1; + IfSet = Op1Op0; + IfClr = Op0Op1; + } else if (isBitwiseInverse(Op0Op1, Op1Op1)) { + Cond = Op1Op1; + IfSet = Op1Op0; + IfClr = Op0Op0; + } else if (isBitwiseInverse(Op1Op0, Op0Op0)) { + Cond = Op0Op0; + IfSet = Op0Op1; + IfClr = Op1Op1; + } else if (isBitwiseInverse(Op1Op1, Op0Op0)) { + Cond = Op0Op0; + IfSet = Op0Op1; + IfClr = Op1Op0; + } else if (isBitwiseInverse(Op1Op0, Op0Op1)) { + Cond = Op0Op1; + IfSet = Op0Op0; + IfClr = Op1Op1; + } else if (isBitwiseInverse(Op1Op1, Op0Op1)) { + Cond = Op0Op1; + IfSet = Op0Op0; + IfClr = Op1Op0; + } + } + + // At this point, IfClr will be set if we have a valid match. + if (!IfClr.getNode()) + return SDValue(); + + assert(Cond.getNode() && IfSet.getNode()); + + // Fold degenerate cases. + if (IsConstantMask) { + if (Mask.isAllOnesValue()) + return IfSet; + else if (Mask == 0) + return IfClr; + } + + // Transform the DAG into an equivalent VSELECT. + return DAG.getNode(ISD::VSELECT, SDLoc(N), Ty, Cond, IfSet, IfClr); + } + + return SDValue(); +} + +static SDValue genConstMult(SDValue X, APInt C, const SDLoc &DL, EVT VT, + EVT ShiftTy, SelectionDAG &DAG) { + // Return 0. + if (C == 0) + return DAG.getConstant(0, DL, VT); + + // Return x. + if (C == 1) + return X; + + // If c is power of 2, return (shl x, log2(c)). + if (C.isPowerOf2()) + return DAG.getNode(ISD::SHL, DL, VT, X, + DAG.getConstant(C.logBase2(), DL, ShiftTy)); + + unsigned BitWidth = C.getBitWidth(); + APInt Floor = APInt(BitWidth, 1) << C.logBase2(); + APInt Ceil = C.isNegative() ? APInt(BitWidth, 0) : + APInt(BitWidth, 1) << C.ceilLogBase2(); + + // If |c - floor_c| <= |c - ceil_c|, + // where floor_c = pow(2, floor(log2(c))) and ceil_c = pow(2, ceil(log2(c))), + // return (add constMult(x, floor_c), constMult(x, c - floor_c)). + if ((C - Floor).ule(Ceil - C)) { + SDValue Op0 = genConstMult(X, Floor, DL, VT, ShiftTy, DAG); + SDValue Op1 = genConstMult(X, C - Floor, DL, VT, ShiftTy, DAG); + return DAG.getNode(ISD::ADD, DL, VT, Op0, Op1); + } + + // If |c - floor_c| > |c - ceil_c|, + // return (sub constMult(x, ceil_c), constMult(x, ceil_c - c)). + SDValue Op0 = genConstMult(X, Ceil, DL, VT, ShiftTy, DAG); + SDValue Op1 = genConstMult(X, Ceil - C, DL, VT, ShiftTy, DAG); + return DAG.getNode(ISD::SUB, DL, VT, Op0, Op1); +} + +static SDValue performMULCombine(SDNode *N, SelectionDAG &DAG, + const TargetLowering::DAGCombinerInfo &DCI, + const MaxisSETargetLowering *TL) { + EVT VT = N->getValueType(0); + + if (ConstantSDNode *C = dyn_cast(N->getOperand(1))) + if (!VT.isVector()) + return genConstMult(N->getOperand(0), C->getAPIntValue(), SDLoc(N), VT, + TL->getScalarShiftAmountTy(DAG.getDataLayout(), VT), + DAG); + + return SDValue(N, 0); +} + +static SDValue performDSPShiftCombine(unsigned Opc, SDNode *N, EVT Ty, + SelectionDAG &DAG, + const MaxisSubtarget &Subtarget) { + // See if this is a vector splat immediate node. + APInt SplatValue, SplatUndef; + unsigned SplatBitSize; + bool HasAnyUndefs; + unsigned EltSize = Ty.getScalarSizeInBits(); + BuildVectorSDNode *BV = dyn_cast(N->getOperand(1)); + + if (!Subtarget.hasDSP()) + return SDValue(); + + if (!BV || + !BV->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, HasAnyUndefs, + EltSize, !Subtarget.isLittle()) || + (SplatBitSize != EltSize) || + (SplatValue.getZExtValue() >= EltSize)) + return SDValue(); + + SDLoc DL(N); + return DAG.getNode(Opc, DL, Ty, N->getOperand(0), + DAG.getConstant(SplatValue.getZExtValue(), DL, MVT::i32)); +} + +static SDValue performSHLCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + EVT Ty = N->getValueType(0); + + if ((Ty != MVT::v2i16) && (Ty != MVT::v4i8)) + return SDValue(); + + return performDSPShiftCombine(MaxisISD::SHLL_DSP, N, Ty, DAG, Subtarget); +} + +// Fold sign-extensions into MaxisISD::VEXTRACT_[SZ]EXT_ELT for MSA and fold +// constant splats into MaxisISD::SHRA_DSP for DSPr2. +// +// Performs the following transformations: +// - Changes MaxisISD::VEXTRACT_[SZ]EXT_ELT to sign extension if its +// sign/zero-extension is completely overwritten by the new one performed by +// the ISD::SRA and ISD::SHL nodes. +// - Removes redundant sign extensions performed by an ISD::SRA and ISD::SHL +// sequence. +// +// See performDSPShiftCombine for more information about the transformation +// used for DSPr2. +static SDValue performSRACombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + EVT Ty = N->getValueType(0); + + if (Subtarget.hasMSA()) { + SDValue Op0 = N->getOperand(0); + SDValue Op1 = N->getOperand(1); + + // (sra (shl (MaxisVExtract[SZ]Ext $a, $b, $c), imm:$d), imm:$d) + // where $d + sizeof($c) == 32 + // or $d + sizeof($c) <= 32 and SExt + // -> (MaxisVExtractSExt $a, $b, $c) + if (Op0->getOpcode() == ISD::SHL && Op1 == Op0->getOperand(1)) { + SDValue Op0Op0 = Op0->getOperand(0); + ConstantSDNode *ShAmount = dyn_cast(Op1); + + if (!ShAmount) + return SDValue(); + + if (Op0Op0->getOpcode() != MaxisISD::VEXTRACT_SEXT_ELT && + Op0Op0->getOpcode() != MaxisISD::VEXTRACT_ZEXT_ELT) + return SDValue(); + + EVT ExtendTy = cast(Op0Op0->getOperand(2))->getVT(); + unsigned TotalBits = ShAmount->getZExtValue() + ExtendTy.getSizeInBits(); + + if (TotalBits == 32 || + (Op0Op0->getOpcode() == MaxisISD::VEXTRACT_SEXT_ELT && + TotalBits <= 32)) { + SDValue Ops[] = { Op0Op0->getOperand(0), Op0Op0->getOperand(1), + Op0Op0->getOperand(2) }; + return DAG.getNode(MaxisISD::VEXTRACT_SEXT_ELT, SDLoc(Op0Op0), + Op0Op0->getVTList(), + makeArrayRef(Ops, Op0Op0->getNumOperands())); + } + } + } + + if ((Ty != MVT::v2i16) && ((Ty != MVT::v4i8) || !Subtarget.hasDSPR2())) + return SDValue(); + + return performDSPShiftCombine(MaxisISD::SHRA_DSP, N, Ty, DAG, Subtarget); +} + + +static SDValue performSRLCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const MaxisSubtarget &Subtarget) { + EVT Ty = N->getValueType(0); + + if (((Ty != MVT::v2i16) || !Subtarget.hasDSPR2()) && (Ty != MVT::v4i8)) + return SDValue(); + + return performDSPShiftCombine(MaxisISD::SHRL_DSP, N, Ty, DAG, Subtarget); +} + +static bool isLegalDSPCondCode(EVT Ty, ISD::CondCode CC) { + bool IsV216 = (Ty == MVT::v2i16); + + switch (CC) { + case ISD::SETEQ: + case ISD::SETNE: return true; + case ISD::SETLT: + case ISD::SETLE: + case ISD::SETGT: + case ISD::SETGE: return IsV216; + case ISD::SETULT: + case ISD::SETULE: + case ISD::SETUGT: + case ISD::SETUGE: return !IsV216; + default: return false; + } +} + +static SDValue performSETCCCombine(SDNode *N, SelectionDAG &DAG) { + EVT Ty = N->getValueType(0); + + if ((Ty != MVT::v2i16) && (Ty != MVT::v4i8)) + return SDValue(); + + if (!isLegalDSPCondCode(Ty, cast(N->getOperand(2))->get())) + return SDValue(); + + return DAG.getNode(MaxisISD::SETCC_DSP, SDLoc(N), Ty, N->getOperand(0), + N->getOperand(1), N->getOperand(2)); +} + +static SDValue performVSELECTCombine(SDNode *N, SelectionDAG &DAG) { + EVT Ty = N->getValueType(0); + + if (Ty.is128BitVector() && Ty.isInteger()) { + // Try the following combines: + // (vselect (setcc $a, $b, SETLT), $b, $a)) -> (vsmax $a, $b) + // (vselect (setcc $a, $b, SETLE), $b, $a)) -> (vsmax $a, $b) + // (vselect (setcc $a, $b, SETLT), $a, $b)) -> (vsmin $a, $b) + // (vselect (setcc $a, $b, SETLE), $a, $b)) -> (vsmin $a, $b) + // (vselect (setcc $a, $b, SETULT), $b, $a)) -> (vumax $a, $b) + // (vselect (setcc $a, $b, SETULE), $b, $a)) -> (vumax $a, $b) + // (vselect (setcc $a, $b, SETULT), $a, $b)) -> (vumin $a, $b) + // (vselect (setcc $a, $b, SETULE), $a, $b)) -> (vumin $a, $b) + // SETGT/SETGE/SETUGT/SETUGE variants of these will show up initially but + // will be expanded to equivalent SETLT/SETLE/SETULT/SETULE versions by the + // legalizer. + SDValue Op0 = N->getOperand(0); + + if (Op0->getOpcode() != ISD::SETCC) + return SDValue(); + + ISD::CondCode CondCode = cast(Op0->getOperand(2))->get(); + bool Signed; + + if (CondCode == ISD::SETLT || CondCode == ISD::SETLE) + Signed = true; + else if (CondCode == ISD::SETULT || CondCode == ISD::SETULE) + Signed = false; + else + return SDValue(); + + SDValue Op1 = N->getOperand(1); + SDValue Op2 = N->getOperand(2); + SDValue Op0Op0 = Op0->getOperand(0); + SDValue Op0Op1 = Op0->getOperand(1); + + if (Op1 == Op0Op0 && Op2 == Op0Op1) + return DAG.getNode(Signed ? MaxisISD::VSMIN : MaxisISD::VUMIN, SDLoc(N), + Ty, Op1, Op2); + else if (Op1 == Op0Op1 && Op2 == Op0Op0) + return DAG.getNode(Signed ? MaxisISD::VSMAX : MaxisISD::VUMAX, SDLoc(N), + Ty, Op1, Op2); + } else if ((Ty == MVT::v2i16) || (Ty == MVT::v4i8)) { + SDValue SetCC = N->getOperand(0); + + if (SetCC.getOpcode() != MaxisISD::SETCC_DSP) + return SDValue(); + + return DAG.getNode(MaxisISD::SELECT_CC_DSP, SDLoc(N), Ty, + SetCC.getOperand(0), SetCC.getOperand(1), + N->getOperand(1), N->getOperand(2), SetCC.getOperand(2)); + } + + return SDValue(); +} + +static SDValue performXORCombine(SDNode *N, SelectionDAG &DAG, + const MaxisSubtarget &Subtarget) { + EVT Ty = N->getValueType(0); + + if (Subtarget.hasMSA() && Ty.is128BitVector() && Ty.isInteger()) { + // Try the following combines: + // (xor (or $a, $b), (build_vector allones)) + // (xor (or $a, $b), (bitcast (build_vector allones))) + SDValue Op0 = N->getOperand(0); + SDValue Op1 = N->getOperand(1); + SDValue NotOp; + + if (ISD::isBuildVectorAllOnes(Op0.getNode())) + NotOp = Op1; + else if (ISD::isBuildVectorAllOnes(Op1.getNode())) + NotOp = Op0; + else + return SDValue(); + + if (NotOp->getOpcode() == ISD::OR) + return DAG.getNode(MaxisISD::VNOR, SDLoc(N), Ty, NotOp->getOperand(0), + NotOp->getOperand(1)); + } + + return SDValue(); +} + +SDValue +MaxisSETargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const { + SelectionDAG &DAG = DCI.DAG; + SDValue Val; + + switch (N->getOpcode()) { + case ISD::AND: + Val = performANDCombine(N, DAG, DCI, Subtarget); + break; + case ISD::OR: + Val = performORCombine(N, DAG, DCI, Subtarget); + break; + case ISD::MUL: + return performMULCombine(N, DAG, DCI, this); + case ISD::SHL: + Val = performSHLCombine(N, DAG, DCI, Subtarget); + break; + case ISD::SRA: + return performSRACombine(N, DAG, DCI, Subtarget); + case ISD::SRL: + return performSRLCombine(N, DAG, DCI, Subtarget); + case ISD::VSELECT: + return performVSELECTCombine(N, DAG); + case ISD::XOR: + Val = performXORCombine(N, DAG, Subtarget); + break; + case ISD::SETCC: + Val = performSETCCCombine(N, DAG); + break; + } + + if (Val.getNode()) { + DEBUG(dbgs() << "\nMaxisSE DAG Combine:\n"; + N->printrWithDepth(dbgs(), &DAG); + dbgs() << "\n=> \n"; + Val.getNode()->printrWithDepth(dbgs(), &DAG); + dbgs() << "\n"); + return Val; + } + + return MaxisTargetLowering::PerformDAGCombine(N, DCI); +} + +MachineBasicBlock * +MaxisSETargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + switch (MI.getOpcode()) { + default: + return MaxisTargetLowering::EmitInstrWithCustomInserter(MI, BB); + case Maxis::BPOSGE32_PSEUDO: + return emitBPOSGE32(MI, BB); + case Maxis::SNZ_B_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BNZ_B); + case Maxis::SNZ_H_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BNZ_H); + case Maxis::SNZ_W_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BNZ_W); + case Maxis::SNZ_D_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BNZ_D); + case Maxis::SNZ_V_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BNZ_V); + case Maxis::SZ_B_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BZ_B); + case Maxis::SZ_H_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BZ_H); + case Maxis::SZ_W_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BZ_W); + case Maxis::SZ_D_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BZ_D); + case Maxis::SZ_V_PSEUDO: + return emitMSACBranchPseudo(MI, BB, Maxis::BZ_V); + case Maxis::COPY_FW_PSEUDO: + return emitCOPY_FW(MI, BB); + case Maxis::COPY_FD_PSEUDO: + return emitCOPY_FD(MI, BB); + case Maxis::INSERT_FW_PSEUDO: + return emitINSERT_FW(MI, BB); + case Maxis::INSERT_FD_PSEUDO: + return emitINSERT_FD(MI, BB); + case Maxis::INSERT_B_VIDX_PSEUDO: + case Maxis::INSERT_B_VIDX64_PSEUDO: + return emitINSERT_DF_VIDX(MI, BB, 1, false); + case Maxis::INSERT_H_VIDX_PSEUDO: + case Maxis::INSERT_H_VIDX64_PSEUDO: + return emitINSERT_DF_VIDX(MI, BB, 2, false); + case Maxis::INSERT_W_VIDX_PSEUDO: + case Maxis::INSERT_W_VIDX64_PSEUDO: + return emitINSERT_DF_VIDX(MI, BB, 4, false); + case Maxis::INSERT_D_VIDX_PSEUDO: + case Maxis::INSERT_D_VIDX64_PSEUDO: + return emitINSERT_DF_VIDX(MI, BB, 8, false); + case Maxis::INSERT_FW_VIDX_PSEUDO: + case Maxis::INSERT_FW_VIDX64_PSEUDO: + return emitINSERT_DF_VIDX(MI, BB, 4, true); + case Maxis::INSERT_FD_VIDX_PSEUDO: + case Maxis::INSERT_FD_VIDX64_PSEUDO: + return emitINSERT_DF_VIDX(MI, BB, 8, true); + case Maxis::FILL_FW_PSEUDO: + return emitFILL_FW(MI, BB); + case Maxis::FILL_FD_PSEUDO: + return emitFILL_FD(MI, BB); + case Maxis::FEXP2_W_1_PSEUDO: + return emitFEXP2_W_1(MI, BB); + case Maxis::FEXP2_D_1_PSEUDO: + return emitFEXP2_D_1(MI, BB); + case Maxis::ST_F16: + return emitST_F16_PSEUDO(MI, BB); + case Maxis::LD_F16: + return emitLD_F16_PSEUDO(MI, BB); + case Maxis::MSA_FP_EXTEND_W_PSEUDO: + return emitFPEXTEND_PSEUDO(MI, BB, false); + case Maxis::MSA_FP_ROUND_W_PSEUDO: + return emitFPROUND_PSEUDO(MI, BB, false); + case Maxis::MSA_FP_EXTEND_D_PSEUDO: + return emitFPEXTEND_PSEUDO(MI, BB, true); + case Maxis::MSA_FP_ROUND_D_PSEUDO: + return emitFPROUND_PSEUDO(MI, BB, true); + } +} + +bool MaxisSETargetLowering::isEligibleForTailCallOptimization( + const CCState &CCInfo, unsigned NextStackOffset, + const MaxisFunctionInfo &FI) const { + if (!UseMaxisTailCalls) + return false; + + // Exception has to be cleared with eret. + if (FI.isISR()) + return false; + + // Return false if either the callee or caller has a byval argument. + if (CCInfo.getInRegsParamsCount() > 0 || FI.hasByvalArg()) + return false; + + // Return true if the callee's argument area is no larger than the + // caller's. + return NextStackOffset <= FI.getIncomingArgSize(); +} + +void MaxisSETargetLowering:: +getOpndList(SmallVectorImpl &Ops, + std::deque> &RegsToPass, + bool IsPICCall, bool GlobalOrExternal, bool InternalLinkage, + bool IsCallReloc, CallLoweringInfo &CLI, SDValue Callee, + SDValue Chain) const { + Ops.push_back(Callee); + MaxisTargetLowering::getOpndList(Ops, RegsToPass, IsPICCall, GlobalOrExternal, + InternalLinkage, IsCallReloc, CLI, Callee, + Chain); +} + +SDValue MaxisSETargetLowering::lowerLOAD(SDValue Op, SelectionDAG &DAG) const { + LoadSDNode &Nd = *cast(Op); + + if (Nd.getMemoryVT() != MVT::f64 || !NoDPLoadStore) + return MaxisTargetLowering::lowerLOAD(Op, DAG); + + // Replace a double precision load with two i32 loads and a buildpair64. + SDLoc DL(Op); + SDValue Ptr = Nd.getBasePtr(), Chain = Nd.getChain(); + EVT PtrVT = Ptr.getValueType(); + + // i32 load from lower address. + SDValue Lo = DAG.getLoad(MVT::i32, DL, Chain, Ptr, MachinePointerInfo(), + Nd.getAlignment(), Nd.getMemOperand()->getFlags()); + + // i32 load from higher address. + Ptr = DAG.getNode(ISD::ADD, DL, PtrVT, Ptr, DAG.getConstant(4, DL, PtrVT)); + SDValue Hi = DAG.getLoad( + MVT::i32, DL, Lo.getValue(1), Ptr, MachinePointerInfo(), + std::min(Nd.getAlignment(), 4U), Nd.getMemOperand()->getFlags()); + + if (!Subtarget.isLittle()) + std::swap(Lo, Hi); + + SDValue BP = DAG.getNode(MaxisISD::BuildPairF64, DL, MVT::f64, Lo, Hi); + SDValue Ops[2] = {BP, Hi.getValue(1)}; + return DAG.getMergeValues(Ops, DL); +} + +SDValue MaxisSETargetLowering::lowerSTORE(SDValue Op, SelectionDAG &DAG) const { + StoreSDNode &Nd = *cast(Op); + + if (Nd.getMemoryVT() != MVT::f64 || !NoDPLoadStore) + return MaxisTargetLowering::lowerSTORE(Op, DAG); + + // Replace a double precision store with two extractelement64s and i32 stores. + SDLoc DL(Op); + SDValue Val = Nd.getValue(), Ptr = Nd.getBasePtr(), Chain = Nd.getChain(); + EVT PtrVT = Ptr.getValueType(); + SDValue Lo = DAG.getNode(MaxisISD::ExtractElementF64, DL, MVT::i32, + Val, DAG.getConstant(0, DL, MVT::i32)); + SDValue Hi = DAG.getNode(MaxisISD::ExtractElementF64, DL, MVT::i32, + Val, DAG.getConstant(1, DL, MVT::i32)); + + if (!Subtarget.isLittle()) + std::swap(Lo, Hi); + + // i32 store to lower address. + Chain = + DAG.getStore(Chain, DL, Lo, Ptr, MachinePointerInfo(), Nd.getAlignment(), + Nd.getMemOperand()->getFlags(), Nd.getAAInfo()); + + // i32 store to higher address. + Ptr = DAG.getNode(ISD::ADD, DL, PtrVT, Ptr, DAG.getConstant(4, DL, PtrVT)); + return DAG.getStore(Chain, DL, Hi, Ptr, MachinePointerInfo(), + std::min(Nd.getAlignment(), 4U), + Nd.getMemOperand()->getFlags(), Nd.getAAInfo()); +} + +SDValue MaxisSETargetLowering::lowerMulDiv(SDValue Op, unsigned NewOpc, + bool HasLo, bool HasHi, + SelectionDAG &DAG) const { + // MAXIS32r6/MAXIS64r6 removed accumulator based multiplies. + assert(!Subtarget.hasMaxis32r6()); + + EVT Ty = Op.getOperand(0).getValueType(); + SDLoc DL(Op); + SDValue Mult = DAG.getNode(NewOpc, DL, MVT::Untyped, + Op.getOperand(0), Op.getOperand(1)); + SDValue Lo, Hi; + + if (HasLo) + Lo = DAG.getNode(MaxisISD::MFLO, DL, Ty, Mult); + if (HasHi) + Hi = DAG.getNode(MaxisISD::MFHI, DL, Ty, Mult); + + if (!HasLo || !HasHi) + return HasLo ? Lo : Hi; + + SDValue Vals[] = { Lo, Hi }; + return DAG.getMergeValues(Vals, DL); +} + +static SDValue initAccumulator(SDValue In, const SDLoc &DL, SelectionDAG &DAG) { + SDValue InLo = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, In, + DAG.getConstant(0, DL, MVT::i32)); + SDValue InHi = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, In, + DAG.getConstant(1, DL, MVT::i32)); + return DAG.getNode(MaxisISD::MTLOHI, DL, MVT::Untyped, InLo, InHi); +} + +static SDValue extractLOHI(SDValue Op, const SDLoc &DL, SelectionDAG &DAG) { + SDValue Lo = DAG.getNode(MaxisISD::MFLO, DL, MVT::i32, Op); + SDValue Hi = DAG.getNode(MaxisISD::MFHI, DL, MVT::i32, Op); + return DAG.getNode(ISD::BUILD_PAIR, DL, MVT::i64, Lo, Hi); +} + +// This function expands maxis intrinsic nodes which have 64-bit input operands +// or output values. +// +// out64 = intrinsic-node in64 +// => +// lo = copy (extract-element (in64, 0)) +// hi = copy (extract-element (in64, 1)) +// maxis-specific-node +// v0 = copy lo +// v1 = copy hi +// out64 = merge-values (v0, v1) +// +static SDValue lowerDSPIntr(SDValue Op, SelectionDAG &DAG, unsigned Opc) { + SDLoc DL(Op); + bool HasChainIn = Op->getOperand(0).getValueType() == MVT::Other; + SmallVector Ops; + unsigned OpNo = 0; + + // See if Op has a chain input. + if (HasChainIn) + Ops.push_back(Op->getOperand(OpNo++)); + + // The next operand is the intrinsic opcode. + assert(Op->getOperand(OpNo).getOpcode() == ISD::TargetConstant); + + // See if the next operand has type i64. + SDValue Opnd = Op->getOperand(++OpNo), In64; + + if (Opnd.getValueType() == MVT::i64) + In64 = initAccumulator(Opnd, DL, DAG); + else + Ops.push_back(Opnd); + + // Push the remaining operands. + for (++OpNo ; OpNo < Op->getNumOperands(); ++OpNo) + Ops.push_back(Op->getOperand(OpNo)); + + // Add In64 to the end of the list. + if (In64.getNode()) + Ops.push_back(In64); + + // Scan output. + SmallVector ResTys; + + for (SDNode::value_iterator I = Op->value_begin(), E = Op->value_end(); + I != E; ++I) + ResTys.push_back((*I == MVT::i64) ? MVT::Untyped : *I); + + // Create node. + SDValue Val = DAG.getNode(Opc, DL, ResTys, Ops); + SDValue Out = (ResTys[0] == MVT::Untyped) ? extractLOHI(Val, DL, DAG) : Val; + + if (!HasChainIn) + return Out; + + assert(Val->getValueType(1) == MVT::Other); + SDValue Vals[] = { Out, SDValue(Val.getNode(), 1) }; + return DAG.getMergeValues(Vals, DL); +} + +// Lower an MSA copy intrinsic into the specified SelectionDAG node +static SDValue lowerMSACopyIntr(SDValue Op, SelectionDAG &DAG, unsigned Opc) { + SDLoc DL(Op); + SDValue Vec = Op->getOperand(1); + SDValue Idx = Op->getOperand(2); + EVT ResTy = Op->getValueType(0); + EVT EltTy = Vec->getValueType(0).getVectorElementType(); + + SDValue Result = DAG.getNode(Opc, DL, ResTy, Vec, Idx, + DAG.getValueType(EltTy)); + + return Result; +} + +static SDValue lowerMSASplatZExt(SDValue Op, unsigned OpNr, SelectionDAG &DAG) { + EVT ResVecTy = Op->getValueType(0); + EVT ViaVecTy = ResVecTy; + bool BigEndian = !DAG.getSubtarget().getTargetTriple().isLittleEndian(); + SDLoc DL(Op); + + // When ResVecTy == MVT::v2i64, LaneA is the upper 32 bits of the lane and + // LaneB is the lower 32-bits. Otherwise LaneA and LaneB are alternating + // lanes. + SDValue LaneA = Op->getOperand(OpNr); + SDValue LaneB; + + if (ResVecTy == MVT::v2i64) { + LaneB = DAG.getConstant(0, DL, MVT::i32); + ViaVecTy = MVT::v4i32; + if(BigEndian) + std::swap(LaneA, LaneB); + } else + LaneB = LaneA; + + SDValue Ops[16] = { LaneA, LaneB, LaneA, LaneB, LaneA, LaneB, LaneA, LaneB, + LaneA, LaneB, LaneA, LaneB, LaneA, LaneB, LaneA, LaneB }; + + SDValue Result = DAG.getBuildVector( + ViaVecTy, DL, makeArrayRef(Ops, ViaVecTy.getVectorNumElements())); + + if (ViaVecTy != ResVecTy) { + SDValue One = DAG.getConstant(1, DL, ViaVecTy); + Result = DAG.getNode(ISD::BITCAST, DL, ResVecTy, + DAG.getNode(ISD::AND, DL, ViaVecTy, Result, One)); + } + + return Result; +} + +static SDValue lowerMSASplatImm(SDValue Op, unsigned ImmOp, SelectionDAG &DAG, + bool IsSigned = false) { + return DAG.getConstant( + APInt(Op->getValueType(0).getScalarType().getSizeInBits(), + Op->getConstantOperandVal(ImmOp), IsSigned), + SDLoc(Op), Op->getValueType(0)); +} + +static SDValue getBuildVectorSplat(EVT VecTy, SDValue SplatValue, + bool BigEndian, SelectionDAG &DAG) { + EVT ViaVecTy = VecTy; + SDValue SplatValueA = SplatValue; + SDValue SplatValueB = SplatValue; + SDLoc DL(SplatValue); + + if (VecTy == MVT::v2i64) { + // v2i64 BUILD_VECTOR must be performed via v4i32 so split into i32's. + ViaVecTy = MVT::v4i32; + + SplatValueA = DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, SplatValue); + SplatValueB = DAG.getNode(ISD::SRL, DL, MVT::i64, SplatValue, + DAG.getConstant(32, DL, MVT::i32)); + SplatValueB = DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, SplatValueB); + } + + // We currently hold the parts in little endian order. Swap them if + // necessary. + if (BigEndian) + std::swap(SplatValueA, SplatValueB); + + SDValue Ops[16] = { SplatValueA, SplatValueB, SplatValueA, SplatValueB, + SplatValueA, SplatValueB, SplatValueA, SplatValueB, + SplatValueA, SplatValueB, SplatValueA, SplatValueB, + SplatValueA, SplatValueB, SplatValueA, SplatValueB }; + + SDValue Result = DAG.getBuildVector( + ViaVecTy, DL, makeArrayRef(Ops, ViaVecTy.getVectorNumElements())); + + if (VecTy != ViaVecTy) + Result = DAG.getNode(ISD::BITCAST, DL, VecTy, Result); + + return Result; +} + +static SDValue lowerMSABinaryBitImmIntr(SDValue Op, SelectionDAG &DAG, + unsigned Opc, SDValue Imm, + bool BigEndian) { + EVT VecTy = Op->getValueType(0); + SDValue Exp2Imm; + SDLoc DL(Op); + + // The DAG Combiner can't constant fold bitcasted vectors yet so we must do it + // here for now. + if (VecTy == MVT::v2i64) { + if (ConstantSDNode *CImm = dyn_cast(Imm)) { + APInt BitImm = APInt(64, 1) << CImm->getAPIntValue(); + + SDValue BitImmHiOp = DAG.getConstant(BitImm.lshr(32).trunc(32), DL, + MVT::i32); + SDValue BitImmLoOp = DAG.getConstant(BitImm.trunc(32), DL, MVT::i32); + + if (BigEndian) + std::swap(BitImmLoOp, BitImmHiOp); + + Exp2Imm = DAG.getNode( + ISD::BITCAST, DL, MVT::v2i64, + DAG.getBuildVector(MVT::v4i32, DL, + {BitImmLoOp, BitImmHiOp, BitImmLoOp, BitImmHiOp})); + } + } + + if (!Exp2Imm.getNode()) { + // We couldnt constant fold, do a vector shift instead + + // Extend i32 to i64 if necessary. Sign or zero extend doesn't matter since + // only values 0-63 are valid. + if (VecTy == MVT::v2i64) + Imm = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i64, Imm); + + Exp2Imm = getBuildVectorSplat(VecTy, Imm, BigEndian, DAG); + + Exp2Imm = DAG.getNode(ISD::SHL, DL, VecTy, DAG.getConstant(1, DL, VecTy), + Exp2Imm); + } + + return DAG.getNode(Opc, DL, VecTy, Op->getOperand(1), Exp2Imm); +} + +static SDValue truncateVecElts(SDValue Op, SelectionDAG &DAG) { + SDLoc DL(Op); + EVT ResTy = Op->getValueType(0); + SDValue Vec = Op->getOperand(2); + bool BigEndian = !DAG.getSubtarget().getTargetTriple().isLittleEndian(); + MVT ResEltTy = ResTy == MVT::v2i64 ? MVT::i64 : MVT::i32; + SDValue ConstValue = DAG.getConstant(Vec.getScalarValueSizeInBits() - 1, + DL, ResEltTy); + SDValue SplatVec = getBuildVectorSplat(ResTy, ConstValue, BigEndian, DAG); + + return DAG.getNode(ISD::AND, DL, ResTy, Vec, SplatVec); +} + +static SDValue lowerMSABitClear(SDValue Op, SelectionDAG &DAG) { + EVT ResTy = Op->getValueType(0); + SDLoc DL(Op); + SDValue One = DAG.getConstant(1, DL, ResTy); + SDValue Bit = DAG.getNode(ISD::SHL, DL, ResTy, One, truncateVecElts(Op, DAG)); + + return DAG.getNode(ISD::AND, DL, ResTy, Op->getOperand(1), + DAG.getNOT(DL, Bit, ResTy)); +} + +static SDValue lowerMSABitClearImm(SDValue Op, SelectionDAG &DAG) { + SDLoc DL(Op); + EVT ResTy = Op->getValueType(0); + APInt BitImm = APInt(ResTy.getScalarSizeInBits(), 1) + << cast(Op->getOperand(2))->getAPIntValue(); + SDValue BitMask = DAG.getConstant(~BitImm, DL, ResTy); + + return DAG.getNode(ISD::AND, DL, ResTy, Op->getOperand(1), BitMask); +} + +SDValue MaxisSETargetLowering::lowerINTRINSIC_WO_CHAIN(SDValue Op, + SelectionDAG &DAG) const { + SDLoc DL(Op); + unsigned Intrinsic = cast(Op->getOperand(0))->getZExtValue(); + switch (Intrinsic) { + default: + return SDValue(); + case Intrinsic::maxis_shilo: + return lowerDSPIntr(Op, DAG, MaxisISD::SHILO); + case Intrinsic::maxis_dpau_h_qbl: + return lowerDSPIntr(Op, DAG, MaxisISD::DPAU_H_QBL); + case Intrinsic::maxis_dpau_h_qbr: + return lowerDSPIntr(Op, DAG, MaxisISD::DPAU_H_QBR); + case Intrinsic::maxis_dpsu_h_qbl: + return lowerDSPIntr(Op, DAG, MaxisISD::DPSU_H_QBL); + case Intrinsic::maxis_dpsu_h_qbr: + return lowerDSPIntr(Op, DAG, MaxisISD::DPSU_H_QBR); + case Intrinsic::maxis_dpa_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPA_W_PH); + case Intrinsic::maxis_dps_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPS_W_PH); + case Intrinsic::maxis_dpax_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPAX_W_PH); + case Intrinsic::maxis_dpsx_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPSX_W_PH); + case Intrinsic::maxis_mulsa_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::MULSA_W_PH); + case Intrinsic::maxis_mult: + return lowerDSPIntr(Op, DAG, MaxisISD::Mult); + case Intrinsic::maxis_multu: + return lowerDSPIntr(Op, DAG, MaxisISD::Multu); + case Intrinsic::maxis_madd: + return lowerDSPIntr(Op, DAG, MaxisISD::MAdd); + case Intrinsic::maxis_maddu: + return lowerDSPIntr(Op, DAG, MaxisISD::MAddu); + case Intrinsic::maxis_msub: + return lowerDSPIntr(Op, DAG, MaxisISD::MSub); + case Intrinsic::maxis_msubu: + return lowerDSPIntr(Op, DAG, MaxisISD::MSubu); + case Intrinsic::maxis_addv_b: + case Intrinsic::maxis_addv_h: + case Intrinsic::maxis_addv_w: + case Intrinsic::maxis_addv_d: + return DAG.getNode(ISD::ADD, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_addvi_b: + case Intrinsic::maxis_addvi_h: + case Intrinsic::maxis_addvi_w: + case Intrinsic::maxis_addvi_d: + return DAG.getNode(ISD::ADD, DL, Op->getValueType(0), Op->getOperand(1), + lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_and_v: + return DAG.getNode(ISD::AND, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_andi_b: + return DAG.getNode(ISD::AND, DL, Op->getValueType(0), Op->getOperand(1), + lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_bclr_b: + case Intrinsic::maxis_bclr_h: + case Intrinsic::maxis_bclr_w: + case Intrinsic::maxis_bclr_d: + return lowerMSABitClear(Op, DAG); + case Intrinsic::maxis_bclri_b: + case Intrinsic::maxis_bclri_h: + case Intrinsic::maxis_bclri_w: + case Intrinsic::maxis_bclri_d: + return lowerMSABitClearImm(Op, DAG); + case Intrinsic::maxis_binsli_b: + case Intrinsic::maxis_binsli_h: + case Intrinsic::maxis_binsli_w: + case Intrinsic::maxis_binsli_d: { + // binsli_x(IfClear, IfSet, nbits) -> (vselect LBitsMask, IfSet, IfClear) + EVT VecTy = Op->getValueType(0); + EVT EltTy = VecTy.getVectorElementType(); + if (Op->getConstantOperandVal(3) >= EltTy.getSizeInBits()) + report_fatal_error("Immediate out of range"); + APInt Mask = APInt::getHighBitsSet(EltTy.getSizeInBits(), + Op->getConstantOperandVal(3) + 1); + return DAG.getNode(ISD::VSELECT, DL, VecTy, + DAG.getConstant(Mask, DL, VecTy, true), + Op->getOperand(2), Op->getOperand(1)); + } + case Intrinsic::maxis_binsri_b: + case Intrinsic::maxis_binsri_h: + case Intrinsic::maxis_binsri_w: + case Intrinsic::maxis_binsri_d: { + // binsri_x(IfClear, IfSet, nbits) -> (vselect RBitsMask, IfSet, IfClear) + EVT VecTy = Op->getValueType(0); + EVT EltTy = VecTy.getVectorElementType(); + if (Op->getConstantOperandVal(3) >= EltTy.getSizeInBits()) + report_fatal_error("Immediate out of range"); + APInt Mask = APInt::getLowBitsSet(EltTy.getSizeInBits(), + Op->getConstantOperandVal(3) + 1); + return DAG.getNode(ISD::VSELECT, DL, VecTy, + DAG.getConstant(Mask, DL, VecTy, true), + Op->getOperand(2), Op->getOperand(1)); + } + case Intrinsic::maxis_bmnz_v: + return DAG.getNode(ISD::VSELECT, DL, Op->getValueType(0), Op->getOperand(3), + Op->getOperand(2), Op->getOperand(1)); + case Intrinsic::maxis_bmnzi_b: + return DAG.getNode(ISD::VSELECT, DL, Op->getValueType(0), + lowerMSASplatImm(Op, 3, DAG), Op->getOperand(2), + Op->getOperand(1)); + case Intrinsic::maxis_bmz_v: + return DAG.getNode(ISD::VSELECT, DL, Op->getValueType(0), Op->getOperand(3), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_bmzi_b: + return DAG.getNode(ISD::VSELECT, DL, Op->getValueType(0), + lowerMSASplatImm(Op, 3, DAG), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_bneg_b: + case Intrinsic::maxis_bneg_h: + case Intrinsic::maxis_bneg_w: + case Intrinsic::maxis_bneg_d: { + EVT VecTy = Op->getValueType(0); + SDValue One = DAG.getConstant(1, DL, VecTy); + + return DAG.getNode(ISD::XOR, DL, VecTy, Op->getOperand(1), + DAG.getNode(ISD::SHL, DL, VecTy, One, + truncateVecElts(Op, DAG))); + } + case Intrinsic::maxis_bnegi_b: + case Intrinsic::maxis_bnegi_h: + case Intrinsic::maxis_bnegi_w: + case Intrinsic::maxis_bnegi_d: + return lowerMSABinaryBitImmIntr(Op, DAG, ISD::XOR, Op->getOperand(2), + !Subtarget.isLittle()); + case Intrinsic::maxis_bnz_b: + case Intrinsic::maxis_bnz_h: + case Intrinsic::maxis_bnz_w: + case Intrinsic::maxis_bnz_d: + return DAG.getNode(MaxisISD::VALL_NONZERO, DL, Op->getValueType(0), + Op->getOperand(1)); + case Intrinsic::maxis_bnz_v: + return DAG.getNode(MaxisISD::VANY_NONZERO, DL, Op->getValueType(0), + Op->getOperand(1)); + case Intrinsic::maxis_bsel_v: + // bsel_v(Mask, IfClear, IfSet) -> (vselect Mask, IfSet, IfClear) + return DAG.getNode(ISD::VSELECT, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(3), + Op->getOperand(2)); + case Intrinsic::maxis_bseli_b: + // bseli_v(Mask, IfClear, IfSet) -> (vselect Mask, IfSet, IfClear) + return DAG.getNode(ISD::VSELECT, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 3, DAG), + Op->getOperand(2)); + case Intrinsic::maxis_bset_b: + case Intrinsic::maxis_bset_h: + case Intrinsic::maxis_bset_w: + case Intrinsic::maxis_bset_d: { + EVT VecTy = Op->getValueType(0); + SDValue One = DAG.getConstant(1, DL, VecTy); + + return DAG.getNode(ISD::OR, DL, VecTy, Op->getOperand(1), + DAG.getNode(ISD::SHL, DL, VecTy, One, + truncateVecElts(Op, DAG))); + } + case Intrinsic::maxis_bseti_b: + case Intrinsic::maxis_bseti_h: + case Intrinsic::maxis_bseti_w: + case Intrinsic::maxis_bseti_d: + return lowerMSABinaryBitImmIntr(Op, DAG, ISD::OR, Op->getOperand(2), + !Subtarget.isLittle()); + case Intrinsic::maxis_bz_b: + case Intrinsic::maxis_bz_h: + case Intrinsic::maxis_bz_w: + case Intrinsic::maxis_bz_d: + return DAG.getNode(MaxisISD::VALL_ZERO, DL, Op->getValueType(0), + Op->getOperand(1)); + case Intrinsic::maxis_bz_v: + return DAG.getNode(MaxisISD::VANY_ZERO, DL, Op->getValueType(0), + Op->getOperand(1)); + case Intrinsic::maxis_ceq_b: + case Intrinsic::maxis_ceq_h: + case Intrinsic::maxis_ceq_w: + case Intrinsic::maxis_ceq_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETEQ); + case Intrinsic::maxis_ceqi_b: + case Intrinsic::maxis_ceqi_h: + case Intrinsic::maxis_ceqi_w: + case Intrinsic::maxis_ceqi_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + lowerMSASplatImm(Op, 2, DAG, true), ISD::SETEQ); + case Intrinsic::maxis_cle_s_b: + case Intrinsic::maxis_cle_s_h: + case Intrinsic::maxis_cle_s_w: + case Intrinsic::maxis_cle_s_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETLE); + case Intrinsic::maxis_clei_s_b: + case Intrinsic::maxis_clei_s_h: + case Intrinsic::maxis_clei_s_w: + case Intrinsic::maxis_clei_s_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + lowerMSASplatImm(Op, 2, DAG, true), ISD::SETLE); + case Intrinsic::maxis_cle_u_b: + case Intrinsic::maxis_cle_u_h: + case Intrinsic::maxis_cle_u_w: + case Intrinsic::maxis_cle_u_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETULE); + case Intrinsic::maxis_clei_u_b: + case Intrinsic::maxis_clei_u_h: + case Intrinsic::maxis_clei_u_w: + case Intrinsic::maxis_clei_u_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + lowerMSASplatImm(Op, 2, DAG), ISD::SETULE); + case Intrinsic::maxis_clt_s_b: + case Intrinsic::maxis_clt_s_h: + case Intrinsic::maxis_clt_s_w: + case Intrinsic::maxis_clt_s_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETLT); + case Intrinsic::maxis_clti_s_b: + case Intrinsic::maxis_clti_s_h: + case Intrinsic::maxis_clti_s_w: + case Intrinsic::maxis_clti_s_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + lowerMSASplatImm(Op, 2, DAG, true), ISD::SETLT); + case Intrinsic::maxis_clt_u_b: + case Intrinsic::maxis_clt_u_h: + case Intrinsic::maxis_clt_u_w: + case Intrinsic::maxis_clt_u_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETULT); + case Intrinsic::maxis_clti_u_b: + case Intrinsic::maxis_clti_u_h: + case Intrinsic::maxis_clti_u_w: + case Intrinsic::maxis_clti_u_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + lowerMSASplatImm(Op, 2, DAG), ISD::SETULT); + case Intrinsic::maxis_copy_s_b: + case Intrinsic::maxis_copy_s_h: + case Intrinsic::maxis_copy_s_w: + return lowerMSACopyIntr(Op, DAG, MaxisISD::VEXTRACT_SEXT_ELT); + case Intrinsic::maxis_copy_s_d: + if (Subtarget.hasMaxis64()) + // Lower directly into VEXTRACT_SEXT_ELT since i64 is legal on Maxis64. + return lowerMSACopyIntr(Op, DAG, MaxisISD::VEXTRACT_SEXT_ELT); + else { + // Lower into the generic EXTRACT_VECTOR_ELT node and let the type + // legalizer and EXTRACT_VECTOR_ELT lowering sort it out. + return DAG.getNode(ISD::EXTRACT_VECTOR_ELT, SDLoc(Op), + Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + } + case Intrinsic::maxis_copy_u_b: + case Intrinsic::maxis_copy_u_h: + case Intrinsic::maxis_copy_u_w: + return lowerMSACopyIntr(Op, DAG, MaxisISD::VEXTRACT_ZEXT_ELT); + case Intrinsic::maxis_copy_u_d: + if (Subtarget.hasMaxis64()) + // Lower directly into VEXTRACT_ZEXT_ELT since i64 is legal on Maxis64. + return lowerMSACopyIntr(Op, DAG, MaxisISD::VEXTRACT_ZEXT_ELT); + else { + // Lower into the generic EXTRACT_VECTOR_ELT node and let the type + // legalizer and EXTRACT_VECTOR_ELT lowering sort it out. + // Note: When i64 is illegal, this results in copy_s.w instructions + // instead of copy_u.w instructions. This makes no difference to the + // behaviour since i64 is only illegal when the register file is 32-bit. + return DAG.getNode(ISD::EXTRACT_VECTOR_ELT, SDLoc(Op), + Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + } + case Intrinsic::maxis_div_s_b: + case Intrinsic::maxis_div_s_h: + case Intrinsic::maxis_div_s_w: + case Intrinsic::maxis_div_s_d: + return DAG.getNode(ISD::SDIV, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_div_u_b: + case Intrinsic::maxis_div_u_h: + case Intrinsic::maxis_div_u_w: + case Intrinsic::maxis_div_u_d: + return DAG.getNode(ISD::UDIV, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_fadd_w: + case Intrinsic::maxis_fadd_d: + // TODO: If intrinsics have fast-math-flags, propagate them. + return DAG.getNode(ISD::FADD, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + // Don't lower maxis_fcaf_[wd] since LLVM folds SETFALSE condcodes away + case Intrinsic::maxis_fceq_w: + case Intrinsic::maxis_fceq_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETOEQ); + case Intrinsic::maxis_fcle_w: + case Intrinsic::maxis_fcle_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETOLE); + case Intrinsic::maxis_fclt_w: + case Intrinsic::maxis_fclt_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETOLT); + case Intrinsic::maxis_fcne_w: + case Intrinsic::maxis_fcne_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETONE); + case Intrinsic::maxis_fcor_w: + case Intrinsic::maxis_fcor_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETO); + case Intrinsic::maxis_fcueq_w: + case Intrinsic::maxis_fcueq_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETUEQ); + case Intrinsic::maxis_fcule_w: + case Intrinsic::maxis_fcule_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETULE); + case Intrinsic::maxis_fcult_w: + case Intrinsic::maxis_fcult_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETULT); + case Intrinsic::maxis_fcun_w: + case Intrinsic::maxis_fcun_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETUO); + case Intrinsic::maxis_fcune_w: + case Intrinsic::maxis_fcune_d: + return DAG.getSetCC(DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2), ISD::SETUNE); + case Intrinsic::maxis_fdiv_w: + case Intrinsic::maxis_fdiv_d: + // TODO: If intrinsics have fast-math-flags, propagate them. + return DAG.getNode(ISD::FDIV, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_ffint_u_w: + case Intrinsic::maxis_ffint_u_d: + return DAG.getNode(ISD::UINT_TO_FP, DL, Op->getValueType(0), + Op->getOperand(1)); + case Intrinsic::maxis_ffint_s_w: + case Intrinsic::maxis_ffint_s_d: + return DAG.getNode(ISD::SINT_TO_FP, DL, Op->getValueType(0), + Op->getOperand(1)); + case Intrinsic::maxis_fill_b: + case Intrinsic::maxis_fill_h: + case Intrinsic::maxis_fill_w: + case Intrinsic::maxis_fill_d: { + EVT ResTy = Op->getValueType(0); + SmallVector Ops(ResTy.getVectorNumElements(), + Op->getOperand(1)); + + // If ResTy is v2i64 then the type legalizer will break this node down into + // an equivalent v4i32. + return DAG.getBuildVector(ResTy, DL, Ops); + } + case Intrinsic::maxis_fexp2_w: + case Intrinsic::maxis_fexp2_d: { + // TODO: If intrinsics have fast-math-flags, propagate them. + EVT ResTy = Op->getValueType(0); + return DAG.getNode( + ISD::FMUL, SDLoc(Op), ResTy, Op->getOperand(1), + DAG.getNode(ISD::FEXP2, SDLoc(Op), ResTy, Op->getOperand(2))); + } + case Intrinsic::maxis_flog2_w: + case Intrinsic::maxis_flog2_d: + return DAG.getNode(ISD::FLOG2, DL, Op->getValueType(0), Op->getOperand(1)); + case Intrinsic::maxis_fmadd_w: + case Intrinsic::maxis_fmadd_d: + return DAG.getNode(ISD::FMA, SDLoc(Op), Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2), Op->getOperand(3)); + case Intrinsic::maxis_fmul_w: + case Intrinsic::maxis_fmul_d: + // TODO: If intrinsics have fast-math-flags, propagate them. + return DAG.getNode(ISD::FMUL, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_fmsub_w: + case Intrinsic::maxis_fmsub_d: { + // TODO: If intrinsics have fast-math-flags, propagate them. + EVT ResTy = Op->getValueType(0); + return DAG.getNode(ISD::FSUB, SDLoc(Op), ResTy, Op->getOperand(1), + DAG.getNode(ISD::FMUL, SDLoc(Op), ResTy, + Op->getOperand(2), Op->getOperand(3))); + } + case Intrinsic::maxis_frint_w: + case Intrinsic::maxis_frint_d: + return DAG.getNode(ISD::FRINT, DL, Op->getValueType(0), Op->getOperand(1)); + case Intrinsic::maxis_fsqrt_w: + case Intrinsic::maxis_fsqrt_d: + return DAG.getNode(ISD::FSQRT, DL, Op->getValueType(0), Op->getOperand(1)); + case Intrinsic::maxis_fsub_w: + case Intrinsic::maxis_fsub_d: + // TODO: If intrinsics have fast-math-flags, propagate them. + return DAG.getNode(ISD::FSUB, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_ftrunc_u_w: + case Intrinsic::maxis_ftrunc_u_d: + return DAG.getNode(ISD::FP_TO_UINT, DL, Op->getValueType(0), + Op->getOperand(1)); + case Intrinsic::maxis_ftrunc_s_w: + case Intrinsic::maxis_ftrunc_s_d: + return DAG.getNode(ISD::FP_TO_SINT, DL, Op->getValueType(0), + Op->getOperand(1)); + case Intrinsic::maxis_ilvev_b: + case Intrinsic::maxis_ilvev_h: + case Intrinsic::maxis_ilvev_w: + case Intrinsic::maxis_ilvev_d: + return DAG.getNode(MaxisISD::ILVEV, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_ilvl_b: + case Intrinsic::maxis_ilvl_h: + case Intrinsic::maxis_ilvl_w: + case Intrinsic::maxis_ilvl_d: + return DAG.getNode(MaxisISD::ILVL, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_ilvod_b: + case Intrinsic::maxis_ilvod_h: + case Intrinsic::maxis_ilvod_w: + case Intrinsic::maxis_ilvod_d: + return DAG.getNode(MaxisISD::ILVOD, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_ilvr_b: + case Intrinsic::maxis_ilvr_h: + case Intrinsic::maxis_ilvr_w: + case Intrinsic::maxis_ilvr_d: + return DAG.getNode(MaxisISD::ILVR, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_insert_b: + case Intrinsic::maxis_insert_h: + case Intrinsic::maxis_insert_w: + case Intrinsic::maxis_insert_d: + return DAG.getNode(ISD::INSERT_VECTOR_ELT, SDLoc(Op), Op->getValueType(0), + Op->getOperand(1), Op->getOperand(3), Op->getOperand(2)); + case Intrinsic::maxis_insve_b: + case Intrinsic::maxis_insve_h: + case Intrinsic::maxis_insve_w: + case Intrinsic::maxis_insve_d: { + // Report an error for out of range values. + int64_t Max; + switch (Intrinsic) { + case Intrinsic::maxis_insve_b: Max = 15; break; + case Intrinsic::maxis_insve_h: Max = 7; break; + case Intrinsic::maxis_insve_w: Max = 3; break; + case Intrinsic::maxis_insve_d: Max = 1; break; + default: llvm_unreachable("Unmatched intrinsic"); + } + int64_t Value = cast(Op->getOperand(2))->getSExtValue(); + if (Value < 0 || Value > Max) + report_fatal_error("Immediate out of range"); + return DAG.getNode(MaxisISD::INSVE, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2), Op->getOperand(3), + DAG.getConstant(0, DL, MVT::i32)); + } + case Intrinsic::maxis_ldi_b: + case Intrinsic::maxis_ldi_h: + case Intrinsic::maxis_ldi_w: + case Intrinsic::maxis_ldi_d: + return lowerMSASplatImm(Op, 1, DAG, true); + case Intrinsic::maxis_lsa: + case Intrinsic::maxis_dlsa: { + EVT ResTy = Op->getValueType(0); + return DAG.getNode(ISD::ADD, SDLoc(Op), ResTy, Op->getOperand(1), + DAG.getNode(ISD::SHL, SDLoc(Op), ResTy, + Op->getOperand(2), Op->getOperand(3))); + } + case Intrinsic::maxis_maddv_b: + case Intrinsic::maxis_maddv_h: + case Intrinsic::maxis_maddv_w: + case Intrinsic::maxis_maddv_d: { + EVT ResTy = Op->getValueType(0); + return DAG.getNode(ISD::ADD, SDLoc(Op), ResTy, Op->getOperand(1), + DAG.getNode(ISD::MUL, SDLoc(Op), ResTy, + Op->getOperand(2), Op->getOperand(3))); + } + case Intrinsic::maxis_max_s_b: + case Intrinsic::maxis_max_s_h: + case Intrinsic::maxis_max_s_w: + case Intrinsic::maxis_max_s_d: + return DAG.getNode(MaxisISD::VSMAX, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_max_u_b: + case Intrinsic::maxis_max_u_h: + case Intrinsic::maxis_max_u_w: + case Intrinsic::maxis_max_u_d: + return DAG.getNode(MaxisISD::VUMAX, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_maxi_s_b: + case Intrinsic::maxis_maxi_s_h: + case Intrinsic::maxis_maxi_s_w: + case Intrinsic::maxis_maxi_s_d: + return DAG.getNode(MaxisISD::VSMAX, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG, true)); + case Intrinsic::maxis_maxi_u_b: + case Intrinsic::maxis_maxi_u_h: + case Intrinsic::maxis_maxi_u_w: + case Intrinsic::maxis_maxi_u_d: + return DAG.getNode(MaxisISD::VUMAX, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_min_s_b: + case Intrinsic::maxis_min_s_h: + case Intrinsic::maxis_min_s_w: + case Intrinsic::maxis_min_s_d: + return DAG.getNode(MaxisISD::VSMIN, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_min_u_b: + case Intrinsic::maxis_min_u_h: + case Intrinsic::maxis_min_u_w: + case Intrinsic::maxis_min_u_d: + return DAG.getNode(MaxisISD::VUMIN, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_mini_s_b: + case Intrinsic::maxis_mini_s_h: + case Intrinsic::maxis_mini_s_w: + case Intrinsic::maxis_mini_s_d: + return DAG.getNode(MaxisISD::VSMIN, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG, true)); + case Intrinsic::maxis_mini_u_b: + case Intrinsic::maxis_mini_u_h: + case Intrinsic::maxis_mini_u_w: + case Intrinsic::maxis_mini_u_d: + return DAG.getNode(MaxisISD::VUMIN, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_mod_s_b: + case Intrinsic::maxis_mod_s_h: + case Intrinsic::maxis_mod_s_w: + case Intrinsic::maxis_mod_s_d: + return DAG.getNode(ISD::SREM, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_mod_u_b: + case Intrinsic::maxis_mod_u_h: + case Intrinsic::maxis_mod_u_w: + case Intrinsic::maxis_mod_u_d: + return DAG.getNode(ISD::UREM, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_mulv_b: + case Intrinsic::maxis_mulv_h: + case Intrinsic::maxis_mulv_w: + case Intrinsic::maxis_mulv_d: + return DAG.getNode(ISD::MUL, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_msubv_b: + case Intrinsic::maxis_msubv_h: + case Intrinsic::maxis_msubv_w: + case Intrinsic::maxis_msubv_d: { + EVT ResTy = Op->getValueType(0); + return DAG.getNode(ISD::SUB, SDLoc(Op), ResTy, Op->getOperand(1), + DAG.getNode(ISD::MUL, SDLoc(Op), ResTy, + Op->getOperand(2), Op->getOperand(3))); + } + case Intrinsic::maxis_nlzc_b: + case Intrinsic::maxis_nlzc_h: + case Intrinsic::maxis_nlzc_w: + case Intrinsic::maxis_nlzc_d: + return DAG.getNode(ISD::CTLZ, DL, Op->getValueType(0), Op->getOperand(1)); + case Intrinsic::maxis_nor_v: { + SDValue Res = DAG.getNode(ISD::OR, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + return DAG.getNOT(DL, Res, Res->getValueType(0)); + } + case Intrinsic::maxis_nori_b: { + SDValue Res = DAG.getNode(ISD::OR, DL, Op->getValueType(0), + Op->getOperand(1), + lowerMSASplatImm(Op, 2, DAG)); + return DAG.getNOT(DL, Res, Res->getValueType(0)); + } + case Intrinsic::maxis_or_v: + return DAG.getNode(ISD::OR, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_ori_b: + return DAG.getNode(ISD::OR, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_pckev_b: + case Intrinsic::maxis_pckev_h: + case Intrinsic::maxis_pckev_w: + case Intrinsic::maxis_pckev_d: + return DAG.getNode(MaxisISD::PCKEV, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_pckod_b: + case Intrinsic::maxis_pckod_h: + case Intrinsic::maxis_pckod_w: + case Intrinsic::maxis_pckod_d: + return DAG.getNode(MaxisISD::PCKOD, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2)); + case Intrinsic::maxis_pcnt_b: + case Intrinsic::maxis_pcnt_h: + case Intrinsic::maxis_pcnt_w: + case Intrinsic::maxis_pcnt_d: + return DAG.getNode(ISD::CTPOP, DL, Op->getValueType(0), Op->getOperand(1)); + case Intrinsic::maxis_sat_s_b: + case Intrinsic::maxis_sat_s_h: + case Intrinsic::maxis_sat_s_w: + case Intrinsic::maxis_sat_s_d: + case Intrinsic::maxis_sat_u_b: + case Intrinsic::maxis_sat_u_h: + case Intrinsic::maxis_sat_u_w: + case Intrinsic::maxis_sat_u_d: { + // Report an error for out of range values. + int64_t Max; + switch (Intrinsic) { + case Intrinsic::maxis_sat_s_b: + case Intrinsic::maxis_sat_u_b: Max = 7; break; + case Intrinsic::maxis_sat_s_h: + case Intrinsic::maxis_sat_u_h: Max = 15; break; + case Intrinsic::maxis_sat_s_w: + case Intrinsic::maxis_sat_u_w: Max = 31; break; + case Intrinsic::maxis_sat_s_d: + case Intrinsic::maxis_sat_u_d: Max = 63; break; + default: llvm_unreachable("Unmatched intrinsic"); + } + int64_t Value = cast(Op->getOperand(2))->getSExtValue(); + if (Value < 0 || Value > Max) + report_fatal_error("Immediate out of range"); + return SDValue(); + } + case Intrinsic::maxis_shf_b: + case Intrinsic::maxis_shf_h: + case Intrinsic::maxis_shf_w: { + int64_t Value = cast(Op->getOperand(2))->getSExtValue(); + if (Value < 0 || Value > 255) + report_fatal_error("Immediate out of range"); + return DAG.getNode(MaxisISD::SHF, DL, Op->getValueType(0), + Op->getOperand(2), Op->getOperand(1)); + } + case Intrinsic::maxis_sldi_b: + case Intrinsic::maxis_sldi_h: + case Intrinsic::maxis_sldi_w: + case Intrinsic::maxis_sldi_d: { + // Report an error for out of range values. + int64_t Max; + switch (Intrinsic) { + case Intrinsic::maxis_sldi_b: Max = 15; break; + case Intrinsic::maxis_sldi_h: Max = 7; break; + case Intrinsic::maxis_sldi_w: Max = 3; break; + case Intrinsic::maxis_sldi_d: Max = 1; break; + default: llvm_unreachable("Unmatched intrinsic"); + } + int64_t Value = cast(Op->getOperand(3))->getSExtValue(); + if (Value < 0 || Value > Max) + report_fatal_error("Immediate out of range"); + return SDValue(); + } + case Intrinsic::maxis_sll_b: + case Intrinsic::maxis_sll_h: + case Intrinsic::maxis_sll_w: + case Intrinsic::maxis_sll_d: + return DAG.getNode(ISD::SHL, DL, Op->getValueType(0), Op->getOperand(1), + truncateVecElts(Op, DAG)); + case Intrinsic::maxis_slli_b: + case Intrinsic::maxis_slli_h: + case Intrinsic::maxis_slli_w: + case Intrinsic::maxis_slli_d: + return DAG.getNode(ISD::SHL, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_splat_b: + case Intrinsic::maxis_splat_h: + case Intrinsic::maxis_splat_w: + case Intrinsic::maxis_splat_d: + // We can't lower via VECTOR_SHUFFLE because it requires constant shuffle + // masks, nor can we lower via BUILD_VECTOR & EXTRACT_VECTOR_ELT because + // EXTRACT_VECTOR_ELT can't extract i64's on MAXIS32. + // Instead we lower to MaxisISD::VSHF and match from there. + return DAG.getNode(MaxisISD::VSHF, DL, Op->getValueType(0), + lowerMSASplatZExt(Op, 2, DAG), Op->getOperand(1), + Op->getOperand(1)); + case Intrinsic::maxis_splati_b: + case Intrinsic::maxis_splati_h: + case Intrinsic::maxis_splati_w: + case Intrinsic::maxis_splati_d: + return DAG.getNode(MaxisISD::VSHF, DL, Op->getValueType(0), + lowerMSASplatImm(Op, 2, DAG), Op->getOperand(1), + Op->getOperand(1)); + case Intrinsic::maxis_sra_b: + case Intrinsic::maxis_sra_h: + case Intrinsic::maxis_sra_w: + case Intrinsic::maxis_sra_d: + return DAG.getNode(ISD::SRA, DL, Op->getValueType(0), Op->getOperand(1), + truncateVecElts(Op, DAG)); + case Intrinsic::maxis_srai_b: + case Intrinsic::maxis_srai_h: + case Intrinsic::maxis_srai_w: + case Intrinsic::maxis_srai_d: + return DAG.getNode(ISD::SRA, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_srari_b: + case Intrinsic::maxis_srari_h: + case Intrinsic::maxis_srari_w: + case Intrinsic::maxis_srari_d: { + // Report an error for out of range values. + int64_t Max; + switch (Intrinsic) { + case Intrinsic::maxis_srari_b: Max = 7; break; + case Intrinsic::maxis_srari_h: Max = 15; break; + case Intrinsic::maxis_srari_w: Max = 31; break; + case Intrinsic::maxis_srari_d: Max = 63; break; + default: llvm_unreachable("Unmatched intrinsic"); + } + int64_t Value = cast(Op->getOperand(2))->getSExtValue(); + if (Value < 0 || Value > Max) + report_fatal_error("Immediate out of range"); + return SDValue(); + } + case Intrinsic::maxis_srl_b: + case Intrinsic::maxis_srl_h: + case Intrinsic::maxis_srl_w: + case Intrinsic::maxis_srl_d: + return DAG.getNode(ISD::SRL, DL, Op->getValueType(0), Op->getOperand(1), + truncateVecElts(Op, DAG)); + case Intrinsic::maxis_srli_b: + case Intrinsic::maxis_srli_h: + case Intrinsic::maxis_srli_w: + case Intrinsic::maxis_srli_d: + return DAG.getNode(ISD::SRL, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_srlri_b: + case Intrinsic::maxis_srlri_h: + case Intrinsic::maxis_srlri_w: + case Intrinsic::maxis_srlri_d: { + // Report an error for out of range values. + int64_t Max; + switch (Intrinsic) { + case Intrinsic::maxis_srlri_b: Max = 7; break; + case Intrinsic::maxis_srlri_h: Max = 15; break; + case Intrinsic::maxis_srlri_w: Max = 31; break; + case Intrinsic::maxis_srlri_d: Max = 63; break; + default: llvm_unreachable("Unmatched intrinsic"); + } + int64_t Value = cast(Op->getOperand(2))->getSExtValue(); + if (Value < 0 || Value > Max) + report_fatal_error("Immediate out of range"); + return SDValue(); + } + case Intrinsic::maxis_subv_b: + case Intrinsic::maxis_subv_h: + case Intrinsic::maxis_subv_w: + case Intrinsic::maxis_subv_d: + return DAG.getNode(ISD::SUB, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_subvi_b: + case Intrinsic::maxis_subvi_h: + case Intrinsic::maxis_subvi_w: + case Intrinsic::maxis_subvi_d: + return DAG.getNode(ISD::SUB, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::maxis_vshf_b: + case Intrinsic::maxis_vshf_h: + case Intrinsic::maxis_vshf_w: + case Intrinsic::maxis_vshf_d: + return DAG.getNode(MaxisISD::VSHF, DL, Op->getValueType(0), + Op->getOperand(1), Op->getOperand(2), Op->getOperand(3)); + case Intrinsic::maxis_xor_v: + return DAG.getNode(ISD::XOR, DL, Op->getValueType(0), Op->getOperand(1), + Op->getOperand(2)); + case Intrinsic::maxis_xori_b: + return DAG.getNode(ISD::XOR, DL, Op->getValueType(0), + Op->getOperand(1), lowerMSASplatImm(Op, 2, DAG)); + case Intrinsic::thread_pointer: { + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + return DAG.getNode(MaxisISD::ThreadPointer, DL, PtrVT); + } + } +} + +static SDValue lowerMSALoadIntr(SDValue Op, SelectionDAG &DAG, unsigned Intr, + const MaxisSubtarget &Subtarget) { + SDLoc DL(Op); + SDValue ChainIn = Op->getOperand(0); + SDValue Address = Op->getOperand(2); + SDValue Offset = Op->getOperand(3); + EVT ResTy = Op->getValueType(0); + EVT PtrTy = Address->getValueType(0); + + // For N64 addresses have the underlying type MVT::i64. This intrinsic + // however takes an i32 signed constant offset. The actual type of the + // intrinsic is a scaled signed i10. + if (Subtarget.isABI_N64()) + Offset = DAG.getNode(ISD::SIGN_EXTEND, DL, PtrTy, Offset); + + Address = DAG.getNode(ISD::ADD, DL, PtrTy, Address, Offset); + return DAG.getLoad(ResTy, DL, ChainIn, Address, MachinePointerInfo(), + /* Alignment = */ 16); +} + +SDValue MaxisSETargetLowering::lowerINTRINSIC_W_CHAIN(SDValue Op, + SelectionDAG &DAG) const { + unsigned Intr = cast(Op->getOperand(1))->getZExtValue(); + switch (Intr) { + default: + return SDValue(); + case Intrinsic::maxis_extp: + return lowerDSPIntr(Op, DAG, MaxisISD::EXTP); + case Intrinsic::maxis_extpdp: + return lowerDSPIntr(Op, DAG, MaxisISD::EXTPDP); + case Intrinsic::maxis_extr_w: + return lowerDSPIntr(Op, DAG, MaxisISD::EXTR_W); + case Intrinsic::maxis_extr_r_w: + return lowerDSPIntr(Op, DAG, MaxisISD::EXTR_R_W); + case Intrinsic::maxis_extr_rs_w: + return lowerDSPIntr(Op, DAG, MaxisISD::EXTR_RS_W); + case Intrinsic::maxis_extr_s_h: + return lowerDSPIntr(Op, DAG, MaxisISD::EXTR_S_H); + case Intrinsic::maxis_mthlip: + return lowerDSPIntr(Op, DAG, MaxisISD::MTHLIP); + case Intrinsic::maxis_mulsaq_s_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::MULSAQ_S_W_PH); + case Intrinsic::maxis_maq_s_w_phl: + return lowerDSPIntr(Op, DAG, MaxisISD::MAQ_S_W_PHL); + case Intrinsic::maxis_maq_s_w_phr: + return lowerDSPIntr(Op, DAG, MaxisISD::MAQ_S_W_PHR); + case Intrinsic::maxis_maq_sa_w_phl: + return lowerDSPIntr(Op, DAG, MaxisISD::MAQ_SA_W_PHL); + case Intrinsic::maxis_maq_sa_w_phr: + return lowerDSPIntr(Op, DAG, MaxisISD::MAQ_SA_W_PHR); + case Intrinsic::maxis_dpaq_s_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPAQ_S_W_PH); + case Intrinsic::maxis_dpsq_s_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPSQ_S_W_PH); + case Intrinsic::maxis_dpaq_sa_l_w: + return lowerDSPIntr(Op, DAG, MaxisISD::DPAQ_SA_L_W); + case Intrinsic::maxis_dpsq_sa_l_w: + return lowerDSPIntr(Op, DAG, MaxisISD::DPSQ_SA_L_W); + case Intrinsic::maxis_dpaqx_s_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPAQX_S_W_PH); + case Intrinsic::maxis_dpaqx_sa_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPAQX_SA_W_PH); + case Intrinsic::maxis_dpsqx_s_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPSQX_S_W_PH); + case Intrinsic::maxis_dpsqx_sa_w_ph: + return lowerDSPIntr(Op, DAG, MaxisISD::DPSQX_SA_W_PH); + case Intrinsic::maxis_ld_b: + case Intrinsic::maxis_ld_h: + case Intrinsic::maxis_ld_w: + case Intrinsic::maxis_ld_d: + return lowerMSALoadIntr(Op, DAG, Intr, Subtarget); + } +} + +static SDValue lowerMSAStoreIntr(SDValue Op, SelectionDAG &DAG, unsigned Intr, + const MaxisSubtarget &Subtarget) { + SDLoc DL(Op); + SDValue ChainIn = Op->getOperand(0); + SDValue Value = Op->getOperand(2); + SDValue Address = Op->getOperand(3); + SDValue Offset = Op->getOperand(4); + EVT PtrTy = Address->getValueType(0); + + // For N64 addresses have the underlying type MVT::i64. This intrinsic + // however takes an i32 signed constant offset. The actual type of the + // intrinsic is a scaled signed i10. + if (Subtarget.isABI_N64()) + Offset = DAG.getNode(ISD::SIGN_EXTEND, DL, PtrTy, Offset); + + Address = DAG.getNode(ISD::ADD, DL, PtrTy, Address, Offset); + + return DAG.getStore(ChainIn, DL, Value, Address, MachinePointerInfo(), + /* Alignment = */ 16); +} + +SDValue MaxisSETargetLowering::lowerINTRINSIC_VOID(SDValue Op, + SelectionDAG &DAG) const { + unsigned Intr = cast(Op->getOperand(1))->getZExtValue(); + switch (Intr) { + default: + return SDValue(); + case Intrinsic::maxis_st_b: + case Intrinsic::maxis_st_h: + case Intrinsic::maxis_st_w: + case Intrinsic::maxis_st_d: + return lowerMSAStoreIntr(Op, DAG, Intr, Subtarget); + } +} + +/// \brief Check if the given BuildVectorSDNode is a splat. +/// This method currently relies on DAG nodes being reused when equivalent, +/// so it's possible for this to return false even when isConstantSplat returns +/// true. +static bool isSplatVector(const BuildVectorSDNode *N) { + unsigned int nOps = N->getNumOperands(); + assert(nOps > 1 && "isSplatVector has 0 or 1 sized build vector"); + + SDValue Operand0 = N->getOperand(0); + + for (unsigned int i = 1; i < nOps; ++i) { + if (N->getOperand(i) != Operand0) + return false; + } + + return true; +} + +// Lower ISD::EXTRACT_VECTOR_ELT into MaxisISD::VEXTRACT_SEXT_ELT. +// +// The non-value bits resulting from ISD::EXTRACT_VECTOR_ELT are undefined. We +// choose to sign-extend but we could have equally chosen zero-extend. The +// DAGCombiner will fold any sign/zero extension of the ISD::EXTRACT_VECTOR_ELT +// result into this node later (possibly changing it to a zero-extend in the +// process). +SDValue MaxisSETargetLowering:: +lowerEXTRACT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const { + SDLoc DL(Op); + EVT ResTy = Op->getValueType(0); + SDValue Op0 = Op->getOperand(0); + EVT VecTy = Op0->getValueType(0); + + if (!VecTy.is128BitVector()) + return SDValue(); + + if (ResTy.isInteger()) { + SDValue Op1 = Op->getOperand(1); + EVT EltTy = VecTy.getVectorElementType(); + return DAG.getNode(MaxisISD::VEXTRACT_SEXT_ELT, DL, ResTy, Op0, Op1, + DAG.getValueType(EltTy)); + } + + return Op; +} + +static bool isConstantOrUndef(const SDValue Op) { + if (Op->isUndef()) + return true; + if (isa(Op)) + return true; + if (isa(Op)) + return true; + return false; +} + +static bool isConstantOrUndefBUILD_VECTOR(const BuildVectorSDNode *Op) { + for (unsigned i = 0; i < Op->getNumOperands(); ++i) + if (isConstantOrUndef(Op->getOperand(i))) + return true; + return false; +} + +// Lowers ISD::BUILD_VECTOR into appropriate SelectionDAG nodes for the +// backend. +// +// Lowers according to the following rules: +// - Constant splats are legal as-is as long as the SplatBitSize is a power of +// 2 less than or equal to 64 and the value fits into a signed 10-bit +// immediate +// - Constant splats are lowered to bitconverted BUILD_VECTORs if SplatBitSize +// is a power of 2 less than or equal to 64 and the value does not fit into a +// signed 10-bit immediate +// - Non-constant splats are legal as-is. +// - Non-constant non-splats are lowered to sequences of INSERT_VECTOR_ELT. +// - All others are illegal and must be expanded. +SDValue MaxisSETargetLowering::lowerBUILD_VECTOR(SDValue Op, + SelectionDAG &DAG) const { + BuildVectorSDNode *Node = cast(Op); + EVT ResTy = Op->getValueType(0); + SDLoc DL(Op); + APInt SplatValue, SplatUndef; + unsigned SplatBitSize; + bool HasAnyUndefs; + + if (!Subtarget.hasMSA() || !ResTy.is128BitVector()) + return SDValue(); + + if (Node->isConstantSplat(SplatValue, SplatUndef, SplatBitSize, + HasAnyUndefs, 8, + !Subtarget.isLittle()) && SplatBitSize <= 64) { + // We can only cope with 8, 16, 32, or 64-bit elements + if (SplatBitSize != 8 && SplatBitSize != 16 && SplatBitSize != 32 && + SplatBitSize != 64) + return SDValue(); + + // If the value isn't an integer type we will have to bitcast + // from an integer type first. Also, if there are any undefs, we must + // lower them to defined values first. + if (ResTy.isInteger() && !HasAnyUndefs) + return Op; + + EVT ViaVecTy; + + switch (SplatBitSize) { + default: + return SDValue(); + case 8: + ViaVecTy = MVT::v16i8; + break; + case 16: + ViaVecTy = MVT::v8i16; + break; + case 32: + ViaVecTy = MVT::v4i32; + break; + case 64: + // There's no fill.d to fall back on for 64-bit values + return SDValue(); + } + + // SelectionDAG::getConstant will promote SplatValue appropriately. + SDValue Result = DAG.getConstant(SplatValue, DL, ViaVecTy); + + // Bitcast to the type we originally wanted + if (ViaVecTy != ResTy) + Result = DAG.getNode(ISD::BITCAST, SDLoc(Node), ResTy, Result); + + return Result; + } else if (isSplatVector(Node)) + return Op; + else if (!isConstantOrUndefBUILD_VECTOR(Node)) { + // Use INSERT_VECTOR_ELT operations rather than expand to stores. + // The resulting code is the same length as the expansion, but it doesn't + // use memory operations + EVT ResTy = Node->getValueType(0); + + assert(ResTy.isVector()); + + unsigned NumElts = ResTy.getVectorNumElements(); + SDValue Vector = DAG.getUNDEF(ResTy); + for (unsigned i = 0; i < NumElts; ++i) { + Vector = DAG.getNode(ISD::INSERT_VECTOR_ELT, DL, ResTy, Vector, + Node->getOperand(i), + DAG.getConstant(i, DL, MVT::i32)); + } + return Vector; + } + + return SDValue(); +} + +// Lower VECTOR_SHUFFLE into SHF (if possible). +// +// SHF splits the vector into blocks of four elements, then shuffles these +// elements according to a <4 x i2> constant (encoded as an integer immediate). +// +// It is therefore possible to lower into SHF when the mask takes the form: +// +// When undef's appear they are treated as if they were whatever value is +// necessary in order to fit the above forms. +// +// For example: +// %2 = shufflevector <8 x i16> %0, <8 x i16> undef, +// <8 x i32> +// is lowered to: +// (SHF_H $w0, $w1, 27) +// where the 27 comes from: +// 3 + (2 << 2) + (1 << 4) + (0 << 6) +static SDValue lowerVECTOR_SHUFFLE_SHF(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + int SHFIndices[4] = { -1, -1, -1, -1 }; + + if (Indices.size() < 4) + return SDValue(); + + for (unsigned i = 0; i < 4; ++i) { + for (unsigned j = i; j < Indices.size(); j += 4) { + int Idx = Indices[j]; + + // Convert from vector index to 4-element subvector index + // If an index refers to an element outside of the subvector then give up + if (Idx != -1) { + Idx -= 4 * (j / 4); + if (Idx < 0 || Idx >= 4) + return SDValue(); + } + + // If the mask has an undef, replace it with the current index. + // Note that it might still be undef if the current index is also undef + if (SHFIndices[i] == -1) + SHFIndices[i] = Idx; + + // Check that non-undef values are the same as in the mask. If they + // aren't then give up + if (!(Idx == -1 || Idx == SHFIndices[i])) + return SDValue(); + } + } + + // Calculate the immediate. Replace any remaining undefs with zero + APInt Imm(32, 0); + for (int i = 3; i >= 0; --i) { + int Idx = SHFIndices[i]; + + if (Idx == -1) + Idx = 0; + + Imm <<= 2; + Imm |= Idx & 0x3; + } + + SDLoc DL(Op); + return DAG.getNode(MaxisISD::SHF, DL, ResTy, + DAG.getConstant(Imm, DL, MVT::i32), Op->getOperand(0)); +} + +/// Determine whether a range fits a regular pattern of values. +/// This function accounts for the possibility of jumping over the End iterator. +template +static bool +fitsRegularPattern(typename SmallVectorImpl::const_iterator Begin, + unsigned CheckStride, + typename SmallVectorImpl::const_iterator End, + ValType ExpectedIndex, unsigned ExpectedIndexStride) { + auto &I = Begin; + + while (I != End) { + if (*I != -1 && *I != ExpectedIndex) + return false; + ExpectedIndex += ExpectedIndexStride; + + // Incrementing past End is undefined behaviour so we must increment one + // step at a time and check for End at each step. + for (unsigned n = 0; n < CheckStride && I != End; ++n, ++I) + ; // Empty loop body. + } + return true; +} + +// Determine whether VECTOR_SHUFFLE is a SPLATI. +// +// It is a SPLATI when the mask is: +// +// where x is any valid index. +// +// When undef's appear in the mask they are treated as if they were whatever +// value is necessary in order to fit the above form. +static bool isVECTOR_SHUFFLE_SPLATI(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + assert((Indices.size() % 2) == 0); + + int SplatIndex = -1; + for (const auto &V : Indices) { + if (V != -1) { + SplatIndex = V; + break; + } + } + + return fitsRegularPattern(Indices.begin(), 1, Indices.end(), SplatIndex, + 0); +} + +// Lower VECTOR_SHUFFLE into ILVEV (if possible). +// +// ILVEV interleaves the even elements from each vector. +// +// It is possible to lower into ILVEV when the mask consists of two of the +// following forms interleaved: +// <0, 2, 4, ...> +// +// where n is the number of elements in the vector. +// For example: +// <0, 0, 2, 2, 4, 4, ...> +// <0, n, 2, n+2, 4, n+4, ...> +// +// When undef's appear in the mask they are treated as if they were whatever +// value is necessary in order to fit the above forms. +static SDValue lowerVECTOR_SHUFFLE_ILVEV(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + assert((Indices.size() % 2) == 0); + + SDValue Wt; + SDValue Ws; + const auto &Begin = Indices.begin(); + const auto &End = Indices.end(); + + // Check even elements are taken from the even elements of one half or the + // other and pick an operand accordingly. + if (fitsRegularPattern(Begin, 2, End, 0, 2)) + Wt = Op->getOperand(0); + else if (fitsRegularPattern(Begin, 2, End, Indices.size(), 2)) + Wt = Op->getOperand(1); + else + return SDValue(); + + // Check odd elements are taken from the even elements of one half or the + // other and pick an operand accordingly. + if (fitsRegularPattern(Begin + 1, 2, End, 0, 2)) + Ws = Op->getOperand(0); + else if (fitsRegularPattern(Begin + 1, 2, End, Indices.size(), 2)) + Ws = Op->getOperand(1); + else + return SDValue(); + + return DAG.getNode(MaxisISD::ILVEV, SDLoc(Op), ResTy, Ws, Wt); +} + +// Lower VECTOR_SHUFFLE into ILVOD (if possible). +// +// ILVOD interleaves the odd elements from each vector. +// +// It is possible to lower into ILVOD when the mask consists of two of the +// following forms interleaved: +// <1, 3, 5, ...> +// +// where n is the number of elements in the vector. +// For example: +// <1, 1, 3, 3, 5, 5, ...> +// <1, n+1, 3, n+3, 5, n+5, ...> +// +// When undef's appear in the mask they are treated as if they were whatever +// value is necessary in order to fit the above forms. +static SDValue lowerVECTOR_SHUFFLE_ILVOD(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + assert((Indices.size() % 2) == 0); + + SDValue Wt; + SDValue Ws; + const auto &Begin = Indices.begin(); + const auto &End = Indices.end(); + + // Check even elements are taken from the odd elements of one half or the + // other and pick an operand accordingly. + if (fitsRegularPattern(Begin, 2, End, 1, 2)) + Wt = Op->getOperand(0); + else if (fitsRegularPattern(Begin, 2, End, Indices.size() + 1, 2)) + Wt = Op->getOperand(1); + else + return SDValue(); + + // Check odd elements are taken from the odd elements of one half or the + // other and pick an operand accordingly. + if (fitsRegularPattern(Begin + 1, 2, End, 1, 2)) + Ws = Op->getOperand(0); + else if (fitsRegularPattern(Begin + 1, 2, End, Indices.size() + 1, 2)) + Ws = Op->getOperand(1); + else + return SDValue(); + + return DAG.getNode(MaxisISD::ILVOD, SDLoc(Op), ResTy, Wt, Ws); +} + +// Lower VECTOR_SHUFFLE into ILVR (if possible). +// +// ILVR interleaves consecutive elements from the right (lowest-indexed) half of +// each vector. +// +// It is possible to lower into ILVR when the mask consists of two of the +// following forms interleaved: +// <0, 1, 2, ...> +// +// where n is the number of elements in the vector. +// For example: +// <0, 0, 1, 1, 2, 2, ...> +// <0, n, 1, n+1, 2, n+2, ...> +// +// When undef's appear in the mask they are treated as if they were whatever +// value is necessary in order to fit the above forms. +static SDValue lowerVECTOR_SHUFFLE_ILVR(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + assert((Indices.size() % 2) == 0); + + SDValue Wt; + SDValue Ws; + const auto &Begin = Indices.begin(); + const auto &End = Indices.end(); + + // Check even elements are taken from the right (lowest-indexed) elements of + // one half or the other and pick an operand accordingly. + if (fitsRegularPattern(Begin, 2, End, 0, 1)) + Wt = Op->getOperand(0); + else if (fitsRegularPattern(Begin, 2, End, Indices.size(), 1)) + Wt = Op->getOperand(1); + else + return SDValue(); + + // Check odd elements are taken from the right (lowest-indexed) elements of + // one half or the other and pick an operand accordingly. + if (fitsRegularPattern(Begin + 1, 2, End, 0, 1)) + Ws = Op->getOperand(0); + else if (fitsRegularPattern(Begin + 1, 2, End, Indices.size(), 1)) + Ws = Op->getOperand(1); + else + return SDValue(); + + return DAG.getNode(MaxisISD::ILVR, SDLoc(Op), ResTy, Ws, Wt); +} + +// Lower VECTOR_SHUFFLE into ILVL (if possible). +// +// ILVL interleaves consecutive elements from the left (highest-indexed) half +// of each vector. +// +// It is possible to lower into ILVL when the mask consists of two of the +// following forms interleaved: +// +// +// where n is the number of elements in the vector and x is half n. +// For example: +// +// +// +// When undef's appear in the mask they are treated as if they were whatever +// value is necessary in order to fit the above forms. +static SDValue lowerVECTOR_SHUFFLE_ILVL(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + assert((Indices.size() % 2) == 0); + + unsigned HalfSize = Indices.size() / 2; + SDValue Wt; + SDValue Ws; + const auto &Begin = Indices.begin(); + const auto &End = Indices.end(); + + // Check even elements are taken from the left (highest-indexed) elements of + // one half or the other and pick an operand accordingly. + if (fitsRegularPattern(Begin, 2, End, HalfSize, 1)) + Wt = Op->getOperand(0); + else if (fitsRegularPattern(Begin, 2, End, Indices.size() + HalfSize, 1)) + Wt = Op->getOperand(1); + else + return SDValue(); + + // Check odd elements are taken from the left (highest-indexed) elements of + // one half or the other and pick an operand accordingly. + if (fitsRegularPattern(Begin + 1, 2, End, HalfSize, 1)) + Ws = Op->getOperand(0); + else if (fitsRegularPattern(Begin + 1, 2, End, Indices.size() + HalfSize, + 1)) + Ws = Op->getOperand(1); + else + return SDValue(); + + return DAG.getNode(MaxisISD::ILVL, SDLoc(Op), ResTy, Ws, Wt); +} + +// Lower VECTOR_SHUFFLE into PCKEV (if possible). +// +// PCKEV copies the even elements of each vector into the result vector. +// +// It is possible to lower into PCKEV when the mask consists of two of the +// following forms concatenated: +// <0, 2, 4, ...> +// +// where n is the number of elements in the vector. +// For example: +// <0, 2, 4, ..., 0, 2, 4, ...> +// <0, 2, 4, ..., n, n+2, n+4, ...> +// +// When undef's appear in the mask they are treated as if they were whatever +// value is necessary in order to fit the above forms. +static SDValue lowerVECTOR_SHUFFLE_PCKEV(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + assert((Indices.size() % 2) == 0); + + SDValue Wt; + SDValue Ws; + const auto &Begin = Indices.begin(); + const auto &Mid = Indices.begin() + Indices.size() / 2; + const auto &End = Indices.end(); + + if (fitsRegularPattern(Begin, 1, Mid, 0, 2)) + Wt = Op->getOperand(0); + else if (fitsRegularPattern(Begin, 1, Mid, Indices.size(), 2)) + Wt = Op->getOperand(1); + else + return SDValue(); + + if (fitsRegularPattern(Mid, 1, End, 0, 2)) + Ws = Op->getOperand(0); + else if (fitsRegularPattern(Mid, 1, End, Indices.size(), 2)) + Ws = Op->getOperand(1); + else + return SDValue(); + + return DAG.getNode(MaxisISD::PCKEV, SDLoc(Op), ResTy, Ws, Wt); +} + +// Lower VECTOR_SHUFFLE into PCKOD (if possible). +// +// PCKOD copies the odd elements of each vector into the result vector. +// +// It is possible to lower into PCKOD when the mask consists of two of the +// following forms concatenated: +// <1, 3, 5, ...> +// +// where n is the number of elements in the vector. +// For example: +// <1, 3, 5, ..., 1, 3, 5, ...> +// <1, 3, 5, ..., n+1, n+3, n+5, ...> +// +// When undef's appear in the mask they are treated as if they were whatever +// value is necessary in order to fit the above forms. +static SDValue lowerVECTOR_SHUFFLE_PCKOD(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + assert((Indices.size() % 2) == 0); + + SDValue Wt; + SDValue Ws; + const auto &Begin = Indices.begin(); + const auto &Mid = Indices.begin() + Indices.size() / 2; + const auto &End = Indices.end(); + + if (fitsRegularPattern(Begin, 1, Mid, 1, 2)) + Wt = Op->getOperand(0); + else if (fitsRegularPattern(Begin, 1, Mid, Indices.size() + 1, 2)) + Wt = Op->getOperand(1); + else + return SDValue(); + + if (fitsRegularPattern(Mid, 1, End, 1, 2)) + Ws = Op->getOperand(0); + else if (fitsRegularPattern(Mid, 1, End, Indices.size() + 1, 2)) + Ws = Op->getOperand(1); + else + return SDValue(); + + return DAG.getNode(MaxisISD::PCKOD, SDLoc(Op), ResTy, Ws, Wt); +} + +// Lower VECTOR_SHUFFLE into VSHF. +// +// This mostly consists of converting the shuffle indices in Indices into a +// BUILD_VECTOR and adding it as an operand to the resulting VSHF. There is +// also code to eliminate unused operands of the VECTOR_SHUFFLE. For example, +// if the type is v8i16 and all the indices are less than 8 then the second +// operand is unused and can be replaced with anything. We choose to replace it +// with the used operand since this reduces the number of instructions overall. +static SDValue lowerVECTOR_SHUFFLE_VSHF(SDValue Op, EVT ResTy, + SmallVector Indices, + SelectionDAG &DAG) { + SmallVector Ops; + SDValue Op0; + SDValue Op1; + EVT MaskVecTy = ResTy.changeVectorElementTypeToInteger(); + EVT MaskEltTy = MaskVecTy.getVectorElementType(); + bool Using1stVec = false; + bool Using2ndVec = false; + SDLoc DL(Op); + int ResTyNumElts = ResTy.getVectorNumElements(); + + for (int i = 0; i < ResTyNumElts; ++i) { + // Idx == -1 means UNDEF + int Idx = Indices[i]; + + if (0 <= Idx && Idx < ResTyNumElts) + Using1stVec = true; + if (ResTyNumElts <= Idx && Idx < ResTyNumElts * 2) + Using2ndVec = true; + } + + for (SmallVector::iterator I = Indices.begin(); I != Indices.end(); + ++I) + Ops.push_back(DAG.getTargetConstant(*I, DL, MaskEltTy)); + + SDValue MaskVec = DAG.getBuildVector(MaskVecTy, DL, Ops); + + if (Using1stVec && Using2ndVec) { + Op0 = Op->getOperand(0); + Op1 = Op->getOperand(1); + } else if (Using1stVec) + Op0 = Op1 = Op->getOperand(0); + else if (Using2ndVec) + Op0 = Op1 = Op->getOperand(1); + else + llvm_unreachable("shuffle vector mask references neither vector operand?"); + + // VECTOR_SHUFFLE concatenates the vectors in an vectorwise fashion. + // <0b00, 0b01> + <0b10, 0b11> -> <0b00, 0b01, 0b10, 0b11> + // VSHF concatenates the vectors in a bitwise fashion: + // <0b00, 0b01> + <0b10, 0b11> -> + // 0b0100 + 0b1110 -> 0b01001110 + // <0b10, 0b11, 0b00, 0b01> + // We must therefore swap the operands to get the correct result. + return DAG.getNode(MaxisISD::VSHF, DL, ResTy, MaskVec, Op1, Op0); +} + +// Lower VECTOR_SHUFFLE into one of a number of instructions depending on the +// indices in the shuffle. +SDValue MaxisSETargetLowering::lowerVECTOR_SHUFFLE(SDValue Op, + SelectionDAG &DAG) const { + ShuffleVectorSDNode *Node = cast(Op); + EVT ResTy = Op->getValueType(0); + + if (!ResTy.is128BitVector()) + return SDValue(); + + int ResTyNumElts = ResTy.getVectorNumElements(); + SmallVector Indices; + + for (int i = 0; i < ResTyNumElts; ++i) + Indices.push_back(Node->getMaskElt(i)); + + // splati.[bhwd] is preferable to the others but is matched from + // MaxisISD::VSHF. + if (isVECTOR_SHUFFLE_SPLATI(Op, ResTy, Indices, DAG)) + return lowerVECTOR_SHUFFLE_VSHF(Op, ResTy, Indices, DAG); + SDValue Result; + if ((Result = lowerVECTOR_SHUFFLE_ILVEV(Op, ResTy, Indices, DAG))) + return Result; + if ((Result = lowerVECTOR_SHUFFLE_ILVOD(Op, ResTy, Indices, DAG))) + return Result; + if ((Result = lowerVECTOR_SHUFFLE_ILVL(Op, ResTy, Indices, DAG))) + return Result; + if ((Result = lowerVECTOR_SHUFFLE_ILVR(Op, ResTy, Indices, DAG))) + return Result; + if ((Result = lowerVECTOR_SHUFFLE_PCKEV(Op, ResTy, Indices, DAG))) + return Result; + if ((Result = lowerVECTOR_SHUFFLE_PCKOD(Op, ResTy, Indices, DAG))) + return Result; + if ((Result = lowerVECTOR_SHUFFLE_SHF(Op, ResTy, Indices, DAG))) + return Result; + return lowerVECTOR_SHUFFLE_VSHF(Op, ResTy, Indices, DAG); +} + +MachineBasicBlock * +MaxisSETargetLowering::emitBPOSGE32(MachineInstr &MI, + MachineBasicBlock *BB) const { + // $bb: + // bposge32_pseudo $vr0 + // => + // $bb: + // bposge32 $tbb + // $fbb: + // li $vr2, 0 + // b $sink + // $tbb: + // li $vr1, 1 + // $sink: + // $vr0 = phi($vr2, $fbb, $vr1, $tbb) + + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + const TargetRegisterClass *RC = &Maxis::GPR32RegClass; + DebugLoc DL = MI.getDebugLoc(); + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = std::next(MachineFunction::iterator(BB)); + MachineFunction *F = BB->getParent(); + MachineBasicBlock *FBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *TBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *Sink = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, FBB); + F->insert(It, TBB); + F->insert(It, Sink); + + // Transfer the remainder of BB and its successor edges to Sink. + Sink->splice(Sink->begin(), BB, std::next(MachineBasicBlock::iterator(MI)), + BB->end()); + Sink->transferSuccessorsAndUpdatePHIs(BB); + + // Add successors. + BB->addSuccessor(FBB); + BB->addSuccessor(TBB); + FBB->addSuccessor(Sink); + TBB->addSuccessor(Sink); + + // Insert the real bposge32 instruction to $BB. + BuildMI(BB, DL, TII->get(Maxis::BPOSGE32)).addMBB(TBB); + // Insert the real bposge32c instruction to $BB. + BuildMI(BB, DL, TII->get(Maxis::BPOSGE32C_MMR3)).addMBB(TBB); + + // Fill $FBB. + unsigned VR2 = RegInfo.createVirtualRegister(RC); + BuildMI(*FBB, FBB->end(), DL, TII->get(Maxis::ADDiu), VR2) + .addReg(Maxis::ZERO).addImm(0); + BuildMI(*FBB, FBB->end(), DL, TII->get(Maxis::B)).addMBB(Sink); + + // Fill $TBB. + unsigned VR1 = RegInfo.createVirtualRegister(RC); + BuildMI(*TBB, TBB->end(), DL, TII->get(Maxis::ADDiu), VR1) + .addReg(Maxis::ZERO).addImm(1); + + // Insert phi function to $Sink. + BuildMI(*Sink, Sink->begin(), DL, TII->get(Maxis::PHI), + MI.getOperand(0).getReg()) + .addReg(VR2) + .addMBB(FBB) + .addReg(VR1) + .addMBB(TBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return Sink; +} + +MachineBasicBlock *MaxisSETargetLowering::emitMSACBranchPseudo( + MachineInstr &MI, MachineBasicBlock *BB, unsigned BranchOp) const { + // $bb: + // vany_nonzero $rd, $ws + // => + // $bb: + // bnz.b $ws, $tbb + // b $fbb + // $fbb: + // li $rd1, 0 + // b $sink + // $tbb: + // li $rd2, 1 + // $sink: + // $rd = phi($rd1, $fbb, $rd2, $tbb) + + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + const TargetRegisterClass *RC = &Maxis::GPR32RegClass; + DebugLoc DL = MI.getDebugLoc(); + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator It = std::next(MachineFunction::iterator(BB)); + MachineFunction *F = BB->getParent(); + MachineBasicBlock *FBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *TBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *Sink = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(It, FBB); + F->insert(It, TBB); + F->insert(It, Sink); + + // Transfer the remainder of BB and its successor edges to Sink. + Sink->splice(Sink->begin(), BB, std::next(MachineBasicBlock::iterator(MI)), + BB->end()); + Sink->transferSuccessorsAndUpdatePHIs(BB); + + // Add successors. + BB->addSuccessor(FBB); + BB->addSuccessor(TBB); + FBB->addSuccessor(Sink); + TBB->addSuccessor(Sink); + + // Insert the real bnz.b instruction to $BB. + BuildMI(BB, DL, TII->get(BranchOp)) + .addReg(MI.getOperand(1).getReg()) + .addMBB(TBB); + + // Fill $FBB. + unsigned RD1 = RegInfo.createVirtualRegister(RC); + BuildMI(*FBB, FBB->end(), DL, TII->get(Maxis::ADDiu), RD1) + .addReg(Maxis::ZERO).addImm(0); + BuildMI(*FBB, FBB->end(), DL, TII->get(Maxis::B)).addMBB(Sink); + + // Fill $TBB. + unsigned RD2 = RegInfo.createVirtualRegister(RC); + BuildMI(*TBB, TBB->end(), DL, TII->get(Maxis::ADDiu), RD2) + .addReg(Maxis::ZERO).addImm(1); + + // Insert phi function to $Sink. + BuildMI(*Sink, Sink->begin(), DL, TII->get(Maxis::PHI), + MI.getOperand(0).getReg()) + .addReg(RD1) + .addMBB(FBB) + .addReg(RD2) + .addMBB(TBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return Sink; +} + +// Emit the COPY_FW pseudo instruction. +// +// copy_fw_pseudo $fd, $ws, n +// => +// copy_u_w $rt, $ws, $n +// mtc1 $rt, $fd +// +// When n is zero, the equivalent operation can be performed with (potentially) +// zero instructions due to register overlaps. This optimization is never valid +// for lane 1 because it would require FR=0 mode which isn't supported by MSA. +MachineBasicBlock * +MaxisSETargetLowering::emitCOPY_FW(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Fd = MI.getOperand(0).getReg(); + unsigned Ws = MI.getOperand(1).getReg(); + unsigned Lane = MI.getOperand(2).getImm(); + + if (Lane == 0) { + unsigned Wt = Ws; + if (!Subtarget.useOddSPReg()) { + // We must copy to an even-numbered MSA register so that the + // single-precision sub-register is also guaranteed to be even-numbered. + Wt = RegInfo.createVirtualRegister(&Maxis::MSA128WEvensRegClass); + + BuildMI(*BB, MI, DL, TII->get(Maxis::COPY), Wt).addReg(Ws); + } + + BuildMI(*BB, MI, DL, TII->get(Maxis::COPY), Fd).addReg(Wt, 0, Maxis::sub_lo); + } else { + unsigned Wt = RegInfo.createVirtualRegister( + Subtarget.useOddSPReg() ? &Maxis::MSA128WRegClass : + &Maxis::MSA128WEvensRegClass); + + BuildMI(*BB, MI, DL, TII->get(Maxis::SPLATI_W), Wt).addReg(Ws).addImm(Lane); + BuildMI(*BB, MI, DL, TII->get(Maxis::COPY), Fd).addReg(Wt, 0, Maxis::sub_lo); + } + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit the COPY_FD pseudo instruction. +// +// copy_fd_pseudo $fd, $ws, n +// => +// splati.d $wt, $ws, $n +// copy $fd, $wt:sub_64 +// +// When n is zero, the equivalent operation can be performed with (potentially) +// zero instructions due to register overlaps. This optimization is always +// valid because FR=1 mode which is the only supported mode in MSA. +MachineBasicBlock * +MaxisSETargetLowering::emitCOPY_FD(MachineInstr &MI, + MachineBasicBlock *BB) const { + assert(Subtarget.isFP64bit()); + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + unsigned Fd = MI.getOperand(0).getReg(); + unsigned Ws = MI.getOperand(1).getReg(); + unsigned Lane = MI.getOperand(2).getImm() * 2; + DebugLoc DL = MI.getDebugLoc(); + + if (Lane == 0) + BuildMI(*BB, MI, DL, TII->get(Maxis::COPY), Fd).addReg(Ws, 0, Maxis::sub_64); + else { + unsigned Wt = RegInfo.createVirtualRegister(&Maxis::MSA128DRegClass); + + BuildMI(*BB, MI, DL, TII->get(Maxis::SPLATI_D), Wt).addReg(Ws).addImm(1); + BuildMI(*BB, MI, DL, TII->get(Maxis::COPY), Fd).addReg(Wt, 0, Maxis::sub_64); + } + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit the INSERT_FW pseudo instruction. +// +// insert_fw_pseudo $wd, $wd_in, $n, $fs +// => +// subreg_to_reg $wt:sub_lo, $fs +// insve_w $wd[$n], $wd_in, $wt[0] +MachineBasicBlock * +MaxisSETargetLowering::emitINSERT_FW(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Wd = MI.getOperand(0).getReg(); + unsigned Wd_in = MI.getOperand(1).getReg(); + unsigned Lane = MI.getOperand(2).getImm(); + unsigned Fs = MI.getOperand(3).getReg(); + unsigned Wt = RegInfo.createVirtualRegister( + Subtarget.useOddSPReg() ? &Maxis::MSA128WRegClass : + &Maxis::MSA128WEvensRegClass); + + BuildMI(*BB, MI, DL, TII->get(Maxis::SUBREG_TO_REG), Wt) + .addImm(0) + .addReg(Fs) + .addImm(Maxis::sub_lo); + BuildMI(*BB, MI, DL, TII->get(Maxis::INSVE_W), Wd) + .addReg(Wd_in) + .addImm(Lane) + .addReg(Wt) + .addImm(0); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit the INSERT_FD pseudo instruction. +// +// insert_fd_pseudo $wd, $fs, n +// => +// subreg_to_reg $wt:sub_64, $fs +// insve_d $wd[$n], $wd_in, $wt[0] +MachineBasicBlock * +MaxisSETargetLowering::emitINSERT_FD(MachineInstr &MI, + MachineBasicBlock *BB) const { + assert(Subtarget.isFP64bit()); + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Wd = MI.getOperand(0).getReg(); + unsigned Wd_in = MI.getOperand(1).getReg(); + unsigned Lane = MI.getOperand(2).getImm(); + unsigned Fs = MI.getOperand(3).getReg(); + unsigned Wt = RegInfo.createVirtualRegister(&Maxis::MSA128DRegClass); + + BuildMI(*BB, MI, DL, TII->get(Maxis::SUBREG_TO_REG), Wt) + .addImm(0) + .addReg(Fs) + .addImm(Maxis::sub_64); + BuildMI(*BB, MI, DL, TII->get(Maxis::INSVE_D), Wd) + .addReg(Wd_in) + .addImm(Lane) + .addReg(Wt) + .addImm(0); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit the INSERT_([BHWD]|F[WD])_VIDX pseudo instruction. +// +// For integer: +// (INSERT_([BHWD]|F[WD])_PSEUDO $wd, $wd_in, $n, $rs) +// => +// (SLL $lanetmp1, $lane, +// (SUBREG_TO_REG $wt, $fs, ) +// (SLL $lanetmp1, $lane, getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Wd = MI.getOperand(0).getReg(); + unsigned SrcVecReg = MI.getOperand(1).getReg(); + unsigned LaneReg = MI.getOperand(2).getReg(); + unsigned SrcValReg = MI.getOperand(3).getReg(); + + const TargetRegisterClass *VecRC = nullptr; + // FIXME: This should be true for N32 too. + const TargetRegisterClass *GPRRC = + Subtarget.isABI_N64() ? &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + unsigned SubRegIdx = Subtarget.isABI_N64() ? Maxis::sub_32 : 0; + unsigned ShiftOp = Subtarget.isABI_N64() ? Maxis::DSLL : Maxis::SLL; + unsigned EltLog2Size; + unsigned InsertOp = 0; + unsigned InsveOp = 0; + switch (EltSizeInBytes) { + default: + llvm_unreachable("Unexpected size"); + case 1: + EltLog2Size = 0; + InsertOp = Maxis::INSERT_B; + InsveOp = Maxis::INSVE_B; + VecRC = &Maxis::MSA128BRegClass; + break; + case 2: + EltLog2Size = 1; + InsertOp = Maxis::INSERT_H; + InsveOp = Maxis::INSVE_H; + VecRC = &Maxis::MSA128HRegClass; + break; + case 4: + EltLog2Size = 2; + InsertOp = Maxis::INSERT_W; + InsveOp = Maxis::INSVE_W; + VecRC = &Maxis::MSA128WRegClass; + break; + case 8: + EltLog2Size = 3; + InsertOp = Maxis::INSERT_D; + InsveOp = Maxis::INSVE_D; + VecRC = &Maxis::MSA128DRegClass; + break; + } + + if (IsFP) { + unsigned Wt = RegInfo.createVirtualRegister(VecRC); + BuildMI(*BB, MI, DL, TII->get(Maxis::SUBREG_TO_REG), Wt) + .addImm(0) + .addReg(SrcValReg) + .addImm(EltSizeInBytes == 8 ? Maxis::sub_64 : Maxis::sub_lo); + SrcValReg = Wt; + } + + // Convert the lane index into a byte index + if (EltSizeInBytes != 1) { + unsigned LaneTmp1 = RegInfo.createVirtualRegister(GPRRC); + BuildMI(*BB, MI, DL, TII->get(ShiftOp), LaneTmp1) + .addReg(LaneReg) + .addImm(EltLog2Size); + LaneReg = LaneTmp1; + } + + // Rotate bytes around so that the desired lane is element zero + unsigned WdTmp1 = RegInfo.createVirtualRegister(VecRC); + BuildMI(*BB, MI, DL, TII->get(Maxis::SLD_B), WdTmp1) + .addReg(SrcVecReg) + .addReg(SrcVecReg) + .addReg(LaneReg, 0, SubRegIdx); + + unsigned WdTmp2 = RegInfo.createVirtualRegister(VecRC); + if (IsFP) { + // Use insve.df to insert to element zero + BuildMI(*BB, MI, DL, TII->get(InsveOp), WdTmp2) + .addReg(WdTmp1) + .addImm(0) + .addReg(SrcValReg) + .addImm(0); + } else { + // Use insert.df to insert to element zero + BuildMI(*BB, MI, DL, TII->get(InsertOp), WdTmp2) + .addReg(WdTmp1) + .addReg(SrcValReg) + .addImm(0); + } + + // Rotate elements the rest of the way for a full rotation. + // sld.df inteprets $rt modulo the number of columns so we only need to negate + // the lane index to do this. + unsigned LaneTmp2 = RegInfo.createVirtualRegister(GPRRC); + BuildMI(*BB, MI, DL, TII->get(Subtarget.isABI_N64() ? Maxis::DSUB : Maxis::SUB), + LaneTmp2) + .addReg(Subtarget.isABI_N64() ? Maxis::ZERO_64 : Maxis::ZERO) + .addReg(LaneReg); + BuildMI(*BB, MI, DL, TII->get(Maxis::SLD_B), Wd) + .addReg(WdTmp2) + .addReg(WdTmp2) + .addReg(LaneTmp2, 0, SubRegIdx); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit the FILL_FW pseudo instruction. +// +// fill_fw_pseudo $wd, $fs +// => +// implicit_def $wt1 +// insert_subreg $wt2:subreg_lo, $wt1, $fs +// splati.w $wd, $wt2[0] +MachineBasicBlock * +MaxisSETargetLowering::emitFILL_FW(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Wd = MI.getOperand(0).getReg(); + unsigned Fs = MI.getOperand(1).getReg(); + unsigned Wt1 = RegInfo.createVirtualRegister( + Subtarget.useOddSPReg() ? &Maxis::MSA128WRegClass + : &Maxis::MSA128WEvensRegClass); + unsigned Wt2 = RegInfo.createVirtualRegister( + Subtarget.useOddSPReg() ? &Maxis::MSA128WRegClass + : &Maxis::MSA128WEvensRegClass); + + BuildMI(*BB, MI, DL, TII->get(Maxis::IMPLICIT_DEF), Wt1); + BuildMI(*BB, MI, DL, TII->get(Maxis::INSERT_SUBREG), Wt2) + .addReg(Wt1) + .addReg(Fs) + .addImm(Maxis::sub_lo); + BuildMI(*BB, MI, DL, TII->get(Maxis::SPLATI_W), Wd).addReg(Wt2).addImm(0); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit the FILL_FD pseudo instruction. +// +// fill_fd_pseudo $wd, $fs +// => +// implicit_def $wt1 +// insert_subreg $wt2:subreg_64, $wt1, $fs +// splati.d $wd, $wt2[0] +MachineBasicBlock * +MaxisSETargetLowering::emitFILL_FD(MachineInstr &MI, + MachineBasicBlock *BB) const { + assert(Subtarget.isFP64bit()); + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Wd = MI.getOperand(0).getReg(); + unsigned Fs = MI.getOperand(1).getReg(); + unsigned Wt1 = RegInfo.createVirtualRegister(&Maxis::MSA128DRegClass); + unsigned Wt2 = RegInfo.createVirtualRegister(&Maxis::MSA128DRegClass); + + BuildMI(*BB, MI, DL, TII->get(Maxis::IMPLICIT_DEF), Wt1); + BuildMI(*BB, MI, DL, TII->get(Maxis::INSERT_SUBREG), Wt2) + .addReg(Wt1) + .addReg(Fs) + .addImm(Maxis::sub_64); + BuildMI(*BB, MI, DL, TII->get(Maxis::SPLATI_D), Wd).addReg(Wt2).addImm(0); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit the ST_F16_PSEDUO instruction to store a f16 value from an MSA +// register. +// +// STF16 MSA128F16:$wd, mem_simm10:$addr +// => +// copy_u.h $rtemp,$wd[0] +// sh $rtemp, $addr +// +// Safety: We can't use st.h & co as they would over write the memory after +// the destination. It would require half floats be allocated 16 bytes(!) of +// space. +MachineBasicBlock * +MaxisSETargetLowering::emitST_F16_PSEUDO(MachineInstr &MI, + MachineBasicBlock *BB) const { + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Ws = MI.getOperand(0).getReg(); + unsigned Rt = MI.getOperand(1).getReg(); + const MachineMemOperand &MMO = **MI.memoperands_begin(); + unsigned Imm = MMO.getOffset(); + + // Caution: A load via the GOT can expand to a GPR32 operand, a load via + // spill and reload can expand as a GPR64 operand. Examine the + // operand in detail and default to ABI. + const TargetRegisterClass *RC = + MI.getOperand(1).isReg() ? RegInfo.getRegClass(MI.getOperand(1).getReg()) + : (Subtarget.isABI_O32() ? &Maxis::GPR32RegClass + : &Maxis::GPR64RegClass); + const bool UsingMaxis32 = RC == &Maxis::GPR32RegClass; + unsigned Rs = RegInfo.createVirtualRegister(&Maxis::GPR32RegClass); + + BuildMI(*BB, MI, DL, TII->get(Maxis::COPY_U_H), Rs).addReg(Ws).addImm(0); + if(!UsingMaxis32) { + unsigned Tmp = RegInfo.createVirtualRegister(&Maxis::GPR64RegClass); + BuildMI(*BB, MI, DL, TII->get(Maxis::SUBREG_TO_REG), Tmp) + .addImm(0) + .addReg(Rs) + .addImm(Maxis::sub_32); + Rs = Tmp; + } + BuildMI(*BB, MI, DL, TII->get(UsingMaxis32 ? Maxis::SH : Maxis::SH64)) + .addReg(Rs) + .addReg(Rt) + .addImm(Imm) + .addMemOperand(BB->getParent()->getMachineMemOperand( + &MMO, MMO.getOffset(), MMO.getSize())); + + MI.eraseFromParent(); + return BB; +} + +// Emit the LD_F16_PSEDUO instruction to load a f16 value into an MSA register. +// +// LD_F16 MSA128F16:$wd, mem_simm10:$addr +// => +// lh $rtemp, $addr +// fill.h $wd, $rtemp +// +// Safety: We can't use ld.h & co as they over-read from the source. +// Additionally, if the address is not modulo 16, 2 cases can occur: +// a) Segmentation fault as the load instruction reads from a memory page +// memory it's not supposed to. +// b) The load crosses an implementation specific boundary, requiring OS +// intervention. +MachineBasicBlock * +MaxisSETargetLowering::emitLD_F16_PSEUDO(MachineInstr &MI, + MachineBasicBlock *BB) const { + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Wd = MI.getOperand(0).getReg(); + + // Caution: A load via the GOT can expand to a GPR32 operand, a load via + // spill and reload can expand as a GPR64 operand. Examine the + // operand in detail and default to ABI. + const TargetRegisterClass *RC = + MI.getOperand(1).isReg() ? RegInfo.getRegClass(MI.getOperand(1).getReg()) + : (Subtarget.isABI_O32() ? &Maxis::GPR32RegClass + : &Maxis::GPR64RegClass); + + const bool UsingMaxis32 = RC == &Maxis::GPR32RegClass; + unsigned Rt = RegInfo.createVirtualRegister(RC); + + MachineInstrBuilder MIB = + BuildMI(*BB, MI, DL, TII->get(UsingMaxis32 ? Maxis::LH : Maxis::LH64), Rt); + for (unsigned i = 1; i < MI.getNumOperands(); i++) + MIB.add(MI.getOperand(i)); + + if(!UsingMaxis32) { + unsigned Tmp = RegInfo.createVirtualRegister(&Maxis::GPR32RegClass); + BuildMI(*BB, MI, DL, TII->get(Maxis::COPY), Tmp).addReg(Rt, 0, Maxis::sub_32); + Rt = Tmp; + } + + BuildMI(*BB, MI, DL, TII->get(Maxis::FILL_H), Wd).addReg(Rt); + + MI.eraseFromParent(); + return BB; +} + +// Emit the FPROUND_PSEUDO instruction. +// +// Round an FGR64Opnd, FGR32Opnd to an f16. +// +// Safety: Cycle the operand through the GPRs so the result always ends up +// the correct MSA register. +// +// FIXME: This copying is strictly unnecessary. If we could tie FGR32Opnd:$Fs +// / FGR64Opnd:$Fs and MSA128F16:$Wd to the same physical register +// (which they can be, as the MSA registers are defined to alias the +// FPU's 64 bit and 32 bit registers) the result can be accessed using +// the correct register class. That requires operands be tie-able across +// register classes which have a sub/super register class relationship. +// +// For FPG32Opnd: +// +// FPROUND MSA128F16:$wd, FGR32Opnd:$fs +// => +// mfc1 $rtemp, $fs +// fill.w $rtemp, $wtemp +// fexdo.w $wd, $wtemp, $wtemp +// +// For FPG64Opnd on maxis32r2+: +// +// FPROUND MSA128F16:$wd, FGR64Opnd:$fs +// => +// mfc1 $rtemp, $fs +// fill.w $rtemp, $wtemp +// mfhc1 $rtemp2, $fs +// insert.w $wtemp[1], $rtemp2 +// insert.w $wtemp[3], $rtemp2 +// fexdo.w $wtemp2, $wtemp, $wtemp +// fexdo.h $wd, $temp2, $temp2 +// +// For FGR64Opnd on maxis64r2+: +// +// FPROUND MSA128F16:$wd, FGR64Opnd:$fs +// => +// dmfc1 $rtemp, $fs +// fill.d $rtemp, $wtemp +// fexdo.w $wtemp2, $wtemp, $wtemp +// fexdo.h $wd, $wtemp2, $wtemp2 +// +// Safety note: As $wtemp is UNDEF, we may provoke a spurious exception if the +// undef bits are "just right" and the exception enable bits are +// set. By using fill.w to replicate $fs into all elements over +// insert.w for one element, we avoid that potiential case. If +// fexdo.[hw] causes an exception in, the exception is valid and it +// occurs for all elements. +MachineBasicBlock * +MaxisSETargetLowering::emitFPROUND_PSEUDO(MachineInstr &MI, + MachineBasicBlock *BB, + bool IsFGR64) const { + + // Strictly speaking, we need MAXIS32R5 to support MSA. We'll be generous + // here. It's technically doable to support MAXIS32 here, but the ISA forbids + // it. + assert(Subtarget.hasMSA() && Subtarget.hasMaxis32r2()); + + bool IsFGR64onMaxis64 = Subtarget.hasMaxis64() && IsFGR64; + bool IsFGR64onMaxis32 = !Subtarget.hasMaxis64() && IsFGR64; + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Wd = MI.getOperand(0).getReg(); + unsigned Fs = MI.getOperand(1).getReg(); + + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + unsigned Wtemp = RegInfo.createVirtualRegister(&Maxis::MSA128WRegClass); + const TargetRegisterClass *GPRRC = + IsFGR64onMaxis64 ? &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + unsigned MFC1Opc = IsFGR64onMaxis64 + ? Maxis::DMFC1 + : (IsFGR64onMaxis32 ? Maxis::MFC1_D64 : Maxis::MFC1); + unsigned FILLOpc = IsFGR64onMaxis64 ? Maxis::FILL_D : Maxis::FILL_W; + + // Perform the register class copy as mentioned above. + unsigned Rtemp = RegInfo.createVirtualRegister(GPRRC); + BuildMI(*BB, MI, DL, TII->get(MFC1Opc), Rtemp).addReg(Fs); + BuildMI(*BB, MI, DL, TII->get(FILLOpc), Wtemp).addReg(Rtemp); + unsigned WPHI = Wtemp; + + if (IsFGR64onMaxis32) { + unsigned Rtemp2 = RegInfo.createVirtualRegister(GPRRC); + BuildMI(*BB, MI, DL, TII->get(Maxis::MFHC1_D64), Rtemp2).addReg(Fs); + unsigned Wtemp2 = RegInfo.createVirtualRegister(&Maxis::MSA128WRegClass); + unsigned Wtemp3 = RegInfo.createVirtualRegister(&Maxis::MSA128WRegClass); + BuildMI(*BB, MI, DL, TII->get(Maxis::INSERT_W), Wtemp2) + .addReg(Wtemp) + .addReg(Rtemp2) + .addImm(1); + BuildMI(*BB, MI, DL, TII->get(Maxis::INSERT_W), Wtemp3) + .addReg(Wtemp2) + .addReg(Rtemp2) + .addImm(3); + WPHI = Wtemp3; + } + + if (IsFGR64) { + unsigned Wtemp2 = RegInfo.createVirtualRegister(&Maxis::MSA128WRegClass); + BuildMI(*BB, MI, DL, TII->get(Maxis::FEXDO_W), Wtemp2) + .addReg(WPHI) + .addReg(WPHI); + WPHI = Wtemp2; + } + + BuildMI(*BB, MI, DL, TII->get(Maxis::FEXDO_H), Wd).addReg(WPHI).addReg(WPHI); + + MI.eraseFromParent(); + return BB; +} + +// Emit the FPEXTEND_PSEUDO instruction. +// +// Expand an f16 to either a FGR32Opnd or FGR64Opnd. +// +// Safety: Cycle the result through the GPRs so the result always ends up +// the correct floating point register. +// +// FIXME: This copying is strictly unnecessary. If we could tie FGR32Opnd:$Fd +// / FGR64Opnd:$Fd and MSA128F16:$Ws to the same physical register +// (which they can be, as the MSA registers are defined to alias the +// FPU's 64 bit and 32 bit registers) the result can be accessed using +// the correct register class. That requires operands be tie-able across +// register classes which have a sub/super register class relationship. I +// haven't checked. +// +// For FGR32Opnd: +// +// FPEXTEND FGR32Opnd:$fd, MSA128F16:$ws +// => +// fexupr.w $wtemp, $ws +// copy_s.w $rtemp, $ws[0] +// mtc1 $rtemp, $fd +// +// For FGR64Opnd on Maxis64: +// +// FPEXTEND FGR64Opnd:$fd, MSA128F16:$ws +// => +// fexupr.w $wtemp, $ws +// fexupr.d $wtemp2, $wtemp +// copy_s.d $rtemp, $wtemp2s[0] +// dmtc1 $rtemp, $fd +// +// For FGR64Opnd on Maxis32: +// +// FPEXTEND FGR64Opnd:$fd, MSA128F16:$ws +// => +// fexupr.w $wtemp, $ws +// fexupr.d $wtemp2, $wtemp +// copy_s.w $rtemp, $wtemp2[0] +// mtc1 $rtemp, $ftemp +// copy_s.w $rtemp2, $wtemp2[1] +// $fd = mthc1 $rtemp2, $ftemp +MachineBasicBlock * +MaxisSETargetLowering::emitFPEXTEND_PSEUDO(MachineInstr &MI, + MachineBasicBlock *BB, + bool IsFGR64) const { + + // Strictly speaking, we need MAXIS32R5 to support MSA. We'll be generous + // here. It's technically doable to support MAXIS32 here, but the ISA forbids + // it. + assert(Subtarget.hasMSA() && Subtarget.hasMaxis32r2()); + + bool IsFGR64onMaxis64 = Subtarget.hasMaxis64() && IsFGR64; + bool IsFGR64onMaxis32 = !Subtarget.hasMaxis64() && IsFGR64; + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Fd = MI.getOperand(0).getReg(); + unsigned Ws = MI.getOperand(1).getReg(); + + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + const TargetRegisterClass *GPRRC = + IsFGR64onMaxis64 ? &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + unsigned MTC1Opc = IsFGR64onMaxis64 + ? Maxis::DMTC1 + : (IsFGR64onMaxis32 ? Maxis::MTC1_D64 : Maxis::MTC1); + unsigned COPYOpc = IsFGR64onMaxis64 ? Maxis::COPY_S_D : Maxis::COPY_S_W; + + unsigned Wtemp = RegInfo.createVirtualRegister(&Maxis::MSA128WRegClass); + unsigned WPHI = Wtemp; + + BuildMI(*BB, MI, DL, TII->get(Maxis::FEXUPR_W), Wtemp).addReg(Ws); + if (IsFGR64) { + WPHI = RegInfo.createVirtualRegister(&Maxis::MSA128DRegClass); + BuildMI(*BB, MI, DL, TII->get(Maxis::FEXUPR_D), WPHI).addReg(Wtemp); + } + + // Perform the safety regclass copy mentioned above. + unsigned Rtemp = RegInfo.createVirtualRegister(GPRRC); + unsigned FPRPHI = IsFGR64onMaxis32 + ? RegInfo.createVirtualRegister(&Maxis::FGR64RegClass) + : Fd; + BuildMI(*BB, MI, DL, TII->get(COPYOpc), Rtemp).addReg(WPHI).addImm(0); + BuildMI(*BB, MI, DL, TII->get(MTC1Opc), FPRPHI).addReg(Rtemp); + + if (IsFGR64onMaxis32) { + unsigned Rtemp2 = RegInfo.createVirtualRegister(GPRRC); + BuildMI(*BB, MI, DL, TII->get(Maxis::COPY_S_W), Rtemp2) + .addReg(WPHI) + .addImm(1); + BuildMI(*BB, MI, DL, TII->get(Maxis::MTHC1_D64), Fd) + .addReg(FPRPHI) + .addReg(Rtemp2); + } + + MI.eraseFromParent(); + return BB; +} + +// Emit the FEXP2_W_1 pseudo instructions. +// +// fexp2_w_1_pseudo $wd, $wt +// => +// ldi.w $ws, 1 +// fexp2.w $wd, $ws, $wt +MachineBasicBlock * +MaxisSETargetLowering::emitFEXP2_W_1(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + const TargetRegisterClass *RC = &Maxis::MSA128WRegClass; + unsigned Ws1 = RegInfo.createVirtualRegister(RC); + unsigned Ws2 = RegInfo.createVirtualRegister(RC); + DebugLoc DL = MI.getDebugLoc(); + + // Splat 1.0 into a vector + BuildMI(*BB, MI, DL, TII->get(Maxis::LDI_W), Ws1).addImm(1); + BuildMI(*BB, MI, DL, TII->get(Maxis::FFINT_U_W), Ws2).addReg(Ws1); + + // Emit 1.0 * fexp2(Wt) + BuildMI(*BB, MI, DL, TII->get(Maxis::FEXP2_W), MI.getOperand(0).getReg()) + .addReg(Ws2) + .addReg(MI.getOperand(1).getReg()); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} + +// Emit the FEXP2_D_1 pseudo instructions. +// +// fexp2_d_1_pseudo $wd, $wt +// => +// ldi.d $ws, 1 +// fexp2.d $wd, $ws, $wt +MachineBasicBlock * +MaxisSETargetLowering::emitFEXP2_D_1(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + MachineRegisterInfo &RegInfo = BB->getParent()->getRegInfo(); + const TargetRegisterClass *RC = &Maxis::MSA128DRegClass; + unsigned Ws1 = RegInfo.createVirtualRegister(RC); + unsigned Ws2 = RegInfo.createVirtualRegister(RC); + DebugLoc DL = MI.getDebugLoc(); + + // Splat 1.0 into a vector + BuildMI(*BB, MI, DL, TII->get(Maxis::LDI_D), Ws1).addImm(1); + BuildMI(*BB, MI, DL, TII->get(Maxis::FFINT_U_D), Ws2).addReg(Ws1); + + // Emit 1.0 * fexp2(Wt) + BuildMI(*BB, MI, DL, TII->get(Maxis::FEXP2_D), MI.getOperand(0).getReg()) + .addReg(Ws2) + .addReg(MI.getOperand(1).getReg()); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + return BB; +} diff --git a/lib/Target/Maxis/MaxisSEISelLowering.h b/lib/Target/Maxis/MaxisSEISelLowering.h new file mode 100644 index 00000000..75827c09 --- /dev/null +++ b/lib/Target/Maxis/MaxisSEISelLowering.h @@ -0,0 +1,142 @@ +//===- MaxisSEISelLowering.h - MaxisSE DAG Lowering Interface -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Subclass of MaxisTargetLowering specialized for maxis32/64. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISSEISELLOWERING_H +#define LLVM_LIB_TARGET_MAXIS_MAXISSEISELLOWERING_H + +#include "MaxisISelLowering.h" +#include "llvm/CodeGen/MachineValueType.h" +#include "llvm/CodeGen/SelectionDAGNodes.h" + +namespace llvm { + +class MachineBasicBlock; +class MachineInstr; +class MaxisSubtarget; +class MaxisTargetMachine; +class SelectionDAG; +class TargetRegisterClass; + + class MaxisSETargetLowering : public MaxisTargetLowering { + public: + explicit MaxisSETargetLowering(const MaxisTargetMachine &TM, + const MaxisSubtarget &STI); + + /// \brief Enable MSA support for the given integer type and Register + /// class. + void addMSAIntType(MVT::SimpleValueType Ty, const TargetRegisterClass *RC); + + /// \brief Enable MSA support for the given floating-point type and + /// Register class. + void addMSAFloatType(MVT::SimpleValueType Ty, + const TargetRegisterClass *RC); + + bool allowsMisalignedMemoryAccesses(EVT VT, unsigned AS = 0, + unsigned Align = 1, + bool *Fast = nullptr) const override; + + SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; + + SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *MBB) const override; + + bool isShuffleMaskLegal(ArrayRef Mask, EVT VT) const override { + return false; + } + + const TargetRegisterClass *getRepRegClassFor(MVT VT) const override; + + private: + bool isEligibleForTailCallOptimization( + const CCState &CCInfo, unsigned NextStackOffset, + const MaxisFunctionInfo &FI) const override; + + void + getOpndList(SmallVectorImpl &Ops, + std::deque> &RegsToPass, + bool IsPICCall, bool GlobalOrExternal, bool InternalLinkage, + bool IsCallReloc, CallLoweringInfo &CLI, SDValue Callee, + SDValue Chain) const override; + + SDValue lowerLOAD(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerSTORE(SDValue Op, SelectionDAG &DAG) const; + + SDValue lowerMulDiv(SDValue Op, unsigned NewOpc, bool HasLo, bool HasHi, + SelectionDAG &DAG) const; + + SDValue lowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerINTRINSIC_W_CHAIN(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerINTRINSIC_VOID(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerEXTRACT_VECTOR_ELT(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerBUILD_VECTOR(SDValue Op, SelectionDAG &DAG) const; + /// \brief Lower VECTOR_SHUFFLE into one of a number of instructions + /// depending on the indices in the shuffle. + SDValue lowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG) const; + SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const; + + MachineBasicBlock *emitBPOSGE32(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *emitMSACBranchPseudo(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned BranchOp) const; + /// \brief Emit the COPY_FW pseudo instruction + MachineBasicBlock *emitCOPY_FW(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the COPY_FD pseudo instruction + MachineBasicBlock *emitCOPY_FD(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the INSERT_FW pseudo instruction + MachineBasicBlock *emitINSERT_FW(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the INSERT_FD pseudo instruction + MachineBasicBlock *emitINSERT_FD(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the INSERT_([BHWD]|F[WD])_VIDX pseudo instruction + MachineBasicBlock *emitINSERT_DF_VIDX(MachineInstr &MI, + MachineBasicBlock *BB, + unsigned EltSizeInBytes, + bool IsFP) const; + /// \brief Emit the FILL_FW pseudo instruction + MachineBasicBlock *emitFILL_FW(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the FILL_FD pseudo instruction + MachineBasicBlock *emitFILL_FD(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the FEXP2_W_1 pseudo instructions. + MachineBasicBlock *emitFEXP2_W_1(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the FEXP2_D_1 pseudo instructions. + MachineBasicBlock *emitFEXP2_D_1(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the FILL_FW pseudo instruction + MachineBasicBlock *emitLD_F16_PSEUDO(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the FILL_FD pseudo instruction + MachineBasicBlock *emitST_F16_PSEUDO(MachineInstr &MI, + MachineBasicBlock *BB) const; + /// \brief Emit the FEXP2_W_1 pseudo instructions. + MachineBasicBlock *emitFPEXTEND_PSEUDO(MachineInstr &MI, + MachineBasicBlock *BB, + bool IsFGR64) const; + /// \brief Emit the FEXP2_D_1 pseudo instructions. + MachineBasicBlock *emitFPROUND_PSEUDO(MachineInstr &MI, + MachineBasicBlock *BBi, + bool IsFGR64) const; + }; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISSEISELLOWERING_H diff --git a/lib/Target/Maxis/MaxisSEInstrInfo.cpp b/lib/Target/Maxis/MaxisSEInstrInfo.cpp new file mode 100644 index 00000000..a8f8868a --- /dev/null +++ b/lib/Target/Maxis/MaxisSEInstrInfo.cpp @@ -0,0 +1,779 @@ +//===-- MaxisSEInstrInfo.cpp - Maxis32/64 Instruction Information -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis32/64 implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "MaxisSEInstrInfo.h" +#include "InstPrinter/MaxisInstPrinter.h" +#include "MaxisAnalyzeImmediate.h" +#include "MaxisMachineFunction.h" +#include "MaxisTargetMachine.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +MaxisSEInstrInfo::MaxisSEInstrInfo(const MaxisSubtarget &STI) + : MaxisInstrInfo(STI, STI.isPositionIndependent() ? Maxis::B : Maxis::J), + RI() {} + +const MaxisRegisterInfo &MaxisSEInstrInfo::getRegisterInfo() const { + return RI; +} + +/// isLoadFromStackSlot - If the specified machine instruction is a direct +/// load from a stack slot, return the virtual or physical register number of +/// the destination along with the FrameIndex of the loaded stack slot. If +/// not, return 0. This predicate must return 0 if the instruction has +/// any side effects other than loading from the stack slot. +unsigned MaxisSEInstrInfo::isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + unsigned Opc = MI.getOpcode(); + + if ((Opc == Maxis::LW) || (Opc == Maxis::LD) || + (Opc == Maxis::LWC1) || (Opc == Maxis::LDC1) || (Opc == Maxis::LDC164)) { + if ((MI.getOperand(1).isFI()) && // is a stack slot + (MI.getOperand(2).isImm()) && // the imm is zero + (isZeroImm(MI.getOperand(2)))) { + FrameIndex = MI.getOperand(1).getIndex(); + return MI.getOperand(0).getReg(); + } + } + + return 0; +} + +/// isStoreToStackSlot - If the specified machine instruction is a direct +/// store to a stack slot, return the virtual or physical register number of +/// the source reg along with the FrameIndex of the loaded stack slot. If +/// not, return 0. This predicate must return 0 if the instruction has +/// any side effects other than storing to the stack slot. +unsigned MaxisSEInstrInfo::isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + unsigned Opc = MI.getOpcode(); + + if ((Opc == Maxis::SW) || (Opc == Maxis::SD) || + (Opc == Maxis::SWC1) || (Opc == Maxis::SDC1) || (Opc == Maxis::SDC164)) { + if ((MI.getOperand(1).isFI()) && // is a stack slot + (MI.getOperand(2).isImm()) && // the imm is zero + (isZeroImm(MI.getOperand(2)))) { + FrameIndex = MI.getOperand(1).getIndex(); + return MI.getOperand(0).getReg(); + } + } + return 0; +} + +void MaxisSEInstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + const DebugLoc &DL, unsigned DestReg, + unsigned SrcReg, bool KillSrc) const { + unsigned Opc = 0, ZeroReg = 0; + bool isMicroMaxis = Subtarget.inMicroMaxisMode(); + + if (Maxis::GPR32RegClass.contains(DestReg)) { // Copy to CPU Reg. + if (Maxis::GPR32RegClass.contains(SrcReg)) { + if (isMicroMaxis) + Opc = Maxis::MOVE16_MM; + else + Opc = Maxis::OR, ZeroReg = Maxis::ZERO; + } else if (Maxis::CCRRegClass.contains(SrcReg)) + Opc = Maxis::CFC1; + else if (Maxis::FGR32RegClass.contains(SrcReg)) + Opc = Maxis::MFC1; + else if (Maxis::HI32RegClass.contains(SrcReg)) { + Opc = isMicroMaxis ? Maxis::MFHI16_MM : Maxis::MFHI; + SrcReg = 0; + } else if (Maxis::LO32RegClass.contains(SrcReg)) { + Opc = isMicroMaxis ? Maxis::MFLO16_MM : Maxis::MFLO; + SrcReg = 0; + } else if (Maxis::HI32DSPRegClass.contains(SrcReg)) + Opc = Maxis::MFHI_DSP; + else if (Maxis::LO32DSPRegClass.contains(SrcReg)) + Opc = Maxis::MFLO_DSP; + else if (Maxis::DSPCCRegClass.contains(SrcReg)) { + BuildMI(MBB, I, DL, get(Maxis::RDDSP), DestReg).addImm(1 << 4) + .addReg(SrcReg, RegState::Implicit | getKillRegState(KillSrc)); + return; + } + else if (Maxis::MSACtrlRegClass.contains(SrcReg)) + Opc = Maxis::CFCMSA; + } + else if (Maxis::GPR32RegClass.contains(SrcReg)) { // Copy from CPU Reg. + if (Maxis::CCRRegClass.contains(DestReg)) + Opc = Maxis::CTC1; + else if (Maxis::FGR32RegClass.contains(DestReg)) + Opc = Maxis::MTC1; + else if (Maxis::HI32RegClass.contains(DestReg)) + Opc = Maxis::MTHI, DestReg = 0; + else if (Maxis::LO32RegClass.contains(DestReg)) + Opc = Maxis::MTLO, DestReg = 0; + else if (Maxis::HI32DSPRegClass.contains(DestReg)) + Opc = Maxis::MTHI_DSP; + else if (Maxis::LO32DSPRegClass.contains(DestReg)) + Opc = Maxis::MTLO_DSP; + else if (Maxis::DSPCCRegClass.contains(DestReg)) { + BuildMI(MBB, I, DL, get(Maxis::WRDSP)) + .addReg(SrcReg, getKillRegState(KillSrc)).addImm(1 << 4) + .addReg(DestReg, RegState::ImplicitDefine); + return; + } else if (Maxis::MSACtrlRegClass.contains(DestReg)) { + BuildMI(MBB, I, DL, get(Maxis::CTCMSA)) + .addReg(DestReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + return; + } + } + else if (Maxis::FGR32RegClass.contains(DestReg, SrcReg)) + Opc = Maxis::FMOV_S; + else if (Maxis::AFGR64RegClass.contains(DestReg, SrcReg)) + Opc = Maxis::FMOV_D32; + else if (Maxis::FGR64RegClass.contains(DestReg, SrcReg)) + Opc = Maxis::FMOV_D64; + else if (Maxis::GPR64RegClass.contains(DestReg)) { // Copy to CPU64 Reg. + if (Maxis::GPR64RegClass.contains(SrcReg)) + Opc = Maxis::OR64, ZeroReg = Maxis::ZERO_64; + else if (Maxis::HI64RegClass.contains(SrcReg)) + Opc = Maxis::MFHI64, SrcReg = 0; + else if (Maxis::LO64RegClass.contains(SrcReg)) + Opc = Maxis::MFLO64, SrcReg = 0; + else if (Maxis::FGR64RegClass.contains(SrcReg)) + Opc = Maxis::DMFC1; + } + else if (Maxis::GPR64RegClass.contains(SrcReg)) { // Copy from CPU64 Reg. + if (Maxis::HI64RegClass.contains(DestReg)) + Opc = Maxis::MTHI64, DestReg = 0; + else if (Maxis::LO64RegClass.contains(DestReg)) + Opc = Maxis::MTLO64, DestReg = 0; + else if (Maxis::FGR64RegClass.contains(DestReg)) + Opc = Maxis::DMTC1; + } + else if (Maxis::MSA128BRegClass.contains(DestReg)) { // Copy to MSA reg + if (Maxis::MSA128BRegClass.contains(SrcReg)) + Opc = Maxis::MOVE_V; + } + + assert(Opc && "Cannot copy registers"); + + MachineInstrBuilder MIB = BuildMI(MBB, I, DL, get(Opc)); + + if (DestReg) + MIB.addReg(DestReg, RegState::Define); + + if (SrcReg) + MIB.addReg(SrcReg, getKillRegState(KillSrc)); + + if (ZeroReg) + MIB.addReg(ZeroReg); +} + +void MaxisSEInstrInfo:: +storeRegToStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + unsigned SrcReg, bool isKill, int FI, + const TargetRegisterClass *RC, const TargetRegisterInfo *TRI, + int64_t Offset) const { + DebugLoc DL; + MachineMemOperand *MMO = GetMemOperand(MBB, FI, MachineMemOperand::MOStore); + + unsigned Opc = 0; + + if (Maxis::GPR32RegClass.hasSubClassEq(RC)) + Opc = Maxis::SW; + else if (Maxis::GPR64RegClass.hasSubClassEq(RC)) + Opc = Maxis::SD; + else if (Maxis::ACC64RegClass.hasSubClassEq(RC)) + Opc = Maxis::STORE_ACC64; + else if (Maxis::ACC64DSPRegClass.hasSubClassEq(RC)) + Opc = Maxis::STORE_ACC64DSP; + else if (Maxis::ACC128RegClass.hasSubClassEq(RC)) + Opc = Maxis::STORE_ACC128; + else if (Maxis::DSPCCRegClass.hasSubClassEq(RC)) + Opc = Maxis::STORE_CCOND_DSP; + else if (Maxis::FGR32RegClass.hasSubClassEq(RC)) + Opc = Maxis::SWC1; + else if (Maxis::AFGR64RegClass.hasSubClassEq(RC)) + Opc = Maxis::SDC1; + else if (Maxis::FGR64RegClass.hasSubClassEq(RC)) + Opc = Maxis::SDC164; + else if (TRI->isTypeLegalForClass(*RC, MVT::v16i8)) + Opc = Maxis::ST_B; + else if (TRI->isTypeLegalForClass(*RC, MVT::v8i16) || + TRI->isTypeLegalForClass(*RC, MVT::v8f16)) + Opc = Maxis::ST_H; + else if (TRI->isTypeLegalForClass(*RC, MVT::v4i32) || + TRI->isTypeLegalForClass(*RC, MVT::v4f32)) + Opc = Maxis::ST_W; + else if (TRI->isTypeLegalForClass(*RC, MVT::v2i64) || + TRI->isTypeLegalForClass(*RC, MVT::v2f64)) + Opc = Maxis::ST_D; + else if (Maxis::LO32RegClass.hasSubClassEq(RC)) + Opc = Maxis::SW; + else if (Maxis::LO64RegClass.hasSubClassEq(RC)) + Opc = Maxis::SD; + else if (Maxis::HI32RegClass.hasSubClassEq(RC)) + Opc = Maxis::SW; + else if (Maxis::HI64RegClass.hasSubClassEq(RC)) + Opc = Maxis::SD; + else if (Maxis::DSPRRegClass.hasSubClassEq(RC)) + Opc = Maxis::SWDSP; + + // Hi, Lo are normally caller save but they are callee save + // for interrupt handling. + const Function &Func = MBB.getParent()->getFunction(); + if (Func.hasFnAttribute("interrupt")) { + if (Maxis::HI32RegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(Maxis::MFHI), Maxis::K0); + SrcReg = Maxis::K0; + } else if (Maxis::HI64RegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(Maxis::MFHI64), Maxis::K0_64); + SrcReg = Maxis::K0_64; + } else if (Maxis::LO32RegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(Maxis::MFLO), Maxis::K0); + SrcReg = Maxis::K0; + } else if (Maxis::LO64RegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(Maxis::MFLO64), Maxis::K0_64); + SrcReg = Maxis::K0_64; + } + } + + assert(Opc && "Register class not handled!"); + BuildMI(MBB, I, DL, get(Opc)).addReg(SrcReg, getKillRegState(isKill)) + .addFrameIndex(FI).addImm(Offset).addMemOperand(MMO); +} + +void MaxisSEInstrInfo:: +loadRegFromStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + unsigned DestReg, int FI, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, int64_t Offset) const { + DebugLoc DL; + if (I != MBB.end()) DL = I->getDebugLoc(); + MachineMemOperand *MMO = GetMemOperand(MBB, FI, MachineMemOperand::MOLoad); + unsigned Opc = 0; + + const Function &Func = MBB.getParent()->getFunction(); + bool ReqIndirectLoad = Func.hasFnAttribute("interrupt") && + (DestReg == Maxis::LO0 || DestReg == Maxis::LO0_64 || + DestReg == Maxis::HI0 || DestReg == Maxis::HI0_64); + + if (Maxis::GPR32RegClass.hasSubClassEq(RC)) + Opc = Maxis::LW; + else if (Maxis::GPR64RegClass.hasSubClassEq(RC)) + Opc = Maxis::LD; + else if (Maxis::ACC64RegClass.hasSubClassEq(RC)) + Opc = Maxis::LOAD_ACC64; + else if (Maxis::ACC64DSPRegClass.hasSubClassEq(RC)) + Opc = Maxis::LOAD_ACC64DSP; + else if (Maxis::ACC128RegClass.hasSubClassEq(RC)) + Opc = Maxis::LOAD_ACC128; + else if (Maxis::DSPCCRegClass.hasSubClassEq(RC)) + Opc = Maxis::LOAD_CCOND_DSP; + else if (Maxis::FGR32RegClass.hasSubClassEq(RC)) + Opc = Maxis::LWC1; + else if (Maxis::AFGR64RegClass.hasSubClassEq(RC)) + Opc = Maxis::LDC1; + else if (Maxis::FGR64RegClass.hasSubClassEq(RC)) + Opc = Maxis::LDC164; + else if (TRI->isTypeLegalForClass(*RC, MVT::v16i8)) + Opc = Maxis::LD_B; + else if (TRI->isTypeLegalForClass(*RC, MVT::v8i16) || + TRI->isTypeLegalForClass(*RC, MVT::v8f16)) + Opc = Maxis::LD_H; + else if (TRI->isTypeLegalForClass(*RC, MVT::v4i32) || + TRI->isTypeLegalForClass(*RC, MVT::v4f32)) + Opc = Maxis::LD_W; + else if (TRI->isTypeLegalForClass(*RC, MVT::v2i64) || + TRI->isTypeLegalForClass(*RC, MVT::v2f64)) + Opc = Maxis::LD_D; + else if (Maxis::HI32RegClass.hasSubClassEq(RC)) + Opc = Maxis::LW; + else if (Maxis::HI64RegClass.hasSubClassEq(RC)) + Opc = Maxis::LD; + else if (Maxis::LO32RegClass.hasSubClassEq(RC)) + Opc = Maxis::LW; + else if (Maxis::LO64RegClass.hasSubClassEq(RC)) + Opc = Maxis::LD; + else if (Maxis::DSPRRegClass.hasSubClassEq(RC)) + Opc = Maxis::LWDSP; + + assert(Opc && "Register class not handled!"); + + if (!ReqIndirectLoad) + BuildMI(MBB, I, DL, get(Opc), DestReg) + .addFrameIndex(FI) + .addImm(Offset) + .addMemOperand(MMO); + else { + // Load HI/LO through K0. Notably the DestReg is encoded into the + // instruction itself. + unsigned Reg = Maxis::K0; + unsigned LdOp = Maxis::MTLO; + if (DestReg == Maxis::HI0) + LdOp = Maxis::MTHI; + + if (Subtarget.getABI().ArePtrs64bit()) { + Reg = Maxis::K0_64; + if (DestReg == Maxis::HI0_64) + LdOp = Maxis::MTHI64; + else + LdOp = Maxis::MTLO64; + } + + BuildMI(MBB, I, DL, get(Opc), Reg) + .addFrameIndex(FI) + .addImm(Offset) + .addMemOperand(MMO); + BuildMI(MBB, I, DL, get(LdOp)).addReg(Reg); + } +} + +bool MaxisSEInstrInfo::expandPostRAPseudo(MachineInstr &MI) const { + MachineBasicBlock &MBB = *MI.getParent(); + bool isMicroMaxis = Subtarget.inMicroMaxisMode(); + unsigned Opc; + + switch (MI.getDesc().getOpcode()) { + default: + return false; + case Maxis::RetRA: + expandRetRA(MBB, MI); + break; + case Maxis::ERet: + expandERet(MBB, MI); + break; + case Maxis::PseudoMFHI: + Opc = isMicroMaxis ? Maxis::MFHI16_MM : Maxis::MFHI; + expandPseudoMFHiLo(MBB, MI, Opc); + break; + case Maxis::PseudoMFLO: + Opc = isMicroMaxis ? Maxis::MFLO16_MM : Maxis::MFLO; + expandPseudoMFHiLo(MBB, MI, Opc); + break; + case Maxis::PseudoMFHI64: + expandPseudoMFHiLo(MBB, MI, Maxis::MFHI64); + break; + case Maxis::PseudoMFLO64: + expandPseudoMFHiLo(MBB, MI, Maxis::MFLO64); + break; + case Maxis::PseudoMTLOHI: + expandPseudoMTLoHi(MBB, MI, Maxis::MTLO, Maxis::MTHI, false); + break; + case Maxis::PseudoMTLOHI64: + expandPseudoMTLoHi(MBB, MI, Maxis::MTLO64, Maxis::MTHI64, false); + break; + case Maxis::PseudoMTLOHI_DSP: + expandPseudoMTLoHi(MBB, MI, Maxis::MTLO_DSP, Maxis::MTHI_DSP, true); + break; + case Maxis::PseudoCVT_S_W: + expandCvtFPInt(MBB, MI, Maxis::CVT_S_W, Maxis::MTC1, false); + break; + case Maxis::PseudoCVT_D32_W: + expandCvtFPInt(MBB, MI, Maxis::CVT_D32_W, Maxis::MTC1, false); + break; + case Maxis::PseudoCVT_S_L: + expandCvtFPInt(MBB, MI, Maxis::CVT_S_L, Maxis::DMTC1, true); + break; + case Maxis::PseudoCVT_D64_W: + expandCvtFPInt(MBB, MI, Maxis::CVT_D64_W, Maxis::MTC1, true); + break; + case Maxis::PseudoCVT_D64_L: + expandCvtFPInt(MBB, MI, Maxis::CVT_D64_L, Maxis::DMTC1, true); + break; + case Maxis::BuildPairF64: + expandBuildPairF64(MBB, MI, false); + break; + case Maxis::BuildPairF64_64: + expandBuildPairF64(MBB, MI, true); + break; + case Maxis::ExtractElementF64: + expandExtractElementF64(MBB, MI, false); + break; + case Maxis::ExtractElementF64_64: + expandExtractElementF64(MBB, MI, true); + break; + case Maxis::MAXISeh_return32: + case Maxis::MAXISeh_return64: + expandEhReturn(MBB, MI); + break; + } + + MBB.erase(MI); + return true; +} + +/// getOppositeBranchOpc - Return the inverse of the specified +/// opcode, e.g. turning BEQ to BNE. +unsigned MaxisSEInstrInfo::getOppositeBranchOpc(unsigned Opc) const { + switch (Opc) { + default: llvm_unreachable("Illegal opcode!"); + case Maxis::BEQ: return Maxis::BNE; + case Maxis::BEQ_MM: return Maxis::BNE_MM; + case Maxis::BNE: return Maxis::BEQ; + case Maxis::BNE_MM: return Maxis::BEQ_MM; + case Maxis::BGTZ: return Maxis::BLEZ; + case Maxis::BGEZ: return Maxis::BLTZ; + case Maxis::BLTZ: return Maxis::BGEZ; + case Maxis::BLEZ: return Maxis::BGTZ; + case Maxis::BEQ64: return Maxis::BNE64; + case Maxis::BNE64: return Maxis::BEQ64; + case Maxis::BGTZ64: return Maxis::BLEZ64; + case Maxis::BGEZ64: return Maxis::BLTZ64; + case Maxis::BLTZ64: return Maxis::BGEZ64; + case Maxis::BLEZ64: return Maxis::BGTZ64; + case Maxis::BC1T: return Maxis::BC1F; + case Maxis::BC1F: return Maxis::BC1T; + case Maxis::BEQZC_MM: return Maxis::BNEZC_MM; + case Maxis::BNEZC_MM: return Maxis::BEQZC_MM; + case Maxis::BEQZC: return Maxis::BNEZC; + case Maxis::BNEZC: return Maxis::BEQZC; + case Maxis::BEQC: return Maxis::BNEC; + case Maxis::BNEC: return Maxis::BEQC; + case Maxis::BGTZC: return Maxis::BLEZC; + case Maxis::BGEZC: return Maxis::BLTZC; + case Maxis::BLTZC: return Maxis::BGEZC; + case Maxis::BLEZC: return Maxis::BGTZC; + case Maxis::BEQZC64: return Maxis::BNEZC64; + case Maxis::BNEZC64: return Maxis::BEQZC64; + case Maxis::BEQC64: return Maxis::BNEC64; + case Maxis::BNEC64: return Maxis::BEQC64; + case Maxis::BGEC64: return Maxis::BLTC64; + case Maxis::BGEUC64: return Maxis::BLTUC64; + case Maxis::BLTC64: return Maxis::BGEC64; + case Maxis::BLTUC64: return Maxis::BGEUC64; + case Maxis::BGTZC64: return Maxis::BLEZC64; + case Maxis::BGEZC64: return Maxis::BLTZC64; + case Maxis::BLTZC64: return Maxis::BGEZC64; + case Maxis::BLEZC64: return Maxis::BGTZC64; + case Maxis::BBIT0: return Maxis::BBIT1; + case Maxis::BBIT1: return Maxis::BBIT0; + case Maxis::BBIT032: return Maxis::BBIT132; + case Maxis::BBIT132: return Maxis::BBIT032; + } +} + +/// Adjust SP by Amount bytes. +void MaxisSEInstrInfo::adjustStackPtr(unsigned SP, int64_t Amount, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + MaxisABIInfo ABI = Subtarget.getABI(); + DebugLoc DL; + unsigned ADDiu = ABI.GetPtrAddiuOp(); + + if (Amount == 0) + return; + + if (isInt<16>(Amount)) { + // addi sp, sp, amount + BuildMI(MBB, I, DL, get(ADDiu), SP).addReg(SP).addImm(Amount); + } else { + // For numbers which are not 16bit integers we synthesize Amount inline + // then add or subtract it from sp. + unsigned Opc = ABI.GetPtrAdduOp(); + if (Amount < 0) { + Opc = ABI.GetPtrSubuOp(); + Amount = -Amount; + } + unsigned Reg = loadImmediate(Amount, MBB, I, DL, nullptr); + BuildMI(MBB, I, DL, get(Opc), SP).addReg(SP).addReg(Reg, RegState::Kill); + } +} + +/// This function generates the sequence of instructions needed to get the +/// result of adding register REG and immediate IMM. +unsigned MaxisSEInstrInfo::loadImmediate(int64_t Imm, MachineBasicBlock &MBB, + MachineBasicBlock::iterator II, + const DebugLoc &DL, + unsigned *NewImm) const { + MaxisAnalyzeImmediate AnalyzeImm; + const MaxisSubtarget &STI = Subtarget; + MachineRegisterInfo &RegInfo = MBB.getParent()->getRegInfo(); + unsigned Size = STI.isABI_N64() ? 64 : 32; + unsigned LUi = STI.isABI_N64() ? Maxis::LUi64 : Maxis::LUi; + unsigned ZEROReg = STI.isABI_N64() ? Maxis::ZERO_64 : Maxis::ZERO; + const TargetRegisterClass *RC = STI.isABI_N64() ? + &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + bool LastInstrIsADDiu = NewImm; + + const MaxisAnalyzeImmediate::InstSeq &Seq = + AnalyzeImm.Analyze(Imm, Size, LastInstrIsADDiu); + MaxisAnalyzeImmediate::InstSeq::const_iterator Inst = Seq.begin(); + + assert(Seq.size() && (!LastInstrIsADDiu || (Seq.size() > 1))); + + // The first instruction can be a LUi, which is different from other + // instructions (ADDiu, ORI and SLL) in that it does not have a register + // operand. + unsigned Reg = RegInfo.createVirtualRegister(RC); + + if (Inst->Opc == LUi) + BuildMI(MBB, II, DL, get(LUi), Reg).addImm(SignExtend64<16>(Inst->ImmOpnd)); + else + BuildMI(MBB, II, DL, get(Inst->Opc), Reg).addReg(ZEROReg) + .addImm(SignExtend64<16>(Inst->ImmOpnd)); + + // Build the remaining instructions in Seq. + for (++Inst; Inst != Seq.end() - LastInstrIsADDiu; ++Inst) + BuildMI(MBB, II, DL, get(Inst->Opc), Reg).addReg(Reg, RegState::Kill) + .addImm(SignExtend64<16>(Inst->ImmOpnd)); + + if (LastInstrIsADDiu) + *NewImm = Inst->ImmOpnd; + + return Reg; +} + +unsigned MaxisSEInstrInfo::getAnalyzableBrOpc(unsigned Opc) const { + return (Opc == Maxis::BEQ || Opc == Maxis::BEQ_MM || Opc == Maxis::BNE || + Opc == Maxis::BNE_MM || Opc == Maxis::BGTZ || Opc == Maxis::BGEZ || + Opc == Maxis::BLTZ || Opc == Maxis::BLEZ || Opc == Maxis::BEQ64 || + Opc == Maxis::BNE64 || Opc == Maxis::BGTZ64 || Opc == Maxis::BGEZ64 || + Opc == Maxis::BLTZ64 || Opc == Maxis::BLEZ64 || Opc == Maxis::BC1T || + Opc == Maxis::BC1F || Opc == Maxis::B || Opc == Maxis::J || + Opc == Maxis::B_MM || Opc == Maxis::BEQZC_MM || + Opc == Maxis::BNEZC_MM || Opc == Maxis::BEQC || Opc == Maxis::BNEC || + Opc == Maxis::BLTC || Opc == Maxis::BGEC || Opc == Maxis::BLTUC || + Opc == Maxis::BGEUC || Opc == Maxis::BGTZC || Opc == Maxis::BLEZC || + Opc == Maxis::BGEZC || Opc == Maxis::BLTZC || Opc == Maxis::BEQZC || + Opc == Maxis::BNEZC || Opc == Maxis::BEQZC64 || Opc == Maxis::BNEZC64 || + Opc == Maxis::BEQC64 || Opc == Maxis::BNEC64 || Opc == Maxis::BGEC64 || + Opc == Maxis::BGEUC64 || Opc == Maxis::BLTC64 || Opc == Maxis::BLTUC64 || + Opc == Maxis::BGTZC64 || Opc == Maxis::BGEZC64 || + Opc == Maxis::BLTZC64 || Opc == Maxis::BLEZC64 || Opc == Maxis::BC || + Opc == Maxis::BBIT0 || Opc == Maxis::BBIT1 || Opc == Maxis::BBIT032 || + Opc == Maxis::BBIT132) ? Opc : 0; +} + +void MaxisSEInstrInfo::expandRetRA(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + + MachineInstrBuilder MIB; + if (Subtarget.isGP64bit()) + MIB = BuildMI(MBB, I, I->getDebugLoc(), get(Maxis::PseudoReturn64)) + .addReg(Maxis::RA_64, RegState::Undef); + else + MIB = BuildMI(MBB, I, I->getDebugLoc(), get(Maxis::PseudoReturn)) + .addReg(Maxis::RA, RegState::Undef); + + // Retain any imp-use flags. + for (auto & MO : I->operands()) { + if (MO.isImplicit()) + MIB.add(MO); + } +} + +void MaxisSEInstrInfo::expandERet(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + BuildMI(MBB, I, I->getDebugLoc(), get(Maxis::ERET)); +} + +std::pair +MaxisSEInstrInfo::compareOpndSize(unsigned Opc, + const MachineFunction &MF) const { + const MCInstrDesc &Desc = get(Opc); + assert(Desc.NumOperands == 2 && "Unary instruction expected."); + const MaxisRegisterInfo *RI = &getRegisterInfo(); + unsigned DstRegSize = RI->getRegSizeInBits(*getRegClass(Desc, 0, RI, MF)); + unsigned SrcRegSize = RI->getRegSizeInBits(*getRegClass(Desc, 1, RI, MF)); + + return std::make_pair(DstRegSize > SrcRegSize, DstRegSize < SrcRegSize); +} + +void MaxisSEInstrInfo::expandPseudoMFHiLo(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned NewOpc) const { + BuildMI(MBB, I, I->getDebugLoc(), get(NewOpc), I->getOperand(0).getReg()); +} + +void MaxisSEInstrInfo::expandPseudoMTLoHi(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned LoOpc, + unsigned HiOpc, + bool HasExplicitDef) const { + // Expand + // lo_hi pseudomtlohi $gpr0, $gpr1 + // to these two instructions: + // mtlo $gpr0 + // mthi $gpr1 + + DebugLoc DL = I->getDebugLoc(); + const MachineOperand &SrcLo = I->getOperand(1), &SrcHi = I->getOperand(2); + MachineInstrBuilder LoInst = BuildMI(MBB, I, DL, get(LoOpc)); + MachineInstrBuilder HiInst = BuildMI(MBB, I, DL, get(HiOpc)); + + // Add lo/hi registers if the mtlo/hi instructions created have explicit + // def registers. + if (HasExplicitDef) { + unsigned DstReg = I->getOperand(0).getReg(); + unsigned DstLo = getRegisterInfo().getSubReg(DstReg, Maxis::sub_lo); + unsigned DstHi = getRegisterInfo().getSubReg(DstReg, Maxis::sub_hi); + LoInst.addReg(DstLo, RegState::Define); + HiInst.addReg(DstHi, RegState::Define); + } + + LoInst.addReg(SrcLo.getReg(), getKillRegState(SrcLo.isKill())); + HiInst.addReg(SrcHi.getReg(), getKillRegState(SrcHi.isKill())); +} + +void MaxisSEInstrInfo::expandCvtFPInt(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + unsigned CvtOpc, unsigned MovOpc, + bool IsI64) const { + const MCInstrDesc &CvtDesc = get(CvtOpc), &MovDesc = get(MovOpc); + const MachineOperand &Dst = I->getOperand(0), &Src = I->getOperand(1); + unsigned DstReg = Dst.getReg(), SrcReg = Src.getReg(), TmpReg = DstReg; + unsigned KillSrc = getKillRegState(Src.isKill()); + DebugLoc DL = I->getDebugLoc(); + bool DstIsLarger, SrcIsLarger; + + std::tie(DstIsLarger, SrcIsLarger) = + compareOpndSize(CvtOpc, *MBB.getParent()); + + if (DstIsLarger) + TmpReg = getRegisterInfo().getSubReg(DstReg, Maxis::sub_lo); + + if (SrcIsLarger) + DstReg = getRegisterInfo().getSubReg(DstReg, Maxis::sub_lo); + + BuildMI(MBB, I, DL, MovDesc, TmpReg).addReg(SrcReg, KillSrc); + BuildMI(MBB, I, DL, CvtDesc, DstReg).addReg(TmpReg, RegState::Kill); +} + +void MaxisSEInstrInfo::expandExtractElementF64(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + bool FP64) const { + unsigned DstReg = I->getOperand(0).getReg(); + unsigned SrcReg = I->getOperand(1).getReg(); + unsigned N = I->getOperand(2).getImm(); + DebugLoc dl = I->getDebugLoc(); + + assert(N < 2 && "Invalid immediate"); + unsigned SubIdx = N ? Maxis::sub_hi : Maxis::sub_lo; + unsigned SubReg = getRegisterInfo().getSubReg(SrcReg, SubIdx); + + // FPXX on MAXIS-II or MAXIS32r1 should have been handled with a spill/reload + // in MaxisSEFrameLowering.cpp. + assert(!(Subtarget.isABI_FPXX() && !Subtarget.hasMaxis32r2())); + + // FP64A (FP64 with nooddspreg) should have been handled with a spill/reload + // in MaxisSEFrameLowering.cpp. + assert(!(Subtarget.isFP64bit() && !Subtarget.useOddSPReg())); + + if (SubIdx == Maxis::sub_hi && Subtarget.hasMTHC1()) { + // FIXME: Strictly speaking MFHC1 only reads the top 32-bits however, we + // claim to read the whole 64-bits as part of a white lie used to + // temporarily work around a widespread bug in the -mfp64 support. + // The problem is that none of the 32-bit fpu ops mention the fact + // that they clobber the upper 32-bits of the 64-bit FPR. Fixing that + // requires a major overhaul of the FPU implementation which can't + // be done right now due to time constraints. + // MFHC1 is one of two instructions that are affected since they are + // the only instructions that don't read the lower 32-bits. + // We therefore pretend that it reads the bottom 32-bits to + // artificially create a dependency and prevent the scheduler + // changing the behaviour of the code. + BuildMI(MBB, I, dl, get(FP64 ? Maxis::MFHC1_D64 : Maxis::MFHC1_D32), DstReg) + .addReg(SrcReg); + } else + BuildMI(MBB, I, dl, get(Maxis::MFC1), DstReg).addReg(SubReg); +} + +void MaxisSEInstrInfo::expandBuildPairF64(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, + bool FP64) const { + unsigned DstReg = I->getOperand(0).getReg(); + unsigned LoReg = I->getOperand(1).getReg(), HiReg = I->getOperand(2).getReg(); + const MCInstrDesc& Mtc1Tdd = get(Maxis::MTC1); + DebugLoc dl = I->getDebugLoc(); + const TargetRegisterInfo &TRI = getRegisterInfo(); + + // When mthc1 is available, use: + // mtc1 Lo, $fp + // mthc1 Hi, $fp + // + // Otherwise, for O32 FPXX ABI: + // spill + reload via ldc1 + // This case is handled by the frame lowering code. + // + // Otherwise, for FP32: + // mtc1 Lo, $fp + // mtc1 Hi, $fp + 1 + // + // The case where dmtc1 is available doesn't need to be handled here + // because it never creates a BuildPairF64 node. + + // FPXX on MAXIS-II or MAXIS32r1 should have been handled with a spill/reload + // in MaxisSEFrameLowering.cpp. + assert(!(Subtarget.isABI_FPXX() && !Subtarget.hasMaxis32r2())); + + // FP64A (FP64 with nooddspreg) should have been handled with a spill/reload + // in MaxisSEFrameLowering.cpp. + assert(!(Subtarget.isFP64bit() && !Subtarget.useOddSPReg())); + + BuildMI(MBB, I, dl, Mtc1Tdd, TRI.getSubReg(DstReg, Maxis::sub_lo)) + .addReg(LoReg); + + if (Subtarget.hasMTHC1()) { + // FIXME: The .addReg(DstReg) is a white lie used to temporarily work + // around a widespread bug in the -mfp64 support. + // The problem is that none of the 32-bit fpu ops mention the fact + // that they clobber the upper 32-bits of the 64-bit FPR. Fixing that + // requires a major overhaul of the FPU implementation which can't + // be done right now due to time constraints. + // MTHC1 is one of two instructions that are affected since they are + // the only instructions that don't read the lower 32-bits. + // We therefore pretend that it reads the bottom 32-bits to + // artificially create a dependency and prevent the scheduler + // changing the behaviour of the code. + BuildMI(MBB, I, dl, get(FP64 ? Maxis::MTHC1_D64 : Maxis::MTHC1_D32), DstReg) + .addReg(DstReg) + .addReg(HiReg); + } else if (Subtarget.isABI_FPXX()) + llvm_unreachable("BuildPairF64 not expanded in frame lowering code!"); + else + BuildMI(MBB, I, dl, Mtc1Tdd, TRI.getSubReg(DstReg, Maxis::sub_hi)) + .addReg(HiReg); +} + +void MaxisSEInstrInfo::expandEhReturn(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + // This pseudo instruction is generated as part of the lowering of + // ISD::EH_RETURN. We convert it to a stack increment by OffsetReg, and + // indirect jump to TargetReg + MaxisABIInfo ABI = Subtarget.getABI(); + unsigned ADDU = ABI.GetPtrAdduOp(); + unsigned SP = Subtarget.isGP64bit() ? Maxis::SP_64 : Maxis::SP; + unsigned RA = Subtarget.isGP64bit() ? Maxis::RA_64 : Maxis::RA; + unsigned T9 = Subtarget.isGP64bit() ? Maxis::T9_64 : Maxis::T9; + unsigned ZERO = Subtarget.isGP64bit() ? Maxis::ZERO_64 : Maxis::ZERO; + unsigned OffsetReg = I->getOperand(0).getReg(); + unsigned TargetReg = I->getOperand(1).getReg(); + + // addu $ra, $v0, $zero + // addu $sp, $sp, $v1 + // jr $ra (via RetRA) + const TargetMachine &TM = MBB.getParent()->getTarget(); + if (TM.isPositionIndependent()) + BuildMI(MBB, I, I->getDebugLoc(), get(ADDU), T9) + .addReg(TargetReg) + .addReg(ZERO); + BuildMI(MBB, I, I->getDebugLoc(), get(ADDU), RA) + .addReg(TargetReg) + .addReg(ZERO); + BuildMI(MBB, I, I->getDebugLoc(), get(ADDU), SP).addReg(SP).addReg(OffsetReg); + expandRetRA(MBB, I); +} + +const MaxisInstrInfo *llvm::createMaxisSEInstrInfo(const MaxisSubtarget &STI) { + return new MaxisSEInstrInfo(STI); +} diff --git a/lib/Target/Maxis/MaxisSEInstrInfo.h b/lib/Target/Maxis/MaxisSEInstrInfo.h new file mode 100644 index 00000000..78c32e69 --- /dev/null +++ b/lib/Target/Maxis/MaxisSEInstrInfo.h @@ -0,0 +1,119 @@ +//===-- MaxisSEInstrInfo.h - Maxis32/64 Instruction Information ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis32/64 implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISSEINSTRINFO_H +#define LLVM_LIB_TARGET_MAXIS_MAXISSEINSTRINFO_H + +#include "MaxisInstrInfo.h" +#include "MaxisSERegisterInfo.h" + +namespace llvm { + +class MaxisSEInstrInfo : public MaxisInstrInfo { + const MaxisSERegisterInfo RI; + +public: + explicit MaxisSEInstrInfo(const MaxisSubtarget &STI); + + const MaxisRegisterInfo &getRegisterInfo() const override; + + /// isLoadFromStackSlot - If the specified machine instruction is a direct + /// load from a stack slot, return the virtual or physical register number of + /// the destination along with the FrameIndex of the loaded stack slot. If + /// not, return 0. This predicate must return 0 if the instruction has + /// any side effects other than loading from the stack slot. + unsigned isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + + /// isStoreToStackSlot - If the specified machine instruction is a direct + /// store to a stack slot, return the virtual or physical register number of + /// the source reg along with the FrameIndex of the loaded stack slot. If + /// not, return 0. This predicate must return 0 if the instruction has + /// any side effects other than storing to the stack slot. + unsigned isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const DebugLoc &DL, unsigned DestReg, unsigned SrcReg, + bool KillSrc) const override; + + void storeRegToStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + unsigned SrcReg, bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + int64_t Offset) const override; + + void loadRegFromStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + unsigned DestReg, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI, + int64_t Offset) const override; + + bool expandPostRAPseudo(MachineInstr &MI) const override; + + unsigned getOppositeBranchOpc(unsigned Opc) const override; + + /// Adjust SP by Amount bytes. + void adjustStackPtr(unsigned SP, int64_t Amount, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const override; + + /// Emit a series of instructions to load an immediate. If NewImm is a + /// non-NULL parameter, the last instruction is not emitted, but instead + /// its immediate operand is returned in NewImm. + unsigned loadImmediate(int64_t Imm, MachineBasicBlock &MBB, + MachineBasicBlock::iterator II, const DebugLoc &DL, + unsigned *NewImm) const; + +private: + unsigned getAnalyzableBrOpc(unsigned Opc) const override; + + void expandRetRA(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const; + + void expandERet(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const; + + std::pair compareOpndSize(unsigned Opc, + const MachineFunction &MF) const; + + void expandPseudoMFHiLo(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + unsigned NewOpc) const; + + void expandPseudoMTLoHi(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + unsigned LoOpc, unsigned HiOpc, + bool HasExplicitDef) const; + + /// Expand pseudo Int-to-FP conversion instructions. + /// + /// For example, the following pseudo instruction + /// PseudoCVT_D32_W D2, A5 + /// gets expanded into these two instructions: + /// MTC1 F4, A5 + /// CVT_D32_W D2, F4 + /// + /// We do this expansion post-RA to avoid inserting a floating point copy + /// instruction between MTC1 and CVT_D32_W. + void expandCvtFPInt(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + unsigned CvtOpc, unsigned MovOpc, bool IsI64) const; + + void expandExtractElementF64(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, bool FP64) const; + void expandBuildPairF64(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, bool FP64) const; + void expandEhReturn(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const; +}; + +} + +#endif diff --git a/lib/Target/Maxis/MaxisSERegisterInfo.cpp b/lib/Target/Maxis/MaxisSERegisterInfo.cpp new file mode 100644 index 00000000..9544564f --- /dev/null +++ b/lib/Target/Maxis/MaxisSERegisterInfo.cpp @@ -0,0 +1,260 @@ +//===-- MaxisSERegisterInfo.cpp - MAXIS32/64 Register Information -== -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the MAXIS32/64 implementation of the TargetRegisterInfo +// class. +// +//===----------------------------------------------------------------------===// + +#include "MaxisSERegisterInfo.h" +#include "Maxis.h" +#include "MaxisMachineFunction.h" +#include "MaxisSEInstrInfo.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfo.h" +#include "llvm/IR/Function.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" +#include "llvm/Target/TargetOptions.h" + +using namespace llvm; + +#define DEBUG_TYPE "maxis-reg-info" + +MaxisSERegisterInfo::MaxisSERegisterInfo() : MaxisRegisterInfo() {} + +bool MaxisSERegisterInfo:: +requiresRegisterScavenging(const MachineFunction &MF) const { + return true; +} + +bool MaxisSERegisterInfo:: +requiresFrameIndexScavenging(const MachineFunction &MF) const { + return true; +} + +const TargetRegisterClass * +MaxisSERegisterInfo::intRegClass(unsigned Size) const { + if (Size == 4) + return &Maxis::GPR32RegClass; + + assert(Size == 8); + return &Maxis::GPR64RegClass; +} + +/// Get the size of the offset supported by the given load/store/inline asm. +/// The result includes the effects of any scale factors applied to the +/// instruction immediate. +static inline unsigned getLoadStoreOffsetSizeInBits(const unsigned Opcode, + MachineOperand MO) { + switch (Opcode) { + case Maxis::LD_B: + case Maxis::ST_B: + return 10; + case Maxis::LD_H: + case Maxis::ST_H: + return 10 + 1 /* scale factor */; + case Maxis::LD_W: + case Maxis::ST_W: + return 10 + 2 /* scale factor */; + case Maxis::LD_D: + case Maxis::ST_D: + return 10 + 3 /* scale factor */; + case Maxis::LL: + case Maxis::LL64: + case Maxis::LLD: + case Maxis::LLE: + case Maxis::SC: + case Maxis::SC64: + case Maxis::SCD: + case Maxis::SCE: + return 16; + case Maxis::LLE_MM: + case Maxis::LLE_MMR6: + case Maxis::LL_MM: + case Maxis::SCE_MM: + case Maxis::SCE_MMR6: + case Maxis::SC_MM: + return 12; + case Maxis::LL64_R6: + case Maxis::LL_R6: + case Maxis::LLD_R6: + case Maxis::SC64_R6: + case Maxis::SCD_R6: + case Maxis::SC_R6: + return 9; + case Maxis::INLINEASM: { + unsigned ConstraintID = InlineAsm::getMemoryConstraintID(MO.getImm()); + switch (ConstraintID) { + case InlineAsm::Constraint_ZC: { + const MaxisSubtarget &Subtarget = MO.getParent() + ->getParent() + ->getParent() + ->getSubtarget(); + if (Subtarget.inMicroMaxisMode()) + return 12; + + if (Subtarget.hasMaxis32r6()) + return 9; + + return 16; + } + default: + return 16; + } + } + default: + return 16; + } +} + +/// Get the scale factor applied to the immediate in the given load/store. +static inline unsigned getLoadStoreOffsetAlign(const unsigned Opcode) { + switch (Opcode) { + case Maxis::LD_H: + case Maxis::ST_H: + return 2; + case Maxis::LD_W: + case Maxis::ST_W: + return 4; + case Maxis::LD_D: + case Maxis::ST_D: + return 8; + default: + return 1; + } +} + +void MaxisSERegisterInfo::eliminateFI(MachineBasicBlock::iterator II, + unsigned OpNo, int FrameIndex, + uint64_t StackSize, + int64_t SPOffset) const { + MachineInstr &MI = *II; + MachineFunction &MF = *MI.getParent()->getParent(); + MachineFrameInfo &MFI = MF.getFrameInfo(); + MaxisFunctionInfo *MaxisFI = MF.getInfo(); + + MaxisABIInfo ABI = + static_cast(MF.getTarget()).getABI(); + const MaxisRegisterInfo *RegInfo = + static_cast(MF.getSubtarget().getRegisterInfo()); + + const std::vector &CSI = MFI.getCalleeSavedInfo(); + int MinCSFI = 0; + int MaxCSFI = -1; + + if (CSI.size()) { + MinCSFI = CSI[0].getFrameIdx(); + MaxCSFI = CSI[CSI.size() - 1].getFrameIdx(); + } + + bool EhDataRegFI = MaxisFI->isEhDataRegFI(FrameIndex); + bool IsISRRegFI = MaxisFI->isISRRegFI(FrameIndex); + // The following stack frame objects are always referenced relative to $sp: + // 1. Outgoing arguments. + // 2. Pointer to dynamically allocated stack space. + // 3. Locations for callee-saved registers. + // 4. Locations for eh data registers. + // 5. Locations for ISR saved Coprocessor 0 registers 12 & 14. + // Everything else is referenced relative to whatever register + // getFrameRegister() returns. + unsigned FrameReg; + + if ((FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI) || EhDataRegFI || + IsISRRegFI) + FrameReg = ABI.GetStackPtr(); + else if (RegInfo->needsStackRealignment(MF)) { + if (MFI.hasVarSizedObjects() && !MFI.isFixedObjectIndex(FrameIndex)) + FrameReg = ABI.GetBasePtr(); + else if (MFI.isFixedObjectIndex(FrameIndex)) + FrameReg = getFrameRegister(MF); + else + FrameReg = ABI.GetStackPtr(); + } else + FrameReg = getFrameRegister(MF); + + // Calculate final offset. + // - There is no need to change the offset if the frame object is one of the + // following: an outgoing argument, pointer to a dynamically allocated + // stack space or a $gp restore location, + // - If the frame object is any of the following, its offset must be adjusted + // by adding the size of the stack: + // incoming argument, callee-saved register location or local variable. + bool IsKill = false; + int64_t Offset; + + Offset = SPOffset + (int64_t)StackSize; + Offset += MI.getOperand(OpNo + 1).getImm(); + + DEBUG(errs() << "Offset : " << Offset << "\n" << "<--------->\n"); + + if (!MI.isDebugValue()) { + // Make sure Offset fits within the field available. + // For MSA instructions, this is a 10-bit signed immediate (scaled by + // element size), otherwise it is a 16-bit signed immediate. + unsigned OffsetBitSize = + getLoadStoreOffsetSizeInBits(MI.getOpcode(), MI.getOperand(OpNo - 1)); + unsigned OffsetAlign = getLoadStoreOffsetAlign(MI.getOpcode()); + + if (OffsetBitSize < 16 && isInt<16>(Offset) && + (!isIntN(OffsetBitSize, Offset) || + OffsetToAlignment(Offset, OffsetAlign) != 0)) { + // If we have an offset that needs to fit into a signed n-bit immediate + // (where n < 16) and doesn't, but does fit into 16-bits then use an ADDiu + MachineBasicBlock &MBB = *MI.getParent(); + DebugLoc DL = II->getDebugLoc(); + const TargetRegisterClass *PtrRC = + ABI.ArePtrs64bit() ? &Maxis::GPR64RegClass : &Maxis::GPR32RegClass; + MachineRegisterInfo &RegInfo = MBB.getParent()->getRegInfo(); + unsigned Reg = RegInfo.createVirtualRegister(PtrRC); + const MaxisSEInstrInfo &TII = + *static_cast( + MBB.getParent()->getSubtarget().getInstrInfo()); + BuildMI(MBB, II, DL, TII.get(ABI.GetPtrAddiuOp()), Reg) + .addReg(FrameReg) + .addImm(Offset); + + FrameReg = Reg; + Offset = 0; + IsKill = true; + } else if (!isInt<16>(Offset)) { + // Otherwise split the offset into 16-bit pieces and add it in multiple + // instructions. + MachineBasicBlock &MBB = *MI.getParent(); + DebugLoc DL = II->getDebugLoc(); + unsigned NewImm = 0; + const MaxisSEInstrInfo &TII = + *static_cast( + MBB.getParent()->getSubtarget().getInstrInfo()); + unsigned Reg = TII.loadImmediate(Offset, MBB, II, DL, + OffsetBitSize == 16 ? &NewImm : nullptr); + BuildMI(MBB, II, DL, TII.get(ABI.GetPtrAdduOp()), Reg).addReg(FrameReg) + .addReg(Reg, RegState::Kill); + + FrameReg = Reg; + Offset = SignExtend64<16>(NewImm); + IsKill = true; + } + } + + MI.getOperand(OpNo).ChangeToRegister(FrameReg, false, false, IsKill); + MI.getOperand(OpNo + 1).ChangeToImmediate(Offset); +} diff --git a/lib/Target/Maxis/MaxisSERegisterInfo.h b/lib/Target/Maxis/MaxisSERegisterInfo.h new file mode 100644 index 00000000..0f87faf4 --- /dev/null +++ b/lib/Target/Maxis/MaxisSERegisterInfo.h @@ -0,0 +1,41 @@ +//===-- MaxisSERegisterInfo.h - Maxis32/64 Register Information ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Maxis32/64 implementation of the TargetRegisterInfo +// class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISSEREGISTERINFO_H +#define LLVM_LIB_TARGET_MAXIS_MAXISSEREGISTERINFO_H + +#include "MaxisRegisterInfo.h" + +namespace llvm { +class MaxisSEInstrInfo; + +class MaxisSERegisterInfo : public MaxisRegisterInfo { +public: + MaxisSERegisterInfo(); + + bool requiresRegisterScavenging(const MachineFunction &MF) const override; + + bool requiresFrameIndexScavenging(const MachineFunction &MF) const override; + + const TargetRegisterClass *intRegClass(unsigned Size) const override; + +private: + void eliminateFI(MachineBasicBlock::iterator II, unsigned OpNo, + int FrameIndex, uint64_t StackSize, + int64_t SPOffset) const override; +}; + +} // end namespace llvm + +#endif diff --git a/lib/Target/Maxis/MaxisSchedule.td b/lib/Target/Maxis/MaxisSchedule.td new file mode 100644 index 00000000..d7af8e8b --- /dev/null +++ b/lib/Target/Maxis/MaxisSchedule.td @@ -0,0 +1,690 @@ +//===-- MaxisSchedule.td - Maxis Scheduling Definitions ------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Functional units across Maxis chips sets. Based on GCC/Maxis backend files. +//===----------------------------------------------------------------------===// +def ALU : FuncUnit; +def IMULDIV : FuncUnit; + +//===----------------------------------------------------------------------===// +// Instruction Itinerary classes used for Maxis +//===----------------------------------------------------------------------===// +// IIM16Alu is a placeholder class for most MAXIS16 instructions. +def IIM16Alu : InstrItinClass; +def IIPseudo : InstrItinClass; + +def II_ABS : InstrItinClass; +def II_ADDI : InstrItinClass; +def II_ADDIU : InstrItinClass; +def II_ADDIUPC : InstrItinClass; +def II_ADD : InstrItinClass; +def II_ADDU : InstrItinClass; +def II_ADD_D : InstrItinClass; +def II_ADD_S : InstrItinClass; +def II_ALIGN : InstrItinClass; +def II_AND : InstrItinClass; +def II_ANDI : InstrItinClass; +def II_ALUIPC : InstrItinClass; +def II_AUI : InstrItinClass; +def II_AUIPC : InstrItinClass; +def II_B : InstrItinClass; +def II_BADDU : InstrItinClass; +def II_BBIT : InstrItinClass; // bbit[01], bbit[01]32 +def II_BALC : InstrItinClass; +def II_BC : InstrItinClass; +def II_BC1F : InstrItinClass; +def II_BC1FL : InstrItinClass; +def II_BC1T : InstrItinClass; +def II_BC1TL : InstrItinClass; +def II_BC1CCZ : InstrItinClass; +def II_BC2CCZ : InstrItinClass; +def II_BCC : InstrItinClass; // beq and bne +def II_BCCZ : InstrItinClass; // b[gl][et]z +def II_BCCC : InstrItinClass; // bc +def II_BCCZAL : InstrItinClass; // bgezal and bltzal +def II_BCCZALS : InstrItinClass; // bgezals and bltzals +def II_BCCZC : InstrItinClass; // beqzc, bnezc +def II_BITSWAP : InstrItinClass; +def II_CEIL : InstrItinClass; +def II_CFC1 : InstrItinClass; +def II_CFC2 : InstrItinClass; +def II_CLO : InstrItinClass; +def II_CLZ : InstrItinClass; +def II_CTC1 : InstrItinClass; +def II_CTC2 : InstrItinClass; +def II_CVT : InstrItinClass; +def II_C_CC_D : InstrItinClass; // Any c..d instruction +def II_C_CC_S : InstrItinClass; // Any c..s instruction +def II_CMP_CC_D : InstrItinClass; // Any cmp..d instruction +def II_CMP_CC_S : InstrItinClass; // Any cmp..s instruction +def II_CLASS_D : InstrItinClass; +def II_CLASS_S : InstrItinClass; +def II_DADDIU : InstrItinClass; +def II_DADDU : InstrItinClass; +def II_DADDI : InstrItinClass; +def II_DADD : InstrItinClass; +def II_DAHI : InstrItinClass; +def II_DATI : InstrItinClass; +def II_DAUI : InstrItinClass; +def II_DALIGN : InstrItinClass; +def II_DBITSWAP : InstrItinClass; +def II_DCLO : InstrItinClass; +def II_DCLZ : InstrItinClass; +def II_DDIV : InstrItinClass; +def II_DDIVU : InstrItinClass; +def II_DIV : InstrItinClass; +def II_DIVU : InstrItinClass; +def II_DIV_D : InstrItinClass; +def II_DIV_S : InstrItinClass; +def II_DMFC0 : InstrItinClass; +def II_DMT : InstrItinClass; +def II_DMTC0 : InstrItinClass; +def II_DMFC1 : InstrItinClass; +def II_DMTC1 : InstrItinClass; +def II_DMOD : InstrItinClass; +def II_DMODU : InstrItinClass; +def II_DMUH : InstrItinClass; +def II_DMUHU : InstrItinClass; +def II_DMFC2 : InstrItinClass; +def II_DMTC2 : InstrItinClass; +def II_DMUL : InstrItinClass; +def II_DMULU : InstrItinClass; +def II_DMULT : InstrItinClass; +def II_DMULTU : InstrItinClass; +def II_DROTR : InstrItinClass; +def II_DROTR32 : InstrItinClass; +def II_DROTRV : InstrItinClass; +def II_DSLL : InstrItinClass; +def II_DSLL32 : InstrItinClass; +def II_DSLLV : InstrItinClass; +def II_DSRA : InstrItinClass; +def II_DSRA32 : InstrItinClass; +def II_DSRAV : InstrItinClass; +def II_DSRL : InstrItinClass; +def II_DSRL32 : InstrItinClass; +def II_DSRLV : InstrItinClass; +def II_DSBH : InstrItinClass; +def II_DSHD : InstrItinClass; +def II_DSUBU : InstrItinClass; +def II_DSUB : InstrItinClass; +def II_DVPE : InstrItinClass; +def II_EMT : InstrItinClass; +def II_EVPE : InstrItinClass; +def II_EXT : InstrItinClass; // Any EXT instruction +def II_FLOOR : InstrItinClass; +def II_FORK : InstrItinClass; +def II_INS : InstrItinClass; // Any INS instruction +def II_IndirectBranchPseudo : InstrItinClass; // Indirect branch pseudo. +def II_J : InstrItinClass; +def II_JAL : InstrItinClass; +def II_JALR : InstrItinClass; +def II_JALR_HB : InstrItinClass; +def II_JALRC : InstrItinClass; +def II_JALRS : InstrItinClass; +def II_JALS : InstrItinClass; +def II_JIC : InstrItinClass; +def II_JIALC : InstrItinClass; +def II_JR : InstrItinClass; +def II_JR_HB : InstrItinClass; +def II_JRADDIUSP : InstrItinClass; +def II_JRC : InstrItinClass; +def II_ReturnPseudo : InstrItinClass; // Return pseudo. +def II_ERET : InstrItinClass; +def II_DERET : InstrItinClass; +def II_ERETNC : InstrItinClass; +def II_EHB : InstrItinClass; +def II_SDBBP : InstrItinClass; +def II_SSNOP : InstrItinClass; +def II_SYSCALL : InstrItinClass; +def II_PAUSE : InstrItinClass; +def II_WAIT : InstrItinClass; +def II_EI : InstrItinClass; +def II_DI : InstrItinClass; +def II_TEQ : InstrItinClass; +def II_TEQI : InstrItinClass; +def II_TGE : InstrItinClass; +def II_TGEI : InstrItinClass; +def II_TGEIU : InstrItinClass; +def II_TGEU : InstrItinClass; +def II_TNE : InstrItinClass; +def II_TNEI : InstrItinClass; +def II_TLT : InstrItinClass; +def II_TLTI : InstrItinClass; +def II_TLTU : InstrItinClass; +def II_TTLTIU : InstrItinClass; +def II_TLBP : InstrItinClass; +def II_TLBR : InstrItinClass; +def II_TLBWI : InstrItinClass; +def II_TLBWR : InstrItinClass; +def II_TRAP : InstrItinClass; +def II_BREAK : InstrItinClass; +def II_SYNC : InstrItinClass; +def II_SYNCI : InstrItinClass; +def II_LB : InstrItinClass; +def II_LBE : InstrItinClass; +def II_LBU : InstrItinClass; +def II_LBUE : InstrItinClass; +def II_LD : InstrItinClass; +def II_LDC1 : InstrItinClass; +def II_LDC2 : InstrItinClass; +def II_LDC3 : InstrItinClass; +def II_LDL : InstrItinClass; +def II_LDR : InstrItinClass; +def II_LDPC : InstrItinClass; +def II_LDXC1 : InstrItinClass; +def II_LH : InstrItinClass; +def II_LHE : InstrItinClass; +def II_LHU : InstrItinClass; +def II_LHUE : InstrItinClass; +def II_LL : InstrItinClass; +def II_LI : InstrItinClass; +def II_LLD : InstrItinClass; +def II_LUI : InstrItinClass; +def II_LUXC1 : InstrItinClass; +def II_LW : InstrItinClass; +def II_LWE : InstrItinClass; +def II_LWC1 : InstrItinClass; +def II_LWC2 : InstrItinClass; +def II_LWC3 : InstrItinClass; +def II_LWM : InstrItinClass; +def II_LWL : InstrItinClass; +def II_LWLE : InstrItinClass; +def II_LWPC : InstrItinClass; +def II_LWP : InstrItinClass; +def II_LWR : InstrItinClass; +def II_LWRE : InstrItinClass; +def II_LWU : InstrItinClass; +def II_LWUPC : InstrItinClass; +def II_LWXC1 : InstrItinClass; +def II_LWXS : InstrItinClass; +def II_LSA : InstrItinClass; +def II_DLSA : InstrItinClass; +def II_MADD : InstrItinClass; +def II_MADDU : InstrItinClass; +def II_MADD_D : InstrItinClass; +def II_MADD_S : InstrItinClass; +def II_MADDF_D : InstrItinClass; +def II_MADDF_S : InstrItinClass; +def II_MAX_D : InstrItinClass; +def II_MAX_S : InstrItinClass; +def II_MAXA_D : InstrItinClass; +def II_MAXA_S : InstrItinClass; +def II_MIN_D : InstrItinClass; +def II_MIN_S : InstrItinClass; +def II_MINA_D : InstrItinClass; +def II_MINA_S : InstrItinClass; +def II_MFC0 : InstrItinClass; +def II_MFHC0 : InstrItinClass; +def II_MFC1 : InstrItinClass; +def II_MFHC1 : InstrItinClass; +def II_MFC2 : InstrItinClass; +def II_MFHI_MFLO : InstrItinClass; // mfhi and mflo +def II_MFTR : InstrItinClass; +def II_MOD : InstrItinClass; +def II_MODU : InstrItinClass; +def II_MOVE : InstrItinClass; +def II_MOVF : InstrItinClass; +def II_MOVF_D : InstrItinClass; +def II_MOVF_S : InstrItinClass; +def II_MOVN : InstrItinClass; +def II_MOVN_D : InstrItinClass; +def II_MOVN_S : InstrItinClass; +def II_MOVT : InstrItinClass; +def II_MOVT_D : InstrItinClass; +def II_MOVT_S : InstrItinClass; +def II_MOVZ : InstrItinClass; +def II_MOVZ_D : InstrItinClass; +def II_MOVZ_S : InstrItinClass; +def II_MOV_D : InstrItinClass; +def II_MOV_S : InstrItinClass; +def II_MSUB : InstrItinClass; +def II_MSUBU : InstrItinClass; +def II_MSUB_D : InstrItinClass; +def II_MSUB_S : InstrItinClass; +def II_MSUBF_D : InstrItinClass; +def II_MSUBF_S : InstrItinClass; +def II_MTC0 : InstrItinClass; +def II_MTHC0 : InstrItinClass; +def II_MTC1 : InstrItinClass; +def II_MTHC1 : InstrItinClass; +def II_MTC2 : InstrItinClass; +def II_MTHI_MTLO : InstrItinClass; // mthi and mtlo +def II_MTTR : InstrItinClass; +def II_MUL : InstrItinClass; +def II_MUH : InstrItinClass; +def II_MUHU : InstrItinClass; +def II_MULU : InstrItinClass; +def II_MULT : InstrItinClass; +def II_MULTU : InstrItinClass; +def II_MUL_D : InstrItinClass; +def II_MUL_S : InstrItinClass; +def II_NEG : InstrItinClass; +def II_NMADD_D : InstrItinClass; +def II_NMADD_S : InstrItinClass; +def II_NMSUB_D : InstrItinClass; +def II_NMSUB_S : InstrItinClass; +def II_NOR : InstrItinClass; +def II_NOT : InstrItinClass; +def II_OR : InstrItinClass; +def II_ORI : InstrItinClass; +def II_POP : InstrItinClass; +def II_RDHWR : InstrItinClass; +def II_RESTORE : InstrItinClass; +def II_RECIP_S : InstrItinClass; +def II_RECIP_D : InstrItinClass; +def II_RINT_S : InstrItinClass; +def II_RINT_D : InstrItinClass; +def II_ROTR : InstrItinClass; +def II_ROTRV : InstrItinClass; +def II_ROUND : InstrItinClass; +def II_RSQRT_S : InstrItinClass; +def II_RSQRT_D : InstrItinClass; +def II_SAVE : InstrItinClass; +def II_SC : InstrItinClass; +def II_SCD : InstrItinClass; +def II_SB : InstrItinClass; +def II_SBE : InstrItinClass; +def II_SD : InstrItinClass; +def II_SDC1 : InstrItinClass; +def II_SDC2 : InstrItinClass; +def II_SDC3 : InstrItinClass; +def II_SDL : InstrItinClass; +def II_SDR : InstrItinClass; +def II_SDXC1 : InstrItinClass; +def II_SEB : InstrItinClass; +def II_SEH : InstrItinClass; +def II_SELCCZ : InstrItinClass; +def II_SELCCZ_D : InstrItinClass; +def II_SELCCZ_S : InstrItinClass; +def II_SEQ_SNE : InstrItinClass; // seq and sne +def II_SEQI_SNEI : InstrItinClass; // seqi and snei +def II_SH : InstrItinClass; +def II_SHE : InstrItinClass; +def II_SLL : InstrItinClass; +def II_SLLV : InstrItinClass; +def II_SLTI_SLTIU : InstrItinClass; // slti and sltiu +def II_SLT_SLTU : InstrItinClass; // slt and sltu +def II_SQRT_D : InstrItinClass; +def II_SQRT_S : InstrItinClass; +def II_SEL_D : InstrItinClass; +def II_SEL_S : InstrItinClass; +def II_SRA : InstrItinClass; +def II_SRAV : InstrItinClass; +def II_SRL : InstrItinClass; +def II_SRLV : InstrItinClass; +def II_SUB : InstrItinClass; +def II_SUBU : InstrItinClass; +def II_SUB_D : InstrItinClass; +def II_SUB_S : InstrItinClass; +def II_SUXC1 : InstrItinClass; +def II_SW : InstrItinClass; +def II_SWE : InstrItinClass; +def II_SWC1 : InstrItinClass; +def II_SWC2 : InstrItinClass; +def II_SWC3 : InstrItinClass; +def II_SWL : InstrItinClass; +def II_SWLE : InstrItinClass; +def II_SWM : InstrItinClass; +def II_SWP : InstrItinClass; +def II_SWR : InstrItinClass; +def II_SWRE : InstrItinClass; +def II_SWXC1 : InstrItinClass; +def II_TRUNC : InstrItinClass; +def II_WSBH : InstrItinClass; +def II_XOR : InstrItinClass; +def II_XORI : InstrItinClass; +def II_CACHE : InstrItinClass; +def II_PREF : InstrItinClass; +def II_CACHEE : InstrItinClass; +def II_PREFE : InstrItinClass; +def II_LLE : InstrItinClass; +def II_SCE : InstrItinClass; +def II_TLBINV : InstrItinClass; +def II_TLBINVF : InstrItinClass; +def II_WRPGPR : InstrItinClass; +def II_RDPGPR : InstrItinClass; +def II_DVP : InstrItinClass; +def II_EVP : InstrItinClass; +def II_YIELD : InstrItinClass; + +//===----------------------------------------------------------------------===// +// Maxis Generic instruction itineraries. +//===----------------------------------------------------------------------===// +def MaxisGenericItineraries : ProcessorItineraries<[ALU, IMULDIV], [], [ + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]>, + InstrItinData]> +]>; diff --git a/lib/Target/Maxis/MaxisScheduleGeneric.td b/lib/Target/Maxis/MaxisScheduleGeneric.td new file mode 100644 index 00000000..07ded343 --- /dev/null +++ b/lib/Target/Maxis/MaxisScheduleGeneric.td @@ -0,0 +1,1063 @@ +//=- MaxisScheduleGeneric.td - Generic Scheduling Definitions -*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the interAptiv processor in a manner of speaking. It +// describes a hypothetical version of the in-order MAXIS32R2 interAptiv with all +// branches of the MAXIS ISAs, ASEs and ISA variants. The itinerary lists are +// broken down into per ISA lists, so that this file can be used to rapidly +// develop new schedule models. +// +//===----------------------------------------------------------------------===// +def MaxisGenericModel : SchedMachineModel { + int IssueWidth = 1; + int MicroOpBufferSize = 0; + + // These figures assume an L1 hit. + int LoadLatency = 2; + int MispredictPenalty = 4; + + int HighLatency = 37; + list UnsupportedFeatures = []; + + let CompleteModel = 1; + let PostRAScheduler = 1; +} + +let SchedModel = MaxisGenericModel in { + +// ALU Pipeline +// ============ + +def GenericALU : ProcResource<1> { let BufferSize = 1; } +def GenericIssueALU : ProcResource<1> { let Super = GenericALU; } + +def GenericWriteALU : SchedWriteRes<[GenericIssueALU]>; + +// and, lui, nor, or, slti, sltiu, sub, subu, xor +// add, addi, addiu, addu, andi, ori, rotr, se[bh], sllv?, sr[al]v?, slt, sltu, +// xori +def : ItinRW<[GenericWriteALU], [II_ADD, II_ADDU, II_ADDI, II_ADDIU, II_ANDI, + II_AND, II_ANDI, II_CLO, II_CLZ, II_EXT, + II_INS, II_LUI, II_MULT, II_MULTU, II_NOR, + II_ORI, II_OR, II_ROTR, II_ROTRV, II_SEB, + II_SEH, II_SLTI_SLTIU, II_SLT_SLTU, II_SLL, + II_SRA, II_SRL, II_SLLV, II_SRAV, II_SRLV, + II_SSNOP, II_SUB, II_SUBU, II_WSBH, II_XOR, + II_XORI]>; + +def : InstRW<[GenericWriteALU], (instrs COPY)>; + +def GenericMDU : ProcResource<1> { let BufferSize = 1; } +def GenericIssueMDU : ProcResource<1> { let Super = GenericALU; } +def GenericIssueDIV : ProcResource<1> { let Super = GenericMDU; } +def GenericWriteHILO : SchedWriteRes<[GenericIssueMDU]>; +def GenericWriteALULong : SchedWriteRes<[GenericIssueALU]> { let Latency = 5; } +def GenericWriteMove : SchedWriteRes<[GenericIssueALU]> { let Latency = 2; } + +def : ItinRW<[GenericWriteHILO], [II_MADD, II_MADDU, II_MSUB, II_MSUBU]>; + +def GenericWriteMDUtoGPR : SchedWriteRes<[GenericIssueMDU]> { + let Latency = 5; +} + +def : ItinRW<[GenericWriteMDUtoGPR], [II_MUL]>; + +def GenericWriteDIV : SchedWriteRes<[GenericIssueDIV]> { + // Estimated worst case + let Latency = 33; + let ResourceCycles = [1, 33]; +} +def GenericWriteDIVU : SchedWriteRes<[GenericIssueDIV]> { + // Estimated worst case + let Latency = 31; + let ResourceCycles = [1, 31]; +} + +def : ItinRW<[GenericWriteDIV], [II_DIV]>; + +def : ItinRW<[GenericWriteDIVU], [II_DIVU]>; + +// MAXIS64 +// ====== + +def : ItinRW<[GenericWriteALU], [II_DADDIU, II_DADDU, II_DADDI, II_DADD, + II_DCLO, II_DCLZ, II_DROTR, II_DROTR32, + II_DROTRV, II_DSBH, II_DSHD, II_DSLL, + II_DSLL32, II_DSLLV, II_DSRA, II_DSRA32, + II_DSRAV, II_DSRL, II_DSRL32, II_DSRLV, + II_DSUBU, II_DSUB]>; + +def : ItinRW<[GenericWriteDIV], [II_DDIV]>; + +def : ItinRW<[GenericWriteDIVU], [II_DDIVU]>; + +def : ItinRW<[GenericWriteMDUtoGPR], [II_DMUL]>; + +def : ItinRW<[GenericWriteHILO], [II_DMULU, II_DMULT, II_DMULTU]>; + +// MAXIS16e +// ======= + +def : ItinRW<[GenericWriteALU], [IIM16Alu, IIPseudo]>; + +// microMAXIS +// ========= + +def : ItinRW<[GenericWriteALU], [II_MOVE, II_LI, II_NOT]>; + +// MAXISR6 +// ====== + +def GenericWriteMul : SchedWriteRes<[GenericIssueMDU]> { let Latency = 4; } +def : ItinRW<[GenericWriteMul], [II_MUH, II_MUHU, II_MULU]>; + +def : ItinRW<[GenericWriteDIV], [II_MOD, II_MODU]>; + +def : ItinRW<[GenericWriteALU], [II_ADDIUPC, II_ALIGN, II_ALUIPC, II_AUI, + II_AUIPC, II_BITSWAP, II_LSA, II_SELCCZ]>; + +// MAXIS64R6 +// ======== + +def : ItinRW<[GenericWriteALU], [II_DALIGN, II_DAHI, II_DATI, II_DAUI, + II_DBITSWAP, II_DLSA]>; + +def : ItinRW<[GenericWriteMDUtoGPR], [II_DMUH, II_DMUHU]>; +def : ItinRW<[GenericWriteDIV], [II_DMOD, II_DMODU]>; + +// clo, clz, di, mfhi, mflo +def : ItinRW<[GenericWriteALULong], [II_MFHI_MFLO]>; +def : ItinRW<[GenericWriteALU], [II_MOVN, II_MOVZ]>; +def : ItinRW<[GenericWriteMove], [II_MTHI_MTLO, II_RDHWR]>; + + +// CTISTD Pipeline +// --------------- + +def GenericIssueCTISTD : ProcResource<1> { let Super = GenericALU; } + +def GenericLDST : ProcResource<1> { let BufferSize = 1; } +def GenericIssueLDST : ProcResource<1> { let Super = GenericLDST; } + +def GenericWriteJump : SchedWriteRes<[GenericIssueCTISTD]>; +def GenericWriteJumpAndLink : SchedWriteRes<[GenericIssueCTISTD]> { + let Latency = 2; +} + +// b, beq, beql, bg[et]z, bl[et]z, bne, bnel, j, syscall, jal, bltzal, jalx, +// jalr, jr.hb, jr, jalr.hb, jarlc, jialc +def : ItinRW<[GenericWriteJump], [II_B, II_BCC, II_BCCZ, II_BCCZAL, II_J, + II_JR, II_JR_HB, II_ERET, II_ERETNC, + II_DERET]>; + +def : ItinRW<[GenericWriteJumpAndLink], [II_JAL, II_JALR, II_JALR_HB, + II_BC2CCZ]>; + +def : ItinRW<[GenericWriteJump], [II_JRC, II_JRADDIUSP]>; + +def : ItinRW<[GenericWriteJumpAndLink], [II_BCCZALS, II_JALS, II_JALRS]>; + +// MAXISR6 +// ====== + +def : ItinRW<[GenericWriteJumpAndLink], [II_BALC, II_JALRC, II_JIALC]>; + +def : ItinRW<[GenericWriteJump], [II_JIC, II_BC, II_BCCC, II_BCCZC]>; + + +def GenericWriteTrap : SchedWriteRes<[GenericIssueCTISTD]>; + +def : ItinRW<[GenericWriteTrap], [II_BREAK, II_SYSCALL, II_TEQ, II_TEQI, + II_TGE, II_TGEI, II_TGEIU, II_TGEU, II_TNE, + II_TNEI, II_TLT, II_TLTI, II_TLTU, II_TTLTIU, + II_TRAP, II_SDBBP]>; + +// COP0 Pipeline +// ============= + +def GenericCOP0 : ProcResource<1> { let BufferSize = 1; } + +def GenericIssueCOP0 : ProcResource<1> { let Super = GenericCOP0; } +def GenericWriteCOP0TLB : SchedWriteRes<[GenericIssueCOP0]> { let Latency = 4; } +def GenericWriteCOP0 : SchedWriteRes<[GenericIssueCOP0]> { let Latency = 3; } +def GenericReadCOP0 : SchedWriteRes<[GenericIssueCOP0]> { let Latency = 2; } +def GenericReadWritePGPR : SchedWriteRes<[GenericIssueCOP0]>; +def GenericReadWriteCOP0Long : SchedWriteRes<[GenericIssueCOP0]> { + let Latency = 5; +} +def GenericWriteCOP0Short : SchedWriteRes<[GenericIssueCOP0]>; + +def : ItinRW<[GenericWriteCOP0TLB], [II_TLBP, II_TLBR, II_TLBWI, II_TLBWR]>; +def : ItinRW<[GenericWriteCOP0TLB], [II_TLBINV, II_TLBINVF]>; + +def : ItinRW<[GenericReadCOP0], [II_MFC0]>; +def : ItinRW<[GenericWriteCOP0], [II_MTC0]>; + +def : ItinRW<[GenericWriteCOP0], [II_EVP, II_DVP]>; + +// MAXISR5 +// ====== +def : ItinRW<[GenericReadCOP0], [II_MFHC0]>; +def : ItinRW<[GenericWriteCOP0], [II_MTHC0]>; + +// MAXIS64 +// ====== + +def : ItinRW<[GenericReadCOP0], [II_DMFC0]>; +def : ItinRW<[GenericWriteCOP0], [II_DMTC0]>; + +def : ItinRW<[GenericWriteCOP0], [II_RDPGPR, II_WRPGPR]>; + +def : ItinRW<[GenericWriteCOP0], [II_DI, II_EI]>; + +def : ItinRW<[GenericWriteCOP0], [II_EHB, II_PAUSE, II_WAIT]>; + +def GenericCOP2 : ProcResource<1> { let BufferSize = 1; } +def GenericWriteCOPOther : SchedWriteRes<[GenericCOP2]>; + +def : ItinRW<[GenericWriteCOPOther], [II_MFC2, II_MTC2, II_DMFC2, II_DMTC2]>; + +// LDST Pipeline +// ------------- + +def GenericWriteLoad : SchedWriteRes<[GenericIssueLDST]> { + let Latency = 2; +} + +def GenericWritePref : SchedWriteRes<[GenericIssueLDST]>; +def GenericWriteSync : SchedWriteRes<[GenericIssueLDST]>; +def GenericWriteCache : SchedWriteRes<[GenericIssueLDST]> { let Latency = 5; } + +def GenericWriteStore : SchedWriteRes<[GenericIssueLDST]>; +def GenericWriteStoreSC : SchedWriteRes<[GenericIssueLDST]> { let Latency = 2; } + +def GenericWriteGPRFromBypass : SchedWriteRes<[GenericIssueLDST]> { + let Latency = 2; +} + +def GenericWriteStoreFromOtherUnits : SchedWriteRes<[GenericIssueLDST]>; +def GenericWriteLoadToOtherUnits : SchedWriteRes<[GenericIssueLDST]> { + let Latency = 0; +} + +// l[bhw], l[bh]u, ll +def : ItinRW<[GenericWriteLoad], [II_LB, II_LBU, II_LH, II_LHU, II_LW, II_LL, + II_LWC2, II_LWC3, II_LDC2, II_LDC3]>; + +// lw[lr] +def : ItinRW<[GenericWriteLoad], [II_LWL, II_LWR]>; + +// MAXIS64 loads +def : ItinRW<[GenericWriteLoad], [II_LD, II_LLD, II_LWU]>; + +// ld[lr] +def : ItinRW<[GenericWriteLoad], [II_LDL, II_LDR]>; + +// MAXIS32 EVA +def : ItinRW<[GenericWriteLoad], [II_LBE, II_LBUE, II_LHE, II_LHUE, II_LWE, + II_LLE]>; + +def : ItinRW<[GenericWriteLoad], [II_LWLE, II_LWRE]>; + +// MAXIS MT instructions +// ==================== + +def : ItinRW<[GenericWriteMove], [II_DMT, II_DVPE, II_EMT, II_EVPE, II_MFTR, + II_MTTR]>; + +def : ItinRW<[GenericReadWriteCOP0Long], [II_YIELD]>; + +def : ItinRW<[GenericWriteCOP0Short], [II_FORK]>; + +// MAXIS32R6 and MAXIS16e +// ==================== + +def : ItinRW<[GenericWriteLoad], [II_LWPC]>; + +// MAXIS64R6 +// ==================== + +def : ItinRW<[GenericWriteLoad], [II_LWUPC, II_LDPC]>; + + +// s[bhw], sc, s[dw]c[23] +def : ItinRW<[GenericWriteStore], [II_SB, II_SH, II_SW, II_SWC2, II_SWC3, + II_SDC2, II_SDC3]>; + +def : ItinRW<[GenericWriteStoreSC], [II_SC]>; + +// PreMAXISR6 sw[lr] +def : ItinRW<[GenericWriteStore], [II_SWL, II_SWR]>; + +// EVA ASE stores +def : ItinRW<[GenericWriteStore], [II_SBE, II_SHE, II_SWE, II_SCE]>; + +def : ItinRW<[GenericWriteStore], [II_SWLE, II_SWRE]>; + +// MAXIS64 +// ====== + +def : ItinRW<[GenericWriteStore], [II_SD, II_SCD]>; + +// PreMAXISR6 stores +// ================ + +def : ItinRW<[GenericWriteStore], [II_SDL, II_SDR]>; + +// MAXIS16e +// ======= + +def : ItinRW<[GenericWriteLoad], [II_RESTORE]>; + +def : ItinRW<[GenericWriteStore], [II_SAVE]>; + +// microMAXIS +// ========= + +def : ItinRW<[GenericWriteLoad], [II_LWM, II_LWP, II_LWXS]>; + +def : ItinRW<[GenericWriteStore], [II_SWM, II_SWP]>; + +// pref +def : ItinRW<[GenericWritePref], [II_PREF]>; + +def : ItinRW<[GenericWritePref], [II_PREFE]>; + +// cache +def : ItinRW<[GenericWriteCache], [II_CACHE]>; + +def : ItinRW<[GenericWriteCache], [II_CACHEE]>; + +// sync +def : ItinRW<[GenericWriteSync], [II_SYNC]>; + +def : ItinRW<[GenericWriteSync], [II_SYNCI]>; + +// FPU Pipelines +// ============= + +def GenericFPQ : ProcResource<1> { let BufferSize = 1; } +def GenericIssueFPUS : ProcResource<1> { let Super = GenericFPQ; } +def GenericIssueFPUL : ProcResource<1> { let Super = GenericFPQ; } +def GenericIssueFPULoad : ProcResource<1> { let Super = GenericFPQ; } +def GenericIssueFPUStore : ProcResource<1> { let Super = GenericFPQ; } +def GenericIssueFPUMove : ProcResource<1> { let Super = GenericFPQ; } +def GenericFPUDivSqrt : ProcResource<1> { let Super = GenericFPQ; } + +// The floating point compare of the 24k series including interAptiv has a +// listed latency of 1-2. Using the higher latency here. + +def GenericWriteFPUCmp : SchedWriteRes<[GenericIssueFPUS]> { let Latency = 2; } +def GenericWriteFPUS : SchedWriteRes<[GenericIssueFPUS]> { let Latency = 4; } +def GenericWriteFPUL : SchedWriteRes<[GenericIssueFPUL]> { let Latency = 5; } +def GenericWriteFPUStore : SchedWriteRes<[GenericIssueFPUStore]> { let + Latency = 1; +} +def GenericWriteFPULoad : SchedWriteRes<[GenericIssueFPULoad]> { + let Latency = 2; +} +def GenericWriteFPUMoveFP : SchedWriteRes<[GenericIssueFPUMove]> { + let Latency = 4; +} +def GenericWriteFPUMoveGPRFPU : SchedWriteRes<[GenericIssueFPUMove]> { + let Latency = 2; +} +def GenericWriteFPUDivS : SchedWriteRes<[GenericFPUDivSqrt]> { + let Latency = 17; + let ResourceCycles = [ 14 ]; +} +def GenericWriteFPUDivD : SchedWriteRes<[GenericFPUDivSqrt]> { + let Latency = 32; + let ResourceCycles = [ 29 ]; +} +def GenericWriteFPURcpS : SchedWriteRes<[GenericFPUDivSqrt]> { + let Latency = 13; + let ResourceCycles = [ 10 ]; +} +def GenericWriteFPURcpD : SchedWriteRes<[GenericFPUDivSqrt]> { + let Latency = 25; + let ResourceCycles = [ 21 ]; +} +def GenericWriteFPURsqrtS : SchedWriteRes<[GenericFPUDivSqrt]> { + let Latency = 17; + let ResourceCycles = [ 14 ]; +} +def GenericWriteFPURsqrtD : SchedWriteRes<[GenericFPUDivSqrt]> { + let Latency = 32; + let ResourceCycles = [ 29 ]; +} +def GenericWriteFPUSqrtS : SchedWriteRes<[GenericFPUDivSqrt]> { + let Latency = 17; + let ResourceCycles = [ 14 ]; +} +def GenericWriteFPUSqrtD : SchedWriteRes<[GenericFPUDivSqrt]> { + let Latency = 29; + let ResourceCycles = [ 29 ]; +} + +// Floating point compare and branch +// --------------------------------- +// +// c..[ds], bc1[tf], bc1[tf]l +def : ItinRW<[GenericWriteFPUCmp], [II_C_CC_D, II_C_CC_S, II_BC1F, II_BC1T, + II_BC1FL, II_BC1TL]>; + +def : ItinRW<[GenericWriteFPUCmp], [II_CMP_CC_D, II_CMP_CC_S]>; + +// Short Pipe +// ---------- +// +// abs.[ds], abs.ps, add.[ds], neg.[ds], neg.ps, madd.s, msub.s, nmadd,s +// nmsub.s, sub.[ds], mul.s + +def : ItinRW<[GenericWriteFPUS], [II_ABS, II_ADD_D, II_ADD_S, II_MADD_S, + II_MSUB_S, II_MUL_S, II_NEG, II_NMADD_S, + II_NMSUB_S, II_SUB_S, II_SUB_D]>; +// mov[tf].[ds] + +def : ItinRW<[GenericWriteFPUS], [II_MOVF_S, II_MOVF_D, II_MOVT_S, II_MOVT_D]>; + +// MAXISR6 +// ------ +// +// sel(eq|ne).[ds], max.[ds], maxa.[ds], min.[ds], mina.[ds], class.[ds] +def : ItinRW<[GenericWriteFPUS], [II_SELCCZ_S, II_SELCCZ_D, II_MAX_S, + II_MAX_D, II_MAXA_S, II_MAXA_D, II_MIN_S, + II_MIN_D, II_MINA_S, II_MINA_D, II_CLASS_S, + II_CLASS_D]>; + +// Long Pipe +// ---------- +// +// nmadd.d, nmsub.d, mul.[ds], mul.ps, ceil.[wl].[sd], cvt.d.[sw], cvt.s.[dw], +// cvt.w.[sd], cvt.[sw].ps, trunc.w.[ds], trunc.w.ps, floor.[ds], +// round.[lw].[ds], floor.[lw].ds + +// madd.d, msub.dm mul.d, mul.ps, nmadd.d, nmsub.d, ceil.[wl].[sd], cvt.d.[sw], +// cvt.s.[dw], cvt.w.[sd], cvt.[sw].ps, round.[lw].[ds], floor.[lw].ds, +// trunc.w.[ds], trunc.w.ps, +def : ItinRW<[GenericWriteFPUL], [II_MADD_D, II_MSUB_D, II_MUL_D, II_NMADD_D, + II_NMSUB_D, II_CEIL, II_CVT, + II_FLOOR, II_ROUND, II_TRUNC]>; + +// div.[ds], div.ps +def : ItinRW<[GenericWriteFPUDivS], [II_DIV_S]>; +def : ItinRW<[GenericWriteFPUDivD], [II_DIV_D]>; + +// sqrt.[ds], sqrt.ps +def : ItinRW<[GenericWriteFPUSqrtS], [II_SQRT_S]>; +def : ItinRW<[GenericWriteFPUSqrtD], [II_SQRT_D]>; + +// rsqrt.[ds], recip.[ds] +def : ItinRW<[GenericWriteFPURcpS], [II_RECIP_S, II_RSQRT_S]>; +def : ItinRW<[GenericWriteFPURcpD], [II_RECIP_D, II_RSQRT_D]>; + +// MAXISR6 +// ====== +// +// rint.[ds] +def : ItinRW<[GenericWriteFPUL], [II_RINT_S, II_RINT_D]>; + +// Load Pipe +// --------- + +// ctc1, mtc1, mthc1, cfc1, mfc1, mfhc1 +def : ItinRW<[GenericWriteFPUMoveGPRFPU], [II_CFC1, II_CTC1, II_MFC1, II_MFHC1, + II_MTC1, II_MTHC1]>; + +// swc1, swxc1 +def : ItinRW<[GenericWriteFPUStore], [II_SDC1, II_SDXC1, II_SUXC1, II_SWC1, + II_SWXC1]>; + +// movn.[ds], movz.[ds] +def : ItinRW<[GenericWriteFPUMoveFP], [II_MOV_D, II_MOV_S, II_MOVF, II_MOVT, + II_MOVN_D, II_MOVN_S, II_MOVZ_D, + II_MOVZ_S]>; + +// l[dw]x?c1 +def : ItinRW<[GenericWriteFPULoad], [II_LDC1, II_LDXC1, II_LUXC1, II_LWC1, + II_LWXC1]>; + +// MAXIS64 +// ====== + +def : ItinRW<[GenericWriteFPUMoveGPRFPU], [II_DMFC1, II_DMTC1]>; + +// MAXISR6 +// ====== + +def : ItinRW<[GenericWriteFPUS], [II_MADDF_S, II_MSUBF_S]>; + +def : ItinRW<[GenericWriteFPUS], [II_MADDF_D, II_MSUBF_D]>; + +def : ItinRW<[GenericWriteFPUCmp], [II_BC1CCZ, II_SEL_D, II_SEL_S]>; + +// Cavium Networks MAXIS (cnMAXIS) - Octeon, HasCnMaxis +// ================================================= + +def : ItinRW<[GenericWriteALU], [II_SEQ_SNE, II_SEQI_SNEI, II_POP, II_BADDU, + II_BBIT]>; + +// MAXIS DSP ASE, HasDSP +// ==================== + +def GenericDSP : ProcResource<1> { let BufferSize = 1; } +def GenericDSPShort : SchedWriteRes<[GenericDSP]> { let Latency = 2; } +def GenericDSPLong : SchedWriteRes<[GenericDSP]> { let Latency = 6; } +def GenericDSPBypass : SchedWriteRes<[GenericDSP]> { let Latency = 1; } +def GenericDSPMTHILO : SchedWriteRes<[GenericDSP]> { let Latency = 5; } +def GenericDSPLoad : SchedWriteRes<[GenericDSP]> { let Latency = 4; } +def GenericDSPMTHLIP : SchedWriteRes<[GenericDSP]> { let Latency = 5; } + +def : InstRW<[GenericDSPLong], (instregex "^EXTRV_RS_W$")>; +def : InstRW<[GenericDSPLong], (instregex "^EXTRV_R_W$")>; +def : InstRW<[GenericDSPLong], (instregex "^EXTRV_S_H$")>; +def : InstRW<[GenericDSPLong], (instregex "^EXTRV_W$")>; +def : InstRW<[GenericDSPLong], (instregex "^EXTR_RS_W$")>; +def : InstRW<[GenericDSPLong], (instregex "^EXTR_R_W$")>; +def : InstRW<[GenericDSPLong], (instregex "^EXTR_S_H$")>; +def : InstRW<[GenericDSPLong], (instregex "^EXTR_W$")>; +def : InstRW<[GenericDSPLong], (instregex "^INSV$")>; + +def : InstRW<[GenericDSPMTHLIP], (instregex "^MTHLIP$")>; +def : InstRW<[GenericDSPMTHILO], (instregex "^MTHI_DSP$")>; +def : InstRW<[GenericDSPMTHILO], (instregex "^MTLO_DSP$")>; + +def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQ_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQ_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQ_S_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDSC$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDU_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDU_S_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDWC$")>; +def : InstRW<[GenericDSPShort], (instregex "^BITREV$")>; +def : InstRW<[GenericDSPShort], (instregex "^BPOSGE32$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGU_EQ_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGU_LE_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGU_LT_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPU_EQ_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPU_LE_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPU_LT_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMP_EQ_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMP_LE_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMP_LT_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAQ_SA_L_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAQ_S_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAU_H_QBL$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAU_H_QBR$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSQ_SA_L_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSQ_S_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSU_H_QBL$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSU_H_QBR$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTPDPV$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTPDP$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTPV$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTP$")>; +def : InstRW<[GenericDSPShort], (instregex "^LBUX$")>; +def : InstRW<[GenericDSPShort], (instregex "^LHX$")>; +def : InstRW<[GenericDSPShort], (instregex "^LWX$")>; +def : InstRW<[GenericDSPShort], (instregex "^MADDU_DSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^MADD_DSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^MAQ_SA_W_PHL$")>; +def : InstRW<[GenericDSPShort], (instregex "^MAQ_SA_W_PHR$")>; +def : InstRW<[GenericDSPShort], (instregex "^MAQ_S_W_PHL$")>; +def : InstRW<[GenericDSPShort], (instregex "^MAQ_S_W_PHR$")>; +def : InstRW<[GenericDSPShort], (instregex "^MFHI_DSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^MFLO_DSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^MODSUB$")>; +def : InstRW<[GenericDSPShort], (instregex "^MSUBU_DSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^MSUB_DSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULEQ_S_W_PHL$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULEQ_S_W_PHR$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULEU_S_PH_QBL$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULEU_S_PH_QBR$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULQ_RS_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULSAQ_S_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULTU_DSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULT_DSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^PACKRL_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^PICK_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^PICK_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBLA$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBL$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBRA$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBR$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQ_W_PHL$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQ_W_PHR$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBLA$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBL$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBRA$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBR$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECRQU_S_QB_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_PH_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_QB_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_RS_PH_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^RADDU_W_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^RDDSP$")>; +def : InstRW<[GenericDSPShort], (instregex "^REPLV_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^REPLV_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^REPL_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^REPL_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHILOV$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHILO$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLLV_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLLV_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLLV_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLLV_S_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLL_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLL_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLL_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLL_S_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRLV_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRL_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQ_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQ_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQ_S_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBU_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBU_S_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^WRDSP$")>; + +// MAXIS DSP R2 - hasDSP, HasDSPR2, InMicroMaxis +// =========================================== + +def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQH_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQH_R_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQH_R_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQH_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDUH_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDUH_R_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDU_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDU_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^APPEND$")>; +def : InstRW<[GenericDSPShort], (instregex "^BALIGN$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_EQ_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_LE_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_LT_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPA_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAQX_SA_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAQX_S_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAX_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPS_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSQX_S_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSQX_SA_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSX_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^MUL_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^MUL_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULQ_RS_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULQ_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULQ_S_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULSA_W_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECR_QB_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECR_SRA_PH_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECR_SRA_R_PH_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^PREPEND$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRL_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRLV_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQH_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQH_R_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQH_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQH_R_W$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBU_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBU_S_PH$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBUH_QB$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBUH_R_QB$")>; + +// microMAXIS DSP R1 - HasDSP, InMicroMaxis +// ====================================== + +def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQ_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQ_S_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQ_S_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDSC_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDU_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDU_S_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDWC_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^BITREV_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^BPOSGE32_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGU_EQ_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGU_LE_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGU_LT_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPU_EQ_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPU_LE_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPU_LT_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMP_EQ_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMP_LE_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMP_LT_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAQ_SA_L_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAQ_S_W_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAU_H_QBL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAU_H_QBR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSQ_SA_L_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSQ_S_W_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSU_H_QBL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSU_H_QBR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTPDPV_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTPDP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTPV_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTRV_RS_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTRV_R_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTRV_S_H_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTRV_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTR_RS_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTR_R_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTR_S_H_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^EXTR_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^INSV_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^LBUX_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^LHX_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^LWX_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MADDU_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MADD_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MAQ_SA_W_PHL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MAQ_SA_W_PHR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MAQ_S_W_PHL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MAQ_S_W_PHR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MFHI_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MFLO_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MODSUB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MOVEP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MOVEP_MMR6$")>; +def : InstRW<[GenericDSPShort], (instregex "^MOVN_I_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MOVZ_I_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MSUBU_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MSUB_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MTHI_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MTHLIP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MTLO_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULEQ_S_W_PHL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULEQ_S_W_PHR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULEU_S_PH_QBL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULEU_S_PH_QBR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULQ_RS_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULSAQ_S_W_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULTU_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULT_DSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PACKRL_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PICK_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PICK_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBLA_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBRA_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQU_PH_QBR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQ_W_PHL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEQ_W_PHR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBLA_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBL_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBRA_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECEU_PH_QBR_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECRQU_S_QB_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_PH_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_QB_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECRQ_RS_PH_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^RADDU_W_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^RDDSP_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^REPLV_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^REPLV_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^REPL_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^REPL_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHILOV_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHILO_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLLV_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLLV_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLLV_S_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLLV_S_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLL_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLL_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLL_S_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHLL_S_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRLV_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRL_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQ_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQ_S_PH_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQ_S_W_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBU_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBU_S_QB_MM$")>; +def : InstRW<[GenericDSPShort], (instregex "^WRDSP_MM$")>; + + +// microMAXIS DSP R2 - hasDSP, HasDSPR2, InMicroMaxis +// ================================================ + +def : InstRW<[GenericDSPShort], (instregex "^ABSQ_S_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQH_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQH_R_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQH_R_W_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDQH_W_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDUH_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDUH_R_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDU_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^ADDU_S_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^APPEND_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^BALIGN_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_EQ_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_LE_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^CMPGDU_LT_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPA_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAQX_SA_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAQX_S_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPAX_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPS_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSQX_S_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSQX_SA_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^DPSX_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^MUL_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^MUL_S_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULQ_RS_W_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULQ_S_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULQ_S_W_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^MULSA_W_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECR_QB_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECR_SRA_PH_W_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^PRECR_SRA_R_PH_W_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^PREPEND_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRA_R_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRAV_R_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRL_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SHRLV_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQH_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQH_R_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQH_W_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBQH_R_W_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBU_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBU_S_PH_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBUH_QB_MMR2$")>; +def : InstRW<[GenericDSPShort], (instregex "^SUBUH_R_QB_MMR2$")>; + +// microMAXIS DSP R3 - hasDSP, hasDSPR2, hasDSPR3, InMicroMaxis +// ========================================================== + +def : InstRW<[GenericDSPShort], (instregex "^BPOSGE32C_MMR3$")>; + +// MAXIS MSA ASE - hasMSA +// ===================== + +def GenericWriteMSAShortLogic : SchedWriteRes<[GenericIssueFPUS]>; +def GenericWriteMSAShortInt : SchedWriteRes<[GenericIssueFPUS]> { +let Latency = 2; +} +def GenericWriteMoveOtherUnitsToFPU : SchedWriteRes<[GenericIssueFPUS]>; +def GenericWriteMSAOther3 : SchedWriteRes<[GenericIssueFPUS]> { +let Latency = 3; +} +def GenericWriteMSALongInt : SchedWriteRes<[GenericIssueFPUS]> { +let Latency = 5; +} +def GenericWriteFPUDivI : SchedWriteRes<[GenericFPQ]> { + let Latency = 33; + let ResourceCycles = [ 33 ]; +} + +// FPUS is also used in moves from floating point and MSA registers to general +// purpose registers. +def GenericWriteMoveFPUSToOtherUnits : SchedWriteRes<[GenericIssueFPUS]> { + let Latency = 0; +} + +// FPUL is also used in moves from floating point and MSA registers to general +// purpose registers. +def GenericWriteMoveFPULToOtherUnits : SchedWriteRes<[GenericIssueFPUL]>; + + +// adds_a.[bhwd], adds_[asu].[bhwd], addvi?.[bhwd], asub_[us].[bhwd], +// aver?_[us].[bhwd] +def : InstRW<[GenericWriteMSAShortInt], (instregex "^ADD_A_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^ADDS_[ASU]_[BHWD]$")>; + +// TODO: ADDVI_[BHW] might be 1 cycle latency rather than 2. Need to confirm it. +// add.[bhwd], addvi.[bhwd], asub_[us].[bhwd], ave.[bhwd], aver.[bhwd] +def : InstRW<[GenericWriteMSAShortInt], (instregex "^ADDVI?_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^ASUB_[US].[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^AVER?_[US].[BHWD]$")>; + +// and.v, andi.b, move.v, ldi.[bhwd], xor.v, nor.v, xori.b, nori.b +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^MOVE_V$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^LDI_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)_V$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)I_B$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)I_B$")>; + +// vshf.[bhwd], binsl.[bhwd], binsr.[bhwd], insert.[bhwd], sld?.[bhwd], +// bset.[bhwd], bclr.[bhwd], bneg.[bhwd], bsel_v, bseli_b +def : InstRW<[GenericWriteMSAShortInt], (instregex "^VSHF_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BINSL|BINSLI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BINSR|BINSRI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^INSERT_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^(SLD|SLDI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BSET|BSETI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BCLR|BCLRI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BNEG|BNEGI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^(BSEL_V|BSELI_B)$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^BMN*Z.*$")>; + +// pcnt.[bhwd], sat_s.[bhwd], sat_u.bhwd] +def : InstRW<[GenericWriteMSAOther3], (instregex "^PCNT_[BHWD]$")>; +def : InstRW<[GenericWriteMSAOther3], (instregex "^SAT_(S|U)_[BHWD]$")>; + +// bnz.[bhwdv], cfcmsa, ctcmsa +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(BNZ|BZ)_[BHWDV]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^C(F|T)CMSA$")>; + +// shf.[bhw], fill[bhwd], splat?.[bhwd] +def : InstRW<[GenericWriteMSAShortInt], (instregex "^SHF_[BHW]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^FILL_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^(SPLAT|SPLATI)_[BHWD]$")>; + +// pcnt.[bhwd], sat_s.[bhwd], sat_u.bhwd] +def : InstRW<[GenericWriteMSAOther3], (instregex "^PCNT_[BHWD]$")>; +def : InstRW<[GenericWriteMSAOther3], (instregex "^SAT_(S|U)_[BHWD]$")>; + +// fexp2_w, fexp2_d +def : InstRW<[GenericWriteFPUS], (instregex "^FEXP2_(W|D)$")>; + +// compare, converts, round to int, floating point truncate. +def : InstRW<[GenericWriteFPUS], (instregex "^(CLT|CLTI)_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^(CLE|CLEI)_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^(CEQ|CEQI)_[BHWD]$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^CMP_UN_(S|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^CMP_UEQ_(S|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^CMP_EQ_(S|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^CMP_LT_(S|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^CMP_ULT_(S|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^CMP_LE_(S|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^CMP_ULE_(S|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FS(AF|EQ|LT|LE|NE|OR)_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FSUEQ_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FSULE_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FSULT_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FSUNE_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FSUN_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCAF_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCEQ_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCLE_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCLT_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCNE_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCOR_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCUEQ_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCULE_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCULT_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCUNE_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FCUN_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FABS_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FFINT_(U|S)_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FFQL_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FFQR_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FTINT_(U|S)_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FRINT_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FTQ_(H|W)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FTRUNC_(U|S)_(W|D)$")>; + +// fexdo.[hw], fexupl.[wd], fexupr.[wd] +def : InstRW<[GenericWriteFPUS], (instregex "^FEXDO_(H|W)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FEXUPL_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FEXUPR_(W|D)$")>; + +// fclass.[wd], fmax.[wd], fmax_a.[wd], fmin.[wd], fmin_a.[wd], flog2.[wd] +def : InstRW<[GenericWriteFPUS], (instregex "^FCLASS_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FMAX_A_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FMAX_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FMIN_A_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FMIN_(W|D)$")>; +def : InstRW<[GenericWriteFPUS], (instregex "^FLOG2_(W|D)$")>; + +// interleave right/left, interleave even/odd, insert +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(ILVR|ILVL)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(ILVEV|ILVOD)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^INSVE_[BHWD]$")>; + +// subs_?.[bhwd], subsus_?.[bhwd], subsuu_?.[bhwd], subvi.[bhwd], subv.[bhwd], +def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBS_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBSUS_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBSUU_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBVI_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortInt], (instregex "^SUBV_[BHWD]$")>; + +// mod_[su].[bhwd], div_[su].[bhwd] +def : InstRW<[GenericWriteFPUDivI], (instregex "^MOD_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteFPUDivI], (instregex "^DIV_(S|U)_[BHWD]$")>; + +// hadd_[su].[bhwd], hsub_[su].[bhwd], max_[sua].[bhwd], min_[sua].[bhwd], +// maxi_[su].[bhwd], mini_[su].[bhwd], sra?.[bhwd], srar?.[bhwd], srlr.[bhwd], +// sll?.[bhwd], pckev.[bhwd], pckod.[bhwd], nloc.[bhwd], nlzc.[bhwd], +// insve.[bhwd] +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^HADD_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^HSUB_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(MAX|MIN)_S_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(MAX|MIN)_U_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(MAX|MIN)_A_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], + (instregex "^(MAXI|MINI)_(S|U)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SRA|SRAI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SRL|SRLI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SRAR|SRARI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SRLR|SRLRI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(SLL|SLLI)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(PCKEV|PCKOD)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^(NLOC|NLZC)_[BHWD]$")>; +def : InstRW<[GenericWriteMSAShortLogic], (instregex "^INSVE_[BHWD]$")>; + +// dpadd_?.[bhwd], dpsub_?.[bhwd], dotp_?.[bhwd], msubv.[bhwd], maddv.[bhwd] +// mulv.[bhwd]. +def : InstRW<[GenericWriteMSALongInt], (instregex "^DPADD_(S|U)_[HWD]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^DPSUB_(S|U)_[HWD]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^DOTP_(S|U)_[HWD]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^MSUBV_[BHWD]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^MADDV_[BHWD]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^MULV_[BHWD]$")>; + +// madd?.q.[hw], msub?.q.[hw], mul?.q.[hw] +def : InstRW<[GenericWriteMSALongInt], (instregex "^MADDR_Q_[HW]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^MADD_Q_[HW]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^MSUBR_Q_[HW]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^MSUB_Q_[HW]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^MULR_Q_[HW]$")>; +def : InstRW<[GenericWriteMSALongInt], (instregex "^MUL_Q_[HW]$")>; + +// fadd.[dw], fmadd.[dw], fmul.[dw], frcp.[dw], frsqrt.[dw], fsqrt.[dw] +// fsub.[dw], fdiv.[dw] +def : InstRW<[GenericWriteFPUL], (instregex "^FADD_[DW]$")>; +def : InstRW<[GenericWriteFPUL], (instregex "^FMADD_[DW]$")>; +def : InstRW<[GenericWriteFPUL], (instregex "^FMSUB_[DW]$")>; +def : InstRW<[GenericWriteFPUL], (instregex "^FMUL_[DW]$")>; +def : InstRW<[GenericWriteFPUL], (instregex "^FRCP_[DW]$")>; +def : InstRW<[GenericWriteFPUL], (instregex "^FRSQRT_[DW]$")>; +def : InstRW<[GenericWriteFPUL], (instregex "^FSQRT_[DW]$")>; +def : InstRW<[GenericWriteFPUL], (instregex "^FSUB_[DW]$")>; +def : InstRW<[GenericWriteFPUL], (instregex "^FDIV_[DW]$")>; + +// copy.[su]_[bhwd] +def : InstRW<[GenericWriteFPUMoveGPRFPU], (instregex "^COPY_U_[BHW]$")>; +def : InstRW<[GenericWriteFPUMoveGPRFPU], (instregex "^COPY_S_[BHWD]$")>; + +def : InstRW<[GenericWriteFPUStore], (instregex "^ST_[BHWD]$")>; +def : InstRW<[GenericWriteFPULoad], (instregex "^LD_[BHWD]$")>; +} diff --git a/lib/Target/Maxis/MaxisScheduleP5600.td b/lib/Target/Maxis/MaxisScheduleP5600.td new file mode 100644 index 00000000..d979b1d3 --- /dev/null +++ b/lib/Target/Maxis/MaxisScheduleP5600.td @@ -0,0 +1,586 @@ +//==- MaxisScheduleP5600.td - P5600 Scheduling Definitions --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +def MaxisP5600Model : SchedMachineModel { + int IssueWidth = 2; // 2x dispatched per cycle + int MicroOpBufferSize = 48; // min(48, 48, 64) + int LoadLatency = 4; + int MispredictPenalty = 8; // TODO: Estimated + + let CompleteModel = 1; + + list UnsupportedFeatures = [HasMaxis32r6, HasMaxis64r6, + HasMaxis64, HasMaxis64r2, HasCnMaxis, + InMicroMaxis, InMaxis16Mode, + HasMicroMaxis32r6, HasDSP, + HasDSPR2, HasMT]; + +} + +let SchedModel = MaxisP5600Model in { + +// ALQ Pipelines +// ============= + +def P5600ALQ : ProcResource<1> { let BufferSize = 16; } +def P5600IssueALU : ProcResource<1> { let Super = P5600ALQ; } + +// ALU Pipeline +// ------------ + +def P5600WriteALU : SchedWriteRes<[P5600IssueALU]>; + +// and, lui, nor, or, slti, sltiu, sub, subu, xor +def : ItinRW<[P5600WriteALU], + [II_AND, II_LUI, II_NOR, II_OR, II_SLTI_SLTIU, II_SUB, II_SUBU, + II_XOR]>; + +// AGQ Pipelines +// ============= + +def P5600AGQ : ProcResource<3> { let BufferSize = 16; } +def P5600IssueAL2 : ProcResource<1> { let Super = P5600AGQ; } +def P5600IssueCTISTD : ProcResource<1> { let Super = P5600AGQ; } +def P5600IssueLDST : ProcResource<1> { let Super = P5600AGQ; } + +def P5600AL2Div : ProcResource<1>; +// Pseudo-resource used to block CTISTD when handling multi-pipeline splits. +def P5600CTISTD : ProcResource<1>; + +// CTISTD Pipeline +// --------------- + +def P5600WriteJump : SchedWriteRes<[P5600IssueCTISTD, P5600CTISTD]>; +def P5600WriteJumpAndLink : SchedWriteRes<[P5600IssueCTISTD, P5600CTISTD]> { + let Latency = 2; +} + +// b, beq, beql, bg[et]z, bl[et]z, bne, bnel, j, syscall, jal, bltzal, +// jalr, jr.hb, jr +def : ItinRW<[P5600WriteJump], [II_B, II_BCC, II_BCCZ, II_BCCZAL, II_J, II_JR, + II_JR_HB, II_DERET, II_ERET, II_ERETNC, + II_SYSCALL, II_BREAK, II_SDBBP, II_SSNOP, + II_TEQ, II_TEQI, II_TGE, II_TGEI, II_TGEIU, + II_TGEU, II_TLT, II_TLTI, II_TLTU, II_TNE, + II_TNEI, II_TRAP, II_TTLTIU, II_WAIT, + II_PAUSE]>; + +def : ItinRW<[P5600WriteJumpAndLink], [II_JAL, II_JALR, II_JALR_HB]>; + +def P5600COP0 : SchedWriteRes<[P5600IssueCTISTD, P5600CTISTD]>; + +def : ItinRW<[P5600COP0], [II_TLBINV, II_TLBINVF, II_TLBP, II_TLBR, II_TLBWI, + II_TLBWR, II_MFC0, II_MTC0]>; +// LDST Pipeline +// ------------- + +def P5600WriteLoad : SchedWriteRes<[P5600IssueLDST]> { + let Latency = 4; +} + +def P5600WriteLoadShifted : SchedWriteRes<[P5600IssueLDST, P5600CTISTD]> { + let Latency = 4; +} + +def P5600WriteCache : SchedWriteRes<[P5600IssueLDST]>; + +def P5600WriteStore : SchedWriteRes<[P5600IssueLDST, P5600CTISTD]> { + // FIXME: This is a bit pessimistic. P5600CTISTD is only used during cycle 2 + // not during 0, 1, and 2. + let ResourceCycles = [ 1, 3 ]; +} + +def P5600WriteGPRFromBypass : SchedWriteRes<[P5600IssueLDST]> { + let Latency = 2; +} + +def P5600WriteStoreFromOtherUnits : SchedWriteRes<[P5600IssueLDST]>; +def P5600WriteLoadToOtherUnits : SchedWriteRes<[P5600IssueLDST]> { + let Latency = 0; +} + +// l[bhw], l[bh]u, ll +def : ItinRW<[P5600WriteLoad], [II_LB, II_LBE, II_LBU, II_LBUE, II_LH, II_LHE, + II_LHU, II_LHUE, II_LW, II_LWE, II_LL, II_LLE, + II_LWPC]>; + +// lw[lr] +def : ItinRW<[P5600WriteLoadShifted], [II_LWL, II_LWLE, II_LWR, II_LWRE]>; + +// s[bhw], sw[lr] +def : ItinRW<[P5600WriteStore], [II_SB, II_SBE, II_SH, II_SHE, II_SW, II_SWE, + II_SWL, II_SWLE, II_SWR, II_SWRE, II_SC, + II_SCE]>; + +// pref, cache, sync, synci +def : ItinRW<[P5600WriteCache], [II_PREF, II_PREFE, II_CACHE, II_CACHEE, + II_SYNC, II_SYNCI]>; + +// LDST is also used in moves from general purpose registers to floating point +// and MSA. +def P5600WriteMoveGPRToOtherUnits : SchedWriteRes<[P5600IssueLDST]> { + let Latency = 0; +} + +// AL2 Pipeline +// ------------ + +def P5600WriteAL2 : SchedWriteRes<[P5600IssueAL2]>; +def P5600WriteAL2BitExt : SchedWriteRes<[P5600IssueAL2]> { let Latency = 2; } +def P5600WriteAL2ShadowMov : SchedWriteRes<[P5600IssueAL2]> { let Latency = 2; } +def P5600WriteAL2CondMov : SchedWriteRes<[P5600IssueAL2, P5600CTISTD]> { + let Latency = 2; +} +def P5600WriteAL2Div : SchedWriteRes<[P5600IssueAL2, P5600AL2Div]> { + // Estimated worst case + let Latency = 34; + let ResourceCycles = [1, 34]; +} +def P5600WriteAL2DivU : SchedWriteRes<[P5600IssueAL2, P5600AL2Div]> { + // Estimated worst case + let Latency = 34; + let ResourceCycles = [1, 34]; +} +def P5600WriteAL2Mul : SchedWriteRes<[P5600IssueAL2]> { let Latency = 3; } +def P5600WriteAL2Mult: SchedWriteRes<[P5600IssueAL2]> { let Latency = 5; } +def P5600WriteAL2MAdd: SchedWriteRes<[P5600IssueAL2, P5600CTISTD]> { + let Latency = 5; +} + +// clo, clz, di, ei, mfhi, mflo +def : ItinRW<[P5600WriteAL2], [II_CLO, II_CLZ, II_DI, II_EI, II_MFHI_MFLO]>; + +// ehb, rdhwr, rdpgpr, wrpgpr, wsbh +def : ItinRW<[P5600WriteAL2ShadowMov], [II_EHB, II_RDHWR, II_WSBH]>; + +// mov[nz] +def : ItinRW<[P5600WriteAL2CondMov], [II_MOVN, II_MOVZ]>; + +// divu? +def : ItinRW<[P5600WriteAL2Div], [II_DIV]>; +def : ItinRW<[P5600WriteAL2DivU], [II_DIVU]>; + +// mul +def : ItinRW<[P5600WriteAL2Mul], [II_MUL]>; +// multu?, multu? +def : ItinRW<[P5600WriteAL2Mult], [II_MULT, II_MULTU]>; +// maddu?, msubu?, mthi, mtlo +def : ItinRW<[P5600WriteAL2MAdd], + [II_MADD, II_MADDU, II_MSUB, II_MSUBU, II_MTHI_MTLO]>; + +// ext, ins +def : ItinRW<[P5600WriteAL2BitExt], [II_EXT, II_INS]>; + +// Either ALU or AL2 Pipelines +// --------------------------- +// +// Some instructions can choose between ALU and AL2, but once dispatched to +// ALQ or AGQ respectively they are committed to that path. +// The decision is based on the outcome of the most recent selection when the +// choice was last available. For now, we assume ALU is always chosen. + +def P5600WriteEitherALU : SchedWriteVariant< + // FIXME: Implement selection predicate + [SchedVar, [P5600WriteALU]>, + SchedVar, [P5600WriteAL2]> + ]>; + +// add, addi, addiu, addu, andi, ori, rotr, se[bh], sllv?, sr[al]v?, slt, sltu, +// xori +def : ItinRW<[P5600WriteEitherALU], + [II_ADD, II_ADDI, II_ADDIU, II_ANDI, II_ORI, II_ROTR, II_SEB, II_SEH, + II_SLT_SLTU, II_SLL, II_SRA, II_SRL, II_XORI, II_ADDU, II_SLLV, + II_SRAV, II_SRLV, II_LSA]>; +def : InstRW<[], (instrs COPY)>; + +// FPU Pipelines +// ============= + +def P5600FPQ : ProcResource<3> { let BufferSize = 16; } +def P5600IssueFPUS : ProcResource<1> { let Super = P5600FPQ; } +def P5600IssueFPUL : ProcResource<1> { let Super = P5600FPQ; } +def P5600IssueFPULoad : ProcResource<1> { let Super = P5600FPQ; } + +def P5600FPUDivSqrt : ProcResource<2>; + +def P5600WriteFPUS : SchedWriteRes<[P5600IssueFPUS]>; +def P5600WriteFPUL : SchedWriteRes<[P5600IssueFPUL]> { let Latency = 4; } +def P5600WriteFPUL_MADDSUB : SchedWriteRes<[P5600IssueFPUL]> { let Latency = 6; } +def P5600WriteFPUDivI : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 23 / 27 + let Latency = 23; // Using common case + let ResourceCycles = [ 1, 23 ]; +} +def P5600WriteFPUDivS : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 23 / 27 + let Latency = 23; // Using common case + let ResourceCycles = [ 1, 23 ]; +} +def P5600WriteFPUDivD : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 31 / 35 + let Latency = 31; // Using common case + let ResourceCycles = [ 1, 31 ]; +} +def P5600WriteFPURcpS : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 19 / 23 + let Latency = 19; // Using common case + let ResourceCycles = [ 1, 19 ]; +} +def P5600WriteFPURcpD : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 27 / 31 + let Latency = 27; // Using common case + let ResourceCycles = [ 1, 27 ]; +} +def P5600WriteFPURsqrtS : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 27 / 27 + let Latency = 27; // Using common case + let ResourceCycles = [ 1, 27 ]; +} +def P5600WriteFPURsqrtD : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 27 / 31 + let Latency = 27; // Using common case + let ResourceCycles = [ 1, 27 ]; +} +def P5600WriteFPUSqrtS : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 27 / 31 + let Latency = 27; // Using common case + let ResourceCycles = [ 1, 27 ]; +} +def P5600WriteFPUSqrtD : SchedWriteRes<[P5600IssueFPUL, P5600FPUDivSqrt]> { + // Best/Common/Worst case = 7 / 35 / 39 + let Latency = 35; // Using common case + let ResourceCycles = [ 1, 35 ]; +} +def P5600WriteMSAShortLogic : SchedWriteRes<[P5600IssueFPUS]>; +def P5600WriteMSAShortInt : SchedWriteRes<[P5600IssueFPUS]> { let Latency = 2; } +def P5600WriteMoveOtherUnitsToFPU : SchedWriteRes<[P5600IssueFPUS]>; +def P5600WriteMSAOther3 : SchedWriteRes<[P5600IssueFPUS]> { let Latency = 3; } +def P5600WriteMSALongInt : SchedWriteRes<[P5600IssueFPUS]> { let Latency = 5; } + +// vshf.[bhwd], binsl.[bhwd], binsr.[bhwd], insert.[bhwd], sld?.[bhwd], +// bset.[bhwd], bclr.[bhwd], bneg.[bhwd], bsel_v, bseli_b +def : InstRW<[P5600WriteMSAShortInt], (instregex "^VSHF_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BINSL|BINSLI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BINSR|BINSRI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^INSERT_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(SLD|SLDI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BSET|BSETI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BCLR|BCLRI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BNEG|BNEGI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BSEL_V|BSELI_B)$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^BMN*Z.*$")>; + +// pcnt.[bhwd], sat_s.[bhwd], sat_u.bhwd] +def : InstRW<[P5600WriteMSAOther3], (instregex "^PCNT_[BHWD]$")>; +def : InstRW<[P5600WriteMSAOther3], (instregex "^SAT_(S|U)_[BHWD]$")>; + +// bnz.[bhwdv], cfcmsa, ctcmsa +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(BNZ|BZ)_[BHWDV]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^C(F|T)CMSA$")>; + +// FPUS is also used in moves from floating point and MSA registers to general +// purpose registers. +def P5600WriteMoveFPUSToOtherUnits : SchedWriteRes<[P5600IssueFPUS]> { + let Latency = 0; +} + +// FPUL is also used in moves from floating point and MSA registers to general +// purpose registers. +def P5600WriteMoveFPULToOtherUnits : SchedWriteRes<[P5600IssueFPUL]>; + +// Short Pipe +// ---------- +// +// abs.[ds], abs.ps, bc1[tf]l?, mov[tf].[ds], mov[tf], mov.[ds], [cm][ft]c1, +// m[ft]hc1, neg.[ds], neg.ps, nor.v, nori.b, or.v, ori.b, xor.v, xori.b, +// sdxc1, sdc1, st.[bhwd], swc1, swxc1 +def : ItinRW<[P5600WriteFPUS], [II_ABS, II_MOVF_D, II_MOVF_S, II_MOVT_D, + II_MOVT_S, II_MOV_D, II_MOV_S, II_NEG]>; + +// adds_a.[bhwd], adds_[asu].[bhwd], addvi?.[bhwd], asub_[us].[bhwd], +// aver?_[us].[bhwd], shf.[bhw], fill[bhwd], splat?.[bhwd] +def : InstRW<[P5600WriteMSAShortInt], (instregex "^ADD_A_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^ADDS_[ASU]_[BHWD]$")>; +// TODO: ADDVI_[BHW] might be 1 cycle latency rather than 2. Need to confirm it. +def : InstRW<[P5600WriteMSAShortInt], (instregex "^ADDVI?_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^ASUB_[US].[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^AVER?_[US].[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^SHF_[BHW]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^FILL_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(SPLAT|SPLATI)_[BHWD]$")>; + +// and.v, andi.b, move.v, ldi.[bhwd] +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^MOVE_V$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^LDI_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)_V$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(AND|OR|[XN]OR)I_B$")>; + +// vshf.[bhwd], binsl.[bhwd], binsr.[bhwd], insert.[bhwd], sld?.[bhwd], +// bset.[bhwd], bclr.[bhwd], bneg.[bhwd], bsel_v, bseli_b +def : InstRW<[P5600WriteMSAShortInt], (instregex "^VSHF_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BINSL|BINSLI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BINSR|BINSRI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^INSERT_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(SLD|SLDI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BSET|BSETI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BCLR|BCLRI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BNEG|BNEGI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^(BSEL_V|BSELI_B)$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^BMN*Z.*$")>; + +// pcnt.[bhwd], sat_s.[bhwd], sat_u.bhwd] +def : InstRW<[P5600WriteMSAOther3], (instregex "^PCNT_[BHWD]$")>; +def : InstRW<[P5600WriteMSAOther3], (instregex "^SAT_(S|U)_[BHWD]$")>; + +// fexp2_w, fexp2_d +def : InstRW<[P5600WriteFPUS], (instregex "^FEXP2_(W|D)$")>; + +// compare, converts, round to int, floating point truncate. +def : InstRW<[P5600WriteFPUS], (instregex "^(CLT|CLTI)_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^(CLE|CLEI)_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^(CEQ|CEQI)_[BHWD]$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^CMP_UN_(S|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^CMP_UEQ_(S|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^CMP_EQ_(S|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^CMP_LT_(S|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^CMP_ULT_(S|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^CMP_LE_(S|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^CMP_ULE_(S|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FS(AF|EQ|LT|LE|NE|OR)_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FSUEQ_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FSULE_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FSULT_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FSUNE_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FSUN_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCAF_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCEQ_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCLE_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCLT_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCNE_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCOR_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCUEQ_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCULE_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCULT_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCUNE_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FCUN_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FABS_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FFINT_(U|S)_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FFQL_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FFQR_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FTINT_(U|S)_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FRINT_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FTQ_(H|W)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FTRUNC_(U|S)_(W|D)$")>; + +// fexdo.[hw], fexupl.[wd], fexupr.[wd] +def : InstRW<[P5600WriteFPUS], (instregex "^FEXDO_(H|W)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FEXUPL_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FEXUPR_(W|D)$")>; + +// fclass.[wd], fmax.[wd], fmax_a.[wd], fmin.[wd], fmin_a.[wd], flog2.[wd] +def : InstRW<[P5600WriteFPUS], (instregex "^FCLASS_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FMAX_A_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FMAX_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FMIN_A_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FMIN_(W|D)$")>; +def : InstRW<[P5600WriteFPUS], (instregex "^FLOG2_(W|D)$")>; + +// interleave right/left, interleave even/odd, insert +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(ILVR|ILVL)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(ILVEV|ILVOD)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^INSVE_[BHWD]$")>; + +// subs_?.[bhwd], subsus_?.[bhwd], subsuu_?.[bhwd], subvi.[bhwd], subv.[bhwd], +def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBS_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBSUS_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBSUU_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBVI_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortInt], (instregex "^SUBV_[BHWD]$")>; + +// mod_[su].[bhwd], div_[su].[bhwd] +def : InstRW<[P5600WriteFPUDivI], (instregex "^MOD_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteFPUDivI], (instregex "^DIV_(S|U)_[BHWD]$")>; + +// hadd_[su].[bhwd], hsub_[su].[bhwd], max_[sua].[bhwd], min_[sua].[bhwd], +// maxi_[su].[bhwd], mini_[su].[bhwd], sra?.[bhwd], srar?.[bhwd], srlr.[bhwd], +// sll?.[bhwd], pckev.[bhwd], pckod.[bhwd], nloc.[bhwd], nlzc.[bhwd], +// insve.[bhwd] +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^HADD_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^HSUB_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(MAX|MIN)_S_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(MAX|MIN)_U_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(MAX|MIN)_A_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(MAXI|MINI)_(S|U)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SRA|SRAI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SRL|SRLI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SRAR|SRARI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SRLR|SRLRI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(SLL|SLLI)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(PCKEV|PCKOD)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^(NLOC|NLZC)_[BHWD]$")>; +def : InstRW<[P5600WriteMSAShortLogic], (instregex "^INSVE_[BHWD]$")>; + +// Long Pipe +// ---------- +// +// add.[ds], add.ps, cvt.d.[sw], cvt.s.[dw], cvt.w.[sd], cvt.[sw].ps, +// cvt.ps.[sw], c..[ds], c..ps, mul.[ds], mul.ps, sub.[ds], sub.ps, +// trunc.w.[ds], trunc.w.ps +def : ItinRW<[P5600WriteFPUL], + [II_ADD_D, II_ADD_S, II_CVT, II_C_CC_D, II_C_CC_S, II_MUL_D, + II_MUL_S, II_SUB_D, II_SUB_S, II_TRUNC]>; + +// div.[ds], div.ps +def : ItinRW<[P5600WriteFPUDivS], [II_DIV_S]>; +def : ItinRW<[P5600WriteFPUDivD], [II_DIV_D]>; + +// sqrt.[ds], sqrt.ps +def : ItinRW<[P5600WriteFPUSqrtS], [II_SQRT_S]>; +def : ItinRW<[P5600WriteFPUSqrtD], [II_SQRT_D]>; + +// frcp.[wd], frsqrt.[wd] +def : InstRW<[P5600WriteFPURsqrtD], (instregex "^FRCP_(W|D)$")>; +def : InstRW<[P5600WriteFPURsqrtD], (instregex "^FRSQRT_(W|D)$")>; + +def : ItinRW<[P5600WriteFPURsqrtD], [II_RECIP_D, II_RSQRT_D]>; +def : ItinRW<[P5600WriteFPURsqrtS], [II_RECIP_S, II_RSQRT_S]>; + +// fmadd.[wd], fmsubb.[wd], fdiv.[wd], fsqrt.[wd], fmul.[wd], fadd.[wd], +// fsub.[wd] +def : InstRW<[P5600WriteFPUL_MADDSUB], (instregex "^FMADD_(W|D)$")>; +def : InstRW<[P5600WriteFPUL_MADDSUB], (instregex "^FMSUB_(W|D)$")>; +def : InstRW<[P5600WriteFPUDivS], (instregex "^FDIV_W$")>; +def : InstRW<[P5600WriteFPUDivD], (instregex "^FDIV_D$")>; +def : InstRW<[P5600WriteFPUSqrtS], (instregex "^FSQRT_W$")>; +def : InstRW<[P5600WriteFPUSqrtD], (instregex "^FSQRT_D$")>; +def : InstRW<[P5600WriteFPUL], (instregex "^FMUL_(W|D)$")>; +def : InstRW<[P5600WriteFPUL], (instregex "^FADD_(W|D)$")>; +def : InstRW<[P5600WriteFPUL], (instregex "^FSUB_(W|D)$")>; + +// dpadd_?.[bhwd], dpsub_?.[bhwd], dotp_?.[bhwd], msubv.[bhwd], maddv.[bhwd] +// mulv.[bhwd]. +def : InstRW<[P5600WriteMSALongInt], (instregex "^DPADD_(S|U)_[HWD]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^DPSUB_(S|U)_[HWD]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^DOTP_(S|U)_[HWD]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^MSUBV_[BHWD]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^MADDV_[BHWD]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^MULV_[BHWD]$")>; + +def : InstRW<[P5600WriteMSALongInt], (instregex "^MADDR_Q_[HW]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^MADD_Q_[HW]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^MSUBR_Q_[HW]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^MSUB_Q_[HW]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^MULR_Q_[HW]$")>; +def : InstRW<[P5600WriteMSALongInt], (instregex "^MUL_Q_[HW]$")>; + +// madd.[ds], msub.[ds], nmadd.[ds], nmsub.[ds], +// Operand 0 is read on cycle 5. All other operands are read on operand 0. +def : ItinRW<[SchedReadAdvance<5>, P5600WriteFPUL_MADDSUB], + [II_MADD_D, II_MADD_S, II_MSUB_D, II_MSUB_S, II_NMADD_D, + II_NMADD_S, II_NMSUB_D, II_NMSUB_S]>; + +// madd.ps, msub.ps, nmadd.ps, nmsub.ps +// Operand 0 and 1 are read on cycle 5. All others are read on operand 0. +// (none of these instructions exist in the backend yet) + +// Load Pipe +// --------- +// +// This is typically used in conjunction with the load pipeline under the AGQ +// All the instructions are in the 'Tricky Instructions' section. + +def P5600WriteLoadOtherUnitsToFPU : SchedWriteRes<[P5600IssueFPULoad]> { + let Latency = 4; +} + +// Tricky Instructions +// =================== +// +// These instructions are split across multiple uops (in different pipelines) +// that must cooperate to complete the operation + +// FIXME: This isn't quite right since the implementation of WriteSequence +// current aggregates the resources and ignores the exact cycle they are +// used. +def P5600WriteMoveGPRToFPU : WriteSequence<[P5600WriteMoveGPRToOtherUnits, + P5600WriteMoveOtherUnitsToFPU]>; + +// FIXME: This isn't quite right since the implementation of WriteSequence +// current aggregates the resources and ignores the exact cycle they are +// used. +def P5600WriteMoveFPUToGPR : WriteSequence<[P5600WriteMoveFPUSToOtherUnits, + P5600WriteGPRFromBypass]>; + +// FIXME: This isn't quite right since the implementation of WriteSequence +// current aggregates the resources and ignores the exact cycle they are +// used. +def P5600WriteStoreFPUS : WriteSequence<[P5600WriteMoveFPUSToOtherUnits, + P5600WriteStoreFromOtherUnits]>; + +// FIXME: This isn't quite right since the implementation of WriteSequence +// current aggregates the resources and ignores the exact cycle they are +// used. +def P5600WriteStoreFPUL : WriteSequence<[P5600WriteMoveFPULToOtherUnits, + P5600WriteStoreFromOtherUnits]>; + +// FIXME: This isn't quite right since the implementation of WriteSequence +// current aggregates the resources and ignores the exact cycle they are +// used. +def P5600WriteLoadFPU : WriteSequence<[P5600WriteLoadToOtherUnits, + P5600WriteLoadOtherUnitsToFPU]>; + +// ctc1, mtc1, mthc1 +def : ItinRW<[P5600WriteMoveGPRToFPU], [II_CTC1, II_MTC1, II_MTHC1]>; + +// copy.[su]_[bhwd] +def : InstRW<[P5600WriteMoveFPUToGPR], (instregex "^COPY_U_[BHW]$")>; +def : InstRW<[P5600WriteMoveFPUToGPR], (instregex "^COPY_S_[BHWD]$")>; + +// bc1[ft], cfc1, mfc1, mfhc1, movf, movt +def : ItinRW<[P5600WriteMoveFPUToGPR], + [II_BC1F, II_BC1FL, II_BC1T, II_BC1TL, II_CFC1, II_MFC1, II_MFHC1, II_MOVF, II_MOVT]>; + +// swc1, swxc1, st.[bhwd] +def : ItinRW<[P5600WriteStoreFPUS], [II_SDC1, II_SDXC1, II_SUXC1, II_SWC1, + II_SWXC1]>; +def : InstRW<[P5600WriteStoreFPUS], (instregex "^ST_[BHWD]$")>; + +// movn.[ds], movz.[ds] +def : ItinRW<[P5600WriteStoreFPUL], [II_MOVN_D, II_MOVN_S, II_MOVZ_D, II_MOVZ_S]>; + +// l[dw]x?c1, ld.[bhwd] +def : ItinRW<[P5600WriteLoadFPU], [II_LDC1, II_LDXC1, II_LWC1, II_LWXC1, II_LUXC1]>; +def : InstRW<[P5600WriteLoadFPU], (instregex "LD_[BHWD]")>; + +// Unsupported Instructions +// ======================== +// +// The following instruction classes are never valid on P5600. +// II_DADDIU, II_DADDU, II_DMFC1, II_DMTC1, II_DMULT, II_DMULTU, II_DROTR, +// II_DROTR32, II_DROTRV, II_DDIV, II_DSLL, II_DSLL32, II_DSLLV, II_DSRA, +// II_DSRA32, II_DSRAV, II_DSRL, II_DSRL32, II_DSRLV, II_DSUBU, II_DDIVU, +// II_JALRC, II_LD, II_LD[LR], II_RESTORE, II_SAVE, II_SD, II_SDC1, II_SD[LR] +// +// The following instructions are never valid on P5600. +// addq.ph, repl.ph, repl.qb, subq.ph, subu_s.qb +// +// Guesswork +// ========= +// +// This section is largely temporary guesswork. + +// ceil.[lw].[ds], floor.[lw].[ds] +// Reason behind guess: trunc.[lw].ds and the various cvt's are in FPUL +def : ItinRW<[P5600WriteFPUL], [II_CEIL, II_FLOOR, II_ROUND]>; + +// rotrv +// Reason behind guess: rotr is in the same category and the two register forms +// generally follow the immediate forms in this category +def : ItinRW<[P5600WriteEitherALU], [II_ROTRV]>; +} diff --git a/lib/Target/Maxis/MaxisSubtarget.cpp b/lib/Target/Maxis/MaxisSubtarget.cpp new file mode 100644 index 00000000..3096e203 --- /dev/null +++ b/lib/Target/Maxis/MaxisSubtarget.cpp @@ -0,0 +1,188 @@ +//===-- MaxisSubtarget.cpp - Maxis Subtarget Information --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Maxis specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "MaxisSubtarget.h" +#include "Maxis.h" +#include "MaxisMachineFunction.h" +#include "MaxisRegisterInfo.h" +#include "MaxisTargetMachine.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +#define DEBUG_TYPE "maxis-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "MaxisGenSubtargetInfo.inc" + +// FIXME: Maybe this should be on by default when Maxis16 is specified +// +static cl::opt + Mixed16_32("maxis-mixed-16-32", cl::init(false), + cl::desc("Allow for a mixture of Maxis16 " + "and Maxis32 code in a single output file"), + cl::Hidden); + +static cl::opt Maxis_Os16("maxis-os16", cl::init(false), + cl::desc("Compile all functions that don't use " + "floating point as Maxis 16"), + cl::Hidden); + +static cl::opt Maxis16HardFloat("maxis16-hard-float", cl::NotHidden, + cl::desc("Enable maxis16 hard float."), + cl::init(false)); + +static cl::opt + Maxis16ConstantIslands("maxis16-constant-islands", cl::NotHidden, + cl::desc("Enable maxis16 constant islands."), + cl::init(true)); + +static cl::opt + GPOpt("mgpopt", cl::Hidden, + cl::desc("Enable gp-relative addressing of maxis small data items")); + +void MaxisSubtarget::anchor() {} + +MaxisSubtarget::MaxisSubtarget(const Triple &TT, StringRef CPU, StringRef FS, + bool little, const MaxisTargetMachine &TM, + unsigned StackAlignOverride) + : MaxisGenSubtargetInfo(TT, CPU, FS), MaxisArchVersion(MaxisDefault), + IsLittle(little), IsSoftFloat(false), IsSingleFloat(false), IsFPXX(false), + NoABICalls(false), IsFP64bit(false), UseOddSPReg(true), + IsNaN2008bit(false), IsGP64bit(false), HasVFPU(false), HasCnMaxis(false), + HasMaxis3_32(false), HasMaxis3_32r2(false), HasMaxis4_32(false), + HasMaxis4_32r2(false), HasMaxis5_32r2(false), InMaxis16Mode(false), + InMaxis16HardFloat(Maxis16HardFloat), InMicroMaxisMode(false), HasDSP(false), + HasDSPR2(false), HasDSPR3(false), AllowMixed16_32(Mixed16_32 | Maxis_Os16), + Os16(Maxis_Os16), HasMSA(false), UseTCCInDIV(false), HasSym32(false), + HasEVA(false), DisableMadd4(false), HasMT(false), + StackAlignOverride(StackAlignOverride), TM(TM), TargetTriple(TT), + TSInfo(), InstrInfo(MaxisInstrInfo::create( + initializeSubtargetDependencies(CPU, FS, TM))), + FrameLowering(MaxisFrameLowering::create(*this)), + TLInfo(MaxisTargetLowering::create(TM, *this)) { + + if (MaxisArchVersion == MaxisDefault) + MaxisArchVersion = Maxis32; + + // Don't even attempt to generate code for MAXIS-I and MAXIS-V. They have not + // been tested and currently exist for the integrated assembler only. + if (MaxisArchVersion == Maxis1) + report_fatal_error("Code generation for MAXIS-I is not implemented", false); + if (MaxisArchVersion == Maxis5) + report_fatal_error("Code generation for MAXIS-V is not implemented", false); + + // Check if Architecture and ABI are compatible. + assert(((!isGP64bit() && isABI_O32()) || + (isGP64bit() && (isABI_N32() || isABI_N64()))) && + "Invalid Arch & ABI pair."); + + if (hasMSA() && !isFP64bit()) + report_fatal_error("MSA requires a 64-bit FPU register file (FR=1 mode). " + "See -mattr=+fp64.", + false); + + if (!isABI_O32() && !useOddSPReg()) + report_fatal_error("-mattr=+nooddspreg requires the O32 ABI.", false); + + if (IsFPXX && (isABI_N32() || isABI_N64())) + report_fatal_error("FPXX is not permitted for the N32/N64 ABI's.", false); + + if (hasMaxis64r6() && InMicroMaxisMode) + report_fatal_error("microMAXIS64R6 is not supported", false); + + if (hasMaxis32r6()) { + StringRef ISA = hasMaxis64r6() ? "MAXIS64r6" : "MAXIS32r6"; + + assert(isFP64bit()); + assert(isNaN2008()); + if (hasDSP()) + report_fatal_error(ISA + " is not compatible with the DSP ASE", false); + } + + if (NoABICalls && TM.isPositionIndependent()) + report_fatal_error("position-independent code requires '-mabicalls'"); + + if (isABI_N64() && !TM.isPositionIndependent() && !hasSym32()) + NoABICalls = true; + + // Set UseSmallSection. + UseSmallSection = GPOpt; + if (!NoABICalls && GPOpt) { + errs() << "warning: cannot use small-data accesses for '-mabicalls'" + << "\n"; + UseSmallSection = false; + } +} + +bool MaxisSubtarget::isPositionIndependent() const { + return TM.isPositionIndependent(); +} + +/// This overrides the PostRAScheduler bit in the SchedModel for any CPU. +bool MaxisSubtarget::enablePostRAScheduler() const { return true; } + +void MaxisSubtarget::getCriticalPathRCs(RegClassVector &CriticalPathRCs) const { + CriticalPathRCs.clear(); + CriticalPathRCs.push_back(isGP64bit() ? &Maxis::GPR64RegClass + : &Maxis::GPR32RegClass); +} + +CodeGenOpt::Level MaxisSubtarget::getOptLevelToEnablePostRAScheduler() const { + return CodeGenOpt::Aggressive; +} + +MaxisSubtarget & +MaxisSubtarget::initializeSubtargetDependencies(StringRef CPU, StringRef FS, + const TargetMachine &TM) { + std::string CPUName = MAXIS_MC::selectMaxisCPU(TM.getTargetTriple(), CPU); + + // Parse features string. + ParseSubtargetFeatures(CPUName, FS); + // Initialize scheduling itinerary for the specified CPU. + InstrItins = getInstrItineraryForCPU(CPUName); + + if (InMaxis16Mode && !IsSoftFloat) + InMaxis16HardFloat = true; + + if (StackAlignOverride) + stackAlignment = StackAlignOverride; + else if (isABI_N32() || isABI_N64()) + stackAlignment = 16; + else { + assert(isABI_O32() && "Unknown ABI for stack alignment!"); + stackAlignment = 8; + } + + return *this; +} + +bool MaxisSubtarget::useConstantIslands() { + DEBUG(dbgs() << "use constant islands " << Maxis16ConstantIslands << "\n"); + return Maxis16ConstantIslands; +} + +Reloc::Model MaxisSubtarget::getRelocationModel() const { + return TM.getRelocationModel(); +} + +bool MaxisSubtarget::isABI_N64() const { return getABI().IsN64(); } +bool MaxisSubtarget::isABI_N32() const { return getABI().IsN32(); } +bool MaxisSubtarget::isABI_O32() const { return getABI().IsO32(); } +const MaxisABIInfo &MaxisSubtarget::getABI() const { return TM.getABI(); } diff --git a/lib/Target/Maxis/MaxisSubtarget.h b/lib/Target/Maxis/MaxisSubtarget.h new file mode 100644 index 00000000..62c7a6d8 --- /dev/null +++ b/lib/Target/Maxis/MaxisSubtarget.h @@ -0,0 +1,342 @@ +//===-- MaxisSubtarget.h - Define Subtarget for the Maxis ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the Maxis specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISSUBTARGET_H +#define LLVM_LIB_TARGET_MAXIS_MAXISSUBTARGET_H + +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MaxisFrameLowering.h" +#include "MaxisISelLowering.h" +#include "MaxisInstrInfo.h" +#include "llvm/CodeGen/SelectionDAGTargetInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/MC/MCInstrItineraries.h" +#include "llvm/Support/ErrorHandling.h" +#include + +#define GET_SUBTARGETINFO_HEADER +#include "MaxisGenSubtargetInfo.inc" + +namespace llvm { +class StringRef; + +class MaxisTargetMachine; + +class MaxisSubtarget : public MaxisGenSubtargetInfo { + virtual void anchor(); + + enum MaxisArchEnum { + MaxisDefault, + Maxis1, Maxis2, Maxis32, Maxis32r2, Maxis32r3, Maxis32r5, Maxis32r6, Maxis32Max, + Maxis3, Maxis4, Maxis5, Maxis64, Maxis64r2, Maxis64r3, Maxis64r5, Maxis64r6 + }; + + enum class CPU { P5600 }; + + // Maxis architecture version + MaxisArchEnum MaxisArchVersion; + + // Processor implementation (unused but required to exist by + // tablegen-erated code). + CPU ProcImpl; + + // IsLittle - The target is Little Endian + bool IsLittle; + + // IsSoftFloat - The target does not support any floating point instructions. + bool IsSoftFloat; + + // IsSingleFloat - The target only supports single precision float + // point operations. This enable the target to use all 32 32-bit + // floating point registers instead of only using even ones. + bool IsSingleFloat; + + // IsFPXX - MAXIS O32 modeless ABI. + bool IsFPXX; + + // NoABICalls - Disable SVR4-style position-independent code. + bool NoABICalls; + + // IsFP64bit - The target processor has 64-bit floating point registers. + bool IsFP64bit; + + /// Are odd single-precision registers permitted? + /// This corresponds to -modd-spreg and -mno-odd-spreg + bool UseOddSPReg; + + // IsNan2008 - IEEE 754-2008 NaN encoding. + bool IsNaN2008bit; + + // IsGP64bit - General-purpose registers are 64 bits wide + bool IsGP64bit; + + // IsPTR64bit - Pointers are 64 bit wide + bool IsPTR64bit; + + // HasVFPU - Processor has a vector floating point unit. + bool HasVFPU; + + // CPU supports cnMAXIS (Cavium Networks Octeon CPU). + bool HasCnMaxis; + + // isLinux - Target system is Linux. Is false we consider ELFOS for now. + bool IsLinux; + + // UseSmallSection - Small section is used. + bool UseSmallSection; + + /// Features related to the presence of specific instructions. + + // HasMaxis3_32 - The subset of MAXIS-III instructions added to MAXIS32 + bool HasMaxis3_32; + + // HasMaxis3_32r2 - The subset of MAXIS-III instructions added to MAXIS32r2 + bool HasMaxis3_32r2; + + // HasMaxis4_32 - Has the subset of MAXIS-IV present in MAXIS32 + bool HasMaxis4_32; + + // HasMaxis4_32r2 - Has the subset of MAXIS-IV present in MAXIS32r2 + bool HasMaxis4_32r2; + + // HasMaxis5_32r2 - Has the subset of MAXIS-V present in MAXIS32r2 + bool HasMaxis5_32r2; + + // InMaxis16 -- can process Maxis16 instructions + bool InMaxis16Mode; + + // Maxis16 hard float + bool InMaxis16HardFloat; + + // InMicroMaxis -- can process MicroMaxis instructions + bool InMicroMaxisMode; + + // HasDSP, HasDSPR2, HasDSPR3 -- supports DSP ASE. + bool HasDSP, HasDSPR2, HasDSPR3; + + // Allow mixed Maxis16 and Maxis32 in one source file + bool AllowMixed16_32; + + // Optimize for space by compiling all functions as Maxis 16 unless + // it needs floating point. Functions needing floating point are + // compiled as Maxis32 + bool Os16; + + // HasMSA -- supports MSA ASE. + bool HasMSA; + + // UseTCCInDIV -- Enables the use of trapping in the assembler. + bool UseTCCInDIV; + + // Sym32 -- On Maxis64 symbols are 32 bits. + bool HasSym32; + + // HasEVA -- supports EVA ASE. + bool HasEVA; + + // nomadd4 - disables generation of 4-operand madd.s, madd.d and + // related instructions. + bool DisableMadd4; + + // HasMT -- support MT ASE. + bool HasMT; + + // Disable use of the `jal` instruction. + bool UseLongCalls = false; + + /// 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; + + /// The overridden stack alignment. + unsigned StackAlignOverride; + + InstrItineraryData InstrItins; + + // We can override the determination of whether we are in maxis16 mode + // as from the command line + enum {NoOverride, Maxis16Override, NoMaxis16Override} OverrideMode; + + const MaxisTargetMachine &TM; + + Triple TargetTriple; + + const SelectionDAGTargetInfo TSInfo; + std::unique_ptr InstrInfo; + std::unique_ptr FrameLowering; + std::unique_ptr TLInfo; + +public: + bool isPositionIndependent() const; + /// This overrides the PostRAScheduler bit in the SchedModel for each CPU. + bool enablePostRAScheduler() const override; + void getCriticalPathRCs(RegClassVector &CriticalPathRCs) const override; + CodeGenOpt::Level getOptLevelToEnablePostRAScheduler() const override; + + bool isABI_N64() const; + bool isABI_N32() const; + bool isABI_O32() const; + const MaxisABIInfo &getABI() const; + bool isABI_FPXX() const { return isABI_O32() && IsFPXX; } + + /// This constructor initializes the data members to match that + /// of the specified triple. + MaxisSubtarget(const Triple &TT, StringRef CPU, StringRef FS, bool little, + const MaxisTargetMachine &TM, unsigned StackAlignOverride); + + /// ParseSubtargetFeatures - Parses features string setting specified + /// subtarget options. Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef FS); + + bool hasMaxis1() const { return MaxisArchVersion >= Maxis1; } + bool hasMaxis2() const { return MaxisArchVersion >= Maxis2; } + bool hasMaxis3() const { return MaxisArchVersion >= Maxis3; } + bool hasMaxis4() const { return MaxisArchVersion >= Maxis4; } + bool hasMaxis5() const { return MaxisArchVersion >= Maxis5; } + bool hasMaxis4_32() const { return HasMaxis4_32; } + bool hasMaxis4_32r2() const { return HasMaxis4_32r2; } + bool hasMaxis32() const { + return (MaxisArchVersion >= Maxis32 && MaxisArchVersion < Maxis32Max) || + hasMaxis64(); + } + bool hasMaxis32r2() const { + return (MaxisArchVersion >= Maxis32r2 && MaxisArchVersion < Maxis32Max) || + hasMaxis64r2(); + } + bool hasMaxis32r3() const { + return (MaxisArchVersion >= Maxis32r3 && MaxisArchVersion < Maxis32Max) || + hasMaxis64r2(); + } + bool hasMaxis32r5() const { + return (MaxisArchVersion >= Maxis32r5 && MaxisArchVersion < Maxis32Max) || + hasMaxis64r5(); + } + bool hasMaxis32r6() const { + return (MaxisArchVersion >= Maxis32r6 && MaxisArchVersion < Maxis32Max) || + hasMaxis64r6(); + } + bool hasMaxis64() const { return MaxisArchVersion >= Maxis64; } + bool hasMaxis64r2() const { return MaxisArchVersion >= Maxis64r2; } + bool hasMaxis64r3() const { return MaxisArchVersion >= Maxis64r3; } + bool hasMaxis64r5() const { return MaxisArchVersion >= Maxis64r5; } + bool hasMaxis64r6() const { return MaxisArchVersion >= Maxis64r6; } + + bool hasCnMaxis() const { return HasCnMaxis; } + + bool isLittle() const { return IsLittle; } + bool isABICalls() const { return !NoABICalls; } + bool isFPXX() const { return IsFPXX; } + bool isFP64bit() const { return IsFP64bit; } + bool useOddSPReg() const { return UseOddSPReg; } + bool noOddSPReg() const { return !UseOddSPReg; } + bool isNaN2008() const { return IsNaN2008bit; } + bool isGP64bit() const { return IsGP64bit; } + bool isGP32bit() const { return !IsGP64bit; } + unsigned getGPRSizeInBytes() const { return isGP64bit() ? 8 : 4; } + bool isPTR64bit() const { return IsPTR64bit; } + bool isPTR32bit() const { return !IsPTR64bit; } + bool hasSym32() const { + return (HasSym32 && isABI_N64()) || isABI_N32() || isABI_O32(); + } + bool isSingleFloat() const { return IsSingleFloat; } + bool isTargetELF() const { return TargetTriple.isOSBinFormatELF(); } + bool hasVFPU() const { return HasVFPU; } + bool inMaxis16Mode() const { return InMaxis16Mode; } + bool inMaxis16ModeDefault() const { + return InMaxis16Mode; + } + // Hard float for maxis16 means essentially to compile as soft float + // but to use a runtime library for soft float that is written with + // native maxis32 floating point instructions (those runtime routines + // run in maxis32 hard float mode). + bool inMaxis16HardFloat() const { + return inMaxis16Mode() && InMaxis16HardFloat; + } + bool inMicroMaxisMode() const { return InMicroMaxisMode; } + bool inMicroMaxis32r6Mode() const { return InMicroMaxisMode && hasMaxis32r6(); } + bool hasDSP() const { return HasDSP; } + bool hasDSPR2() const { return HasDSPR2; } + bool hasDSPR3() const { return HasDSPR3; } + bool hasMSA() const { return HasMSA; } + bool disableMadd4() const { return DisableMadd4; } + bool hasEVA() const { return HasEVA; } + bool hasMT() const { return HasMT; } + bool useSmallSection() const { return UseSmallSection; } + + bool hasStandardEncoding() const { return !inMaxis16Mode(); } + + bool useSoftFloat() const { return IsSoftFloat; } + + bool useLongCalls() const { return UseLongCalls; } + + bool enableLongBranchPass() const { + return hasStandardEncoding() || allowMixed16_32(); + } + + /// Features related to the presence of specific instructions. + bool hasExtractInsert() const { return !inMaxis16Mode() && hasMaxis32r2(); } + bool hasMTHC1() const { return hasMaxis32r2(); } + + bool allowMixed16_32() const { return inMaxis16ModeDefault() | + AllowMixed16_32; } + + bool os16() const { return Os16; } + + bool isTargetNaCl() const { return TargetTriple.isOSNaCl(); } + + bool isXRaySupported() const override { return true; } + + // for now constant islands are on for the whole compilation unit but we only + // really use them if in addition we are in maxis16 mode + static bool useConstantIslands(); + + unsigned getStackAlignment() const { return stackAlignment; } + + // Grab relocation model + Reloc::Model getRelocationModel() const; + + MaxisSubtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS, + const TargetMachine &TM); + + /// Does the system support unaligned memory access. + /// + /// MAXIS32r6/MAXIS64r6 require full unaligned access support but does not + /// specify which component of the system provides it. Hardware, software, and + /// hybrid implementations are all valid. + bool systemSupportsUnalignedAccess() const { return hasMaxis32r6(); } + + // Set helper classes + void setHelperClassesMaxis16(); + void setHelperClassesMaxisSE(); + + const SelectionDAGTargetInfo *getSelectionDAGInfo() const override { + return &TSInfo; + } + const MaxisInstrInfo *getInstrInfo() const override { return InstrInfo.get(); } + const TargetFrameLowering *getFrameLowering() const override { + return FrameLowering.get(); + } + const MaxisRegisterInfo *getRegisterInfo() const override { + return &InstrInfo->getRegisterInfo(); + } + const MaxisTargetLowering *getTargetLowering() const override { + return TLInfo.get(); + } + const InstrItineraryData *getInstrItineraryData() const override { + return &InstrItins; + } +}; +} // End llvm namespace + +#endif diff --git a/lib/Target/Maxis/MaxisTargetMachine.cpp b/lib/Target/Maxis/MaxisTargetMachine.cpp new file mode 100644 index 00000000..322fe847 --- /dev/null +++ b/lib/Target/Maxis/MaxisTargetMachine.cpp @@ -0,0 +1,287 @@ +//===-- MaxisTargetMachine.cpp - Define TargetMachine for Maxis -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the info about Maxis target spec. +// +//===----------------------------------------------------------------------===// + +#include "MaxisTargetMachine.h" +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MCTargetDesc/MaxisMCTargetDesc.h" +#include "Maxis.h" +#include "Maxis16ISelDAGToDAG.h" +#include "MaxisSEISelDAGToDAG.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetObjectFile.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/BasicTTIImpl.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetOptions.h" +#include + +using namespace llvm; + +#define DEBUG_TYPE "maxis" + +extern "C" void LLVMInitializeMaxisTarget() { + // Register the target. + RegisterTargetMachine X(getTheMaxisTarget()); + RegisterTargetMachine Y(getTheMaxiselTarget()); + RegisterTargetMachine A(getTheMaxis64Target()); + RegisterTargetMachine B(getTheMaxis64elTarget()); +} + +static std::string computeDataLayout(const Triple &TT, StringRef CPU, + const TargetOptions &Options, + bool isLittle) { + std::string Ret; + MaxisABIInfo ABI = MaxisABIInfo::computeTargetABI(TT, CPU, Options.MCOptions); + + // There are both little and big endian maxis. + if (isLittle) + Ret += "e"; + else + Ret += "E"; + + if (ABI.IsO32()) + Ret += "-m:m"; + else + Ret += "-m:e"; + + // Pointers are 32 bit on some ABIs. + if (!ABI.IsN64()) + Ret += "-p:32:32"; + + // 8 and 16 bit integers only need to have natural alignment, but try to + // align them to 32 bits. 64 bit integers have natural alignment. + Ret += "-i8:8:32-i16:16:32-i64:64"; + + // 32 bit registers are always available and the stack is at least 64 bit + // aligned. On N64 64 bit registers are also available and the stack is + // 128 bit aligned. + if (ABI.IsN64() || ABI.IsN32()) + Ret += "-n32:64-S128"; + else + Ret += "-n32-S64"; + + return Ret; +} + +static Reloc::Model getEffectiveRelocModel(bool JIT, + Optional RM) { + if (!RM.hasValue() || JIT) + return Reloc::Static; + return *RM; +} + +static CodeModel::Model getEffectiveCodeModel(Optional CM) { + if (CM) + return *CM; + return CodeModel::Small; +} + +// On function prologue, the stack is created by decrementing +// its pointer. Once decremented, all references are done with positive +// offset from the stack/frame pointer, using StackGrowsUp enables +// an easier handling. +// Using CodeModel::Large enables different CALL behavior. +MaxisTargetMachine::MaxisTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT, + bool isLittle) + : LLVMTargetMachine(T, computeDataLayout(TT, CPU, Options, isLittle), TT, + CPU, FS, Options, getEffectiveRelocModel(JIT, RM), + getEffectiveCodeModel(CM), OL), + isLittle(isLittle), TLOF(llvm::make_unique()), + ABI(MaxisABIInfo::computeTargetABI(TT, CPU, Options.MCOptions)), + Subtarget(nullptr), DefaultSubtarget(TT, CPU, FS, isLittle, *this, + Options.StackAlignmentOverride), + NoMaxis16Subtarget(TT, CPU, FS.empty() ? "-maxis16" : FS.str() + ",-maxis16", + isLittle, *this, Options.StackAlignmentOverride), + Maxis16Subtarget(TT, CPU, FS.empty() ? "+maxis16" : FS.str() + ",+maxis16", + isLittle, *this, Options.StackAlignmentOverride) { + Subtarget = &DefaultSubtarget; + initAsmInfo(); +} + +MaxisTargetMachine::~MaxisTargetMachine() = default; + +void MaxisebTargetMachine::anchor() {} + +MaxisebTargetMachine::MaxisebTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT) + : MaxisTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, false) {} + +void MaxiselTargetMachine::anchor() {} + +MaxiselTargetMachine::MaxiselTargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT) + : MaxisTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, true) {} + +const MaxisSubtarget * +MaxisTargetMachine::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; + bool hasMaxis16Attr = + !F.getFnAttribute("maxis16").hasAttribute(Attribute::None); + bool hasNoMaxis16Attr = + !F.getFnAttribute("nomaxis16").hasAttribute(Attribute::None); + + bool HasMicroMaxisAttr = + !F.getFnAttribute("micromaxis").hasAttribute(Attribute::None); + bool HasNoMicroMaxisAttr = + !F.getFnAttribute("nomicromaxis").hasAttribute(Attribute::None); + + // FIXME: This is related to the code below to reset the target options, + // we need to know whether or not the soft float flag is set on the + // function, so we can enable it as a subtarget feature. + bool softFloat = + F.hasFnAttribute("use-soft-float") && + F.getFnAttribute("use-soft-float").getValueAsString() == "true"; + + if (hasMaxis16Attr) + FS += FS.empty() ? "+maxis16" : ",+maxis16"; + else if (hasNoMaxis16Attr) + FS += FS.empty() ? "-maxis16" : ",-maxis16"; + if (HasMicroMaxisAttr) + FS += FS.empty() ? "+micromaxis" : ",+micromaxis"; + else if (HasNoMicroMaxisAttr) + FS += FS.empty() ? "-micromaxis" : ",-micromaxis"; + if (softFloat) + FS += FS.empty() ? "+soft-float" : ",+soft-float"; + + 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, isLittle, *this, + Options.StackAlignmentOverride); + } + return I.get(); +} + +void MaxisTargetMachine::resetSubtarget(MachineFunction *MF) { + DEBUG(dbgs() << "resetSubtarget\n"); + + Subtarget = const_cast(getSubtargetImpl(MF->getFunction())); + MF->setSubtarget(Subtarget); +} + +namespace { + +/// Maxis Code Generator Pass Configuration Options. +class MaxisPassConfig : public TargetPassConfig { +public: + MaxisPassConfig(MaxisTargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) { + // The current implementation of long branch pass requires a scratch + // register ($at) to be available before branch instructions. Tail merging + // can break this requirement, so disable it when long branch pass is + // enabled. + EnableTailMerge = !getMaxisSubtarget().enableLongBranchPass(); + } + + MaxisTargetMachine &getMaxisTargetMachine() const { + return getTM(); + } + + const MaxisSubtarget &getMaxisSubtarget() const { + return *getMaxisTargetMachine().getSubtargetImpl(); + } + + void addIRPasses() override; + bool addInstSelector() override; + void addPreEmitPass() override; + void addPreRegAlloc() override; +}; + +} // end anonymous namespace + +TargetPassConfig *MaxisTargetMachine::createPassConfig(PassManagerBase &PM) { + return new MaxisPassConfig(*this, PM); +} + +void MaxisPassConfig::addIRPasses() { + TargetPassConfig::addIRPasses(); + addPass(createAtomicExpandPass()); + if (getMaxisSubtarget().os16()) + addPass(createMaxisOs16Pass()); + if (getMaxisSubtarget().inMaxis16HardFloat()) + addPass(createMaxis16HardFloatPass()); +} +// Install an instruction selector pass using +// the ISelDag to gen Maxis code. +bool MaxisPassConfig::addInstSelector() { + addPass(createMaxisModuleISelDagPass()); + addPass(createMaxis16ISelDag(getMaxisTargetMachine(), getOptLevel())); + addPass(createMaxisSEISelDag(getMaxisTargetMachine(), getOptLevel())); + return false; +} + +void MaxisPassConfig::addPreRegAlloc() { + addPass(createMaxisOptimizePICCallPass()); +} + +TargetTransformInfo +MaxisTargetMachine::getTargetTransformInfo(const Function &F) { + if (Subtarget->allowMixed16_32()) { + DEBUG(errs() << "No Target Transform Info Pass Added\n"); + // FIXME: This is no longer necessary as the TTI returned is per-function. + return TargetTransformInfo(F.getParent()->getDataLayout()); + } + + DEBUG(errs() << "Target Transform Info Pass Added\n"); + return TargetTransformInfo(BasicTTIImpl(this, F)); +} + +// Implemented by targets that want to run passes immediately before +// machine code is emitted. return true if -print-machineinstrs should +// print out the code after the passes. +void MaxisPassConfig::addPreEmitPass() { + addPass(createMicroMaxisSizeReductionPass()); + + // The delay slot filler and the long branch passes can potientially create + // forbidden slot/ hazards for MAXISR6 which the hazard schedule pass will + // fix. Any new pass must come before the hazard schedule pass. + addPass(createMaxisDelaySlotFillerPass()); + addPass(createMaxisLongBranchPass()); + addPass(createMaxisHazardSchedule()); + addPass(createMaxisConstantIslandPass()); +} diff --git a/lib/Target/Maxis/MaxisTargetMachine.h b/lib/Target/Maxis/MaxisTargetMachine.h new file mode 100644 index 00000000..8695c3a8 --- /dev/null +++ b/lib/Target/Maxis/MaxisTargetMachine.h @@ -0,0 +1,101 @@ +//===- MaxisTargetMachine.h - Define TargetMachine for Maxis ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the Maxis specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISTARGETMACHINE_H +#define LLVM_LIB_TARGET_MAXIS_MAXISTARGETMACHINE_H + +#include "MCTargetDesc/MaxisABIInfo.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CodeGen.h" +#include "llvm/Target/TargetMachine.h" +#include + +namespace llvm { + +class MaxisTargetMachine : public LLVMTargetMachine { + bool isLittle; + std::unique_ptr TLOF; + // Selected ABI + MaxisABIInfo ABI; + MaxisSubtarget *Subtarget; + MaxisSubtarget DefaultSubtarget; + MaxisSubtarget NoMaxis16Subtarget; + MaxisSubtarget Maxis16Subtarget; + + mutable StringMap> SubtargetMap; + +public: + MaxisTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT, bool isLittle); + ~MaxisTargetMachine() override; + + TargetTransformInfo getTargetTransformInfo(const Function &F) override; + + const MaxisSubtarget *getSubtargetImpl() const { + if (Subtarget) + return Subtarget; + return &DefaultSubtarget; + } + + const MaxisSubtarget *getSubtargetImpl(const Function &F) const override; + + /// \brief Reset the subtarget for the Maxis target. + void resetSubtarget(MachineFunction *MF); + + // Pass Pipeline Configuration + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } + + bool isLittleEndian() const { return isLittle; } + const MaxisABIInfo &getABI() const { return ABI; } + + bool isMachineVerifierClean() const override { + return false; + } +}; + +/// Maxis32/64 big endian target machine. +/// +class MaxisebTargetMachine : public MaxisTargetMachine { + virtual void anchor(); + +public: + MaxisebTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT); +}; + +/// Maxis32/64 little endian target machine. +/// +class MaxiselTargetMachine : public MaxisTargetMachine { + virtual void anchor(); + +public: + MaxiselTargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT); +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_MAXIS_MAXISTARGETMACHINE_H diff --git a/lib/Target/Maxis/MaxisTargetObjectFile.cpp b/lib/Target/Maxis/MaxisTargetObjectFile.cpp new file mode 100644 index 00000000..ceb600ce --- /dev/null +++ b/lib/Target/Maxis/MaxisTargetObjectFile.cpp @@ -0,0 +1,194 @@ +//===-- MaxisTargetObjectFile.cpp - Maxis Object Files ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisTargetObjectFile.h" +#include "MaxisSubtarget.h" +#include "MaxisTargetMachine.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("maxis-ssection-threshold", cl::Hidden, + cl::desc("Small data and bss section threshold size (default=8)"), + cl::init(8)); + +static cl::opt +LocalSData("mlocal-sdata", cl::Hidden, + cl::desc("MAXIS: Use gp_rel for object-local data."), + cl::init(true)); + +static cl::opt +ExternSData("mextern-sdata", cl::Hidden, + cl::desc("MAXIS: Use gp_rel for data that is not defined by the " + "current object."), + cl::init(true)); + +static cl::opt +EmbeddedData("membedded-data", cl::Hidden, + cl::desc("MAXIS: Try to allocate variables in the following" + " sections if possible: .rodata, .sdata, .data ."), + cl::init(false)); + +void MaxisTargetObjectFile::Initialize(MCContext &Ctx, const TargetMachine &TM){ + TargetLoweringObjectFileELF::Initialize(Ctx, TM); + InitializeELF(TM.Options.UseInitArray); + + SmallDataSection = getContext().getELFSection( + ".sdata", ELF::SHT_PROGBITS, + ELF::SHF_WRITE | ELF::SHF_ALLOC | ELF::SHF_MAXIS_GPREL); + + SmallBSSSection = getContext().getELFSection(".sbss", ELF::SHT_NOBITS, + ELF::SHF_WRITE | ELF::SHF_ALLOC | + ELF::SHF_MAXIS_GPREL); + this->TM = &static_cast(TM); +} + +// A address must be loaded from a small section if its size is less than the +// small section size threshold. Data in this section must be addressed using +// gp_rel operator. +static bool IsInSmallSection(uint64_t Size) { + // gcc has traditionally not treated zero-sized objects as small data, so this + // is effectively part of the ABI. + return Size > 0 && Size <= SSThreshold; +} + +/// Return true if this global address should be placed into small data/bss +/// section. +bool MaxisTargetObjectFile::IsGlobalInSmallSection( + const GlobalObject *GO, const TargetMachine &TM) const { + // We first check the case where global is a declaration, because finding + // section kind using getKindForGlobal() is only allowed for global + // definitions. + if (GO->isDeclaration() || GO->hasAvailableExternallyLinkage()) + return IsGlobalInSmallSectionImpl(GO, TM); + + return IsGlobalInSmallSection(GO, TM, getKindForGlobal(GO, TM)); +} + +/// Return true if this global address should be placed into small data/bss +/// section. +bool MaxisTargetObjectFile:: +IsGlobalInSmallSection(const GlobalObject *GO, const TargetMachine &TM, + SectionKind Kind) const { + return IsGlobalInSmallSectionImpl(GO, TM) && + (Kind.isData() || Kind.isBSS() || Kind.isCommon() || + Kind.isReadOnly()); +} + +/// Return true if this global address should be placed into small data/bss +/// section. This method does all the work, except for checking the section +/// kind. +bool MaxisTargetObjectFile:: +IsGlobalInSmallSectionImpl(const GlobalObject *GO, + const TargetMachine &TM) const { + const MaxisSubtarget &Subtarget = + *static_cast(TM).getSubtargetImpl(); + + // Return if small section is not available. + if (!Subtarget.useSmallSection()) + return false; + + // Only global variables, not functions. + const GlobalVariable *GVA = dyn_cast(GO); + if (!GVA) + return false; + + // If the variable has an explicit section, it is placed in that section but + // it's addressing mode may change. + if (GVA->hasSection()) { + StringRef Section = GVA->getSection(); + + // Explicitly placing any variable in the small data section overrides + // the global -G value. + if (Section == ".sdata" || Section == ".sbss") + return true; + + // Otherwise reject accessing it through the gp pointer. There are some + // historic cases which GCC doesn't appear to respect any more. These + // are .lit4, .lit8 and .srdata. For the moment reject these as well. + return false; + } + + // Enforce -mlocal-sdata. + if (!LocalSData && GVA->hasLocalLinkage()) + return false; + + // Enforce -mextern-sdata. + if (!ExternSData && ((GVA->hasExternalLinkage() && GVA->isDeclaration()) || + GVA->hasCommonLinkage())) + return false; + + // Enforce -membedded-data. + if (EmbeddedData && GVA->isConstant()) + return false; + + Type *Ty = GVA->getValueType(); + + // It is possible that the type of the global is unsized, i.e. a declaration + // of a extern struct. In this case don't presume it is in the small data + // section. This happens e.g. when building the FreeBSD kernel. + if (!Ty->isSized()) + return false; + + return IsInSmallSection( + GVA->getParent()->getDataLayout().getTypeAllocSize(Ty)); +} + +MCSection *MaxisTargetObjectFile::SelectSectionForGlobal( + const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + // TODO: Could also support "weak" symbols as well with ".gnu.linkonce.s.*" + // sections? + + // Handle Small Section classification here. + if (Kind.isBSS() && IsGlobalInSmallSection(GO, TM, Kind)) + return SmallBSSSection; + if (Kind.isData() && IsGlobalInSmallSection(GO, TM, Kind)) + return SmallDataSection; + if (Kind.isReadOnly() && IsGlobalInSmallSection(GO, TM, Kind)) + return SmallDataSection; + + // Otherwise, we work the same as ELF. + return TargetLoweringObjectFileELF::SelectSectionForGlobal(GO, Kind, TM); +} + +/// Return true if this constant should be placed into small data section. +bool MaxisTargetObjectFile::IsConstantInSmallSection( + const DataLayout &DL, const Constant *CN, const TargetMachine &TM) const { + return (static_cast(TM) + .getSubtargetImpl() + ->useSmallSection() && + LocalSData && IsInSmallSection(DL.getTypeAllocSize(CN->getType()))); +} + +/// Return true if this constant should be placed into small data section. +MCSection *MaxisTargetObjectFile::getSectionForConstant(const DataLayout &DL, + SectionKind Kind, + const Constant *C, + unsigned &Align) const { + if (IsConstantInSmallSection(DL, C, *TM)) + return SmallDataSection; + + // Otherwise, we work the same as ELF. + return TargetLoweringObjectFileELF::getSectionForConstant(DL, Kind, C, Align); +} + +const MCExpr * +MaxisTargetObjectFile::getDebugThreadLocalSymbol(const MCSymbol *Sym) const { + const MCExpr *Expr = + MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); + return MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(0x8000, getContext()), getContext()); +} diff --git a/lib/Target/Maxis/MaxisTargetObjectFile.h b/lib/Target/Maxis/MaxisTargetObjectFile.h new file mode 100644 index 00000000..8f7c2e44 --- /dev/null +++ b/lib/Target/Maxis/MaxisTargetObjectFile.h @@ -0,0 +1,50 @@ +//===-- llvm/Target/MaxisTargetObjectFile.h - Maxis Object Info ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISTARGETOBJECTFILE_H +#define LLVM_LIB_TARGET_MAXIS_MAXISTARGETOBJECTFILE_H + +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" + +namespace llvm { +class MaxisTargetMachine; + class MaxisTargetObjectFile : public TargetLoweringObjectFileELF { + MCSection *SmallDataSection; + MCSection *SmallBSSSection; + const MaxisTargetMachine *TM; + + bool IsGlobalInSmallSection(const GlobalObject *GO, const TargetMachine &TM, + SectionKind Kind) const; + bool IsGlobalInSmallSectionImpl(const GlobalObject *GO, + const TargetMachine &TM) const; + public: + + void Initialize(MCContext &Ctx, const TargetMachine &TM) override; + + /// Return true if this global address should be placed into small data/bss + /// section. + bool IsGlobalInSmallSection(const GlobalObject *GO, + const TargetMachine &TM) const; + + MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind, + const TargetMachine &TM) const override; + + /// Return true if this constant should be placed into small data section. + bool IsConstantInSmallSection(const DataLayout &DL, const Constant *CN, + const TargetMachine &TM) const; + + MCSection *getSectionForConstant(const DataLayout &DL, SectionKind Kind, + const Constant *C, + unsigned &Align) const override; + /// Describe a TLS variable address within debug info. + const MCExpr *getDebugThreadLocalSymbol(const MCSymbol *Sym) const override; + }; +} // end namespace llvm + +#endif diff --git a/lib/Target/Maxis/MaxisTargetStreamer.h b/lib/Target/Maxis/MaxisTargetStreamer.h new file mode 100644 index 00000000..a56178fa --- /dev/null +++ b/lib/Target/Maxis/MaxisTargetStreamer.h @@ -0,0 +1,335 @@ +//===-- MaxisTargetStreamer.h - Maxis Target Streamer ------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_MAXIS_MAXISTARGETSTREAMER_H +#define LLVM_LIB_TARGET_MAXIS_MAXISTARGETSTREAMER_H + +#include "MCTargetDesc/MaxisABIFlagsSection.h" +#include "MCTargetDesc/MaxisABIInfo.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" + +namespace llvm { + +struct MaxisABIFlagsSection; + +class MaxisTargetStreamer : public MCTargetStreamer { +public: + MaxisTargetStreamer(MCStreamer &S); + + virtual void setPic(bool Value) {} + + virtual void emitDirectiveSetMicroMaxis(); + virtual void emitDirectiveSetNoMicroMaxis(); + virtual void setUsesMicroMaxis(); + virtual void emitDirectiveSetMaxis16(); + virtual void emitDirectiveSetNoMaxis16(); + + virtual void emitDirectiveSetReorder(); + virtual void emitDirectiveSetNoReorder(); + virtual void emitDirectiveSetMacro(); + virtual void emitDirectiveSetNoMacro(); + virtual void emitDirectiveSetMsa(); + virtual void emitDirectiveSetNoMsa(); + virtual void emitDirectiveSetMt(); + virtual void emitDirectiveSetNoMt(); + virtual void emitDirectiveSetAt(); + virtual void emitDirectiveSetAtWithArg(unsigned RegNo); + virtual void emitDirectiveSetNoAt(); + virtual void emitDirectiveEnd(StringRef Name); + + virtual void emitDirectiveEnt(const MCSymbol &Symbol); + virtual void emitDirectiveAbiCalls(); + virtual void emitDirectiveNaN2008(); + virtual void emitDirectiveNaNLegacy(); + virtual void emitDirectiveOptionPic0(); + virtual void emitDirectiveOptionPic2(); + virtual void emitDirectiveInsn(); + virtual void emitFrame(unsigned StackReg, unsigned StackSize, + unsigned ReturnReg); + virtual void emitMask(unsigned CPUBitmask, int CPUTopSavedRegOff); + virtual void emitFMask(unsigned FPUBitmask, int FPUTopSavedRegOff); + + virtual void emitDirectiveSetArch(StringRef Arch); + virtual void emitDirectiveSetMaxis0(); + virtual void emitDirectiveSetMaxis1(); + virtual void emitDirectiveSetMaxis2(); + virtual void emitDirectiveSetMaxis3(); + virtual void emitDirectiveSetMaxis4(); + virtual void emitDirectiveSetMaxis5(); + virtual void emitDirectiveSetMaxis32(); + virtual void emitDirectiveSetMaxis32R2(); + virtual void emitDirectiveSetMaxis32R3(); + virtual void emitDirectiveSetMaxis32R5(); + virtual void emitDirectiveSetMaxis32R6(); + virtual void emitDirectiveSetMaxis64(); + virtual void emitDirectiveSetMaxis64R2(); + virtual void emitDirectiveSetMaxis64R3(); + virtual void emitDirectiveSetMaxis64R5(); + virtual void emitDirectiveSetMaxis64R6(); + virtual void emitDirectiveSetDsp(); + virtual void emitDirectiveSetDspr2(); + virtual void emitDirectiveSetNoDsp(); + virtual void emitDirectiveSetPop(); + virtual void emitDirectiveSetPush(); + virtual void emitDirectiveSetSoftFloat(); + virtual void emitDirectiveSetHardFloat(); + + // PIC support + virtual void emitDirectiveCpLoad(unsigned RegNo); + virtual bool emitDirectiveCpRestore(int Offset, + function_ref GetATReg, + SMLoc IDLoc, const MCSubtargetInfo *STI); + virtual void emitDirectiveCpsetup(unsigned RegNo, int RegOrOffset, + const MCSymbol &Sym, bool IsReg); + virtual void emitDirectiveCpreturn(unsigned SaveLocation, + bool SaveLocationIsRegister); + + // FP abiflags directives + virtual void emitDirectiveModuleFP(); + virtual void emitDirectiveModuleOddSPReg(); + virtual void emitDirectiveModuleSoftFloat(); + virtual void emitDirectiveModuleHardFloat(); + virtual void emitDirectiveModuleMT(); + virtual void emitDirectiveSetFp(MaxisABIFlagsSection::FpABIKind Value); + virtual void emitDirectiveSetOddSPReg(); + virtual void emitDirectiveSetNoOddSPReg(); + + void emitR(unsigned Opcode, unsigned Reg0, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitII(unsigned Opcode, int16_t Imm1, int16_t Imm2, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitRX(unsigned Opcode, unsigned Reg0, MCOperand Op1, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitRI(unsigned Opcode, unsigned Reg0, int32_t Imm, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitRR(unsigned Opcode, unsigned Reg0, unsigned Reg1, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitRRX(unsigned Opcode, unsigned Reg0, unsigned Reg1, MCOperand Op2, + SMLoc IDLoc, const MCSubtargetInfo *STI); + void emitRRR(unsigned Opcode, unsigned Reg0, unsigned Reg1, unsigned Reg2, + SMLoc IDLoc, const MCSubtargetInfo *STI); + void emitRRI(unsigned Opcode, unsigned Reg0, unsigned Reg1, int16_t Imm, + SMLoc IDLoc, const MCSubtargetInfo *STI); + void emitRRIII(unsigned Opcode, unsigned Reg0, unsigned Reg1, int16_t Imm0, + int16_t Imm1, int16_t Imm2, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitAddu(unsigned DstReg, unsigned SrcReg, unsigned TrgReg, bool Is64Bit, + const MCSubtargetInfo *STI); + void emitDSLL(unsigned DstReg, unsigned SrcReg, int16_t ShiftAmount, + SMLoc IDLoc, const MCSubtargetInfo *STI); + void emitEmptyDelaySlot(bool hasShortDelaySlot, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitNop(SMLoc IDLoc, const MCSubtargetInfo *STI); + + /// Emit a store instruction with an offset. If the offset is out of range + /// then it will be synthesized using the assembler temporary. + /// + /// GetATReg() is a callback that can be used to obtain the current assembler + /// temporary and is only called when the assembler temporary is required. It + /// must handle the case where no assembler temporary is available (typically + /// by reporting an error). + void emitStoreWithImmOffset(unsigned Opcode, unsigned SrcReg, + unsigned BaseReg, int64_t Offset, + function_ref GetATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitStoreWithSymOffset(unsigned Opcode, unsigned SrcReg, + unsigned BaseReg, MCOperand &HiOperand, + MCOperand &LoOperand, unsigned ATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitLoadWithImmOffset(unsigned Opcode, unsigned DstReg, unsigned BaseReg, + int64_t Offset, unsigned TmpReg, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitLoadWithSymOffset(unsigned Opcode, unsigned DstReg, unsigned BaseReg, + MCOperand &HiOperand, MCOperand &LoOperand, + unsigned ATReg, SMLoc IDLoc, + const MCSubtargetInfo *STI); + void emitGPRestore(int Offset, SMLoc IDLoc, const MCSubtargetInfo *STI); + + void forbidModuleDirective() { ModuleDirectiveAllowed = false; } + void reallowModuleDirective() { ModuleDirectiveAllowed = true; } + bool isModuleDirectiveAllowed() { return ModuleDirectiveAllowed; } + + // This method enables template classes to set internal abi flags + // structure values. + template + void updateABIInfo(const PredicateLibrary &P) { + ABI = P.getABI(); + ABIFlagsSection.setAllFromPredicates(P); + } + + MaxisABIFlagsSection &getABIFlagsSection() { return ABIFlagsSection; } + const MaxisABIInfo &getABI() const { + assert(ABI.hasValue() && "ABI hasn't been set!"); + return *ABI; + } + +protected: + llvm::Optional ABI; + MaxisABIFlagsSection ABIFlagsSection; + + bool GPRInfoSet; + unsigned GPRBitMask; + int GPROffset; + + bool FPRInfoSet; + unsigned FPRBitMask; + int FPROffset; + + bool FrameInfoSet; + int FrameOffset; + unsigned FrameReg; + unsigned ReturnReg; + +private: + bool ModuleDirectiveAllowed; +}; + +// This part is for ascii assembly output +class MaxisTargetAsmStreamer : public MaxisTargetStreamer { + formatted_raw_ostream &OS; + +public: + MaxisTargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); + void emitDirectiveSetMicroMaxis() override; + void emitDirectiveSetNoMicroMaxis() override; + void emitDirectiveSetMaxis16() override; + void emitDirectiveSetNoMaxis16() override; + + void emitDirectiveSetReorder() override; + void emitDirectiveSetNoReorder() override; + void emitDirectiveSetMacro() override; + void emitDirectiveSetNoMacro() override; + void emitDirectiveSetMsa() override; + void emitDirectiveSetNoMsa() override; + void emitDirectiveSetMt() override; + void emitDirectiveSetNoMt() override; + void emitDirectiveSetAt() override; + void emitDirectiveSetAtWithArg(unsigned RegNo) override; + void emitDirectiveSetNoAt() override; + void emitDirectiveEnd(StringRef Name) override; + + void emitDirectiveEnt(const MCSymbol &Symbol) override; + void emitDirectiveAbiCalls() override; + void emitDirectiveNaN2008() override; + void emitDirectiveNaNLegacy() override; + void emitDirectiveOptionPic0() override; + void emitDirectiveOptionPic2() override; + void emitDirectiveInsn() override; + void emitFrame(unsigned StackReg, unsigned StackSize, + unsigned ReturnReg) override; + void emitMask(unsigned CPUBitmask, int CPUTopSavedRegOff) override; + void emitFMask(unsigned FPUBitmask, int FPUTopSavedRegOff) override; + + void emitDirectiveSetArch(StringRef Arch) override; + void emitDirectiveSetMaxis0() override; + void emitDirectiveSetMaxis1() override; + void emitDirectiveSetMaxis2() override; + void emitDirectiveSetMaxis3() override; + void emitDirectiveSetMaxis4() override; + void emitDirectiveSetMaxis5() override; + void emitDirectiveSetMaxis32() override; + void emitDirectiveSetMaxis32R2() override; + void emitDirectiveSetMaxis32R3() override; + void emitDirectiveSetMaxis32R5() override; + void emitDirectiveSetMaxis32R6() override; + void emitDirectiveSetMaxis64() override; + void emitDirectiveSetMaxis64R2() override; + void emitDirectiveSetMaxis64R3() override; + void emitDirectiveSetMaxis64R5() override; + void emitDirectiveSetMaxis64R6() override; + void emitDirectiveSetDsp() override; + void emitDirectiveSetDspr2() override; + void emitDirectiveSetNoDsp() override; + void emitDirectiveSetPop() override; + void emitDirectiveSetPush() override; + void emitDirectiveSetSoftFloat() override; + void emitDirectiveSetHardFloat() override; + + // PIC support + void emitDirectiveCpLoad(unsigned RegNo) override; + + /// Emit a .cprestore directive. If the offset is out of range then it will + /// be synthesized using the assembler temporary. + /// + /// GetATReg() is a callback that can be used to obtain the current assembler + /// temporary and is only called when the assembler temporary is required. It + /// must handle the case where no assembler temporary is available (typically + /// by reporting an error). + bool emitDirectiveCpRestore(int Offset, function_ref GetATReg, + SMLoc IDLoc, const MCSubtargetInfo *STI) override; + void emitDirectiveCpsetup(unsigned RegNo, int RegOrOffset, + const MCSymbol &Sym, bool IsReg) override; + void emitDirectiveCpreturn(unsigned SaveLocation, + bool SaveLocationIsRegister) override; + + // FP abiflags directives + void emitDirectiveModuleFP() override; + void emitDirectiveModuleOddSPReg() override; + void emitDirectiveModuleSoftFloat() override; + void emitDirectiveModuleHardFloat() override; + void emitDirectiveModuleMT() override; + void emitDirectiveSetFp(MaxisABIFlagsSection::FpABIKind Value) override; + void emitDirectiveSetOddSPReg() override; + void emitDirectiveSetNoOddSPReg() override; +}; + +// This part is for ELF object output +class MaxisTargetELFStreamer : public MaxisTargetStreamer { + bool MicroMaxisEnabled; + const MCSubtargetInfo &STI; + bool Pic; + +public: + bool isMicroMaxisEnabled() const { return MicroMaxisEnabled; } + MCELFStreamer &getStreamer(); + MaxisTargetELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI); + + void setPic(bool Value) override { Pic = Value; } + + void emitLabel(MCSymbol *Symbol) override; + void emitAssignment(MCSymbol *Symbol, const MCExpr *Value) override; + void finish() override; + + void emitDirectiveSetMicroMaxis() override; + void emitDirectiveSetNoMicroMaxis() override; + void setUsesMicroMaxis() override; + void emitDirectiveSetMaxis16() override; + + void emitDirectiveSetNoReorder() override; + void emitDirectiveEnd(StringRef Name) override; + + void emitDirectiveEnt(const MCSymbol &Symbol) override; + void emitDirectiveAbiCalls() override; + void emitDirectiveNaN2008() override; + void emitDirectiveNaNLegacy() override; + void emitDirectiveOptionPic0() override; + void emitDirectiveOptionPic2() override; + void emitDirectiveInsn() override; + void emitFrame(unsigned StackReg, unsigned StackSize, + unsigned ReturnReg) override; + void emitMask(unsigned CPUBitmask, int CPUTopSavedRegOff) override; + void emitFMask(unsigned FPUBitmask, int FPUTopSavedRegOff) override; + + // PIC support + void emitDirectiveCpLoad(unsigned RegNo) override; + bool emitDirectiveCpRestore(int Offset, function_ref GetATReg, + SMLoc IDLoc, const MCSubtargetInfo *STI) override; + void emitDirectiveCpsetup(unsigned RegNo, int RegOrOffset, + const MCSymbol &Sym, bool IsReg) override; + void emitDirectiveCpreturn(unsigned SaveLocation, + bool SaveLocationIsRegister) override; + + void emitMaxisAbiFlags(); +}; +} +#endif diff --git a/lib/Target/Maxis/MicroMaxis32r6InstrFormats.td b/lib/Target/Maxis/MicroMaxis32r6InstrFormats.td new file mode 100644 index 00000000..45824442 --- /dev/null +++ b/lib/Target/Maxis/MicroMaxis32r6InstrFormats.td @@ -0,0 +1,1109 @@ +//=- MicroMaxis32r6InstrFormats.td - Maxis32r6 Instruction Formats -*- tablegen -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes microMAXIS32r6 instruction formats. +// +//===----------------------------------------------------------------------===// + +class MMR6Arch { + string Arch = "micromaxisr6"; + string BaseOpcode = opstr; + string DecoderNamespace = "MicroMaxisR6"; +} + +// Class used for microMAXIS32r6 instructions. +class MicroMaxisR6Inst16 : PredicateControl { + string DecoderNamespace = "MicroMaxisR6"; + let InsnPredicates = [HasMicroMaxis32r6]; +} + +//===----------------------------------------------------------------------===// +// +// Disambiguators +// +//===----------------------------------------------------------------------===// +// +// Some encodings are ambiguous except by comparing field values. + +class MMDecodeDisambiguatedBy : DecodeDisambiguates { + string DecoderNamespace = "MicroMaxisR6_Ambiguous"; +} + +//===----------------------------------------------------------------------===// +// +// Encoding Formats +// +//===----------------------------------------------------------------------===// + +class BC16_FM_MM16R6 { + bits<10> offset; + + bits<16> Inst; + + let Inst{15-10} = 0x33; + let Inst{9-0} = offset; +} + +class BEQZC_BNEZC_FM_MM16R6 op> : MicroMaxisR6Inst16 { + bits<3> rs; + bits<7> offset; + + bits<16> Inst; + + let Inst{15-10} = op; + let Inst{9-7} = rs; + let Inst{6-0} = offset; +} + +class POOL16C_JALRC_FM_MM16R6 op> { + bits<5> rs; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-5} = rs; + let Inst{4-0} = op; +} + +class POP35_BOVC_FM_MMR6 : MaxisR6Inst, MMR6Arch { + bits<5> rt; + bits<5> rs; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 0b011101; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-0} = offset; +} + +class POP37_BNVC_FM_MMR6 : MaxisR6Inst, MMR6Arch { + bits<5> rt; + bits<5> rs; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 0b011111; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-0} = offset; +} + +class POOL16C_JRCADDIUSP_FM_MM16R6 op> { + bits<5> imm; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-5} = imm; + let Inst{4-0} = op; +} + +class POOL16C_LWM_SWM_FM_MM16R6 funct> { + bits<2> rt; + bits<4> addr; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-8} = rt; + let Inst{7-4} = addr; + let Inst{3-0} = funct; +} + +class POOL32A_BITSWAP_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rd; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rd; + let Inst{15-12} = 0b0000; + let Inst{11-6} = funct; + let Inst{5-0} = 0b111100; +} + +class CACHE_PREF_FM_MMR6 opgroup, bits<4> funct> : MaxisR6Inst { + bits<21> addr; + bits<5> hint; + + bits<32> Inst; + + let Inst{31-26} = opgroup; + let Inst{25-21} = hint; + let Inst{20-16} = addr{20-16}; + let Inst{15-12} = funct; + let Inst{11-0} = addr{11-0}; +} + +class ARITH_FM_MMR6 funct> : MMR6Arch { + bits<5> rd; + bits<5> rt; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = rd; + let Inst{10} = 0; + let Inst{9-0} = funct; +} + +class ADDI_FM_MMR6 op> : MMR6Arch { + bits<5> rt; + bits<5> rs; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-0} = imm16; +} + +class POOL32C_ST_EVA_FM_MMR6 op, bits<3> funct> : MaxisR6Inst { + bits<21> addr; + bits<5> hint; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = hint; + let Inst{20-16} = base; + let Inst{15-12} = 0b1010; + let Inst{11-9} = funct; + let Inst{8-0} = offset; +} + +class LB32_FM_MMR6 : MaxisR6Inst { + bits<21> addr; + bits<5> rt; + bits<5> base = addr{20-16}; + bits<16> offset = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = 0b000111; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-0} = offset; +} + +class LBU32_FM_MMR6 : MaxisR6Inst { + bits<21> addr; + bits<5> rt; + bits<5> base = addr{20-16}; + bits<16> offset = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = 0b000101; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-0} = offset; +} + +class POOL32C_LB_LBU_FM_MMR6 funct> : MaxisR6Inst { + bits<21> addr; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0b011000; + let Inst{25-21} = rt; + let Inst{20-16} = addr{20-16}; + let Inst{15-12} = 0b0110; + let Inst{11-9} = funct; + let Inst{8-0} = addr{8-0}; +} + +class SIGN_EXTEND_FM_MMR6 funct> + : MMR6Arch { + bits<5> rd; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rd; + let Inst{20-16} = rt; + let Inst{15-6} = funct; + let Inst{5-0} = 0b111100; +} + +class PCREL19_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rt; + bits<19> imm; + + bits<32> Inst; + + let Inst{31-26} = 0b011110; + let Inst{25-21} = rt; + let Inst{20-19} = funct; + let Inst{18-0} = imm; +} + +class PCREL16_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rt; + bits<16> imm; + + bits<32> Inst; + + let Inst{31-26} = 0b011110; + let Inst{25-21} = rt; + let Inst{20-16} = funct; + let Inst{15-0} = imm; +} + +class POOL32A_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = rd; + let Inst{10} = 0; + let Inst{9-0} = funct; +} + +class POOL32A_PAUSE_FM_MMR6 op> : MMR6Arch { + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = 0; + let Inst{20-16} = 0; + let Inst{15-11} = op; + let Inst{10-6} = 0; + let Inst{5-0} = 0; +} + +class POOL32A_RDPGPR_FM_MMR6 funct> { + bits<5> rt; + bits<5> rd; + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rd; + let Inst{15-6} = funct; + let Inst{5-0} = 0b111100; +} + +class POOL32A_RDHWR_FM_MMR6 { + bits<5> rt; + bits<5> rs; + bits<3> sel; + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-14} = 0; + let Inst{13-11} = sel; + let Inst{10} = 0; + let Inst{9-0} = 0b0111000000; +} + +class POOL32A_SYNC_FM_MMR6 { + bits<5> stype; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = 0; + let Inst{20-16} = stype; + let Inst{15-6} = 0b0110101101; + let Inst{5-0} = 0b111100; +} + +class POOL32I_SYNCI_FM_MMR6 { + bits<21> addr; + bits<5> base = addr{20-16}; + bits<16> immediate = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = 0b010000; + let Inst{25-21} = 0b01100; + let Inst{20-16} = base; + let Inst{15-0} = immediate; +} + +class POOL32A_2R_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-6} = funct; + let Inst{5-0} = 0b111100; +} + +class SPECIAL_2R_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rs; + let Inst{20-16} = 0b00000; + let Inst{15-11} = rt; + let Inst{10-6} = 0b00001; + let Inst{5-0} = funct; +} + +class POOL32A_ALIGN_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + bits<2> bp; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rs; + let Inst{20-16} = rt; + let Inst{15-11} = rd; + let Inst{10-9} = bp; + let Inst{8-6} = 0b000; + let Inst{5-0} = funct; +} + +class AUI_FM_MMR6 : MaxisR6Inst { + bits<5> rs; + bits<5> rt; + bits<16> imm; + + bits<32> Inst; + + let Inst{31-26} = 0b000100; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-0} = imm; +} + +class POOL32A_LSA_FM funct> : MaxisR6Inst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + bits<2> imm2; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = rd; + let Inst{10-9} = imm2; + let Inst{8-6} = 0b000; + let Inst{5-0} = funct; +} + +class SB32_SH32_STORE_FM_MMR6 op> { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<16> offset = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-0} = offset; +} + +class POOL32C_STORE_EVA_FM_MMR6 funct> { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = 0b011000; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-12} = 0b1010; + let Inst{11-9} = funct; + let Inst{8-0} = offset; +} + +class LOAD_WORD_EVA_FM_MMR6 funct> { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = 0b011000; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-12} = 0b0110; + let Inst{11-9} = funct; + let Inst{8-0} = offset; +} + +class LOAD_WORD_FM_MMR6 { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<16> offset = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = 0b111111; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-0} = offset; +} + +class LOAD_UPPER_IMM_FM_MMR6 { + bits<5> rt; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = 0b000100; + let Inst{25-21} = rt; + let Inst{20-16} = 0; + let Inst{15-0} = imm16; +} + +class CMP_BRANCH_1R_RT_OFF16_FM_MMR6 funct> + : MMR6Arch, MaxisR6Inst { + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = funct; + let Inst{25-21} = rt; + let Inst{20-16} = 0b00000; + let Inst{15-0} = offset; +} + +class CMP_BRANCH_1R_BOTH_OFF16_FM_MMR6 funct> + : MMR6Arch, MaxisR6Inst { + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = funct; + let Inst{25-21} = rt; + let Inst{20-16} = rt; + let Inst{15-0} = offset; +} + +class POOL32A_JALRC_FM_MMR6 funct> + : MaxisR6Inst, MMR6Arch { + bits<5> rt; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-6} = funct; + let Inst{5-0} = 0b111100; +} + +class POOL32A_EXT_INS_FM_MMR6 funct> + : MMR6Arch, MaxisR6Inst { + bits<5> rt; + bits<5> rs; + bits<5> size; + bits<5> pos; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = size; + let Inst{10-6} = pos; + let Inst{5-0} = funct; +} + +class POOL32A_ERET_FM_MMR6 funct> + : MMR6Arch { + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-16} = 0x00; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class ERETNC_FM_MMR6 : MMR6Arch { + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-17} = 0x00; + let Inst{16-16} = 0x01; + let Inst{15-6} = 0x3cd; + let Inst{5-0} = 0x3c; +} + +class BREAK_MMR6_ENC : MMR6Arch { + bits<10> code_1; + bits<10> code_2; + bits<32> Inst; + let Inst{31-26} = 0x0; + let Inst{25-16} = code_1; + let Inst{15-6} = code_2; + let Inst{5-0} = 0x07; +} + +class BARRIER_MMR6_ENC op> : MMR6Arch { + bits<32> Inst; + + let Inst{31-26} = 0x0; + let Inst{25-21} = 0x0; + let Inst{20-16} = 0x0; + let Inst{15-11} = op; + let Inst{10-6} = 0x0; + let Inst{5-0} = 0x0; +} + +class POOL32A_EIDI_MMR6_ENC funct> + : MMR6Arch { + bits<32> Inst; + bits<5> rt; // Actually rs but we're sharing code with the standard encodings which call it rt + + let Inst{31-26} = 0x00; + let Inst{25-21} = 0x00; + let Inst{20-16} = rt; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class SHIFT_MMR6_ENC funct, bit rotate> : MMR6Arch { + bits<5> rd; + bits<5> rt; + bits<5> shamt; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rd; + let Inst{20-16} = rt; + let Inst{15-11} = shamt; + let Inst{10} = rotate; + let Inst{9-0} = funct; +} + +class SW32_FM_MMR6 op> : MMR6Arch { + bits<5> rt; + bits<21> addr; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = addr{20-16}; + let Inst{15-0} = addr{15-0}; +} + +class POOL32C_SWE_FM_MMR6 op, bits<4> fmt, + bits<3> funct> : MMR6Arch { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-12} = fmt; + let Inst{11-9} = funct; + let Inst{8-0} = offset; +} + +class POOL32F_ARITH_FM_MMR6 fmt, bits<8> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15-11} = fd; + let Inst{10} = 0; + let Inst{9-8} = fmt; + let Inst{7-0} = funct; +} + +class POOL32F_ARITHF_FM_MMR6 fmt, bits<9> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15-11} = fd; + let Inst{10-9} = fmt; + let Inst{8-0} = funct; +} + +class POOL32F_MOV_NEG_FM_MMR6 fmt, bits<7> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15} = 0; + let Inst{14-13} = fmt; + let Inst{12-6} = funct; + let Inst{5-0} = 0b111011; +} + +class POOL32F_MINMAX_FM fmt, bits<9> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15-11} = fd; + let Inst{10-9} = fmt; + let Inst{8-0} = funct; +} + +class POOL32F_CMP_FM format, FIELD_CMP_COND Cond> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15-11} = fd; + let Inst{10-6} = Cond.Value; + let Inst{5-0} = format; +} + +class POOL32F_CVT_LW_FM funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + + bits<32> Inst; + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15} = 0; + let Inst{14} = fmt; + let Inst{13-6} = funct; + let Inst{5-0} = 0b111011; +} + +class POOL32F_CVT_DS_FM fmt, bits<7> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + + bits<32> Inst; + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15} = 0; + let Inst{14-13} = fmt; + let Inst{12-6} = funct; + let Inst{5-0} = 0b111011; +} + +class POOL32F_ABS_FM_MMR6 fmt, bits<7> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15} = 0; + let Inst{14-13} = fmt; + let Inst{12-6} = funct; + let Inst{5-0} = 0b111011; +} + +class POOL32F_MATH_FM_MMR6 fmt, bits<8> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15} = 0; + let Inst{14} = fmt; + let Inst{13-6} = funct; + let Inst{5-0} = 0b111011; +} + +class POOL16A_ADDU16_FM_MMR6 : MicroMaxisR6Inst16 { + bits<3> rs; + bits<3> rt; + bits<3> rd; + + bits<16> Inst; + + let Inst{15-10} = 0b000001; + let Inst{9-7} = rs; + let Inst{6-4} = rt; + let Inst{3-1} = rd; + let Inst{0} = 0; +} + +class POOL16C_AND16_FM_MMR6 : MicroMaxisR6Inst16 { + bits<3> rt; + bits<3> rs; + + bits<16> Inst; + + let Inst{15-10} = 0b010001; + let Inst{9-7} = rt; + let Inst{6-4} = rs; + let Inst{3-0} = 0b0001; +} + +class POOL16C_NOT16_FM_MMR6 : MicroMaxisR6Inst16 { + bits<3> rt; + bits<3> rs; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-7} = rt; + let Inst{6-4} = rs; + let Inst{3-0} = 0b0000; +} + +class POOL16C_MOVEP16_FM_MMR6 : MicroMaxisR6Inst16 { + bits<3> dst_regs; + bits<3> rt; + bits<3> rs; + + bits<16> Inst; + + let Inst{15-10} = 0b010001; + let Inst{9-7} = dst_regs; + let Inst{6-4} = rt; + let Inst{3} = rs{2}; + let Inst{2} = 0b1; + let Inst{1-0} = rs{1-0}; +} + +class POOL16C_OR16_XOR16_FM_MMR6 op> : MicroMaxisR6Inst16 { + bits<3> rt; + bits<3> rs; + + bits<16> Inst; + + let Inst{15-10} = 0b010001; + let Inst{9-7} = rt; + let Inst{6-4} = rs; + let Inst{3-0} = op; +} + +class POOL16C_BREAKPOINT_FM_MMR6 op> { + bits<4> code_; + bits<16> Inst; + + let Inst{15-10} = 0b010001; + let Inst{9-6} = code_; + let Inst{5-0} = op; +} + +class POOL16A_SUBU16_FM_MMR6 { + bits<3> rs; + bits<3> rt; + bits<3> rd; + + bits<16> Inst; + + let Inst{15-10} = 0b000001; + let Inst{9-7} = rs; + let Inst{6-4} = rt; + let Inst{3-1} = rd; + let Inst{0} = 0b1; +} + +class POOL32A_WRPGPR_WSBH_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rt; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class POOL32F_RECIP_ROUND_FM_MMR6 fmt, bits<8> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15} = 0; + let Inst{14} = fmt; + let Inst{13-6} = funct; + let Inst{5-0} = 0b111011; +} + +class POOL32F_RINT_FM_MMR6 fmt> + : MMR6Arch, MaxisR6Inst { + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = fs; + let Inst{20-16} = fd; + let Inst{15-11} = 0; + let Inst{10-9} = fmt; + let Inst{8-0} = 0b000100000; +} + +class POOL32F_SEL_FM_MMR6 fmt, bits<9> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> ft; + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15-11} = fd; + let Inst{10-9} = fmt; + let Inst{8-0} = funct; +} + +class POOL32F_CLASS_FM_MMR6 fmt, bits<9> funct> + : MMR6Arch, MaxisR6Inst { + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = fs; + let Inst{20-16} = fd; + let Inst{15-11} = 0b00000; + let Inst{10-9} = fmt; + let Inst{8-0} = funct; +} + +class POOL32A_TLBINV_FM_MMR6 funct> + : MMR6Arch, MaxisR6Inst { + bits<32> Inst; + + let Inst{31-26} = 0x0; + let Inst{25-16} = 0x0; + let Inst{15-6} = funct; + let Inst{5-0} = 0b111100; +} + +class POOL32A_MFTC0_FM_MMR6 funct, bits<6> opcode> + : MMR6Arch, MaxisR6Inst { + bits<5> rt; + bits<5> rs; + bits<3> sel; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-14} = 0; + let Inst{13-11} = sel; + let Inst{10-6} = funct; + let Inst{5-0} = opcode; +} + +class POOL32F_MFTC1_FM_MMR6 funct> + : MMR6Arch { + bits<5> rt; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0b010101; + let Inst{25-21} = rt; + let Inst{20-16} = fs; + let Inst{15-14} = 0; + let Inst{13-6} = funct; + let Inst{5-0} = 0b111011; +} + +class POOL32A_MFTC2_FM_MMR6 funct> + : MMR6Arch, MaxisR6Inst { + bits<5> rt; + bits<5> impl; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = impl; + let Inst{15-6} = funct; + let Inst{5-0} = 0b111100; +} + +class CMP_BRANCH_2R_OFF16_FM_MMR6 funct> + : MaxisR6Inst, MMR6Arch { + bits<5> rt; + bits<5> rs; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = funct; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-0} = offset; +} + +class POOL32A_DVPEVP_FM_MMR6 funct> + : MMR6Arch, MaxisR6Inst { + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = 0b00000; + let Inst{20-16} = rs; + let Inst{15-6} = funct; + let Inst{5-0} = 0b111100; +} + +class POOL32B_LWP_SWP_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rd; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<12> offset = addr{11-0}; + + bits<32> Inst; + + let Inst{31-26} = 0x8; + let Inst{25-21} = rd; + let Inst{20-16} = base; + let Inst{15-12} = funct; + let Inst{11-0} = offset; +} + +class CMP_BRANCH_OFF21_FM_MMR6 funct> : MaxisR6Inst { + bits<5> rs; + bits<21> offset; + + bits<32> Inst; + + let Inst{31-26} = funct; + let Inst{25-21} = rs; + let Inst{20-0} = offset; +} + +class POOL32I_BRANCH_COP_1_2_FM_MMR6 funct> + : MMR6Arch { + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 0b010000; + let Inst{25-21} = funct; + let Inst{20-16} = rt; + let Inst{15-0} = offset; +} + +class LDWC1_SDWC1_FM_MMR6 funct> + : MMR6Arch { + bits<5> ft; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<16> offset = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = funct; + let Inst{25-21} = ft; + let Inst{20-16} = base; + let Inst{15-0} = offset; +} + +class POOL32B_LDWC2_SDWC2_FM_MMR6 funct> + : MMR6Arch, MaxisR6Inst { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<11> offset = addr{10-0}; + + bits<32> Inst; + + let Inst{31-26} = 0b001000; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-12} = funct; + let Inst{11} = 0; + let Inst{10-0} = offset; +} diff --git a/lib/Target/Maxis/MicroMaxis32r6InstrInfo.td b/lib/Target/Maxis/MicroMaxis32r6InstrInfo.td new file mode 100644 index 00000000..4ab991fb --- /dev/null +++ b/lib/Target/Maxis/MicroMaxis32r6InstrInfo.td @@ -0,0 +1,1894 @@ +//=- MicroMaxis32r6InstrInfo.td - MicroMaxis r6 Instruction Information -*- tablegen -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes microMAXISr6 instructions. +// +//===----------------------------------------------------------------------===// + +def brtarget21_mm : Operand { + let EncoderMethod = "getBranchTarget21OpValueMM"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTarget21MM"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def brtarget26_mm : Operand { + let EncoderMethod = "getBranchTarget26OpValueMM"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTarget26MM"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def brtargetr6 : Operand { + let EncoderMethod = "getBranchTargetOpValueMMR6"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTargetMM"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def brtarget_lsl2_mm : Operand { + let EncoderMethod = "getBranchTargetOpValueLsl2MMR6"; + let OperandType = "OPERAND_PCREL"; + // Instructions that use this operand have their decoder method + // set with DecodeDisambiguates + let DecoderMethod = ""; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +//===----------------------------------------------------------------------===// +// +// Instruction Encodings +// +//===----------------------------------------------------------------------===// +class ADD_MMR6_ENC : ARITH_FM_MMR6<"add", 0x110>; +class ADDIU_MMR6_ENC : ADDI_FM_MMR6<"addiu", 0xc>; +class ADDU_MMR6_ENC : ARITH_FM_MMR6<"addu", 0x150>; +class ADDIUPC_MMR6_ENC : PCREL19_FM_MMR6<0b00>; +class ALUIPC_MMR6_ENC : PCREL16_FM_MMR6<0b11111>; +class AND_MMR6_ENC : ARITH_FM_MMR6<"and", 0x250>; +class ANDI_MMR6_ENC : ADDI_FM_MMR6<"andi", 0x34>; +class AUIPC_MMR6_ENC : PCREL16_FM_MMR6<0b11110>; +class ALIGN_MMR6_ENC : POOL32A_ALIGN_FM_MMR6<0b011111>; +class AUI_MMR6_ENC : AUI_FM_MMR6; +class BALC_MMR6_ENC : BRANCH_OFF26_FM<0b101101>; +class BC_MMR6_ENC : BRANCH_OFF26_FM<0b100101>; +class BC16_MMR6_ENC : BC16_FM_MM16R6; +class BEQZC16_MMR6_ENC : BEQZC_BNEZC_FM_MM16R6<0x23>; +class BNEZC16_MMR6_ENC : BEQZC_BNEZC_FM_MM16R6<0x2b>; +class BITSWAP_MMR6_ENC : POOL32A_BITSWAP_FM_MMR6<0b101100>; +class BRK_MMR6_ENC : BREAK_MMR6_ENC<"break">; +class BEQZC_MMR6_ENC : CMP_BRANCH_OFF21_FM_MMR6<"beqzc", 0b100000>; +class BNEZC_MMR6_ENC : CMP_BRANCH_OFF21_FM_MMR6<"bnezc", 0b101000>; +class BGEC_MMR6_ENC : CMP_BRANCH_2R_OFF16_FM_MMR6<"bgec", 0b111101>, + DecodeDisambiguates<"POP75GroupBranchMMR6">; +class BGEUC_MMR6_ENC : CMP_BRANCH_2R_OFF16_FM_MMR6<"bgeuc", 0b110000>, + DecodeDisambiguates<"BlezGroupBranchMMR6">; +class BLTC_MMR6_ENC : CMP_BRANCH_2R_OFF16_FM_MMR6<"bltc", 0b110101>, + DecodeDisambiguates<"POP65GroupBranchMMR6">; +class BLTUC_MMR6_ENC : CMP_BRANCH_2R_OFF16_FM_MMR6<"bltuc", 0b111000>, + DecodeDisambiguates<"BgtzGroupBranchMMR6">; +class BEQC_MMR6_ENC : CMP_BRANCH_2R_OFF16_FM_MMR6<"beqc", 0b011101>; +class BNEC_MMR6_ENC : CMP_BRANCH_2R_OFF16_FM_MMR6<"bnec", 0b011111>; +class BLTZC_MMR6_ENC : CMP_BRANCH_1R_BOTH_OFF16_FM_MMR6<"bltzc", 0b110101>, + DecodeDisambiguates<"POP65GroupBranchMMR6">; +class BLEZC_MMR6_ENC : CMP_BRANCH_1R_RT_OFF16_FM_MMR6<"blezc", 0b111101>, + DecodeDisambiguates<"POP75GroupBranchMMR6">; +class BGEZC_MMR6_ENC : CMP_BRANCH_1R_BOTH_OFF16_FM_MMR6<"bgezc", 0b111101>, + DecodeDisambiguates<"POP75GroupBranchMMR6">; +class BGTZC_MMR6_ENC : CMP_BRANCH_1R_RT_OFF16_FM_MMR6<"bgtzc", 0b110101>, + DecodeDisambiguates<"POP65GroupBranchMMR6">; +class BEQZALC_MMR6_ENC : CMP_BRANCH_1R_RT_OFF16_FM_MMR6<"beqzalc", 0b011101>, + DecodeDisambiguates<"POP35GroupBranchMMR6">; +class BNEZALC_MMR6_ENC : CMP_BRANCH_1R_RT_OFF16_FM_MMR6<"bnezalc", 0b011111>, + DecodeDisambiguates<"POP37GroupBranchMMR6">; +class BGTZALC_MMR6_ENC : CMP_BRANCH_1R_RT_OFF16_FM_MMR6<"bgtzalc", 0b111000>, + MMDecodeDisambiguatedBy<"BgtzGroupBranchMMR6">; +class BLTZALC_MMR6_ENC : CMP_BRANCH_1R_BOTH_OFF16_FM_MMR6<"bltzalc", 0b111000>, + MMDecodeDisambiguatedBy<"BgtzGroupBranchMMR6">; +class BGEZALC_MMR6_ENC : CMP_BRANCH_1R_BOTH_OFF16_FM_MMR6<"bgezalc", 0b110000>, + MMDecodeDisambiguatedBy<"BlezGroupBranchMMR6">; +class BLEZALC_MMR6_ENC : CMP_BRANCH_1R_RT_OFF16_FM_MMR6<"blezalc", 0b110000>, + MMDecodeDisambiguatedBy<"BlezGroupBranchMMR6">; +class CACHE_MMR6_ENC : CACHE_PREF_FM_MMR6<0b001000, 0b0110>; +class CLO_MMR6_ENC : POOL32A_2R_FM_MMR6<0b0100101100>; +class CLZ_MMR6_ENC : SPECIAL_2R_FM_MMR6<0b010000>; +class DIV_MMR6_ENC : ARITH_FM_MMR6<"div", 0x118>; +class DIVU_MMR6_ENC : ARITH_FM_MMR6<"divu", 0x198>; +class EHB_MMR6_ENC : BARRIER_MMR6_ENC<"ehb", 0x3>; +class EI_MMR6_ENC : POOL32A_EIDI_MMR6_ENC<"ei", 0x15d>; +class DI_MMR6_ENC : POOL32A_EIDI_MMR6_ENC<"di", 0b0100011101>; +class ERET_MMR6_ENC : POOL32A_ERET_FM_MMR6<"eret", 0x3cd>; +class DERET_MMR6_ENC : POOL32A_ERET_FM_MMR6<"eret", 0b1110001101>; +class ERETNC_MMR6_ENC : ERETNC_FM_MMR6<"eretnc">; +class JALRC16_MMR6_ENC : POOL16C_JALRC_FM_MM16R6<0xb>; +class JIALC_MMR6_ENC : JMP_IDX_COMPACT_FM<0b100000>; +class JIC_MMR6_ENC : JMP_IDX_COMPACT_FM<0b101000>; +class JRC16_MMR6_ENC: POOL16C_JALRC_FM_MM16R6<0x3>; +class JRCADDIUSP_MMR6_ENC : POOL16C_JRCADDIUSP_FM_MM16R6<0x13>; +class LSA_MMR6_ENC : POOL32A_LSA_FM<0b001111>; +class LWP_MMR6_ENC : POOL32B_LWP_SWP_FM_MMR6<0x1>; +class LWPC_MMR6_ENC : PCREL19_FM_MMR6<0b01>; +class LWM16_MMR6_ENC : POOL16C_LWM_SWM_FM_MM16R6<0x2>; +class MFC0_MMR6_ENC : POOL32A_MFTC0_FM_MMR6<"mfc0", 0b00011, 0b111100>; +class MFC1_MMR6_ENC : POOL32F_MFTC1_FM_MMR6<"mfc1", 0b10000000>; +class MFC2_MMR6_ENC : POOL32A_MFTC2_FM_MMR6<"mfc2", 0b0100110100>; +class MFHC0_MMR6_ENC : POOL32A_MFTC0_FM_MMR6<"mfhc0", 0b00011, 0b110100>; +class MFHC1_MMR6_ENC : POOL32F_MFTC1_FM_MMR6<"mfhc1", 0b11000000>; +class MFHC2_MMR6_ENC : POOL32A_MFTC2_FM_MMR6<"mfhc2", 0b1000110100>; +class MOD_MMR6_ENC : ARITH_FM_MMR6<"mod", 0x158>; +class MODU_MMR6_ENC : ARITH_FM_MMR6<"modu", 0x1d8>; +class MUL_MMR6_ENC : ARITH_FM_MMR6<"mul", 0x18>; +class MUH_MMR6_ENC : ARITH_FM_MMR6<"muh", 0x58>; +class MULU_MMR6_ENC : ARITH_FM_MMR6<"mulu", 0x98>; +class MUHU_MMR6_ENC : ARITH_FM_MMR6<"muhu", 0xd8>; +class MTC0_MMR6_ENC : POOL32A_MFTC0_FM_MMR6<"mtc0", 0b01011, 0b111100>; +class MTC1_MMR6_ENC : POOL32F_MFTC1_FM_MMR6<"mtc1", 0b10100000>; +class MTC2_MMR6_ENC : POOL32A_MFTC2_FM_MMR6<"mtc2", 0b0101110100>; +class MTHC0_MMR6_ENC : POOL32A_MFTC0_FM_MMR6<"mthc0", 0b01011, 0b110100>; +class MTHC1_MMR6_ENC : POOL32F_MFTC1_FM_MMR6<"mthc1", 0b11100000>; +class MTHC2_MMR6_ENC : POOL32A_MFTC2_FM_MMR6<"mthc2", 0b1001110100>; +class NOR_MMR6_ENC : ARITH_FM_MMR6<"nor", 0x2d0>; +class OR_MMR6_ENC : ARITH_FM_MMR6<"or", 0x290>; +class ORI_MMR6_ENC : ADDI_FM_MMR6<"ori", 0x14>; +class PREF_MMR6_ENC : CACHE_PREF_FM_MMR6<0b011000, 0b0010>; +class SB16_MMR6_ENC : LOAD_STORE_FM_MM16<0x22>; +class SEB_MMR6_ENC : SIGN_EXTEND_FM_MMR6<"seb", 0b0010101100>; +class SEH_MMR6_ENC : SIGN_EXTEND_FM_MMR6<"seh", 0b0011101100>; +class SELEQZ_MMR6_ENC : POOL32A_FM_MMR6<0b0101000000>; +class SELNEZ_MMR6_ENC : POOL32A_FM_MMR6<0b0110000000>; +class SH16_MMR6_ENC : LOAD_STORE_FM_MM16<0x2a>; +class SLL_MMR6_ENC : SHIFT_MMR6_ENC<"sll", 0x00, 0b0>; +class SUB_MMR6_ENC : ARITH_FM_MMR6<"sub", 0x190>; +class SUBU_MMR6_ENC : ARITH_FM_MMR6<"subu", 0x1d0>; +class SW_MMR6_ENC : SW32_FM_MMR6<"sw", 0x3e>; +class SWE_MMR6_ENC : POOL32C_SWE_FM_MMR6<"swe", 0x18, 0xa, 0x7>; +class SW16_MMR6_ENC : LOAD_STORE_FM_MM16<0x3a>; +class SWM16_MMR6_ENC : POOL16C_LWM_SWM_FM_MM16R6<0xa>; +class SWSP_MMR6_ENC : LOAD_STORE_SP_FM_MM16<0x32>; +class SWP_MMR6_ENC : POOL32B_LWP_SWP_FM_MMR6<0x9>; +class PREFE_MMR6_ENC : POOL32C_ST_EVA_FM_MMR6<0b011000, 0b010>; +class CACHEE_MMR6_ENC : POOL32C_ST_EVA_FM_MMR6<0b011000, 0b011>; +class WRPGPR_MMR6_ENC : POOL32A_WRPGPR_WSBH_FM_MMR6<0x3c5>; +class WSBH_MMR6_ENC : POOL32A_WRPGPR_WSBH_FM_MMR6<0x1ec>; +class LB_MMR6_ENC : LB32_FM_MMR6; +class LBU_MMR6_ENC : LBU32_FM_MMR6; +class LBE_MMR6_ENC : POOL32C_LB_LBU_FM_MMR6<0b100>; +class LBUE_MMR6_ENC : POOL32C_LB_LBU_FM_MMR6<0b000>; +class PAUSE_MMR6_ENC : POOL32A_PAUSE_FM_MMR6<"pause", 0b00101>; +class RDHWR_MMR6_ENC : POOL32A_RDHWR_FM_MMR6; +class WAIT_MMR6_ENC : WAIT_FM_MM, MMR6Arch<"wait">; +class SSNOP_MMR6_ENC : BARRIER_FM_MM<0x1>, MMR6Arch<"ssnop">; +class SYNC_MMR6_ENC : POOL32A_SYNC_FM_MMR6; +class SYNCI_MMR6_ENC : POOL32I_SYNCI_FM_MMR6, MMR6Arch<"synci">; +class RDPGPR_MMR6_ENC : POOL32A_RDPGPR_FM_MMR6<0b1110000101>; +class SDBBP_MMR6_ENC : SDBBP_FM_MM, MMR6Arch<"sdbbp">; +class XOR_MMR6_ENC : ARITH_FM_MMR6<"xor", 0x310>; +class XORI_MMR6_ENC : ADDI_FM_MMR6<"xori", 0x1c>; +class ABS_S_MMR6_ENC : POOL32F_ABS_FM_MMR6<"abs.s", 0, 0b0001101>; +class ABS_D_MMR6_ENC : POOL32F_ABS_FM_MMR6<"abs.d", 1, 0b0001101>; +class FLOOR_L_S_MMR6_ENC : POOL32F_MATH_FM_MMR6<"floor.l.s", 0, 0b00001100>; +class FLOOR_L_D_MMR6_ENC : POOL32F_MATH_FM_MMR6<"floor.l.d", 1, 0b00001100>; +class FLOOR_W_S_MMR6_ENC : POOL32F_MATH_FM_MMR6<"floor.w.s", 0, 0b00101100>; +class FLOOR_W_D_MMR6_ENC : POOL32F_MATH_FM_MMR6<"floor.w.d", 1, 0b00101100>; +class CEIL_L_S_MMR6_ENC : POOL32F_MATH_FM_MMR6<"ceil.l.s", 0, 0b01001100>; +class CEIL_L_D_MMR6_ENC : POOL32F_MATH_FM_MMR6<"ceil.l.d", 1, 0b01001100>; +class CEIL_W_S_MMR6_ENC : POOL32F_MATH_FM_MMR6<"ceil.w.s", 0, 0b01101100>; +class CEIL_W_D_MMR6_ENC : POOL32F_MATH_FM_MMR6<"ceil.w.d", 1, 0b01101100>; +class TRUNC_L_S_MMR6_ENC : POOL32F_MATH_FM_MMR6<"trunc.l.s", 0, 0b10001100>; +class TRUNC_L_D_MMR6_ENC : POOL32F_MATH_FM_MMR6<"trunc.l.d", 1, 0b10001100>; +class TRUNC_W_S_MMR6_ENC : POOL32F_MATH_FM_MMR6<"trunc.w.s", 0, 0b10101100>; +class TRUNC_W_D_MMR6_ENC : POOL32F_MATH_FM_MMR6<"trunc.w.d", 1, 0b10101100>; +class SQRT_S_MMR6_ENC : POOL32F_MATH_FM_MMR6<"sqrt.s", 0, 0b00101000>; +class SQRT_D_MMR6_ENC : POOL32F_MATH_FM_MMR6<"sqrt.d", 1, 0b00101000>; +class SB_MMR6_ENC : SB32_SH32_STORE_FM_MMR6<0b000110>; +class SBE_MMR6_ENC : POOL32C_STORE_EVA_FM_MMR6<0b100>; +class SCE_MMR6_ENC : POOL32C_STORE_EVA_FM_MMR6<0b110>; +class SH_MMR6_ENC : SB32_SH32_STORE_FM_MMR6<0b001110>; +class SHE_MMR6_ENC : POOL32C_STORE_EVA_FM_MMR6<0b101>; +class LLE_MMR6_ENC : LOAD_WORD_EVA_FM_MMR6<0b110>; +class LWE_MMR6_ENC : LOAD_WORD_EVA_FM_MMR6<0b111>; +class LW_MMR6_ENC : LOAD_WORD_FM_MMR6; +class LUI_MMR6_ENC : LOAD_UPPER_IMM_FM_MMR6; +class JALRC_HB_MMR6_ENC : POOL32A_JALRC_FM_MMR6<"jalrc.hb", 0b0001111100>; +class RINT_S_MMR6_ENC : POOL32F_RINT_FM_MMR6<"rint.s", 0>; +class RINT_D_MMR6_ENC : POOL32F_RINT_FM_MMR6<"rint.d", 1>; +class ROUND_L_S_MMR6_ENC : POOL32F_RECIP_ROUND_FM_MMR6<"round.l.s", 0, + 0b11001100>; +class ROUND_L_D_MMR6_ENC : POOL32F_RECIP_ROUND_FM_MMR6<"round.l.d", 1, + 0b11001100>; +class ROUND_W_S_MMR6_ENC : POOL32F_RECIP_ROUND_FM_MMR6<"round.w.s", 0, + 0b11101100>; +class ROUND_W_D_MMR6_ENC : POOL32F_RECIP_ROUND_FM_MMR6<"round.w.d", 1, + 0b11101100>; +class SEL_S_MMR6_ENC : POOL32F_SEL_FM_MMR6<"sel.s", 0, 0b010111000>; +class SEL_D_MMR6_ENC : POOL32F_SEL_FM_MMR6<"sel.d", 1, 0b010111000>; +class SELEQZ_S_MMR6_ENC : POOL32F_SEL_FM_MMR6<"seleqz.s", 0, 0b000111000>; +class SELEQZ_D_MMR6_ENC : POOL32F_SEL_FM_MMR6<"seleqz.d", 1, 0b000111000>; +class SELNEZ_S_MMR6_ENC : POOL32F_SEL_FM_MMR6<"selnez.s", 0, 0b001111000>; +class SELNEZ_D_MMR6_ENC : POOL32F_SEL_FM_MMR6<"selnez.d", 1, 0b001111000>; +class CLASS_S_MMR6_ENC : POOL32F_CLASS_FM_MMR6<"class.s", 0, 0b001100000>; +class CLASS_D_MMR6_ENC : POOL32F_CLASS_FM_MMR6<"class.d", 1, 0b001100000>; +class EXT_MMR6_ENC : POOL32A_EXT_INS_FM_MMR6<"ext", 0b101100>; +class INS_MMR6_ENC : POOL32A_EXT_INS_FM_MMR6<"ins", 0b001100>; +class JALRC_MMR6_ENC : POOL32A_JALRC_FM_MMR6<"jalrc", 0b0000111100>; +class BOVC_MMR6_ENC : POP35_BOVC_FM_MMR6<"bovc">; +class BNVC_MMR6_ENC : POP37_BNVC_FM_MMR6<"bnvc">; +class ADDU16_MMR6_ENC : POOL16A_ADDU16_FM_MMR6; +class AND16_MMR6_ENC : POOL16C_AND16_FM_MMR6; +class ANDI16_MMR6_ENC : ANDI_FM_MM16<0b001011>, MicroMaxisR6Inst16; +class NOT16_MMR6_ENC : POOL16C_NOT16_FM_MMR6; +class OR16_MMR6_ENC : POOL16C_OR16_XOR16_FM_MMR6<0b1001>; +class SLL16_MMR6_ENC : SHIFT_FM_MM16<0>, MicroMaxisR6Inst16; +class SRL16_MMR6_ENC : SHIFT_FM_MM16<1>, MicroMaxisR6Inst16; +class BREAK16_MMR6_ENC : POOL16C_BREAKPOINT_FM_MMR6<0b011011>; +class LI16_MMR6_ENC : LI_FM_MM16; +class MOVE16_MMR6_ENC : MOVE_FM_MM16<0b000011>; +class MOVEP_MMR6_ENC : POOL16C_MOVEP16_FM_MMR6; +class SDBBP16_MMR6_ENC : POOL16C_BREAKPOINT_FM_MMR6<0b111011>; +class SUBU16_MMR6_ENC : POOL16A_SUBU16_FM_MMR6; +class XOR16_MMR6_ENC : POOL16C_OR16_XOR16_FM_MMR6<0b1000>; +class TLBINV_MMR6_ENC : POOL32A_TLBINV_FM_MMR6<"tlbinv", 0x10d>; +class TLBINVF_MMR6_ENC : POOL32A_TLBINV_FM_MMR6<"tlbinvf", 0x14d>; +class DVP_MMR6_ENC : POOL32A_DVPEVP_FM_MMR6<"dvp", 0b0001100101>; +class EVP_MMR6_ENC : POOL32A_DVPEVP_FM_MMR6<"evp", 0b0011100101>; +class BC1EQZC_MMR6_ENC : POOL32I_BRANCH_COP_1_2_FM_MMR6<"bc1eqzc", 0b01000>; +class BC1NEZC_MMR6_ENC : POOL32I_BRANCH_COP_1_2_FM_MMR6<"bc1nezc", 0b01001>; +class BC2EQZC_MMR6_ENC : POOL32I_BRANCH_COP_1_2_FM_MMR6<"bc2eqzc", 0b01010>; +class BC2NEZC_MMR6_ENC : POOL32I_BRANCH_COP_1_2_FM_MMR6<"bc2nezc", 0b01011>; +class LDC1_MMR6_ENC : LDWC1_SDWC1_FM_MMR6<"ldc1", 0b101111>; +class SDC1_MMR6_ENC : LDWC1_SDWC1_FM_MMR6<"sdc1", 0b101110>; +class LDC2_MMR6_ENC : POOL32B_LDWC2_SDWC2_FM_MMR6<"ldc2", 0b0010>; +class SDC2_MMR6_ENC : POOL32B_LDWC2_SDWC2_FM_MMR6<"sdc2", 0b1010>; +class LWC2_MMR6_ENC : POOL32B_LDWC2_SDWC2_FM_MMR6<"lwc2", 0b0000>; +class SWC2_MMR6_ENC : POOL32B_LDWC2_SDWC2_FM_MMR6<"swc2", 0b1000>; + +/// Floating Point Instructions +class FADD_S_MMR6_ENC : POOL32F_ARITH_FM_MMR6<"add.s", 0, 0b00110000>; +class FADD_D_MMR6_ENC : POOL32F_ARITH_FM_MMR6<"add.d", 1, 0b00110000>; +class FSUB_S_MMR6_ENC : POOL32F_ARITH_FM_MMR6<"sub.s", 0, 0b01110000>; +class FSUB_D_MMR6_ENC : POOL32F_ARITH_FM_MMR6<"sub.d", 1, 0b01110000>; +class FMUL_S_MMR6_ENC : POOL32F_ARITH_FM_MMR6<"mul.s", 0, 0b10110000>; +class FMUL_D_MMR6_ENC : POOL32F_ARITH_FM_MMR6<"mul.d", 1, 0b10110000>; +class FDIV_S_MMR6_ENC : POOL32F_ARITH_FM_MMR6<"div.s", 0, 0b11110000>; +class FDIV_D_MMR6_ENC : POOL32F_ARITH_FM_MMR6<"div.d", 1, 0b11110000>; +class MADDF_S_MMR6_ENC : POOL32F_ARITHF_FM_MMR6<"maddf.s", 0, 0b110111000>; +class MADDF_D_MMR6_ENC : POOL32F_ARITHF_FM_MMR6<"maddf.d", 1, 0b110111000>; +class MSUBF_S_MMR6_ENC : POOL32F_ARITHF_FM_MMR6<"msubf.s", 0, 0b111111000>; +class MSUBF_D_MMR6_ENC : POOL32F_ARITHF_FM_MMR6<"msubf.d", 1, 0b111111000>; +class FMOV_S_MMR6_ENC : POOL32F_MOV_NEG_FM_MMR6<"mov.s", 0, 0b0000001>; +class FMOV_D_MMR6_ENC : POOL32F_MOV_NEG_FM_MMR6<"mov.d", 1, 0b0000001>; +class FNEG_S_MMR6_ENC : POOL32F_MOV_NEG_FM_MMR6<"neg.s", 0, 0b0101101>; +class FNEG_D_MMR6_ENC : POOL32F_MOV_NEG_FM_MMR6<"neg.d", 1, 0b0101101>; +class MAX_S_MMR6_ENC : POOL32F_MINMAX_FM<"max.s", 0, 0b000001011>; +class MAX_D_MMR6_ENC : POOL32F_MINMAX_FM<"max.d", 1, 0b000001011>; +class MAXA_S_MMR6_ENC : POOL32F_MINMAX_FM<"maxa.s", 0, 0b000101011>; +class MAXA_D_MMR6_ENC : POOL32F_MINMAX_FM<"maxa.d", 1, 0b000101011>; +class MIN_S_MMR6_ENC : POOL32F_MINMAX_FM<"min.s", 0, 0b000000011>; +class MIN_D_MMR6_ENC : POOL32F_MINMAX_FM<"min.d", 1, 0b000000011>; +class MINA_S_MMR6_ENC : POOL32F_MINMAX_FM<"mina.s", 0, 0b000100011>; +class MINA_D_MMR6_ENC : POOL32F_MINMAX_FM<"mina.d", 1, 0b000100011>; + +class CVT_L_S_MMR6_ENC : POOL32F_CVT_LW_FM<"cvt.l.s", 0, 0b00000100>; +class CVT_L_D_MMR6_ENC : POOL32F_CVT_LW_FM<"cvt.l.d", 1, 0b00000100>; +class CVT_W_S_MMR6_ENC : POOL32F_CVT_LW_FM<"cvt.w.s", 0, 0b00100100>; +class CVT_W_D_MMR6_ENC : POOL32F_CVT_LW_FM<"cvt.w.d", 1, 0b00100100>; +class CVT_D_S_MMR6_ENC : POOL32F_CVT_DS_FM<"cvt.d.s", 0, 0b1001101>; +class CVT_D_W_MMR6_ENC : POOL32F_CVT_DS_FM<"cvt.d.w", 1, 0b1001101>; +class CVT_D_L_MMR6_ENC : POOL32F_CVT_DS_FM<"cvt.d.l", 2, 0b1001101>; +class CVT_S_D_MMR6_ENC : POOL32F_CVT_DS_FM<"cvt.s.d", 0, 0b1101101>; +class CVT_S_W_MMR6_ENC : POOL32F_CVT_DS_FM<"cvt.s.w", 1, 0b1101101>; +class CVT_S_L_MMR6_ENC : POOL32F_CVT_DS_FM<"cvt.s.l", 2, 0b1101101>; + +//===----------------------------------------------------------------------===// +// +// Instruction Descriptions +// +//===----------------------------------------------------------------------===// + +class CMP_CBR_RT_Z_MMR6_DESC_BASE + : BRANCH_DESC_BASE { + dag InOperandList = (ins GPROpnd:$rt, opnd:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(instr_asm, "\t$rt, $offset"); + list Defs = [AT]; + InstrItinClass Itinerary = II_BCCZC; +} + +class BEQZALC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"beqzalc", brtarget_mm, + GPR32Opnd> { + list Defs = [RA]; +} + +class BGEZALC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"bgezalc", brtarget_mm, + GPR32Opnd> { + list Defs = [RA]; +} + +class BGTZALC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"bgtzalc", brtarget_mm, + GPR32Opnd> { + list Defs = [RA]; +} + +class BLEZALC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"blezalc", brtarget_mm, + GPR32Opnd> { + list Defs = [RA]; +} + +class BLTZALC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"bltzalc", brtarget_mm, + GPR32Opnd> { + list Defs = [RA]; +} + +class BNEZALC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"bnezalc", brtarget_mm, + GPR32Opnd> { + list Defs = [RA]; +} + +class BLTZC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"bltzc", brtarget_lsl2_mm, + GPR32Opnd>; +class BLEZC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"blezc", brtarget_lsl2_mm, + GPR32Opnd>; +class BGEZC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"bgezc", brtarget_lsl2_mm, + GPR32Opnd>; +class BGTZC_MMR6_DESC : CMP_CBR_RT_Z_MMR6_DESC_BASE<"bgtzc", brtarget_lsl2_mm, + GPR32Opnd>; + +class CMP_CBR_2R_MMR6_DESC_BASE : BRANCH_DESC_BASE { + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt, opnd:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(instr_asm, "\t$rs, $rt, $offset"); + list Defs = [AT]; + InstrItinClass Itinerary = II_BCCC; +} + +class BGEC_MMR6_DESC : CMP_CBR_2R_MMR6_DESC_BASE<"bgec", brtarget_lsl2_mm, + GPR32Opnd>; +class BGEUC_MMR6_DESC : CMP_CBR_2R_MMR6_DESC_BASE<"bgeuc", brtarget_lsl2_mm, + GPR32Opnd>; +class BLTC_MMR6_DESC : CMP_CBR_2R_MMR6_DESC_BASE<"bltc", brtarget_lsl2_mm, + GPR32Opnd>; +class BLTUC_MMR6_DESC : CMP_CBR_2R_MMR6_DESC_BASE<"bltuc", brtarget_lsl2_mm, + GPR32Opnd>; +class BEQC_MMR6_DESC : CMP_CBR_2R_MMR6_DESC_BASE<"beqc", brtarget_lsl2_mm, + GPR32Opnd>; +class BNEC_MMR6_DESC : CMP_CBR_2R_MMR6_DESC_BASE<"bnec", brtarget_lsl2_mm, + GPR32Opnd>; + +class ADD_MMR6_DESC : ArithLogicR<"add", GPR32Opnd, 1, II_ADD>; +class ADDIU_MMR6_DESC : ArithLogicI<"addiu", simm16, GPR32Opnd, II_ADDIU, immSExt16, add>; +class ADDU_MMR6_DESC : ArithLogicR<"addu", GPR32Opnd, 1, II_ADDU>; +class MUL_MMR6_DESC : ArithLogicR<"mul", GPR32Opnd, 1, II_MUL, mul>; +class MUH_MMR6_DESC : ArithLogicR<"muh", GPR32Opnd, 1, II_MUH, mulhs>; +class MULU_MMR6_DESC : ArithLogicR<"mulu", GPR32Opnd, 1, II_MULU>; +class MUHU_MMR6_DESC : ArithLogicR<"muhu", GPR32Opnd, 1, II_MUHU, mulhu>; + +class BC_MMR6_DESC_BASE + : BRANCH_DESC_BASE, MMR6Arch { + dag InOperandList = (ins opnd:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(instr_asm, "\t$offset"); + bit isBarrier = 1; + InstrItinClass Itinerary = Itin; +} + +class BALC_MMR6_DESC : BC_MMR6_DESC_BASE<"balc", brtarget26_mm, II_BALC> { + bit isCall = 1; + list Defs = [RA]; +} +class BC_MMR6_DESC : BC_MMR6_DESC_BASE<"bc", brtarget26_mm, II_BC> { + list Pattern = [(br bb:$offset)]; +} + +class BC16_MMR6_DESC : MicroMaxisInst16<(outs), (ins brtarget10_mm:$offset), + !strconcat("bc16", "\t$offset"), [], + II_BC, FrmI>, + MMR6Arch<"bc16">, MicroMaxisR6Inst16 { + let isBranch = 1; + let isTerminator = 1; + let isBarrier = 1; + let hasDelaySlot = 0; + let AdditionalPredicates = [RelocPIC]; + let Defs = [AT]; +} + +class BEQZC_BNEZC_MM16R6_DESC_BASE + : CBranchZeroMM, MMR6Arch { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 0; + let Defs = [AT]; +} +class BEQZC16_MMR6_DESC : BEQZC_BNEZC_MM16R6_DESC_BASE<"beqzc16">; +class BNEZC16_MMR6_DESC : BEQZC_BNEZC_MM16R6_DESC_BASE<"bnezc16">; + +class SUB_MMR6_DESC : ArithLogicR<"sub", GPR32Opnd, 0, II_SUB>; +class SUBU_MMR6_DESC : ArithLogicR<"subu", GPR32Opnd, 0,II_SUBU>; + +class BITSWAP_MMR6_DESC_BASE + : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rt"); + list Pattern = []; + InstrItinClass Itinerary = II_BITSWAP; +} + +class BITSWAP_MMR6_DESC : BITSWAP_MMR6_DESC_BASE<"bitswap", GPR32Opnd>; + +class BRK_MMR6_DESC : BRK_FT<"break">; + +class CACHE_HINT_MMR6_DESC + : MMR6Arch { + dag OutOperandList = (outs); + dag InOperandList = (ins MemOpnd:$addr, uimm5:$hint); + string AsmString = !strconcat(instr_asm, "\t$hint, $addr"); + list Pattern = []; + string DecoderMethod = "DecodeCacheOpMM"; + InstrItinClass Itinerary = Itin; +} + +class CACHE_MMR6_DESC : CACHE_HINT_MMR6_DESC<"cache", mem_mm_12, GPR32Opnd, + II_CACHE>; +class PREF_MMR6_DESC : CACHE_HINT_MMR6_DESC<"pref", mem_mm_12, GPR32Opnd, + II_PREF>; + +class PREFE_CACHEE_MMR6_DESC_BASE + : CACHE_HINT_MMR6_DESC { + string DecoderMethod = "DecodePrefeOpMM"; +} + +class PREFE_MMR6_DESC : PREFE_CACHEE_MMR6_DESC_BASE<"prefe", mem_mm_9, + GPR32Opnd, II_PREFE>; +class CACHEE_MMR6_DESC : PREFE_CACHEE_MMR6_DESC_BASE<"cachee", mem_mm_9, + GPR32Opnd, II_CACHEE>; + +class LB_LBU_MMR6_DESC_BASE + : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins MemOpnd:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + string DecoderMethod = "DecodeLoadByte15"; + bit mayLoad = 1; + InstrItinClass Itinerary = Itin; +} +class LB_MMR6_DESC : LB_LBU_MMR6_DESC_BASE<"lb", mem_mm_16, GPR32Opnd, II_LB>; +class LBU_MMR6_DESC : LB_LBU_MMR6_DESC_BASE<"lbu", mem_mm_16, GPR32Opnd, + II_LBU>; + +class LBE_LBUE_MMR6_DESC_BASE + : LB_LBU_MMR6_DESC_BASE { + let DecoderMethod = "DecodeLoadByte9"; +} +class LBE_MMR6_DESC : LBE_LBUE_MMR6_DESC_BASE<"lbe", mem_mm_9, GPR32Opnd, + II_LBE>; +class LBUE_MMR6_DESC : LBE_LBUE_MMR6_DESC_BASE<"lbue", mem_mm_9, GPR32Opnd, + II_LBUE>; + +class CLO_CLZ_MMR6_DESC_BASE : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins GPROpnd:$rs); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs"); + InstrItinClass Itinerary = Itin; +} + +class CLO_MMR6_DESC : CLO_CLZ_MMR6_DESC_BASE<"clo", GPR32Opnd, II_CLO>; +class CLZ_MMR6_DESC : CLO_CLZ_MMR6_DESC_BASE<"clz", GPR32Opnd, II_CLZ>; + +class EHB_MMR6_DESC : Barrier<"ehb", II_EHB>; +class EI_MMR6_DESC : DEI_FT<"ei", GPR32Opnd, II_EI>; +class DI_MMR6_DESC : DEI_FT<"di", GPR32Opnd, II_DI>; + +class ERET_MMR6_DESC : ER_FT<"eret", II_ERET>; +class DERET_MMR6_DESC : ER_FT<"deret", II_DERET>; +class ERETNC_MMR6_DESC : ER_FT<"eretnc", II_ERETNC>; + +class JALRC16_MMR6_DESC_BASE + : MicroMaxisInst16<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), + [(MaxisJmpLink RO:$rs)], II_JALR, FrmR>, + MMR6Arch, MicroMaxisR6Inst16 { + let isCall = 1; + let hasDelaySlot = 0; + let Defs = [RA]; +} +class JALRC16_MMR6_DESC : JALRC16_MMR6_DESC_BASE<"jalr", GPR32Opnd>; + +class JMP_MMR6_IDX_COMPACT_DESC_BASE + : MMR6Arch { + dag InOperandList = (ins GPROpnd:$rt, opnd:$offset); + string AsmString = !strconcat(opstr, "\t$rt, $offset"); + list Pattern = []; + bit isTerminator = 1; + bit hasDelaySlot = 0; + InstrItinClass Itinerary = Itin; +} + +class JIALC_MMR6_DESC : JMP_MMR6_IDX_COMPACT_DESC_BASE<"jialc", calloffset16, + GPR32Opnd, II_JIALC> { + bit isCall = 1; + list Defs = [RA]; +} + +class JIC_MMR6_DESC : JMP_MMR6_IDX_COMPACT_DESC_BASE<"jic", jmpoffset16, + GPR32Opnd, II_JIC> { + bit isBarrier = 1; + list Defs = [AT]; +} + +class JRC16_MMR6_DESC_BASE + : MicroMaxisInst16<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), + [], II_JR, FrmR>, + MMR6Arch, MicroMaxisR6Inst16 { + let hasDelaySlot = 0; + let isBranch = 1; + let isIndirectBranch = 1; +} +class JRC16_MMR6_DESC : JRC16_MMR6_DESC_BASE<"jrc16", GPR32Opnd>; + +class JRCADDIUSP_MMR6_DESC + : MicroMaxisInst16<(outs), (ins uimm5_lsl2:$imm), "jrcaddiusp\t$imm", + [], II_JRADDIUSP, FrmR>, + MMR6Arch<"jrcaddiusp">, MicroMaxisR6Inst16 { + let hasDelaySlot = 0; + let isTerminator = 1; + let isBarrier = 1; + let isBranch = 1; + let isIndirectBranch = 1; +} + +class ALIGN_MMR6_DESC_BASE + : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt, ImmOpnd:$bp); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt, $bp"); + list Pattern = []; + InstrItinClass Itinerary = Itin; +} + +class ALIGN_MMR6_DESC : ALIGN_MMR6_DESC_BASE<"align", GPR32Opnd, uimm2, + II_ALIGN>; + +class AUI_MMR6_DESC_BASE : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins GPROpnd:$rs, uimm16:$imm); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs, $imm"); + list Pattern = []; + InstrItinClass Itinerary = Itin; +} + +class AUI_MMR6_DESC : AUI_MMR6_DESC_BASE<"aui", GPR32Opnd, II_AUI>; + +class SEB_MMR6_DESC : SignExtInReg<"seb", i8, GPR32Opnd, II_SEB>; +class SEH_MMR6_DESC : SignExtInReg<"seh", i16, GPR32Opnd, II_SEH>; +class ALUIPC_MMR6_DESC_BASE : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins simm16:$imm); + string AsmString = !strconcat(instr_asm, "\t$rt, $imm"); + list Pattern = []; + InstrItinClass Itinerary = Itin; +} + +class ALUIPC_MMR6_DESC : ALUIPC_MMR6_DESC_BASE<"aluipc", GPR32Opnd, II_ALUIPC>; +class AUIPC_MMR6_DESC : ALUIPC_MMR6_DESC_BASE<"auipc", GPR32Opnd, II_AUIPC>; + +class LSA_MMR6_DESC_BASE + : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt, ImmOpnd:$imm2); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs, $rd, $imm2"); + list Pattern = []; + InstrItinClass Itinerary = Itin; +} + +class LSA_MMR6_DESC : LSA_MMR6_DESC_BASE<"lsa", GPR32Opnd, uimm2_plus1, II_LSA>; + +class PCREL_MMR6_DESC_BASE + : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rt); + dag InOperandList = (ins ImmOpnd:$imm); + string AsmString = !strconcat(instr_asm, "\t$rt, $imm"); + list Pattern = []; + InstrItinClass Itinerary = Itin; +} + +class ADDIUPC_MMR6_DESC : PCREL_MMR6_DESC_BASE<"addiupc", GPR32Opnd, + simm19_lsl2, II_ADDIUPC>; +class LWPC_MMR6_DESC: PCREL_MMR6_DESC_BASE<"lwpc", GPR32Opnd, simm19_lsl2, + II_LWPC>; + +class LWP_MMR6_DESC : MMR6Arch<"lwp"> { + dag OutOperandList = (outs regpair:$rd); + dag InOperandList = (ins mem_simm12:$addr); + string AsmString = !strconcat("lwp", "\t$rd, $addr"); + list Pattern = []; + InstrItinClass Itinerary = II_LWP; + ComplexPattern Addr = addr; + Format f = FrmI; + string BaseOpcode = "lwp"; + string DecoderMethod = "DecodeMemMMImm12"; + bit mayLoad = 1; +} + +class SWP_MMR6_DESC : MMR6Arch<"swp"> { + dag OutOperandList = (outs); + dag InOperandList = (ins regpair:$rd, mem_simm12:$addr); + string AsmString = !strconcat("swp", "\t$rd, $addr"); + list Pattern = []; + InstrItinClass Itinerary = II_SWP; + ComplexPattern Addr = addr; + Format f = FrmI; + string BaseOpcode = "swp"; + string DecoderMethod = "DecodeMemMMImm12"; + bit mayStore = 1; +} + +class SELEQNE_Z_MMR6_DESC_BASE : MMR6Arch { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt); + string AsmString = !strconcat(instr_asm, "\t$rd, $rs, $rt"); + list Pattern = []; + InstrItinClass Itinerary = Itin; +} + +class SELEQZ_MMR6_DESC : SELEQNE_Z_MMR6_DESC_BASE<"seleqz", GPR32Opnd, + II_SELCCZ>; +class SELNEZ_MMR6_DESC : SELEQNE_Z_MMR6_DESC_BASE<"selnez", GPR32Opnd, + II_SELCCZ>; +class PAUSE_MMR6_DESC : Barrier<"pause", II_PAUSE>; +class RDHWR_MMR6_DESC : MMR6Arch<"rdhwr">, MaxisR6Inst { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins HWRegsOpnd:$rs, uimm3:$sel); + string AsmString = !strconcat("rdhwr", "\t$rt, $rs, $sel"); + list Pattern = []; + InstrItinClass Itinerary = II_RDHWR; + Format Form = FrmR; +} + +class WAIT_MMR6_DESC : WaitMM<"wait">; +// FIXME: ssnop should not be defined for R6. Per MD000582 microMAXIS32 6.03: +// Assemblers targeting specifically Release 6 should reject the SSNOP +// instruction with an error. +class SSNOP_MMR6_DESC : Barrier<"ssnop", II_SSNOP>; +class SLL_MMR6_DESC : shift_rotate_imm<"sll", uimm5, GPR32Opnd, II_SLL>; + +class DIVMOD_MMR6_DESC_BASE + : MaxisR6Inst { + dag OutOperandList = (outs GPROpnd:$rd); + dag InOperandList = (ins GPROpnd:$rs, GPROpnd:$rt); + string AsmString = !strconcat(opstr, "\t$rd, $rs, $rt"); + list Pattern = [(set GPROpnd:$rd, (OpNode GPROpnd:$rs, GPROpnd:$rt))]; + string BaseOpcode = opstr; + Format f = FrmR; + let isCommutable = 0; + let isReMaterializable = 1; + InstrItinClass Itinerary = Itin; + + // This instruction doesn't trap division by zero itself. We must insert + // teq instructions as well. + bit usesCustomInserter = 1; +} +class DIV_MMR6_DESC : DIVMOD_MMR6_DESC_BASE<"div", GPR32Opnd, II_DIV, sdiv>; +class DIVU_MMR6_DESC : DIVMOD_MMR6_DESC_BASE<"divu", GPR32Opnd, II_DIVU, udiv>; +class MOD_MMR6_DESC : DIVMOD_MMR6_DESC_BASE<"mod", GPR32Opnd, II_MOD, srem>; +class MODU_MMR6_DESC : DIVMOD_MMR6_DESC_BASE<"modu", GPR32Opnd, II_MODU, urem>; +class AND_MMR6_DESC : ArithLogicR<"and", GPR32Opnd, 1, II_AND, and>; +class ANDI_MMR6_DESC : ArithLogicI<"andi", uimm16, GPR32Opnd, II_ANDI>; +class NOR_MMR6_DESC : LogicNOR<"nor", GPR32Opnd>; +class OR_MMR6_DESC : ArithLogicR<"or", GPR32Opnd, 1, II_OR, or>; +class ORI_MMR6_DESC : ArithLogicI<"ori", uimm16, GPR32Opnd, II_ORI, immZExt16, + or> { + int AddedComplexity = 1; +} +class XOR_MMR6_DESC : ArithLogicR<"xor", GPR32Opnd, 1, II_XOR, xor>; +class XORI_MMR6_DESC : ArithLogicI<"xori", uimm16, GPR32Opnd, II_XORI, + immZExt16, xor>; + +class SWE_MMR6_DESC_BASE : + InstSE<(outs), (ins RO:$rt, MO:$addr), !strconcat(opstr, "\t$rt, $addr"), + [(OpNode RO:$rt, Addr:$addr)], Itin, FrmI, opstr> { + let DecoderMethod = "DecodeMem"; + let mayStore = 1; +} +class SW_MMR6_DESC : Store<"sw", GPR32Opnd> { + InstrItinClass Itinerary = II_SW; +} +class SWE_MMR6_DESC : SWE_MMR6_DESC_BASE<"swe", GPR32Opnd, mem_simm9, II_SWE>; + +class WRPGPR_WSBH_MMR6_DESC_BASE : MMR6Arch { + dag InOperandList = (ins RO:$rs); + dag OutOperandList = (outs RO:$rt); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs"); + list Pattern = []; + Format f = FrmR; + string BaseOpcode = instr_asm; + bit hasSideEffects = 0; + InstrItinClass Itinerary = Itin; +} +class WRPGPR_MMR6_DESC : WRPGPR_WSBH_MMR6_DESC_BASE<"wrpgpr", GPR32Opnd, + II_WRPGPR>; +class WSBH_MMR6_DESC : WRPGPR_WSBH_MMR6_DESC_BASE<"wsbh", GPR32Opnd, II_WSBH>; + +class MTC0_MMR6_DESC_BASE { + dag InOperandList = (ins SrcRC:$rt, uimm3:$sel); + dag OutOperandList = (outs DstRC:$rs); + string AsmString = !strconcat(opstr, "\t$rt, $rs, $sel"); + list Pattern = []; + Format f = FrmFR; + string BaseOpcode = opstr; + InstrItinClass Itinerary = Itin; +} +class MTC1_MMR6_DESC_BASE< + string opstr, RegisterOperand DstRC, RegisterOperand SrcRC, + InstrItinClass Itin = NoItinerary, SDPatternOperator OpNode = null_frag> + : MaxisR6Inst { + dag InOperandList = (ins SrcRC:$rt); + dag OutOperandList = (outs DstRC:$fs); + string AsmString = !strconcat(opstr, "\t$rt, $fs"); + list Pattern = [(set DstRC:$fs, (OpNode SrcRC:$rt))]; + Format f = FrmFR; + InstrItinClass Itinerary = Itin; + string BaseOpcode = opstr; +} +class MTC1_64_MMR6_DESC_BASE< + string opstr, RegisterOperand DstRC, RegisterOperand SrcRC, + InstrItinClass Itin = NoItinerary> : MaxisR6Inst { + dag InOperandList = (ins DstRC:$fs_in, SrcRC:$rt); + dag OutOperandList = (outs DstRC:$fs); + string AsmString = !strconcat(opstr, "\t$rt, $fs"); + list Pattern = []; + Format f = FrmFR; + InstrItinClass Itinerary = Itin; + string BaseOpcode = opstr; + // $fs_in is part of a white lie to work around a widespread bug in the FPU + // implementation. See expandBuildPairF64 for details. + let Constraints = "$fs = $fs_in"; +} +class MTC2_MMR6_DESC_BASE { + dag InOperandList = (ins SrcRC:$rt); + dag OutOperandList = (outs DstRC:$impl); + string AsmString = !strconcat(opstr, "\t$rt, $impl"); + list Pattern = []; + Format f = FrmFR; + string BaseOpcode = opstr; + InstrItinClass Itinerary = Itin; +} + +class MTC0_MMR6_DESC : MTC0_MMR6_DESC_BASE<"mtc0", COP0Opnd, GPR32Opnd, + II_MTC0>; +class MTC1_MMR6_DESC : MTC1_MMR6_DESC_BASE<"mtc1", FGR32Opnd, GPR32Opnd, + II_MTC1, bitconvert>, HARDFLOAT; +class MTC2_MMR6_DESC : MTC2_MMR6_DESC_BASE<"mtc2", COP2Opnd, GPR32Opnd, + II_MTC2>; +class MTHC0_MMR6_DESC : MTC0_MMR6_DESC_BASE<"mthc0", COP0Opnd, GPR32Opnd, + II_MTHC0>; +class MTHC1_D32_MMR6_DESC : MTC1_64_MMR6_DESC_BASE<"mthc1", AFGR64Opnd, + GPR32Opnd, II_MTC1>, + HARDFLOAT, FGR_32; +class MTHC1_D64_MMR6_DESC : MTC1_64_MMR6_DESC_BASE<"mthc1", FGR64Opnd, + GPR32Opnd, II_MTC1>, + HARDFLOAT, FGR_64; +class MTHC2_MMR6_DESC : MTC2_MMR6_DESC_BASE<"mthc2", COP2Opnd, GPR32Opnd, + II_MTC2>; + +class MFC0_MMR6_DESC_BASE { + dag InOperandList = (ins SrcRC:$rs, uimm3:$sel); + dag OutOperandList = (outs DstRC:$rt); + string AsmString = !strconcat(opstr, "\t$rt, $rs, $sel"); + list Pattern = []; + Format f = FrmFR; + string BaseOpcode = opstr; + InstrItinClass Itinerary = Itin; +} +class MFC1_MMR6_DESC_BASE : MaxisR6Inst { + dag InOperandList = (ins SrcRC:$fs); + dag OutOperandList = (outs DstRC:$rt); + string AsmString = !strconcat(opstr, "\t$rt, $fs"); + list Pattern = [(set DstRC:$rt, (OpNode SrcRC:$fs))]; + Format f = FrmFR; + InstrItinClass Itinerary = Itin; + string BaseOpcode = opstr; +} +class MFC2_MMR6_DESC_BASE { + dag InOperandList = (ins SrcRC:$impl); + dag OutOperandList = (outs DstRC:$rt); + string AsmString = !strconcat(opstr, "\t$rt, $impl"); + list Pattern = []; + Format f = FrmFR; + string BaseOpcode = opstr; + InstrItinClass Itinerary = Itin; +} +class MFC0_MMR6_DESC : MFC0_MMR6_DESC_BASE<"mfc0", GPR32Opnd, COP0Opnd, + II_MFC0>; +class MFC1_MMR6_DESC : MFC1_MMR6_DESC_BASE<"mfc1", GPR32Opnd, FGR32Opnd, + II_MFC1, bitconvert>, HARDFLOAT; +class MFC2_MMR6_DESC : MFC2_MMR6_DESC_BASE<"mfc2", GPR32Opnd, COP2Opnd, + II_MFC2>; +class MFHC0_MMR6_DESC : MFC0_MMR6_DESC_BASE<"mfhc0", GPR32Opnd, COP0Opnd, + II_MFHC0>; +class MFHC1_D32_MMR6_DESC : MFC1_MMR6_DESC_BASE<"mfhc1", GPR32Opnd, AFGR64Opnd, + II_MFHC1>, HARDFLOAT, FGR_32; +class MFHC1_D64_MMR6_DESC : MFC1_MMR6_DESC_BASE<"mfhc1", GPR32Opnd, FGR64Opnd, + II_MFHC1>, HARDFLOAT, FGR_64; +class MFHC2_MMR6_DESC : MFC2_MMR6_DESC_BASE<"mfhc2", GPR32Opnd, COP2Opnd, + II_MFC2>; + +class LDC1_D64_MMR6_DESC : MaxisR6Inst, HARDFLOAT, FGR_64 { + dag InOperandList = (ins mem_mm_16:$addr); + dag OutOperandList = (outs FGR64Opnd:$ft); + string AsmString = !strconcat("ldc1", "\t$ft, $addr"); + list Pattern = [(set FGR64Opnd:$ft, (load addrimm16:$addr))]; + Format f = FrmFI; + InstrItinClass Itinerary = II_LDC1; + string BaseOpcode = "ldc1"; + bit mayLoad = 1; + let DecoderMethod = "DecodeFMemMMR2"; +} + +class SDC1_D64_MMR6_DESC : MaxisR6Inst, HARDFLOAT, FGR_64 { + dag InOperandList = (ins FGR64Opnd:$ft, mem_mm_16:$addr); + dag OutOperandList = (outs); + string AsmString = !strconcat("sdc1", "\t$ft, $addr"); + list Pattern = [(store FGR64Opnd:$ft, addrimm16:$addr)]; + Format f = FrmFI; + InstrItinClass Itinerary = II_SDC1; + string BaseOpcode = "sdc1"; + bit mayStore = 1; + let DecoderMethod = "DecodeFMemMMR2"; +} + +class LDC2_LWC2_MMR6_DESC_BASE { + dag OutOperandList = (outs COP2Opnd:$rt); + dag InOperandList = (ins mem_mm_11:$addr); + string AsmString = !strconcat(opstr, "\t$rt, $addr"); + list Pattern = [(set COP2Opnd:$rt, (load addrimm11:$addr))]; + Format f = FrmFI; + InstrItinClass Itinerary = itin; + string BaseOpcode = opstr; + bit mayLoad = 1; + string DecoderMethod = "DecodeFMemCop2MMR6"; +} +class LDC2_MMR6_DESC : LDC2_LWC2_MMR6_DESC_BASE<"ldc2", II_LDC2>; +class LWC2_MMR6_DESC : LDC2_LWC2_MMR6_DESC_BASE<"lwc2", II_LWC2>; + +class SDC2_SWC2_MMR6_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins COP2Opnd:$rt, mem_mm_11:$addr); + string AsmString = !strconcat(opstr, "\t$rt, $addr"); + list Pattern = [(store COP2Opnd:$rt, addrimm11:$addr)]; + Format f = FrmFI; + InstrItinClass Itinerary = itin; + string BaseOpcode = opstr; + bit mayStore = 1; + string DecoderMethod = "DecodeFMemCop2MMR6"; +} +class SDC2_MMR6_DESC : SDC2_SWC2_MMR6_DESC_BASE<"sdc2", II_SDC2>; +class SWC2_MMR6_DESC : SDC2_SWC2_MMR6_DESC_BASE<"swc2", II_SWC2>; + +/// Floating Point Instructions +class FARITH_MMR6_DESC_BASE : HARDFLOAT { + dag OutOperandList = (outs RC:$fd); + dag InOperandList = (ins RC:$ft, RC:$fs); + string AsmString = !strconcat(instr_asm, "\t$fd, $fs, $ft"); + list Pattern = [(set RC:$fd, (OpNode RC:$fs, RC:$ft))]; + InstrItinClass Itinerary = Itin; + bit isCommutable = isComm; +} +class FADD_S_MMR6_DESC + : FARITH_MMR6_DESC_BASE<"add.s", FGR32Opnd, II_ADD_S, 1, fadd>; +class FADD_D_MMR6_DESC + : FARITH_MMR6_DESC_BASE<"add.d", AFGR64Opnd, II_ADD_D, 1, fadd>; +class FSUB_S_MMR6_DESC + : FARITH_MMR6_DESC_BASE<"sub.s", FGR32Opnd, II_SUB_S, 0, fsub>; +class FSUB_D_MMR6_DESC + : FARITH_MMR6_DESC_BASE<"sub.d", AFGR64Opnd, II_SUB_D, 0, fsub>; +class FMUL_S_MMR6_DESC + : FARITH_MMR6_DESC_BASE<"mul.s", FGR32Opnd, II_MUL_S, 1, fmul>; +class FMUL_D_MMR6_DESC + : FARITH_MMR6_DESC_BASE<"mul.d", AFGR64Opnd, II_MUL_D, 1, fmul>; +class FDIV_S_MMR6_DESC + : FARITH_MMR6_DESC_BASE<"div.s", FGR32Opnd, II_DIV_S, 0, fdiv>; +class FDIV_D_MMR6_DESC + : FARITH_MMR6_DESC_BASE<"div.d", AFGR64Opnd, II_DIV_D, 0, fdiv>; +class MADDF_S_MMR6_DESC : COP1_4R_DESC_BASE<"maddf.s", FGR32Opnd, + II_MADDF_S>, HARDFLOAT; +class MADDF_D_MMR6_DESC : COP1_4R_DESC_BASE<"maddf.d", FGR64Opnd, + II_MADDF_D>, HARDFLOAT; +class MSUBF_S_MMR6_DESC : COP1_4R_DESC_BASE<"msubf.s", FGR32Opnd, + II_MSUBF_S>, HARDFLOAT; +class MSUBF_D_MMR6_DESC : COP1_4R_DESC_BASE<"msubf.d", FGR64Opnd, + II_MSUBF_D>, HARDFLOAT; + +class FMOV_FNEG_MMR6_DESC_BASE + : HARDFLOAT, NeverHasSideEffects { + dag OutOperandList = (outs DstRC:$ft); + dag InOperandList = (ins SrcRC:$fs); + string AsmString = !strconcat(instr_asm, "\t$ft, $fs"); + list Pattern = [(set DstRC:$ft, (OpNode SrcRC:$fs))]; + InstrItinClass Itinerary = Itin; + Format Form = FrmFR; +} +class FMOV_S_MMR6_DESC + : FMOV_FNEG_MMR6_DESC_BASE<"mov.s", FGR32Opnd, FGR32Opnd, II_MOV_S>; +class FMOV_D_MMR6_DESC + : FMOV_FNEG_MMR6_DESC_BASE<"mov.d", AFGR64Opnd, AFGR64Opnd, II_MOV_D>; +class FNEG_S_MMR6_DESC + : FMOV_FNEG_MMR6_DESC_BASE<"neg.s", FGR32Opnd, FGR32Opnd, II_NEG, fneg>; +class FNEG_D_MMR6_DESC + : FMOV_FNEG_MMR6_DESC_BASE<"neg.d", AFGR64Opnd, AFGR64Opnd, II_NEG, fneg>; + +class MAX_S_MMR6_DESC : MAX_MIN_DESC_BASE<"max.s", FGR32Opnd, II_MAX_S>, + HARDFLOAT; +class MAX_D_MMR6_DESC : MAX_MIN_DESC_BASE<"max.d", FGR64Opnd, II_MAX_D>, + HARDFLOAT; +class MIN_S_MMR6_DESC : MAX_MIN_DESC_BASE<"min.s", FGR32Opnd, II_MIN_S>, + HARDFLOAT; +class MIN_D_MMR6_DESC : MAX_MIN_DESC_BASE<"min.d", FGR64Opnd, II_MIN_D>, + HARDFLOAT; + +class MAXA_S_MMR6_DESC : MAX_MIN_DESC_BASE<"maxa.s", FGR32Opnd, II_MAXA_S>, + HARDFLOAT; +class MAXA_D_MMR6_DESC : MAX_MIN_DESC_BASE<"maxa.d", FGR64Opnd, II_MAXA_D>, + HARDFLOAT; +class MINA_S_MMR6_DESC : MAX_MIN_DESC_BASE<"mina.s", FGR32Opnd, II_MINA_S>, + HARDFLOAT; +class MINA_D_MMR6_DESC : MAX_MIN_DESC_BASE<"mina.d", FGR64Opnd, II_MINA_D>, + HARDFLOAT; + +class CVT_MMR6_DESC_BASE< + string instr_asm, RegisterOperand DstRC, RegisterOperand SrcRC, + InstrItinClass Itin, SDPatternOperator OpNode = null_frag> + : HARDFLOAT, NeverHasSideEffects { + dag OutOperandList = (outs DstRC:$ft); + dag InOperandList = (ins SrcRC:$fs); + string AsmString = !strconcat(instr_asm, "\t$ft, $fs"); + list Pattern = [(set DstRC:$ft, (OpNode SrcRC:$fs))]; + InstrItinClass Itinerary = Itin; + Format Form = FrmFR; +} + +class CVT_L_S_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.l.s", FGR64Opnd, FGR32Opnd, + II_CVT>; +class CVT_L_D_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.l.d", FGR64Opnd, FGR64Opnd, + II_CVT>; +class CVT_W_S_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.w.s", FGR32Opnd, FGR32Opnd, + II_CVT>; +class CVT_W_D_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.w.d", FGR32Opnd, AFGR64Opnd, + II_CVT>; +class CVT_D_S_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.d.s", FGR32Opnd, AFGR64Opnd, + II_CVT>; +class CVT_D_W_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.d.w", FGR32Opnd, AFGR64Opnd, + II_CVT>; +class CVT_D_L_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.d.l", FGR64Opnd, FGR64Opnd, + II_CVT>, FGR_64; +class CVT_S_D_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.s.d", AFGR64Opnd, FGR32Opnd, + II_CVT>; +class CVT_S_W_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.s.w", FGR32Opnd, FGR32Opnd, + II_CVT>; +class CVT_S_L_MMR6_DESC : CVT_MMR6_DESC_BASE<"cvt.s.l", FGR64Opnd, FGR32Opnd, + II_CVT>, FGR_64; + +multiclass CMP_CC_MMR6 format, string Typestr, + RegisterOperand FGROpnd, InstrItinClass Itin> { + def CMP_AF_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.af.", Typestr), format, FIELD_CMP_COND_AF>, + CMP_CONDN_DESC_BASE<"af", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_UN_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.un.", Typestr), format, FIELD_CMP_COND_UN>, + CMP_CONDN_DESC_BASE<"un", Typestr, FGROpnd, Itin, setuo>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_EQ_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.eq.", Typestr), format, FIELD_CMP_COND_EQ>, + CMP_CONDN_DESC_BASE<"eq", Typestr, FGROpnd, Itin, setoeq>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_UEQ_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.ueq.", Typestr), format, FIELD_CMP_COND_UEQ>, + CMP_CONDN_DESC_BASE<"ueq", Typestr, FGROpnd, Itin, setueq>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_LT_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.lt.", Typestr), format, FIELD_CMP_COND_LT>, + CMP_CONDN_DESC_BASE<"lt", Typestr, FGROpnd, Itin, setolt>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_ULT_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.ult.", Typestr), format, FIELD_CMP_COND_ULT>, + CMP_CONDN_DESC_BASE<"ult", Typestr, FGROpnd, Itin, setult>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_LE_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.le.", Typestr), format, FIELD_CMP_COND_LE>, + CMP_CONDN_DESC_BASE<"le", Typestr, FGROpnd, Itin, setole>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_ULE_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.ule.", Typestr), format, FIELD_CMP_COND_ULE>, + CMP_CONDN_DESC_BASE<"ule", Typestr, FGROpnd, Itin, setule>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_SAF_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.saf.", Typestr), format, FIELD_CMP_COND_SAF>, + CMP_CONDN_DESC_BASE<"saf", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_SUN_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.sun.", Typestr), format, FIELD_CMP_COND_SUN>, + CMP_CONDN_DESC_BASE<"sun", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_SEQ_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.seq.", Typestr), format, FIELD_CMP_COND_SEQ>, + CMP_CONDN_DESC_BASE<"seq", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_SUEQ_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.sueq.", Typestr), format, FIELD_CMP_COND_SUEQ>, + CMP_CONDN_DESC_BASE<"sueq", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_SLT_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.slt.", Typestr), format, FIELD_CMP_COND_SLT>, + CMP_CONDN_DESC_BASE<"slt", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_SULT_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.sult.", Typestr), format, FIELD_CMP_COND_SULT>, + CMP_CONDN_DESC_BASE<"sult", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_SLE_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.sle.", Typestr), format, FIELD_CMP_COND_SLE>, + CMP_CONDN_DESC_BASE<"sle", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; + def CMP_SULE_#NAME : R6MMR6Rel, POOL32F_CMP_FM< + !strconcat("cmp.sule.", Typestr), format, FIELD_CMP_COND_SULE>, + CMP_CONDN_DESC_BASE<"sule", Typestr, FGROpnd, Itin>, HARDFLOAT, + ISA_MICROMAXIS32R6; +} + +class ABSS_FT_MMR6_DESC_BASE + : HARDFLOAT, NeverHasSideEffects { + dag OutOperandList = (outs DstRC:$ft); + dag InOperandList = (ins SrcRC:$fs); + string AsmString = !strconcat(instr_asm, "\t$ft, $fs"); + list Pattern = [(set DstRC:$ft, (OpNode SrcRC:$fs))]; + InstrItinClass Itinerary = Itin; + Format Form = FrmFR; + list EncodingPredicates = [HasStdEnc]; +} + +class ABS_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"abs.s", FGR32Opnd, FGR32Opnd, + II_ABS, fabs>; +class ABS_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"abs.d", AFGR64Opnd, AFGR64Opnd, + II_ABS, fabs>; +class FLOOR_L_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"floor.l.s", FGR64Opnd, + FGR32Opnd, II_FLOOR>; +class FLOOR_L_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"floor.l.d", FGR64Opnd, + FGR64Opnd, II_FLOOR>; +class FLOOR_W_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"floor.w.s", FGR32Opnd, + FGR32Opnd, II_FLOOR>; +class FLOOR_W_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"floor.w.d", FGR32Opnd, + AFGR64Opnd, II_FLOOR>; +class CEIL_L_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"ceil.l.s", FGR64Opnd, + FGR32Opnd, II_CEIL>; +class CEIL_L_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"ceil.l.d", FGR64Opnd, + FGR64Opnd, II_CEIL>; +class CEIL_W_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"ceil.w.s", FGR32Opnd, + FGR32Opnd, II_CEIL>; +class CEIL_W_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"ceil.w.d", FGR32Opnd, + AFGR64Opnd, II_CEIL>; +class TRUNC_L_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"trunc.l.s", FGR64Opnd, + FGR32Opnd, II_TRUNC>; +class TRUNC_L_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"trunc.l.d", FGR64Opnd, + FGR64Opnd, II_TRUNC>; +class TRUNC_W_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"trunc.w.s", FGR32Opnd, + FGR32Opnd, II_TRUNC>; +class TRUNC_W_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"trunc.w.d", FGR32Opnd, + AFGR64Opnd, II_TRUNC>; +class SQRT_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"sqrt.s", FGR32Opnd, FGR32Opnd, + II_SQRT_S, fsqrt>; +class SQRT_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"sqrt.d", AFGR64Opnd, AFGR64Opnd, + II_SQRT_D, fsqrt>; +class ROUND_L_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"round.l.s", FGR64Opnd, + FGR32Opnd, II_ROUND>; +class ROUND_L_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"round.l.d", FGR64Opnd, + FGR64Opnd, II_ROUND>; +class ROUND_W_S_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"round.w.s", FGR32Opnd, + FGR32Opnd, II_ROUND>; +class ROUND_W_D_MMR6_DESC : ABSS_FT_MMR6_DESC_BASE<"round.w.d", FGR64Opnd, + FGR64Opnd, II_ROUND>; + +class SEL_S_MMR6_DESC : COP1_SEL_DESC_BASE<"sel.s", FGR32Opnd, II_SEL_S>; +class SEL_D_MMR6_DESC : COP1_SEL_D_DESC_BASE<"sel.d", FGR64Opnd, II_SEL_D>; + +class SELEQZ_S_MMR6_DESC : SELEQNEZ_DESC_BASE<"seleqz.s", FGR32Opnd, + II_SELCCZ_S>; +class SELEQZ_D_MMR6_DESC : SELEQNEZ_DESC_BASE<"seleqz.d", FGR64Opnd, + II_SELCCZ_D>; +class SELNEZ_S_MMR6_DESC : SELEQNEZ_DESC_BASE<"selnez.s", FGR32Opnd, + II_SELCCZ_S>; +class SELNEZ_D_MMR6_DESC : SELEQNEZ_DESC_BASE<"selnez.d", FGR64Opnd, + II_SELCCZ_D>; +class RINT_S_MMR6_DESC : CLASS_RINT_DESC_BASE<"rint.s", FGR32Opnd, + II_RINT_S>; +class RINT_D_MMR6_DESC : CLASS_RINT_DESC_BASE<"rint.d", FGR64Opnd, + II_RINT_S>; +class CLASS_S_MMR6_DESC : CLASS_RINT_DESC_BASE<"class.s", FGR32Opnd, + II_CLASS_S>; +class CLASS_D_MMR6_DESC : CLASS_RINT_DESC_BASE<"class.d", FGR64Opnd, + II_CLASS_S>; + +class STORE_MMR6_DESC_BASE + : Store, MMR6Arch { + let DecoderMethod = "DecodeMemMMImm16"; + InstrItinClass Itinerary = Itin; +} +class SB_MMR6_DESC : STORE_MMR6_DESC_BASE<"sb", GPR32Opnd, II_SB>; + +class STORE_EVA_MMR6_DESC_BASE + : MMR6Arch, MaxisR6Inst { + dag OutOperandList = (outs); + dag InOperandList = (ins RO:$rt, mem_simm9:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + string DecoderMethod = "DecodeStoreEvaOpMM"; + bit mayStore = 1; + InstrItinClass Itinerary = Itin; +} +class SBE_MMR6_DESC : STORE_EVA_MMR6_DESC_BASE<"sbe", GPR32Opnd, II_SBE>; +class SCE_MMR6_DESC : STORE_EVA_MMR6_DESC_BASE<"sce", GPR32Opnd, II_SCE>; +class SH_MMR6_DESC : STORE_MMR6_DESC_BASE<"sh", GPR32Opnd, II_SH>; +class SHE_MMR6_DESC : STORE_EVA_MMR6_DESC_BASE<"she", GPR32Opnd, II_SHE>; +class LOAD_WORD_EVA_MMR6_DESC_BASE + : MMR6Arch, MaxisR6Inst { + dag OutOperandList = (outs RO:$rt); + dag InOperandList = (ins mem_simm9:$addr); + string AsmString = !strconcat(instr_asm, "\t$rt, $addr"); + string DecoderMethod = "DecodeMemMMImm9"; + bit mayLoad = 1; + InstrItinClass Itinerary = Itin; +} +class LLE_MMR6_DESC : LOAD_WORD_EVA_MMR6_DESC_BASE<"lle", GPR32Opnd, II_LLE>; +class LWE_MMR6_DESC : LOAD_WORD_EVA_MMR6_DESC_BASE<"lwe", GPR32Opnd, II_LWE>; +class ADDU16_MMR6_DESC : ArithRMM16<"addu16", GPRMM16Opnd, 1, II_ADDU, add>, + MMR6Arch<"addu16"> { + int AddedComplexity = 1; +} +class AND16_MMR6_DESC : LogicRMM16<"and16", GPRMM16Opnd, II_AND, and>, + MMR6Arch<"and16"> { + int AddedComplexity = 1; +} +class ANDI16_MMR6_DESC : AndImmMM16<"andi16", GPRMM16Opnd, II_AND>, + MMR6Arch<"andi16">; +class NOT16_MMR6_DESC : NotMM16<"not16", GPRMM16Opnd>, MMR6Arch<"not16"> { + int AddedComplexity = 1; +} +class OR16_MMR6_DESC : LogicRMM16<"or16", GPRMM16Opnd, II_OR, or>, + MMR6Arch<"or16"> { + int AddedComplexity = 1; +} +class SLL16_MMR6_DESC : ShiftIMM16<"sll16", uimm3_shift, GPRMM16Opnd, II_SLL>, + MMR6Arch<"sll16">; +class SRL16_MMR6_DESC : ShiftIMM16<"srl16", uimm3_shift, GPRMM16Opnd, II_SRL>, + MMR6Arch<"srl16">; +class BREAK16_MMR6_DESC : BrkSdbbp16MM<"break16", II_BREAK>, MMR6Arch<"break16">, + MicroMaxisR6Inst16; +class LI16_MMR6_DESC : LoadImmMM16<"li16", li16_imm, GPRMM16Opnd>, + MMR6Arch<"li16">, MicroMaxisR6Inst16, IsAsCheapAsAMove; +class MOVE16_MMR6_DESC : MoveMM16<"move16", GPR32Opnd>, MMR6Arch<"move16">, + MicroMaxisR6Inst16; +class MOVEP_MMR6_DESC : MovePMM16<"movep", GPRMM16OpndMoveP>, MMR6Arch<"movep">; +class SDBBP16_MMR6_DESC : BrkSdbbp16MM<"sdbbp16", II_SDBBP>, MMR6Arch<"sdbbp16">, + MicroMaxisR6Inst16; +class SUBU16_MMR6_DESC : ArithRMM16<"subu16", GPRMM16Opnd, 0, II_SUBU, sub>, + MMR6Arch<"subu16">, MicroMaxisR6Inst16 { + int AddedComplexity = 1; +} +class XOR16_MMR6_DESC : LogicRMM16<"xor16", GPRMM16Opnd, II_XOR, xor>, + MMR6Arch<"xor16"> { + int AddedComplexity = 1; +} + +class LW_MMR6_DESC : MMR6Arch<"lw">, MaxisR6Inst { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins mem:$addr); + string AsmString = "lw\t$rt, $addr"; + let DecoderMethod = "DecodeMemMMImm16"; + let canFoldAsLoad = 1; + let mayLoad = 1; + list Pattern = [(set GPR32Opnd:$rt, (load addrDefault:$addr))]; + InstrItinClass Itinerary = II_LW; +} + +class LUI_MMR6_DESC : IsAsCheapAsAMove, MMR6Arch<"lui">, MaxisR6Inst{ + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins uimm16:$imm16); + string AsmString = "lui\t$rt, $imm16"; + list Pattern = []; + bit hasSideEffects = 0; + bit isReMaterializable = 1; + InstrItinClass Itinerary = II_LUI; + Format Form = FrmI; +} + +class SYNC_MMR6_DESC : MMR6Arch<"sync">, MaxisR6Inst { + dag OutOperandList = (outs); + dag InOperandList = (ins uimm5:$stype); + string AsmString = !strconcat("sync", "\t$stype"); + list Pattern = [(MaxisSync immZExt5:$stype)]; + InstrItinClass Itinerary = II_SYNC; + bit HasSideEffects = 1; +} + +class SYNCI_MMR6_DESC : SYNCI_FT<"synci"> { + let DecoderMethod = "DecodeSynciR6"; +} + +class RDPGPR_MMR6_DESC : MMR6Arch<"rdpgpr">, MaxisR6Inst { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins GPR32Opnd:$rd); + string AsmString = !strconcat("rdpgpr", "\t$rt, $rd"); + InstrItinClass Itinerary = II_RDPGPR; +} + +class SDBBP_MMR6_DESC : MaxisR6Inst { + dag OutOperandList = (outs); + dag InOperandList = (ins uimm20:$code_); + string AsmString = !strconcat("sdbbp", "\t$code_"); + list Pattern = []; + InstrItinClass Itinerary = II_SDBBP; +} + +class LWM16_MMR6_DESC + : MicroMaxisInst16<(outs reglist16:$rt), (ins mem_mm_4sp:$addr), + !strconcat("lwm16", "\t$rt, $addr"), [], + II_LWM, FrmI>, + MMR6Arch<"lwm16">, MicroMaxisR6Inst16 { + let DecoderMethod = "DecodeMemMMReglistImm4Lsl2"; + let mayLoad = 1; + ComplexPattern Addr = addr; +} + +class SWM16_MMR6_DESC + : MicroMaxisInst16<(outs), (ins reglist16:$rt, mem_mm_4sp:$addr), + !strconcat("swm16", "\t$rt, $addr"), [], + II_SWM, FrmI>, + MMR6Arch<"swm16">, MicroMaxisR6Inst16 { + let DecoderMethod = "DecodeMemMMReglistImm4Lsl2"; + let mayStore = 1; + ComplexPattern Addr = addr; +} + +class SB16_MMR6_DESC_BASE + : MicroMaxisInst16<(outs), (ins RTOpnd:$rt, MemOpnd:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], Itin, FrmI>, + MMR6Arch, MicroMaxisR6Inst16 { + let DecoderMethod = "DecodeMemMMImm4"; + let mayStore = 1; +} +class SB16_MMR6_DESC : SB16_MMR6_DESC_BASE<"sb16", GPRMM16OpndZero, GPRMM16Opnd, + truncstorei8, II_SB, mem_mm_4>; +class SH16_MMR6_DESC : SB16_MMR6_DESC_BASE<"sh16", GPRMM16OpndZero, GPRMM16Opnd, + truncstorei16, II_SH, mem_mm_4_lsl1>; +class SW16_MMR6_DESC : SB16_MMR6_DESC_BASE<"sw16", GPRMM16OpndZero, GPRMM16Opnd, + store, II_SW, mem_mm_4_lsl2>; + +class SWSP_MMR6_DESC + : MicroMaxisInst16<(outs), (ins GPR32Opnd:$rt, mem_mm_sp_imm5_lsl2:$offset), + !strconcat("sw", "\t$rt, $offset"), [], II_SW, FrmI>, + MMR6Arch<"sw">, MicroMaxisR6Inst16 { + let DecoderMethod = "DecodeMemMMSPImm5Lsl2"; + let mayStore = 1; +} + +class JALRC_HB_MMR6_DESC { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins GPR32Opnd:$rs); + string AsmString = !strconcat("jalrc.hb", "\t$rt, $rs"); + list Pattern = []; + InstrItinClass Itinerary = II_JALR_HB; + Format Form = FrmJ; + bit isIndirectBranch = 1; + bit hasDelaySlot = 0; +} + +class TLBINV_MMR6_DESC_BASE { + dag OutOperandList = (outs); + dag InOperandList = (ins); + string AsmString = opstr; + list Pattern = []; + InstrItinClass Itinerary = Itin; +} + +class TLBINV_MMR6_DESC : TLBINV_MMR6_DESC_BASE<"tlbinv", II_TLBINV>; +class TLBINVF_MMR6_DESC : TLBINV_MMR6_DESC_BASE<"tlbinvf", II_TLBINVF>; + +class DVPEVP_MMR6_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rs); + dag InOperandList = (ins); + string AsmString = !strconcat(opstr, "\t$rs"); + list Pattern = []; + InstrItinClass Itinerary = Itin; + bit hasUnModeledSideEffects = 1; +} + +class DVP_MMR6_DESC : DVPEVP_MMR6_DESC_BASE<"dvp", II_DVP>; +class EVP_MMR6_DESC : DVPEVP_MMR6_DESC_BASE<"evp", II_EVP>; + +class BEQZC_MMR6_DESC + : CMP_CBR_EQNE_Z_DESC_BASE<"beqzc", brtarget21_mm, GPR32Opnd>, + MMR6Arch<"beqzc">; +class BNEZC_MMR6_DESC + : CMP_CBR_EQNE_Z_DESC_BASE<"bnezc", brtarget21_mm, GPR32Opnd>, + MMR6Arch<"bnezc">; + +class BRANCH_COP1_MMR6_DESC_BASE : + InstSE<(outs), (ins FGR64Opnd:$rt, brtarget_mm:$offset), + !strconcat(opstr, "\t$rt, $offset"), [], II_BC1CCZ, FrmI>, + HARDFLOAT, BRANCH_DESC_BASE { + list Defs = [AT]; +} + +class BC1EQZC_MMR6_DESC : BRANCH_COP1_MMR6_DESC_BASE<"bc1eqzc">; +class BC1NEZC_MMR6_DESC : BRANCH_COP1_MMR6_DESC_BASE<"bc1nezc">; + +class BRANCH_COP2_MMR6_DESC_BASE + : BRANCH_DESC_BASE { + dag InOperandList = (ins COP2Opnd:$rt, brtarget_mm:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(opstr, "\t$rt, $offset"); + list Defs = [AT]; + InstrItinClass Itinerary = Itin; +} + +class BC2EQZC_MMR6_DESC : BRANCH_COP2_MMR6_DESC_BASE<"bc2eqzc", II_BC2CCZ>; +class BC2NEZC_MMR6_DESC : BRANCH_COP2_MMR6_DESC_BASE<"bc2nezc", II_BC2CCZ>; + +class EXT_MMR6_DESC { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins GPR32Opnd:$rs, uimm5:$pos, uimm5_plus1:$size); + string AsmString = !strconcat("ext", "\t$rt, $rs, $pos, $size"); + list Pattern = [(set GPR32Opnd:$rt, (MaxisExt GPR32Opnd:$rs, imm:$pos, + imm:$size))]; + InstrItinClass Itinerary = II_EXT; + Format Form = FrmR; + string BaseOpcode = "ext"; +} + +class INS_MMR6_DESC { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins GPR32Opnd:$rs, uimm5:$pos, uimm5_inssize_plus1:$size, + GPR32Opnd:$src); + string AsmString = !strconcat("ins", "\t$rt, $rs, $pos, $size"); + list Pattern = [(set GPR32Opnd:$rt, (MaxisIns GPR32Opnd:$rs, imm:$pos, + imm:$size, GPR32Opnd:$src))]; + InstrItinClass Itinerary = II_INS; + Format Form = FrmR; + string BaseOpcode = "ins"; + string Constraints = "$src = $rt"; +} + +class JALRC_MMR6_DESC { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins GPR32Opnd:$rs); + string AsmString = !strconcat("jalrc", "\t$rt, $rs"); + list Pattern = []; + InstrItinClass Itinerary = II_JALRC; + bit isCall = 1; + bit hasDelaySlot = 0; + list Defs = [RA]; +} + +class BOVC_BNVC_MMR6_DESC_BASE + : BRANCH_DESC_BASE { + dag InOperandList = (ins GPROpnd:$rt, GPROpnd:$rs, opnd:$offset); + dag OutOperandList = (outs); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs, $offset"); + list Defs = [AT]; + InstrItinClass Itinerary = II_BCCC; +} + +class BOVC_MMR6_DESC : BOVC_BNVC_MMR6_DESC_BASE<"bovc", brtargetr6, GPR32Opnd>; +class BNVC_MMR6_DESC : BOVC_BNVC_MMR6_DESC_BASE<"bnvc", brtargetr6, GPR32Opnd>; + +//===----------------------------------------------------------------------===// +// +// Instruction Definitions +// +//===----------------------------------------------------------------------===// + +let DecoderNamespace = "MicroMaxisR6" in { +def ADD_MMR6 : StdMMR6Rel, ADD_MMR6_DESC, ADD_MMR6_ENC, ISA_MICROMAXIS32R6; +def ADDIU_MMR6 : StdMMR6Rel, ADDIU_MMR6_DESC, ADDIU_MMR6_ENC, ISA_MICROMAXIS32R6; +def ADDU_MMR6 : StdMMR6Rel, ADDU_MMR6_DESC, ADDU_MMR6_ENC, ISA_MICROMAXIS32R6; +def ADDIUPC_MMR6 : R6MMR6Rel, ADDIUPC_MMR6_ENC, ADDIUPC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def ALUIPC_MMR6 : R6MMR6Rel, ALUIPC_MMR6_ENC, ALUIPC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def AND_MMR6 : StdMMR6Rel, AND_MMR6_DESC, AND_MMR6_ENC, ISA_MICROMAXIS32R6; +def ANDI_MMR6 : StdMMR6Rel, ANDI_MMR6_DESC, ANDI_MMR6_ENC, ISA_MICROMAXIS32R6; +def AUIPC_MMR6 : R6MMR6Rel, AUIPC_MMR6_ENC, AUIPC_MMR6_DESC, ISA_MICROMAXIS32R6; +def ALIGN_MMR6 : R6MMR6Rel, ALIGN_MMR6_ENC, ALIGN_MMR6_DESC, ISA_MICROMAXIS32R6; +def AUI_MMR6 : R6MMR6Rel, AUI_MMR6_ENC, AUI_MMR6_DESC, ISA_MICROMAXIS32R6; +def BALC_MMR6 : R6MMR6Rel, BALC_MMR6_ENC, BALC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BC_MMR6 : R6MMR6Rel, BC_MMR6_ENC, BC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BC16_MMR6 : StdMMR6Rel, BC16_MMR6_DESC, BC16_MMR6_ENC, ISA_MICROMAXIS32R6; +def BEQZC_MMR6 : R6MMR6Rel, BEQZC_MMR6_ENC, BEQZC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BEQZC16_MMR6 : StdMMR6Rel, BEQZC16_MMR6_DESC, BEQZC16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def BNEZC_MMR6 : R6MMR6Rel, BNEZC_MMR6_ENC, BNEZC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BNEZC16_MMR6 : StdMMR6Rel, BNEZC16_MMR6_DESC, BNEZC16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def BITSWAP_MMR6 : R6MMR6Rel, BITSWAP_MMR6_ENC, BITSWAP_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BEQZALC_MMR6 : R6MMR6Rel, BEQZALC_MMR6_ENC, BEQZALC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BNEZALC_MMR6 : R6MMR6Rel, BNEZALC_MMR6_ENC, BNEZALC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BREAK_MMR6 : StdMMR6Rel, BRK_MMR6_DESC, BRK_MMR6_ENC, ISA_MICROMAXIS32R6; +def CACHE_MMR6 : R6MMR6Rel, CACHE_MMR6_ENC, CACHE_MMR6_DESC, ISA_MICROMAXIS32R6; +def CLO_MMR6 : R6MMR6Rel, CLO_MMR6_ENC, CLO_MMR6_DESC, ISA_MICROMAXIS32R6; +def CLZ_MMR6 : R6MMR6Rel, CLZ_MMR6_ENC, CLZ_MMR6_DESC, ISA_MICROMAXIS32R6; +def DIV_MMR6 : R6MMR6Rel, DIV_MMR6_DESC, DIV_MMR6_ENC, ISA_MICROMAXIS32R6; +def DIVU_MMR6 : R6MMR6Rel, DIVU_MMR6_DESC, DIVU_MMR6_ENC, ISA_MICROMAXIS32R6; +def EHB_MMR6 : StdMMR6Rel, EHB_MMR6_DESC, EHB_MMR6_ENC, ISA_MICROMAXIS32R6; +def EI_MMR6 : StdMMR6Rel, EI_MMR6_DESC, EI_MMR6_ENC, ISA_MICROMAXIS32R6; +def DI_MMR6 : StdMMR6Rel, DI_MMR6_DESC, DI_MMR6_ENC, ISA_MICROMAXIS32R6; +def ERET_MMR6 : StdMMR6Rel, ERET_MMR6_DESC, ERET_MMR6_ENC, ISA_MICROMAXIS32R6; +def DERET_MMR6 : StdMMR6Rel, DERET_MMR6_DESC, DERET_MMR6_ENC, ISA_MICROMAXIS32R6; +def ERETNC_MMR6 : R6MMR6Rel, ERETNC_MMR6_DESC, ERETNC_MMR6_ENC, + ISA_MICROMAXIS32R6; +def JALRC16_MMR6 : R6MMR6Rel, JALRC16_MMR6_DESC, JALRC16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def JIALC_MMR6 : R6MMR6Rel, JIALC_MMR6_ENC, JIALC_MMR6_DESC, ISA_MICROMAXIS32R6; +def JIC_MMR6 : R6MMR6Rel, JIC_MMR6_ENC, JIC_MMR6_DESC, ISA_MICROMAXIS32R6; +def JRC16_MMR6 : R6MMR6Rel, JRC16_MMR6_DESC, JRC16_MMR6_ENC, ISA_MICROMAXIS32R6; +def JRCADDIUSP_MMR6 : R6MMR6Rel, JRCADDIUSP_MMR6_DESC, JRCADDIUSP_MMR6_ENC, + ISA_MICROMAXIS32R6; +def LSA_MMR6 : R6MMR6Rel, LSA_MMR6_ENC, LSA_MMR6_DESC, ISA_MICROMAXIS32R6; +def LWP_MMR6 : StdMMR6Rel, LWP_MMR6_ENC, LWP_MMR6_DESC, ISA_MICROMAXIS32R6; +def LWPC_MMR6 : R6MMR6Rel, LWPC_MMR6_ENC, LWPC_MMR6_DESC, ISA_MICROMAXIS32R6; +def LWM16_MMR6 : StdMMR6Rel, LWM16_MMR6_DESC, LWM16_MMR6_ENC, ISA_MICROMAXIS32R6; +def MTC0_MMR6 : StdMMR6Rel, MTC0_MMR6_ENC, MTC0_MMR6_DESC, ISA_MICROMAXIS32R6; +def MTC1_MMR6 : StdMMR6Rel, MTC1_MMR6_DESC, MTC1_MMR6_ENC, ISA_MICROMAXIS32R6; +def MTC2_MMR6 : StdMMR6Rel, MTC2_MMR6_ENC, MTC2_MMR6_DESC, ISA_MICROMAXIS32R6; +def MTHC0_MMR6 : R6MMR6Rel, MTHC0_MMR6_ENC, MTHC0_MMR6_DESC, ISA_MICROMAXIS32R6; +def MTHC1_D32_MMR6 : StdMMR6Rel, MTHC1_D32_MMR6_DESC, MTHC1_MMR6_ENC, ISA_MICROMAXIS32R6; +let DecoderNamespace = "MicroMaxisFP64" in { + def MTHC1_D64_MMR6 : R6MMR6Rel, MTHC1_D64_MMR6_DESC, MTHC1_MMR6_ENC, + ISA_MICROMAXIS32R6; +} +def MTHC2_MMR6 : StdMMR6Rel, MTHC2_MMR6_ENC, MTHC2_MMR6_DESC, ISA_MICROMAXIS32R6; +def MFC0_MMR6 : StdMMR6Rel, MFC0_MMR6_ENC, MFC0_MMR6_DESC, ISA_MICROMAXIS32R6; +def MFC1_MMR6 : StdMMR6Rel, MFC1_MMR6_DESC, MFC1_MMR6_ENC, ISA_MICROMAXIS32R6; +def MFC2_MMR6 : StdMMR6Rel, MFC2_MMR6_ENC, MFC2_MMR6_DESC, ISA_MICROMAXIS32R6; +def MFHC0_MMR6 : R6MMR6Rel, MFHC0_MMR6_ENC, MFHC0_MMR6_DESC, ISA_MICROMAXIS32R6; +def MFHC1_D32_MMR6 : StdMMR6Rel, MFHC1_D32_MMR6_DESC, MFHC1_MMR6_ENC, + ISA_MICROMAXIS32R6; +let DecoderNamespace = "MicroMaxisFP64" in { + def MFHC1_D64_MMR6 : StdMMR6Rel, MFHC1_D64_MMR6_DESC, MFHC1_MMR6_ENC, + ISA_MICROMAXIS32R6; +} +def MFHC2_MMR6 : StdMMR6Rel, MFHC2_MMR6_ENC, MFHC2_MMR6_DESC, ISA_MICROMAXIS32R6; +def MOD_MMR6 : R6MMR6Rel, MOD_MMR6_DESC, MOD_MMR6_ENC, ISA_MICROMAXIS32R6; +def MODU_MMR6 : R6MMR6Rel, MODU_MMR6_DESC, MODU_MMR6_ENC, ISA_MICROMAXIS32R6; +def MUL_MMR6 : R6MMR6Rel, MUL_MMR6_DESC, MUL_MMR6_ENC, ISA_MICROMAXIS32R6; +def MUH_MMR6 : R6MMR6Rel, MUH_MMR6_DESC, MUH_MMR6_ENC, ISA_MICROMAXIS32R6; +def MULU_MMR6 : R6MMR6Rel, MULU_MMR6_DESC, MULU_MMR6_ENC, ISA_MICROMAXIS32R6; +def MUHU_MMR6 : R6MMR6Rel, MUHU_MMR6_DESC, MUHU_MMR6_ENC, ISA_MICROMAXIS32R6; +def NOR_MMR6 : StdMMR6Rel, NOR_MMR6_DESC, NOR_MMR6_ENC, ISA_MICROMAXIS32R6; +def OR_MMR6 : StdMMR6Rel, OR_MMR6_DESC, OR_MMR6_ENC, ISA_MICROMAXIS32R6; +def ORI_MMR6 : StdMMR6Rel, ORI_MMR6_DESC, ORI_MMR6_ENC, ISA_MICROMAXIS32R6; +def PREF_MMR6 : R6MMR6Rel, PREF_MMR6_ENC, PREF_MMR6_DESC, ISA_MICROMAXIS32R6; +def SB16_MMR6 : StdMMR6Rel, SB16_MMR6_DESC, SB16_MMR6_ENC, ISA_MICROMAXIS32R6; +def SEB_MMR6 : StdMMR6Rel, SEB_MMR6_DESC, SEB_MMR6_ENC, ISA_MICROMAXIS32R6; +def SEH_MMR6 : StdMMR6Rel, SEH_MMR6_DESC, SEH_MMR6_ENC, ISA_MICROMAXIS32R6; +def SELEQZ_MMR6 : R6MMR6Rel, SELEQZ_MMR6_ENC, SELEQZ_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SELNEZ_MMR6 : R6MMR6Rel, SELNEZ_MMR6_ENC, SELNEZ_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SH16_MMR6 : StdMMR6Rel, SH16_MMR6_DESC, SH16_MMR6_ENC, ISA_MICROMAXIS32R6; +def SLL_MMR6 : StdMMR6Rel, SLL_MMR6_DESC, SLL_MMR6_ENC, ISA_MICROMAXIS32R6; +def SUB_MMR6 : StdMMR6Rel, SUB_MMR6_DESC, SUB_MMR6_ENC, ISA_MICROMAXIS32R6; +def SUBU_MMR6 : StdMMR6Rel, SUBU_MMR6_DESC, SUBU_MMR6_ENC, ISA_MICROMAXIS32R6; +def SW16_MMR6 : StdMMR6Rel, SW16_MMR6_DESC, SW16_MMR6_ENC, ISA_MICROMAXIS32R6; +def SWM16_MMR6 : StdMMR6Rel, SWM16_MMR6_DESC, SWM16_MMR6_ENC, ISA_MICROMAXIS32R6; +def SWSP_MMR6 : StdMMR6Rel, SWSP_MMR6_DESC, SWSP_MMR6_ENC, ISA_MICROMAXIS32R6; +def SWP_MMR6 : StdMMR6Rel, SWP_MMR6_ENC, SWP_MMR6_DESC, ISA_MICROMAXIS32R6; +def PREFE_MMR6 : StdMMR6Rel, PREFE_MMR6_ENC, PREFE_MMR6_DESC, ISA_MICROMAXIS32R6; +def CACHEE_MMR6 : StdMMR6Rel, CACHEE_MMR6_ENC, CACHEE_MMR6_DESC, + ISA_MICROMAXIS32R6; +def WRPGPR_MMR6 : StdMMR6Rel, WRPGPR_MMR6_ENC, WRPGPR_MMR6_DESC, + ISA_MICROMAXIS32R6; +def WSBH_MMR6 : StdMMR6Rel, WSBH_MMR6_ENC, WSBH_MMR6_DESC, ISA_MICROMAXIS32R6; +def LB_MMR6 : R6MMR6Rel, LB_MMR6_ENC, LB_MMR6_DESC, ISA_MICROMAXIS32R6; +def LBU_MMR6 : R6MMR6Rel, LBU_MMR6_ENC, LBU_MMR6_DESC, ISA_MICROMAXIS32R6; +def LBE_MMR6 : R6MMR6Rel, LBE_MMR6_ENC, LBE_MMR6_DESC, ISA_MICROMAXIS32R6; +def LBUE_MMR6 : R6MMR6Rel, LBUE_MMR6_ENC, LBUE_MMR6_DESC, ISA_MICROMAXIS32R6; +def PAUSE_MMR6 : StdMMR6Rel, PAUSE_MMR6_DESC, PAUSE_MMR6_ENC, ISA_MICROMAXIS32R6; +def RDHWR_MMR6 : R6MMR6Rel, RDHWR_MMR6_DESC, RDHWR_MMR6_ENC, ISA_MICROMAXIS32R6; +def WAIT_MMR6 : StdMMR6Rel, WAIT_MMR6_DESC, WAIT_MMR6_ENC, ISA_MICROMAXIS32R6; +def SSNOP_MMR6 : StdMMR6Rel, SSNOP_MMR6_DESC, SSNOP_MMR6_ENC, ISA_MICROMAXIS32R6; +def SYNC_MMR6 : StdMMR6Rel, SYNC_MMR6_DESC, SYNC_MMR6_ENC, ISA_MICROMAXIS32R6; +def SYNCI_MMR6 : StdMMR6Rel, SYNCI_MMR6_DESC, SYNCI_MMR6_ENC, ISA_MICROMAXIS32R6; +def RDPGPR_MMR6 : R6MMR6Rel, RDPGPR_MMR6_DESC, RDPGPR_MMR6_ENC, + ISA_MICROMAXIS32R6; +def SDBBP_MMR6 : R6MMR6Rel, SDBBP_MMR6_DESC, SDBBP_MMR6_ENC, ISA_MICROMAXIS32R6; +def XOR_MMR6 : StdMMR6Rel, XOR_MMR6_DESC, XOR_MMR6_ENC, ISA_MICROMAXIS32R6; +def XORI_MMR6 : StdMMR6Rel, XORI_MMR6_DESC, XORI_MMR6_ENC, ISA_MICROMAXIS32R6; +let DecoderMethod = "DecodeMemMMImm16" in { + def SW_MMR6 : StdMMR6Rel, SW_MMR6_DESC, SW_MMR6_ENC, ISA_MICROMAXIS32R6; +} +let DecoderMethod = "DecodeMemMMImm9" in { + def SWE_MMR6 : StdMMR6Rel, SWE_MMR6_DESC, SWE_MMR6_ENC, ISA_MICROMAXIS32R6; +} +/// Floating Point Instructions +def FADD_S_MMR6 : StdMMR6Rel, FADD_S_MMR6_ENC, FADD_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FADD_D_MMR6 : StdMMR6Rel, FADD_D_MMR6_ENC, FADD_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FSUB_S_MMR6 : StdMMR6Rel, FSUB_S_MMR6_ENC, FSUB_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FSUB_D_MMR6 : StdMMR6Rel, FSUB_D_MMR6_ENC, FSUB_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FMUL_S_MMR6 : StdMMR6Rel, FMUL_S_MMR6_ENC, FMUL_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FMUL_D_MMR6 : StdMMR6Rel, FMUL_D_MMR6_ENC, FMUL_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FDIV_S_MMR6 : StdMMR6Rel, FDIV_S_MMR6_ENC, FDIV_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FDIV_D_MMR6 : StdMMR6Rel, FDIV_D_MMR6_ENC, FDIV_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def MADDF_S_MMR6 : R6MMR6Rel, MADDF_S_MMR6_ENC, MADDF_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def MADDF_D_MMR6 : R6MMR6Rel, MADDF_D_MMR6_ENC, MADDF_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def MSUBF_S_MMR6 : R6MMR6Rel, MSUBF_S_MMR6_ENC, MSUBF_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def MSUBF_D_MMR6 : R6MMR6Rel, MSUBF_D_MMR6_ENC, MSUBF_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FMOV_S_MMR6 : StdMMR6Rel, FMOV_S_MMR6_ENC, FMOV_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FMOV_D_MMR6 : StdMMR6Rel, FMOV_D_MMR6_ENC, FMOV_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FNEG_S_MMR6 : StdMMR6Rel, FNEG_S_MMR6_ENC, FNEG_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FNEG_D_MMR6 : StdMMR6Rel, FNEG_D_MMR6_ENC, FNEG_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def MAX_S_MMR6 : R6MMR6Rel, MAX_S_MMR6_ENC, MAX_S_MMR6_DESC, ISA_MICROMAXIS32R6; +def MAX_D_MMR6 : R6MMR6Rel, MAX_D_MMR6_ENC, MAX_D_MMR6_DESC, ISA_MICROMAXIS32R6; +def MIN_S_MMR6 : R6MMR6Rel, MIN_S_MMR6_ENC, MIN_S_MMR6_DESC, ISA_MICROMAXIS32R6; +def MIN_D_MMR6 : R6MMR6Rel, MIN_D_MMR6_ENC, MIN_D_MMR6_DESC, ISA_MICROMAXIS32R6; +def MAXA_S_MMR6 : R6MMR6Rel, MAXA_S_MMR6_ENC, MAXA_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def MAXA_D_MMR6 : R6MMR6Rel, MAXA_D_MMR6_ENC, MAXA_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def MINA_S_MMR6 : R6MMR6Rel, MINA_S_MMR6_ENC, MINA_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def MINA_D_MMR6 : R6MMR6Rel, MINA_D_MMR6_ENC, MINA_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_L_S_MMR6 : StdMMR6Rel, CVT_L_S_MMR6_ENC, CVT_L_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_L_D_MMR6 : StdMMR6Rel, CVT_L_D_MMR6_ENC, CVT_L_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_W_S_MMR6 : StdMMR6Rel, CVT_W_S_MMR6_ENC, CVT_W_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_W_D_MMR6 : StdMMR6Rel, CVT_W_D_MMR6_ENC, CVT_W_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_D_S_MMR6 : StdMMR6Rel, CVT_D_S_MMR6_ENC, CVT_D_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_D_W_MMR6 : StdMMR6Rel, CVT_D_W_MMR6_ENC, CVT_D_W_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_D_L_MMR6 : StdMMR6Rel, CVT_D_L_MMR6_ENC, CVT_D_L_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_S_D_MMR6 : StdMMR6Rel, CVT_S_D_MMR6_ENC, CVT_S_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_S_W_MMR6 : StdMMR6Rel, CVT_S_W_MMR6_ENC, CVT_S_W_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CVT_S_L_MMR6 : StdMMR6Rel, CVT_S_L_MMR6_ENC, CVT_S_L_MMR6_DESC, + ISA_MICROMAXIS32R6; +defm S_MMR6 : CMP_CC_MMR6<0b000101, "s", FGR32Opnd, II_CMP_CC_S>; +defm D_MMR6 : CMP_CC_MMR6<0b010101, "d", FGR64Opnd, II_CMP_CC_D>; +def ABS_S_MMR6 : StdMMR6Rel, ABS_S_MMR6_ENC, ABS_S_MMR6_DESC, ISA_MICROMAXIS32R6; +def ABS_D_MMR6 : StdMMR6Rel, ABS_D_MMR6_ENC, ABS_D_MMR6_DESC, ISA_MICROMAXIS32R6; +def FLOOR_L_S_MMR6 : StdMMR6Rel, FLOOR_L_S_MMR6_ENC, FLOOR_L_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FLOOR_L_D_MMR6 : StdMMR6Rel, FLOOR_L_D_MMR6_ENC, FLOOR_L_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FLOOR_W_S_MMR6 : StdMMR6Rel, FLOOR_W_S_MMR6_ENC, FLOOR_W_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def FLOOR_W_D_MMR6 : StdMMR6Rel, FLOOR_W_D_MMR6_ENC, FLOOR_W_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CEIL_L_S_MMR6 : StdMMR6Rel, CEIL_L_S_MMR6_ENC, CEIL_L_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CEIL_L_D_MMR6 : StdMMR6Rel, CEIL_L_D_MMR6_ENC, CEIL_L_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CEIL_W_S_MMR6 : StdMMR6Rel, CEIL_W_S_MMR6_ENC, CEIL_W_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CEIL_W_D_MMR6 : StdMMR6Rel, CEIL_W_D_MMR6_ENC, CEIL_W_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def TRUNC_L_S_MMR6 : StdMMR6Rel, TRUNC_L_S_MMR6_ENC, TRUNC_L_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def TRUNC_L_D_MMR6 : StdMMR6Rel, TRUNC_L_D_MMR6_ENC, TRUNC_L_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def TRUNC_W_S_MMR6 : StdMMR6Rel, TRUNC_W_S_MMR6_ENC, TRUNC_W_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def TRUNC_W_D_MMR6 : StdMMR6Rel, TRUNC_W_D_MMR6_ENC, TRUNC_W_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SQRT_S_MMR6 : StdMMR6Rel, SQRT_S_MMR6_ENC, SQRT_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SQRT_D_MMR6 : StdMMR6Rel, SQRT_D_MMR6_ENC, SQRT_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SB_MMR6 : StdMMR6Rel, SB_MMR6_DESC, SB_MMR6_ENC, ISA_MICROMAXIS32R6; +def SBE_MMR6 : StdMMR6Rel, SBE_MMR6_DESC, SBE_MMR6_ENC, ISA_MICROMAXIS32R6; +def SCE_MMR6 : StdMMR6Rel, SCE_MMR6_DESC, SCE_MMR6_ENC, ISA_MICROMAXIS32R6; +def SH_MMR6 : StdMMR6Rel, SH_MMR6_DESC, SH_MMR6_ENC, ISA_MICROMAXIS32R6; +def SHE_MMR6 : StdMMR6Rel, SHE_MMR6_DESC, SHE_MMR6_ENC, ISA_MICROMAXIS32R6; +def LLE_MMR6 : StdMMR6Rel, LLE_MMR6_DESC, LLE_MMR6_ENC, ISA_MICROMAXIS32R6; +def LWE_MMR6 : StdMMR6Rel, LWE_MMR6_DESC, LWE_MMR6_ENC, ISA_MICROMAXIS32R6; +def LW_MMR6 : StdMMR6Rel, LW_MMR6_DESC, LW_MMR6_ENC, ISA_MICROMAXIS32R6; +def LUI_MMR6 : R6MMR6Rel, LUI_MMR6_DESC, LUI_MMR6_ENC, ISA_MICROMAXIS32R6; +def ADDU16_MMR6 : StdMMR6Rel, ADDU16_MMR6_DESC, ADDU16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def AND16_MMR6 : StdMMR6Rel, AND16_MMR6_DESC, AND16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def ANDI16_MMR6 : StdMMR6Rel, ANDI16_MMR6_DESC, ANDI16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def NOT16_MMR6 : StdMMR6Rel, NOT16_MMR6_DESC, NOT16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def OR16_MMR6 : StdMMR6Rel, OR16_MMR6_DESC, OR16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def SLL16_MMR6 : StdMMR6Rel, SLL16_MMR6_DESC, SLL16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def SRL16_MMR6 : StdMMR6Rel, SRL16_MMR6_DESC, SRL16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def BREAK16_MMR6 : StdMMR6Rel, BREAK16_MMR6_DESC, BREAK16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def LI16_MMR6 : StdMMR6Rel, LI16_MMR6_DESC, LI16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def MOVE16_MMR6 : StdMMR6Rel, MOVE16_MMR6_DESC, MOVE16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def MOVEP_MMR6 : StdMMR6Rel, MOVEP_MMR6_DESC, MOVEP_MMR6_ENC, + ISA_MICROMAXIS32R6; +def SDBBP16_MMR6 : StdMMR6Rel, SDBBP16_MMR6_DESC, SDBBP16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def SUBU16_MMR6 : StdMMR6Rel, SUBU16_MMR6_DESC, SUBU16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def XOR16_MMR6 : StdMMR6Rel, XOR16_MMR6_DESC, XOR16_MMR6_ENC, + ISA_MICROMAXIS32R6; +def JALRC_HB_MMR6 : R6MMR6Rel, JALRC_HB_MMR6_ENC, JALRC_HB_MMR6_DESC, + ISA_MICROMAXIS32R6; +def EXT_MMR6 : StdMMR6Rel, EXT_MMR6_ENC, EXT_MMR6_DESC, ISA_MICROMAXIS32R6; +def INS_MMR6 : StdMMR6Rel, INS_MMR6_ENC, INS_MMR6_DESC, ISA_MICROMAXIS32R6; +def JALRC_MMR6 : R6MMR6Rel, JALRC_MMR6_ENC, JALRC_MMR6_DESC, ISA_MICROMAXIS32R6; +def RINT_S_MMR6 : StdMMR6Rel, RINT_S_MMR6_ENC, RINT_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def RINT_D_MMR6 : StdMMR6Rel, RINT_D_MMR6_ENC, RINT_D_MMR6_DESC, ISA_MICROMAXIS32R6; +def ROUND_L_S_MMR6 : StdMMR6Rel, ROUND_L_S_MMR6_ENC, ROUND_L_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def ROUND_L_D_MMR6 : StdMMR6Rel, ROUND_L_D_MMR6_ENC, ROUND_L_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def ROUND_W_S_MMR6 : StdMMR6Rel, ROUND_W_S_MMR6_ENC, ROUND_W_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def ROUND_W_D_MMR6 : StdMMR6Rel, ROUND_W_D_MMR6_ENC, ROUND_W_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SEL_S_MMR6 : R6MMR6Rel, SEL_S_MMR6_ENC, SEL_S_MMR6_DESC, ISA_MICROMAXIS32R6; +def SEL_D_MMR6 : R6MMR6Rel, SEL_D_MMR6_ENC, SEL_D_MMR6_DESC, ISA_MICROMAXIS32R6; +def SELEQZ_S_MMR6 : R6MMR6Rel, SELEQZ_S_MMR6_ENC, SELEQZ_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SELEQZ_D_MMR6 : R6MMR6Rel, SELEQZ_D_MMR6_ENC, SELEQZ_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SELNEZ_S_MMR6 : R6MMR6Rel, SELNEZ_S_MMR6_ENC, SELNEZ_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def SELNEZ_D_MMR6 : R6MMR6Rel, SELNEZ_D_MMR6_ENC, SELNEZ_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CLASS_S_MMR6 : StdMMR6Rel, CLASS_S_MMR6_ENC, CLASS_S_MMR6_DESC, + ISA_MICROMAXIS32R6; +def CLASS_D_MMR6 : StdMMR6Rel, CLASS_D_MMR6_ENC, CLASS_D_MMR6_DESC, + ISA_MICROMAXIS32R6; +def TLBINV_MMR6 : StdMMR6Rel, TLBINV_MMR6_ENC, TLBINV_MMR6_DESC, + ISA_MICROMAXIS32R6; +def TLBINVF_MMR6 : StdMMR6Rel, TLBINVF_MMR6_ENC, TLBINVF_MMR6_DESC, + ISA_MICROMAXIS32R6; +def DVP_MMR6 : R6MMR6Rel, DVP_MMR6_ENC, DVP_MMR6_DESC, ISA_MICROMAXIS32R6; +def EVP_MMR6 : R6MMR6Rel, EVP_MMR6_ENC, EVP_MMR6_DESC, ISA_MICROMAXIS32R6; +def BC1EQZC_MMR6 : R6MMR6Rel, BC1EQZC_MMR6_DESC, BC1EQZC_MMR6_ENC, + ISA_MICROMAXIS32R6; +def BC1NEZC_MMR6 : R6MMR6Rel, BC1NEZC_MMR6_DESC, BC1NEZC_MMR6_ENC, + ISA_MICROMAXIS32R6; +def BC2EQZC_MMR6 : R6MMR6Rel, MaxisR6Inst, BC2EQZC_MMR6_ENC, BC2EQZC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BC2NEZC_MMR6 : R6MMR6Rel, MaxisR6Inst, BC2NEZC_MMR6_ENC, BC2NEZC_MMR6_DESC, + ISA_MICROMAXIS32R6; +let DecoderNamespace = "MicroMaxisFP64" in { + def LDC1_D64_MMR6 : StdMMR6Rel, LDC1_D64_MMR6_DESC, LDC1_MMR6_ENC, + ISA_MICROMAXIS32R6 { + let BaseOpcode = "LDC164"; + } + def SDC1_D64_MMR6 : StdMMR6Rel, SDC1_D64_MMR6_DESC, SDC1_MMR6_ENC, + ISA_MICROMAXIS32R6; +} +def LDC2_MMR6 : StdMMR6Rel, LDC2_MMR6_ENC, LDC2_MMR6_DESC, ISA_MICROMAXIS32R6; +def SDC2_MMR6 : StdMMR6Rel, SDC2_MMR6_ENC, SDC2_MMR6_DESC, ISA_MICROMAXIS32R6; +def LWC2_MMR6 : StdMMR6Rel, LWC2_MMR6_ENC, LWC2_MMR6_DESC, ISA_MICROMAXIS32R6; +def SWC2_MMR6 : StdMMR6Rel, SWC2_MMR6_ENC, SWC2_MMR6_DESC, ISA_MICROMAXIS32R6; +} + +def BOVC_MMR6 : R6MMR6Rel, BOVC_MMR6_ENC, BOVC_MMR6_DESC, ISA_MICROMAXIS32R6, + MMDecodeDisambiguatedBy<"POP35GroupBranchMMR6">; +def BNVC_MMR6 : R6MMR6Rel, BNVC_MMR6_ENC, BNVC_MMR6_DESC, ISA_MICROMAXIS32R6, + MMDecodeDisambiguatedBy<"POP37GroupBranchMMR6">; +def BGEC_MMR6 : R6MMR6Rel, BGEC_MMR6_ENC, BGEC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BGEUC_MMR6 : R6MMR6Rel, BGEUC_MMR6_ENC, BGEUC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BLTC_MMR6 : R6MMR6Rel, BLTC_MMR6_ENC, BLTC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BLTUC_MMR6 : R6MMR6Rel, BLTUC_MMR6_ENC, BLTUC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BEQC_MMR6 : R6MMR6Rel, BEQC_MMR6_ENC, BEQC_MMR6_DESC, ISA_MICROMAXIS32R6, + DecodeDisambiguates<"POP35GroupBranchMMR6">; +def BNEC_MMR6 : R6MMR6Rel, BNEC_MMR6_ENC, BNEC_MMR6_DESC, ISA_MICROMAXIS32R6, + DecodeDisambiguates<"POP37GroupBranchMMR6">; +def BLTZC_MMR6 : R6MMR6Rel, BLTZC_MMR6_ENC, BLTZC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BLEZC_MMR6 : R6MMR6Rel, BLEZC_MMR6_ENC, BLEZC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BGEZC_MMR6 : R6MMR6Rel, BGEZC_MMR6_ENC, BGEZC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BGTZC_MMR6 : R6MMR6Rel, BGTZC_MMR6_ENC, BGTZC_MMR6_DESC, ISA_MICROMAXIS32R6; +def BGEZALC_MMR6 : R6MMR6Rel, BGEZALC_MMR6_ENC, BGEZALC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BGTZALC_MMR6 : R6MMR6Rel, BGTZALC_MMR6_ENC, BGTZALC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BLEZALC_MMR6 : R6MMR6Rel, BLEZALC_MMR6_ENC, BLEZALC_MMR6_DESC, + ISA_MICROMAXIS32R6; +def BLTZALC_MMR6 : R6MMR6Rel, BLTZALC_MMR6_ENC, BLTZALC_MMR6_DESC, + ISA_MICROMAXIS32R6; + +//===----------------------------------------------------------------------===// +// +// MicroMaxis instruction aliases +// +//===----------------------------------------------------------------------===// + +def : MaxisInstAlias<"ei", (EI_MMR6 ZERO), 1>, ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"di", (DI_MMR6 ZERO), 1>, ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"nop", (SLL_MMR6 ZERO, ZERO, 0), 1>, ISA_MICROMAXIS32R6; +def B_MMR6_Pseudo : MaxisAsmPseudoInst<(outs), (ins brtarget_mm:$offset), + !strconcat("b", "\t$offset")> { + string DecoderNamespace = "MicroMaxisR6"; +} +def : MaxisInstAlias<"sync", (SYNC_MMR6 0), 1>, ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"sdbbp", (SDBBP_MMR6 0), 1>, ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"rdhwr $rt, $rs", + (RDHWR_MMR6 GPR32Opnd:$rt, HWRegsOpnd:$rs, 0), 1>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"mtc0 $rt, $rs", + (MTC0_MMR6 COP0Opnd:$rs, GPR32Opnd:$rt, 0), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"mthc0 $rt, $rs", + (MTHC0_MMR6 COP0Opnd:$rs, GPR32Opnd:$rt, 0), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"mfc0 $rt, $rs", + (MFC0_MMR6 GPR32Opnd:$rt, COP0Opnd:$rs, 0), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"mfhc0 $rt, $rs", + (MFHC0_MMR6 GPR32Opnd:$rt, COP0Opnd:$rs, 0), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"jalrc.hb $rs", (JALRC_HB_MMR6 RA, GPR32Opnd:$rs), 1>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"dvp", (DVP_MMR6 ZERO), 0>, ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"evp", (EVP_MMR6 ZERO), 0>, ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"jalrc $rs", (JALRC_MMR6 RA, GPR32Opnd:$rs), 1>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"and $rs, $rt, $imm", + (ANDI_MMR6 GPR32Opnd:$rs, GPR32Opnd:$rt, uimm16:$imm), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"and $rs, $imm", + (ANDI_MMR6 GPR32Opnd:$rs, GPR32Opnd:$rs, uimm16:$imm), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"or $rs, $rt, $imm", + (ORI_MMR6 GPR32Opnd:$rs, GPR32Opnd:$rt, uimm16:$imm), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"or $rs, $imm", + (ORI_MMR6 GPR32Opnd:$rs, GPR32Opnd:$rs, uimm16:$imm), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"xor $rs, $rt, $imm", + (XORI_MMR6 GPR32Opnd:$rs, GPR32Opnd:$rt, uimm16:$imm), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"xor $rs, $imm", + (XORI_MMR6 GPR32Opnd:$rs, GPR32Opnd:$rs, uimm16:$imm), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"not $rt, $rs", + (NOR_MMR6 GPR32Opnd:$rt, GPR32Opnd:$rs, ZERO), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"seh $rd", (SEH_MMR6 GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"seb $rd", (SEB_MMR6 GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, + ISA_MICROMAXIS32R6; +def : MaxisInstAlias<"lapc $rd, $imm", + (ADDIUPC_MMR6 GPR32Opnd:$rd, simm19_lsl2:$imm)>, + ISA_MICROMAXIS32R6; + +//===----------------------------------------------------------------------===// +// +// MicroMaxis arbitrary patterns that map to one or more instructions +// +//===----------------------------------------------------------------------===// + +def : MaxisPat<(store GPRMM16:$src, addrimm4lsl2:$addr), + (SW16_MMR6 GPRMM16:$src, addrimm4lsl2:$addr)>, ISA_MICROMAXIS32R6; +def : MaxisPat<(subc GPR32:$lhs, GPR32:$rhs), + (SUBU_MMR6 GPR32:$lhs, GPR32:$rhs)>, ISA_MICROMAXIS32R6; + +def : MaxisPat<(select i32:$cond, i32:$t, i32:$f), + (OR_MM (SELNEZ_MMR6 i32:$t, i32:$cond), + (SELEQZ_MMR6 i32:$f, i32:$cond))>, + ISA_MICROMAXIS32R6; +def : MaxisPat<(select i32:$cond, i32:$t, immz), + (SELNEZ_MMR6 i32:$t, i32:$cond)>, + ISA_MICROMAXIS32R6; +def : MaxisPat<(select i32:$cond, immz, i32:$f), + (SELEQZ_MMR6 i32:$f, i32:$cond)>, + ISA_MICROMAXIS32R6; + +defm : SelectInt_Pats, ISA_MICROMAXIS32R6; + +defm S_MMR6 : Cmp_Pats, ISA_MICROMAXIS32R6; +defm D_MMR6 : Cmp_Pats, ISA_MICROMAXIS32R6; + +def : MaxisPat<(and GPRMM16:$src, immZExtAndi16:$imm), + (ANDI16_MMR6 GPRMM16:$src, immZExtAndi16:$imm)>, + ISA_MICROMAXIS32R6; +def : MaxisPat<(and GPR32:$src, immZExt16:$imm), + (ANDI_MMR6 GPR32:$src, immZExt16:$imm)>, ISA_MICROMAXIS32R6; +def : MaxisPat<(i32 immZExt16:$imm), + (XORI_MMR6 ZERO, immZExt16:$imm)>, ISA_MICROMAXIS32R6; +def : MaxisPat<(not GPRMM16:$in), + (NOT16_MMR6 GPRMM16:$in)>, ISA_MICROMAXIS32R6; +def : MaxisPat<(not GPR32:$in), + (NOR_MMR6 GPR32Opnd:$in, ZERO)>, ISA_MICROMAXIS32R6; +// Patterns for load with a reg+imm operand. +let AddedComplexity = 41 in { + def : LoadRegImmPat, FGR_64, ISA_MICROMAXIS32R6; + def : StoreRegImmPat, FGR_64, ISA_MICROMAXIS32R6; +} + +def TAILCALL_MMR6 : TailCall, ISA_MICROMAXIS32R6; + +def : MaxisPat<(MaxisTailCall (iPTR tglobaladdr:$dst)), + (TAILCALL_MMR6 tglobaladdr:$dst)>, ISA_MICROMAXIS32R6; + +def : MaxisPat<(MaxisTailCall (iPTR texternalsym:$dst)), + (TAILCALL_MMR6 texternalsym:$dst)>, ISA_MICROMAXIS32R6; + diff --git a/lib/Target/Maxis/MicroMaxisDSPInstrFormats.td b/lib/Target/Maxis/MicroMaxisDSPInstrFormats.td new file mode 100644 index 00000000..c372032a --- /dev/null +++ b/lib/Target/Maxis/MicroMaxisDSPInstrFormats.td @@ -0,0 +1,302 @@ +//===-- MicroMaxisDSPInstrFormats.td - Instruction Formats --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +class MMDSPInst + : MaxisInst<(outs), (ins), "", [], NoItinerary, FrmOther>, PredicateControl { + let InsnPredicates = [HasDSP]; + let AdditionalPredicates = [InMicroMaxis]; + string BaseOpcode = opstr; + string Arch = "mmdsp"; + let DecoderNamespace = "MicroMaxis"; +} + +class MMDSPInstAlias + : InstAlias, PredicateControl { + let InsnPredicates = [HasDSP]; + let AdditionalPredicates = [InMicroMaxis]; +} + +class POOL32A_3R_FMT op> : MMDSPInst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = rd; + let Inst{10-0} = op; +} + +class POOL32A_2R_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-6} = op; + let Inst{5-0} = 0b111100; +} + +class POOL32A_2RAC_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<2> ac; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-14} = ac; + let Inst{13-6} = op; + let Inst{5-0} = 0b111100; +} + +class POOL32A_3RB0_FMT op> : MMDSPInst { + bits<5> rd; + bits<5> rs; + bits<5> rt; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = rd; + let Inst{10} = 0b0; + let Inst{9-0} = op; +} + +class POOL32A_2RSA4_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<4> sa; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-12} = sa; + let Inst{11-0} = op; +} + +class POOL32A_2RSA3_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<3> sa; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-13} = sa; + let Inst{12-6} = op; + let Inst{5-0} = 0b111100; +} + +class POOL32A_2RSA5B0_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<5> sa; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = sa; + let Inst{10} = 0b0; + let Inst{9-0} = op; +} + +class POOL32A_2RSA4B0_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<4> sa; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-12} = sa; + let Inst{11} = 0b0; + let Inst{10-0} = op; +} + +class POOL32A_2RSA4OP6_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<4> sa; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-12} = sa; + let Inst{11-6} = op; + let Inst{5-0} = 0b111100; +} + +class POOL32A_1RIMM5AC_FMT funct> : MMDSPInst { + bits<5> rt; + bits<5> imm; + bits<2> ac; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = imm; + let Inst{15-14} = ac; + let Inst{13-6} = funct; + let Inst{5-0} = 0b111100; +} + +class POOL32A_2RSA5_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<5> sa; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = sa; + let Inst{10-0} = op; +} + +class POOL32A_1RMEMB0_FMT funct> : MMDSPInst { + bits<5> index; + bits<5> base; + bits<5> rd; + + let Inst{31-26} = 0; + let Inst{25-21} = index; + let Inst{20-16} = base; + let Inst{15-11} = rd; + let Inst{10} = 0b0; + let Inst{9-0} = funct; +} + +class POOL32A_1RAC_FMT funct> : MMDSPInst { + bits<5> rs; + bits<2> ac; + + let Inst{31-26} = 0; + let Inst{25-21} = 0; + let Inst{20-16} = rs; + let Inst{15-14} = ac; + let Inst{13-6} = funct; + let Inst{5-0} = 0b111100; +} + +class POOL32A_1RMASK7_FMT op> : MMDSPInst { + bits<5> rt; + bits<7> mask; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-14} = mask; + let Inst{13-6} = op; + let Inst{5-0} = 0b111100; +} + +class POOL32A_1RIMM10_FMT op> : MMDSPInst { + bits<5> rd; + bits<10> imm; + + let Inst{31-26} = 0; + let Inst{25-16} = imm; + let Inst{15-11} = rd; + let Inst{10} = 0; + let Inst{9-0} = op; +} + +class POOL32A_1RIMM8_FMT op> : MMDSPInst { + bits<5> rt; + bits<8> imm; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-13} = imm; + let Inst{12} = 0; + let Inst{11-6} = op; + let Inst{5-0} = 0b111100; +} + +class POOL32A_4B0SHIFT6AC4B0_FMT op> : MMDSPInst { + bits<6> shift; + bits<2> ac; + + let Inst{31-26} = 0b000000; + let Inst{25-22} = 0b0000; + let Inst{21-16} = shift; + let Inst{15-14} = ac; + let Inst{13-10} = 0b0000; + let Inst{9-0} = op; +} + +class POOL32A_5B01RAC_FMT op> : MMDSPInst { + bits<5> rs; + bits<2> ac; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = 0b00000; + let Inst{20-16} = rs; + let Inst{15-14} = ac; + let Inst{13-6} = op; + let Inst{5-0} = 0b111100; +} + +class POOL32I_IMMB0_FMT op> : MMDSPInst { + bits<16> offset; + + let Inst{31-26} = 0b010000; + let Inst{25-21} = op; + let Inst{20-16} = 0; + let Inst{15-0} = offset; +} + +class POOL32A_2RBP_FMT : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<2> bp; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-14} = bp; + let Inst{13-6} = 0b00100010; + let Inst{5-0} = 0b111100; +} + +class POOL32A_2RB0_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-10} = 0; + let Inst{9-0} = op; +} + +class POOL32S_3RB0_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + bits<5> rd; + + let Inst{31-26} = 0b010110; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = rd; + let Inst{10} = 0b0; + let Inst{9-0} = op; +} + +class POOL32A_2R2B0_FMT op> : MMDSPInst { + bits<5> rt; + bits<5> rs; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = 0; + let Inst{10} = 0; + let Inst{9-0} = op; +} diff --git a/lib/Target/Maxis/MicroMaxisDSPInstrInfo.td b/lib/Target/Maxis/MicroMaxisDSPInstrInfo.td new file mode 100644 index 00000000..47c04a32 --- /dev/null +++ b/lib/Target/Maxis/MicroMaxisDSPInstrInfo.td @@ -0,0 +1,608 @@ +//===- MicroMaxisDSPInstrInfo.td - Micromaxis DSP instructions -*- tablegen *-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes MicroMaxis DSP instructions. +// +//===----------------------------------------------------------------------===// + +// Instruction encoding. +class ADDQ_PH_MM_ENC : POOL32A_3R_FMT<"addq.ph", 0b00000001101>; +class ADDQ_S_PH_MM_ENC : POOL32A_3R_FMT<"addq_s.ph", 0b10000001101>; +class ADDQ_S_W_MM_ENC : POOL32A_3RB0_FMT<"addq_s.w", 0b1100000101>; +class ADDQH_PH_MMR2_ENC : POOL32A_3R_FMT<"addqh.ph", 0b00001001101>; +class ADDQH_R_PH_MMR2_ENC : POOL32A_3R_FMT<"addqh_r.ph", 0b10001001101>; +class ADDQH_W_MMR2_ENC: POOL32A_3R_FMT<"addqh.w", 0b00010001101>; +class ADDQH_R_W_MMR2_ENC : POOL32A_3R_FMT<"addqh_r.w", 0b10010001101>; +class ADDU_PH_MMR2_ENC : POOL32A_3R_FMT<"addu.ph", 0b00100001101>; +class ADDU_S_PH_MMR2_ENC : POOL32A_3R_FMT<"addu_s.ph", 0b10100001101>; +class ADDU_QB_MM_ENC : POOL32A_3R_FMT<"addu.qb", 0b00011001101>; +class ADDU_S_QB_MM_ENC : POOL32A_3R_FMT<"addu_s.qb", 0b10011001101>; +class ADDUH_QB_MMR2_ENC : POOL32A_3R_FMT<"adduh.qb", 0b00101001101>; +class ADDUH_R_QB_MMR2_ENC : POOL32A_3R_FMT<"adduh_r.qb", 0b10101001101>; +class ADDSC_MM_ENC : POOL32A_3RB0_FMT<"addsc", 0b1110000101>; +class ADDWC_MM_ENC : POOL32A_3RB0_FMT<"addwc", 0b1111000101>; +class DPA_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"dpa.w.ph", 0b00000010>; +class DPAQ_S_W_PH_MM_ENC : POOL32A_2RAC_FMT<"dpaq_s.w.ph", 0b00001010>; +class DPAQ_SA_L_W_MM_ENC : POOL32A_2RAC_FMT<"dpaq_sa.l.w", 0b01001010>; +class DPAQX_S_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"dpaqx_s.w.ph", 0b10001010>; +class DPAQX_SA_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"dpaqx_sa.w.ph", 0b11001010>; +class DPAU_H_QBL_MM_ENC : POOL32A_2RAC_FMT<"dpau.h.qbl", 0b10000010>; +class DPAU_H_QBR_MM_ENC : POOL32A_2RAC_FMT<"dpau.h.qbr", 0b11000010>; +class DPAX_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"dpax.w.ph", 0b01000010>; +class ABSQ_S_PH_MM_ENC : POOL32A_2R_FMT<"absq_s.ph", 0b0001000100>; +class ABSQ_S_W_MM_ENC : POOL32A_2R_FMT<"absq_s.w", 0b0010000100>; +class ABSQ_S_QB_MMR2_ENC : POOL32A_2R_FMT<"absq_s.qb", 0b0000000100>; +class INSV_MM_ENC : POOL32A_2R_FMT<"insv", 0b0100000100>; +class MADD_DSP_MM_ENC : POOL32A_2RAC_FMT<"madd", 0b00101010>; +class MADDU_DSP_MM_ENC : POOL32A_2RAC_FMT<"maddu", 0b01101010>; +class MSUB_DSP_MM_ENC : POOL32A_2RAC_FMT<"msub", 0b10101010>; +class MSUBU_DSP_MM_ENC : POOL32A_2RAC_FMT<"msubu", 0b11101010>; +class MULT_DSP_MM_ENC : POOL32A_2RAC_FMT<"mult", 0b00110010>; +class MULTU_DSP_MM_ENC : POOL32A_2RAC_FMT<"multu", 0b01110010>; +class SHLL_PH_MM_ENC : POOL32A_2RSA4_FMT<"shll.ph", 0b001110110101>; +class SHLL_S_PH_MM_ENC : POOL32A_2RSA4_FMT<"shll_s.ph", 0b101110110101>; +class SHLL_QB_MM_ENC : POOL32A_2RSA3_FMT<"shll.qb", 0b0100001>; +class SHLLV_PH_MM_ENC : POOL32A_3R_FMT<"shllv.ph", 0b00000001110>; +class SHLLV_S_PH_MM_ENC : POOL32A_3R_FMT<"shllv_s.ph", 0b10000001110>; +class SHLLV_QB_MM_ENC : POOL32A_3RB0_FMT<"shllv.qb", 0b1110010101>; +class SHLLV_S_W_MM_ENC : POOL32A_3RB0_FMT<"shllv_s.w", 0b1111010101>; +class SHLL_S_W_MM_ENC : POOL32A_2RSA5B0_FMT<"shll_s.w", 0b1111110101>; +class SHRA_QB_MMR2_ENC : POOL32A_2RSA3_FMT<"shra.qb", 0b0000111>; +class SHRA_R_QB_MMR2_ENC : POOL32A_2RSA3_FMT<"shra_r.qb", 0b1000111>; +class SHRA_PH_MM_ENC : POOL32A_2RSA4B0_FMT<"shra.ph", 0b01100110101>; +class SHRA_R_PH_MM_ENC : POOL32A_2RSA4B0_FMT<"shra_r.ph", 0b11100110101>; +class SHRAV_PH_MM_ENC : POOL32A_3R_FMT<"shrav.ph", 0b00110001101>; +class SHRAV_R_PH_MM_ENC : POOL32A_3R_FMT<"shrav_r.ph", 0b10110001101>; +class SHRAV_QB_MMR2_ENC : POOL32A_3R_FMT<"shrav.qb", 0b00111001101>; +class SHRAV_R_QB_MMR2_ENC : POOL32A_3R_FMT<"shrav_r.qb", 0b10111001101>; +class SHRAV_R_W_MM_ENC : POOL32A_3RB0_FMT<"shrav_r.w", 0b1011010101>; +class SHRA_R_W_MM_ENC : POOL32A_2RSA5B0_FMT<"shra_r.w", 0b1011110101>; +class SHRL_PH_MMR2_ENC : POOL32A_2RSA4OP6_FMT<"shrl.ph", 0b001111>; +class SHRL_QB_MM_ENC : POOL32A_2RSA3_FMT<"shrl.qb", 0b1100001>; +class SHRLV_PH_MMR2_ENC : POOL32A_3RB0_FMT<"shrlv.ph", 0b1100010101>; +class SHRLV_QB_MM_ENC : POOL32A_3RB0_FMT<"shrlv.qb", 0b1101010101>; +class PRECEQ_W_PHL_MM_ENC : POOL32A_2R_FMT<"preceq.w.phl", 0b0101000100>; +class PRECEQ_W_PHR_MM_ENC : POOL32A_2R_FMT<"preceq.w.phr", 0b0110000100>; +class PRECEQU_PH_QBL_MM_ENC : POOL32A_2R_FMT<"precequ.ph.qbl", 0b0111000100>; +class PRECEQU_PH_QBLA_MM_ENC : POOL32A_2R_FMT<"precequ.ph.qbla", 0b0111001100>; +class PRECEQU_PH_QBR_MM_ENC : POOL32A_2R_FMT<"precequ.ph.qbr", 0b1001000100>; +class PRECEQU_PH_QBRA_MM_ENC : POOL32A_2R_FMT<"precequ.ph.qbra", 0b1001001100>; +class PRECEU_PH_QBL_MM_ENC : POOL32A_2R_FMT<"preceu.ph.qbl", 0b1011000100>; +class PRECEU_PH_QBLA_MM_ENC : POOL32A_2R_FMT<"preceu.ph.qbla", 0b1011001100>; +class PRECEU_PH_QBR_MM_ENC : POOL32A_2R_FMT<"preceu.ph.qbr", 0b1101000100>; +class PRECEU_PH_QBRA_MM_ENC : POOL32A_2R_FMT<"preceu.ph.qbra", 0b1101001100>; +class SUBQ_PH_MM_ENC : POOL32A_3R_FMT<"subq.ph", 0b01000001101>; +class SUBQ_S_PH_MM_ENC : POOL32A_3R_FMT<"subq_s.ph", 0b11000001101>; +class SUBQ_S_W_MM_ENC : POOL32A_3RB0_FMT<"subq_s.w", 0b1101000101>; +class SUBQH_PH_MMR2_ENC : POOL32A_3R_FMT<"subqh.ph", 0b01001001101>; +class SUBQH_R_PH_MMR2_ENC : POOL32A_3R_FMT<"subqh_r.ph", 0b11001001101>; +class SUBQH_W_MMR2_ENC : POOL32A_3R_FMT<"subqh.w", 0b01010001101>; +class SUBQH_R_W_MMR2_ENC : POOL32A_3R_FMT<"subqh_r.w", 0b11010001101>; +class SUBU_PH_MMR2_ENC : POOL32A_3R_FMT<"subu.ph", 0b01100001101>; +class SUBU_S_PH_MMR2_ENC : POOL32A_3R_FMT<"subu_s.ph", 0b11100001101>; +class SUBU_QB_MM_ENC : POOL32A_3R_FMT<"subu.qb", 0b01011001101>; +class SUBU_S_QB_MM_ENC : POOL32A_3R_FMT<"subu_s.qb", 0b11011001101>; +class SUBUH_QB_MMR2_ENC : POOL32A_3R_FMT<"subuh.qb", 0b01101001101>; +class SUBUH_R_QB_MMR2_ENC : POOL32A_3R_FMT<"subuh_r.qb", 0b11101001101>; +class EXTP_MM_ENC : POOL32A_1RIMM5AC_FMT<"extp", 0b10011001>; +class EXTPDP_MM_ENC : POOL32A_1RIMM5AC_FMT<"extpdp", 0b11011001>; +class EXTPDPV_MM_ENC : POOL32A_2RAC_FMT<"extpdpv", 0b11100010>; +class EXTPV_MM_ENC : POOL32A_2RAC_FMT<"extpv", 0b10100010>; +class EXTR_W_MM_ENC : POOL32A_1RIMM5AC_FMT<"extr.w", 0b00111001>; +class EXTR_R_W_MM_ENC : POOL32A_1RIMM5AC_FMT<"extr_r.w", 0b01111001>; +class EXTR_RS_W_MM_ENC : POOL32A_1RIMM5AC_FMT<"extr_rs.w", 0b10111001>; +class EXTR_S_H_MM_ENC : POOL32A_1RIMM5AC_FMT<"extr_s.h", 0b11111001>; +class EXTRV_W_MM_ENC : POOL32A_2RAC_FMT<"extrv.w", 0b00111010>; +class EXTRV_R_W_MM_ENC : POOL32A_2RAC_FMT<"extrv_r.w", 0b01111010>; +class EXTRV_RS_W_MM_ENC : POOL32A_2RAC_FMT<"extrv_rs.w", 0b10111010>; +class EXTRV_S_H_MM_ENC : POOL32A_2RAC_FMT<"extrv_s.h", 0b11111010>; +class DPS_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"dps.w.ph", 0b00010010>; +class DPSQ_S_W_PH_MM_ENC : POOL32A_2RAC_FMT<"dpsq_s.w.ph", 0b00011010>; +class DPSQ_SA_L_W_MM_ENC : POOL32A_2RAC_FMT<"dpsq_sa.l.w", 0b01011010>; +class DPSQX_S_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"dpsqx_s.w.ph", 0b10011010>; +class DPSQX_SA_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"dpsqx_sa.w.ph", 0b11011010>; +class DPSU_H_QBL_MM_ENC : POOL32A_2RAC_FMT<"dpsu.h.qbl", 0b10010010>; +class DPSU_H_QBR_MM_ENC : POOL32A_2RAC_FMT<"dpsu.h.qbr", 0b11010010>; +class DPSX_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"dpsx.w.ph", 0b01010010>; +class MUL_PH_MMR2_ENC : POOL32A_3R_FMT<"mul.ph", 0b00000101101>; +class MUL_S_PH_MMR2_ENC : POOL32A_3R_FMT<"mul_s.ph", 0b10000101101>; +class MULEQ_S_W_PHL_MM_ENC : POOL32A_3RB0_FMT<"muleq_s.w.phl", 0b0000100101>; +class MULEQ_S_W_PHR_MM_ENC : POOL32A_3RB0_FMT<"muleq_s.w.phr", 0b0001100101>; +class MULEU_S_PH_QBL_MM_ENC : POOL32A_3RB0_FMT<"muleu_s.ph.qbl", 0b0010010101>; +class MULEU_S_PH_QBR_MM_ENC : POOL32A_3RB0_FMT<"muleu_s.ph.qbr", 0b0011010101>; +class MULQ_RS_PH_MM_ENC : POOL32A_3RB0_FMT<"mulq_rs.ph", 0b0100010101>; +class MULQ_RS_W_MMR2_ENC : POOL32A_3RB0_FMT<"mulq_rs.w", 0b0110010101>; +class MULQ_S_PH_MMR2_ENC : POOL32A_3RB0_FMT<"mulq_s.ph", 0b0101010101>; +class MULQ_S_W_MMR2_ENC : POOL32A_3RB0_FMT<"mulq_s.w", 0b0111010101>; +class PRECR_QB_PH_MMR2_ENC : POOL32A_3RB0_FMT<"precr.qb.ph", 0b0001101101>; +class PRECR_SRA_PH_W_MMR2_ENC + : POOL32A_2RSA5_FMT<"precr_sra.ph.w", 0b01111001101>; +class PRECR_SRA_R_PH_W_MMR2_ENC + : POOL32A_2RSA5_FMT<"precr_sra_r.ph.w", 0b11111001101>; +class PRECRQ_PH_W_MM_ENC : POOL32A_3RB0_FMT<"precrq.ph.w", 0b0011101101>; +class PRECRQ_QB_PH_MM_ENC : POOL32A_3RB0_FMT<"precrq.qb.ph", 0b0010101101>; +class PRECRQU_S_QB_PH_MM_ENC + : POOL32A_3RB0_FMT<"precrqu_s.qb.ph", 0b0101101101>; +class PRECRQ_RS_PH_W_MM_ENC : POOL32A_3RB0_FMT<"precrq_rs.ph.w", 0b0100101101>; +class LBUX_MM_ENC : POOL32A_1RMEMB0_FMT<"lbux", 0b1000100101>; +class LHX_MM_ENC : POOL32A_1RMEMB0_FMT<"lhx", 0b0101100101>; +class LWX_MM_ENC : POOL32A_1RMEMB0_FMT<"lwx", 0b0110100101>; +class MAQ_S_W_PHL_MM_ENC : POOL32A_2RAC_FMT<"maq_s.w.phl", 0b01101001>; +class MAQ_SA_W_PHL_MM_ENC : POOL32A_2RAC_FMT<"maq_sa.w.phl", 0b11101001>; +class MAQ_S_W_PHR_MM_ENC : POOL32A_2RAC_FMT<"maq_s.w.phr", 0b00101001>; +class MAQ_SA_W_PHR_MM_ENC : POOL32A_2RAC_FMT<"maq_sa.w.phr", 0b10101001>; +class MFHI_MM_ENC : POOL32A_1RAC_FMT<"mfhi", 0b00000001>; +class MFLO_MM_ENC : POOL32A_1RAC_FMT<"mflo", 0b01000001>; +class MTHI_MM_ENC : POOL32A_1RAC_FMT<"mthi", 0b10000001>; +class MTLO_MM_ENC : POOL32A_1RAC_FMT<"mthi", 0b11000001>; +class PREPEND_MMR2_ENC : POOL32A_2RSA5B0_FMT<"prepend", 0b1001010101>; +class RADDU_W_QB_MM_ENC : POOL32A_2R_FMT<"raddu.w.qb", 0b1111000100>; +class RDDSP_MM_ENC : POOL32A_1RMASK7_FMT<"rddsp", 0b00011001>; +class REPL_PH_MM_ENC : POOL32A_1RIMM10_FMT<"repl.ph", 0b0000111101>; +class REPL_QB_MM_ENC : POOL32A_1RIMM8_FMT<"repl.qb", 0b010111>; +class REPLV_PH_MM_ENC : POOL32A_2R_FMT<"replv.ph", 0b0000001100>; +class REPLV_QB_MM_ENC : POOL32A_2R_FMT<"replv.qb", 0b0001001100>; +class MTHLIP_MM_ENC : POOL32A_1RAC_FMT<"mthlip", 0b00001001>; +class PACKRL_PH_MM_ENC : POOL32A_3RB0_FMT<"packrl.ph", 0b0110101101>; +class PICK_PH_MM_ENC : POOL32A_3RB0_FMT<"pick.ph", 0b1000101101>; +class PICK_QB_MM_ENC : POOL32A_3RB0_FMT<"pick.qb", 0b0111101101>; +class SHILO_MM_ENC : POOL32A_4B0SHIFT6AC4B0_FMT<"shilo", 0b0000011101>; +class SHILOV_MM_ENC : POOL32A_5B01RAC_FMT<"shilov", 0b01001001>; +class WRDSP_MM_ENC : POOL32A_1RMASK7_FMT<"wrdsp", 0b01011001>; +class APPEND_MMR2_ENC : POOL32A_2RSA5B0_FMT<"append", 0b1000010101>; +class MODSUB_MM_ENC : POOL32A_3RB0_FMT<"modsub", 0b1010010101>; +class MULSA_W_PH_MMR2_ENC : POOL32A_2RAC_FMT<"mulsa.w.ph", 0b10110010>; +class MULSAQ_S_W_PH_MM_ENC : POOL32A_2RAC_FMT<"mulsaq_s.w.ph", 0b11110010>; +class BPOSGE32C_MMR3_ENC : POOL32I_IMMB0_FMT<"bposge32c", 0b11001>; +class BITREV_MM_ENC : POOL32A_2R_FMT<"bitrev", 0b0011000100>; +class BALIGN_MMR2_ENC : POOL32A_2RBP_FMT<"balign">; +class BPOSGE32_MM_ENC : POOL32I_IMMB0_FMT<"bposge32", 0b11011>; +class CMP_EQ_PH_MM_ENC : POOL32A_2RB0_FMT<"cmp.eq.ph", 0b0000000101>; +class CMP_LE_PH_MM_ENC : POOL32A_2RB0_FMT<"cmp.le.ph", 0b0010000101>; +class CMP_LT_PH_MM_ENC : POOL32A_2RB0_FMT<"cmp.lt.ph", 0b0001000101>; +class CMPGDU_EQ_QB_MMR2_ENC : POOL32A_3RB0_FMT<"cmpgdu.eq.qb", 0b0110000101>; +class CMPGDU_LT_QB_MMR2_ENC : POOL32A_3RB0_FMT<"cmpgdu.lt.qb", 0b0111000101>; +class CMPGDU_LE_QB_MMR2_ENC : POOL32A_3RB0_FMT<"cmpgdu.le.qb", 0b1000000101>; +class CMPGU_EQ_QB_MM_ENC : POOL32S_3RB0_FMT<"cmpgu.eq.qb", 0b0011000101>; +class CMPGU_LT_QB_MM_ENC : POOL32S_3RB0_FMT<"cmpgu.lt.qb", 0b0100000101>; +class CMPGU_LE_QB_MM_ENC : POOL32S_3RB0_FMT<"cmpgu.le.qb", 0b0101000101>; +class CMPU_EQ_QB_MM_ENC : POOL32A_2R2B0_FMT<"cmpu.eq.qb", 0b1001000101>; +class CMPU_LT_QB_MM_ENC : POOL32A_2R2B0_FMT<"cmpu.lt.qb", 0b1010000101>; +class CMPU_LE_QB_MM_ENC : POOL32A_2R2B0_FMT<"cmpu.le.qb", 0b1011000101>; + +// Instruction desc. +class ABSQ_S_PH_MM_R2_DESC_BASE { + dag OutOperandList = (outs ROD:$rt); + dag InOperandList = (ins ROS:$rs); + string AsmString = !strconcat(opstr, "\t$rt, $rs"); + list Pattern = [(set ROD:$rt, (OpNode ROS:$rs))]; + InstrItinClass Itinerary = itin; +} +class ABSQ_S_PH_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "absq_s.ph", int_maxis_absq_s_ph, NoItinerary, DSPROpnd>, Defs<[DSPOutFlag20]>; +class ABSQ_S_W_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "absq_s.w", int_maxis_absq_s_w, NoItinerary, GPR32Opnd>, Defs<[DSPOutFlag20]>; +class ABSQ_S_QB_MMR2_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "absq_s.qb", int_maxis_absq_s_qb, NoItinerary, DSPROpnd>, Defs<[DSPOutFlag20]>; +class PRECEQ_W_PHL_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "preceq.w.phl", int_maxis_preceq_w_phl, NoItinerary, GPR32Opnd, DSPROpnd>; +class PRECEQ_W_PHR_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "preceq.w.phr", int_maxis_preceq_w_phr, NoItinerary, GPR32Opnd, DSPROpnd>; +class PRECEQU_PH_QBL_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "precequ.ph.qbl", int_maxis_precequ_ph_qbl, NoItinerary, DSPROpnd>; +class PRECEQU_PH_QBLA_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "precequ.ph.qbla", int_maxis_precequ_ph_qbla, NoItinerary, DSPROpnd>; +class PRECEQU_PH_QBR_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "precequ.ph.qbr", int_maxis_precequ_ph_qbr, NoItinerary, DSPROpnd>; +class PRECEQU_PH_QBRA_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "precequ.ph.qbra", int_maxis_precequ_ph_qbra, NoItinerary, DSPROpnd>; +class PRECEU_PH_QBL_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "preceu.ph.qbl", int_maxis_preceu_ph_qbl, NoItinerary, DSPROpnd>; +class PRECEU_PH_QBLA_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "preceu.ph.qbla", int_maxis_preceu_ph_qbla, NoItinerary, DSPROpnd>; +class PRECEU_PH_QBR_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "preceu.ph.qbr", int_maxis_preceu_ph_qbr, NoItinerary, DSPROpnd>; +class PRECEU_PH_QBRA_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE< + "preceu.ph.qbra", int_maxis_preceu_ph_qbra, NoItinerary, DSPROpnd>; + +class SHLL_R2_MM_DESC_BASE { + dag OutOperandList = (outs RO:$rt); + dag InOperandList = (ins RO:$rs, ImmOpnd:$sa); + string AsmString = !strconcat(instr_asm, "\t$rt, $rs, $sa"); + list Pattern = [(set RO:$rt, (OpNode RO:$rs, ImmPat:$sa))]; + InstrItinClass Itinerary = itin; + bit hasSideEffects = 1; +} +class SHLL_PH_MM_DESC : SHLL_R2_MM_DESC_BASE< + "shll.ph", null_frag, immZExt4, NoItinerary, DSPROpnd, uimm4>, + Defs<[DSPOutFlag22]>; +class SHLL_S_PH_MM_DESC : SHLL_R2_MM_DESC_BASE< + "shll_s.ph", int_maxis_shll_s_ph, immZExt4, NoItinerary, DSPROpnd, uimm4>, + Defs<[DSPOutFlag22]>; +class SHLL_QB_MM_DESC : SHLL_R2_MM_DESC_BASE< + "shll.qb", null_frag, immZExt3, NoItinerary, DSPROpnd, uimm3>, + Defs<[DSPOutFlag22]>; +class SHLL_S_W_MM_DESC : SHLL_R2_MM_DESC_BASE< + "shll_s.w", int_maxis_shll_s_w, immZExt5, NoItinerary, GPR32Opnd, uimm5>, + Defs<[DSPOutFlag22]>; +class SHRA_QB_MMR2_DESC : SHLL_R2_MM_DESC_BASE< + "shra.qb", null_frag, immZExt3, NoItinerary, DSPROpnd, uimm3>; +class SHRA_R_QB_MMR2_DESC : SHLL_R2_MM_DESC_BASE< + "shra_r.qb", int_maxis_shra_r_qb, immZExt3, NoItinerary, DSPROpnd, uimm3>; +class SHRA_PH_MM_DESC : SHLL_R2_MM_DESC_BASE< + "shra.ph", null_frag, immZExt4, NoItinerary, DSPROpnd, uimm4>; +class SHRA_R_PH_MM_DESC : SHLL_R2_MM_DESC_BASE< + "shra_r.ph", int_maxis_shra_r_ph, immZExt4, NoItinerary, DSPROpnd, uimm4>; +class SHRA_R_W_MM_DESC : SHLL_R2_MM_DESC_BASE< + "shra_r.w", int_maxis_shra_r_w, immZExt5, NoItinerary, GPR32Opnd, uimm5>; +class SHRL_QB_MM_DESC : SHLL_R2_MM_DESC_BASE< + "shrl.qb", null_frag, immZExt3, NoItinerary, DSPROpnd, uimm3>; +class SHRL_PH_MMR2_DESC : SHLL_R2_MM_DESC_BASE< + "shrl.ph", null_frag, immZExt4, NoItinerary, DSPROpnd, uimm4>; + +class SHLLV_R3_MM_DESC_BASE { + dag OutOperandList = (outs RO:$rd); + dag InOperandList = (ins RO:$rt, GPR32Opnd:$rs); + string AsmString = !strconcat(instr_asm, "\t$rd, $rt, $rs"); + list Pattern = [(set RO:$rd, (OpNode RO:$rt, GPR32Opnd:$rs))]; + InstrItinClass Itinerary = itin; +} +class SHLLV_PH_MM_DESC : SHLLV_R3_MM_DESC_BASE< + "shllv.ph", int_maxis_shll_ph, NoItinerary, DSPROpnd>, Defs<[DSPOutFlag22]>; +class SHLLV_S_PH_MM_DESC : SHLLV_R3_MM_DESC_BASE< + "shllv_s.ph", int_maxis_shll_s_ph, NoItinerary, DSPROpnd>, + Defs<[DSPOutFlag22]>; +class SHLLV_QB_MM_DESC : SHLLV_R3_MM_DESC_BASE< + "shllv.qb", int_maxis_shll_qb, NoItinerary, DSPROpnd>, Defs<[DSPOutFlag22]>; +class SHLLV_S_W_MM_DESC : SHLLV_R3_MM_DESC_BASE< + "shllv_s.w", int_maxis_shll_s_w, NoItinerary, GPR32Opnd>, Defs<[DSPOutFlag22]>; +class SHRAV_PH_MM_DESC : SHLLV_R3_MM_DESC_BASE< + "shrav.ph", int_maxis_shra_ph, NoItinerary, DSPROpnd>; +class SHRAV_R_PH_MM_DESC : SHLLV_R3_MM_DESC_BASE< + "shrav_r.ph", int_maxis_shra_r_ph, NoItinerary, DSPROpnd>; +class SHRAV_QB_MMR2_DESC : SHLLV_R3_MM_DESC_BASE< + "shrav.qb", int_maxis_shra_qb, NoItinerary, DSPROpnd>; +class SHRAV_R_QB_MMR2_DESC : SHLLV_R3_MM_DESC_BASE< + "shrav_r.qb", int_maxis_shra_r_qb, NoItinerary, DSPROpnd>; +class SHRAV_R_W_MM_DESC : SHLLV_R3_MM_DESC_BASE< + "shrav_r.w", int_maxis_shra_r_w, NoItinerary, GPR32Opnd>; +class SHRLV_PH_MMR2_DESC : SHLLV_R3_MM_DESC_BASE< + "shrlv.ph", int_maxis_shrl_ph, NoItinerary, DSPROpnd>; +class SHRLV_QB_MM_DESC : SHLLV_R3_MM_DESC_BASE< + "shrlv.qb", int_maxis_shrl_qb, NoItinerary, DSPROpnd>; + +class EXT_MM_2R_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins ACC64DSPOpnd:$ac, GPR32Opnd:$rs); + string AsmString = !strconcat(instr_asm, "\t$rt, $ac, $rs"); + InstrItinClass Itinerary = itin; +} +class EXT_MM_1R_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins ACC64DSPOpnd:$ac, uimm5:$imm); + string AsmString = !strconcat(instr_asm, "\t$rt, $ac, $imm"); + InstrItinClass Itinerary = itin; +} + +class EXTP_MM_DESC + : EXT_MM_1R_DESC_BASE<"extp", MaxisEXTP, NoItinerary>, + Uses<[DSPPos]>, Defs<[DSPEFI]>; +class EXTPDP_MM_DESC + : EXT_MM_1R_DESC_BASE<"extpdp", MaxisEXTPDP, NoItinerary>, + Uses<[DSPPos]>, Defs<[DSPPos, DSPEFI]>; +class EXTPDPV_MM_DESC + : EXT_MM_2R_DESC_BASE<"extpdpv", MaxisEXTPDP, NoItinerary>, + Uses<[DSPPos]>, Defs<[DSPPos, DSPEFI]>; +class EXTPV_MM_DESC + : EXT_MM_2R_DESC_BASE<"extpv", MaxisEXTP, NoItinerary>, + Uses<[DSPPos]>, Defs<[DSPEFI]>; +class EXTR_W_MM_DESC + : EXT_MM_1R_DESC_BASE<"extr.w", MaxisEXTR_W, NoItinerary>, + Defs<[DSPOutFlag23]>; +class EXTR_R_W_MM_DESC + : EXT_MM_1R_DESC_BASE<"extr_r.w", MaxisEXTR_R_W, NoItinerary>, + Defs<[DSPOutFlag23]>; +class EXTR_RS_W_MM_DESC + : EXT_MM_1R_DESC_BASE<"extr_rs.w", MaxisEXTR_RS_W, NoItinerary>, + Defs<[DSPOutFlag23]>; +class EXTR_S_H_MM_DESC + : EXT_MM_1R_DESC_BASE<"extr_s.h", MaxisEXTR_S_H, NoItinerary>, + Defs<[DSPOutFlag23]>; +class EXTRV_W_MM_DESC + : EXT_MM_2R_DESC_BASE<"extrv.w", MaxisEXTR_W, NoItinerary>, + Defs<[DSPOutFlag23]>; +class EXTRV_R_W_MM_DESC + : EXT_MM_2R_DESC_BASE<"extrv_r.w", MaxisEXTR_R_W, NoItinerary>, + Defs<[DSPOutFlag23]>; +class EXTRV_RS_W_MM_DESC + : EXT_MM_2R_DESC_BASE<"extrv_rs.w", MaxisEXTR_RS_W, NoItinerary>, + Defs<[DSPOutFlag23]>; +class EXTRV_S_H_MM_DESC + : EXT_MM_2R_DESC_BASE<"extrv_s.h", MaxisEXTR_S_H, NoItinerary>, + Defs<[DSPOutFlag23]>; + +class MFHI_MM_DESC_BASE { + dag OutOperandList = (outs GPR32Opnd:$rs); + dag InOperandList = (ins RO:$ac); + string AsmString = !strconcat(instr_asm, "\t$rs, $ac"); + list Pattern = [(set GPR32Opnd:$rs, (OpNode RO:$ac))]; + InstrItinClass Itinerary = itin; +} + +class MFHI_MM_DESC : MFHI_MM_DESC_BASE<"mfhi", ACC64DSPOpnd, MaxisMFHI, + NoItinerary>; +class MFLO_MM_DESC : MFHI_MM_DESC_BASE<"mflo", ACC64DSPOpnd, MaxisMFLO, + NoItinerary>; + +class RADDU_W_QB_MM_DESC { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins DSPROpnd:$rs); + string AsmString = !strconcat("raddu.w.qb", "\t$rt, $rs"); + list Pattern = [(set GPR32Opnd:$rt, (int_maxis_raddu_w_qb DSPROpnd:$rs))]; + InstrItinClass Itinerary = NoItinerary; + string BaseOpcode = "raddu.w.qb"; +} + +class RDDSP_MM_DESC { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins uimm7:$mask); + string AsmString = !strconcat("rddsp", "\t$rt, $mask"); + list Pattern = [(set GPR32Opnd:$rt, (int_maxis_rddsp immZExt7:$mask))]; + InstrItinClass Itinerary = NoItinerary; +} + +class REPL_QB_MM_DESC { + dag OutOperandList = (outs DSPROpnd:$rt); + dag InOperandList = (ins uimm8:$imm); + string AsmString = !strconcat("repl.qb", "\t$rt, $imm"); + list Pattern = [(set DSPROpnd:$rt, (int_maxis_repl_qb immZExt8:$imm))]; + InstrItinClass Itinerary = NoItinerary; +} + +class REPLV_PH_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE<"replv.ph", int_maxis_repl_ph, + NoItinerary, DSPROpnd, + GPR32Opnd>; +class REPLV_QB_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE<"replv.qb", int_maxis_repl_qb, + NoItinerary, DSPROpnd, + GPR32Opnd>; + +class WRDSP_MM_DESC { + dag OutOperandList = (outs); + dag InOperandList = (ins GPR32Opnd:$rt, uimm7:$mask); + string AsmString = !strconcat("wrdsp", "\t$rt, $mask"); + list Pattern = [(int_maxis_wrdsp GPR32Opnd:$rt, immZExt7:$mask)]; + InstrItinClass Itinerary = NoItinerary; +} + +class BPOSGE32C_MMR3_DESC { + dag OutOperandList = (outs); + dag InOperandList = (ins brtarget1SImm16:$offset); + string AsmString = !strconcat("bposge32c", "\t$offset"); + InstrItinClass Itinerary = NoItinerary; + bit isBranch = 1; + bit isTerminator = 1; + bit hasDelaySlot = 0; +} + +class BALIGN_MMR2_DESC { + dag OutOperandList = (outs GPR32Opnd:$rt); + dag InOperandList = (ins GPR32Opnd:$rs, uimm2:$bp, GPR32Opnd:$src); + string AsmString = !strconcat("balign", "\t$rt, $rs, $bp"); + list Pattern = [(set GPR32Opnd:$rt, (int_maxis_balign GPR32Opnd:$src, + GPR32Opnd:$rs, + immZExt2:$bp))]; + InstrItinClass Itinerary = NoItinerary; + string Constraints = "$src = $rt"; +} + +class BITREV_MM_DESC : ABSQ_S_PH_MM_R2_DESC_BASE<"bitrev", int_maxis_bitrev, + NoItinerary, GPR32Opnd>; + +class BPOSGE32_MM_DESC : BPOSGE32_DESC_BASE<"bposge32", brtarget_mm, + NoItinerary>; + +let DecoderNamespace = "MicroMaxisDSP", Arch = "mmdsp", + AdditionalPredicates = [HasDSP, InMicroMaxis] in { + def LWDSP_MM : Load<"lw", DSPROpnd, null_frag, II_LW>, DspMMRel, + LW_FM_MM<0x3f>; + def SWDSP_MM : Store<"sw", DSPROpnd, null_frag, II_SW>, DspMMRel, + LW_FM_MM<0x3e>; +} +// Instruction defs. +// microMAXIS DSP Rev 1 +def ADDQ_PH_MM : DspMMRel, ADDQ_PH_MM_ENC, ADDQ_PH_DESC; +def ADDQ_S_PH_MM : DspMMRel, ADDQ_S_PH_MM_ENC, ADDQ_S_PH_DESC; +def ADDQ_S_W_MM : DspMMRel, ADDQ_S_W_MM_ENC, ADDQ_S_W_DESC; +def ADDU_QB_MM : DspMMRel, ADDU_QB_MM_ENC, ADDU_QB_DESC; +def ADDU_S_QB_MM : DspMMRel, ADDU_S_QB_MM_ENC, ADDU_S_QB_DESC; +def ADDSC_MM : DspMMRel, ADDSC_MM_ENC, ADDSC_DESC; +def ADDWC_MM : DspMMRel, ADDWC_MM_ENC, ADDWC_DESC; +def DPAQ_S_W_PH_MM : DspMMRel, DPAQ_S_W_PH_MM_ENC, DPAQ_S_W_PH_DESC; +def DPAQ_SA_L_W_MM : DspMMRel, DPAQ_SA_L_W_MM_ENC, DPAQ_SA_L_W_DESC; +def DPAU_H_QBL_MM : DspMMRel, DPAU_H_QBL_MM_ENC, DPAU_H_QBL_DESC; +def DPAU_H_QBR_MM : DspMMRel, DPAU_H_QBR_MM_ENC, DPAU_H_QBR_DESC; +def ABSQ_S_PH_MM : DspMMRel, ABSQ_S_PH_MM_ENC, ABSQ_S_PH_MM_DESC; +def ABSQ_S_W_MM : DspMMRel, ABSQ_S_W_MM_ENC, ABSQ_S_W_MM_DESC; +def INSV_MM : DspMMRel, INSV_MM_ENC, INSV_DESC; +def MADD_DSP_MM : DspMMRel, MADD_DSP_MM_ENC, MADD_DSP_DESC; +def MADDU_DSP_MM : DspMMRel, MADDU_DSP_MM_ENC, MADDU_DSP_DESC; +def MSUB_DSP_MM : DspMMRel, MSUB_DSP_MM_ENC, MSUB_DSP_DESC; +def MSUBU_DSP_MM : DspMMRel, MSUBU_DSP_MM_ENC, MSUBU_DSP_DESC; +def MULT_DSP_MM : DspMMRel, MULT_DSP_MM_ENC, MULT_DSP_DESC; +def MULTU_DSP_MM : DspMMRel, MULTU_DSP_MM_ENC, MULTU_DSP_DESC; +def SHLL_PH_MM : DspMMRel, SHLL_PH_MM_ENC, SHLL_PH_MM_DESC; +def SHLL_S_PH_MM : DspMMRel, SHLL_S_PH_MM_ENC, SHLL_S_PH_MM_DESC; +def SHLL_QB_MM : DspMMRel, SHLL_QB_MM_ENC, SHLL_QB_MM_DESC; +def SHLLV_PH_MM : DspMMRel, SHLLV_PH_MM_ENC, SHLLV_PH_MM_DESC; +def SHLLV_S_PH_MM : DspMMRel, SHLLV_S_PH_MM_ENC, SHLLV_S_PH_MM_DESC; +def SHLLV_QB_MM : DspMMRel, SHLLV_QB_MM_ENC, SHLLV_QB_MM_DESC; +def SHLLV_S_W_MM : DspMMRel, SHLLV_S_W_MM_ENC, SHLLV_S_W_MM_DESC; +def SHLL_S_W_MM : DspMMRel, SHLL_S_W_MM_ENC, SHLL_S_W_MM_DESC; +def SHRA_PH_MM : DspMMRel, SHRA_PH_MM_ENC, SHRA_PH_MM_DESC; +def SHRA_R_PH_MM : DspMMRel, SHRA_R_PH_MM_ENC, SHRA_R_PH_MM_DESC; +def SHRAV_PH_MM : DspMMRel, SHRAV_PH_MM_ENC, SHRAV_PH_MM_DESC; +def SHRAV_R_PH_MM : DspMMRel, SHRAV_R_PH_MM_ENC, SHRAV_R_PH_MM_DESC; +def SHRAV_R_W_MM : DspMMRel, SHRAV_R_W_MM_ENC, SHRAV_R_W_MM_DESC; +def SHRA_R_W_MM : DspMMRel, SHRA_R_W_MM_ENC, SHRA_R_W_MM_DESC; +def SHRL_QB_MM : DspMMRel, SHRL_QB_MM_ENC, SHRL_QB_MM_DESC; +def SHRLV_QB_MM : DspMMRel, SHRLV_QB_MM_ENC, SHRLV_QB_MM_DESC; +def PRECEQ_W_PHL_MM : DspMMRel, PRECEQ_W_PHL_MM_ENC, PRECEQ_W_PHL_MM_DESC; +def PRECEQ_W_PHR_MM : DspMMRel, PRECEQ_W_PHR_MM_ENC, PRECEQ_W_PHR_MM_DESC; +def PRECEQU_PH_QBL_MM : DspMMRel, PRECEQU_PH_QBL_MM_ENC, PRECEQU_PH_QBL_MM_DESC; +def PRECEQU_PH_QBLA_MM : DspMMRel, PRECEQU_PH_QBLA_MM_ENC, + PRECEQU_PH_QBLA_MM_DESC; +def PRECEQU_PH_QBR_MM : DspMMRel, PRECEQU_PH_QBR_MM_ENC, PRECEQU_PH_QBR_MM_DESC; +def PRECEQU_PH_QBRA_MM : DspMMRel, PRECEQU_PH_QBRA_MM_ENC, + PRECEQU_PH_QBRA_MM_DESC; +def PRECEU_PH_QBL_MM : DspMMRel, PRECEU_PH_QBL_MM_ENC, PRECEU_PH_QBL_MM_DESC; +def PRECEU_PH_QBLA_MM : DspMMRel, PRECEU_PH_QBLA_MM_ENC, PRECEU_PH_QBLA_MM_DESC; +def PRECEU_PH_QBR_MM : DspMMRel, PRECEU_PH_QBR_MM_ENC, PRECEU_PH_QBR_MM_DESC; +def PRECEU_PH_QBRA_MM : DspMMRel, PRECEU_PH_QBRA_MM_ENC, PRECEU_PH_QBRA_MM_DESC; +def SUBQ_PH_MM : DspMMRel, SUBQ_PH_MM_ENC, SUBQ_PH_DESC; +def SUBQ_S_PH_MM : DspMMRel, SUBQ_S_PH_MM_ENC, SUBQ_S_PH_DESC; +def SUBQ_S_W_MM : DspMMRel, SUBQ_S_W_MM_ENC, SUBQ_S_W_DESC; +def SUBU_QB_MM : DspMMRel, SUBU_QB_MM_ENC, SUBU_QB_DESC; +def SUBU_S_QB_MM : DspMMRel, SUBU_S_QB_MM_ENC, SUBU_S_QB_DESC; +def EXTP_MM : DspMMRel, EXTP_MM_ENC, EXTP_MM_DESC; +def EXTPDP_MM : DspMMRel, EXTPDP_MM_ENC, EXTPDP_MM_DESC; +def EXTPDPV_MM : DspMMRel, EXTPDPV_MM_ENC, EXTPDPV_MM_DESC; +def EXTPV_MM : DspMMRel, EXTPV_MM_ENC, EXTPV_MM_DESC; +def EXTR_W_MM : DspMMRel, EXTR_W_MM_ENC, EXTR_W_MM_DESC; +def EXTR_R_W_MM : DspMMRel, EXTR_R_W_MM_ENC, EXTR_R_W_MM_DESC; +def EXTR_RS_W_MM : DspMMRel, EXTR_RS_W_MM_ENC, EXTR_RS_W_MM_DESC; +def EXTR_S_H_MM : DspMMRel, EXTR_S_H_MM_ENC, EXTR_S_H_MM_DESC; +def EXTRV_W_MM : DspMMRel, EXTRV_W_MM_ENC, EXTRV_W_MM_DESC; +def EXTRV_R_W_MM : DspMMRel, EXTRV_R_W_MM_ENC, EXTRV_R_W_MM_DESC; +def EXTRV_RS_W_MM : DspMMRel, EXTRV_RS_W_MM_ENC, EXTRV_RS_W_MM_DESC; +def EXTRV_S_H_MM : DspMMRel, EXTRV_S_H_MM_ENC, EXTRV_S_H_MM_DESC; +def DPSQ_S_W_PH_MM : DspMMRel, DPSQ_S_W_PH_MM_ENC, DPSQ_S_W_PH_DESC; +def DPSQ_SA_L_W_MM : DspMMRel, DPSQ_SA_L_W_MM_ENC, DPSQ_SA_L_W_DESC; +def DPSU_H_QBL_MM : DspMMRel, DPSU_H_QBL_MM_ENC, DPSU_H_QBL_DESC; +def DPSU_H_QBR_MM : DspMMRel, DPSU_H_QBR_MM_ENC, DPSU_H_QBR_DESC; +def MULEQ_S_W_PHL_MM : DspMMRel, MULEQ_S_W_PHL_MM_ENC, MULEQ_S_W_PHL_DESC; +def MULEQ_S_W_PHR_MM : DspMMRel, MULEQ_S_W_PHR_MM_ENC, MULEQ_S_W_PHR_DESC; +def MULEU_S_PH_QBL_MM : DspMMRel, MULEU_S_PH_QBL_MM_ENC, MULEU_S_PH_QBL_DESC; +def MULEU_S_PH_QBR_MM : DspMMRel, MULEU_S_PH_QBR_MM_ENC, MULEU_S_PH_QBR_DESC; +def MULQ_RS_PH_MM : DspMMRel, MULQ_RS_PH_MM_ENC, MULQ_RS_PH_DESC; +def PRECRQ_PH_W_MM : DspMMRel, PRECRQ_PH_W_MM_ENC, PRECRQ_PH_W_DESC; +def PRECRQ_QB_PH_MM : DspMMRel, PRECRQ_QB_PH_MM_ENC, PRECRQ_QB_PH_DESC; +def PRECRQU_S_QB_PH_MM : DspMMRel, PRECRQU_S_QB_PH_MM_ENC, PRECRQU_S_QB_PH_DESC; +def PRECRQ_RS_PH_W_MM : DspMMRel, PRECRQ_RS_PH_W_MM_ENC, PRECRQ_RS_PH_W_DESC; +def LBUX_MM : DspMMRel, LBUX_MM_ENC, LBUX_DESC; +def LHX_MM : DspMMRel, LHX_MM_ENC, LHX_DESC; +def LWX_MM : DspMMRel, LWX_MM_ENC, LWX_DESC; +def MAQ_S_W_PHL_MM : DspMMRel, MAQ_S_W_PHL_MM_ENC, MAQ_S_W_PHL_DESC; +def MAQ_SA_W_PHL_MM : DspMMRel, MAQ_SA_W_PHL_MM_ENC, MAQ_SA_W_PHL_DESC; +def MAQ_S_W_PHR_MM : DspMMRel, MAQ_S_W_PHR_MM_ENC, MAQ_S_W_PHR_DESC; +def MAQ_SA_W_PHR_MM : DspMMRel, MAQ_SA_W_PHR_MM_ENC, MAQ_SA_W_PHR_DESC; +def MFHI_DSP_MM : DspMMRel, MFHI_MM_ENC, MFHI_MM_DESC; +def MFLO_DSP_MM : DspMMRel, MFLO_MM_ENC, MFLO_MM_DESC; +def MTHI_DSP_MM : DspMMRel, MTHI_MM_ENC, MTHI_DESC; +def MTLO_DSP_MM : DspMMRel, MTLO_MM_ENC, MTLO_DESC; +def RADDU_W_QB_MM : DspMMRel, RADDU_W_QB_MM_ENC, RADDU_W_QB_MM_DESC; +def RDDSP_MM : DspMMRel, RDDSP_MM_ENC, RDDSP_MM_DESC; +def REPL_PH_MM : DspMMRel, REPL_PH_MM_ENC, REPL_PH_DESC; +def REPL_QB_MM : DspMMRel, REPL_QB_MM_ENC, REPL_QB_MM_DESC; +def REPLV_PH_MM : DspMMRel, REPLV_PH_MM_ENC, REPLV_PH_MM_DESC; +def REPLV_QB_MM : DspMMRel, REPLV_QB_MM_ENC, REPLV_QB_MM_DESC; +def MTHLIP_MM : DspMMRel, MTHLIP_MM_ENC, MTHLIP_DESC; +def PACKRL_PH_MM : DspMMRel, PACKRL_PH_MM_ENC, PACKRL_PH_DESC; +def PICK_PH_MM : DspMMRel, PICK_PH_MM_ENC, PICK_PH_DESC; +def PICK_QB_MM : DspMMRel, PICK_QB_MM_ENC, PICK_QB_DESC; +def SHILO_MM : DspMMRel, SHILO_MM_ENC, SHILO_DESC; +def SHILOV_MM : DspMMRel, SHILOV_MM_ENC, SHILOV_DESC; +def WRDSP_MM : DspMMRel, WRDSP_MM_ENC, WRDSP_MM_DESC; +def MODSUB_MM : DspMMRel, MODSUB_MM_ENC, MODSUB_DESC; +def MULSAQ_S_W_PH_MM : DspMMRel, MULSAQ_S_W_PH_MM_ENC, MULSAQ_S_W_PH_DESC; +def BITREV_MM : DspMMRel, BITREV_MM_ENC, BITREV_MM_DESC; +def BPOSGE32_MM : DspMMRel, BPOSGE32_MM_ENC, BPOSGE32_MM_DESC, + ISA_MAXIS1_NOT_32R6_64R6; +def CMP_EQ_PH_MM : DspMMRel, CMP_EQ_PH_MM_ENC, CMP_EQ_PH_DESC; +def CMP_LT_PH_MM : DspMMRel, CMP_LT_PH_MM_ENC, CMP_LT_PH_DESC; +def CMP_LE_PH_MM : DspMMRel, CMP_LE_PH_MM_ENC, CMP_LE_PH_DESC; +def CMPGU_EQ_QB_MM : DspMMRel, CMPGU_EQ_QB_MM_ENC, CMPGU_EQ_QB_DESC; +def CMPGU_LT_QB_MM : DspMMRel, CMPGU_LT_QB_MM_ENC, CMPGU_LT_QB_DESC; +def CMPGU_LE_QB_MM : DspMMRel, CMPGU_LE_QB_MM_ENC, CMPGU_LE_QB_DESC; +def CMPU_EQ_QB_MM : DspMMRel, CMPU_EQ_QB_MM_ENC, CMPU_EQ_QB_DESC; +def CMPU_LT_QB_MM : DspMMRel, CMPU_LT_QB_MM_ENC, CMPU_LT_QB_DESC; +def CMPU_LE_QB_MM : DspMMRel, CMPU_LE_QB_MM_ENC, CMPU_LE_QB_DESC; +// microMAXIS DSP Rev 2 +def ABSQ_S_QB_MMR2 : DspMMRel, ABSQ_S_QB_MMR2_ENC, ABSQ_S_QB_MMR2_DESC, + ISA_DSPR2; +def ADDQH_PH_MMR2 : DspMMRel, ADDQH_PH_MMR2_ENC, ADDQH_PH_DESC, ISA_DSPR2; +def ADDQH_R_PH_MMR2 : DspMMRel, ADDQH_R_PH_MMR2_ENC, ADDQH_R_PH_DESC, ISA_DSPR2; +def ADDQH_W_MMR2 : DspMMRel, ADDQH_W_MMR2_ENC, ADDQH_W_DESC, ISA_DSPR2; +def ADDQH_R_W_MMR2 : DspMMRel, ADDQH_R_W_MMR2_ENC, ADDQH_R_W_DESC, ISA_DSPR2; +def ADDU_PH_MMR2 : DspMMRel, ADDU_PH_MMR2_ENC, ADDU_PH_DESC, ISA_DSPR2; +def ADDU_S_PH_MMR2 : DspMMRel, ADDU_S_PH_MMR2_ENC, ADDU_S_PH_DESC, ISA_DSPR2; +def ADDUH_QB_MMR2 : DspMMRel, ADDUH_QB_MMR2_ENC, ADDUH_QB_DESC, ISA_DSPR2; +def ADDUH_R_QB_MMR2 : DspMMRel, ADDUH_R_QB_MMR2_ENC, ADDUH_R_QB_DESC, ISA_DSPR2; +def DPA_W_PH_MMR2 : DspMMRel, DPA_W_PH_MMR2_ENC, DPA_W_PH_DESC, ISA_DSPR2; +def DPAQX_S_W_PH_MMR2 : DspMMRel, DPAQX_S_W_PH_MMR2_ENC, DPAQX_S_W_PH_DESC, + ISA_DSPR2; +def DPAQX_SA_W_PH_MMR2 : DspMMRel, DPAQX_SA_W_PH_MMR2_ENC, DPAQX_SA_W_PH_DESC, + ISA_DSPR2; +def DPAX_W_PH_MMR2 : DspMMRel, DPAX_W_PH_MMR2_ENC, DPAX_W_PH_DESC, ISA_DSPR2; +def SHRA_QB_MMR2 : DspMMRel, SHRA_QB_MMR2_ENC, SHRA_QB_MMR2_DESC, ISA_DSPR2; +def SHRA_R_QB_MMR2 : DspMMRel, SHRA_R_QB_MMR2_ENC, SHRA_R_QB_MMR2_DESC, + ISA_DSPR2; +def SHRAV_QB_MMR2 : DspMMRel, SHRAV_QB_MMR2_ENC, SHRAV_QB_MMR2_DESC, ISA_DSPR2; +def SHRAV_R_QB_MMR2 : DspMMRel, SHRAV_R_QB_MMR2_ENC, SHRAV_R_QB_MMR2_DESC, + ISA_DSPR2; +def BALIGN_MMR2 : DspMMRel, BALIGN_MMR2_ENC, BALIGN_MMR2_DESC, ISA_DSPR2; +def CMPGDU_EQ_QB_MMR2 : DspMMRel, CMPGDU_EQ_QB_MMR2_ENC, CMPGDU_EQ_QB_DESC, + ISA_DSPR2; +def CMPGDU_LT_QB_MMR2 : DspMMRel, CMPGDU_LT_QB_MMR2_ENC, CMPGDU_LT_QB_DESC, + ISA_DSPR2; +def CMPGDU_LE_QB_MMR2 : DspMMRel, CMPGDU_LE_QB_MMR2_ENC, CMPGDU_LE_QB_DESC, + ISA_DSPR2; +def SHRL_PH_MMR2 : DspMMRel, SHRL_PH_MMR2_ENC, SHRL_PH_MMR2_DESC, ISA_DSPR2; +def SHRLV_PH_MMR2 : DspMMRel, SHRLV_PH_MMR2_ENC, SHRLV_PH_MMR2_DESC, ISA_DSPR2; +def SUBQH_PH_MMR2 : DspMMRel, SUBQH_PH_MMR2_ENC, SUBQH_PH_DESC, ISA_DSPR2; +def SUBQH_R_PH_MMR2 : DspMMRel, SUBQH_R_PH_MMR2_ENC, SUBQH_R_PH_DESC, ISA_DSPR2; +def SUBQH_W_MMR2 : DspMMRel, SUBQH_W_MMR2_ENC, SUBQH_W_DESC, ISA_DSPR2; +def SUBQH_R_W_MMR2 : DspMMRel, SUBQH_R_W_MMR2_ENC, SUBQH_R_W_DESC, ISA_DSPR2; +def SUBU_PH_MMR2 : DspMMRel, SUBU_PH_MMR2_ENC, SUBU_PH_DESC, ISA_DSPR2; +def SUBU_S_PH_MMR2 : DspMMRel, SUBU_S_PH_MMR2_ENC, SUBU_S_PH_DESC, ISA_DSPR2; +def SUBUH_QB_MMR2 : DspMMRel, SUBUH_QB_MMR2_ENC, SUBUH_QB_DESC, ISA_DSPR2; +def SUBUH_R_QB_MMR2 : DspMMRel, SUBUH_R_QB_MMR2_ENC, SUBUH_R_QB_DESC, ISA_DSPR2; +def DPS_W_PH_MMR2 : DspMMRel, DPS_W_PH_MMR2_ENC, DPS_W_PH_DESC, ISA_DSPR2; +def DPSQX_S_W_PH_MMR2 : DspMMRel, DPSQX_S_W_PH_MMR2_ENC, DPSQX_S_W_PH_DESC, + ISA_DSPR2; +def DPSQX_SA_W_PH_MMR2 : DspMMRel, DPSQX_SA_W_PH_MMR2_ENC, DPSQX_SA_W_PH_DESC, + ISA_DSPR2; +def DPSX_W_PH_MMR2 : DspMMRel, DPSX_W_PH_MMR2_ENC, DPSX_W_PH_DESC, ISA_DSPR2; +def MUL_PH_MMR2 : DspMMRel, MUL_PH_MMR2_ENC, MUL_PH_DESC, ISA_DSPR2; +def MUL_S_PH_MMR2 : DspMMRel, MUL_S_PH_MMR2_ENC, MUL_S_PH_DESC, ISA_DSPR2; +def MULQ_RS_W_MMR2 : DspMMRel, MULQ_RS_W_MMR2_ENC, MULQ_RS_W_DESC, ISA_DSPR2; +def MULQ_S_PH_MMR2 : DspMMRel, MULQ_S_PH_MMR2_ENC, MULQ_S_PH_DESC, ISA_DSPR2; +def MULQ_S_W_MMR2 : DspMMRel, MULQ_S_W_MMR2_ENC, MULQ_S_W_DESC, ISA_DSPR2; +def PRECR_QB_PH_MMR2 : DspMMRel, PRECR_QB_PH_MMR2_ENC, PRECR_QB_PH_DESC, + ISA_DSPR2; +def PRECR_SRA_PH_W_MMR2 : DspMMRel, PRECR_SRA_PH_W_MMR2_ENC, + PRECR_SRA_PH_W_DESC, ISA_DSPR2; +def PRECR_SRA_R_PH_W_MMR2 : DspMMRel, PRECR_SRA_R_PH_W_MMR2_ENC, + PRECR_SRA_R_PH_W_DESC, ISA_DSPR2; +def PREPEND_MMR2 : DspMMRel, PREPEND_MMR2_ENC, PREPEND_DESC, ISA_DSPR2; + +// Instruction alias. +def : MMDSPInstAlias<"wrdsp $rt", (WRDSP_MM GPR32Opnd:$rt, 0x1F), 1>; +def APPEND_MMR2 : DspMMRel, APPEND_MMR2_ENC, APPEND_DESC, ISA_DSPR2; +def MULSA_W_PH_MMR2 : DspMMRel, MULSA_W_PH_MMR2_ENC, MULSA_W_PH_DESC, ISA_DSPR2; +// microMAXIS DSP Rev 3 +def BPOSGE32C_MMR3 : DspMMRel, BPOSGE32C_MMR3_ENC, BPOSGE32C_MMR3_DESC, + ISA_DSPR3; diff --git a/lib/Target/Maxis/MicroMaxisInstrFPU.td b/lib/Target/Maxis/MicroMaxisInstrFPU.td new file mode 100644 index 00000000..1fcd6a45 --- /dev/null +++ b/lib/Target/Maxis/MicroMaxisInstrFPU.td @@ -0,0 +1,349 @@ +//==- MicroMaxisInstrFPU.td - microMAXIS FPU Instruction Info -*- tablegen -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the microMAXIS FPU instruction set. +// +//===----------------------------------------------------------------------===// + +let isCodeGenOnly = 1 in { +def FADD_S_MM : MMRel, ADDS_FT<"add.s", FGR32Opnd, II_ADD_S, 1, fadd>, + ADDS_FM_MM<0, 0x30>, ISA_MICROMAXIS; +def FDIV_S_MM : MMRel, ADDS_FT<"div.s", FGR32Opnd, II_DIV_S, 0, fdiv>, + ADDS_FM_MM<0, 0xf0>, ISA_MICROMAXIS; +def FMUL_S_MM : MMRel, ADDS_FT<"mul.s", FGR32Opnd, II_MUL_S, 1, fmul>, + ADDS_FM_MM<0, 0xb0>, ISA_MICROMAXIS; +def FSUB_S_MM : MMRel, ADDS_FT<"sub.s", FGR32Opnd, II_SUB_S, 0, fsub>, + ADDS_FM_MM<0, 0x70>, ISA_MICROMAXIS; + +def FADD_MM : MMRel, ADDS_FT<"add.d", AFGR64Opnd, II_ADD_D, 1, fadd>, + ADDS_FM_MM<1, 0x30>, ISA_MICROMAXIS; +def FDIV_MM : MMRel, ADDS_FT<"div.d", AFGR64Opnd, II_DIV_D, 0, fdiv>, + ADDS_FM_MM<1, 0xf0>, ISA_MICROMAXIS; +def FMUL_MM : MMRel, ADDS_FT<"mul.d", AFGR64Opnd, II_MUL_D, 1, fmul>, + ADDS_FM_MM<1, 0xb0>, ISA_MICROMAXIS; +def FSUB_MM : MMRel, ADDS_FT<"sub.d", AFGR64Opnd, II_SUB_D, 0, fsub>, + ADDS_FM_MM<1, 0x70>, ISA_MICROMAXIS; + +def LWXC1_MM : MMRel, LWXC1_FT<"lwxc1", FGR32Opnd, II_LWXC1, load>, + LWXC1_FM_MM<0x48>, ISA_MICROMAXIS32_NOT_MAXIS32R6; +def SWXC1_MM : MMRel, SWXC1_FT<"swxc1", FGR32Opnd, II_SWXC1, store>, + SWXC1_FM_MM<0x88>, ISA_MICROMAXIS32_NOT_MAXIS32R6; + +// FIXME: These instruction definitions are incorrect. They should be 64-bit +// FPU only. +def LUXC1_MM : MMRel, LWXC1_FT<"luxc1", AFGR64Opnd, II_LUXC1>, + LWXC1_FM_MM<0x148>, ISA_MICROMAXIS32_NOT_MAXIS32R6; +def SUXC1_MM : MMRel, SWXC1_FT<"suxc1", AFGR64Opnd, II_SUXC1>, + SWXC1_FM_MM<0x188>, ISA_MICROMAXIS32_NOT_MAXIS32R6; + +def FCMP_S32_MM : MMRel, CEQS_FT<"s", FGR32, II_C_CC_S, MaxisFPCmp>, + CEQS_FM_MM<0>, ISA_MICROMAXIS32_NOT_MAXIS32R6 { + // FIXME: This is a required to work around the fact that these instructions + // only use $fcc0. Ideally, MaxisFPCmp nodes could be removed and the + // fcc register set is used directly. + bits<3> fcc = 0; +} + +def FCMP_D32_MM : MMRel, CEQS_FT<"d", AFGR64, II_C_CC_D, MaxisFPCmp>, + CEQS_FM_MM<1>, ISA_MICROMAXIS32_NOT_MAXIS32R6 { + // FIXME: This is a required to work around the fact that these instructions + // only use $fcc0. Ideally, MaxisFPCmp nodes could be removed and the + // fcc register set is used directly. + bits<3> fcc = 0; +} + +} + +let DecoderNamespace = "MicroMaxis" in { + def BC1F_MM : MMRel, BC1F_FT<"bc1f", brtarget_mm, II_BC1F, MAXIS_BRANCH_F>, + BC1F_FM_MM<0x1c>, ISA_MICROMAXIS32_NOT_MAXIS32R6; + def BC1T_MM : MMRel, BC1F_FT<"bc1t", brtarget_mm, II_BC1T, MAXIS_BRANCH_T>, + BC1F_FM_MM<0x1d>, ISA_MICROMAXIS32_NOT_MAXIS32R6; +} + +let isCodeGenOnly = 1 in { +def CVT_W_S_MM : MMRel, ABSS_FT<"cvt.w.s", FGR32Opnd, FGR32Opnd, II_CVT>, + ROUND_W_FM_MM<0, 0x24>, ISA_MICROMAXIS; +def ROUND_W_S_MM : MMRel, StdMMR6Rel, ABSS_FT<"round.w.s", FGR32Opnd, FGR32Opnd, + II_ROUND>, ROUND_W_FM_MM<0, 0xec>, + ISA_MICROMAXIS; + +def CEIL_W_MM : MMRel, ABSS_FT<"ceil.w.d", FGR32Opnd, AFGR64Opnd, II_CEIL>, + ROUND_W_FM_MM<1, 0x6c>, ISA_MICROMAXIS, FGR_32; +def CVT_W_MM : MMRel, ABSS_FT<"cvt.w.d", FGR32Opnd, AFGR64Opnd, II_CVT>, + ROUND_W_FM_MM<1, 0x24>, ISA_MICROMAXIS, FGR_32; +def FLOOR_W_MM : MMRel, ABSS_FT<"floor.w.d", FGR32Opnd, AFGR64Opnd, II_FLOOR>, + ROUND_W_FM_MM<1, 0x2c>, ISA_MICROMAXIS, FGR_32; +def ROUND_W_MM : MMRel, StdMMR6Rel, ABSS_FT<"round.w.d", FGR32Opnd, AFGR64Opnd, + II_ROUND>, ROUND_W_FM_MM<1, 0xec>, + ISA_MICROMAXIS, FGR_32; +def TRUNC_W_MM : MMRel, ABSS_FT<"trunc.w.d", FGR32Opnd, AFGR64Opnd, II_TRUNC>, + ROUND_W_FM_MM<1, 0xac>, ISA_MICROMAXIS, FGR_32; + +def FSQRT_MM : MMRel, ABSS_FT<"sqrt.d", AFGR64Opnd, AFGR64Opnd, II_SQRT_D, + fsqrt>, ROUND_W_FM_MM<1, 0x28>, + ISA_MICROMAXIS, FGR_32; + +def CVT_L_S_MM : MMRel, ABSS_FT<"cvt.l.s", FGR64Opnd, FGR32Opnd, II_CVT>, + ROUND_W_FM_MM<0, 0x4>, ISA_MICROMAXIS, FGR_64; +def CVT_L_D64_MM : MMRel, ABSS_FT<"cvt.l.d", FGR64Opnd, FGR64Opnd, II_CVT>, + ROUND_W_FM_MM<1, 0x4>, ISA_MICROMAXIS, FGR_64; + +} + +let DecoderNamespace = "MicroMaxis" in { + def FABS_S_MM : MMRel, ABSS_FT<"abs.s", FGR32Opnd, FGR32Opnd, II_ABS, fabs>, + ABS_FM_MM<0, 0xd>, ISA_MICROMAXIS; + def FABS_MM : MMRel, ABSS_FT<"abs.d", AFGR64Opnd, AFGR64Opnd, II_ABS, fabs>, + ABS_FM_MM<1, 0xd>, ISA_MICROMAXIS, FGR_32; +} + +let isCodeGenOnly = 1 in { +def FMOV_S_MM : MMRel, ABSS_FT<"mov.s", FGR32Opnd, FGR32Opnd, II_MOV_S>, + ABS_FM_MM<0, 0x1>, ISA_MICROMAXIS; +def FNEG_S_MM : MMRel, ABSS_FT<"neg.s", FGR32Opnd, FGR32Opnd, II_NEG, fneg>, + ABS_FM_MM<0, 0x2d>, ISA_MICROMAXIS; +def CVT_D_S_MM : MMRel, ABSS_FT<"cvt.d.s", AFGR64Opnd, FGR32Opnd, II_CVT>, + ABS_FM_MM<0, 0x4d>, ISA_MICROMAXIS, FGR_32; +def CVT_D32_W_MM : MMRel, ABSS_FT<"cvt.d.w", AFGR64Opnd, FGR32Opnd, II_CVT>, + ABS_FM_MM<1, 0x4d>, ISA_MICROMAXIS, FGR_32; +def CVT_S_D32_MM : MMRel, ABSS_FT<"cvt.s.d", FGR32Opnd, AFGR64Opnd, II_CVT>, + ABS_FM_MM<0, 0x6d>, ISA_MICROMAXIS, FGR_32; +def CVT_S_W_MM : MMRel, ABSS_FT<"cvt.s.w", FGR32Opnd, FGR32Opnd, II_CVT>, + ABS_FM_MM<1, 0x6d>, ISA_MICROMAXIS; + +def FNEG_MM : MMRel, ABSS_FT<"neg.d", AFGR64Opnd, AFGR64Opnd, II_NEG, fneg>, + ABS_FM_MM<1, 0x2d>, ISA_MICROMAXIS, FGR_32; + +def FMOV_D32_MM : MMRel, ABSS_FT<"mov.d", AFGR64Opnd, AFGR64Opnd, II_MOV_D>, + ABS_FM_MM<1, 0x1>, ISA_MICROMAXIS, FGR_32; + +def MOVZ_I_S_MM : MMRel, CMov_I_F_FT<"movz.s", GPR32Opnd, FGR32Opnd, + II_MOVZ_S>, CMov_I_F_FM_MM<0x78, 0>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; +def MOVN_I_S_MM : MMRel, CMov_I_F_FT<"movn.s", GPR32Opnd, FGR32Opnd, + II_MOVN_S>, CMov_I_F_FM_MM<0x38, 0>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; +def MOVZ_I_D32_MM : MMRel, CMov_I_F_FT<"movz.d", GPR32Opnd, AFGR64Opnd, + II_MOVZ_D>, CMov_I_F_FM_MM<0x78, 1>, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +def MOVN_I_D32_MM : MMRel, CMov_I_F_FT<"movn.d", GPR32Opnd, AFGR64Opnd, + II_MOVN_D>, CMov_I_F_FM_MM<0x38, 1>, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; + +def MOVT_S_MM : MMRel, CMov_F_F_FT<"movt.s", FGR32Opnd, II_MOVT_S, + MaxisCMovFP_T>, CMov_F_F_FM_MM<0x60, 0>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; +def MOVF_S_MM : MMRel, CMov_F_F_FT<"movf.s", FGR32Opnd, II_MOVF_S, + MaxisCMovFP_F>, CMov_F_F_FM_MM<0x20, 0>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; +def MOVT_D32_MM : MMRel, CMov_F_F_FT<"movt.d", AFGR64Opnd, II_MOVT_D, + MaxisCMovFP_T>, CMov_F_F_FM_MM<0x60, 1>, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +def MOVF_D32_MM : MMRel, CMov_F_F_FT<"movf.d", AFGR64Opnd, II_MOVF_D, + MaxisCMovFP_F>, CMov_F_F_FM_MM<0x20, 1>, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +def MFC1_MM : MMRel, MFC1_FT<"mfc1", GPR32Opnd, FGR32Opnd, + II_MFC1, bitconvert>, MFC1_FM_MM<0x80>, + ISA_MICROMAXIS; +def MTC1_MM : MMRel, MTC1_FT<"mtc1", FGR32Opnd, GPR32Opnd, + II_MTC1, bitconvert>, MFC1_FM_MM<0xa0>, + ISA_MICROMAXIS; + +def MADD_S_MM : MMRel, MADDS_FT<"madd.s", FGR32Opnd, II_MADD_S, fadd>, + MADDS_FM_MM<0x1>, ISA_MICROMAXIS32_NOT_MAXIS32R6; +def MSUB_S_MM : MMRel, MADDS_FT<"msub.s", FGR32Opnd, II_MSUB_S, fsub>, + MADDS_FM_MM<0x21>, ISA_MICROMAXIS32_NOT_MAXIS32R6; +def NMADD_S_MM : MMRel, NMADDS_FT<"nmadd.s", FGR32Opnd, II_NMADD_S, fadd>, + MADDS_FM_MM<0x2>, ISA_MICROMAXIS32_NOT_MAXIS32R6; +def NMSUB_S_MM : MMRel, NMADDS_FT<"nmsub.s", FGR32Opnd, II_NMSUB_S, fsub>, + MADDS_FM_MM<0x22>, ISA_MICROMAXIS32_NOT_MAXIS32R6; + +def MADD_D32_MM : MMRel, MADDS_FT<"madd.d", AFGR64Opnd, II_MADD_D, fadd>, + MADDS_FM_MM<0x9>, ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +def MSUB_D32_MM : MMRel, MADDS_FT<"msub.d", AFGR64Opnd, II_MSUB_D, fsub>, + MADDS_FM_MM<0x29>, ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +def NMADD_D32_MM : MMRel, NMADDS_FT<"nmadd.d", AFGR64Opnd, II_NMADD_D, fadd>, + MADDS_FM_MM<0xa>, ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +def NMSUB_D32_MM : MMRel, NMADDS_FT<"nmsub.d", AFGR64Opnd, II_NMSUB_D, fsub>, + MADDS_FM_MM<0x2a>, ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +} + +def FLOOR_W_S_MM : MMRel, ABSS_FT<"floor.w.s", FGR32Opnd, FGR32Opnd, + II_FLOOR>, ROUND_W_FM_MM<0, 0x2c>, + ISA_MICROMAXIS; +def TRUNC_W_S_MM : MMRel, StdMMR6Rel, ABSS_FT<"trunc.w.s", FGR32Opnd, + FGR32Opnd, II_TRUNC>, + ROUND_W_FM_MM<0, 0xac>, ISA_MICROMAXIS; +def CEIL_W_S_MM : MMRel, ABSS_FT<"ceil.w.s", FGR32Opnd, FGR32Opnd, II_CEIL>, + ROUND_W_FM_MM<0, 0x6c>, ISA_MICROMAXIS; +def FSQRT_S_MM : MMRel, ABSS_FT<"sqrt.s", FGR32Opnd, FGR32Opnd, II_SQRT_S, + fsqrt>, ROUND_W_FM_MM<0, 0x28>, ISA_MICROMAXIS; +def MTHC1_MM : MMRel, MTC1_64_FT<"mthc1", AFGR64Opnd, GPR32Opnd, II_MTHC1>, + MFC1_FM_MM<0xe0>, ISA_MICROMAXIS, FGR_32; +def MFHC1_MM : MMRel, MFC1_FT<"mfhc1", GPR32Opnd, AFGR64Opnd, II_MFHC1>, + MFC1_FM_MM<0xc0>, ISA_MICROMAXIS, FGR_32; + +let DecoderNamespace = "MicroMaxis" in { + def CFC1_MM : MMRel, MFC1_FT<"cfc1", GPR32Opnd, CCROpnd, II_CFC1>, + MFC1_FM_MM<0x40>, ISA_MICROMAXIS; + def CTC1_MM : MMRel, MTC1_FT<"ctc1", CCROpnd, GPR32Opnd, II_CTC1>, + MFC1_FM_MM<0x60>, ISA_MICROMAXIS; + def RECIP_S_MM : MMRel, ABSS_FT<"recip.s", FGR32Opnd, FGR32Opnd, + II_RECIP_S>, + ROUND_W_FM_MM<0b0, 0b01001000>, ISA_MICROMAXIS; + def RECIP_D32_MM : MMRel, ABSS_FT<"recip.d", AFGR64Opnd, AFGR64Opnd, + II_RECIP_D>, + ROUND_W_FM_MM<0b1, 0b01001000>, ISA_MICROMAXIS, FGR_32 { + let BaseOpcode = "RECIP_D32"; + } + let DecoderNamespace = "MicroMaxisFP64" in + def RECIP_D64_MM : MMRel, ABSS_FT<"recip.d", FGR64Opnd, FGR64Opnd, + II_RECIP_D>, + ROUND_W_FM_MM<0b1, 0b01001000>, ISA_MICROMAXIS, FGR_64; + def RSQRT_S_MM : MMRel, ABSS_FT<"rsqrt.s", FGR32Opnd, FGR32Opnd, + II_RECIP_S>, + ROUND_W_FM_MM<0b0, 0b00001000>; + def RSQRT_D32_MM : MMRel, ABSS_FT<"rsqrt.d", AFGR64Opnd, AFGR64Opnd, + II_RECIP_D>, + ROUND_W_FM_MM<0b1, 0b00001000>, ISA_MICROMAXIS, FGR_32 { + let BaseOpcode = "RSQRT_D32"; + } + let DecoderNamespace = "MicroMaxisFP64" in + def RSQRT_D64_MM : MMRel, ABSS_FT<"rsqrt.d", FGR64Opnd, FGR64Opnd, + II_RECIP_D>, + ROUND_W_FM_MM<0b1, 0b00001000>, ISA_MICROMAXIS, FGR_64; +} + +let DecoderNamespace = "MicroMaxis", DecoderMethod = "DecodeFMemMMR2" in { + def LDC1_MM : MMRel, LW_FT<"ldc1", AFGR64Opnd, mem_mm_16, II_LDC1, load>, + LW_FM_MM<0x2f>, ISA_MICROMAXIS, FGR_32 { + let BaseOpcode = "LDC132"; + } + def SDC1_MM : MMRel, SW_FT<"sdc1", AFGR64Opnd, mem_mm_16, II_SDC1, store>, + LW_FM_MM<0x2e>, ISA_MICROMAXIS, FGR_32; + def LWC1_MM : MMRel, LW_FT<"lwc1", FGR32Opnd, mem_mm_16, II_LWC1, load>, + LW_FM_MM<0x27>, ISA_MICROMAXIS; + def SWC1_MM : MMRel, SW_FT<"swc1", FGR32Opnd, mem_mm_16, II_SWC1, store>, + LW_FM_MM<0x26>, ISA_MICROMAXIS; +} + +multiclass C_COND_MM fmt, + InstrItinClass itin> { + def C_F_#NAME#_MM : MMRel, C_COND_FT<"f", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.f."#NAME; + let isCommutable = 1; + } + def C_UN_#NAME#_MM : MMRel, C_COND_FT<"un", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.un."#NAME; + let isCommutable = 1; + } + def C_EQ_#NAME#_MM : MMRel, C_COND_FT<"eq", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.eq."#NAME; + let isCommutable = 1; + } + def C_UEQ_#NAME#_MM : MMRel, C_COND_FT<"ueq", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.ueq."#NAME; + let isCommutable = 1; + } + def C_OLT_#NAME#_MM : MMRel, C_COND_FT<"olt", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.olt."#NAME; + } + def C_ULT_#NAME#_MM : MMRel, C_COND_FT<"ult", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.ult."#NAME; + } + def C_OLE_#NAME#_MM : MMRel, C_COND_FT<"ole", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.ole."#NAME; + } + def C_ULE_#NAME#_MM : MMRel, C_COND_FT<"ule", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.ule."#NAME; + } + def C_SF_#NAME#_MM : MMRel, C_COND_FT<"sf", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.sf."#NAME; + let isCommutable = 1; + } + def C_NGLE_#NAME#_MM : MMRel, C_COND_FT<"ngle", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.ngle."#NAME; + } + def C_SEQ_#NAME#_MM : MMRel, C_COND_FT<"seq", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.seq."#NAME; + let isCommutable = 1; + } + def C_NGL_#NAME#_MM : MMRel, C_COND_FT<"ngl", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.ngl."#NAME; + } + def C_LT_#NAME#_MM : MMRel, C_COND_FT<"lt", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.lt."#NAME; + } + def C_NGE_#NAME#_MM : MMRel, C_COND_FT<"nge", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.nge."#NAME; + } + def C_LE_#NAME#_MM : MMRel, C_COND_FT<"le", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.le."#NAME; + } + def C_NGT_#NAME#_MM : MMRel, C_COND_FT<"ngt", TypeStr, RC, itin>, + C_COND_FM_MM { + let BaseOpcode = "c.ngt."#NAME; + } +} + +defm S : C_COND_MM<"s", FGR32Opnd, 0b00, II_C_CC_S>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; +defm D32 : C_COND_MM<"d", AFGR64Opnd, 0b01, II_C_CC_D>, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +let DecoderNamespace = "Maxis64" in + defm D64 : C_COND_MM<"d", FGR64Opnd, 0b01, II_C_CC_D>, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_64; + +defm S_MM : C_COND_ALIASES<"s", FGR32Opnd>, HARDFLOAT, + ISA_MICROMAXIS32_NOT_MAXIS32R6; +defm D32_MM : C_COND_ALIASES<"d", AFGR64Opnd>, HARDFLOAT, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +defm D64_MM : C_COND_ALIASES<"d", FGR64Opnd>, HARDFLOAT, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_64; + +defm : BC1_ALIASES, + ISA_MICROMAXIS32_NOT_MAXIS32R6, HARDFLOAT; + + +// To generate NMADD and NMSUB instructions when fneg node is present +let AdditionalPredicates = [NoNaNsFPMath, HasMadd4, + InMicroMaxis, NotMaxis32r6] in { + defm : NMADD_NMSUB, + ISA_MICROMAXIS32_NOT_MAXIS32R6; + defm : NMADD_NMSUB, + ISA_MICROMAXIS32_NOT_MAXIS32R6, FGR_32; +} + +//===----------------------------------------------------------------------===// +// Floating Point Patterns +//===----------------------------------------------------------------------===// + +// Patterns for loads/stores with a reg+imm operand. +let AddedComplexity = 40 in { + def : LoadRegImmPat, ISA_MICROMAXIS, FGR_32; + def : StoreRegImmPat, ISA_MICROMAXIS, FGR_32; + def : LoadRegImmPat, ISA_MICROMAXIS; + def : StoreRegImmPat, ISA_MICROMAXIS; +} diff --git a/lib/Target/Maxis/MicroMaxisInstrFormats.td b/lib/Target/Maxis/MicroMaxisInstrFormats.td new file mode 100644 index 00000000..fce17271 --- /dev/null +++ b/lib/Target/Maxis/MicroMaxisInstrFormats.td @@ -0,0 +1,1055 @@ +//===----------------------------------------------------------------------===// +// MicroMAXIS Base Classes +//===----------------------------------------------------------------------===// + +// +// Base class for MicroMaxis instructions. +// This class does not depend on the instruction size. +// +class MicroMaxisInstBase pattern, + InstrItinClass itin, Format f> : Instruction +{ + let Namespace = "Maxis"; + let DecoderNamespace = "MicroMaxis"; + + let OutOperandList = outs; + let InOperandList = ins; + + let AsmString = asmstr; + let Pattern = pattern; + let Itinerary = itin; + + let Predicates = [InMicroMaxis]; + + Format Form = f; +} + +// +// Base class for MicroMAXIS 16-bit instructions. +// +class MicroMaxisInst16 pattern, + InstrItinClass itin, Format f> : + MicroMaxisInstBase +{ + let Size = 2; + field bits<16> Inst; + field bits<16> SoftFail = 0; + bits<6> Opcode = 0x0; +} + +//===----------------------------------------------------------------------===// +// MicroMAXIS 16-bit Instruction Formats +//===----------------------------------------------------------------------===// + +class ARITH_FM_MM16 { + bits<3> rd; + bits<3> rt; + bits<3> rs; + + bits<16> Inst; + + let Inst{15-10} = 0x01; + let Inst{9-7} = rd; + let Inst{6-4} = rt; + let Inst{3-1} = rs; + let Inst{0} = funct; +} + +class ANDI_FM_MM16 funct> { + bits<3> rd; + bits<3> rs; + bits<4> imm; + + bits<16> Inst; + + let Inst{15-10} = funct; + let Inst{9-7} = rd; + let Inst{6-4} = rs; + let Inst{3-0} = imm; +} + +class LOGIC_FM_MM16 funct> { + bits<3> rt; + bits<3> rs; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-6} = funct; + let Inst{5-3} = rt; + let Inst{2-0} = rs; +} + +class SHIFT_FM_MM16 funct> { + bits<3> rd; + bits<3> rt; + bits<3> shamt; + + bits<16> Inst; + + let Inst{15-10} = 0x09; + let Inst{9-7} = rd; + let Inst{6-4} = rt; + let Inst{3-1} = shamt; + let Inst{0} = funct; +} + +class ADDIUR2_FM_MM16 { + bits<3> rd; + bits<3> rs; + bits<3> imm; + + bits<16> Inst; + + let Inst{15-10} = 0x1b; + let Inst{9-7} = rd; + let Inst{6-4} = rs; + let Inst{3-1} = imm; + let Inst{0} = 0; +} + +class LOAD_STORE_FM_MM16 op> { + bits<3> rt; + bits<7> addr; + + bits<16> Inst; + + let Inst{15-10} = op; + let Inst{9-7} = rt; + let Inst{6-4} = addr{6-4}; + let Inst{3-0} = addr{3-0}; +} + +class LOAD_STORE_SP_FM_MM16 op> { + bits<5> rt; + bits<5> offset; + + bits<16> Inst; + + let Inst{15-10} = op; + let Inst{9-5} = rt; + let Inst{4-0} = offset; +} + +class LOAD_GP_FM_MM16 op> { + bits<3> rt; + bits<7> offset; + + bits<16> Inst; + + let Inst{15-10} = op; + let Inst{9-7} = rt; + let Inst{6-0} = offset; +} + +class ADDIUS5_FM_MM16 { + bits<5> rd; + bits<4> imm; + + bits<16> Inst; + + let Inst{15-10} = 0x13; + let Inst{9-5} = rd; + let Inst{4-1} = imm; + let Inst{0} = 0; +} + +class ADDIUSP_FM_MM16 { + bits<9> imm; + + bits<16> Inst; + + let Inst{15-10} = 0x13; + let Inst{9-1} = imm; + let Inst{0} = 1; +} + +class MOVE_FM_MM16 funct> { + bits<5> rs; + bits<5> rd; + + bits<16> Inst; + + let Inst{15-10} = funct; + let Inst{9-5} = rd; + let Inst{4-0} = rs; +} + +class LI_FM_MM16 { + bits<3> rd; + bits<7> imm; + + bits<16> Inst; + + let Inst{15-10} = 0x3b; + let Inst{9-7} = rd; + let Inst{6-0} = imm; +} + +class JALR_FM_MM16 op> { + bits<5> rs; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-5} = op; + let Inst{4-0} = rs; +} + +class MFHILO_FM_MM16 funct> { + bits<5> rd; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-5} = funct; + let Inst{4-0} = rd; +} + +class JRADDIUSP_FM_MM16 op> { + bits<5> rs; + bits<5> imm; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-5} = op; + let Inst{4-0} = imm; +} + +class ADDIUR1SP_FM_MM16 { + bits<3> rd; + bits<6> imm; + + bits<16> Inst; + + let Inst{15-10} = 0x1b; + let Inst{9-7} = rd; + let Inst{6-1} = imm; + let Inst{0} = 1; +} + +class BRKSDBBP16_FM_MM op> { + bits<4> code_; + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-4} = op; + let Inst{3-0} = code_; +} + +class BEQNEZ_FM_MM16 op> { + bits<3> rs; + bits<7> offset; + + bits<16> Inst; + + let Inst{15-10} = op; + let Inst{9-7} = rs; + let Inst{6-0} = offset; +} + +class B16_FM { + bits<10> offset; + + bits<16> Inst; + + let Inst{15-10} = 0x33; + let Inst{9-0} = offset; +} + +class MOVEP_FM_MM16 { + bits<3> dst_regs; + bits<3> rt; + bits<3> rs; + + bits<16> Inst; + + let Inst{15-10} = 0x21; + let Inst{9-7} = dst_regs; + let Inst{6-4} = rt; + let Inst{3-1} = rs; + let Inst{0} = 0; +} + +//===----------------------------------------------------------------------===// +// MicroMAXIS 32-bit Instruction Formats +//===----------------------------------------------------------------------===// + +class MMArch { + string Arch = "micromaxis"; +} + +class ADD_FM_MM op, bits<10> funct> : MMArch { + bits<5> rt; + bits<5> rs; + bits<5> rd; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = rd; + let Inst{10} = 0; + let Inst{9-0} = funct; +} + +class ADDI_FM_MM op> : MMArch { + bits<5> rs; + bits<5> rt; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-0} = imm16; +} + +class SLTI_FM_MM op> : MMArch { + bits<5> rt; + bits<5> rs; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-0} = imm16; +} + +class LUI_FM_MM : MMArch { + bits<5> rt; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = 0x10; + let Inst{25-21} = 0xd; + let Inst{20-16} = rt; + let Inst{15-0} = imm16; +} + +class MULT_FM_MM funct> : MMArch { + bits<5> rs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class SRA_FM_MM funct, bit rotate> : MMArch { + bits<5> rd; + bits<5> rt; + bits<5> shamt; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rd; + let Inst{20-16} = rt; + let Inst{15-11} = shamt; + let Inst{10} = rotate; + let Inst{9-0} = funct; +} + +class SRLV_FM_MM funct, bit rotate> : MMArch { + bits<5> rd; + bits<5> rt; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = rd; + let Inst{10} = rotate; + let Inst{9-0} = funct; +} + +class LW_FM_MM op> : MMArch { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<16> offset = addr{15-0}; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-0} = offset; +} + +class POOL32C_LHUE_FM_MM op, bits<4> fmt, bits<3> funct> : MMArch { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-12} = fmt; + let Inst{11-9} = funct; + let Inst{8-0} = offset; +} + +class LWL_FM_MM funct> { + bits<5> rt; + bits<21> addr; + + bits<32> Inst; + + let Inst{31-26} = 0x18; + let Inst{25-21} = rt; + let Inst{20-16} = addr{20-16}; + let Inst{15-12} = funct; + let Inst{11-0} = addr{11-0}; +} + +class POOL32C_STEVA_LDEVA_FM_MM type, bits<3> funct> { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = 0x18; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-12} = type; + let Inst{11-9} = funct; + let Inst{8-0} = offset; +} + +class CMov_F_I_FM_MM func> : MMArch { + bits<5> rd; + bits<5> rs; + bits<3> fcc; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = rd; + let Inst{20-16} = rs; + let Inst{15-13} = fcc; + let Inst{12-6} = func; + let Inst{5-0} = 0x3b; +} + +class MTLO_FM_MM funct> : MMArch { + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = 0x00; + let Inst{20-16} = rs; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class MFLO_FM_MM funct> : MMArch { + bits<5> rd; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = 0x00; + let Inst{20-16} = rd; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class CLO_FM_MM funct> : MMArch { + bits<5> rd; + bits<5> rs; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = rd; + let Inst{20-16} = rs; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class SEB_FM_MM funct> : MMArch { + bits<5> rd; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = rd; + let Inst{20-16} = rt; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class EXT_FM_MM funct> : MMArch { + bits<5> rt; + bits<5> rs; + bits<5> pos; + bits<5> size; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-11} = size; + let Inst{10-6} = pos; + let Inst{5-0} = funct; +} + +class J_FM_MM op> : MMArch { + bits<26> target; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-0} = target; +} + +class JR_FM_MM funct> : MMArch { + bits<5> rs; + + bits<32> Inst; + + let Inst{31-21} = 0x00; + let Inst{20-16} = rs; + let Inst{15-14} = 0x0; + let Inst{13-6} = funct; + let Inst{5-0} = 0x3c; +} + +class JALR_FM_MM funct> { + bits<5> rs; + bits<5> rd; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = rd; + let Inst{20-16} = rs; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class BEQ_FM_MM op> : MMArch { + bits<5> rs; + bits<5> rt; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-0} = offset; +} + +class BGEZ_FM_MM funct> : MMArch { + bits<5> rs; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 0x10; + let Inst{25-21} = funct; + let Inst{20-16} = rs; + let Inst{15-0} = offset; +} + +class BGEZAL_FM_MM funct> : MMArch { + bits<5> rs; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 0x10; + let Inst{25-21} = funct; + let Inst{20-16} = rs; + let Inst{15-0} = offset; +} + +class SYNC_FM_MM : MMArch { + bits<5> stype; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = 0x0; + let Inst{20-16} = stype; + let Inst{15-6} = 0x1ad; + let Inst{5-0} = 0x3c; +} + +class SYNCI_FM_MM : MMArch { + bits<5> rs; + bits<16> offset; + bits<32> Inst; + + let Inst{31-26} = 0b010000; + let Inst{25-21} = 0b10000; + let Inst{20-16} = rs; + let Inst{15-0} = offset; +} + +class BRK_FM_MM : MMArch { + bits<10> code_1; + bits<10> code_2; + bits<32> Inst; + let Inst{31-26} = 0x0; + let Inst{25-16} = code_1; + let Inst{15-6} = code_2; + let Inst{5-0} = 0x07; +} + +class SYS_FM_MM : MMArch { + bits<10> code_; + bits<32> Inst; + let Inst{31-26} = 0x0; + let Inst{25-16} = code_; + let Inst{15-6} = 0x22d; + let Inst{5-0} = 0x3c; +} + +class WAIT_FM_MM { + bits<10> code_; + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-16} = code_; + let Inst{15-6} = 0x24d; + let Inst{5-0} = 0x3c; +} + +class ER_FM_MM funct> : MMArch { + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-16} = 0x00; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class EI_FM_MM funct> : MMArch { + bits<32> Inst; + bits<5> rt; + + let Inst{31-26} = 0x00; + let Inst{25-21} = 0x00; + let Inst{20-16} = rt; + let Inst{15-6} = funct; + let Inst{5-0} = 0x3c; +} + +class TEQ_FM_MM funct> : MMArch { + bits<5> rs; + bits<5> rt; + bits<4> code_; + + bits<32> Inst; + + let Inst{31-26} = 0x00; + let Inst{25-21} = rt; + let Inst{20-16} = rs; + let Inst{15-12} = code_; + let Inst{11-6} = funct; + let Inst{5-0} = 0x3c; +} + +class TEQI_FM_MM funct> : MMArch { + bits<5> rs; + bits<16> imm16; + + bits<32> Inst; + + let Inst{31-26} = 0x10; + let Inst{25-21} = funct; + let Inst{20-16} = rs; + let Inst{15-0} = imm16; +} + +class LL_FM_MM funct> : MMArch { + bits<5> rt; + bits<21> addr; + + bits<32> Inst; + + let Inst{31-26} = 0x18; + let Inst{25-21} = rt; + let Inst{20-16} = addr{20-16}; + let Inst{15-12} = funct; + let Inst{11-0} = addr{11-0}; +} + +class LLE_FM_MM funct> { + bits<5> rt; + bits<21> addr; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = 0x18; + let Inst{25-21} = rt; + let Inst{20-16} = base; + let Inst{15-12} = funct; + let Inst{11-9} = 0x6; + let Inst{8-0} = offset; +} + +class ADDS_FM_MM fmt, bits<8> funct> : MMArch { + bits<5> ft; + bits<5> fs; + bits<5> fd; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15-11} = fd; + let Inst{10} = 0; + let Inst{9-8} = fmt; + let Inst{7-0} = funct; + + list Pattern = []; +} + +class LWXC1_FM_MM funct> : MMArch { + bits<5> fd; + bits<5> base; + bits<5> index; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = index; + let Inst{20-16} = base; + let Inst{15-11} = fd; + let Inst{10-9} = 0x0; + let Inst{8-0} = funct; +} + +class SWXC1_FM_MM funct> : MMArch { + bits<5> fs; + bits<5> base; + bits<5> index; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = index; + let Inst{20-16} = base; + let Inst{15-11} = fs; + let Inst{10-9} = 0x0; + let Inst{8-0} = funct; +} + +class CEQS_FM_MM fmt> : MMArch { + bits<5> fs; + bits<5> ft; + bits<3> fcc; + bits<4> cond; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15-13} = fcc; + let Inst{12} = 0; + let Inst{11-10} = fmt; + let Inst{9-6} = cond; + let Inst{5-0} = 0x3c; +} + +class C_COND_FM_MM fmt, bits<4> c> : CEQS_FM_MM { + let cond = c; +} + +class BC1F_FM_MM tf> : MMArch { + bits<3> fcc; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 0x10; + let Inst{25-21} = tf; + let Inst{20-18} = fcc; // cc + let Inst{17-16} = 0x0; + let Inst{15-0} = offset; +} + +class ROUND_W_FM_MM fmt, bits<8> funct> : MMArch { + bits<5> fd; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = fd; + let Inst{20-16} = fs; + let Inst{15} = 0; + let Inst{14} = fmt; + let Inst{13-6} = funct; + let Inst{5-0} = 0x3b; +} + +class ABS_FM_MM fmt, bits<7> funct> : MMArch { + bits<5> fd; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = fd; + let Inst{20-16} = fs; + let Inst{15} = 0; + let Inst{14-13} = fmt; + let Inst{12-6} = funct; + let Inst{5-0} = 0x3b; +} + +class CMov_F_F_FM_MM func, bits<2> fmt> : MMArch { + bits<5> fd; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = fd; + let Inst{20-16} = fs; + let Inst{15-13} = 0x0; //cc + let Inst{12-11} = 0x0; + let Inst{10-9} = fmt; + let Inst{8-0} = func; +} + +class CMov_I_F_FM_MM funct, bits<2> fmt> : MMArch { + bits<5> fd; + bits<5> fs; + bits<5> rt; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = rt; + let Inst{20-16} = fs; + let Inst{15-11} = fd; + let Inst{9-8} = fmt; + let Inst{7-0} = funct; +} + +class MFC1_FM_MM funct> : MMArch { + bits<5> rt; + bits<5> fs; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = rt; + let Inst{20-16} = fs; + let Inst{15-14} = 0x0; + let Inst{13-6} = funct; + let Inst{5-0} = 0x3b; +} + +class MADDS_FM_MM funct>: MMArch { + bits<5> ft; + bits<5> fs; + bits<5> fd; + bits<5> fr; + + bits<32> Inst; + + let Inst{31-26} = 0x15; + let Inst{25-21} = ft; + let Inst{20-16} = fs; + let Inst{15-11} = fd; + let Inst{10-6} = fr; + let Inst{5-0} = funct; +} + +class COMPACT_BRANCH_FM_MM funct> { + bits<5> rs; + bits<16> offset; + + bits<32> Inst; + + let Inst{31-26} = 0x10; + let Inst{25-21} = funct; + let Inst{20-16} = rs; + let Inst{15-0} = offset; +} + +class COP0_TLB_FM_MM op> : MMArch { + bits<32> Inst; + + let Inst{31-26} = 0x0; + let Inst{25-16} = 0x0; + let Inst{15-6} = op; + let Inst{5-0} = 0x3c; +} + +class SDBBP_FM_MM : MMArch { + bits<10> code_; + + bits<32> Inst; + + let Inst{31-26} = 0x0; + let Inst{25-16} = code_; + let Inst{15-6} = 0x36d; + let Inst{5-0} = 0x3c; +} + +class RDHWR_FM_MM : MMArch { + bits<5> rt; + bits<5> rd; + + bits<32> Inst; + + let Inst{31-26} = 0x0; + let Inst{25-21} = rt; + let Inst{20-16} = rd; + let Inst{15-6} = 0x1ac; + let Inst{5-0} = 0x3c; +} + +class LWXS_FM_MM funct> { + bits<5> rd; + bits<5> base; + bits<5> index; + + bits<32> Inst; + + let Inst{31-26} = 0x0; + let Inst{25-21} = index; + let Inst{20-16} = base; + let Inst{15-11} = rd; + let Inst{10} = 0; + let Inst{9-0} = funct; +} + +class LWM_FM_MM funct> : MMArch { + bits<5> rt; + bits<21> addr; + + bits<32> Inst; + + let Inst{31-26} = 0x8; + let Inst{25-21} = rt; + let Inst{20-16} = addr{20-16}; + let Inst{15-12} = funct; + let Inst{11-0} = addr{11-0}; +} + +class LWM_FM_MM16 funct> : MMArch, PredicateControl { + bits<2> rt; + bits<4> addr; + + bits<16> Inst; + + let Inst{15-10} = 0x11; + let Inst{9-6} = funct; + let Inst{5-4} = rt; + let Inst{3-0} = addr; +} + +class CACHE_PREF_FM_MM op, bits<4> funct> : MMArch { + bits<21> addr; + bits<5> hint; + bits<5> base = addr{20-16}; + bits<12> offset = addr{11-0}; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = hint; + let Inst{20-16} = base; + let Inst{15-12} = funct; + let Inst{11-0} = offset; +} + +class CACHE_PREFE_FM_MM op, bits<3> funct> : MMArch { + bits<21> addr; + bits<5> hint; + bits<5> base = addr{20-16}; + bits<9> offset = addr{8-0}; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = hint; + let Inst{20-16} = base; + let Inst{15-12} = 0xA; + let Inst{11-9} = funct; + let Inst{8-0} = offset; +} + +class POOL32F_PREFX_FM_MM op, bits<9> funct> : MMArch { + bits<5> index; + bits<5> base; + bits<5> hint; + + bits<32> Inst; + + let Inst{31-26} = op; + let Inst{25-21} = index; + let Inst{20-16} = base; + let Inst{15-11} = hint; + let Inst{10-9} = 0x0; + let Inst{8-0} = funct; +} + +class BARRIER_FM_MM op> : MMArch { + bits<32> Inst; + + let Inst{31-26} = 0x0; + let Inst{25-21} = 0x0; + let Inst{20-16} = 0x0; + let Inst{15-11} = op; + let Inst{10-6} = 0x0; + let Inst{5-0} = 0x0; +} + +class ADDIUPC_FM_MM { + bits<3> rs; + bits<23> imm; + + bits<32> Inst; + + let Inst{31-26} = 0x1e; + let Inst{25-23} = rs; + let Inst{22-0} = imm; +} + +class POOL32A_CFTC2_FM_MM funct> : MMArch { + bits<5> rt; + bits<5> impl; + + bits<32> Inst; + + let Inst{31-26} = 0b000000; + let Inst{25-21} = rt; + let Inst{20-16} = impl; + let Inst{15-6} = funct; + let Inst{5-0} = 0b111100; +} diff --git a/lib/Target/Maxis/MicroMaxisInstrInfo.td b/lib/Target/Maxis/MicroMaxisInstrInfo.td new file mode 100644 index 00000000..51fcc75f --- /dev/null +++ b/lib/Target/Maxis/MicroMaxisInstrInfo.td @@ -0,0 +1,1191 @@ +def addrimm11 : ComplexPattern; +def addrimm12 : ComplexPattern; +def addrimm16 : ComplexPattern; +def addrimm4lsl2 : ComplexPattern; + +def simm9_addiusp : Operand { + let EncoderMethod = "getSImm9AddiuspValue"; + let DecoderMethod = "DecodeSimm9SP"; +} + +def uimm3_shift : Operand { + let EncoderMethod = "getUImm3Mod8Encoding"; + let DecoderMethod = "DecodePOOL16BEncodedField"; +} + +def simm3_lsa2 : Operand { + let EncoderMethod = "getSImm3Lsa2Value"; + let DecoderMethod = "DecodeAddiur2Simm7"; +} + +def uimm4_andi : Operand { + let EncoderMethod = "getUImm4AndValue"; + let DecoderMethod = "DecodeANDI16Imm"; +} + +def immSExtAddiur2 : ImmLeaf 0);}]>; + +def immSExtAddius5 : ImmLeaf= -8 && Imm <= 7;}]>; + +def immZExtAndi16 : ImmLeaf= 1 && Imm <= 4) || Imm == 7 || Imm == 8 || + Imm == 15 || Imm == 16 || Imm == 31 || Imm == 32 || Imm == 63 || + Imm == 64 || Imm == 255 || Imm == 32768 || Imm == 65535 );}]>; + +def immZExt2Shift : ImmLeaf= 1 && Imm <= 8;}]>; + +def immLi16 : ImmLeaf= -1 && Imm <= 126;}]>; + +def MicroMaxisMemGPRMM16AsmOperand : AsmOperandClass { + let Name = "MicroMaxisMem"; + let RenderMethod = "addMicroMaxisMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithGRPMM16Base"; +} + +// Define the classes of pointers used by microMAXIS. +// The numbers must match those in MaxisRegisterInfo::MaxisPtrClass. +def ptr_gpr16mm_rc : PointerLikeRegClass<1>; +def ptr_sp_rc : PointerLikeRegClass<2>; +def ptr_gp_rc : PointerLikeRegClass<3>; + +class mem_mm_4_generic : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops ptr_gpr16mm_rc, simm4); + let OperandType = "OPERAND_MEMORY"; + let ParserMatchClass = MicroMaxisMemGPRMM16AsmOperand; +} + +def mem_mm_4 : mem_mm_4_generic { + let EncoderMethod = "getMemEncodingMMImm4"; +} + +def mem_mm_4_lsl1 : mem_mm_4_generic { + let EncoderMethod = "getMemEncodingMMImm4Lsl1"; +} + +def mem_mm_4_lsl2 : mem_mm_4_generic { + let EncoderMethod = "getMemEncodingMMImm4Lsl2"; +} + +def MicroMaxisMemSPAsmOperand : AsmOperandClass { + let Name = "MicroMaxisMemSP"; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithUimmWordAlignedOffsetSP<7>"; +} + +def MicroMaxisMemGPAsmOperand : AsmOperandClass { + let Name = "MicroMaxisMemGP"; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithSimmWordAlignedOffsetGP<9>"; +} + +def mem_mm_sp_imm5_lsl2 : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops ptr_sp_rc:$base, simm5:$offset); + let OperandType = "OPERAND_MEMORY"; + let ParserMatchClass = MicroMaxisMemSPAsmOperand; + let EncoderMethod = "getMemEncodingMMSPImm5Lsl2"; +} + +def mem_mm_gp_simm7_lsl2 : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops ptr_gp_rc:$base, simm7_lsl2:$offset); + let OperandType = "OPERAND_MEMORY"; + let ParserMatchClass = MicroMaxisMemGPAsmOperand; + let EncoderMethod = "getMemEncodingMMGPImm7Lsl2"; +} + +def mem_mm_9 : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops ptr_rc, simm9); + let EncoderMethod = "getMemEncodingMMImm9"; + let ParserMatchClass = MaxisMemSimm9AsmOperand; + let OperandType = "OPERAND_MEMORY"; +} + +def mem_mm_11 : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops GPR32, simm11); + let EncoderMethod = "getMemEncodingMMImm11"; + let ParserMatchClass = MaxisMemSimm11AsmOperand; + let OperandType = "OPERAND_MEMORY"; +} + +def mem_mm_12 : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops ptr_rc, simm12); + let EncoderMethod = "getMemEncodingMMImm12"; + let ParserMatchClass = MaxisMemAsmOperand; + let OperandType = "OPERAND_MEMORY"; +} + +def mem_mm_16 : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops ptr_rc, simm16); + let EncoderMethod = "getMemEncodingMMImm16"; + let ParserMatchClass = MaxisMemSimm16AsmOperand; + let OperandType = "OPERAND_MEMORY"; +} + +def MaxisMemUimm4AsmOperand : AsmOperandClass { + let Name = "MemOffsetUimm4"; + let SuperClasses = [MaxisMemAsmOperand]; + let RenderMethod = "addMemOperands"; + let ParserMethod = "parseMemOperand"; + let PredicateMethod = "isMemWithUimmOffsetSP<6>"; +} + +def mem_mm_4sp : Operand { + let PrintMethod = "printMemOperand"; + let MIOperandInfo = (ops ptr_sp_rc, uimm8); + let EncoderMethod = "getMemEncodingMMImm4sp"; + let ParserMatchClass = MaxisMemUimm4AsmOperand; + let OperandType = "OPERAND_MEMORY"; +} + +def jmptarget_mm : Operand { + let EncoderMethod = "getJumpTargetOpValueMM"; +} + +def calltarget_mm : Operand { + let EncoderMethod = "getJumpTargetOpValueMM"; +} + +def brtarget7_mm : Operand { + let EncoderMethod = "getBranchTarget7OpValueMM"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTarget7MM"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def brtarget10_mm : Operand { + let EncoderMethod = "getBranchTargetOpValueMMPC10"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTarget10MM"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def brtarget_mm : Operand { + let EncoderMethod = "getBranchTargetOpValueMM"; + let OperandType = "OPERAND_PCREL"; + let DecoderMethod = "DecodeBranchTargetMM"; + let ParserMatchClass = MaxisJumpTargetAsmOperand; +} + +def simm23_lsl2 : Operand { + let EncoderMethod = "getSimm23Lsl2Encoding"; + let DecoderMethod = "DecodeSimm23Lsl2"; +} + +class CompactBranchMM : + InstSE<(outs), (ins RO:$rs, opnd:$offset), + !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZC, FrmI> { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 0; + let Defs = [AT]; +} + +let canFoldAsLoad = 1 in +class LoadLeftRightMM : + InstSE<(outs RO:$rt), (ins MemOpnd:$addr, RO:$src), + !strconcat(opstr, "\t$rt, $addr"), + [(set RO:$rt, (OpNode addrimm12:$addr, RO:$src))], + Itin, FrmI> { + let DecoderMethod = "DecodeMemMMImm12"; + string Constraints = "$src = $rt"; +} + +class StoreLeftRightMM: + InstSE<(outs), (ins RO:$rt, MemOpnd:$addr), + !strconcat(opstr, "\t$rt, $addr"), + [(OpNode RO:$rt, addrimm12:$addr)], Itin, FrmI> { + let DecoderMethod = "DecodeMemMMImm12"; +} + +/// A register pair used by movep instruction. +def MovePRegPairAsmOperand : AsmOperandClass { + let Name = "MovePRegPair"; + let ParserMethod = "parseMovePRegPair"; + let PredicateMethod = "isMovePRegPair"; +} + +def movep_regpair : Operand { + let EncoderMethod = "getMovePRegPairOpValue"; + let ParserMatchClass = MovePRegPairAsmOperand; + let PrintMethod = "printRegisterList"; + let DecoderMethod = "DecodeMovePRegPair"; + let MIOperandInfo = (ops ptr_rc, ptr_rc); +} + +class MovePMM16 : +MicroMaxisInst16<(outs movep_regpair:$dst_regs), (ins RO:$rs, RO:$rt), + !strconcat(opstr, "\t$dst_regs, $rs, $rt"), [], + NoItinerary, FrmR> { + let isReMaterializable = 1; +} + +/// A register pair used by load/store pair instructions. +def RegPairAsmOperand : AsmOperandClass { + let Name = "RegPair"; + let ParserMethod = "parseRegisterPair"; + let PredicateMethod = "isRegPair"; +} + +def regpair : Operand { + let EncoderMethod = "getRegisterPairOpValue"; + let ParserMatchClass = RegPairAsmOperand; + let PrintMethod = "printRegisterPair"; + let DecoderMethod = "DecodeRegPairOperand"; + let MIOperandInfo = (ops ptr_rc, ptr_rc); +} + +class StorePairMM + : InstSE<(outs), (ins regpair:$rt, mem_simm12:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], II_SWP, FrmI, opstr> { + let DecoderMethod = "DecodeMemMMImm12"; + let mayStore = 1; +} + +class LoadPairMM + : InstSE<(outs regpair:$rt), (ins mem_simm12:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], II_LWP, FrmI, opstr> { + let DecoderMethod = "DecodeMemMMImm12"; + let mayLoad = 1; +} + +class LLBaseMM : + InstSE<(outs RO:$rt), (ins mem_mm_12:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], II_LL, FrmI> { + let DecoderMethod = "DecodeMemMMImm12"; + let mayLoad = 1; +} + +class LLEBaseMM : + InstSE<(outs RO:$rt), (ins mem_simm9:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], II_LLE, FrmI> { + let DecoderMethod = "DecodeMemMMImm9"; + let mayLoad = 1; +} + +class SCBaseMM : + InstSE<(outs RO:$dst), (ins RO:$rt, mem_mm_12:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], II_SC, FrmI> { + let DecoderMethod = "DecodeMemMMImm12"; + let mayStore = 1; + let Constraints = "$rt = $dst"; +} + +class SCEBaseMM : + InstSE<(outs RO:$dst), (ins RO:$rt, mem_simm9:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], II_SCE, FrmI> { + let DecoderMethod = "DecodeMemMMImm9"; + let mayStore = 1; + let Constraints = "$rt = $dst"; +} + +class LoadMM : + InstSE<(outs RO:$rt), (ins MO:$addr), + !strconcat(opstr, "\t$rt, $addr"), + [(set RO:$rt, (OpNode addrimm12:$addr))], Itin, FrmI, opstr> { + let DecoderMethod = "DecodeMemMMImm12"; + let canFoldAsLoad = 1; + let mayLoad = 1; +} + +class ArithRMM16 : + MicroMaxisInst16<(outs RO:$rd), (ins RO:$rs, RO:$rt), + !strconcat(opstr, "\t$rd, $rs, $rt"), + [(set RO:$rd, (OpNode RO:$rs, RO:$rt))], Itin, FrmR> { + let isCommutable = isComm; +} + +class AndImmMM16 : + MicroMaxisInst16<(outs RO:$rd), (ins RO:$rs, uimm4_andi:$imm), + !strconcat(opstr, "\t$rd, $rs, $imm"), [], Itin, FrmI>; + +class LogicRMM16 : + MicroMaxisInst16<(outs RO:$dst), (ins RO:$rs, RO:$rt), + !strconcat(opstr, "\t$rt, $rs"), + [(set RO:$dst, (OpNode RO:$rs, RO:$rt))], Itin, FrmR> { + let isCommutable = 1; + let Constraints = "$rt = $dst"; +} + +class NotMM16 : + MicroMaxisInst16<(outs RO:$rt), (ins RO:$rs), + !strconcat(opstr, "\t$rt, $rs"), + [(set RO:$rt, (not RO:$rs))], II_NOT, FrmR>; + +class ShiftIMM16 : + MicroMaxisInst16<(outs RO:$rd), (ins RO:$rt, ImmOpnd:$shamt), + !strconcat(opstr, "\t$rd, $rt, $shamt"), [], Itin, FrmR>; + +class LoadMM16 : + MicroMaxisInst16<(outs RO:$rt), (ins MemOpnd:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], Itin, FrmI> { + let DecoderMethod = "DecodeMemMMImm4"; + let canFoldAsLoad = 1; + let mayLoad = 1; +} + +class StoreMM16 : + MicroMaxisInst16<(outs), (ins RTOpnd:$rt, MemOpnd:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], Itin, FrmI> { + let DecoderMethod = "DecodeMemMMImm4"; + let mayStore = 1; +} + +class LoadSPMM16 : + MicroMaxisInst16<(outs RO:$rt), (ins MemOpnd:$offset), + !strconcat(opstr, "\t$rt, $offset"), [], Itin, FrmI> { + let DecoderMethod = "DecodeMemMMSPImm5Lsl2"; + let canFoldAsLoad = 1; + let mayLoad = 1; +} + +class StoreSPMM16 : + MicroMaxisInst16<(outs), (ins RO:$rt, MemOpnd:$offset), + !strconcat(opstr, "\t$rt, $offset"), [], Itin, FrmI> { + let DecoderMethod = "DecodeMemMMSPImm5Lsl2"; + let mayStore = 1; +} + +class LoadGPMM16 : + MicroMaxisInst16<(outs RO:$rt), (ins MemOpnd:$offset), + !strconcat(opstr, "\t$rt, $offset"), [], Itin, FrmI> { + let DecoderMethod = "DecodeMemMMGPImm7Lsl2"; + let canFoldAsLoad = 1; + let mayLoad = 1; +} + +class AddImmUR2 : + MicroMaxisInst16<(outs RO:$rd), (ins RO:$rs, simm3_lsa2:$imm), + !strconcat(opstr, "\t$rd, $rs, $imm"), + [], II_ADDIU, FrmR> { + let isCommutable = 1; +} + +class AddImmUS5 : + MicroMaxisInst16<(outs RO:$dst), (ins RO:$rd, simm4:$imm), + !strconcat(opstr, "\t$rd, $imm"), [], II_ADDIU, FrmR> { + let Constraints = "$rd = $dst"; +} + +class AddImmUR1SP : + MicroMaxisInst16<(outs RO:$rd), (ins uimm6_lsl2:$imm), + !strconcat(opstr, "\t$rd, $imm"), [], II_ADDIU, FrmR>; + +class AddImmUSP : + MicroMaxisInst16<(outs), (ins simm9_addiusp:$imm), + !strconcat(opstr, "\t$imm"), [], II_ADDIU, FrmI>; + +class MoveFromHILOMM : + MicroMaxisInst16<(outs RO:$rd), (ins), !strconcat(opstr, "\t$rd"), + [], II_MFHI_MFLO, FrmR> { + let Uses = [UseReg]; + let hasSideEffects = 0; +} + +class MoveMM16 + : MicroMaxisInst16<(outs RO:$rd), (ins RO:$rs), + !strconcat(opstr, "\t$rd, $rs"), [], II_MOVE, FrmR> { + let isReMaterializable = 1; +} + +class LoadImmMM16 : + MicroMaxisInst16<(outs RO:$rd), (ins Od:$imm), + !strconcat(opstr, "\t$rd, $imm"), [], II_LI, FrmI> { + let isReMaterializable = 1; +} + +// 16-bit Jump and Link (Call) +class JumpLinkRegMM16 : + MicroMaxisInst16<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), + [(MaxisJmpLink RO:$rs)], II_JALR, FrmR>, PredicateControl { + let isCall = 1; + let hasDelaySlot = 1; + let Defs = [RA]; +} + +// 16-bit Jump Reg +class JumpRegMM16 : + MicroMaxisInst16<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), + [], II_JR, FrmR> { + let hasDelaySlot = 1; + let isBranch = 1; + let isIndirectBranch = 1; +} + +// Base class for JRADDIUSP instruction. +class JumpRAddiuStackMM16 : + MicroMaxisInst16<(outs), (ins uimm5_lsl2:$imm), "jraddiusp\t$imm", + [], II_JRADDIUSP, FrmR> { + let isTerminator = 1; + let isBarrier = 1; + let isBranch = 1; + let isIndirectBranch = 1; +} + +// 16-bit Jump and Link (Call) - Short Delay Slot +class JumpLinkRegSMM16 : + MicroMaxisInst16<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), + [], II_JALRS, FrmR> { + let isCall = 1; + let hasDelaySlot = 1; + let Defs = [RA]; +} + +// 16-bit Jump Register Compact - No delay slot +class JumpRegCMM16 : + MicroMaxisInst16<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), + [], II_JRC, FrmR> { + let isTerminator = 1; + let isBarrier = 1; + let isBranch = 1; + let isIndirectBranch = 1; +} + +// Break16 and Sdbbp16 +class BrkSdbbp16MM : + MicroMaxisInst16<(outs), (ins uimm4:$code_), + !strconcat(opstr, "\t$code_"), + [], Itin, FrmOther>; + +class CBranchZeroMM : + MicroMaxisInst16<(outs), (ins RO:$rs, opnd:$offset), + !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZ, FrmI> { + let isBranch = 1; + let isTerminator = 1; + let hasDelaySlot = 1; + let Defs = [AT]; +} + +// MicroMAXIS Jump and Link (Call) - Short Delay Slot +let isCall = 1, hasDelaySlot = 1, Defs = [RA] in { + class JumpLinkMM : + InstSE<(outs), (ins opnd:$target), !strconcat(opstr, "\t$target"), + [], II_JALS, FrmJ, opstr> { + let DecoderMethod = "DecodeJumpTargetMM"; + } + + class JumpLinkRegMM: + InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), + [], II_JALRS, FrmR>; + + class BranchCompareToZeroLinkMM : + InstSE<(outs), (ins RO:$rs, opnd:$offset), + !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZALS, FrmI, opstr>; +} + +class LoadWordIndexedScaledMM : + InstSE<(outs RO:$rd), (ins PtrRC:$base, PtrRC:$index), + !strconcat(opstr, "\t$rd, ${index}(${base})"), [], II_LWXS, FrmFI>; + +class PrefetchIndexed : + InstSE<(outs), (ins PtrRC:$base, PtrRC:$index, uimm5:$hint), + !strconcat(opstr, "\t$hint, ${index}(${base})"), [], II_PREF, FrmOther>; + +class AddImmUPC : + InstSE<(outs RO:$rs), (ins simm23_lsl2:$imm), + !strconcat(opstr, "\t$rs, $imm"), [], II_ADDIU, FrmR>; + +/// A list of registers used by load/store multiple instructions. +def RegListAsmOperand : AsmOperandClass { + let Name = "RegList"; + let ParserMethod = "parseRegisterList"; +} + +def reglist : Operand { + let EncoderMethod = "getRegisterListOpValue"; + let ParserMatchClass = RegListAsmOperand; + let PrintMethod = "printRegisterList"; + let DecoderMethod = "DecodeRegListOperand"; +} + +def RegList16AsmOperand : AsmOperandClass { + let Name = "RegList16"; + let ParserMethod = "parseRegisterList"; + let PredicateMethod = "isRegList16"; + let RenderMethod = "addRegListOperands"; +} + +def reglist16 : Operand { + let EncoderMethod = "getRegisterListOpValue16"; + let DecoderMethod = "DecodeRegListOperand16"; + let PrintMethod = "printRegisterList"; + let ParserMatchClass = RegList16AsmOperand; +} + +class StoreMultMM : + InstSE<(outs), (ins reglist:$rt, mem_mm_12:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], Itin, FrmI, opstr> { + let DecoderMethod = "DecodeMemMMImm12"; + let mayStore = 1; +} + +class LoadMultMM : + InstSE<(outs reglist:$rt), (ins mem_mm_12:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], Itin, FrmI, opstr> { + let DecoderMethod = "DecodeMemMMImm12"; + let mayLoad = 1; +} + +class StoreMultMM16 : + MicroMaxisInst16<(outs), (ins reglist16:$rt, mem_mm_4sp:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], Itin, FrmI> { + let DecoderMethod = "DecodeMemMMReglistImm4Lsl2"; + let mayStore = 1; +} + +class LoadMultMM16 : + MicroMaxisInst16<(outs reglist16:$rt), (ins mem_mm_4sp:$addr), + !strconcat(opstr, "\t$rt, $addr"), [], Itin, FrmI> { + let DecoderMethod = "DecodeMemMMReglistImm4Lsl2"; + let mayLoad = 1; +} + +class UncondBranchMM16 : + MicroMaxisInst16<(outs), (ins brtarget10_mm:$offset), + !strconcat(opstr, "\t$offset"), + [], II_B, FrmI> { + let isBranch = 1; + let isTerminator = 1; + let isBarrier = 1; + let hasDelaySlot = 1; + let Predicates = [RelocPIC, InMicroMaxis]; + let Defs = [AT]; +} + +def ADDU16_MM : ArithRMM16<"addu16", GPRMM16Opnd, 1, II_ADDU, add>, + ARITH_FM_MM16<0>, ISA_MICROMAXIS_NOT_32R6; +def AND16_MM : LogicRMM16<"and16", GPRMM16Opnd, II_AND, and>, + LOGIC_FM_MM16<0x2>, ISA_MICROMAXIS_NOT_32R6; +def ANDI16_MM : AndImmMM16<"andi16", GPRMM16Opnd, II_AND>, ANDI_FM_MM16<0x0b>, + ISA_MICROMAXIS_NOT_32R6; +def NOT16_MM : NotMM16<"not16", GPRMM16Opnd>, LOGIC_FM_MM16<0x0>, + ISA_MICROMAXIS_NOT_32R6; +def OR16_MM : LogicRMM16<"or16", GPRMM16Opnd, II_OR, or>, LOGIC_FM_MM16<0x3>, + ISA_MICROMAXIS_NOT_32R6; +def SLL16_MM : ShiftIMM16<"sll16", uimm3_shift, GPRMM16Opnd, II_SLL>, + SHIFT_FM_MM16<0>, ISA_MICROMAXIS_NOT_32R6; +def SRL16_MM : ShiftIMM16<"srl16", uimm3_shift, GPRMM16Opnd, II_SRL>, + SHIFT_FM_MM16<1>, ISA_MICROMAXIS_NOT_32R6; + +def SUBU16_MM : ArithRMM16<"subu16", GPRMM16Opnd, 0, II_SUBU, sub>, + ARITH_FM_MM16<1>, ISA_MICROMAXIS_NOT_32R6; +def XOR16_MM : LogicRMM16<"xor16", GPRMM16Opnd, II_XOR, xor>, + LOGIC_FM_MM16<0x1>, ISA_MICROMAXIS_NOT_32R6; +def LBU16_MM : LoadMM16<"lbu16", GPRMM16Opnd, zextloadi8, II_LBU, + mem_mm_4>, LOAD_STORE_FM_MM16<0x02>; +def LHU16_MM : LoadMM16<"lhu16", GPRMM16Opnd, zextloadi16, II_LHU, + mem_mm_4_lsl1>, LOAD_STORE_FM_MM16<0x0a>; +def LW16_MM : LoadMM16<"lw16", GPRMM16Opnd, load, II_LW, mem_mm_4_lsl2>, + LOAD_STORE_FM_MM16<0x1a>; +def SB16_MM : StoreMM16<"sb16", GPRMM16OpndZero, GPRMM16Opnd, truncstorei8, + II_SB, mem_mm_4>, LOAD_STORE_FM_MM16<0x22>; +def SH16_MM : StoreMM16<"sh16", GPRMM16OpndZero, GPRMM16Opnd, truncstorei16, + II_SH, mem_mm_4_lsl1>, + LOAD_STORE_FM_MM16<0x2a>; +def SW16_MM : StoreMM16<"sw16", GPRMM16OpndZero, GPRMM16Opnd, store, II_SW, + mem_mm_4_lsl2>, LOAD_STORE_FM_MM16<0x3a>; +def LWGP_MM : LoadGPMM16<"lw", GPRMM16Opnd, II_LW, mem_mm_gp_simm7_lsl2>, + LOAD_GP_FM_MM16<0x19>; +def LWSP_MM : LoadSPMM16<"lw", GPR32Opnd, II_LW, mem_mm_sp_imm5_lsl2>, + LOAD_STORE_SP_FM_MM16<0x12>; +def SWSP_MM : StoreSPMM16<"sw", GPR32Opnd, II_SW, mem_mm_sp_imm5_lsl2>, + LOAD_STORE_SP_FM_MM16<0x32>; +def ADDIUR1SP_MM : AddImmUR1SP<"addiur1sp", GPRMM16Opnd>, ADDIUR1SP_FM_MM16; +def ADDIUR2_MM : AddImmUR2<"addiur2", GPRMM16Opnd>, ADDIUR2_FM_MM16; +def ADDIUS5_MM : AddImmUS5<"addius5", GPR32Opnd>, ADDIUS5_FM_MM16; +def ADDIUSP_MM : AddImmUSP<"addiusp">, ADDIUSP_FM_MM16; +def MFHI16_MM : MoveFromHILOMM<"mfhi", GPR32Opnd, AC0>, MFHILO_FM_MM16<0x10>; +def MFLO16_MM : MoveFromHILOMM<"mflo", GPR32Opnd, AC0>, MFHILO_FM_MM16<0x12>; +def MOVE16_MM : MoveMM16<"move", GPR32Opnd>, MOVE_FM_MM16<0x03>; +def MOVEP_MM : MovePMM16<"movep", GPRMM16OpndMoveP>, MOVEP_FM_MM16, + ISA_MICROMAXIS_NOT_32R6; +def LI16_MM : LoadImmMM16<"li16", li16_imm, GPRMM16Opnd>, LI_FM_MM16, + IsAsCheapAsAMove; +def JALR16_MM : JumpLinkRegMM16<"jalr", GPR32Opnd>, JALR_FM_MM16<0x0e>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; +def JALRS16_MM : JumpLinkRegSMM16<"jalrs16", GPR32Opnd>, JALR_FM_MM16<0x0f>; +def JRC16_MM : JumpRegCMM16<"jrc", GPR32Opnd>, JALR_FM_MM16<0x0d>; +def JRADDIUSP : JumpRAddiuStackMM16, JRADDIUSP_FM_MM16<0x18>; +def JR16_MM : JumpRegMM16<"jr16", GPR32Opnd>, JALR_FM_MM16<0x0c>; +def BEQZ16_MM : CBranchZeroMM<"beqz16", brtarget7_mm, GPRMM16Opnd>, + BEQNEZ_FM_MM16<0x23>; +def BNEZ16_MM : CBranchZeroMM<"bnez16", brtarget7_mm, GPRMM16Opnd>, + BEQNEZ_FM_MM16<0x2b>; +def B16_MM : UncondBranchMM16<"b16">, B16_FM; +def BREAK16_MM : BrkSdbbp16MM<"break16", II_BREAK>, BRKSDBBP16_FM_MM<0x28>, + ISA_MICROMAXIS_NOT_32R6; +def SDBBP16_MM : BrkSdbbp16MM<"sdbbp16", II_SDBBP>, BRKSDBBP16_FM_MM<0x2C>, + ISA_MICROMAXIS_NOT_32R6; + +let DecoderNamespace = "MicroMaxis" in { + /// Load and Store Instructions - multiple + def SWM16_MM : StoreMultMM16<"swm16", II_SWM>, LWM_FM_MM16<0x5>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; + def LWM16_MM : LoadMultMM16<"lwm16", II_LWM>, LWM_FM_MM16<0x4>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; + let AdditionalPredicates = [InMicroMaxis] in { + def CFC2_MM : InstSE<(outs GPR32Opnd:$rt), (ins COP2Opnd:$impl), + "cfc2\t$rt, $impl", [], II_CFC2, FrmFR, "cfc2">, + POOL32A_CFTC2_FM_MM<0b1100110100>; + def CTC2_MM : InstSE<(outs COP2Opnd:$impl), (ins GPR32Opnd:$rt), + "ctc2\t$rt, $impl", [], II_CTC2, FrmFR, "ctc2">, + POOL32A_CFTC2_FM_MM<0b1101110100>; + } +} + +class WaitMM : + InstSE<(outs), (ins uimm10:$code_), !strconcat(opstr, "\t$code_"), [], + II_WAIT, FrmOther, opstr>; + +let DecoderNamespace = "MicroMaxis", Predicates = [InMicroMaxis, NotMaxis32r6, + NotMaxis64r6] in { + /// Compact Branch Instructions + def BEQZC_MM : CompactBranchMM<"beqzc", brtarget_mm, seteq, GPR32Opnd>, + COMPACT_BRANCH_FM_MM<0x7>; + def BNEZC_MM : CompactBranchMM<"bnezc", brtarget_mm, setne, GPR32Opnd>, + COMPACT_BRANCH_FM_MM<0x5>; +} +let DecoderNamespace = "MicroMaxis", Predicates = [InMicroMaxis] in { + /// Arithmetic Instructions (ALU Immediate) + def ADDiu_MM : MMRel, ArithLogicI<"addiu", simm16, GPR32Opnd, II_ADDIU>, + ADDI_FM_MM<0xc>; + def ADDi_MM : MMRel, ArithLogicI<"addi", simm16, GPR32Opnd, II_ADDI>, + ADDI_FM_MM<0x4>; + def SLTi_MM : MMRel, SetCC_I<"slti", setlt, simm16, immSExt16, GPR32Opnd>, + SLTI_FM_MM<0x24>; + def SLTiu_MM : MMRel, SetCC_I<"sltiu", setult, simm16, immSExt16, GPR32Opnd>, + SLTI_FM_MM<0x2c>; + def ANDi_MM : MMRel, ArithLogicI<"andi", uimm16, GPR32Opnd, II_ANDI>, + ADDI_FM_MM<0x34>; + def ORi_MM : MMRel, ArithLogicI<"ori", uimm16, GPR32Opnd, II_ORI, immZExt16, + or>, ADDI_FM_MM<0x14>; + def XORi_MM : MMRel, ArithLogicI<"xori", uimm16, GPR32Opnd, II_XORI, + immZExt16, xor>, ADDI_FM_MM<0x1c>; + def LUi_MM : MMRel, LoadUpper<"lui", GPR32Opnd, uimm16_relaxed>, LUI_FM_MM; + + def LEA_ADDiu_MM : MMRel, EffectiveAddress<"addiu", GPR32Opnd>, + LW_FM_MM<0xc>; + + /// Arithmetic Instructions (3-Operand, R-Type) + def ADDu_MM : MMRel, ArithLogicR<"addu", GPR32Opnd, 1, II_ADDU, add>, + ADD_FM_MM<0, 0x150>; + def SUBu_MM : MMRel, ArithLogicR<"subu", GPR32Opnd, 0, II_SUBU, sub>, + ADD_FM_MM<0, 0x1d0>; + def MUL_MM : MMRel, ArithLogicR<"mul", GPR32Opnd, 1, II_MUL>, + ADD_FM_MM<0, 0x210>; + def ADD_MM : MMRel, ArithLogicR<"add", GPR32Opnd, 1, II_ADD>, + ADD_FM_MM<0, 0x110>; + def SUB_MM : MMRel, ArithLogicR<"sub", GPR32Opnd, 0, II_SUB>, + ADD_FM_MM<0, 0x190>; + def SLT_MM : MMRel, SetCC_R<"slt", setlt, GPR32Opnd>, ADD_FM_MM<0, 0x350>; + def SLTu_MM : MMRel, SetCC_R<"sltu", setult, GPR32Opnd>, + ADD_FM_MM<0, 0x390>; + def AND_MM : MMRel, ArithLogicR<"and", GPR32Opnd, 1, II_AND, and>, + ADD_FM_MM<0, 0x250>; + def OR_MM : MMRel, ArithLogicR<"or", GPR32Opnd, 1, II_OR, or>, + ADD_FM_MM<0, 0x290>; + def XOR_MM : MMRel, ArithLogicR<"xor", GPR32Opnd, 1, II_XOR, xor>, + ADD_FM_MM<0, 0x310>; + def NOR_MM : MMRel, LogicNOR<"nor", GPR32Opnd>, ADD_FM_MM<0, 0x2d0>; + def MULT_MM : MMRel, Mult<"mult", II_MULT, GPR32Opnd, [HI0, LO0]>, + MULT_FM_MM<0x22c>; + def MULTu_MM : MMRel, Mult<"multu", II_MULTU, GPR32Opnd, [HI0, LO0]>, + MULT_FM_MM<0x26c>; + def SDIV_MM : MMRel, Div<"div", II_DIV, GPR32Opnd, [HI0, LO0]>, + MULT_FM_MM<0x2ac>, ISA_MAXIS1_NOT_32R6_64R6; + def UDIV_MM : MMRel, Div<"divu", II_DIVU, GPR32Opnd, [HI0, LO0]>, + MULT_FM_MM<0x2ec>, ISA_MAXIS1_NOT_32R6_64R6; + + /// Arithmetic Instructions with PC and Immediate + def ADDIUPC_MM : AddImmUPC<"addiupc", GPRMM16Opnd>, ADDIUPC_FM_MM; + + /// Shift Instructions + def SLL_MM : MMRel, shift_rotate_imm<"sll", uimm5, GPR32Opnd, II_SLL>, + SRA_FM_MM<0, 0>; + def SRL_MM : MMRel, shift_rotate_imm<"srl", uimm5, GPR32Opnd, II_SRL>, + SRA_FM_MM<0x40, 0>; + def SRA_MM : MMRel, shift_rotate_imm<"sra", uimm5, GPR32Opnd, II_SRA>, + SRA_FM_MM<0x80, 0>; + def SLLV_MM : MMRel, shift_rotate_reg<"sllv", GPR32Opnd, II_SLLV>, + SRLV_FM_MM<0x10, 0>; + def SRLV_MM : MMRel, shift_rotate_reg<"srlv", GPR32Opnd, II_SRLV>, + SRLV_FM_MM<0x50, 0>; + def SRAV_MM : MMRel, shift_rotate_reg<"srav", GPR32Opnd, II_SRAV>, + SRLV_FM_MM<0x90, 0>; + def ROTR_MM : MMRel, shift_rotate_imm<"rotr", uimm5, GPR32Opnd, II_ROTR>, + SRA_FM_MM<0xc0, 0> { + list Pattern = [(set GPR32Opnd:$rd, + (rotr GPR32Opnd:$rt, immZExt5:$shamt))]; + } + def ROTRV_MM : MMRel, shift_rotate_reg<"rotrv", GPR32Opnd, II_ROTRV>, + SRLV_FM_MM<0xd0, 0> { + list Pattern = [(set GPR32Opnd:$rd, + (rotr GPR32Opnd:$rt, GPR32Opnd:$rs))]; + } + + /// Load and Store Instructions - aligned + let DecoderMethod = "DecodeMemMMImm16" in { + def LB_MM : LoadMemory<"lb", GPR32Opnd, mem_mm_16, null_frag, II_LB>, + MMRel, LW_FM_MM<0x7>; + def LBu_MM : LoadMemory<"lbu", GPR32Opnd, mem_mm_16, null_frag, II_LBU>, + MMRel, LW_FM_MM<0x5>; + def LH_MM : LoadMemory<"lh", GPR32Opnd, mem_simm16, sextloadi16, II_LH, + addrDefault>, MMRel, LW_FM_MM<0xf>; + def LHu_MM : LoadMemory<"lhu", GPR32Opnd, mem_simm16, zextloadi16, II_LHU>, + MMRel, LW_FM_MM<0xd>; + def LW_MM : Load<"lw", GPR32Opnd, null_frag, II_LW>, MMRel, LW_FM_MM<0x3f>; + def SB_MM : Store<"sb", GPR32Opnd, null_frag, II_SB>, MMRel, + LW_FM_MM<0x6>; + def SH_MM : Store<"sh", GPR32Opnd, null_frag, II_SH>, MMRel, + LW_FM_MM<0xe>; + def SW_MM : Store<"sw", GPR32Opnd, null_frag, II_SW>, MMRel, + LW_FM_MM<0x3e>; + } + + let DecoderMethod = "DecodeMemMMImm9" in { + def LBE_MM : Load<"lbe", GPR32Opnd, null_frag, II_LBE>, + POOL32C_LHUE_FM_MM<0x18, 0x6, 0x4>; + def LBuE_MM : Load<"lbue", GPR32Opnd, null_frag, II_LBUE>, + POOL32C_LHUE_FM_MM<0x18, 0x6, 0x0>; + def LHE_MM : LoadMemory<"lhe", GPR32Opnd, mem_simm9, null_frag, II_LHE>, + POOL32C_LHUE_FM_MM<0x18, 0x6, 0x5>; + def LHuE_MM : LoadMemory<"lhue", GPR32Opnd, mem_simm9, null_frag, II_LHUE>, + POOL32C_LHUE_FM_MM<0x18, 0x6, 0x1>; + def LWE_MM : LoadMemory<"lwe", GPR32Opnd, mem_simm9, null_frag, II_LWE>, + POOL32C_LHUE_FM_MM<0x18, 0x6, 0x7>; + def SBE_MM : StoreMemory<"sbe", GPR32Opnd, mem_simm9, null_frag, II_SBE>, + POOL32C_LHUE_FM_MM<0x18, 0xa, 0x4>; + def SHE_MM : StoreMemory<"she", GPR32Opnd, mem_simm9, null_frag, II_SHE>, + POOL32C_LHUE_FM_MM<0x18, 0xa, 0x5>; + def SWE_MM : StoreMemory<"swe", GPR32Opnd, mem_simm9, null_frag, II_SWE>, + POOL32C_LHUE_FM_MM<0x18, 0xa, 0x7>; + } + + def LWXS_MM : LoadWordIndexedScaledMM<"lwxs", GPR32Opnd>, LWXS_FM_MM<0x118>; + + /// Load and Store Instructions - unaligned + def LWL_MM : LoadLeftRightMM<"lwl", MaxisLWL, GPR32Opnd, mem_mm_12, II_LWL>, + LWL_FM_MM<0x0>; + def LWR_MM : LoadLeftRightMM<"lwr", MaxisLWR, GPR32Opnd, mem_mm_12, II_LWR>, + LWL_FM_MM<0x1>; + def SWL_MM : StoreLeftRightMM<"swl", MaxisSWL, GPR32Opnd, mem_mm_12, II_SWL>, + LWL_FM_MM<0x8>; + def SWR_MM : StoreLeftRightMM<"swr", MaxisSWR, GPR32Opnd, mem_mm_12, II_SWR>, + LWL_FM_MM<0x9>; + let DecoderMethod = "DecodeMemMMImm9" in { + def LWLE_MM : LoadLeftRightMM<"lwle", MaxisLWL, GPR32Opnd, mem_mm_9, + II_LWLE>, POOL32C_STEVA_LDEVA_FM_MM<0x6, 0x2>; + def LWRE_MM : LoadLeftRightMM<"lwre", MaxisLWR, GPR32Opnd, mem_mm_9, + II_LWRE>, POOL32C_STEVA_LDEVA_FM_MM<0x6, 0x3>; + def SWLE_MM : StoreLeftRightMM<"swle", MaxisSWL, GPR32Opnd, mem_mm_9, + II_SWLE>, + POOL32C_STEVA_LDEVA_FM_MM<0xa, 0x0>; + def SWRE_MM : StoreLeftRightMM<"swre", MaxisSWR, GPR32Opnd, mem_mm_9, + II_SWRE>, + POOL32C_STEVA_LDEVA_FM_MM<0xa, 0x1>, ISA_MAXIS1_NOT_32R6_64R6; + } + + /// Load and Store Instructions - multiple + def SWM32_MM : StoreMultMM<"swm32", II_SWM>, LWM_FM_MM<0xd>; + def LWM32_MM : LoadMultMM<"lwm32", II_LWM>, LWM_FM_MM<0x5>; + + /// Load and Store Pair Instructions + def SWP_MM : StorePairMM<"swp">, LWM_FM_MM<0x9>; + def LWP_MM : LoadPairMM<"lwp">, LWM_FM_MM<0x1>; + + /// Load and Store multiple pseudo Instructions + class LoadWordMultMM : + MaxisAsmPseudoInst<(outs reglist:$rt), (ins mem_mm_12:$addr), + !strconcat(instr_asm, "\t$rt, $addr")> ; + + class StoreWordMultMM : + MaxisAsmPseudoInst<(outs), (ins reglist:$rt, mem_mm_12:$addr), + !strconcat(instr_asm, "\t$rt, $addr")> ; + + + def SWM_MM : StoreWordMultMM<"swm">; + def LWM_MM : LoadWordMultMM<"lwm">; + + /// Move Conditional + def MOVZ_I_MM : MMRel, CMov_I_I_FT<"movz", GPR32Opnd, GPR32Opnd, + NoItinerary>, ADD_FM_MM<0, 0x58>; + def MOVN_I_MM : MMRel, CMov_I_I_FT<"movn", GPR32Opnd, GPR32Opnd, + NoItinerary>, ADD_FM_MM<0, 0x18>; + def MOVT_I_MM : MMRel, CMov_F_I_FT<"movt", GPR32Opnd, II_MOVT>, + CMov_F_I_FM_MM<0x25>; + def MOVF_I_MM : MMRel, CMov_F_I_FT<"movf", GPR32Opnd, II_MOVF>, + CMov_F_I_FM_MM<0x5>; + + /// Move to/from HI/LO + def MTHI_MM : MMRel, MoveToLOHI<"mthi", GPR32Opnd, [HI0]>, + MTLO_FM_MM<0x0b5>; + def MTLO_MM : MMRel, MoveToLOHI<"mtlo", GPR32Opnd, [LO0]>, + MTLO_FM_MM<0x0f5>; + def MFHI_MM : MMRel, MoveFromLOHI<"mfhi", GPR32Opnd, AC0>, + MFLO_FM_MM<0x035>; + def MFLO_MM : MMRel, MoveFromLOHI<"mflo", GPR32Opnd, AC0>, + MFLO_FM_MM<0x075>; + + /// Multiply Add/Sub Instructions + def MADD_MM : MMRel, MArithR<"madd", II_MADD, 1>, MULT_FM_MM<0x32c>; + def MADDU_MM : MMRel, MArithR<"maddu", II_MADDU, 1>, MULT_FM_MM<0x36c>; + def MSUB_MM : MMRel, MArithR<"msub", II_MSUB>, MULT_FM_MM<0x3ac>; + def MSUBU_MM : MMRel, MArithR<"msubu", II_MSUBU>, MULT_FM_MM<0x3ec>; + + /// Count Leading + def CLZ_MM : MMRel, CountLeading0<"clz", GPR32Opnd, II_CLZ>, CLO_FM_MM<0x16c>, + ISA_MAXIS32; + def CLO_MM : MMRel, CountLeading1<"clo", GPR32Opnd, II_CLO>, CLO_FM_MM<0x12c>, + ISA_MAXIS32; + + /// Sign Ext In Register Instructions. + def SEB_MM : MMRel, SignExtInReg<"seb", i8, GPR32Opnd, II_SEB>, + SEB_FM_MM<0x0ac>, ISA_MAXIS32R2; + def SEH_MM : MMRel, SignExtInReg<"seh", i16, GPR32Opnd, II_SEH>, + SEB_FM_MM<0x0ec>, ISA_MAXIS32R2; + + /// Word Swap Bytes Within Halfwords + def WSBH_MM : MMRel, SubwordSwap<"wsbh", GPR32Opnd, II_WSBH>, + SEB_FM_MM<0x1ec>, ISA_MAXIS32R2; + // TODO: Add '0 < pos+size <= 32' constraint check to ext instruction + def EXT_MM : MMRel, ExtBase<"ext", GPR32Opnd, uimm5, uimm5_plus1, immZExt5, + immZExt5Plus1, MaxisExt>, EXT_FM_MM<0x2c>; + def INS_MM : MMRel, InsBase<"ins", GPR32Opnd, uimm5, uimm5_inssize_plus1, + immZExt5, immZExt5Plus1>, + EXT_FM_MM<0x0c>; + + /// Jump Instructions +} +let DecoderNamespace = "MicroMaxis", DecoderMethod = "DecodeJumpTargetMM" in + def J_MM : MMRel, JumpFJ, + J_FM_MM<0x35>, AdditionalRequires<[RelocNotPIC]>, + IsBranch, ISA_MICROMAXIS32_NOT_MAXIS32R6; + +let DecoderNamespace = "MicroMaxis", Predicates = [InMicroMaxis] in { + let DecoderMethod = "DecodeJumpTargetMM" in { + def JAL_MM : MMRel, JumpLink<"jal", calltarget_mm>, J_FM_MM<0x3d>; + def JALX_MM : MMRel, JumpLink<"jalx", calltarget>, J_FM_MM<0x3c>; + } + def JR_MM : MMRel, IndirectBranch<"jr", GPR32Opnd>, JR_FM_MM<0x3c>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; + def JALR_MM : JumpLinkReg<"jalr", GPR32Opnd>, JALR_FM_MM<0x03c>; + + /// Jump Instructions - Short Delay Slot + def JALS_MM : JumpLinkMM<"jals", calltarget_mm>, J_FM_MM<0x1d>; + def JALRS_MM : JumpLinkRegMM<"jalrs", GPR32Opnd>, JALR_FM_MM<0x13c>; + + /// Branch Instructions + def BEQ_MM : MMRel, CBranch<"beq", brtarget_mm, seteq, GPR32Opnd>, + BEQ_FM_MM<0x25>; + def BNE_MM : MMRel, CBranch<"bne", brtarget_mm, setne, GPR32Opnd>, + BEQ_FM_MM<0x2d>; + def BGEZ_MM : MMRel, CBranchZero<"bgez", brtarget_mm, setge, GPR32Opnd>, + BGEZ_FM_MM<0x2>; + def BGTZ_MM : MMRel, CBranchZero<"bgtz", brtarget_mm, setgt, GPR32Opnd>, + BGEZ_FM_MM<0x6>; + def BLEZ_MM : MMRel, CBranchZero<"blez", brtarget_mm, setle, GPR32Opnd>, + BGEZ_FM_MM<0x4>; + def BLTZ_MM : MMRel, CBranchZero<"bltz", brtarget_mm, setlt, GPR32Opnd>, + BGEZ_FM_MM<0x0>; + def BGEZAL_MM : MMRel, BGEZAL_FT<"bgezal", brtarget_mm, GPR32Opnd>, + BGEZAL_FM_MM<0x03>; + def BLTZAL_MM : MMRel, BGEZAL_FT<"bltzal", brtarget_mm, GPR32Opnd>, + BGEZAL_FM_MM<0x01>; + + /// Branch Instructions - Short Delay Slot + def BGEZALS_MM : BranchCompareToZeroLinkMM<"bgezals", brtarget_mm, + GPR32Opnd>, BGEZAL_FM_MM<0x13>; + def BLTZALS_MM : BranchCompareToZeroLinkMM<"bltzals", brtarget_mm, + GPR32Opnd>, BGEZAL_FM_MM<0x11>; +} +def B_MM : UncondBranch, IsBranch, ISA_MICROMAXIS; +let DecoderNamespace = "MicroMaxis", Predicates = [InMicroMaxis] in { + + /// Control Instructions + def SYNC_MM : MMRel, SYNC_FT<"sync">, SYNC_FM_MM; + def SYNCI_MM : MMRel, SYNCI_FT<"synci">, SYNCI_FM_MM; + def BREAK_MM : MMRel, BRK_FT<"break">, BRK_FM_MM; + def SYSCALL_MM : MMRel, SYS_FT<"syscall", uimm10, II_SYSCALL>, SYS_FM_MM; + def WAIT_MM : WaitMM<"wait">, WAIT_FM_MM; + def ERET_MM : MMRel, ER_FT<"eret", II_ERET>, ER_FM_MM<0x3cd>; + def DERET_MM : MMRel, ER_FT<"deret", II_DERET>, ER_FM_MM<0x38d>; + def EI_MM : MMRel, DEI_FT<"ei", GPR32Opnd, II_EI>, EI_FM_MM<0x15d>, + ISA_MAXIS32R2; + def DI_MM : MMRel, DEI_FT<"di", GPR32Opnd, II_DI>, EI_FM_MM<0x11d>, + ISA_MAXIS32R2; + + /// Trap Instructions + def TEQ_MM : MMRel, TEQ_FT<"teq", GPR32Opnd, uimm4, II_TEQ>, TEQ_FM_MM<0x0>; + def TGE_MM : MMRel, TEQ_FT<"tge", GPR32Opnd, uimm4, II_TGE>, TEQ_FM_MM<0x08>; + def TGEU_MM : MMRel, TEQ_FT<"tgeu", GPR32Opnd, uimm4, II_TGEU>, + TEQ_FM_MM<0x10>; + def TLT_MM : MMRel, TEQ_FT<"tlt", GPR32Opnd, uimm4, II_TLT>, TEQ_FM_MM<0x20>; + def TLTU_MM : MMRel, TEQ_FT<"tltu", GPR32Opnd, uimm4, II_TLTU>, + TEQ_FM_MM<0x28>; + def TNE_MM : MMRel, TEQ_FT<"tne", GPR32Opnd, uimm4, II_TNE>, TEQ_FM_MM<0x30>; + + def TEQI_MM : MMRel, TEQI_FT<"teqi", GPR32Opnd, II_TEQI>, TEQI_FM_MM<0x0e>; + def TGEI_MM : MMRel, TEQI_FT<"tgei", GPR32Opnd, II_TGEI>, TEQI_FM_MM<0x09>; + def TGEIU_MM : MMRel, TEQI_FT<"tgeiu", GPR32Opnd, II_TGEIU>, + TEQI_FM_MM<0x0b>; + def TLTI_MM : MMRel, TEQI_FT<"tlti", GPR32Opnd, II_TLTI>, TEQI_FM_MM<0x08>; + def TLTIU_MM : MMRel, TEQI_FT<"tltiu", GPR32Opnd, II_TTLTIU>, + TEQI_FM_MM<0x0a>; + def TNEI_MM : MMRel, TEQI_FT<"tnei", GPR32Opnd, II_TNEI>, TEQI_FM_MM<0x0c>; + + /// Load-linked, Store-conditional + def LL_MM : LLBaseMM<"ll", GPR32Opnd>, LL_FM_MM<0x3>; + def SC_MM : SCBaseMM<"sc", GPR32Opnd>, LL_FM_MM<0xb>; + + def LLE_MM : LLEBaseMM<"lle", GPR32Opnd>, LLE_FM_MM<0x6>; + def SCE_MM : SCEBaseMM<"sce", GPR32Opnd>, LLE_FM_MM<0xA>; + + let DecoderMethod = "DecodeCacheOpMM" in { + def CACHE_MM : MMRel, CacheOp<"cache", mem_mm_12, II_CACHE>, + CACHE_PREF_FM_MM<0x08, 0x6>; + def PREF_MM : MMRel, CacheOp<"pref", mem_mm_12, II_PREF>, + CACHE_PREF_FM_MM<0x18, 0x2>; + } + + let DecoderMethod = "DecodePrefeOpMM" in { + def PREFE_MM : MMRel, CacheOp<"prefe", mem_mm_9, II_PREFE>, + CACHE_PREFE_FM_MM<0x18, 0x2>; + def CACHEE_MM : MMRel, CacheOp<"cachee", mem_mm_9, II_CACHEE>, + CACHE_PREFE_FM_MM<0x18, 0x3>; + } + def SSNOP_MM : MMRel, Barrier<"ssnop", II_SSNOP>, BARRIER_FM_MM<0x1>; + def EHB_MM : MMRel, Barrier<"ehb", II_EHB>, BARRIER_FM_MM<0x3>; + def PAUSE_MM : MMRel, Barrier<"pause", II_PAUSE>, BARRIER_FM_MM<0x5>; + + def TLBP_MM : MMRel, TLB<"tlbp", II_TLBP>, COP0_TLB_FM_MM<0x0d>; + def TLBR_MM : MMRel, TLB<"tlbr", II_TLBR>, COP0_TLB_FM_MM<0x4d>; + def TLBWI_MM : MMRel, TLB<"tlbwi", II_TLBWI>, COP0_TLB_FM_MM<0x8d>; + def TLBWR_MM : MMRel, TLB<"tlbwr", II_TLBWR>, COP0_TLB_FM_MM<0xcd>; + + def SDBBP_MM : MMRel, SYS_FT<"sdbbp", uimm10, II_SDBBP>, SDBBP_FM_MM; + + def PREFX_MM : PrefetchIndexed<"prefx">, POOL32F_PREFX_FM_MM<0x15, 0x1A0>; +} + +def TAILCALL_MM : TailCall, ISA_MAXIS1_NOT_32R6_64R6; + +let DecoderNamespace = "MicroMaxis" in { + def RDHWR_MM : MMRel, R6MMR6Rel, ReadHardware, + RDHWR_FM_MM, ISA_MICROMAXIS32_NOT_MAXIS32R6; + def LWU_MM : MMRel, LoadMM<"lwu", GPR32Opnd, zextloadi32, II_LWU, + mem_simm12>, LL_FM_MM<0xe>, + ISA_MICROMAXIS32_NOT_MAXIS32R6; +} + +//===----------------------------------------------------------------------===// +// MicroMaxis arbitrary patterns that map to one or more instructions +//===----------------------------------------------------------------------===// + +let AdditionalPredicates = [InMicroMaxis] in { + def : MaxisPat<(i32 immLi16:$imm), + (LI16_MM immLi16:$imm)>; + + defm : MaterializeImms; +} + +let Predicates = [InMicroMaxis] in { + def : MaxisPat<(not GPRMM16:$in), + (NOT16_MM GPRMM16:$in)>; + def : MaxisPat<(not GPR32:$in), + (NOR_MM GPR32Opnd:$in, ZERO)>; + + def : MaxisPat<(add GPRMM16:$src, immSExtAddiur2:$imm), + (ADDIUR2_MM GPRMM16:$src, immSExtAddiur2:$imm)>; + def : MaxisPat<(add GPR32:$src, immSExtAddius5:$imm), + (ADDIUS5_MM GPR32:$src, immSExtAddius5:$imm)>; + def : MaxisPat<(add GPR32:$src, immSExt16:$imm), + (ADDiu_MM GPR32:$src, immSExt16:$imm)>; + + def : MaxisPat<(and GPRMM16:$src, immZExtAndi16:$imm), + (ANDI16_MM GPRMM16:$src, immZExtAndi16:$imm)>; + def : MaxisPat<(and GPR32:$src, immZExt16:$imm), + (ANDi_MM GPR32:$src, immZExt16:$imm)>; + + def : MaxisPat<(shl GPRMM16:$src, immZExt2Shift:$imm), + (SLL16_MM GPRMM16:$src, immZExt2Shift:$imm)>; + def : MaxisPat<(shl GPR32:$src, immZExt5:$imm), + (SLL_MM GPR32:$src, immZExt5:$imm)>; + def : MaxisPat<(shl GPR32:$lhs, GPR32:$rhs), + (SLLV_MM GPR32:$lhs, GPR32:$rhs)>; + + def : MaxisPat<(srl GPRMM16:$src, immZExt2Shift:$imm), + (SRL16_MM GPRMM16:$src, immZExt2Shift:$imm)>; + def : MaxisPat<(srl GPR32:$src, immZExt5:$imm), + (SRL_MM GPR32:$src, immZExt5:$imm)>; + def : MaxisPat<(srl GPR32:$lhs, GPR32:$rhs), + (SRLV_MM GPR32:$lhs, GPR32:$rhs)>; + + def : MaxisPat<(sra GPR32:$src, immZExt5:$imm), + (SRA_MM GPR32:$src, immZExt5:$imm)>; + def : MaxisPat<(sra GPR32:$lhs, GPR32:$rhs), + (SRAV_MM GPR32:$lhs, GPR32:$rhs)>; + + def : MaxisPat<(store GPRMM16:$src, addrimm4lsl2:$addr), + (SW16_MM GPRMM16:$src, addrimm4lsl2:$addr)>; + def : MaxisPat<(store GPR32:$src, addr:$addr), + (SW_MM GPR32:$src, addr:$addr)>; + + def : MaxisPat<(load addrimm4lsl2:$addr), + (LW16_MM addrimm4lsl2:$addr)>; + def : MaxisPat<(load addr:$addr), + (LW_MM addr:$addr)>; + def : MaxisPat<(subc GPR32:$lhs, GPR32:$rhs), + (SUBu_MM GPR32:$lhs, GPR32:$rhs)>; +} + +def : MaxisPat<(MaxisTailCall (iPTR tglobaladdr:$dst)), + (TAILCALL_MM tglobaladdr:$dst)>, ISA_MICROMAXIS32_NOT_MAXIS32R6; +def : MaxisPat<(MaxisTailCall (iPTR texternalsym:$dst)), + (TAILCALL_MM texternalsym:$dst)>, ISA_MICROMAXIS32_NOT_MAXIS32R6; + +let AddedComplexity = 40 in { + def : MaxisPat<(i32 (sextloadi16 addrRegImm:$a)), + (LH_MM addrRegImm:$a)>; +} +def : MaxisPat<(atomic_load_16 addr:$a), + (LH_MM addr:$a)>; +def : MaxisPat<(i32 (extloadi16 addr:$src)), + (LHu_MM addr:$src)>; + +defm : BrcondPats; + +defm : SeteqPats; +defm : SetlePats; +defm : SetgtPats; +defm : SetgePats; +defm : SetgeImmPats; + +//===----------------------------------------------------------------------===// +// MicroMaxis instruction aliases +//===----------------------------------------------------------------------===// + +class UncondBranchMMPseudo : + MaxisAsmPseudoInst<(outs), (ins brtarget_mm:$offset), + !strconcat(opstr, "\t$offset")>; + +def B_MM_Pseudo : UncondBranchMMPseudo<"b">, ISA_MICROMAXIS; + +let Predicates = [InMicroMaxis] in { + def SDIV_MM_Pseudo : MultDivPseudo, ISA_MAXIS1_NOT_32R6_64R6; + def UDIV_MM_Pseudo : MultDivPseudo, ISA_MAXIS1_NOT_32R6_64R6; + + def : MaxisInstAlias<"wait", (WAIT_MM 0x0), 1>; + def : MaxisInstAlias<"nop", (SLL_MM ZERO, ZERO, 0), 1>; + def : MaxisInstAlias<"nop", (MOVE16_MM ZERO, ZERO), 1>; + def : MaxisInstAlias<"ei", (EI_MM ZERO), 1>, ISA_MAXIS32R2; + def : MaxisInstAlias<"di", (DI_MM ZERO), 1>, ISA_MAXIS32R2; + def : MaxisInstAlias<"teq $rs, $rt", + (TEQ_MM GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>; + def : MaxisInstAlias<"tge $rs, $rt", + (TGE_MM GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>; + def : MaxisInstAlias<"tgeu $rs, $rt", + (TGEU_MM GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>; + def : MaxisInstAlias<"tlt $rs, $rt", + (TLT_MM GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>; + def : MaxisInstAlias<"tltu $rs, $rt", + (TLTU_MM GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>; + def : MaxisInstAlias<"tne $rs, $rt", + (TNE_MM GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>; + def : MaxisInstAlias< + "sgt $rd, $rs, $rt", + (SLT_MM GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias< + "sgt $rs, $rt", + (SLT_MM GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias< + "sgtu $rd, $rs, $rt", + (SLTu_MM GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias< + "sgtu $rs, $rt", + (SLTu_MM GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias<"sll $rd, $rt, $rs", + (SLLV_MM GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias<"sra $rd, $rt, $rs", + (SRAV_MM GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias<"srl $rd, $rt, $rs", + (SRLV_MM GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; + def : MaxisInstAlias<"sll $rd, $rt", + (SLLV_MM GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; + def : MaxisInstAlias<"sra $rd, $rt", + (SRAV_MM GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; + def : MaxisInstAlias<"srl $rd, $rt", + (SRLV_MM GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; + def : MaxisInstAlias<"sll $rd, $shamt", + (SLL_MM GPR32Opnd:$rd, GPR32Opnd:$rd, uimm5:$shamt), 0>; + def : MaxisInstAlias<"sra $rd, $shamt", + (SRA_MM GPR32Opnd:$rd, GPR32Opnd:$rd, uimm5:$shamt), 0>; + def : MaxisInstAlias<"srl $rd, $shamt", + (SRL_MM GPR32Opnd:$rd, GPR32Opnd:$rd, uimm5:$shamt), 0>; + def : MaxisInstAlias<"rotr $rt, $imm", + (ROTR_MM GPR32Opnd:$rt, GPR32Opnd:$rt, uimm5:$imm), 0>; + def : MaxisInstAlias<"syscall", (SYSCALL_MM 0), 1>; + + defm : OneOrTwoOperandMacroImmediateAlias<"add", ADDi_MM>; + + defm : OneOrTwoOperandMacroImmediateAlias<"addu", ADDiu_MM>; + + defm : OneOrTwoOperandMacroImmediateAlias<"and", ANDi_MM>; + + defm : OneOrTwoOperandMacroImmediateAlias<"or", ORi_MM>; + + defm : OneOrTwoOperandMacroImmediateAlias<"xor", XORi_MM>; + + defm : OneOrTwoOperandMacroImmediateAlias<"slt", SLTi_MM>; + + defm : OneOrTwoOperandMacroImmediateAlias<"sltu", SLTiu_MM>; + + def : MaxisInstAlias<"not $rt, $rs", + (NOR_MM GPR32Opnd:$rt, GPR32Opnd:$rs, ZERO), 0>; + def : MaxisInstAlias<"not $rt", + (NOR_MM GPR32Opnd:$rt, GPR32Opnd:$rt, ZERO), 0>; + def : MaxisInstAlias<"bnez $rs,$offset", + (BNE_MM GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>; + def : MaxisInstAlias<"beqz $rs,$offset", + (BEQ_MM GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>; + def : MaxisInstAlias<"seh $rd", (SEH_MM GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, + ISA_MAXIS32R2_NOT_32R6_64R6; + def : MaxisInstAlias<"seb $rd", (SEB_MM GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, + ISA_MAXIS32R2_NOT_32R6_64R6; +} diff --git a/lib/Target/Maxis/MicroMaxisSizeReduction.cpp b/lib/Target/Maxis/MicroMaxisSizeReduction.cpp new file mode 100644 index 00000000..b82f863c --- /dev/null +++ b/lib/Target/Maxis/MicroMaxisSizeReduction.cpp @@ -0,0 +1,516 @@ +//=== MicroMaxisSizeReduction.cpp - MicroMaxis size reduction pass --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +///\file +/// This pass is used to reduce the size of instructions where applicable. +/// +/// TODO: Implement microMAXIS64 support. +/// TODO: Implement support for reducing into lwp/swp instruction. +//===----------------------------------------------------------------------===// +#include "Maxis.h" +#include "MaxisInstrInfo.h" +#include "MaxisSubtarget.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/Support/Debug.h" + +using namespace llvm; + +#define DEBUG_TYPE "micromaxis-reduce-size" + +STATISTIC(NumReduced, "Number of 32-bit instructions reduced to 16-bit ones"); + +namespace { + +/// Order of operands to transfer +// TODO: Will be extended when additional optimizations are added +enum OperandTransfer { + OT_NA, ///< Not applicable + OT_OperandsAll, ///< Transfer all operands + OT_Operands02, ///< Transfer operands 0 and 2 + OT_Operand2, ///< Transfer just operand 2 + OT_OperandsXOR, ///< Transfer operands for XOR16 +}; + +/// Reduction type +// TODO: Will be extended when additional optimizations are added +enum ReduceType { + RT_OneInstr ///< Reduce one instruction into a smaller instruction +}; + +// Information about immediate field restrictions +struct ImmField { + ImmField() : ImmFieldOperand(-1), Shift(0), LBound(0), HBound(0) {} + ImmField(uint8_t Shift, int16_t LBound, int16_t HBound, + int8_t ImmFieldOperand) + : ImmFieldOperand(ImmFieldOperand), Shift(Shift), LBound(LBound), + HBound(HBound) {} + int8_t ImmFieldOperand; // Immediate operand, -1 if it does not exist + uint8_t Shift; // Shift value + int16_t LBound; // Low bound of the immediate operand + int16_t HBound; // High bound of the immediate operand +}; + +/// Information about operands +// TODO: Will be extended when additional optimizations are added +struct OpInfo { + OpInfo(enum OperandTransfer TransferOperands) + : TransferOperands(TransferOperands) {} + OpInfo() : TransferOperands(OT_NA) {} + + enum OperandTransfer + TransferOperands; ///< Operands to transfer to the new instruction +}; + +// Information about opcodes +struct OpCodes { + OpCodes(unsigned WideOpc, unsigned NarrowOpc) + : WideOpc(WideOpc), NarrowOpc(NarrowOpc) {} + + unsigned WideOpc; ///< Wide opcode + unsigned NarrowOpc; ///< Narrow opcode +}; + +/// ReduceTable - A static table with information on mapping from wide +/// opcodes to narrow +struct ReduceEntry { + + enum ReduceType eRType; ///< Reduction type + bool (*ReduceFunction)( + MachineInstr *MI, + const ReduceEntry &Entry); ///< Pointer to reduce function + struct OpCodes Ops; ///< All relevant OpCodes + struct OpInfo OpInf; ///< Characteristics of operands + struct ImmField Imm; ///< Characteristics of immediate field + + ReduceEntry(enum ReduceType RType, struct OpCodes Op, + bool (*F)(MachineInstr *MI, const ReduceEntry &Entry), + struct OpInfo OpInf, struct ImmField Imm) + : eRType(RType), ReduceFunction(F), Ops(Op), OpInf(OpInf), Imm(Imm) {} + + unsigned NarrowOpc() const { return Ops.NarrowOpc; } + unsigned WideOpc() const { return Ops.WideOpc; } + int16_t LBound() const { return Imm.LBound; } + int16_t HBound() const { return Imm.HBound; } + uint8_t Shift() const { return Imm.Shift; } + int8_t ImmField() const { return Imm.ImmFieldOperand; } + enum OperandTransfer TransferOperands() const { + return OpInf.TransferOperands; + } + enum ReduceType RType() const { return eRType; } + + // operator used by std::equal_range + bool operator<(const unsigned int r) const { return (WideOpc() < r); } + + // operator used by std::equal_range + friend bool operator<(const unsigned int r, const struct ReduceEntry &re) { + return (r < re.WideOpc()); + } +}; + +class MicroMaxisSizeReduce : public MachineFunctionPass { +public: + static char ID; + MicroMaxisSizeReduce(); + + static const MaxisInstrInfo *MaxisII; + const MaxisSubtarget *Subtarget; + + bool runOnMachineFunction(MachineFunction &MF) override; + + llvm::StringRef getPassName() const override { + return "microMAXIS instruction size reduction pass"; + } + +private: + /// Reduces width of instructions in the specified basic block. + bool ReduceMBB(MachineBasicBlock &MBB); + + /// Attempts to reduce MI, returns true on success. + bool ReduceMI(const MachineBasicBlock::instr_iterator &MII); + + // Attempts to reduce LW/SW instruction into LWSP/SWSP, + // returns true on success. + static bool ReduceXWtoXWSP(MachineInstr *MI, const ReduceEntry &Entry); + + // Attempts to reduce LBU/LHU instruction into LBU16/LHU16, + // returns true on success. + static bool ReduceLXUtoLXU16(MachineInstr *MI, const ReduceEntry &Entry); + + // Attempts to reduce SB/SH instruction into SB16/SH16, + // returns true on success. + static bool ReduceSXtoSX16(MachineInstr *MI, const ReduceEntry &Entry); + + // Attempts to reduce arithmetic instructions, returns true on success. + static bool ReduceArithmeticInstructions(MachineInstr *MI, + const ReduceEntry &Entry); + + // Attempts to reduce ADDIU into ADDIUSP instruction, + // returns true on success. + static bool ReduceADDIUToADDIUSP(MachineInstr *MI, const ReduceEntry &Entry); + + // Attempts to reduce ADDIU into ADDIUR1SP instruction, + // returns true on success. + static bool ReduceADDIUToADDIUR1SP(MachineInstr *MI, + const ReduceEntry &Entry); + + // Attempts to reduce XOR into XOR16 instruction, + // returns true on success. + static bool ReduceXORtoXOR16(MachineInstr *MI, const ReduceEntry &Entry); + + // Changes opcode of an instruction. + static bool ReplaceInstruction(MachineInstr *MI, const ReduceEntry &Entry); + + // Table with transformation rules for each instruction. + static llvm::SmallVector ReduceTable; +}; + +char MicroMaxisSizeReduce::ID = 0; +const MaxisInstrInfo *MicroMaxisSizeReduce::MaxisII; + +// This table must be sorted by WideOpc as a main criterion and +// ReduceType as a sub-criterion (when wide opcodes are the same). +llvm::SmallVector MicroMaxisSizeReduce::ReduceTable = { + + // ReduceType, OpCodes, ReduceFunction, + // OpInfo(TransferOperands), + // ImmField(Shift, LBound, HBound, ImmFieldPosition) + {RT_OneInstr, OpCodes(Maxis::ADDiu, Maxis::ADDIUR1SP_MM), + ReduceADDIUToADDIUR1SP, OpInfo(OT_Operands02), ImmField(2, 0, 64, 2)}, + {RT_OneInstr, OpCodes(Maxis::ADDiu, Maxis::ADDIUSP_MM), ReduceADDIUToADDIUSP, + OpInfo(OT_Operand2), ImmField(0, 0, 0, 2)}, + {RT_OneInstr, OpCodes(Maxis::ADDiu_MM, Maxis::ADDIUR1SP_MM), + ReduceADDIUToADDIUR1SP, OpInfo(OT_Operands02), ImmField(2, 0, 64, 2)}, + {RT_OneInstr, OpCodes(Maxis::ADDiu_MM, Maxis::ADDIUSP_MM), + ReduceADDIUToADDIUSP, OpInfo(OT_Operand2), ImmField(0, 0, 0, 2)}, + {RT_OneInstr, OpCodes(Maxis::ADDu, Maxis::ADDU16_MM), + ReduceArithmeticInstructions, OpInfo(OT_OperandsAll), + ImmField(0, 0, 0, -1)}, + {RT_OneInstr, OpCodes(Maxis::ADDu_MM, Maxis::ADDU16_MM), + ReduceArithmeticInstructions, OpInfo(OT_OperandsAll), + ImmField(0, 0, 0, -1)}, + {RT_OneInstr, OpCodes(Maxis::LBu, Maxis::LBU16_MM), ReduceLXUtoLXU16, + OpInfo(OT_OperandsAll), ImmField(0, -1, 15, 2)}, + {RT_OneInstr, OpCodes(Maxis::LBu_MM, Maxis::LBU16_MM), ReduceLXUtoLXU16, + OpInfo(OT_OperandsAll), ImmField(0, -1, 15, 2)}, + {RT_OneInstr, OpCodes(Maxis::LEA_ADDiu, Maxis::ADDIUR1SP_MM), + ReduceADDIUToADDIUR1SP, OpInfo(OT_Operands02), ImmField(2, 0, 64, 2)}, + {RT_OneInstr, OpCodes(Maxis::LHu, Maxis::LHU16_MM), ReduceLXUtoLXU16, + OpInfo(OT_OperandsAll), ImmField(1, 0, 16, 2)}, + {RT_OneInstr, OpCodes(Maxis::LHu_MM, Maxis::LHU16_MM), ReduceLXUtoLXU16, + OpInfo(OT_OperandsAll), ImmField(1, 0, 16, 2)}, + {RT_OneInstr, OpCodes(Maxis::LW, Maxis::LWSP_MM), ReduceXWtoXWSP, + OpInfo(OT_OperandsAll), ImmField(2, 0, 32, 2)}, + {RT_OneInstr, OpCodes(Maxis::LW_MM, Maxis::LWSP_MM), ReduceXWtoXWSP, + OpInfo(OT_OperandsAll), ImmField(2, 0, 32, 2)}, + {RT_OneInstr, OpCodes(Maxis::SB, Maxis::SB16_MM), ReduceSXtoSX16, + OpInfo(OT_OperandsAll), ImmField(0, 0, 16, 2)}, + {RT_OneInstr, OpCodes(Maxis::SB_MM, Maxis::SB16_MM), ReduceSXtoSX16, + OpInfo(OT_OperandsAll), ImmField(0, 0, 16, 2)}, + {RT_OneInstr, OpCodes(Maxis::SH, Maxis::SH16_MM), ReduceSXtoSX16, + OpInfo(OT_OperandsAll), ImmField(1, 0, 16, 2)}, + {RT_OneInstr, OpCodes(Maxis::SH_MM, Maxis::SH16_MM), ReduceSXtoSX16, + OpInfo(OT_OperandsAll), ImmField(1, 0, 16, 2)}, + {RT_OneInstr, OpCodes(Maxis::SUBu, Maxis::SUBU16_MM), + ReduceArithmeticInstructions, OpInfo(OT_OperandsAll), + ImmField(0, 0, 0, -1)}, + {RT_OneInstr, OpCodes(Maxis::SUBu_MM, Maxis::SUBU16_MM), + ReduceArithmeticInstructions, OpInfo(OT_OperandsAll), + ImmField(0, 0, 0, -1)}, + {RT_OneInstr, OpCodes(Maxis::SW, Maxis::SWSP_MM), ReduceXWtoXWSP, + OpInfo(OT_OperandsAll), ImmField(2, 0, 32, 2)}, + {RT_OneInstr, OpCodes(Maxis::SW_MM, Maxis::SWSP_MM), ReduceXWtoXWSP, + OpInfo(OT_OperandsAll), ImmField(2, 0, 32, 2)}, + {RT_OneInstr, OpCodes(Maxis::XOR, Maxis::XOR16_MM), ReduceXORtoXOR16, + OpInfo(OT_OperandsXOR), ImmField(0, 0, 0, -1)}, + {RT_OneInstr, OpCodes(Maxis::XOR_MM, Maxis::XOR16_MM), ReduceXORtoXOR16, + OpInfo(OT_OperandsXOR), ImmField(0, 0, 0, -1)}}; +} // namespace + +// Returns true if the machine operand MO is register SP. +static bool IsSP(const MachineOperand &MO) { + if (MO.isReg() && ((MO.getReg() == Maxis::SP))) + return true; + return false; +} + +// Returns true if the machine operand MO is register $16, $17, or $2-$7. +static bool isMMThreeBitGPRegister(const MachineOperand &MO) { + if (MO.isReg() && Maxis::GPRMM16RegClass.contains(MO.getReg())) + return true; + return false; +} + +// Returns true if the machine operand MO is register $0, $17, or $2-$7. +static bool isMMSourceRegister(const MachineOperand &MO) { + if (MO.isReg() && Maxis::GPRMM16ZeroRegClass.contains(MO.getReg())) + return true; + return false; +} + +// Returns true if the operand Op is an immediate value +// and writes the immediate value into variable Imm. +static bool GetImm(MachineInstr *MI, unsigned Op, int64_t &Imm) { + + if (!MI->getOperand(Op).isImm()) + return false; + Imm = MI->getOperand(Op).getImm(); + return true; +} + +// Returns true if the value is a valid immediate for ADDIUSP. +static bool AddiuspImmValue(int64_t Value) { + int64_t Value2 = Value >> 2; + if (((Value & (int64_t)maskTrailingZeros(2)) == Value) && + ((Value2 >= 2 && Value2 <= 257) || (Value2 >= -258 && Value2 <= -3))) + return true; + return false; +} + +// Returns true if the variable Value has the number of least-significant zero +// bits equal to Shift and if the shifted value is between the bounds. +static bool InRange(int64_t Value, unsigned short Shift, int LBound, + int HBound) { + int64_t Value2 = Value >> Shift; + if (((Value & (int64_t)maskTrailingZeros(Shift)) == Value) && + (Value2 >= LBound) && (Value2 < HBound)) + return true; + return false; +} + +// Returns true if immediate operand is in range. +static bool ImmInRange(MachineInstr *MI, const ReduceEntry &Entry) { + + int64_t offset; + + if (!GetImm(MI, Entry.ImmField(), offset)) + return false; + + if (!InRange(offset, Entry.Shift(), Entry.LBound(), Entry.HBound())) + return false; + + return true; +} + +MicroMaxisSizeReduce::MicroMaxisSizeReduce() : MachineFunctionPass(ID) {} + +bool MicroMaxisSizeReduce::ReduceMI( + const MachineBasicBlock::instr_iterator &MII) { + + MachineInstr *MI = &*MII; + unsigned Opcode = MI->getOpcode(); + + // Search the table. + llvm::SmallVector::const_iterator Start = + std::begin(ReduceTable); + llvm::SmallVector::const_iterator End = + std::end(ReduceTable); + + std::pair::const_iterator, + llvm::SmallVector::const_iterator> + Range = std::equal_range(Start, End, Opcode); + + if (Range.first == Range.second) + return false; + + for (llvm::SmallVector::const_iterator Entry = Range.first; + Entry != Range.second; ++Entry) + if (((*Entry).ReduceFunction)(&(*MII), *Entry)) + return true; + + return false; +} + +bool MicroMaxisSizeReduce::ReduceXWtoXWSP(MachineInstr *MI, + const ReduceEntry &Entry) { + + if (!ImmInRange(MI, Entry)) + return false; + + if (!IsSP(MI->getOperand(1))) + return false; + + return ReplaceInstruction(MI, Entry); +} + +bool MicroMaxisSizeReduce::ReduceArithmeticInstructions( + MachineInstr *MI, const ReduceEntry &Entry) { + + if (!isMMThreeBitGPRegister(MI->getOperand(0)) || + !isMMThreeBitGPRegister(MI->getOperand(1)) || + !isMMThreeBitGPRegister(MI->getOperand(2))) + return false; + + return ReplaceInstruction(MI, Entry); +} + +bool MicroMaxisSizeReduce::ReduceADDIUToADDIUR1SP(MachineInstr *MI, + const ReduceEntry &Entry) { + + if (!ImmInRange(MI, Entry)) + return false; + + if (!isMMThreeBitGPRegister(MI->getOperand(0)) || !IsSP(MI->getOperand(1))) + return false; + + return ReplaceInstruction(MI, Entry); +} + +bool MicroMaxisSizeReduce::ReduceADDIUToADDIUSP(MachineInstr *MI, + const ReduceEntry &Entry) { + + int64_t ImmValue; + if (!GetImm(MI, Entry.ImmField(), ImmValue)) + return false; + + if (!AddiuspImmValue(ImmValue)) + return false; + + if (!IsSP(MI->getOperand(0)) || !IsSP(MI->getOperand(1))) + return false; + + return ReplaceInstruction(MI, Entry); +} + +bool MicroMaxisSizeReduce::ReduceLXUtoLXU16(MachineInstr *MI, + const ReduceEntry &Entry) { + + if (!ImmInRange(MI, Entry)) + return false; + + if (!isMMThreeBitGPRegister(MI->getOperand(0)) || + !isMMThreeBitGPRegister(MI->getOperand(1))) + return false; + + return ReplaceInstruction(MI, Entry); +} + +bool MicroMaxisSizeReduce::ReduceSXtoSX16(MachineInstr *MI, + const ReduceEntry &Entry) { + + if (!ImmInRange(MI, Entry)) + return false; + + if (!isMMSourceRegister(MI->getOperand(0)) || + !isMMThreeBitGPRegister(MI->getOperand(1))) + return false; + + return ReplaceInstruction(MI, Entry); +} + +bool MicroMaxisSizeReduce::ReduceXORtoXOR16(MachineInstr *MI, + const ReduceEntry &Entry) { + if (!isMMThreeBitGPRegister(MI->getOperand(0)) || + !isMMThreeBitGPRegister(MI->getOperand(1)) || + !isMMThreeBitGPRegister(MI->getOperand(2))) + return false; + + if (!(MI->getOperand(0).getReg() == MI->getOperand(2).getReg()) && + !(MI->getOperand(0).getReg() == MI->getOperand(1).getReg())) + return false; + + return ReplaceInstruction(MI, Entry); +} + +bool MicroMaxisSizeReduce::ReduceMBB(MachineBasicBlock &MBB) { + bool Modified = false; + MachineBasicBlock::instr_iterator MII = MBB.instr_begin(), + E = MBB.instr_end(); + MachineBasicBlock::instr_iterator NextMII; + + // Iterate through the instructions in the basic block + for (; MII != E; MII = NextMII) { + NextMII = std::next(MII); + MachineInstr *MI = &*MII; + + // Don't reduce bundled instructions or pseudo operations + if (MI->isBundle() || MI->isTransient()) + continue; + + // Try to reduce 32-bit instruction into 16-bit instruction + Modified |= ReduceMI(MII); + } + + return Modified; +} + +bool MicroMaxisSizeReduce::ReplaceInstruction(MachineInstr *MI, + const ReduceEntry &Entry) { + + enum OperandTransfer OpTransfer = Entry.TransferOperands(); + + DEBUG(dbgs() << "Converting 32-bit: " << *MI); + ++NumReduced; + + if (OpTransfer == OT_OperandsAll) { + MI->setDesc(MaxisII->get(Entry.NarrowOpc())); + DEBUG(dbgs() << " to 16-bit: " << *MI); + return true; + } else { + MachineBasicBlock &MBB = *MI->getParent(); + const MCInstrDesc &NewMCID = MaxisII->get(Entry.NarrowOpc()); + DebugLoc dl = MI->getDebugLoc(); + MachineInstrBuilder MIB = BuildMI(MBB, MI, dl, NewMCID); + switch (OpTransfer) { + case OT_Operand2: + MIB.add(MI->getOperand(2)); + break; + case OT_Operands02: { + MIB.add(MI->getOperand(0)); + MIB.add(MI->getOperand(2)); + break; + } + case OT_OperandsXOR: { + if (MI->getOperand(0).getReg() == MI->getOperand(2).getReg()) { + MIB.add(MI->getOperand(0)); + MIB.add(MI->getOperand(1)); + MIB.add(MI->getOperand(2)); + } else { + MIB.add(MI->getOperand(0)); + MIB.add(MI->getOperand(2)); + MIB.add(MI->getOperand(1)); + } + break; + } + default: + llvm_unreachable("Unknown operand transfer!"); + } + + // Transfer MI flags. + MIB.setMIFlags(MI->getFlags()); + + DEBUG(dbgs() << " to 16-bit: " << *MIB); + MBB.erase_instr(MI); + return true; + } + return false; +} + +bool MicroMaxisSizeReduce::runOnMachineFunction(MachineFunction &MF) { + + Subtarget = &static_cast(MF.getSubtarget()); + + // TODO: Add support for the subtarget microMAXIS32R6. + if (!Subtarget->inMicroMaxisMode() || !Subtarget->hasMaxis32r2() || + Subtarget->hasMaxis32r6()) + return false; + + MaxisII = static_cast(Subtarget->getInstrInfo()); + + bool Modified = false; + MachineFunction::iterator I = MF.begin(), E = MF.end(); + + for (; I != E; ++I) + Modified |= ReduceMBB(*I); + return Modified; +} + +/// Returns an instance of the MicroMaxis size reduction pass. +FunctionPass *llvm::createMicroMaxisSizeReductionPass() { + return new MicroMaxisSizeReduce(); +} diff --git a/lib/Target/Maxis/Relocation.txt b/lib/Target/Maxis/Relocation.txt new file mode 100644 index 00000000..9bc9f828 --- /dev/null +++ b/lib/Target/Maxis/Relocation.txt @@ -0,0 +1,92 @@ +MAXIS Relocation Principles + +In LLVM, there are several elements of the llvm::ISD::NodeType enum +that deal with addresses and/or relocations. These are defined in +include/llvm/Target/TargetSelectionDAG.td, namely: + GlobalAddress, GlobalTLSAddress, JumpTable, ConstantPool, + ExternalSymbol, BlockAddress +The MAXIS backend uses several principles to handle these. + +1. Code for lowering addresses references to machine dependent code is +factored into common code for generating different address forms and +is called by the relocation model specific lowering function, using +templated functions. For example: + + // lib/Target/Maxis/MaxisISelLowering.cpp + SDValue MaxisTargetLowering:: + lowerJumpTable(SDValue Op, SelectionDAG &DAG) const + +calls + + template // lib/Target/Maxis/MaxisISelLowering.h + SDValue getAddrLocal(NodeTy *N, const SDLoc &DL, EVT Ty, + SelectionDAG &DAG, bool IsN32OrN64) const + +which calls the overloaded function: + + // lib/Target/Maxis/MaxisISelLowering.h + SDValue getTargetNode(JumpTableSDNode *N, EVT Ty, SelectionDAG &DAG, + unsigned Flag) const; + +2. Generic address nodes are lowered to some combination of target +independent and machine specific SDNodes (for example: +MaxisISD::{Highest, Higher, Hi, Lo}) depending upon relocation model, +ABI, and compilation options. + +The choice of specific instructions that are to be used is delegated +to ISel which in turn relies on TableGen patterns to choose subtarget +specific instructions. For example, in getAddrLocal, the pseudo-code +generated is: + + (add (load (wrapper $gp, %got(sym)), %lo(sym)) + +where "%lo" represents an instance of an SDNode with opcode +"MaxisISD::Lo", "wrapper" indicates one with opcode "MaxisISD::Wrapper", +and "%got" the global table pointer "getGlobalReg(...)". The "add" is +"ISD::ADD", not a target dependent one. + +3. A TableGen multiclass pattern "MaxisHiLoRelocs" is used to define a +template pattern parameterized over the load upper immediate +instruction, add operation, the zero register, and register class. +Here the instantiation of MaxisHiLoRelocs in MaxisInstrInfo.td is used +to MAXIS32 to compute addresses for the static relocation model. + + // lib/Target/Maxis/MaxisInstrInfo.td + multiclass MaxisHiLoRelocs { + def : MaxisPat<(MaxisHi tglobaladdr:$in), (Lui tglobaladdr:$in)>; + ... + def : MaxisPat<(MaxisLo tglobaladdr:$in), (Addiu ZeroReg, tglobaladdr:$in)>; + ... + def : MaxisPat<(add GPROpnd:$hi, (MaxisLo tglobaladdr:$lo)), + (Addiu GPROpnd:$hi, tglobaladdr:$lo)>; + ... + } + defm : MaxisHiLoRelocs; + + // lib/Target/Maxis/Maxis64InstrInfo.td + defm : MaxisHiLoRelocs, SYM_32; + +The instantiation in Maxis64InstrInfo.td is used for MAXIS64 in ILP32 +mode, as guarded by the predicate "SYM_32" and also for a submode of +LP64 where symbols are assumed to be 32 bits wide. + +More details on how multiclasses in TableGen work can be found in the +section "Multiclass definitions and instances" in the document +"TableGen Language Introduction" + +4. Instruction definitions are multiply defined to cover the different +register classes. In some cases, such as LW/LW64, this also accounts +for the difference in the results of instruction execution. On MAXIS32, +"lw" loads a 32 bit value from memory. On MAXIS64, "lw" loads a 32 bit +value from memory and sign extends the value to 64 bits. + + // lib/Target/Maxis/MaxisInstrInfo.td + def LUi : MMRel, LoadUpper<"lui", GPR32Opnd, uimm16_relaxed>, LUI_FM; + // lib/Target/Maxis/Maxis64InstrInfo.td + def LUi64 : LoadUpper<"lui", GPR64Opnd, uimm16_64_relaxed>, LUI_FM; + +defines two names "LUi" and "LUi64" with two different register +classes, but with the same encoding---"LUI_FM". These instructions load a +16-bit immediate into bits 31-16 and clear the lower 15 bits. On MAXIS64, +the result is sign-extended to 64 bits. diff --git a/lib/Target/Maxis/TargetInfo/CMakeLists.txt b/lib/Target/Maxis/TargetInfo/CMakeLists.txt new file mode 100644 index 00000000..9c40be7f --- /dev/null +++ b/lib/Target/Maxis/TargetInfo/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_library(LLVMMaxisInfo + MaxisTargetInfo.cpp + ) diff --git a/lib/Target/Maxis/TargetInfo/LLVMBuild.txt b/lib/Target/Maxis/TargetInfo/LLVMBuild.txt new file mode 100644 index 00000000..f30a874a --- /dev/null +++ b/lib/Target/Maxis/TargetInfo/LLVMBuild.txt @@ -0,0 +1,23 @@ +;===- ./lib/Target/Maxis/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 = MaxisInfo +parent = Maxis +required_libraries = Support +add_to_library_groups = Maxis diff --git a/lib/Target/Maxis/TargetInfo/MaxisTargetInfo.cpp b/lib/Target/Maxis/TargetInfo/MaxisTargetInfo.cpp new file mode 100644 index 00000000..73506413 --- /dev/null +++ b/lib/Target/Maxis/TargetInfo/MaxisTargetInfo.cpp @@ -0,0 +1,48 @@ +//===-- MaxisTargetInfo.cpp - Maxis Target Implementation -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Maxis.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/TargetRegistry.h" +using namespace llvm; + +Target &llvm::getTheMaxisTarget() { + static Target TheMaxisTarget; + return TheMaxisTarget; +} +Target &llvm::getTheMaxiselTarget() { + static Target TheMaxiselTarget; + return TheMaxiselTarget; +} +Target &llvm::getTheMaxis64Target() { + static Target TheMaxis64Target; + return TheMaxis64Target; +} +Target &llvm::getTheMaxis64elTarget() { + static Target TheMaxis64elTarget; + return TheMaxis64elTarget; +} + +extern "C" void LLVMInitializeMaxisTargetInfo() { + RegisterTarget + X(getTheMaxisTarget(), "maxis", "Maxis", "Maxis"); + + RegisterTarget + Y(getTheMaxiselTarget(), "maxisel", "Maxisel", "Maxis"); + + RegisterTarget + A(getTheMaxis64Target(), "maxis64", "Maxis64 [experimental]", "Maxis"); + + RegisterTarget + B(getTheMaxis64elTarget(), "maxis64el", "Maxis64el [experimental]", "Maxis"); +} diff --git a/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/lib/Transforms/Instrumentation/AddressSanitizer.cpp index 8e39f24d..07a18e82 100644 --- a/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -102,6 +102,8 @@ static const uint64_t kSmallX86_64ShadowOffsetAlignMask = ~0xFFFULL; static const uint64_t kLinuxKasan_ShadowOffset64 = 0xdffffc0000000000; static const uint64_t kPPC64_ShadowOffset64 = 1ULL << 44; static const uint64_t kSystemZ_ShadowOffset64 = 1ULL << 52; +static const uint64_t kMAXIS32_ShadowOffset32 = 0x0aaa0000; +static const uint64_t kMAXIS64_ShadowOffset64 = 1ULL << 37; static const uint64_t kMIPS32_ShadowOffset32 = 0x0aaa0000; static const uint64_t kMIPS64_ShadowOffset64 = 1ULL << 37; static const uint64_t kAArch64_ShadowOffset64 = 1ULL << 36; @@ -485,6 +487,10 @@ static ShadowMapping getShadowMapping(Triple &TargetTriple, int LongSize, bool IsSystemZ = TargetTriple.getArch() == Triple::systemz; bool IsX86 = TargetTriple.getArch() == Triple::x86; bool IsX86_64 = TargetTriple.getArch() == Triple::x86_64; + bool IsMAXIS32 = TargetTriple.getArch() == Triple::maxis || + TargetTriple.getArch() == Triple::maxisel; + bool IsMAXIS64 = TargetTriple.getArch() == Triple::maxis64 || + TargetTriple.getArch() == Triple::maxis64el; bool IsMIPS32 = TargetTriple.getArch() == Triple::mips || TargetTriple.getArch() == Triple::mipsel; bool IsMIPS64 = TargetTriple.getArch() == Triple::mips64 || @@ -504,6 +510,8 @@ static ShadowMapping getShadowMapping(Triple &TargetTriple, int LongSize, if (LongSize == 32) { if (IsAndroid) Mapping.Offset = kDynamicShadowSentinel; + else if (IsMAXIS32) + Mapping.Offset = kMAXIS32_ShadowOffset32; else if (IsMIPS32) Mapping.Offset = kMIPS32_ShadowOffset32; else if (IsFreeBSD) @@ -538,7 +546,9 @@ static ShadowMapping getShadowMapping(Triple &TargetTriple, int LongSize, (kSmallX86_64ShadowOffsetAlignMask << Mapping.Scale)); } else if (IsWindows && IsX86_64) { Mapping.Offset = kWindowsShadowOffset64; - } else if (IsMIPS64) + } else if (IsMAXIS64) + Mapping.Offset = kMAXIS64_ShadowOffset64; + else if (IsMIPS64) Mapping.Offset = kMIPS64_ShadowOffset64; else if (IsIOS) // If we're targeting iOS and x86, the binary is built for iOS simulator. diff --git a/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp b/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp index 09bcbb28..fa484d20 100644 --- a/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp +++ b/lib/Transforms/Instrumentation/DataFlowSanitizer.cpp @@ -463,6 +463,8 @@ FunctionType *DataFlowSanitizer::getCustomFunctionType(FunctionType *T) { bool DataFlowSanitizer::doInitialization(Module &M) { Triple TargetTriple(M.getTargetTriple()); bool IsX86_64 = TargetTriple.getArch() == Triple::x86_64; + bool IsMAXIS64 = TargetTriple.getArch() == Triple::maxis64 || + TargetTriple.getArch() == Triple::maxis64el; bool IsMIPS64 = TargetTriple.getArch() == Triple::mips64 || TargetTriple.getArch() == Triple::mips64el; bool IsAArch64 = TargetTriple.getArch() == Triple::aarch64 || @@ -479,6 +481,8 @@ bool DataFlowSanitizer::doInitialization(Module &M) { ShadowPtrMul = ConstantInt::getSigned(IntptrTy, ShadowWidth / 8); if (IsX86_64) ShadowPtrMask = ConstantInt::getSigned(IntptrTy, ~0x700000000000LL); + else if (IsMAXIS64) + ShadowPtrMask = ConstantInt::getSigned(IntptrTy, ~0xF000000000LL); else if (IsMIPS64) ShadowPtrMask = ConstantInt::getSigned(IntptrTy, ~0xF000000000LL); // AArch64 supports multiple VMAs and the shadow mask is set at runtime. diff --git a/lib/Transforms/Instrumentation/EfficiencySanitizer.cpp b/lib/Transforms/Instrumentation/EfficiencySanitizer.cpp index 6864d295..ac17f5aa 100644 --- a/lib/Transforms/Instrumentation/EfficiencySanitizer.cpp +++ b/lib/Transforms/Instrumentation/EfficiencySanitizer.cpp @@ -537,7 +537,8 @@ void EfficiencySanitizer::createDestructor(Module &M, Constant *ToolInfoArg) { bool EfficiencySanitizer::initOnModule(Module &M) { Triple TargetTriple(M.getTargetTriple()); - if (TargetTriple.getArch() == Triple::mips64 || TargetTriple.getArch() == Triple::mips64el) + if (TargetTriple.getArch() == Triple::maxis64 || TargetTriple.getArch() == Triple::maxis64el + || TargetTriple.getArch() == Triple::mips64 || TargetTriple.getArch() == Triple::mips64el) ShadowParams = ShadowParams40; else ShadowParams = ShadowParams47; diff --git a/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/lib/Transforms/Instrumentation/MemorySanitizer.cpp index b3c39b5b..0ab610d8 100644 --- a/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -280,6 +280,14 @@ static const MemoryMapParams Linux_X86_64_MemoryMapParams = { #endif }; +// maxis64 Linux +static const MemoryMapParams Linux_MAXIS64_MemoryMapParams = { + 0, // AndMask (not used) + 0x008000000000, // XorMask + 0, // ShadowBase (not used) + 0x002000000000, // OriginBase +}; + // mips64 Linux static const MemoryMapParams Linux_MIPS64_MemoryMapParams = { 0, // AndMask (not used) @@ -333,6 +341,11 @@ static const PlatformMemoryMapParams Linux_X86_MemoryMapParams = { &Linux_X86_64_MemoryMapParams, }; +static const PlatformMemoryMapParams Linux_MAXIS_MemoryMapParams = { + nullptr, + &Linux_MAXIS64_MemoryMapParams, +}; + static const PlatformMemoryMapParams Linux_MIPS_MemoryMapParams = { nullptr, &Linux_MIPS64_MemoryMapParams, @@ -387,6 +400,7 @@ class MemorySanitizer : public FunctionPass { private: friend struct MemorySanitizerVisitor; friend struct VarArgAMD64Helper; + friend struct VarArgMAXIS64Helper; friend struct VarArgMIPS64Helper; friend struct VarArgAArch64Helper; friend struct VarArgPowerPC64Helper; @@ -607,6 +621,10 @@ bool MemorySanitizer::doInitialization(Module &M) { case Triple::x86: MapParams = Linux_X86_MemoryMapParams.bits32; break; + case Triple::maxis64: + case Triple::maxis64el: + MapParams = Linux_MAXIS_MemoryMapParams.bits64; + break; case Triple::mips64: case Triple::mips64el: MapParams = Linux_MIPS_MemoryMapParams.bits64; @@ -3245,6 +3263,115 @@ struct VarArgAMD64Helper : public VarArgHelper { } }; +/// \brief MAXIS64-specific implementation of VarArgHelper. +struct VarArgMAXIS64Helper : public VarArgHelper { + Function &F; + MemorySanitizer &MS; + MemorySanitizerVisitor &MSV; + Value *VAArgTLSCopy = nullptr; + Value *VAArgSize = nullptr; + + SmallVector VAStartInstrumentationList; + + VarArgMAXIS64Helper(Function &F, MemorySanitizer &MS, + MemorySanitizerVisitor &MSV) : F(F), MS(MS), MSV(MSV) {} + + void visitCallSite(CallSite &CS, IRBuilder<> &IRB) override { + unsigned VAArgOffset = 0; + const DataLayout &DL = F.getParent()->getDataLayout(); + for (CallSite::arg_iterator ArgIt = CS.arg_begin() + + CS.getFunctionType()->getNumParams(), End = CS.arg_end(); + ArgIt != End; ++ArgIt) { + Triple TargetTriple(F.getParent()->getTargetTriple()); + Value *A = *ArgIt; + Value *Base; + uint64_t ArgSize = DL.getTypeAllocSize(A->getType()); + if (TargetTriple.getArch() == Triple::maxis64) { + // Adjusting the shadow for argument with size < 8 to match the placement + // of bits in big endian system + if (ArgSize < 8) + VAArgOffset += (8 - ArgSize); + } + Base = getShadowPtrForVAArgument(A->getType(), IRB, VAArgOffset); + VAArgOffset += ArgSize; + VAArgOffset = alignTo(VAArgOffset, 8); + IRB.CreateAlignedStore(MSV.getShadow(A), Base, kShadowTLSAlignment); + } + + Constant *TotalVAArgSize = ConstantInt::get(IRB.getInt64Ty(), VAArgOffset); + // Here using VAArgOverflowSizeTLS as VAArgSizeTLS to avoid creation of + // a new class member i.e. it is the total size of all VarArgs. + IRB.CreateStore(TotalVAArgSize, MS.VAArgOverflowSizeTLS); + } + + /// \brief Compute the shadow address for a given va_arg. + Value *getShadowPtrForVAArgument(Type *Ty, IRBuilder<> &IRB, + int ArgOffset) { + Value *Base = IRB.CreatePointerCast(MS.VAArgTLS, MS.IntptrTy); + Base = IRB.CreateAdd(Base, ConstantInt::get(MS.IntptrTy, ArgOffset)); + return IRB.CreateIntToPtr(Base, PointerType::get(MSV.getShadowTy(Ty), 0), + "_msarg"); + } + + void visitVAStartInst(VAStartInst &I) override { + IRBuilder<> IRB(&I); + VAStartInstrumentationList.push_back(&I); + Value *VAListTag = I.getArgOperand(0); + Value *ShadowPtr, *OriginPtr; + unsigned Alignment = 8; + std::tie(ShadowPtr, OriginPtr) = + MSV.getShadowOriginPtr(VAListTag, IRB, IRB.getInt8Ty(), Alignment); + IRB.CreateMemSet(ShadowPtr, Constant::getNullValue(IRB.getInt8Ty()), + /* size */ 8, Alignment, false); + } + + void visitVACopyInst(VACopyInst &I) override { + IRBuilder<> IRB(&I); + VAStartInstrumentationList.push_back(&I); + Value *VAListTag = I.getArgOperand(0); + Value *ShadowPtr, *OriginPtr; + unsigned Alignment = 8; + std::tie(ShadowPtr, OriginPtr) = + MSV.getShadowOriginPtr(VAListTag, IRB, IRB.getInt8Ty(), Alignment); + IRB.CreateMemSet(ShadowPtr, Constant::getNullValue(IRB.getInt8Ty()), + /* size */ 8, Alignment, false); + } + + void finalizeInstrumentation() override { + assert(!VAArgSize && !VAArgTLSCopy && + "finalizeInstrumentation called twice"); + IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI()); + VAArgSize = IRB.CreateLoad(MS.VAArgOverflowSizeTLS); + Value *CopySize = IRB.CreateAdd(ConstantInt::get(MS.IntptrTy, 0), + VAArgSize); + + if (!VAStartInstrumentationList.empty()) { + // If there is a va_start in this function, make a backup copy of + // va_arg_tls somewhere in the function entry block. + VAArgTLSCopy = IRB.CreateAlloca(Type::getInt8Ty(*MS.C), CopySize); + IRB.CreateMemCpy(VAArgTLSCopy, MS.VAArgTLS, CopySize, 8); + } + + // Instrument va_start. + // Copy va_list shadow from the backup copy of the TLS contents. + for (size_t i = 0, n = VAStartInstrumentationList.size(); i < n; i++) { + CallInst *OrigInst = VAStartInstrumentationList[i]; + IRBuilder<> IRB(OrigInst->getNextNode()); + Value *VAListTag = OrigInst->getArgOperand(0); + Value *RegSaveAreaPtrPtr = + IRB.CreateIntToPtr(IRB.CreatePtrToInt(VAListTag, MS.IntptrTy), + Type::getInt64PtrTy(*MS.C)); + Value *RegSaveAreaPtr = IRB.CreateLoad(RegSaveAreaPtrPtr); + Value *RegSaveAreaShadowPtr, *RegSaveAreaOriginPtr; + unsigned Alignment = 8; + std::tie(RegSaveAreaShadowPtr, RegSaveAreaOriginPtr) = + MSV.getShadowOriginPtr(RegSaveAreaPtr, IRB, IRB.getInt8Ty(), + Alignment); + IRB.CreateMemCpy(RegSaveAreaShadowPtr, VAArgTLSCopy, CopySize, Alignment); + } + } +}; + /// \brief MIPS64-specific implementation of VarArgHelper. struct VarArgMIPS64Helper : public VarArgHelper { Function &F; diff --git a/tools/clang/include/clang/Basic/Attr.td b/tools/clang/include/clang/Basic/Attr.td index 59f595e0..f6c0c2ec 100644 --- a/tools/clang/include/clang/Basic/Attr.td +++ b/tools/clang/include/clang/Basic/Attr.td @@ -288,6 +288,8 @@ class TargetArch arches> : TargetSpec { } def TargetARM : TargetArch<["arm", "thumb", "armeb", "thumbeb"]>; def TargetAVR : TargetArch<["avr"]>; +def TargetMaxis32 : TargetArch<["maxis", "maxisel"]>; +def TargetAnyMaxis : TargetArch<["maxis", "maxisel", "maxis64", "maxis64el"]>; def TargetMips32 : TargetArch<["mips", "mipsel"]>; def TargetAnyMips : TargetArch<["mips", "mipsel", "mips64", "mips64el"]>; def TargetMSP430 : TargetArch<["msp430"]>; @@ -629,7 +631,7 @@ def Annotate : InheritableParamAttr { def ARMInterrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, MSP430Interrupt's, - // MipsInterrupt's and AnyX86Interrupt's spellings must match. + // MaxisInterrupt's, MipsInterrupt's and AnyX86Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Args = [EnumArgument<"Interrupt", "InterruptType", ["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""], @@ -1177,7 +1179,7 @@ def MSABI : InheritableAttr { } def MSP430Interrupt : InheritableAttr, TargetSpecificAttr { - // NOTE: If you add any additional spellings, ARMInterrupt's, MipsInterrupt's + // NOTE: If you add any additional spellings, ARMInterrupt's, MaxisInterrupt's, MipsInterrupt's // and AnyX86Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Args = [UnsignedArgument<"Number">]; @@ -1186,6 +1188,46 @@ def MSP430Interrupt : InheritableAttr, TargetSpecificAttr { let Documentation = [Undocumented]; } +def Maxis16 : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"maxis16">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +def MaxisInterrupt : InheritableAttr, TargetSpecificAttr { + // NOTE: If you add any additional spellings, ARMInterrupt's, + // MSP430Interrupt's and AnyX86Interrupt's spellings must match. + let Spellings = [GCC<"interrupt">]; + let Subjects = SubjectList<[Function]>; + let Args = [EnumArgument<"Interrupt", "InterruptType", + ["vector=sw0", "vector=sw1", "vector=hw0", + "vector=hw1", "vector=hw2", "vector=hw3", + "vector=hw4", "vector=hw5", "eic", ""], + ["sw0", "sw1", "hw0", "hw1", "hw2", "hw3", + "hw4", "hw5", "eic", "eic"] + >]; + let ParseKind = "Interrupt"; + let Documentation = [MaxisInterruptDocs]; +} + +def MicroMaxis : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"micromaxis">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [MicroMaxisDocs]; +} + +def MaxisLongCall : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"long_call">, GCC<"far">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [MaxisLongCallStyleDocs]; +} + +def MaxisShortCall : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"short_call">, GCC<"near">]; + let Subjects = SubjectList<[Function]>; + let Documentation = [MaxisShortCallStyleDocs]; +} + def Mips16 : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"mips16">]; let Subjects = SubjectList<[Function], ErrorDiag>; @@ -1299,6 +1341,18 @@ def NoInline : InheritableAttr { let Documentation = [Undocumented]; } +def NoMaxis16 : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"nomaxis16">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [Undocumented]; +} + +def NoMicroMaxis : InheritableAttr, TargetSpecificAttr { + let Spellings = [GCC<"nomicromaxis">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [MicroMaxisDocs]; +} + def NoMips16 : InheritableAttr, TargetSpecificAttr { let Spellings = [GCC<"nomips16">]; let Subjects = SubjectList<[Function], ErrorDiag>; @@ -2053,7 +2107,7 @@ def LTOVisibilityPublic : InheritableAttr { def AnyX86Interrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, ARMInterrupt's, - // MSP430Interrupt's and MipsInterrupt's spellings must match. + // MSP430Interrupt's, MaxisInterrupt's, and MipsInterrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Subjects = SubjectList<[HasFunctionProto]>; let ParseKind = "Interrupt"; diff --git a/tools/clang/include/clang/Basic/AttrDocs.td b/tools/clang/include/clang/Basic/AttrDocs.td index dc355832..c4ccbb10 100644 --- a/tools/clang/include/clang/Basic/AttrDocs.td +++ b/tools/clang/include/clang/Basic/AttrDocs.td @@ -1333,6 +1333,103 @@ The semantics are as follows: }]; } +def MaxisInterruptDocs : Documentation { + let Category = DocCatFunction; + let Heading = "interrupt (MAXIS)"; + let Content = [{ +Clang supports the GNU style ``__attribute__((interrupt("ARGUMENT")))`` attribute on +MAXIS targets. This attribute may be attached to a function definition and instructs +the backend to generate appropriate function entry/exit code so that it can be used +directly as an interrupt service routine. + +By default, the compiler will produce a function prologue and epilogue suitable for +an interrupt service routine that handles an External Interrupt Controller (eic) +generated interrupt. This behaviour can be explicitly requested with the "eic" +argument. + +Otherwise, for use with vectored interrupt mode, the argument passed should be +of the form "vector=LEVEL" where LEVEL is one of the following values: +"sw0", "sw1", "hw0", "hw1", "hw2", "hw3", "hw4", "hw5". The compiler will +then set the interrupt mask to the corresponding level which will mask all +interrupts up to and including the argument. + +The semantics are as follows: + +- The prologue is modified so that the Exception Program Counter (EPC) and + Status coprocessor registers are saved to the stack. The interrupt mask is + set so that the function can only be interrupted by a higher priority + interrupt. The epilogue will restore the previous values of EPC and Status. + +- The prologue and epilogue are modified to save and restore all non-kernel + registers as necessary. + +- The FPU is disabled in the prologue, as the floating pointer registers are not + spilled to the stack. + +- The function return sequence is changed to use an exception return instruction. + +- The parameter sets the interrupt mask for the function corresponding to the + interrupt level specified. If no mask is specified the interrupt mask + defaults to "eic". + }]; +} + +def MicroMaxisDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +Clang supports the GNU style ``__attribute__((micromaxis))`` and +``__attribute__((nomicromaxis))`` attributes on MAXIS targets. These attributes +may be attached to a function definition and instructs the backend to generate +or not to generate microMAXIS code for that function. + +These attributes override the `-mmicromaxis` and `-mno-micromaxis` options +on the command line. + }]; +} + +def MaxisLongCallStyleDocs : Documentation { + let Category = DocCatFunction; + let Heading = "long_call (gnu::long_call, gnu::far)"; + let Content = [{ +Clang supports the ``__attribute__((long_call))``, ``__attribute__((far))``, +and ``__attribute__((near))`` attributes on MAXIS targets. These attributes may +only be added to function declarations and change the code generated +by the compiler when directly calling the function. The ``near`` attribute +allows calls to the function to be made using the ``jal`` instruction, which +requires the function to be located in the same naturally aligned 256MB +segment as the caller. The ``long_call`` and ``far`` attributes are synonyms +and require the use of a different call sequence that works regardless +of the distance between the functions. + +These attributes have no effect for position-independent code. + +These attributes take priority over command line switches such +as ``-mlong-calls`` and ``-mno-long-calls``. + }]; +} + +def MaxisShortCallStyleDocs : Documentation { + let Category = DocCatFunction; + let Heading = "short_call (gnu::short_call, gnu::near)"; + let Content = [{ +Clang supports the ``__attribute__((long_call))``, ``__attribute__((far))``, +``__attribute__((short__call))``, and ``__attribute__((near))`` attributes +on MAXIS targets. These attributes may only be added to function declarations +and change the code generated by the compiler when directly calling +the function. The ``short_call`` and ``near`` attributes are synonyms and +allow calls to the function to be made using the ``jal`` instruction, which +requires the function to be located in the same naturally aligned 256MB segment +as the caller. The ``long_call`` and ``far`` attributes are synonyms and +require the use of a different call sequence that works regardless +of the distance between the functions. + +These attributes have no effect for position-independent code. + +These attributes take priority over command line switches such +as ``-mlong-calls`` and ``-mno-long-calls``. + }]; +} + def MipsInterruptDocs : Documentation { let Category = DocCatFunction; let Heading = "interrupt (MIPS)"; diff --git a/tools/clang/include/clang/Basic/BuiltinsMaxis.def b/tools/clang/include/clang/Basic/BuiltinsMaxis.def new file mode 100644 index 00000000..694d7c15 --- /dev/null +++ b/tools/clang/include/clang/Basic/BuiltinsMaxis.def @@ -0,0 +1,900 @@ +//===-- BuiltinsMaxis.def - Maxis Builtin function database --------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the MAXIS-specific builtin function database. Users of +// this file must define the BUILTIN macro to make use of this information. +// +//===----------------------------------------------------------------------===// + +// The format of this database matches clang/Basic/Builtins.def. + +// MAXIS DSP Rev 1 + +// Add/subtract with optional saturation +BUILTIN(__builtin_maxis_addu_qb, "V4ScV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_addu_s_qb, "V4ScV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_subu_qb, "V4ScV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_subu_s_qb, "V4ScV4ScV4Sc", "n") + +BUILTIN(__builtin_maxis_addq_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_addq_s_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_subq_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_subq_s_ph, "V2sV2sV2s", "n") + +BUILTIN(__builtin_maxis_madd, "LLiLLiii", "nc") +BUILTIN(__builtin_maxis_maddu, "LLiLLiUiUi", "nc") +BUILTIN(__builtin_maxis_msub, "LLiLLiii", "nc") +BUILTIN(__builtin_maxis_msubu, "LLiLLiUiUi", "nc") + +BUILTIN(__builtin_maxis_addq_s_w, "iii", "n") +BUILTIN(__builtin_maxis_subq_s_w, "iii", "n") + +BUILTIN(__builtin_maxis_addsc, "iii", "n") +BUILTIN(__builtin_maxis_addwc, "iii", "n") + +BUILTIN(__builtin_maxis_modsub, "iii", "nc") + +BUILTIN(__builtin_maxis_raddu_w_qb, "iV4Sc", "nc") + +BUILTIN(__builtin_maxis_absq_s_ph, "V2sV2s", "n") +BUILTIN(__builtin_maxis_absq_s_w, "ii", "n") + +BUILTIN(__builtin_maxis_precrq_qb_ph, "V4ScV2sV2s", "nc") +BUILTIN(__builtin_maxis_precrqu_s_qb_ph, "V4ScV2sV2s", "n") +BUILTIN(__builtin_maxis_precrq_ph_w, "V2sii", "nc") +BUILTIN(__builtin_maxis_precrq_rs_ph_w, "V2sii", "n") +BUILTIN(__builtin_maxis_preceq_w_phl, "iV2s", "nc") +BUILTIN(__builtin_maxis_preceq_w_phr, "iV2s", "nc") +BUILTIN(__builtin_maxis_precequ_ph_qbl, "V2sV4Sc", "nc") +BUILTIN(__builtin_maxis_precequ_ph_qbr, "V2sV4Sc", "nc") +BUILTIN(__builtin_maxis_precequ_ph_qbla, "V2sV4Sc", "nc") +BUILTIN(__builtin_maxis_precequ_ph_qbra, "V2sV4Sc", "nc") +BUILTIN(__builtin_maxis_preceu_ph_qbl, "V2sV4Sc", "nc") +BUILTIN(__builtin_maxis_preceu_ph_qbr, "V2sV4Sc", "nc") +BUILTIN(__builtin_maxis_preceu_ph_qbla, "V2sV4Sc", "nc") +BUILTIN(__builtin_maxis_preceu_ph_qbra, "V2sV4Sc", "nc") + +BUILTIN(__builtin_maxis_shll_qb, "V4ScV4Sci", "n") +BUILTIN(__builtin_maxis_shrl_qb, "V4ScV4Sci", "nc") +BUILTIN(__builtin_maxis_shll_ph, "V2sV2si", "n") +BUILTIN(__builtin_maxis_shll_s_ph, "V2sV2si", "n") +BUILTIN(__builtin_maxis_shra_ph, "V2sV2si", "nc") +BUILTIN(__builtin_maxis_shra_r_ph, "V2sV2si", "nc") +BUILTIN(__builtin_maxis_shll_s_w, "iii", "n") +BUILTIN(__builtin_maxis_shra_r_w, "iii", "nc") +BUILTIN(__builtin_maxis_shilo, "LLiLLii", "nc") + +BUILTIN(__builtin_maxis_muleu_s_ph_qbl, "V2sV4ScV2s", "n") +BUILTIN(__builtin_maxis_muleu_s_ph_qbr, "V2sV4ScV2s", "n") +BUILTIN(__builtin_maxis_mulq_rs_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_muleq_s_w_phl, "iV2sV2s", "n") +BUILTIN(__builtin_maxis_muleq_s_w_phr, "iV2sV2s", "n") +BUILTIN(__builtin_maxis_mulsaq_s_w_ph, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_maq_s_w_phl, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_maq_s_w_phr, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_maq_sa_w_phl, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_maq_sa_w_phr, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_mult, "LLiii", "nc") +BUILTIN(__builtin_maxis_multu, "LLiUiUi", "nc") + +BUILTIN(__builtin_maxis_dpau_h_qbl, "LLiLLiV4ScV4Sc", "nc") +BUILTIN(__builtin_maxis_dpau_h_qbr, "LLiLLiV4ScV4Sc", "nc") +BUILTIN(__builtin_maxis_dpsu_h_qbl, "LLiLLiV4ScV4Sc", "nc") +BUILTIN(__builtin_maxis_dpsu_h_qbr, "LLiLLiV4ScV4Sc", "nc") +BUILTIN(__builtin_maxis_dpaq_s_w_ph, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_dpsq_s_w_ph, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_dpaq_sa_l_w, "LLiLLiii", "n") +BUILTIN(__builtin_maxis_dpsq_sa_l_w, "LLiLLiii", "n") + +BUILTIN(__builtin_maxis_cmpu_eq_qb, "vV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_cmpu_lt_qb, "vV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_cmpu_le_qb, "vV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_cmpgu_eq_qb, "iV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_cmpgu_lt_qb, "iV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_cmpgu_le_qb, "iV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_cmp_eq_ph, "vV2sV2s", "n") +BUILTIN(__builtin_maxis_cmp_lt_ph, "vV2sV2s", "n") +BUILTIN(__builtin_maxis_cmp_le_ph, "vV2sV2s", "n") + +BUILTIN(__builtin_maxis_extr_s_h, "iLLii", "n") +BUILTIN(__builtin_maxis_extr_w, "iLLii", "n") +BUILTIN(__builtin_maxis_extr_rs_w, "iLLii", "n") +BUILTIN(__builtin_maxis_extr_r_w, "iLLii", "n") +BUILTIN(__builtin_maxis_extp, "iLLii", "n") +BUILTIN(__builtin_maxis_extpdp, "iLLii", "n") + +BUILTIN(__builtin_maxis_wrdsp, "viIi", "n") +BUILTIN(__builtin_maxis_rddsp, "iIi", "n") +BUILTIN(__builtin_maxis_insv, "iii", "n") +BUILTIN(__builtin_maxis_bitrev, "ii", "nc") +BUILTIN(__builtin_maxis_packrl_ph, "V2sV2sV2s", "nc") +BUILTIN(__builtin_maxis_repl_qb, "V4Sci", "nc") +BUILTIN(__builtin_maxis_repl_ph, "V2si", "nc") +BUILTIN(__builtin_maxis_pick_qb, "V4ScV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_pick_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_mthlip, "LLiLLii", "n") +BUILTIN(__builtin_maxis_bposge32, "i", "n") +BUILTIN(__builtin_maxis_lbux, "iv*i", "n") +BUILTIN(__builtin_maxis_lhx, "iv*i", "n") +BUILTIN(__builtin_maxis_lwx, "iv*i", "n") + +// MAXIS DSP Rev 2 + +BUILTIN(__builtin_maxis_absq_s_qb, "V4ScV4Sc", "n") + +BUILTIN(__builtin_maxis_addqh_ph, "V2sV2sV2s", "nc") +BUILTIN(__builtin_maxis_addqh_r_ph, "V2sV2sV2s", "nc") +BUILTIN(__builtin_maxis_addqh_w, "iii", "nc") +BUILTIN(__builtin_maxis_addqh_r_w, "iii", "nc") + +BUILTIN(__builtin_maxis_addu_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_addu_s_ph, "V2sV2sV2s", "n") + +BUILTIN(__builtin_maxis_adduh_qb, "V4ScV4ScV4Sc", "nc") +BUILTIN(__builtin_maxis_adduh_r_qb, "V4ScV4ScV4Sc", "nc") + +BUILTIN(__builtin_maxis_append, "iiiIi", "nc") +BUILTIN(__builtin_maxis_balign, "iiiIi", "nc") + +BUILTIN(__builtin_maxis_cmpgdu_eq_qb, "iV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_cmpgdu_lt_qb, "iV4ScV4Sc", "n") +BUILTIN(__builtin_maxis_cmpgdu_le_qb, "iV4ScV4Sc", "n") + +BUILTIN(__builtin_maxis_dpa_w_ph, "LLiLLiV2sV2s", "nc") +BUILTIN(__builtin_maxis_dps_w_ph, "LLiLLiV2sV2s", "nc") + +BUILTIN(__builtin_maxis_dpaqx_s_w_ph, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_dpaqx_sa_w_ph, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_dpax_w_ph, "LLiLLiV2sV2s", "nc") +BUILTIN(__builtin_maxis_dpsx_w_ph, "LLiLLiV2sV2s", "nc") +BUILTIN(__builtin_maxis_dpsqx_s_w_ph, "LLiLLiV2sV2s", "n") +BUILTIN(__builtin_maxis_dpsqx_sa_w_ph, "LLiLLiV2sV2s", "n") + +BUILTIN(__builtin_maxis_mul_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_mul_s_ph, "V2sV2sV2s", "n") + +BUILTIN(__builtin_maxis_mulq_rs_w, "iii", "n") +BUILTIN(__builtin_maxis_mulq_s_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_mulq_s_w, "iii", "n") +BUILTIN(__builtin_maxis_mulsa_w_ph, "LLiLLiV2sV2s", "nc") + +BUILTIN(__builtin_maxis_precr_qb_ph, "V4ScV2sV2s", "n") +BUILTIN(__builtin_maxis_precr_sra_ph_w, "V2siiIi", "nc") +BUILTIN(__builtin_maxis_precr_sra_r_ph_w, "V2siiIi", "nc") + +BUILTIN(__builtin_maxis_prepend, "iiiIi", "nc") + +BUILTIN(__builtin_maxis_shra_qb, "V4ScV4Sci", "nc") +BUILTIN(__builtin_maxis_shra_r_qb, "V4ScV4Sci", "nc") +BUILTIN(__builtin_maxis_shrl_ph, "V2sV2si", "nc") + +BUILTIN(__builtin_maxis_subqh_ph, "V2sV2sV2s", "nc") +BUILTIN(__builtin_maxis_subqh_r_ph, "V2sV2sV2s", "nc") +BUILTIN(__builtin_maxis_subqh_w, "iii", "nc") +BUILTIN(__builtin_maxis_subqh_r_w, "iii", "nc") + +BUILTIN(__builtin_maxis_subu_ph, "V2sV2sV2s", "n") +BUILTIN(__builtin_maxis_subu_s_ph, "V2sV2sV2s", "n") + +BUILTIN(__builtin_maxis_subuh_qb, "V4ScV4ScV4Sc", "nc") +BUILTIN(__builtin_maxis_subuh_r_qb, "V4ScV4ScV4Sc", "nc") + +// MAXIS MSA + +BUILTIN(__builtin_msa_add_a_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_add_a_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_add_a_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_add_a_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_adds_a_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_adds_a_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_adds_a_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_adds_a_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_adds_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_adds_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_adds_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_adds_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_adds_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_adds_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_adds_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_adds_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_addv_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_addv_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_addv_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_addv_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_addvi_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_addvi_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_addvi_w, "V4iV4iIUi", "nc") +BUILTIN(__builtin_msa_addvi_d, "V2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_and_v, "V16UcV16UcV16Uc", "nc") + +BUILTIN(__builtin_msa_andi_b, "V16UcV16UcIUi", "nc") + +BUILTIN(__builtin_msa_asub_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_asub_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_asub_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_asub_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_asub_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_asub_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_asub_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_asub_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_ave_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_ave_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_ave_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_ave_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_ave_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_ave_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_ave_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_ave_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_aver_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_aver_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_aver_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_aver_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_aver_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_aver_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_aver_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_aver_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_bclr_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_bclr_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_bclr_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_bclr_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_bclri_b, "V16UcV16UcIUi", "nc") +BUILTIN(__builtin_msa_bclri_h, "V8UsV8UsIUi", "nc") +BUILTIN(__builtin_msa_bclri_w, "V4UiV4UiIUi", "nc") +BUILTIN(__builtin_msa_bclri_d, "V2ULLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_binsl_b, "V16UcV16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_binsl_h, "V8UsV8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_binsl_w, "V4UiV4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_binsl_d, "V2ULLiV2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_binsli_b, "V16UcV16UcV16UcIUi", "nc") +BUILTIN(__builtin_msa_binsli_h, "V8UsV8UsV8UsIUi", "nc") +BUILTIN(__builtin_msa_binsli_w, "V4UiV4UiV4UiIUi", "nc") +BUILTIN(__builtin_msa_binsli_d, "V2ULLiV2ULLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_binsr_b, "V16UcV16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_binsr_h, "V8UsV8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_binsr_w, "V4UiV4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_binsr_d, "V2ULLiV2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_binsri_b, "V16UcV16UcV16UcIUi", "nc") +BUILTIN(__builtin_msa_binsri_h, "V8UsV8UsV8UsIUi", "nc") +BUILTIN(__builtin_msa_binsri_w, "V4UiV4UiV4UiIUi", "nc") +BUILTIN(__builtin_msa_binsri_d, "V2ULLiV2ULLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_bmnz_v, "V16UcV16UcV16UcV16Uc", "nc") + +BUILTIN(__builtin_msa_bmnzi_b, "V16UcV16UcV16UcIUi", "nc") + +BUILTIN(__builtin_msa_bmz_v, "V16UcV16UcV16UcV16Uc", "nc") + +BUILTIN(__builtin_msa_bmzi_b, "V16UcV16UcV16UcIUi", "nc") + +BUILTIN(__builtin_msa_bneg_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_bneg_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_bneg_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_bneg_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_bnegi_b, "V16UcV16UcIUi", "nc") +BUILTIN(__builtin_msa_bnegi_h, "V8UsV8UsIUi", "nc") +BUILTIN(__builtin_msa_bnegi_w, "V4UiV4UiIUi", "nc") +BUILTIN(__builtin_msa_bnegi_d, "V2ULLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_bnz_b, "iV16Uc", "nc") +BUILTIN(__builtin_msa_bnz_h, "iV8Us", "nc") +BUILTIN(__builtin_msa_bnz_w, "iV4Ui", "nc") +BUILTIN(__builtin_msa_bnz_d, "iV2ULLi", "nc") + +BUILTIN(__builtin_msa_bnz_v, "iV16Uc", "nc") + +BUILTIN(__builtin_msa_bsel_v, "V16UcV16UcV16UcV16Uc", "nc") + +BUILTIN(__builtin_msa_bseli_b, "V16UcV16UcV16UcIUi", "nc") + +BUILTIN(__builtin_msa_bset_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_bset_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_bset_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_bset_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_bseti_b, "V16UcV16UcIUi", "nc") +BUILTIN(__builtin_msa_bseti_h, "V8UsV8UsIUi", "nc") +BUILTIN(__builtin_msa_bseti_w, "V4UiV4UiIUi", "nc") +BUILTIN(__builtin_msa_bseti_d, "V2ULLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_bz_b, "iV16Uc", "nc") +BUILTIN(__builtin_msa_bz_h, "iV8Us", "nc") +BUILTIN(__builtin_msa_bz_w, "iV4Ui", "nc") +BUILTIN(__builtin_msa_bz_d, "iV2ULLi", "nc") + +BUILTIN(__builtin_msa_bz_v, "iV16Uc", "nc") + +BUILTIN(__builtin_msa_ceq_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_ceq_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_ceq_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_ceq_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_ceqi_b, "V16ScV16ScISi", "nc") +BUILTIN(__builtin_msa_ceqi_h, "V8SsV8SsISi", "nc") +BUILTIN(__builtin_msa_ceqi_w, "V4SiV4SiISi", "nc") +BUILTIN(__builtin_msa_ceqi_d, "V2SLLiV2SLLiISi", "nc") + +BUILTIN(__builtin_msa_cfcmsa, "iIi", "n") + +BUILTIN(__builtin_msa_cle_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_cle_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_cle_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_cle_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_cle_u_b, "V16ScV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_cle_u_h, "V8SsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_cle_u_w, "V4SiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_cle_u_d, "V2SLLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_clei_s_b, "V16ScV16ScISi", "nc") +BUILTIN(__builtin_msa_clei_s_h, "V8SsV8SsISi", "nc") +BUILTIN(__builtin_msa_clei_s_w, "V4SiV4SiISi", "nc") +BUILTIN(__builtin_msa_clei_s_d, "V2SLLiV2SLLiISi", "nc") + +BUILTIN(__builtin_msa_clei_u_b, "V16ScV16UcIUi", "nc") +BUILTIN(__builtin_msa_clei_u_h, "V8SsV8UsIUi", "nc") +BUILTIN(__builtin_msa_clei_u_w, "V4SiV4UiIUi", "nc") +BUILTIN(__builtin_msa_clei_u_d, "V2SLLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_clt_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_clt_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_clt_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_clt_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_clt_u_b, "V16ScV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_clt_u_h, "V8SsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_clt_u_w, "V4SiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_clt_u_d, "V2SLLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_clti_s_b, "V16ScV16ScISi", "nc") +BUILTIN(__builtin_msa_clti_s_h, "V8SsV8SsISi", "nc") +BUILTIN(__builtin_msa_clti_s_w, "V4SiV4SiISi", "nc") +BUILTIN(__builtin_msa_clti_s_d, "V2SLLiV2SLLiISi", "nc") + +BUILTIN(__builtin_msa_clti_u_b, "V16ScV16UcIUi", "nc") +BUILTIN(__builtin_msa_clti_u_h, "V8SsV8UsIUi", "nc") +BUILTIN(__builtin_msa_clti_u_w, "V4SiV4UiIUi", "nc") +BUILTIN(__builtin_msa_clti_u_d, "V2SLLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_copy_s_b, "iV16ScIUi", "nc") +BUILTIN(__builtin_msa_copy_s_h, "iV8SsIUi", "nc") +BUILTIN(__builtin_msa_copy_s_w, "iV4SiIUi", "nc") +BUILTIN(__builtin_msa_copy_s_d, "LLiV2SLLiIUi", "nc") + +BUILTIN(__builtin_msa_copy_u_b, "iV16UcIUi", "nc") +BUILTIN(__builtin_msa_copy_u_h, "iV8UsIUi", "nc") +BUILTIN(__builtin_msa_copy_u_w, "iV4UiIUi", "nc") +BUILTIN(__builtin_msa_copy_u_d, "LLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_ctcmsa, "vIii", "n") + +BUILTIN(__builtin_msa_div_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_div_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_div_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_div_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_div_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_div_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_div_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_div_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_dotp_s_h, "V8SsV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_dotp_s_w, "V4SiV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_dotp_s_d, "V2SLLiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_dotp_u_h, "V8UsV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_dotp_u_w, "V4UiV8UsV8Us", "nc") +BUILTIN(__builtin_msa_dotp_u_d, "V2ULLiV4UiV4Ui", "nc") + +BUILTIN(__builtin_msa_dpadd_s_h, "V8SsV8SsV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_dpadd_s_w, "V4SiV4SiV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_dpadd_s_d, "V2SLLiV2SLLiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_dpadd_u_h, "V8UsV8UsV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_dpadd_u_w, "V4UiV4UiV8UsV8Us", "nc") +BUILTIN(__builtin_msa_dpadd_u_d, "V2ULLiV2ULLiV4UiV4Ui", "nc") + +BUILTIN(__builtin_msa_dpsub_s_h, "V8SsV8SsV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_dpsub_s_w, "V4SiV4SiV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_dpsub_s_d, "V2SLLiV2SLLiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_dpsub_u_h, "V8UsV8UsV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_dpsub_u_w, "V4UiV4UiV8UsV8Us", "nc") +BUILTIN(__builtin_msa_dpsub_u_d, "V2ULLiV2ULLiV4UiV4Ui", "nc") + +BUILTIN(__builtin_msa_fadd_w, "V4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fadd_d, "V2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fcaf_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcaf_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fceq_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fceq_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fclass_w, "V4iV4f", "nc") +BUILTIN(__builtin_msa_fclass_d, "V2LLiV2d", "nc") + +BUILTIN(__builtin_msa_fcle_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcle_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fclt_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fclt_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fcne_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcne_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fcor_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcor_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fcueq_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcueq_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fcule_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcule_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fcult_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcult_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fcun_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcun_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fcune_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fcune_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fdiv_w, "V4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fdiv_d, "V2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fexdo_h, "V8hV4fV4f", "nc") +BUILTIN(__builtin_msa_fexdo_w, "V4fV2dV2d", "nc") + +BUILTIN(__builtin_msa_fexp2_w, "V4fV4fV4i", "nc") +BUILTIN(__builtin_msa_fexp2_d, "V2dV2dV2LLi", "nc") + +BUILTIN(__builtin_msa_fexupl_w, "V4fV8h", "nc") +BUILTIN(__builtin_msa_fexupl_d, "V2dV4f", "nc") + +BUILTIN(__builtin_msa_fexupr_w, "V4fV8h", "nc") +BUILTIN(__builtin_msa_fexupr_d, "V2dV4f", "nc") + +BUILTIN(__builtin_msa_ffint_s_w, "V4fV4Si", "nc") +BUILTIN(__builtin_msa_ffint_s_d, "V2dV2SLLi", "nc") + +BUILTIN(__builtin_msa_ffint_u_w, "V4fV4Ui", "nc") +BUILTIN(__builtin_msa_ffint_u_d, "V2dV2ULLi", "nc") + +// ffql uses integers since long _Fract is not implemented +BUILTIN(__builtin_msa_ffql_w, "V4fV8Ss", "nc") +BUILTIN(__builtin_msa_ffql_d, "V2dV4Si", "nc") + +// ffqr uses integers since long _Fract is not implemented +BUILTIN(__builtin_msa_ffqr_w, "V4fV8Ss", "nc") +BUILTIN(__builtin_msa_ffqr_d, "V2dV4Si", "nc") + +BUILTIN(__builtin_msa_fill_b, "V16Sci", "nc") +BUILTIN(__builtin_msa_fill_h, "V8Ssi", "nc") +BUILTIN(__builtin_msa_fill_w, "V4Sii", "nc") +BUILTIN(__builtin_msa_fill_d, "V2SLLiLLi", "nc") + +BUILTIN(__builtin_msa_flog2_w, "V4fV4f", "nc") +BUILTIN(__builtin_msa_flog2_d, "V2dV2d", "nc") + +BUILTIN(__builtin_msa_fmadd_w, "V4fV4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fmadd_d, "V2dV2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fmax_w, "V4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fmax_d, "V2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fmax_a_w, "V4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fmax_a_d, "V2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fmin_w, "V4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fmin_d, "V2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fmin_a_w, "V4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fmin_a_d, "V2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fmsub_w, "V4fV4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fmsub_d, "V2dV2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fmul_w, "V4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fmul_d, "V2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_frint_w, "V4fV4f", "nc") +BUILTIN(__builtin_msa_frint_d, "V2dV2d", "nc") + +BUILTIN(__builtin_msa_frcp_w, "V4fV4f", "nc") +BUILTIN(__builtin_msa_frcp_d, "V2dV2d", "nc") + +BUILTIN(__builtin_msa_frsqrt_w, "V4fV4f", "nc") +BUILTIN(__builtin_msa_frsqrt_d, "V2dV2d", "nc") + +BUILTIN(__builtin_msa_fsaf_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsaf_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fseq_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fseq_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsle_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsle_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fslt_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fslt_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsne_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsne_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsor_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsor_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsqrt_w, "V4fV4f", "nc") +BUILTIN(__builtin_msa_fsqrt_d, "V2dV2d", "nc") + +BUILTIN(__builtin_msa_fsub_w, "V4fV4fV4f", "nc") +BUILTIN(__builtin_msa_fsub_d, "V2dV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsueq_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsueq_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsule_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsule_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsult_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsult_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsun_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsun_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_fsune_w, "V4iV4fV4f", "nc") +BUILTIN(__builtin_msa_fsune_d, "V2LLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_ftint_s_w, "V4SiV4f", "nc") +BUILTIN(__builtin_msa_ftint_s_d, "V2SLLiV2d", "nc") + +BUILTIN(__builtin_msa_ftint_u_w, "V4UiV4f", "nc") +BUILTIN(__builtin_msa_ftint_u_d, "V2ULLiV2d", "nc") + +BUILTIN(__builtin_msa_ftq_h, "V4UiV4fV4f", "nc") +BUILTIN(__builtin_msa_ftq_w, "V2ULLiV2dV2d", "nc") + +BUILTIN(__builtin_msa_ftrunc_s_w, "V4SiV4f", "nc") +BUILTIN(__builtin_msa_ftrunc_s_d, "V2SLLiV2d", "nc") + +BUILTIN(__builtin_msa_ftrunc_u_w, "V4UiV4f", "nc") +BUILTIN(__builtin_msa_ftrunc_u_d, "V2ULLiV2d", "nc") + +BUILTIN(__builtin_msa_hadd_s_h, "V8SsV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_hadd_s_w, "V4SiV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_hadd_s_d, "V2SLLiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_hadd_u_h, "V8UsV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_hadd_u_w, "V4UiV8UsV8Us", "nc") +BUILTIN(__builtin_msa_hadd_u_d, "V2ULLiV4UiV4Ui", "nc") + +BUILTIN(__builtin_msa_hsub_s_h, "V8SsV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_hsub_s_w, "V4SiV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_hsub_s_d, "V2SLLiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_hsub_u_h, "V8UsV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_hsub_u_w, "V4UiV8UsV8Us", "nc") +BUILTIN(__builtin_msa_hsub_u_d, "V2ULLiV4UiV4Ui", "nc") + +BUILTIN(__builtin_msa_ilvev_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_ilvev_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_ilvev_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_ilvev_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_ilvl_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_ilvl_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_ilvl_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_ilvl_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_ilvod_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_ilvod_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_ilvod_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_ilvod_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_ilvr_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_ilvr_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_ilvr_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_ilvr_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_insert_b, "V16ScV16ScIUii", "nc") +BUILTIN(__builtin_msa_insert_h, "V8SsV8SsIUii", "nc") +BUILTIN(__builtin_msa_insert_w, "V4SiV4SiIUii", "nc") +BUILTIN(__builtin_msa_insert_d, "V2SLLiV2SLLiIUiLLi", "nc") + +BUILTIN(__builtin_msa_insve_b, "V16ScV16ScIUiV16Sc", "nc") +BUILTIN(__builtin_msa_insve_h, "V8SsV8SsIUiV8Ss", "nc") +BUILTIN(__builtin_msa_insve_w, "V4SiV4SiIUiV4Si", "nc") +BUILTIN(__builtin_msa_insve_d, "V2SLLiV2SLLiIUiV2SLLi", "nc") + +BUILTIN(__builtin_msa_ld_b, "V16Scv*Ii", "nc") +BUILTIN(__builtin_msa_ld_h, "V8Ssv*Ii", "nc") +BUILTIN(__builtin_msa_ld_w, "V4Siv*Ii", "nc") +BUILTIN(__builtin_msa_ld_d, "V2SLLiv*Ii", "nc") + +BUILTIN(__builtin_msa_ldi_b, "V16cIi", "nc") +BUILTIN(__builtin_msa_ldi_h, "V8sIi", "nc") +BUILTIN(__builtin_msa_ldi_w, "V4iIi", "nc") +BUILTIN(__builtin_msa_ldi_d, "V2LLiIi", "nc") + +BUILTIN(__builtin_msa_madd_q_h, "V8SsV8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_madd_q_w, "V4SiV4SiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_maddr_q_h, "V8SsV8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_maddr_q_w, "V4SiV4SiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_maddv_b, "V16ScV16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_maddv_h, "V8SsV8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_maddv_w, "V4SiV4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_maddv_d, "V2SLLiV2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_max_a_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_max_a_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_max_a_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_max_a_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_max_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_max_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_max_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_max_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_max_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_max_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_max_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_max_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_maxi_s_b, "V16ScV16ScIi", "nc") +BUILTIN(__builtin_msa_maxi_s_h, "V8SsV8SsIi", "nc") +BUILTIN(__builtin_msa_maxi_s_w, "V4SiV4SiIi", "nc") +BUILTIN(__builtin_msa_maxi_s_d, "V2SLLiV2SLLiIi", "nc") + +BUILTIN(__builtin_msa_maxi_u_b, "V16UcV16UcIi", "nc") +BUILTIN(__builtin_msa_maxi_u_h, "V8UsV8UsIi", "nc") +BUILTIN(__builtin_msa_maxi_u_w, "V4UiV4UiIi", "nc") +BUILTIN(__builtin_msa_maxi_u_d, "V2ULLiV2ULLiIi", "nc") + +BUILTIN(__builtin_msa_min_a_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_min_a_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_min_a_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_min_a_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_min_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_min_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_min_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_min_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_min_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_min_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_min_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_min_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_mini_s_b, "V16ScV16ScIi", "nc") +BUILTIN(__builtin_msa_mini_s_h, "V8SsV8SsIi", "nc") +BUILTIN(__builtin_msa_mini_s_w, "V4SiV4SiIi", "nc") +BUILTIN(__builtin_msa_mini_s_d, "V2SLLiV2SLLiIi", "nc") + +BUILTIN(__builtin_msa_mini_u_b, "V16UcV16UcIi", "nc") +BUILTIN(__builtin_msa_mini_u_h, "V8UsV8UsIi", "nc") +BUILTIN(__builtin_msa_mini_u_w, "V4UiV4UiIi", "nc") +BUILTIN(__builtin_msa_mini_u_d, "V2ULLiV2ULLiIi", "nc") + +BUILTIN(__builtin_msa_mod_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_mod_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_mod_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_mod_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_mod_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_mod_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_mod_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_mod_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_move_v, "V16ScV16Sc", "nc") + +BUILTIN(__builtin_msa_msub_q_h, "V8SsV8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_msub_q_w, "V4SiV4SiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_msubr_q_h, "V8SsV8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_msubr_q_w, "V4SiV4SiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_msubv_b, "V16ScV16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_msubv_h, "V8SsV8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_msubv_w, "V4SiV4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_msubv_d, "V2SLLiV2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_mul_q_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_mul_q_w, "V4SiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_mulr_q_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_mulr_q_w, "V4SiV4SiV4Si", "nc") + +BUILTIN(__builtin_msa_mulv_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_mulv_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_mulv_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_mulv_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_nloc_b, "V16ScV16Sc", "nc") +BUILTIN(__builtin_msa_nloc_h, "V8SsV8Ss", "nc") +BUILTIN(__builtin_msa_nloc_w, "V4SiV4Si", "nc") +BUILTIN(__builtin_msa_nloc_d, "V2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_nlzc_b, "V16ScV16Sc", "nc") +BUILTIN(__builtin_msa_nlzc_h, "V8SsV8Ss", "nc") +BUILTIN(__builtin_msa_nlzc_w, "V4SiV4Si", "nc") +BUILTIN(__builtin_msa_nlzc_d, "V2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_nor_v, "V16UcV16UcV16Uc", "nc") + +BUILTIN(__builtin_msa_nori_b, "V16UcV16cIUi", "nc") + +BUILTIN(__builtin_msa_or_v, "V16UcV16UcV16Uc", "nc") + +BUILTIN(__builtin_msa_ori_b, "V16UcV16UcIUi", "nc") + +BUILTIN(__builtin_msa_pckev_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_pckev_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_pckev_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_pckev_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_pckod_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_pckod_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_pckod_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_pckod_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_pcnt_b, "V16ScV16Sc", "nc") +BUILTIN(__builtin_msa_pcnt_h, "V8SsV8Ss", "nc") +BUILTIN(__builtin_msa_pcnt_w, "V4SiV4Si", "nc") +BUILTIN(__builtin_msa_pcnt_d, "V2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_sat_s_b, "V16ScV16ScIUi", "nc") +BUILTIN(__builtin_msa_sat_s_h, "V8SsV8SsIUi", "nc") +BUILTIN(__builtin_msa_sat_s_w, "V4SiV4SiIUi", "nc") +BUILTIN(__builtin_msa_sat_s_d, "V2SLLiV2SLLiIUi", "nc") + +BUILTIN(__builtin_msa_sat_u_b, "V16UcV16UcIUi", "nc") +BUILTIN(__builtin_msa_sat_u_h, "V8UsV8UsIUi", "nc") +BUILTIN(__builtin_msa_sat_u_w, "V4UiV4UiIUi", "nc") +BUILTIN(__builtin_msa_sat_u_d, "V2ULLiV2ULLiIUi", "nc") + +BUILTIN(__builtin_msa_shf_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_shf_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_shf_w, "V4iV4iIUi", "nc") + +BUILTIN(__builtin_msa_sld_b, "V16cV16cV16cUi", "nc") +BUILTIN(__builtin_msa_sld_h, "V8sV8sV8sUi", "nc") +BUILTIN(__builtin_msa_sld_w, "V4iV4iV4iUi", "nc") +BUILTIN(__builtin_msa_sld_d, "V2LLiV2LLiV2LLiUi", "nc") + +BUILTIN(__builtin_msa_sldi_b, "V16cV16cV16cIUi", "nc") +BUILTIN(__builtin_msa_sldi_h, "V8sV8sV8sIUi", "nc") +BUILTIN(__builtin_msa_sldi_w, "V4iV4iV4iIUi", "nc") +BUILTIN(__builtin_msa_sldi_d, "V2LLiV2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_sll_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_sll_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_sll_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_sll_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_slli_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_slli_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_slli_w, "V4iV4iIUi", "nc") +BUILTIN(__builtin_msa_slli_d, "V2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_splat_b, "V16cV16cUi", "nc") +BUILTIN(__builtin_msa_splat_h, "V8sV8sUi", "nc") +BUILTIN(__builtin_msa_splat_w, "V4iV4iUi", "nc") +BUILTIN(__builtin_msa_splat_d, "V2LLiV2LLiUi", "nc") + +BUILTIN(__builtin_msa_splati_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_splati_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_splati_w, "V4iV4iIUi", "nc") +BUILTIN(__builtin_msa_splati_d, "V2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_sra_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_sra_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_sra_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_sra_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_srai_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_srai_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_srai_w, "V4iV4iIUi", "nc") +BUILTIN(__builtin_msa_srai_d, "V2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_srar_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_srar_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_srar_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_srar_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_srari_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_srari_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_srari_w, "V4iV4iIUi", "nc") +BUILTIN(__builtin_msa_srari_d, "V2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_srl_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_srl_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_srl_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_srl_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_srli_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_srli_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_srli_w, "V4iV4iIUi", "nc") +BUILTIN(__builtin_msa_srli_d, "V2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_srlr_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_srlr_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_srlr_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_srlr_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_srlri_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_srlri_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_srlri_w, "V4iV4iIUi", "nc") +BUILTIN(__builtin_msa_srlri_d, "V2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_st_b, "vV16Scv*Ii", "nc") +BUILTIN(__builtin_msa_st_h, "vV8Ssv*Ii", "nc") +BUILTIN(__builtin_msa_st_w, "vV4Siv*Ii", "nc") +BUILTIN(__builtin_msa_st_d, "vV2SLLiv*Ii", "nc") + +BUILTIN(__builtin_msa_subs_s_b, "V16ScV16ScV16Sc", "nc") +BUILTIN(__builtin_msa_subs_s_h, "V8SsV8SsV8Ss", "nc") +BUILTIN(__builtin_msa_subs_s_w, "V4SiV4SiV4Si", "nc") +BUILTIN(__builtin_msa_subs_s_d, "V2SLLiV2SLLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_subs_u_b, "V16UcV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_subs_u_h, "V8UsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_subs_u_w, "V4UiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_subs_u_d, "V2ULLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_subsus_u_b, "V16UcV16UcV16Sc", "nc") +BUILTIN(__builtin_msa_subsus_u_h, "V8UsV8UsV8Ss", "nc") +BUILTIN(__builtin_msa_subsus_u_w, "V4UiV4UiV4Si", "nc") +BUILTIN(__builtin_msa_subsus_u_d, "V2ULLiV2ULLiV2SLLi", "nc") + +BUILTIN(__builtin_msa_subsuu_s_b, "V16ScV16UcV16Uc", "nc") +BUILTIN(__builtin_msa_subsuu_s_h, "V8SsV8UsV8Us", "nc") +BUILTIN(__builtin_msa_subsuu_s_w, "V4SiV4UiV4Ui", "nc") +BUILTIN(__builtin_msa_subsuu_s_d, "V2SLLiV2ULLiV2ULLi", "nc") + +BUILTIN(__builtin_msa_subv_b, "V16cV16cV16c", "nc") +BUILTIN(__builtin_msa_subv_h, "V8sV8sV8s", "nc") +BUILTIN(__builtin_msa_subv_w, "V4iV4iV4i", "nc") +BUILTIN(__builtin_msa_subv_d, "V2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_subvi_b, "V16cV16cIUi", "nc") +BUILTIN(__builtin_msa_subvi_h, "V8sV8sIUi", "nc") +BUILTIN(__builtin_msa_subvi_w, "V4iV4iIUi", "nc") +BUILTIN(__builtin_msa_subvi_d, "V2LLiV2LLiIUi", "nc") + +BUILTIN(__builtin_msa_vshf_b, "V16cV16cV16cV16c", "nc") +BUILTIN(__builtin_msa_vshf_h, "V8sV8sV8sV8s", "nc") +BUILTIN(__builtin_msa_vshf_w, "V4iV4iV4iV4i", "nc") +BUILTIN(__builtin_msa_vshf_d, "V2LLiV2LLiV2LLiV2LLi", "nc") + +BUILTIN(__builtin_msa_xor_v, "V16cV16cV16c", "nc") + +BUILTIN(__builtin_msa_xori_b, "V16cV16cIUi", "nc") + +#undef BUILTIN diff --git a/tools/clang/include/clang/Basic/DiagnosticCommonKinds.td b/tools/clang/include/clang/Basic/DiagnosticCommonKinds.td index 82ca27b7..be5064fe 100644 --- a/tools/clang/include/clang/Basic/DiagnosticCommonKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticCommonKinds.td @@ -185,6 +185,8 @@ def note_invalid_subexpr_in_const_expr : Note< def err_target_unknown_triple : Error< "unknown target triple '%0', please use -triple or -arch">; def err_target_unknown_cpu : Error<"unknown target CPU '%0'">; +def err_target_unsupported_cpu_for_micromaxis : Error< + "micromaxis is not supported for target CPU '%0'">; def err_target_unsupported_cpu_for_micromips : Error< "micromips is not supported for target CPU '%0'">; def err_target_unknown_abi : Error<"unknown target ABI '%0'">; diff --git a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td index ec624a0c..730fb196 100644 --- a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -268,6 +268,10 @@ def err_anyx86_interrupt_called : Error< def warn_arm_interrupt_calling_convention : Warning< "call to function without interrupt attribute could clobber interruptee's VFP registers">, InGroup; +def warn_maxis_interrupt_attribute : Warning< + "MAXIS 'interrupt' attribute only applies to functions that have " + "%select{no parameters|a 'void' return type}0">, + InGroup; def warn_mips_interrupt_attribute : Warning< "MIPS 'interrupt' attribute only applies to functions that have " "%select{no parameters|a 'void' return type}0">, diff --git a/tools/clang/include/clang/Basic/TargetBuiltins.h b/tools/clang/include/clang/Basic/TargetBuiltins.h index 8f4f5e9a..0b583551 100644 --- a/tools/clang/include/clang/Basic/TargetBuiltins.h +++ b/tools/clang/include/clang/Basic/TargetBuiltins.h @@ -160,6 +160,16 @@ namespace clang { }; } + /// \brief MAXIS builtins + namespace Maxis { + enum { + LastTIBuiltin = clang::Builtin::FirstTSBuiltin-1, +#define BUILTIN(ID, TYPE, ATTRS) BI##ID, +#include "clang/Basic/BuiltinsMaxis.def" + LastTSBuiltin + }; + } + /// \brief MIPS builtins namespace Mips { enum { diff --git a/tools/clang/include/clang/Basic/TargetCXXABI.h b/tools/clang/include/clang/Basic/TargetCXXABI.h index 7fb1f826..825b4e0e 100644 --- a/tools/clang/include/clang/Basic/TargetCXXABI.h +++ b/tools/clang/include/clang/Basic/TargetCXXABI.h @@ -83,6 +83,12 @@ class TargetCXXABI { /// - guard variables are smaller. GenericAArch64, + /// The generic Maxis ABI is a modified version of the Itanium ABI. + /// + /// At the moment, only change from the generic ABI in this case is: + /// - representation of member function pointers adjusted as in ARM. + GenericMAXIS, + /// The generic Mips ABI is a modified version of the Itanium ABI. /// /// At the moment, only change from the generic ABI in this case is: @@ -140,6 +146,7 @@ class TargetCXXABI { case iOS: case iOS64: case WatchOS: + case GenericMAXIS: case GenericMIPS: case WebAssembly: return true; @@ -159,6 +166,7 @@ class TargetCXXABI { case iOS: case iOS64: case WatchOS: + case GenericMAXIS: case GenericMIPS: case WebAssembly: return false; @@ -185,6 +193,7 @@ class TargetCXXABI { return false; case GenericARM: case GenericAArch64: + case GenericMAXIS: case GenericMIPS: // TODO: ARM-style pointers to member functions put the discriminator in // the this adjustment, so they don't require functions to have any @@ -275,6 +284,7 @@ class TargetCXXABI { case GenericItanium: case iOS: // old iOS compilers did not follow this rule case Microsoft: + case GenericMAXIS: case GenericMIPS: return true; } @@ -321,6 +331,7 @@ class TargetCXXABI { case GenericAArch64: case GenericARM: case iOS: + case GenericMAXIS: case GenericMIPS: return UseTailPaddingUnlessPOD03; diff --git a/tools/clang/include/clang/Basic/TargetInfo.h b/tools/clang/include/clang/Basic/TargetInfo.h index 6ba58779..2b76991b 100644 --- a/tools/clang/include/clang/Basic/TargetInfo.h +++ b/tools/clang/include/clang/Basic/TargetInfo.h @@ -781,7 +781,7 @@ class TargetInfo : public RefCountedBase { virtual const char *getClobbers() const = 0; /// \brief Returns true if NaN encoding is IEEE 754-2008. - /// Only MIPS allows a different encoding. + /// Only MAXIS/MIPS allows a different encoding. virtual bool isNan2008() const { return true; } diff --git a/tools/clang/include/clang/Driver/Options.td b/tools/clang/include/clang/Driver/Options.td index ad72aef3..e4c6379e 100644 --- a/tools/clang/include/clang/Driver/Options.td +++ b/tools/clang/include/clang/Driver/Options.td @@ -349,7 +349,7 @@ def F : JoinedOrSeparate<["-"], "F">, Flags<[RenderJoined,CC1Option]>, HelpText<"Add directory to framework include search path">; def G : JoinedOrSeparate<["-"], "G">, Flags<[DriverOption]>, Group, MetaVarName<"">, HelpText<"Put objects of at most bytes " - "into small data section (MIPS / Hexagon)">; + "into small data section (MAXIS / MIPS / Hexagon)">; def G_EQ : Joined<["-"], "G=">, Flags<[DriverOption]>, Group, Alias; def H : Flag<["-"], "H">, Flags<[CC1Option]>, Group, HelpText<"Show header includes and nesting depth">; @@ -1976,6 +1976,10 @@ def mpie_copy_relocations : Flag<["-"], "mpie-copy-relocations">, Group def mno_pie_copy_relocations : Flag<["-"], "mno-pie-copy-relocations">, Group; def mfentry : Flag<["-"], "mfentry">, HelpText<"Insert calls to fentry at function entry (x86 only)">, Flags<[CC1Option]>, Group; +def maxis16 : Flag<["-"], "maxis16">, Group; +def mno_maxis16 : Flag<["-"], "mno-maxis16">, Group; +def mmicromaxis : Flag<["-"], "mmicromaxis">, Group; +def mno_micromaxis : Flag<["-"], "mno-micromaxis">, Group; def mips16 : Flag<["-"], "mips16">, Group; def mno_mips16 : Flag<["-"], "mno-mips16">, Group; def mmicromips : Flag<["-"], "mmicromips">, Group; @@ -2003,45 +2007,90 @@ def mmadd4 : Flag<["-"], "mmadd4">, Group, def mno_madd4 : Flag<["-"], "mno-madd4">, Group, HelpText<"Disable the generation of 4-operand madd.s, madd.d and related instructions.">; def mmsa : Flag<["-"], "mmsa">, Group, - HelpText<"Enable MSA ASE (MIPS only)">; + HelpText<"Enable MSA ASE (MAXIS/MIPS only)">; def mno_msa : Flag<["-"], "mno-msa">, Group, - HelpText<"Disable MSA ASE (MIPS only)">; + HelpText<"Disable MSA ASE (MAXIS/MIPS only)">; def mmt : Flag<["-"], "mmt">, Group, - HelpText<"Enable MT ASE (MIPS only)">; + HelpText<"Enable MT ASE (MAXIS/MIPS only)">; def mno_mt : Flag<["-"], "mno-mt">, Group, - HelpText<"Disable MT ASE (MIPS only)">; + HelpText<"Disable MT ASE (MAXIS/MIPS only)">; def mfp64 : Flag<["-"], "mfp64">, Group, - HelpText<"Use 64-bit floating point registers (MIPS only)">; + HelpText<"Use 64-bit floating point registers (MAXIS/MIPS only)">; def mfp32 : Flag<["-"], "mfp32">, Group, - HelpText<"Use 32-bit floating point registers (MIPS only)">; + HelpText<"Use 32-bit floating point registers (MAXIS/MIPS only)">; def mgpopt : Flag<["-"], "mgpopt">, Group, HelpText<"Use GP relative accesses for symbols known to be in a small" - " data section (MIPS)">; + " data section (MAXIS/MIPS)">; def mno_gpopt : Flag<["-"], "mno-gpopt">, Group, HelpText<"Do not use GP relative accesses for symbols known to be in a small" - " data section (MIPS)">; + " data section (MAXIS/MIPS)">; def mlocal_sdata : Flag<["-"], "mlocal-sdata">, Group, - HelpText<"Extend the -G behaviour to object local data (MIPS)">; + HelpText<"Extend the -G behaviour to object local data (MAXIS/MIPS)">; def mno_local_sdata : Flag<["-"], "mno-local-sdata">, Group, - HelpText<"Do not extend the -G behaviour to object local data (MIPS)">; + HelpText<"Do not extend the -G behaviour to object local data (MAXIS/MIPS)">; def mextern_sdata : Flag<["-"], "mextern-sdata">, Group, HelpText<"Assume that externally defined data is in the small data if it" - " meets the -G threshold (MIPS)">; + " meets the -G threshold (MAXIS/MIPS)">; def mno_extern_sdata : Flag<["-"], "mno-extern-sdata">, Group, HelpText<"Do not assume that externally defined data is in the small data if" - " it meets the -G threshold (MIPS)">; + " it meets the -G threshold (MAXIS/MIPS)">; def membedded_data : Flag<["-"], "membedded-data">, Group, HelpText<"Place constants in the .rodata section instead of the .sdata " - "section even if they meet the -G threshold (MIPS)">; + "section even if they meet the -G threshold (MAXIS/MIPS)">; def mno_embedded_data : Flag<["-"], "mno-embedded-data">, Group, HelpText<"Do not place constants in the .rodata section instead of the " - ".sdata if they meet the -G threshold (MIPS)">; + ".sdata if they meet the -G threshold (MAXIS/MIPS)">; def mnan_EQ : Joined<["-"], "mnan=">, Group; def mabs_EQ : Joined<["-"], "mabs=">, Group; def mabicalls : Flag<["-"], "mabicalls">, Group, - HelpText<"Enable SVR4-style position-independent code (Mips only)">; + HelpText<"Enable SVR4-style position-independent code (Maxis/Mips only)">; def mno_abicalls : Flag<["-"], "mno-abicalls">, Group, - HelpText<"Disable SVR4-style position-independent code (Mips only)">; + HelpText<"Disable SVR4-style position-independent code (Maxis/Mips only)">; +def maxis1 : Flag<["-"], "maxis1">, + Alias, AliasArgs<["maxis1"]>, + HelpText<"Equivalent to -march=maxis1">, Flags<[HelpHidden]>; +def maxis2 : Flag<["-"], "maxis2">, + Alias, AliasArgs<["maxis2"]>, + HelpText<"Equivalent to -march=maxis2">, Flags<[HelpHidden]>; +def maxis3 : Flag<["-"], "maxis3">, + Alias, AliasArgs<["maxis3"]>, + HelpText<"Equivalent to -march=maxis3">, Flags<[HelpHidden]>; +def maxis4 : Flag<["-"], "maxis4">, + Alias, AliasArgs<["maxis4"]>, + HelpText<"Equivalent to -march=maxis4">, Flags<[HelpHidden]>; +def maxis5 : Flag<["-"], "maxis5">, + Alias, AliasArgs<["maxis5"]>, + HelpText<"Equivalent to -march=maxis5">, Flags<[HelpHidden]>; +def maxis32 : Flag<["-"], "maxis32">, + Alias, AliasArgs<["maxis32"]>, + HelpText<"Equivalent to -march=maxis32">, Flags<[HelpHidden]>; +def maxis32r2 : Flag<["-"], "maxis32r2">, + Alias, AliasArgs<["maxis32r2"]>, + HelpText<"Equivalent to -march=maxis32r2">, Flags<[HelpHidden]>; +def maxis32r3 : Flag<["-"], "maxis32r3">, + Alias, AliasArgs<["maxis32r3"]>, + HelpText<"Equivalent to -march=maxis32r3">, Flags<[HelpHidden]>; +def maxis32r5 : Flag<["-"], "maxis32r5">, + Alias, AliasArgs<["maxis32r5"]>, + HelpText<"Equivalent to -march=maxis32r5">, Flags<[HelpHidden]>; +def maxis32r6 : Flag<["-"], "maxis32r6">, + Alias, AliasArgs<["maxis32r6"]>, + HelpText<"Equivalent to -march=maxis32r6">, Flags<[HelpHidden]>; +def maxis64 : Flag<["-"], "maxis64">, + Alias, AliasArgs<["maxis64"]>, + HelpText<"Equivalent to -march=maxis64">, Flags<[HelpHidden]>; +def maxis64r2 : Flag<["-"], "maxis64r2">, + Alias, AliasArgs<["maxis64r2"]>, + HelpText<"Equivalent to -march=maxis64r2">, Flags<[HelpHidden]>; +def maxis64r3 : Flag<["-"], "maxis64r3">, + Alias, AliasArgs<["maxis64r3"]>, + HelpText<"Equivalent to -march=maxis64r3">, Flags<[HelpHidden]>; +def maxis64r5 : Flag<["-"], "maxis64r5">, + Alias, AliasArgs<["maxis64r5"]>, + HelpText<"Equivalent to -march=maxis64r5">, Flags<[HelpHidden]>; +def maxis64r6 : Flag<["-"], "maxis64r6">, + Alias, AliasArgs<["maxis64r6"]>, + HelpText<"Equivalent to -march=maxis64r6">, Flags<[HelpHidden]>; def mips1 : Flag<["-"], "mips1">, Alias, AliasArgs<["mips1"]>, HelpText<"Equivalent to -march=mips1">, Flags<[HelpHidden]>; diff --git a/tools/clang/include/clang/Sema/Sema.h b/tools/clang/include/clang/Sema/Sema.h index 055d14f2..73ac2784 100644 --- a/tools/clang/include/clang/Sema/Sema.h +++ b/tools/clang/include/clang/Sema/Sema.h @@ -10309,6 +10309,7 @@ class Sema { bool CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckAArch64BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); + bool CheckMaxisBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckMipsBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckSystemZBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall); bool CheckX86BuiltinRoundingOrSAE(unsigned BuiltinID, CallExpr *TheCall); diff --git a/tools/clang/include/clang/module.modulemap b/tools/clang/include/clang/module.modulemap index 4097ad2d..aeaab8ee 100644 --- a/tools/clang/include/clang/module.modulemap +++ b/tools/clang/include/clang/module.modulemap @@ -31,6 +31,7 @@ module Clang_Basic { textual header "Basic/Builtins.def" textual header "Basic/BuiltinsHexagon.def" textual header "Basic/BuiltinsLe64.def" + textual header "Basic/BuiltinsMaxis.def" textual header "Basic/BuiltinsMips.def" textual header "Basic/BuiltinsNEON.def" textual header "Basic/BuiltinsNios2.def" diff --git a/tools/clang/lib/AST/ASTContext.cpp b/tools/clang/lib/AST/ASTContext.cpp index c73ae9ef..e793eb6e 100644 --- a/tools/clang/lib/AST/ASTContext.cpp +++ b/tools/clang/lib/AST/ASTContext.cpp @@ -735,6 +735,7 @@ CXXABI *ASTContext::createCXXABI(const TargetInfo &T) { case TargetCXXABI::iOS64: case TargetCXXABI::WatchOS: case TargetCXXABI::GenericAArch64: + case TargetCXXABI::GenericMAXIS: case TargetCXXABI::GenericMIPS: case TargetCXXABI::GenericItanium: case TargetCXXABI::WebAssembly: @@ -9536,6 +9537,7 @@ MangleContext *ASTContext::createMangleContext() { case TargetCXXABI::GenericAArch64: case TargetCXXABI::GenericItanium: case TargetCXXABI::GenericARM: + case TargetCXXABI::GenericMAXIS: case TargetCXXABI::GenericMIPS: case TargetCXXABI::iOS: case TargetCXXABI::iOS64: diff --git a/tools/clang/lib/AST/ExprConstant.cpp b/tools/clang/lib/AST/ExprConstant.cpp index 8d9b3c3b..4a0b69ae 100644 --- a/tools/clang/lib/AST/ExprConstant.cpp +++ b/tools/clang/lib/AST/ExprConstant.cpp @@ -9149,9 +9149,9 @@ static bool TryEvaluateBuiltinNaN(const ASTContext &Context, Result = llvm::APFloat::getQNaN(Sem, false, &fill); } else { // Prior to IEEE 754-2008, architectures were allowed to choose whether - // the first bit of their significand was set for qNaN or sNaN. MIPS chose + // the first bit of their significand was set for qNaN or sNaN. MAXIS/MIPS chose // a different encoding to what became a standard in 2008, and for pre- - // 2008 revisions, MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as + // 2008 revisions, MAXIS/MIPS interpreted sNaN-2008 as qNan and qNaN-2008 as // sNaN. This is now known as "legacy NaN" encoding. if (SNaN) Result = llvm::APFloat::getQNaN(Sem, false, &fill); diff --git a/tools/clang/lib/Basic/CMakeLists.txt b/tools/clang/lib/Basic/CMakeLists.txt index d0c9b902..ae40f078 100644 --- a/tools/clang/lib/Basic/CMakeLists.txt +++ b/tools/clang/lib/Basic/CMakeLists.txt @@ -77,6 +77,7 @@ add_clang_library(clangBasic Targets/Lanai.cpp Targets/Le64.cpp Targets/MSP430.cpp + Targets/Maxis.cpp Targets/Mips.cpp Targets/NVPTX.cpp Targets/Nios2.cpp diff --git a/tools/clang/lib/Basic/Targets.cpp b/tools/clang/lib/Basic/Targets.cpp index 7deebc06..4185a24a 100644 --- a/tools/clang/lib/Basic/Targets.cpp +++ b/tools/clang/lib/Basic/Targets.cpp @@ -23,6 +23,7 @@ #include "Targets/Lanai.h" #include "Targets/Le64.h" #include "Targets/MSP430.h" +#include "Targets/Maxis.h" #include "Targets/Mips.h" #include "Targets/NVPTX.h" #include "Targets/Nios2.h" @@ -244,6 +245,68 @@ TargetInfo *AllocateTarget(const llvm::Triple &Triple, case llvm::Triple::nios2: return new LinuxTargetInfo(Triple, Opts); + case llvm::Triple::maxis: + switch (os) { + case llvm::Triple::Linux: + return new LinuxTargetInfo(Triple, Opts); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo(Triple, Opts); + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo(Triple, Opts); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo(Triple, Opts); + default: + return new MaxisTargetInfo(Triple, Opts); + } + + case llvm::Triple::maxisel: + switch (os) { + case llvm::Triple::Linux: + return new LinuxTargetInfo(Triple, Opts); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo(Triple, Opts); + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo(Triple, Opts); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo(Triple, Opts); + case llvm::Triple::NaCl: + return new NaClTargetInfo(Triple, Opts); + default: + return new MaxisTargetInfo(Triple, Opts); + } + + case llvm::Triple::maxis64: + switch (os) { + case llvm::Triple::Linux: + return new LinuxTargetInfo(Triple, Opts); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo(Triple, Opts); + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo(Triple, Opts); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo(Triple, Opts); + case llvm::Triple::OpenBSD: + return new OpenBSDTargetInfo(Triple, Opts); + default: + return new MaxisTargetInfo(Triple, Opts); + } + + case llvm::Triple::maxis64el: + switch (os) { + case llvm::Triple::Linux: + return new LinuxTargetInfo(Triple, Opts); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo(Triple, Opts); + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo(Triple, Opts); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo(Triple, Opts); + case llvm::Triple::OpenBSD: + return new OpenBSDTargetInfo(Triple, Opts); + default: + return new MaxisTargetInfo(Triple, Opts); + } + case llvm::Triple::mips: switch (os) { case llvm::Triple::Linux: diff --git a/tools/clang/lib/Basic/Targets/Maxis.cpp b/tools/clang/lib/Basic/Targets/Maxis.cpp new file mode 100644 index 00000000..ffecf7f5 --- /dev/null +++ b/tools/clang/lib/Basic/Targets/Maxis.cpp @@ -0,0 +1,252 @@ +//===--- Maxis.cpp - Implement Maxis target feature support -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements Maxis TargetInfo objects. +// +//===----------------------------------------------------------------------===// + +#include "Maxis.h" +#include "Targets.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/MacroBuilder.h" +#include "clang/Basic/TargetBuiltins.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; +using namespace clang::targets; + +const Builtin::Info MaxisTargetInfo::BuiltinInfo[] = { +#define BUILTIN(ID, TYPE, ATTRS) \ + {#ID, TYPE, ATTRS, nullptr, ALL_LANGUAGES, nullptr}, +#define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) \ + {#ID, TYPE, ATTRS, HEADER, ALL_LANGUAGES, nullptr}, +#include "clang/Basic/BuiltinsMaxis.def" +}; + +bool MaxisTargetInfo::processorSupportsGPR64() const { + return llvm::StringSwitch(CPU) + .Case("maxis3", true) + .Case("maxis4", true) + .Case("maxis5", true) + .Case("maxis64", true) + .Case("maxis64r2", true) + .Case("maxis64r3", true) + .Case("maxis64r5", true) + .Case("maxis64r6", true) + .Case("octeon", true) + .Default(false); + return false; +} + +bool MaxisTargetInfo::isValidCPUName(StringRef Name) const { + return llvm::StringSwitch(Name) + .Case("maxis1", true) + .Case("maxis2", true) + .Case("maxis3", true) + .Case("maxis4", true) + .Case("maxis5", true) + .Case("maxis32", true) + .Case("maxis32r2", true) + .Case("maxis32r3", true) + .Case("maxis32r5", true) + .Case("maxis32r6", true) + .Case("maxis64", true) + .Case("maxis64r2", true) + .Case("maxis64r3", true) + .Case("maxis64r5", true) + .Case("maxis64r6", true) + .Case("octeon", true) + .Case("p5600", true) + .Default(false); +} + +void MaxisTargetInfo::getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + if (BigEndian) { + DefineStd(Builder, "MAXISEB", Opts); + Builder.defineMacro("_MAXISEB"); + } else { + DefineStd(Builder, "MAXISEL", Opts); + Builder.defineMacro("_MAXISEL"); + } + + Builder.defineMacro("__maxis__"); + Builder.defineMacro("_maxis"); + if (Opts.GNUMode) + Builder.defineMacro("maxis"); + + if (ABI == "o32") { + Builder.defineMacro("__maxis", "32"); + Builder.defineMacro("_MAXIS_ISA", "_MAXIS_ISA_MAXIS32"); + } else { + Builder.defineMacro("__maxis", "64"); + Builder.defineMacro("__maxis64"); + Builder.defineMacro("__maxis64__"); + Builder.defineMacro("_MAXIS_ISA", "_MAXIS_ISA_MAXIS64"); + } + + const std::string ISARev = llvm::StringSwitch(getCPU()) + .Cases("maxis32", "maxis64", "1") + .Cases("maxis32r2", "maxis64r2", "2") + .Cases("maxis32r3", "maxis64r3", "3") + .Cases("maxis32r5", "maxis64r5", "5") + .Cases("maxis32r6", "maxis64r6", "6") + .Default(""); + if (!ISARev.empty()) + Builder.defineMacro("__maxis_isa_rev", ISARev); + + if (ABI == "o32") { + Builder.defineMacro("__maxis_o32"); + Builder.defineMacro("_ABIO32", "1"); + Builder.defineMacro("_MAXIS_SIM", "_ABIO32"); + } else if (ABI == "n32") { + Builder.defineMacro("__maxis_n32"); + Builder.defineMacro("_ABIN32", "2"); + Builder.defineMacro("_MAXIS_SIM", "_ABIN32"); + } else if (ABI == "n64") { + Builder.defineMacro("__maxis_n64"); + Builder.defineMacro("_ABI64", "3"); + Builder.defineMacro("_MAXIS_SIM", "_ABI64"); + } else + llvm_unreachable("Invalid ABI."); + + if (!IsNoABICalls) { + Builder.defineMacro("__maxis_abicalls"); + if (CanUseBSDABICalls) + Builder.defineMacro("__ABICALLS__"); + } + + Builder.defineMacro("__REGISTER_PREFIX__", ""); + + switch (FloatABI) { + case HardFloat: + Builder.defineMacro("__maxis_hard_float", Twine(1)); + break; + case SoftFloat: + Builder.defineMacro("__maxis_soft_float", Twine(1)); + break; + } + + if (IsSingleFloat) + Builder.defineMacro("__maxis_single_float", Twine(1)); + + Builder.defineMacro("__maxis_fpr", HasFP64 ? Twine(64) : Twine(32)); + Builder.defineMacro("_MAXIS_FPSET", + Twine(32 / (HasFP64 || IsSingleFloat ? 1 : 2))); + + if (IsMaxis16) + Builder.defineMacro("__maxis16", Twine(1)); + + if (IsMicromaxis) + Builder.defineMacro("__maxis_micromaxis", Twine(1)); + + if (IsNan2008) + Builder.defineMacro("__maxis_nan2008", Twine(1)); + + if (IsAbs2008) + Builder.defineMacro("__maxis_abs2008", Twine(1)); + + switch (DspRev) { + default: + break; + case DSP1: + Builder.defineMacro("__maxis_dsp_rev", Twine(1)); + Builder.defineMacro("__maxis_dsp", Twine(1)); + break; + case DSP2: + Builder.defineMacro("__maxis_dsp_rev", Twine(2)); + Builder.defineMacro("__maxis_dspr2", Twine(1)); + Builder.defineMacro("__maxis_dsp", Twine(1)); + break; + } + + if (HasMSA) + Builder.defineMacro("__maxis_msa", Twine(1)); + + if (DisableMadd4) + Builder.defineMacro("__maxis_no_madd4", Twine(1)); + + Builder.defineMacro("_MAXIS_SZPTR", Twine(getPointerWidth(0))); + Builder.defineMacro("_MAXIS_SZINT", Twine(getIntWidth())); + Builder.defineMacro("_MAXIS_SZLONG", Twine(getLongWidth())); + + Builder.defineMacro("_MAXIS_ARCH", "\"" + CPU + "\""); + Builder.defineMacro("_MAXIS_ARCH_" + StringRef(CPU).upper()); + + // These shouldn't be defined for MAXIS-I but there's no need to check + // for that since MAXIS-I isn't supported. + Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_1"); + Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2"); + Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4"); + + // 32-bit MAXIS processors don't have the necessary lld/scd instructions + // found in 64-bit processors. In the case of O32 on a 64-bit processor, + // the instructions exist but using them violates the ABI since they + // require 64-bit GPRs and O32 only supports 32-bit GPRs. + if (ABI == "n32" || ABI == "n64") + Builder.defineMacro("__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8"); +} + +bool MaxisTargetInfo::hasFeature(StringRef Feature) const { + return llvm::StringSwitch(Feature) + .Case("maxis", true) + .Case("fp64", HasFP64) + .Default(false); +} + +ArrayRef MaxisTargetInfo::getTargetBuiltins() const { + return llvm::makeArrayRef(BuiltinInfo, clang::Maxis::LastTSBuiltin - + Builtin::FirstTSBuiltin); +} + +bool MaxisTargetInfo::validateTarget(DiagnosticsEngine &Diags) const { + // microMAXIS64R6 backend was removed. + if ((getTriple().getArch() == llvm::Triple::maxis64 || + getTriple().getArch() == llvm::Triple::maxis64el) && + IsMicromaxis && (ABI == "n32" || ABI == "n64")) { + Diags.Report(diag::err_target_unsupported_cpu_for_micromaxis) << CPU; + return false; + } + // FIXME: It's valid to use O32 on a 64-bit CPU but the backend can't handle + // this yet. It's better to fail here than on the backend assertion. + if (processorSupportsGPR64() && ABI == "o32") { + Diags.Report(diag::err_target_unsupported_abi) << ABI << CPU; + return false; + } + + // 64-bit ABI's require 64-bit CPU's. + if (!processorSupportsGPR64() && (ABI == "n32" || ABI == "n64")) { + Diags.Report(diag::err_target_unsupported_abi) << ABI << CPU; + return false; + } + + // FIXME: It's valid to use O32 on a maxis64/maxis64el triple but the backend + // can't handle this yet. It's better to fail here than on the + // backend assertion. + if ((getTriple().getArch() == llvm::Triple::maxis64 || + getTriple().getArch() == llvm::Triple::maxis64el) && + ABI == "o32") { + Diags.Report(diag::err_target_unsupported_abi_for_triple) + << ABI << getTriple().str(); + return false; + } + + // FIXME: It's valid to use N32/N64 on a maxis/maxisel triple but the backend + // can't handle this yet. It's better to fail here than on the + // backend assertion. + if ((getTriple().getArch() == llvm::Triple::maxis || + getTriple().getArch() == llvm::Triple::maxisel) && + (ABI == "n32" || ABI == "n64")) { + Diags.Report(diag::err_target_unsupported_abi_for_triple) + << ABI << getTriple().str(); + return false; + } + + return true; +} diff --git a/tools/clang/lib/Basic/Targets/Maxis.h b/tools/clang/lib/Basic/Targets/Maxis.h new file mode 100644 index 00000000..a4b8ce45 --- /dev/null +++ b/tools/clang/lib/Basic/Targets/Maxis.h @@ -0,0 +1,397 @@ +//===--- Maxis.h - Declare Maxis target feature support -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares Maxis TargetInfo objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_BASIC_TARGETS_MAXIS_H +#define LLVM_CLANG_LIB_BASIC_TARGETS_MAXIS_H + +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Support/Compiler.h" + +namespace clang { +namespace targets { + +class LLVM_LIBRARY_VISIBILITY MaxisTargetInfo : public TargetInfo { + void setDataLayout() { + StringRef Layout; + + if (ABI == "o32") + Layout = "m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"; + else if (ABI == "n32") + Layout = "m:e-p:32:32-i8:8:32-i16:16:32-i64:64-n32:64-S128"; + else if (ABI == "n64") + Layout = "m:e-i8:8:32-i16:16:32-i64:64-n32:64-S128"; + else + llvm_unreachable("Invalid ABI"); + + if (BigEndian) + resetDataLayout(("E-" + Layout).str()); + else + resetDataLayout(("e-" + Layout).str()); + } + + static const Builtin::Info BuiltinInfo[]; + std::string CPU; + bool IsMaxis16; + bool IsMicromaxis; + bool IsNan2008; + bool IsAbs2008; + bool IsSingleFloat; + bool IsNoABICalls; + bool CanUseBSDABICalls; + enum MaxisFloatABI { HardFloat, SoftFloat } FloatABI; + enum DspRevEnum { NoDSP, DSP1, DSP2 } DspRev; + bool HasMSA; + bool DisableMadd4; + +protected: + bool HasFP64; + std::string ABI; + +public: + MaxisTargetInfo(const llvm::Triple &Triple, const TargetOptions &) + : TargetInfo(Triple), IsMaxis16(false), IsMicromaxis(false), + IsNan2008(false), IsAbs2008(false), IsSingleFloat(false), + IsNoABICalls(false), CanUseBSDABICalls(false), FloatABI(HardFloat), + DspRev(NoDSP), HasMSA(false), DisableMadd4(false), HasFP64(false) { + TheCXXABI.set(TargetCXXABI::GenericMAXIS); + + setABI((getTriple().getArch() == llvm::Triple::maxis || + getTriple().getArch() == llvm::Triple::maxisel) + ? "o32" + : "n64"); + + CPU = ABI == "o32" ? "maxis32r2" : "maxis64r2"; + + CanUseBSDABICalls = Triple.getOS() == llvm::Triple::FreeBSD || + Triple.getOS() == llvm::Triple::OpenBSD; + } + + bool isIEEE754_2008Default() const { + return CPU == "maxis32r6" || CPU == "maxis64r6"; + } + + bool isFP64Default() const { + return CPU == "maxis32r6" || ABI == "n32" || ABI == "n64" || ABI == "64"; + } + + bool isNan2008() const override { return IsNan2008; } + + bool processorSupportsGPR64() const; + + StringRef getABI() const override { return ABI; } + + bool setABI(const std::string &Name) override { + if (Name == "o32") { + setO32ABITypes(); + ABI = Name; + return true; + } + + if (Name == "n32") { + setN32ABITypes(); + ABI = Name; + return true; + } + if (Name == "n64") { + setN64ABITypes(); + ABI = Name; + return true; + } + return false; + } + + void setO32ABITypes() { + Int64Type = SignedLongLong; + IntMaxType = Int64Type; + LongDoubleFormat = &llvm::APFloat::IEEEdouble(); + LongDoubleWidth = LongDoubleAlign = 64; + LongWidth = LongAlign = 32; + MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 32; + PointerWidth = PointerAlign = 32; + PtrDiffType = SignedInt; + SizeType = UnsignedInt; + SuitableAlign = 64; + } + + void setN32N64ABITypes() { + LongDoubleWidth = LongDoubleAlign = 128; + LongDoubleFormat = &llvm::APFloat::IEEEquad(); + if (getTriple().getOS() == llvm::Triple::FreeBSD) { + LongDoubleWidth = LongDoubleAlign = 64; + LongDoubleFormat = &llvm::APFloat::IEEEdouble(); + } + MaxAtomicPromoteWidth = MaxAtomicInlineWidth = 64; + SuitableAlign = 128; + } + + void setN64ABITypes() { + setN32N64ABITypes(); + if (getTriple().getOS() == llvm::Triple::OpenBSD) { + Int64Type = SignedLongLong; + } else { + Int64Type = SignedLong; + } + IntMaxType = Int64Type; + LongWidth = LongAlign = 64; + PointerWidth = PointerAlign = 64; + PtrDiffType = SignedLong; + SizeType = UnsignedLong; + } + + void setN32ABITypes() { + setN32N64ABITypes(); + Int64Type = SignedLongLong; + IntMaxType = Int64Type; + LongWidth = LongAlign = 32; + PointerWidth = PointerAlign = 32; + PtrDiffType = SignedInt; + SizeType = UnsignedInt; + } + + bool isValidCPUName(StringRef Name) const override; + + bool setCPU(const std::string &Name) override { + CPU = Name; + return isValidCPUName(Name); + } + + const std::string &getCPU() const { return CPU; } + bool + initFeatureMap(llvm::StringMap &Features, DiagnosticsEngine &Diags, + StringRef CPU, + const std::vector &FeaturesVec) const override { + if (CPU.empty()) + CPU = getCPU(); + if (CPU == "octeon") + Features["maxis64r2"] = Features["cnmaxis"] = true; + else + Features[CPU] = true; + return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec); + } + + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override; + + ArrayRef getTargetBuiltins() const override; + + bool hasFeature(StringRef Feature) const override; + + BuiltinVaListKind getBuiltinVaListKind() const override { + return TargetInfo::VoidPtrBuiltinVaList; + } + + ArrayRef getGCCRegNames() const override { + static const char *const GCCRegNames[] = { + // CPU register names + // Must match second column of GCCRegAliases + "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", + "$11", "$12", "$13", "$14", "$15", "$16", "$17", "$18", "$19", "$20", + "$21", "$22", "$23", "$24", "$25", "$26", "$27", "$28", "$29", "$30", + "$31", + // Floating point register names + "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7", "$f8", "$f9", + "$f10", "$f11", "$f12", "$f13", "$f14", "$f15", "$f16", "$f17", "$f18", + "$f19", "$f20", "$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27", + "$f28", "$f29", "$f30", "$f31", + // Hi/lo and condition register names + "hi", "lo", "", "$fcc0", "$fcc1", "$fcc2", "$fcc3", "$fcc4", "$fcc5", + "$fcc6", "$fcc7", "$ac1hi", "$ac1lo", "$ac2hi", "$ac2lo", "$ac3hi", + "$ac3lo", + // MSA register names + "$w0", "$w1", "$w2", "$w3", "$w4", "$w5", "$w6", "$w7", "$w8", "$w9", + "$w10", "$w11", "$w12", "$w13", "$w14", "$w15", "$w16", "$w17", "$w18", + "$w19", "$w20", "$w21", "$w22", "$w23", "$w24", "$w25", "$w26", "$w27", + "$w28", "$w29", "$w30", "$w31", + // MSA control register names + "$msair", "$msacsr", "$msaaccess", "$msasave", "$msamodify", + "$msarequest", "$msamap", "$msaunmap" + }; + return llvm::makeArrayRef(GCCRegNames); + } + + bool validateAsmConstraint(const char *&Name, + TargetInfo::ConstraintInfo &Info) const override { + switch (*Name) { + default: + return false; + case 'r': // CPU registers. + case 'd': // Equivalent to "r" unless generating MAXIS16 code. + case 'y': // Equivalent to "r", backward compatibility only. + case 'f': // floating-point registers. + case 'c': // $25 for indirect jumps + case 'l': // lo register + case 'x': // hilo register pair + Info.setAllowsRegister(); + return true; + case 'I': // Signed 16-bit constant + case 'J': // Integer 0 + case 'K': // Unsigned 16-bit constant + case 'L': // Signed 32-bit constant, lower 16-bit zeros (for lui) + case 'M': // Constants not loadable via lui, addiu, or ori + case 'N': // Constant -1 to -65535 + case 'O': // A signed 15-bit constant + case 'P': // A constant between 1 go 65535 + return true; + case 'R': // An address that can be used in a non-macro load or store + Info.setAllowsMemory(); + return true; + case 'Z': + if (Name[1] == 'C') { // An address usable by ll, and sc. + Info.setAllowsMemory(); + Name++; // Skip over 'Z'. + return true; + } + return false; + } + } + + std::string convertConstraint(const char *&Constraint) const override { + std::string R; + switch (*Constraint) { + case 'Z': // Two-character constraint; add "^" hint for later parsing. + if (Constraint[1] == 'C') { + R = std::string("^") + std::string(Constraint, 2); + Constraint++; + return R; + } + break; + } + return TargetInfo::convertConstraint(Constraint); + } + + const char *getClobbers() const override { + // In GCC, $1 is not widely used in generated code (it's used only in a few + // specific situations), so there is no real need for users to add it to + // the clobbers list if they want to use it in their inline assembly code. + // + // In LLVM, $1 is treated as a normal GPR and is always allocatable during + // code generation, so using it in inline assembly without adding it to the + // clobbers list can cause conflicts between the inline assembly code and + // the surrounding generated code. + // + // Another problem is that LLVM is allowed to choose $1 for inline assembly + // operands, which will conflict with the ".set at" assembler option (which + // we use only for inline assembly, in order to maintain compatibility with + // GCC) and will also conflict with the user's usage of $1. + // + // The easiest way to avoid these conflicts and keep $1 as an allocatable + // register for generated code is to automatically clobber $1 for all inline + // assembly code. + // + // FIXME: We should automatically clobber $1 only for inline assembly code + // which actually uses it. This would allow LLVM to use $1 for inline + // assembly operands if the user's assembly code doesn't use it. + return "~{$1}"; + } + + bool handleTargetFeatures(std::vector &Features, + DiagnosticsEngine &Diags) override { + IsMaxis16 = false; + IsMicromaxis = false; + IsNan2008 = isIEEE754_2008Default(); + IsAbs2008 = isIEEE754_2008Default(); + IsSingleFloat = false; + FloatABI = HardFloat; + DspRev = NoDSP; + HasFP64 = isFP64Default(); + + for (const auto &Feature : Features) { + if (Feature == "+single-float") + IsSingleFloat = true; + else if (Feature == "+soft-float") + FloatABI = SoftFloat; + else if (Feature == "+maxis16") + IsMaxis16 = true; + else if (Feature == "+micromaxis") + IsMicromaxis = true; + else if (Feature == "+dsp") + DspRev = std::max(DspRev, DSP1); + else if (Feature == "+dspr2") + DspRev = std::max(DspRev, DSP2); + else if (Feature == "+msa") + HasMSA = true; + else if (Feature == "+nomadd4") + DisableMadd4 = true; + else if (Feature == "+fp64") + HasFP64 = true; + else if (Feature == "-fp64") + HasFP64 = false; + else if (Feature == "+nan2008") + IsNan2008 = true; + else if (Feature == "-nan2008") + IsNan2008 = false; + else if (Feature == "+abs2008") + IsAbs2008 = true; + else if (Feature == "-abs2008") + IsAbs2008 = false; + else if (Feature == "+noabicalls") + IsNoABICalls = true; + } + + setDataLayout(); + + return true; + } + + int getEHDataRegisterNumber(unsigned RegNo) const override { + if (RegNo == 0) + return 4; + if (RegNo == 1) + return 5; + return -1; + } + + bool isCLZForZeroUndef() const override { return false; } + + ArrayRef getGCCRegAliases() const override { + static const TargetInfo::GCCRegAlias O32RegAliases[] = { + {{"at"}, "$1"}, {{"v0"}, "$2"}, {{"v1"}, "$3"}, + {{"a0"}, "$4"}, {{"a1"}, "$5"}, {{"a2"}, "$6"}, + {{"a3"}, "$7"}, {{"t0"}, "$8"}, {{"t1"}, "$9"}, + {{"t2"}, "$10"}, {{"t3"}, "$11"}, {{"t4"}, "$12"}, + {{"t5"}, "$13"}, {{"t6"}, "$14"}, {{"t7"}, "$15"}, + {{"s0"}, "$16"}, {{"s1"}, "$17"}, {{"s2"}, "$18"}, + {{"s3"}, "$19"}, {{"s4"}, "$20"}, {{"s5"}, "$21"}, + {{"s6"}, "$22"}, {{"s7"}, "$23"}, {{"t8"}, "$24"}, + {{"t9"}, "$25"}, {{"k0"}, "$26"}, {{"k1"}, "$27"}, + {{"gp"}, "$28"}, {{"sp", "$sp"}, "$29"}, {{"fp", "$fp"}, "$30"}, + {{"ra"}, "$31"} + }; + static const TargetInfo::GCCRegAlias NewABIRegAliases[] = { + {{"at"}, "$1"}, {{"v0"}, "$2"}, {{"v1"}, "$3"}, + {{"a0"}, "$4"}, {{"a1"}, "$5"}, {{"a2"}, "$6"}, + {{"a3"}, "$7"}, {{"a4"}, "$8"}, {{"a5"}, "$9"}, + {{"a6"}, "$10"}, {{"a7"}, "$11"}, {{"t0"}, "$12"}, + {{"t1"}, "$13"}, {{"t2"}, "$14"}, {{"t3"}, "$15"}, + {{"s0"}, "$16"}, {{"s1"}, "$17"}, {{"s2"}, "$18"}, + {{"s3"}, "$19"}, {{"s4"}, "$20"}, {{"s5"}, "$21"}, + {{"s6"}, "$22"}, {{"s7"}, "$23"}, {{"t8"}, "$24"}, + {{"t9"}, "$25"}, {{"k0"}, "$26"}, {{"k1"}, "$27"}, + {{"gp"}, "$28"}, {{"sp", "$sp"}, "$29"}, {{"fp", "$fp"}, "$30"}, + {{"ra"}, "$31"} + }; + if (ABI == "o32") + return llvm::makeArrayRef(O32RegAliases); + return llvm::makeArrayRef(NewABIRegAliases); + } + + bool hasInt128Type() const override { return ABI == "n32" || ABI == "n64"; } + + bool validateTarget(DiagnosticsEngine &Diags) const override; +}; +} // namespace targets +} // namespace clang + +#endif // LLVM_CLANG_LIB_BASIC_TARGETS_MAXIS_H diff --git a/tools/clang/lib/Basic/Targets/Nios2.h b/tools/clang/lib/Basic/Targets/Nios2.h index aa02f8f6..21f42782 100644 --- a/tools/clang/lib/Basic/Targets/Nios2.h +++ b/tools/clang/lib/Basic/Targets/Nios2.h @@ -114,7 +114,7 @@ class LLVM_LIBRARY_VISIBILITY Nios2TargetInfo : public TargetInfo { return false; case 'r': // CPU registers. - case 'd': // Equivalent to "r" unless generating MIPS16 code. + case 'd': // Equivalent to "r" unless generating MAXIS/MIPS16 code. case 'y': // Equivalent to "r", backwards compatibility only. case 'f': // floating-point registers. case 'c': // $25 for indirect jumps diff --git a/tools/clang/lib/Basic/Targets/OSTargets.h b/tools/clang/lib/Basic/Targets/OSTargets.h index 5af63615..02c4f29a 100644 --- a/tools/clang/lib/Basic/Targets/OSTargets.h +++ b/tools/clang/lib/Basic/Targets/OSTargets.h @@ -204,6 +204,8 @@ class LLVM_LIBRARY_VISIBILITY FreeBSDTargetInfo : public OSTargetInfo { case llvm::Triple::x86_64: this->MCountName = ".mcount"; break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::ppc: @@ -324,6 +326,10 @@ class LLVM_LIBRARY_VISIBILITY LinuxTargetInfo : public OSTargetInfo { switch (Triple.getArch()) { default: break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -395,6 +401,8 @@ class LLVM_LIBRARY_VISIBILITY OpenBSDTargetInfo : public OSTargetInfo { default: this->MCountName = "__mcount"; break; + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::ppc: @@ -506,6 +514,8 @@ class LLVM_LIBRARY_VISIBILITY RTEMSTargetInfo : public OSTargetInfo { case llvm::Triple::x86: // this->MCountName = ".mcount"; break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::ppc: @@ -666,6 +676,8 @@ class LLVM_LIBRARY_VISIBILITY NaClTargetInfo : public OSTargetInfo { this->resetDataLayout("e-m:e-p:32:32-i64:64-n8:16:32-S128"); } else if (Triple.getArch() == llvm::Triple::x86_64) { this->resetDataLayout("e-m:e-p:32:32-i64:64-n8:16:32:64-S128"); + } else if (Triple.getArch() == llvm::Triple::maxisel) { + // Handled on maxis' setDataLayout. } else if (Triple.getArch() == llvm::Triple::mipsel) { // Handled on mips' setDataLayout. } else { diff --git a/tools/clang/lib/Basic/Targets/PNaCl.h b/tools/clang/lib/Basic/Targets/PNaCl.h index 922944e8..ae56179e 100644 --- a/tools/clang/lib/Basic/Targets/PNaCl.h +++ b/tools/clang/lib/Basic/Targets/PNaCl.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_LIB_BASIC_TARGETS_PNACL_H #define LLVM_CLANG_LIB_BASIC_TARGETS_PNACL_H +#include "Maxis.h" #include "Mips.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" @@ -71,6 +72,17 @@ class LLVM_LIBRARY_VISIBILITY PNaClTargetInfo : public TargetInfo { const char *getClobbers() const override { return ""; } }; +// We attempt to use PNaCl (le32) frontend and Maxis32EL backend. +class LLVM_LIBRARY_VISIBILITY NaClMaxis32TargetInfo : public MaxisTargetInfo { +public: + NaClMaxis32TargetInfo(const llvm::Triple &Triple, const TargetOptions &Opts) + : MaxisTargetInfo(Triple, Opts) {} + + BuiltinVaListKind getBuiltinVaListKind() const override { + return TargetInfo::PNaClABIBuiltinVaList; + } +}; + // We attempt to use PNaCl (le32) frontend and Mips32EL backend. class LLVM_LIBRARY_VISIBILITY NaClMips32TargetInfo : public MipsTargetInfo { public: diff --git a/tools/clang/lib/CodeGen/CodeGenModule.cpp b/tools/clang/lib/CodeGen/CodeGenModule.cpp index 21724866..81719576 100644 --- a/tools/clang/lib/CodeGen/CodeGenModule.cpp +++ b/tools/clang/lib/CodeGen/CodeGenModule.cpp @@ -74,6 +74,7 @@ static CGCXXABI *createCXXABI(CodeGenModule &CGM) { case TargetCXXABI::iOS: case TargetCXXABI::iOS64: case TargetCXXABI::WatchOS: + case TargetCXXABI::GenericMAXIS: case TargetCXXABI::GenericMIPS: case TargetCXXABI::GenericItanium: case TargetCXXABI::WebAssembly: diff --git a/tools/clang/lib/CodeGen/ItaniumCXXABI.cpp b/tools/clang/lib/CodeGen/ItaniumCXXABI.cpp index a3c2766d..a323eb09 100644 --- a/tools/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/tools/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -510,6 +510,9 @@ CodeGen::CGCXXABI *CodeGen::CreateItaniumCXXABI(CodeGenModule &CGM) { return new ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true, /* UseARMGuardVarABI = */ true); + case TargetCXXABI::GenericMAXIS: + return new ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true); + case TargetCXXABI::GenericMIPS: return new ItaniumCXXABI(CGM, /* UseARMMethodPtrABI = */ true); diff --git a/tools/clang/lib/CodeGen/TargetInfo.cpp b/tools/clang/lib/CodeGen/TargetInfo.cpp index abd63328..f362bb6f 100644 --- a/tools/clang/lib/CodeGen/TargetInfo.cpp +++ b/tools/clang/lib/CodeGen/TargetInfo.cpp @@ -389,7 +389,7 @@ bool TargetCodeGenInfo::isNoProtoCallVariadic(const CallArgList &args, const FunctionNoProtoType *fnType) const { // The following conventions are known to require this to be false: // x86_stdcall - // MIPS + // MAXIS/MIPS // For everything else, we just prefer false unless we opt out. return false; } @@ -6691,6 +6691,410 @@ void MSP430TargetCodeGenInfo::setTargetAttributes( } } +//===----------------------------------------------------------------------===// +// MAXIS ABI Implementation. This works for both little-endian and +// big-endian variants. +//===----------------------------------------------------------------------===// + +namespace { +class MaxisABIInfo : public ABIInfo { + bool IsO32; + unsigned MinABIStackAlignInBytes, StackAlignInBytes; + void CoerceToIntArgs(uint64_t TySize, + SmallVectorImpl &ArgList) const; + llvm::Type* HandleAggregates(QualType Ty, uint64_t TySize) const; + llvm::Type* returnAggregateInRegs(QualType RetTy, uint64_t Size) const; + llvm::Type* getPaddingType(uint64_t Align, uint64_t Offset) const; +public: + MaxisABIInfo(CodeGenTypes &CGT, bool _IsO32) : + ABIInfo(CGT), IsO32(_IsO32), MinABIStackAlignInBytes(IsO32 ? 4 : 8), + StackAlignInBytes(IsO32 ? 8 : 16) {} + + ABIArgInfo classifyReturnType(QualType RetTy) const; + ABIArgInfo classifyArgumentType(QualType RetTy, uint64_t &Offset) const; + void computeInfo(CGFunctionInfo &FI) const override; + Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const override; + bool shouldSignExtUnsignedType(QualType Ty) const override; +}; + +class MAXISTargetCodeGenInfo : public TargetCodeGenInfo { + unsigned SizeOfUnwindException; +public: + MAXISTargetCodeGenInfo(CodeGenTypes &CGT, bool IsO32) + : TargetCodeGenInfo(new MaxisABIInfo(CGT, IsO32)), + SizeOfUnwindException(IsO32 ? 24 : 32) {} + + int getDwarfEHStackPointer(CodeGen::CodeGenModule &CGM) const override { + return 29; + } + + void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &CGM, + ForDefinition_t IsForDefinition) const override { + const FunctionDecl *FD = dyn_cast_or_null(D); + if (!FD) return; + llvm::Function *Fn = cast(GV); + + if (FD->hasAttr()) + Fn->addFnAttr("long-call"); + else if (FD->hasAttr()) + Fn->addFnAttr("short-call"); + + // Other attributes do not have a meaning for declarations. + if (!IsForDefinition) + return; + + if (FD->hasAttr()) { + Fn->addFnAttr("maxis16"); + } + else if (FD->hasAttr()) { + Fn->addFnAttr("nomaxis16"); + } + + if (FD->hasAttr()) + Fn->addFnAttr("micromaxis"); + else if (FD->hasAttr()) + Fn->addFnAttr("nomicromaxis"); + + const MaxisInterruptAttr *Attr = FD->getAttr(); + if (!Attr) + return; + + const char *Kind; + switch (Attr->getInterrupt()) { + case MaxisInterruptAttr::eic: Kind = "eic"; break; + case MaxisInterruptAttr::sw0: Kind = "sw0"; break; + case MaxisInterruptAttr::sw1: Kind = "sw1"; break; + case MaxisInterruptAttr::hw0: Kind = "hw0"; break; + case MaxisInterruptAttr::hw1: Kind = "hw1"; break; + case MaxisInterruptAttr::hw2: Kind = "hw2"; break; + case MaxisInterruptAttr::hw3: Kind = "hw3"; break; + case MaxisInterruptAttr::hw4: Kind = "hw4"; break; + case MaxisInterruptAttr::hw5: Kind = "hw5"; break; + } + + Fn->addFnAttr("interrupt", Kind); + + } + + bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, + llvm::Value *Address) const override; + + unsigned getSizeOfUnwindException() const override { + return SizeOfUnwindException; + } +}; +} + +void MaxisABIInfo::CoerceToIntArgs( + uint64_t TySize, SmallVectorImpl &ArgList) const { + llvm::IntegerType *IntTy = + llvm::IntegerType::get(getVMContext(), MinABIStackAlignInBytes * 8); + + // Add (TySize / MinABIStackAlignInBytes) args of IntTy. + for (unsigned N = TySize / (MinABIStackAlignInBytes * 8); N; --N) + ArgList.push_back(IntTy); + + // If necessary, add one more integer type to ArgList. + unsigned R = TySize % (MinABIStackAlignInBytes * 8); + + if (R) + ArgList.push_back(llvm::IntegerType::get(getVMContext(), R)); +} + +// In N32/64, an aligned double precision floating point field is passed in +// a register. +llvm::Type* MaxisABIInfo::HandleAggregates(QualType Ty, uint64_t TySize) const { + SmallVector ArgList, IntArgList; + + if (IsO32) { + CoerceToIntArgs(TySize, ArgList); + return llvm::StructType::get(getVMContext(), ArgList); + } + + if (Ty->isComplexType()) + return CGT.ConvertType(Ty); + + const RecordType *RT = Ty->getAs(); + + // Unions/vectors are passed in integer registers. + if (!RT || !RT->isStructureOrClassType()) { + CoerceToIntArgs(TySize, ArgList); + return llvm::StructType::get(getVMContext(), ArgList); + } + + const RecordDecl *RD = RT->getDecl(); + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); + assert(!(TySize % 8) && "Size of structure must be multiple of 8."); + + uint64_t LastOffset = 0; + unsigned idx = 0; + llvm::IntegerType *I64 = llvm::IntegerType::get(getVMContext(), 64); + + // Iterate over fields in the struct/class and check if there are any aligned + // double fields. + for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); + i != e; ++i, ++idx) { + const QualType Ty = i->getType(); + const BuiltinType *BT = Ty->getAs(); + + if (!BT || BT->getKind() != BuiltinType::Double) + continue; + + uint64_t Offset = Layout.getFieldOffset(idx); + if (Offset % 64) // Ignore doubles that are not aligned. + continue; + + // Add ((Offset - LastOffset) / 64) args of type i64. + for (unsigned j = (Offset - LastOffset) / 64; j > 0; --j) + ArgList.push_back(I64); + + // Add double type. + ArgList.push_back(llvm::Type::getDoubleTy(getVMContext())); + LastOffset = Offset + 64; + } + + CoerceToIntArgs(TySize - LastOffset, IntArgList); + ArgList.append(IntArgList.begin(), IntArgList.end()); + + return llvm::StructType::get(getVMContext(), ArgList); +} + +llvm::Type *MaxisABIInfo::getPaddingType(uint64_t OrigOffset, + uint64_t Offset) const { + if (OrigOffset + MinABIStackAlignInBytes > Offset) + return nullptr; + + return llvm::IntegerType::get(getVMContext(), (Offset - OrigOffset) * 8); +} + +ABIArgInfo +MaxisABIInfo::classifyArgumentType(QualType Ty, uint64_t &Offset) const { + Ty = useFirstFieldIfTransparentUnion(Ty); + + uint64_t OrigOffset = Offset; + uint64_t TySize = getContext().getTypeSize(Ty); + uint64_t Align = getContext().getTypeAlign(Ty) / 8; + + Align = std::min(std::max(Align, (uint64_t)MinABIStackAlignInBytes), + (uint64_t)StackAlignInBytes); + unsigned CurrOffset = llvm::alignTo(Offset, Align); + Offset = CurrOffset + llvm::alignTo(TySize, Align * 8) / 8; + + if (isAggregateTypeForABI(Ty) || Ty->isVectorType()) { + // Ignore empty aggregates. + if (TySize == 0) + return ABIArgInfo::getIgnore(); + + if (CGCXXABI::RecordArgABI RAA = getRecordArgABI(Ty, getCXXABI())) { + Offset = OrigOffset + MinABIStackAlignInBytes; + return getNaturalAlignIndirect(Ty, RAA == CGCXXABI::RAA_DirectInMemory); + } + + // If we have reached here, aggregates are passed directly by coercing to + // another structure type. Padding is inserted if the offset of the + // aggregate is unaligned. + ABIArgInfo ArgInfo = + ABIArgInfo::getDirect(HandleAggregates(Ty, TySize), 0, + getPaddingType(OrigOffset, CurrOffset)); + ArgInfo.setInReg(true); + return ArgInfo; + } + + // Treat an enum type as its underlying type. + if (const EnumType *EnumTy = Ty->getAs()) + Ty = EnumTy->getDecl()->getIntegerType(); + + // All integral types are promoted to the GPR width. + if (Ty->isIntegralOrEnumerationType()) + return ABIArgInfo::getExtend(); + + return ABIArgInfo::getDirect( + nullptr, 0, IsO32 ? nullptr : getPaddingType(OrigOffset, CurrOffset)); +} + +llvm::Type* +MaxisABIInfo::returnAggregateInRegs(QualType RetTy, uint64_t Size) const { + const RecordType *RT = RetTy->getAs(); + SmallVector RTList; + + if (RT && RT->isStructureOrClassType()) { + const RecordDecl *RD = RT->getDecl(); + const ASTRecordLayout &Layout = getContext().getASTRecordLayout(RD); + unsigned FieldCnt = Layout.getFieldCount(); + + // N32/64 returns struct/classes in floating point registers if the + // following conditions are met: + // 1. The size of the struct/class is no larger than 128-bit. + // 2. The struct/class has one or two fields all of which are floating + // point types. + // 3. The offset of the first field is zero (this follows what gcc does). + // + // Any other composite results are returned in integer registers. + // + if (FieldCnt && (FieldCnt <= 2) && !Layout.getFieldOffset(0)) { + RecordDecl::field_iterator b = RD->field_begin(), e = RD->field_end(); + for (; b != e; ++b) { + const BuiltinType *BT = b->getType()->getAs(); + + if (!BT || !BT->isFloatingPoint()) + break; + + RTList.push_back(CGT.ConvertType(b->getType())); + } + + if (b == e) + return llvm::StructType::get(getVMContext(), RTList, + RD->hasAttr()); + + RTList.clear(); + } + } + + CoerceToIntArgs(Size, RTList); + return llvm::StructType::get(getVMContext(), RTList); +} + +ABIArgInfo MaxisABIInfo::classifyReturnType(QualType RetTy) const { + uint64_t Size = getContext().getTypeSize(RetTy); + + if (RetTy->isVoidType()) + return ABIArgInfo::getIgnore(); + + // O32 doesn't treat zero-sized structs differently from other structs. + // However, N32/N64 ignores zero sized return values. + if (!IsO32 && Size == 0) + return ABIArgInfo::getIgnore(); + + if (isAggregateTypeForABI(RetTy) || RetTy->isVectorType()) { + if (Size <= 128) { + if (RetTy->isAnyComplexType()) + return ABIArgInfo::getDirect(); + + // O32 returns integer vectors in registers and N32/N64 returns all small + // aggregates in registers. + if (!IsO32 || + (RetTy->isVectorType() && !RetTy->hasFloatingRepresentation())) { + ABIArgInfo ArgInfo = + ABIArgInfo::getDirect(returnAggregateInRegs(RetTy, Size)); + ArgInfo.setInReg(true); + return ArgInfo; + } + } + + return getNaturalAlignIndirect(RetTy); + } + + // Treat an enum type as its underlying type. + if (const EnumType *EnumTy = RetTy->getAs()) + RetTy = EnumTy->getDecl()->getIntegerType(); + + return (RetTy->isPromotableIntegerType() ? + ABIArgInfo::getExtend() : ABIArgInfo::getDirect()); +} + +void MaxisABIInfo::computeInfo(CGFunctionInfo &FI) const { + ABIArgInfo &RetInfo = FI.getReturnInfo(); + if (!getCXXABI().classifyReturnType(FI)) + RetInfo = classifyReturnType(FI.getReturnType()); + + // Check if a pointer to an aggregate is passed as a hidden argument. + uint64_t Offset = RetInfo.isIndirect() ? MinABIStackAlignInBytes : 0; + + for (auto &I : FI.arguments()) + I.info = classifyArgumentType(I.type, Offset); +} + +Address MaxisABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType OrigTy) const { + QualType Ty = OrigTy; + + // Integer arguments are promoted to 32-bit on O32 and 64-bit on N32/N64. + // Pointers are also promoted in the same way but this only matters for N32. + unsigned SlotSizeInBits = IsO32 ? 32 : 64; + unsigned PtrWidth = getTarget().getPointerWidth(0); + bool DidPromote = false; + if ((Ty->isIntegerType() && + getContext().getIntWidth(Ty) < SlotSizeInBits) || + (Ty->isPointerType() && PtrWidth < SlotSizeInBits)) { + DidPromote = true; + Ty = getContext().getIntTypeForBitwidth(SlotSizeInBits, + Ty->isSignedIntegerType()); + } + + auto TyInfo = getContext().getTypeInfoInChars(Ty); + + // The alignment of things in the argument area is never larger than + // StackAlignInBytes. + TyInfo.second = + std::min(TyInfo.second, CharUnits::fromQuantity(StackAlignInBytes)); + + // MinABIStackAlignInBytes is the size of argument slots on the stack. + CharUnits ArgSlotSize = CharUnits::fromQuantity(MinABIStackAlignInBytes); + + Address Addr = emitVoidPtrVAArg(CGF, VAListAddr, Ty, /*indirect*/ false, + TyInfo, ArgSlotSize, /*AllowHigherAlign*/ true); + + + // If there was a promotion, "unpromote" into a temporary. + // TODO: can we just use a pointer into a subset of the original slot? + if (DidPromote) { + Address Temp = CGF.CreateMemTemp(OrigTy, "vaarg.promotion-temp"); + llvm::Value *Promoted = CGF.Builder.CreateLoad(Addr); + + // Truncate down to the right width. + llvm::Type *IntTy = (OrigTy->isIntegerType() ? Temp.getElementType() + : CGF.IntPtrTy); + llvm::Value *V = CGF.Builder.CreateTrunc(Promoted, IntTy); + if (OrigTy->isPointerType()) + V = CGF.Builder.CreateIntToPtr(V, Temp.getElementType()); + + CGF.Builder.CreateStore(V, Temp); + Addr = Temp; + } + + return Addr; +} + +bool MaxisABIInfo::shouldSignExtUnsignedType(QualType Ty) const { + int TySize = getContext().getTypeSize(Ty); + + // MAXIS64 ABI requires unsigned 32 bit integers to be sign extended. + if (Ty->isUnsignedIntegerOrEnumerationType() && TySize == 32) + return true; + + return false; +} + +bool +MAXISTargetCodeGenInfo::initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, + llvm::Value *Address) const { + // This information comes from gcc's implementation, which seems to + // as canonical as it gets. + + // Everything on MAXIS is 4 bytes. Double-precision FP registers + // are aliased to pairs of single-precision FP registers. + llvm::Value *Four8 = llvm::ConstantInt::get(CGF.Int8Ty, 4); + + // 0-31 are the general purpose registers, $0 - $31. + // 32-63 are the floating-point registers, $f0 - $f31. + // 64 and 65 are the multiply/divide registers, $hi and $lo. + // 66 is the (notional, I think) register for signal-handler return. + AssignToArrayRange(CGF.Builder, Address, Four8, 0, 65); + + // 67-74 are the floating-point status registers, $fcc0 - $fcc7. + // They are one bit wide and ignored here. + + // 80-111 are the coprocessor 0 registers, $c0r0 - $c0r31. + // (coprocessor 1 is the FP unit) + // 112-143 are the coprocessor 2 registers, $c2r0 - $c2r31. + // 144-175 are the coprocessor 3 registers, $c3r0 - $c3r31. + // 176-181 are the DSP accumulator registers. + AssignToArrayRange(CGF.Builder, Address, Four8, 80, 181); + return false; +} + //===----------------------------------------------------------------------===// // MIPS ABI Implementation. This works for both little-endian and // big-endian variants. @@ -8816,6 +9220,16 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::le32: return SetCGInfo(new PNaClTargetCodeGenInfo(Types)); + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + if (Triple.getOS() == llvm::Triple::NaCl) + return SetCGInfo(new PNaClTargetCodeGenInfo(Types)); + return SetCGInfo(new MAXISTargetCodeGenInfo(Types, true)); + + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + return SetCGInfo(new MAXISTargetCodeGenInfo(Types, false)); + case llvm::Triple::mips: case llvm::Triple::mipsel: if (Triple.getOS() == llvm::Triple::NaCl) diff --git a/tools/clang/lib/Driver/CMakeLists.txt b/tools/clang/lib/Driver/CMakeLists.txt index 5bf91f2b..f7118028 100644 --- a/tools/clang/lib/Driver/CMakeLists.txt +++ b/tools/clang/lib/Driver/CMakeLists.txt @@ -23,6 +23,7 @@ add_clang_library(clangDriver ToolChain.cpp ToolChains/Arch/AArch64.cpp ToolChains/Arch/ARM.cpp + ToolChains/Arch/Maxis.cpp ToolChains/Arch/Mips.cpp ToolChains/Arch/PPC.cpp ToolChains/Arch/Sparc.cpp @@ -46,6 +47,7 @@ add_clang_library(clangDriver ToolChains/Haiku.cpp ToolChains/Hexagon.cpp ToolChains/Linux.cpp + ToolChains/MaxisLinux.cpp ToolChains/MipsLinux.cpp ToolChains/MinGW.cpp ToolChains/Minix.cpp diff --git a/tools/clang/lib/Driver/Driver.cpp b/tools/clang/lib/Driver/Driver.cpp index 325b233a..7579379b 100644 --- a/tools/clang/lib/Driver/Driver.cpp +++ b/tools/clang/lib/Driver/Driver.cpp @@ -29,6 +29,7 @@ #include "ToolChains/Linux.h" #include "ToolChains/MinGW.h" #include "ToolChains/Minix.h" +#include "ToolChains/MaxisLinux.h" #include "ToolChains/MipsLinux.h" #include "ToolChains/MSVC.h" #include "ToolChains/Myriad.h" @@ -4100,6 +4101,10 @@ const ToolChain &Driver::getToolChain(const ArgList &Args, if (Target.getArch() == llvm::Triple::hexagon) TC = llvm::make_unique(*this, Target, Args); + else if ((Target.getVendor() == llvm::Triple::MaxisTechnologies) && + !Target.hasEnvironment()) + TC = llvm::make_unique(*this, Target, + Args); else if ((Target.getVendor() == llvm::Triple::MipsTechnologies) && !Target.hasEnvironment()) TC = llvm::make_unique(*this, Target, diff --git a/tools/clang/lib/Driver/ToolChains/Arch/Maxis.cpp b/tools/clang/lib/Driver/ToolChains/Arch/Maxis.cpp new file mode 100644 index 00000000..1f93e1dd --- /dev/null +++ b/tools/clang/lib/Driver/ToolChains/Arch/Maxis.cpp @@ -0,0 +1,449 @@ +//===--- Maxis.cpp - Tools Implementations -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Maxis.h" +#include "ToolChains/CommonArgs.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/ArgList.h" + +using namespace clang::driver; +using namespace clang::driver::tools; +using namespace clang; +using namespace llvm::opt; + +bool tools::isMaxisArch(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::maxis || Arch == llvm::Triple::maxisel || + Arch == llvm::Triple::maxis64 || Arch == llvm::Triple::maxis64el; +} + +// Get CPU and ABI names. They are not independent +// so we have to calculate them together. +void maxis::getMaxisCPUAndABI(const ArgList &Args, const llvm::Triple &Triple, + StringRef &CPUName, StringRef &ABIName) { + const char *DefMaxis32CPU = "maxis32r2"; + const char *DefMaxis64CPU = "maxis64r2"; + + // MAXIS32r6 is the default for maxis(el)?-img-linux-gnu and MAXIS64r6 is the + // default for maxis64(el)?-img-linux-gnu. + if (Triple.getVendor() == llvm::Triple::ImaginationTechnologies && + Triple.isGNUEnvironment()) { + DefMaxis32CPU = "maxis32r6"; + DefMaxis64CPU = "maxis64r6"; + } + + // MAXIS64r6 is the default for Android MAXIS64 (maxis64el-linux-android). + if (Triple.isAndroid()) { + DefMaxis32CPU = "maxis32"; + DefMaxis64CPU = "maxis64r6"; + } + + // MAXIS3 is the default for maxis64*-unknown-openbsd. + if (Triple.getOS() == llvm::Triple::OpenBSD) + DefMaxis64CPU = "maxis3"; + + if (Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ, + options::OPT_mcpu_EQ)) + CPUName = A->getValue(); + + if (Arg *A = Args.getLastArg(options::OPT_mabi_EQ)) { + ABIName = A->getValue(); + // Convert a GNU style Maxis ABI name to the name + // accepted by LLVM Maxis backend. + ABIName = llvm::StringSwitch(ABIName) + .Case("32", "o32") + .Case("64", "n64") + .Default(ABIName); + } + + // Setup default CPU and ABI names. + if (CPUName.empty() && ABIName.empty()) { + switch (Triple.getArch()) { + default: + llvm_unreachable("Unexpected triple arch name"); + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + CPUName = DefMaxis32CPU; + break; + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + CPUName = DefMaxis64CPU; + break; + } + } + + if (ABIName.empty() && + (Triple.getVendor() == llvm::Triple::MaxisTechnologies || + Triple.getVendor() == llvm::Triple::ImaginationTechnologies)) { + ABIName = llvm::StringSwitch(CPUName) + .Case("maxis1", "o32") + .Case("maxis2", "o32") + .Case("maxis3", "n64") + .Case("maxis4", "n64") + .Case("maxis5", "n64") + .Case("maxis32", "o32") + .Case("maxis32r2", "o32") + .Case("maxis32r3", "o32") + .Case("maxis32r5", "o32") + .Case("maxis32r6", "o32") + .Case("maxis64", "n64") + .Case("maxis64r2", "n64") + .Case("maxis64r3", "n64") + .Case("maxis64r5", "n64") + .Case("maxis64r6", "n64") + .Case("octeon", "n64") + .Case("p5600", "o32") + .Default(""); + } + + if (ABIName.empty()) { + // Deduce ABI name from the target triple. + if (Triple.getArch() == llvm::Triple::maxis || + Triple.getArch() == llvm::Triple::maxisel) + ABIName = "o32"; + else + ABIName = "n64"; + } + + if (CPUName.empty()) { + // Deduce CPU name from ABI name. + CPUName = llvm::StringSwitch(ABIName) + .Case("o32", DefMaxis32CPU) + .Cases("n32", "n64", DefMaxis64CPU) + .Default(""); + } + + // FIXME: Warn on inconsistent use of -march and -mabi. +} + +std::string maxis::getMaxisABILibSuffix(const ArgList &Args, + const llvm::Triple &Triple) { + StringRef CPUName, ABIName; + tools::maxis::getMaxisCPUAndABI(Args, Triple, CPUName, ABIName); + return llvm::StringSwitch(ABIName) + .Case("o32", "") + .Case("n32", "32") + .Case("n64", "64"); +} + +// Convert ABI name to the GNU tools acceptable variant. +StringRef maxis::getGnuCompatibleMaxisABIName(StringRef ABI) { + return llvm::StringSwitch(ABI) + .Case("o32", "32") + .Case("n64", "64") + .Default(ABI); +} + +// Select the MAXIS float ABI as determined by -msoft-float, -mhard-float, +// and -mfloat-abi=. +maxis::FloatABI maxis::getMaxisFloatABI(const Driver &D, const ArgList &Args) { + maxis::FloatABI ABI = maxis::FloatABI::Invalid; + if (Arg *A = + Args.getLastArg(options::OPT_msoft_float, options::OPT_mhard_float, + options::OPT_mfloat_abi_EQ)) { + if (A->getOption().matches(options::OPT_msoft_float)) + ABI = maxis::FloatABI::Soft; + else if (A->getOption().matches(options::OPT_mhard_float)) + ABI = maxis::FloatABI::Hard; + else { + ABI = llvm::StringSwitch(A->getValue()) + .Case("soft", maxis::FloatABI::Soft) + .Case("hard", maxis::FloatABI::Hard) + .Default(maxis::FloatABI::Invalid); + if (ABI == maxis::FloatABI::Invalid && !StringRef(A->getValue()).empty()) { + D.Diag(clang::diag::err_drv_invalid_mfloat_abi) << A->getAsString(Args); + ABI = maxis::FloatABI::Hard; + } + } + } + + // If unspecified, choose the default based on the platform. + if (ABI == maxis::FloatABI::Invalid) { + // Assume "hard", because it's a default value used by gcc. + // When we start to recognize specific target MAXIS processors, + // we will be able to select the default more correctly. + ABI = maxis::FloatABI::Hard; + } + + assert(ABI != maxis::FloatABI::Invalid && "must select an ABI"); + return ABI; +} + +void maxis::getMAXISTargetFeatures(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args, + std::vector &Features) { + StringRef CPUName; + StringRef ABIName; + getMaxisCPUAndABI(Args, Triple, CPUName, ABIName); + ABIName = getGnuCompatibleMaxisABIName(ABIName); + + // Historically, PIC code for MAXIS was associated with -mabicalls, a.k.a + // SVR4 abicalls. Static code does not use SVR4 calling sequences. An ABI + // extension was developed by Richard Sandiford & Code Sourcery to support + // static code calling PIC code (CPIC). For O32 and N32 this means we have + // several combinations of PIC/static and abicalls. Pure static, static + // with the CPIC extension, and pure PIC code. + + // At final link time, O32 and N32 with CPIC will have another section + // added to the binary which contains the stub functions to perform + // any fixups required for PIC code. + + // For N64, the situation is more regular: code can either be static + // (non-abicalls) or PIC (abicalls). GCC has traditionally picked PIC code + // code for N64. Since Clang has already built the relocation model portion + // of the commandline, we pick add +noabicalls feature in the N64 static + // case. + + // The is another case to be accounted for: -msym32, which enforces that all + // symbols have 32 bits in size. In this case, N64 can in theory use CPIC + // but it is unsupported. + + // The combinations for N64 are: + // a) Static without abicalls and 64bit symbols. + // b) Static with abicalls and 32bit symbols. + // c) PIC with abicalls and 64bit symbols. + + // For case (a) we need to add +noabicalls for N64. + + bool IsN64 = ABIName == "64"; + bool NonPIC = false; + + Arg *LastPICArg = Args.getLastArg(options::OPT_fPIC, options::OPT_fno_PIC, + options::OPT_fpic, options::OPT_fno_pic, + options::OPT_fPIE, options::OPT_fno_PIE, + options::OPT_fpie, options::OPT_fno_pie); + if (LastPICArg) { + Option O = LastPICArg->getOption(); + NonPIC = + (O.matches(options::OPT_fno_PIC) || O.matches(options::OPT_fno_pic) || + O.matches(options::OPT_fno_PIE) || O.matches(options::OPT_fno_pie)); + } + + bool UseAbiCalls = false; + + Arg *ABICallsArg = + Args.getLastArg(options::OPT_mabicalls, options::OPT_mno_abicalls); + UseAbiCalls = + !ABICallsArg || ABICallsArg->getOption().matches(options::OPT_mabicalls); + + if (UseAbiCalls && IsN64 && NonPIC) { + D.Diag(diag::warn_drv_unsupported_abicalls); + UseAbiCalls = false; + } + + if (!UseAbiCalls) + Features.push_back("+noabicalls"); + else + Features.push_back("-noabicalls"); + + if (Arg *A = Args.getLastArg(options::OPT_mlong_calls, + options::OPT_mno_long_calls)) { + if (A->getOption().matches(options::OPT_mno_long_calls)) + Features.push_back("-long-calls"); + else if (!UseAbiCalls) + Features.push_back("+long-calls"); + else + D.Diag(diag::warn_drv_unsupported_longcalls) << (ABICallsArg ? 0 : 1); + } + + maxis::FloatABI FloatABI = maxis::getMaxisFloatABI(D, Args); + if (FloatABI == maxis::FloatABI::Soft) { + // FIXME: Note, this is a hack. We need to pass the selected float + // mode to the MaxisTargetInfoBase to define appropriate macros there. + // Now it is the only method. + Features.push_back("+soft-float"); + } + + if (Arg *A = Args.getLastArg(options::OPT_mnan_EQ)) { + StringRef Val = StringRef(A->getValue()); + if (Val == "2008") { + if (maxis::getIEEE754Standard(CPUName) & maxis::Std2008) + Features.push_back("+nan2008"); + else { + Features.push_back("-nan2008"); + D.Diag(diag::warn_target_unsupported_nan2008) << CPUName; + } + } else if (Val == "legacy") { + if (maxis::getIEEE754Standard(CPUName) & maxis::Legacy) + Features.push_back("-nan2008"); + else { + Features.push_back("+nan2008"); + D.Diag(diag::warn_target_unsupported_nanlegacy) << CPUName; + } + } else + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Val; + } + + if (Arg *A = Args.getLastArg(options::OPT_mabs_EQ)) { + StringRef Val = StringRef(A->getValue()); + if (Val == "2008") { + if (maxis::getIEEE754Standard(CPUName) & maxis::Std2008) { + Features.push_back("+abs2008"); + } else { + Features.push_back("-abs2008"); + D.Diag(diag::warn_target_unsupported_abs2008) << CPUName; + } + } else if (Val == "legacy") { + if (maxis::getIEEE754Standard(CPUName) & maxis::Legacy) { + Features.push_back("-abs2008"); + } else { + Features.push_back("+abs2008"); + D.Diag(diag::warn_target_unsupported_abslegacy) << CPUName; + } + } else { + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Val; + } + } + + AddTargetFeature(Args, Features, options::OPT_msingle_float, + options::OPT_mdouble_float, "single-float"); + AddTargetFeature(Args, Features, options::OPT_maxis16, options::OPT_mno_maxis16, + "maxis16"); + AddTargetFeature(Args, Features, options::OPT_mmicromaxis, + options::OPT_mno_micromaxis, "micromaxis"); + AddTargetFeature(Args, Features, options::OPT_mdsp, options::OPT_mno_dsp, + "dsp"); + AddTargetFeature(Args, Features, options::OPT_mdspr2, options::OPT_mno_dspr2, + "dspr2"); + AddTargetFeature(Args, Features, options::OPT_mmsa, options::OPT_mno_msa, + "msa"); + + // Add the last -mfp32/-mfpxx/-mfp64, if none are given and the ABI is O32 + // pass -mfpxx, or if none are given and fp64a is default, pass fp64 and + // nooddspreg. + if (Arg *A = Args.getLastArg(options::OPT_mfp32, options::OPT_mfpxx, + options::OPT_mfp64)) { + if (A->getOption().matches(options::OPT_mfp32)) + Features.push_back("-fp64"); + else if (A->getOption().matches(options::OPT_mfpxx)) { + Features.push_back("+fpxx"); + Features.push_back("+nooddspreg"); + } else + Features.push_back("+fp64"); + } else if (maxis::shouldUseFPXX(Args, Triple, CPUName, ABIName, FloatABI)) { + Features.push_back("+fpxx"); + Features.push_back("+nooddspreg"); + } else if (maxis::isFP64ADefault(Triple, CPUName)) { + Features.push_back("+fp64"); + Features.push_back("+nooddspreg"); + } + + AddTargetFeature(Args, Features, options::OPT_mno_odd_spreg, + options::OPT_modd_spreg, "nooddspreg"); + AddTargetFeature(Args, Features, options::OPT_mno_madd4, options::OPT_mmadd4, + "nomadd4"); + AddTargetFeature(Args, Features, options::OPT_mmt, options::OPT_mno_mt, "mt"); +} + +maxis::IEEE754Standard maxis::getIEEE754Standard(StringRef &CPU) { + // Strictly speaking, maxis32r2 and maxis64r2 do not conform to the + // IEEE754-2008 standard. Support for this standard was first introduced + // in Release 3. However, other compilers have traditionally allowed it + // for Release 2 so we should do the same. + return (IEEE754Standard)llvm::StringSwitch(CPU) + .Case("maxis1", Legacy) + .Case("maxis2", Legacy) + .Case("maxis3", Legacy) + .Case("maxis4", Legacy) + .Case("maxis5", Legacy) + .Case("maxis32", Legacy) + .Case("maxis32r2", Legacy | Std2008) + .Case("maxis32r3", Legacy | Std2008) + .Case("maxis32r5", Legacy | Std2008) + .Case("maxis32r6", Std2008) + .Case("maxis64", Legacy) + .Case("maxis64r2", Legacy | Std2008) + .Case("maxis64r3", Legacy | Std2008) + .Case("maxis64r5", Legacy | Std2008) + .Case("maxis64r6", Std2008) + .Default(Std2008); +} + +bool maxis::hasCompactBranches(StringRef &CPU) { + // maxis32r6 and maxis64r6 have compact branches. + return llvm::StringSwitch(CPU) + .Case("maxis32r6", true) + .Case("maxis64r6", true) + .Default(false); +} + +bool maxis::hasMaxisAbiArg(const ArgList &Args, const char *Value) { + Arg *A = Args.getLastArg(options::OPT_mabi_EQ); + return A && (A->getValue() == StringRef(Value)); +} + +bool maxis::isUCLibc(const ArgList &Args) { + Arg *A = Args.getLastArg(options::OPT_m_libc_Group); + return A && A->getOption().matches(options::OPT_muclibc); +} + +bool maxis::isNaN2008(const ArgList &Args, const llvm::Triple &Triple) { + if (Arg *NaNArg = Args.getLastArg(options::OPT_mnan_EQ)) + return llvm::StringSwitch(NaNArg->getValue()) + .Case("2008", true) + .Case("legacy", false) + .Default(false); + + // NaN2008 is the default for MAXIS32r6/MAXIS64r6. + return llvm::StringSwitch(getCPUName(Args, Triple)) + .Cases("maxis32r6", "maxis64r6", true) + .Default(false); + + return false; +} + +bool maxis::isFP64ADefault(const llvm::Triple &Triple, StringRef CPUName) { + if (!Triple.isAndroid()) + return false; + + // Android MAXIS32R6 defaults to FP64A. + return llvm::StringSwitch(CPUName) + .Case("maxis32r6", true) + .Default(false); +} + +bool maxis::isFPXXDefault(const llvm::Triple &Triple, StringRef CPUName, + StringRef ABIName, maxis::FloatABI FloatABI) { + if (Triple.getVendor() != llvm::Triple::ImaginationTechnologies && + Triple.getVendor() != llvm::Triple::MaxisTechnologies && + !Triple.isAndroid()) + return false; + + if (ABIName != "32") + return false; + + // FPXX shouldn't be used if either -msoft-float or -mfloat-abi=soft is + // present. + if (FloatABI == maxis::FloatABI::Soft) + return false; + + return llvm::StringSwitch(CPUName) + .Cases("maxis2", "maxis3", "maxis4", "maxis5", true) + .Cases("maxis32", "maxis32r2", "maxis32r3", "maxis32r5", true) + .Cases("maxis64", "maxis64r2", "maxis64r3", "maxis64r5", true) + .Default(false); +} + +bool maxis::shouldUseFPXX(const ArgList &Args, const llvm::Triple &Triple, + StringRef CPUName, StringRef ABIName, + maxis::FloatABI FloatABI) { + bool UseFPXX = isFPXXDefault(Triple, CPUName, ABIName, FloatABI); + + // FPXX shouldn't be used if -msingle-float is present. + if (Arg *A = Args.getLastArg(options::OPT_msingle_float, + options::OPT_mdouble_float)) + if (A->getOption().matches(options::OPT_msingle_float)) + UseFPXX = false; + + return UseFPXX; +} diff --git a/tools/clang/lib/Driver/ToolChains/Arch/Maxis.h b/tools/clang/lib/Driver/ToolChains/Arch/Maxis.h new file mode 100644 index 00000000..1930b19e --- /dev/null +++ b/tools/clang/lib/Driver/ToolChains/Arch/Maxis.h @@ -0,0 +1,62 @@ +//===--- Maxis.h - Maxis-specific Tool Helpers ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_MAXIS_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_MAXIS_H + +#include "clang/Driver/Driver.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Option.h" +#include +#include + +namespace clang { +namespace driver { +namespace tools { + +bool isMaxisArch(llvm::Triple::ArchType Arch); + +namespace maxis { +typedef enum { Legacy = 1, Std2008 = 2 } IEEE754Standard; + +enum class FloatABI { + Invalid, + Soft, + Hard, +}; + +IEEE754Standard getIEEE754Standard(StringRef &CPU); +bool hasCompactBranches(StringRef &CPU); +void getMaxisCPUAndABI(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple, StringRef &CPUName, + StringRef &ABIName); +void getMAXISTargetFeatures(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + std::vector &Features); +StringRef getGnuCompatibleMaxisABIName(StringRef ABI); +maxis::FloatABI getMaxisFloatABI(const Driver &D, const llvm::opt::ArgList &Args); +std::string getMaxisABILibSuffix(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple); +bool hasMaxisAbiArg(const llvm::opt::ArgList &Args, const char *Value); +bool isUCLibc(const llvm::opt::ArgList &Args); +bool isNaN2008(const llvm::opt::ArgList &Args, const llvm::Triple &Triple); +bool isFP64ADefault(const llvm::Triple &Triple, StringRef CPUName); +bool isFPXXDefault(const llvm::Triple &Triple, StringRef CPUName, + StringRef ABIName, maxis::FloatABI FloatABI); +bool shouldUseFPXX(const llvm::opt::ArgList &Args, const llvm::Triple &Triple, + StringRef CPUName, StringRef ABIName, + maxis::FloatABI FloatABI); + +} // end namespace maxis +} // end namespace target +} // end namespace driver +} // end namespace clang + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_MAXIS_H diff --git a/tools/clang/lib/Driver/ToolChains/Clang.cpp b/tools/clang/lib/Driver/ToolChains/Clang.cpp index 484df520..c004eae0 100644 --- a/tools/clang/lib/Driver/ToolChains/Clang.cpp +++ b/tools/clang/lib/Driver/ToolChains/Clang.cpp @@ -10,6 +10,7 @@ #include "Clang.h" #include "Arch/AArch64.h" #include "Arch/ARM.h" +#include "Arch/Maxis.h" #include "Arch/Mips.h" #include "Arch/PPC.h" #include "Arch/Sparc.h" @@ -308,6 +309,13 @@ static void getTargetFeatures(const ToolChain &TC, const llvm::Triple &Triple, switch (Triple.getArch()) { default: break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + maxis::getMAXISTargetFeatures(D, Triple, Args, Features); + break; + case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -531,6 +539,10 @@ static bool useFramePointerForTargetByDefault(const ArgList &Args, if (Triple.isOSLinux() || Triple.getOS() == llvm::Triple::CloudABI) { switch (Triple.getArch()) { // Don't use a frame pointer on linux if optimizing for certain targets. + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + case llvm::Triple::maxis: + case llvm::Triple::maxisel: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::mips: @@ -1378,6 +1390,13 @@ void Clang::RenderTargetOptions(const llvm::Triple &EffectiveTriple, CmdArgs.push_back("-fallow-half-arguments-and-returns"); break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + AddMAXISTargetArgs(Args, CmdArgs); + break; + case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -1469,6 +1488,148 @@ void Clang::AddAArch64TargetArgs(const ArgList &Args, } } +void Clang::AddMAXISTargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + const Driver &D = getToolChain().getDriver(); + StringRef CPUName; + StringRef ABIName; + const llvm::Triple &Triple = getToolChain().getTriple(); + maxis::getMaxisCPUAndABI(Args, Triple, CPUName, ABIName); + + CmdArgs.push_back("-target-abi"); + CmdArgs.push_back(ABIName.data()); + + maxis::FloatABI ABI = maxis::getMaxisFloatABI(D, Args); + if (ABI == maxis::FloatABI::Soft) { + // Floating point operations and argument passing are soft. + CmdArgs.push_back("-msoft-float"); + CmdArgs.push_back("-mfloat-abi"); + CmdArgs.push_back("soft"); + } else { + // Floating point operations and argument passing are hard. + assert(ABI == maxis::FloatABI::Hard && "Invalid float abi!"); + CmdArgs.push_back("-mfloat-abi"); + CmdArgs.push_back("hard"); + } + + if (Arg *A = Args.getLastArg(options::OPT_mxgot, options::OPT_mno_xgot)) { + if (A->getOption().matches(options::OPT_mxgot)) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-mxgot"); + } + } + + if (Arg *A = Args.getLastArg(options::OPT_mldc1_sdc1, + options::OPT_mno_ldc1_sdc1)) { + if (A->getOption().matches(options::OPT_mno_ldc1_sdc1)) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-mno-ldc1-sdc1"); + } + } + + if (Arg *A = Args.getLastArg(options::OPT_mcheck_zero_division, + options::OPT_mno_check_zero_division)) { + if (A->getOption().matches(options::OPT_mno_check_zero_division)) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-mno-check-zero-division"); + } + } + + if (Arg *A = Args.getLastArg(options::OPT_G)) { + StringRef v = A->getValue(); + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back(Args.MakeArgString("-maxis-ssection-threshold=" + v)); + A->claim(); + } + + Arg *GPOpt = Args.getLastArg(options::OPT_mgpopt, options::OPT_mno_gpopt); + Arg *ABICalls = + Args.getLastArg(options::OPT_mabicalls, options::OPT_mno_abicalls); + + // -mabicalls is the default for many MAXIS environments, even with -fno-pic. + // -mgpopt is the default for static, -fno-pic environments but these two + // options conflict. We want to be certain that -mno-abicalls -mgpopt is + // the only case where -mllvm -mgpopt is passed. + // NOTE: We need a warning here or in the backend to warn when -mgpopt is + // passed explicitly when compiling something with -mabicalls + // (implictly) in affect. Currently the warning is in the backend. + // + // When the ABI in use is N64, we also need to determine the PIC mode that + // is in use, as -fno-pic for N64 implies -mno-abicalls. + bool NoABICalls = + ABICalls && ABICalls->getOption().matches(options::OPT_mno_abicalls); + + llvm::Reloc::Model RelocationModel; + unsigned PICLevel; + bool IsPIE; + std::tie(RelocationModel, PICLevel, IsPIE) = + ParsePICArgs(getToolChain(), Args); + + NoABICalls = NoABICalls || + (RelocationModel == llvm::Reloc::Static && ABIName == "n64"); + + bool WantGPOpt = GPOpt && GPOpt->getOption().matches(options::OPT_mgpopt); + // We quietly ignore -mno-gpopt as the backend defaults to -mno-gpopt. + if (NoABICalls && (!GPOpt || WantGPOpt)) { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back("-mgpopt"); + + Arg *LocalSData = Args.getLastArg(options::OPT_mlocal_sdata, + options::OPT_mno_local_sdata); + Arg *ExternSData = Args.getLastArg(options::OPT_mextern_sdata, + options::OPT_mno_extern_sdata); + Arg *EmbeddedData = Args.getLastArg(options::OPT_membedded_data, + options::OPT_mno_embedded_data); + if (LocalSData) { + CmdArgs.push_back("-mllvm"); + if (LocalSData->getOption().matches(options::OPT_mlocal_sdata)) { + CmdArgs.push_back("-mlocal-sdata=1"); + } else { + CmdArgs.push_back("-mlocal-sdata=0"); + } + LocalSData->claim(); + } + + if (ExternSData) { + CmdArgs.push_back("-mllvm"); + if (ExternSData->getOption().matches(options::OPT_mextern_sdata)) { + CmdArgs.push_back("-mextern-sdata=1"); + } else { + CmdArgs.push_back("-mextern-sdata=0"); + } + ExternSData->claim(); + } + + if (EmbeddedData) { + CmdArgs.push_back("-mllvm"); + if (EmbeddedData->getOption().matches(options::OPT_membedded_data)) { + CmdArgs.push_back("-membedded-data=1"); + } else { + CmdArgs.push_back("-membedded-data=0"); + } + EmbeddedData->claim(); + } + + } else if ((!ABICalls || (!NoABICalls && ABICalls)) && WantGPOpt) + D.Diag(diag::warn_drv_unsupported_gpopt) << (ABICalls ? 0 : 1); + + if (GPOpt) + GPOpt->claim(); + + if (Arg *A = Args.getLastArg(options::OPT_mcompact_branches_EQ)) { + StringRef Val = StringRef(A->getValue()); + if (maxis::hasCompactBranches(CPUName)) { + if (Val == "never" || Val == "always" || Val == "optimal") { + CmdArgs.push_back("-mllvm"); + CmdArgs.push_back(Args.MakeArgString("-maxis-compact-branches=" + Val)); + } else + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Val; + } else + D.Diag(diag::warn_target_unsupported_compact_branches) << CPUName; + } +} + void Clang::AddMIPSTargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const { const Driver &D = getToolChain().getDriver(); @@ -1890,6 +2051,7 @@ static void CollectArgsForIntegratedAssembler(Compilation &C, bool TakeNextArg = false; bool UseRelaxRelocations = C.getDefaultToolChain().useRelaxRelocations(); + const char *MaxisTargetFeature = nullptr; const char *MipsTargetFeature = nullptr; for (const Arg *A : Args.filtered(options::OPT_Wa_COMMA, options::OPT_Xassembler)) { @@ -1918,6 +2080,51 @@ static void CollectArgsForIntegratedAssembler(Compilation &C, // recognize but skip over here. continue; break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + if (Value == "--trap") { + CmdArgs.push_back("-target-feature"); + CmdArgs.push_back("+use-tcc-in-div"); + continue; + } + if (Value == "--break") { + CmdArgs.push_back("-target-feature"); + CmdArgs.push_back("-use-tcc-in-div"); + continue; + } + if (Value.startswith("-msoft-float")) { + CmdArgs.push_back("-target-feature"); + CmdArgs.push_back("+soft-float"); + continue; + } + if (Value.startswith("-mhard-float")) { + CmdArgs.push_back("-target-feature"); + CmdArgs.push_back("-soft-float"); + continue; + } + + MaxisTargetFeature = llvm::StringSwitch(Value) + .Case("-maxis1", "+maxis1") + .Case("-maxis2", "+maxis2") + .Case("-maxis3", "+maxis3") + .Case("-maxis4", "+maxis4") + .Case("-maxis5", "+maxis5") + .Case("-maxis32", "+maxis32") + .Case("-maxis32r2", "+maxis32r2") + .Case("-maxis32r3", "+maxis32r3") + .Case("-maxis32r5", "+maxis32r5") + .Case("-maxis32r6", "+maxis32r6") + .Case("-maxis64", "+maxis64") + .Case("-maxis64r2", "+maxis64r2") + .Case("-maxis64r3", "+maxis64r3") + .Case("-maxis64r5", "+maxis64r5") + .Case("-maxis64r6", "+maxis64r6") + .Default(nullptr); + if (MaxisTargetFeature) + continue; + break; case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -2031,7 +2238,10 @@ static void CollectArgsForIntegratedAssembler(Compilation &C, } if (UseRelaxRelocations) CmdArgs.push_back("--mrelax-relocations"); - if (MipsTargetFeature != nullptr) { + if (MaxisTargetFeature != nullptr) { + CmdArgs.push_back("-target-feature"); + CmdArgs.push_back(MaxisTargetFeature); + } else if (MipsTargetFeature != nullptr) { CmdArgs.push_back("-target-feature"); CmdArgs.push_back(MipsTargetFeature); } @@ -4080,8 +4290,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, RawTriple.getOS() != llvm::Triple::Solaris && getToolChain().getArch() != llvm::Triple::hexagon && getToolChain().getArch() != llvm::Triple::xcore && + ((RawTriple.getVendor() != llvm::Triple::MaxisTechnologies) || + RawTriple.hasEnvironment()) || ((RawTriple.getVendor() != llvm::Triple::MipsTechnologies) || - RawTriple.hasEnvironment())) || + RawTriple.hasEnvironment())) KernelOrKext) CmdArgs.push_back("-fno-use-cxa-atexit"); @@ -5141,6 +5353,17 @@ const char *Clang::getDependencyFileName(const ArgList &Args, // Begin ClangAs +void ClangAs::AddMAXISTargetArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + StringRef CPUName; + StringRef ABIName; + const llvm::Triple &Triple = getToolChain().getTriple(); + maxis::getMaxisCPUAndABI(Args, Triple, CPUName, ABIName); + + CmdArgs.push_back("-target-abi"); + CmdArgs.push_back(ABIName.data()); +} + void ClangAs::AddMIPSTargetArgs(const ArgList &Args, ArgStringList &CmdArgs) const { StringRef CPUName; @@ -5309,6 +5532,13 @@ void ClangAs::ConstructJob(Compilation &C, const JobAction &JA, default: break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + AddMAXISTargetArgs(Args, CmdArgs); + break; + case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: diff --git a/tools/clang/lib/Driver/ToolChains/Clang.h b/tools/clang/lib/Driver/ToolChains/Clang.h index e23822b9..e2d79a40 100644 --- a/tools/clang/lib/Driver/ToolChains/Clang.h +++ b/tools/clang/lib/Driver/ToolChains/Clang.h @@ -54,6 +54,8 @@ class LLVM_LIBRARY_VISIBILITY Clang : public Tool { bool KernelOrKext) const; void AddARM64TargetArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; + void AddMAXISTargetArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; void AddMIPSTargetArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; void AddPPCTargetArgs(const llvm::opt::ArgList &Args, @@ -114,6 +116,8 @@ class LLVM_LIBRARY_VISIBILITY ClangAs : public Tool { public: ClangAs(const ToolChain &TC) : Tool("clang::as", "clang integrated assembler", TC, RF_Full) {} + void AddMAXISTargetArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; void AddMIPSTargetArgs(const llvm::opt::ArgList &Args, llvm::opt::ArgStringList &CmdArgs) const; void AddX86TargetArgs(const llvm::opt::ArgList &Args, diff --git a/tools/clang/lib/Driver/ToolChains/CommonArgs.cpp b/tools/clang/lib/Driver/ToolChains/CommonArgs.cpp index f2688012..26c5135f 100644 --- a/tools/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/tools/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -12,6 +12,7 @@ #include "Hexagon.h" #include "Arch/AArch64.h" #include "Arch/ARM.h" +#include "Arch/Maxis.h" #include "Arch/Mips.h" #include "Arch/PPC.h" #include "Arch/SystemZ.h" @@ -286,6 +287,16 @@ std::string tools::getCPUName(const ArgList &Args, const llvm::Triple &T, return getNios2TargetCPU(Args); } + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: { + StringRef CPUName; + StringRef ABIName; + maxis::getMaxisCPUAndABI(Args, T, CPUName, ABIName); + return CPUName; + } + case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -817,6 +828,10 @@ tools::ParsePICArgs(const ToolChain &ToolChain, const ArgList &Args) { case llvm::Triple::thumb: case llvm::Triple::thumbeb: case llvm::Triple::aarch64: + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -840,6 +855,8 @@ tools::ParsePICArgs(const ToolChain &ToolChain, const ArgList &Args) { switch (ToolChain.getArch()) { case llvm::Triple::arm: case llvm::Triple::aarch64: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: case llvm::Triple::mips64: case llvm::Triple::mips64el: case llvm::Triple::x86: @@ -968,9 +985,11 @@ tools::ParsePICArgs(const ToolChain &ToolChain, const ArgList &Args) { if ((ROPI || RWPI) && (PIC || PIE)) ToolChain.getDriver().Diag(diag::err_drv_ropi_rwpi_incompatible_with_pic); - // When targettng MIPS64 with N64, the default is PIC, unless -mno-abicalls is + // When targettng MAXIS/MIPS64 with N64, the default is PIC, unless -mno-abicalls is // used. - if ((Triple.getArch() == llvm::Triple::mips64 || + if ((Triple.getArch() == llvm::Triple::maxis64 || + Triple.getArch() == llvm::Triple::maxis64el || + Triple.getArch() == llvm::Triple::mips64 || Triple.getArch() == llvm::Triple::mips64el) && Args.hasArg(options::OPT_mno_abicalls)) return std::make_tuple(llvm::Reloc::Static, 0U, false); @@ -1035,8 +1054,8 @@ static void AddLibgcc(const llvm::Triple &Triple, const Driver &D, // According to Android ABI, we have to link with libdl if we are // linking with non-static libgcc. // - // NOTE: This fixes a link error on Android MIPS as well. The non-static - // libgcc for MIPS relies on _Unwind_Find_FDE and dl_iterate_phdr from libdl. + // NOTE: This fixes a link error on Android MAXIS/MIPS as well. The non-static + // libgcc for MAXIS/MIPS relies on _Unwind_Find_FDE and dl_iterate_phdr from libdl. if (isAndroid && !StaticLibgcc) CmdArgs.push_back("-ldl"); } diff --git a/tools/clang/lib/Driver/ToolChains/FreeBSD.cpp b/tools/clang/lib/Driver/ToolChains/FreeBSD.cpp index dd0334b9..913f2dd9 100644 --- a/tools/clang/lib/Driver/ToolChains/FreeBSD.cpp +++ b/tools/clang/lib/Driver/ToolChains/FreeBSD.cpp @@ -9,6 +9,7 @@ #include "FreeBSD.h" #include "Arch/ARM.h" +#include "Arch/Maxis.h" #include "Arch/Mips.h" #include "Arch/Sparc.h" #include "CommonArgs.h" @@ -43,6 +44,35 @@ void freebsd::Assembler::ConstructJob(Compilation &C, const JobAction &JA, case llvm::Triple::ppc: CmdArgs.push_back("-a32"); break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: { + StringRef CPUName; + StringRef ABIName; + maxis::getMaxisCPUAndABI(Args, getToolChain().getTriple(), CPUName, ABIName); + + CmdArgs.push_back("-march"); + CmdArgs.push_back(CPUName.data()); + + CmdArgs.push_back("-mabi"); + CmdArgs.push_back(maxis::getGnuCompatibleMaxisABIName(ABIName).data()); + + if (getToolChain().getArch() == llvm::Triple::maxis || + getToolChain().getArch() == llvm::Triple::maxis64) + CmdArgs.push_back("-EB"); + else + CmdArgs.push_back("-EL"); + + if (Arg *A = Args.getLastArg(options::OPT_G)) { + StringRef v = A->getValue(); + CmdArgs.push_back(Args.MakeArgString("-G" + v)); + A->claim(); + } + + AddAssemblerKPIC(getToolChain(), Args, CmdArgs); + break; + } case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -179,7 +209,11 @@ void freebsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, } if (Arg *A = Args.getLastArg(options::OPT_G)) { - if (ToolChain.getArch() == llvm::Triple::mips || + if (ToolChain.getArch() == llvm::Triple::maxis || + ToolChain.getArch() == llvm::Triple::maxisel || + ToolChain.getArch() == llvm::Triple::maxis64 || + ToolChain.getArch() == llvm::Triple::maxis64el || + ToolChain.getArch() == llvm::Triple::mips || ToolChain.getArch() == llvm::Triple::mipsel || ToolChain.getArch() == llvm::Triple::mips64 || ToolChain.getArch() == llvm::Triple::mips64el) { @@ -381,12 +415,14 @@ bool FreeBSD::isPIEDefault() const { return getSanitizerArgs().requiresPIE(); } SanitizerMask FreeBSD::getSupportedSanitizers() const { const bool IsX86 = getTriple().getArch() == llvm::Triple::x86; const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64; + const bool IsMAXIS64 = getTriple().getArch() == llvm::Triple::maxis64 || + getTriple().getArch() == llvm::Triple::maxis64el; const bool IsMIPS64 = getTriple().getArch() == llvm::Triple::mips64 || getTriple().getArch() == llvm::Triple::mips64el; SanitizerMask Res = ToolChain::getSupportedSanitizers(); Res |= SanitizerKind::Address; Res |= SanitizerKind::Vptr; - if (IsX86_64 || IsMIPS64) { + if (IsX86_64 || IsMAXIS64 || IsMIPS64) { Res |= SanitizerKind::Leak; Res |= SanitizerKind::Thread; } diff --git a/tools/clang/lib/Driver/ToolChains/Gnu.cpp b/tools/clang/lib/Driver/ToolChains/Gnu.cpp index 7845781f..3a589b52 100644 --- a/tools/clang/lib/Driver/ToolChains/Gnu.cpp +++ b/tools/clang/lib/Driver/ToolChains/Gnu.cpp @@ -10,6 +10,7 @@ #include "Gnu.h" #include "Linux.h" #include "Arch/ARM.h" +#include "Arch/Maxis.h" #include "Arch/Mips.h" #include "Arch/PPC.h" #include "Arch/Sparc.h" @@ -276,6 +277,18 @@ static const char *getLDMOption(const llvm::Triple &T, const ArgList &Args) { return "elf32_sparc"; case llvm::Triple::sparcv9: return "elf64_sparc"; + case llvm::Triple::maxis: + return "elf32btsmaxis"; + case llvm::Triple::maxisel: + return "elf32ltsmaxis"; + case llvm::Triple::maxis64: + if (tools::maxis::hasMaxisAbiArg(Args, "n32")) + return "elf32btsmaxisn32"; + return "elf64btsmaxis"; + case llvm::Triple::maxis64el: + if (tools::maxis::hasMaxisAbiArg(Args, "n32")) + return "elf32ltsmaxisn32"; + return "elf64ltsmaxis"; case llvm::Triple::mips: return "elf32btsmip"; case llvm::Triple::mipsel: @@ -327,7 +340,8 @@ void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA, const bool IsPIE = getPIE(Args, ToolChain); const bool HasCRTBeginEndFiles = ToolChain.getTriple().hasEnvironment() || - (ToolChain.getTriple().getVendor() != llvm::Triple::MipsTechnologies); + (ToolChain.getTriple().getVendor() != llvm::Triple::MaxisTechnologies + && ToolChain.getTriple().getVendor() != llvm::Triple::MipsTechnologies); ArgStringList CmdArgs; @@ -681,6 +695,90 @@ void tools::gnutools::Assembler::ConstructJob(Compilation &C, break; } + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: { + StringRef CPUName; + StringRef ABIName; + maxis::getMaxisCPUAndABI(Args, getToolChain().getTriple(), CPUName, ABIName); + ABIName = maxis::getGnuCompatibleMaxisABIName(ABIName); + + CmdArgs.push_back("-march"); + CmdArgs.push_back(CPUName.data()); + + CmdArgs.push_back("-mabi"); + CmdArgs.push_back(ABIName.data()); + + // -mno-shared should be emitted unless -fpic, -fpie, -fPIC, -fPIE, + // or -mshared (not implemented) is in effect. + if (RelocationModel == llvm::Reloc::Static) + CmdArgs.push_back("-mno-shared"); + + // LLVM doesn't support -mplt yet and acts as if it is always given. + // However, -mplt has no effect with the N64 ABI. + if (ABIName != "64" && !Args.hasArg(options::OPT_mno_abicalls)) + CmdArgs.push_back("-call_nonpic"); + + if (getToolChain().getArch() == llvm::Triple::maxis || + getToolChain().getArch() == llvm::Triple::maxis64) + CmdArgs.push_back("-EB"); + else + CmdArgs.push_back("-EL"); + + if (Arg *A = Args.getLastArg(options::OPT_mnan_EQ)) { + if (StringRef(A->getValue()) == "2008") + CmdArgs.push_back(Args.MakeArgString("-mnan=2008")); + } + + // Add the last -mfp32/-mfpxx/-mfp64 or -mfpxx if it is enabled by default. + if (Arg *A = Args.getLastArg(options::OPT_mfp32, options::OPT_mfpxx, + options::OPT_mfp64)) { + A->claim(); + A->render(Args, CmdArgs); + } else if (maxis::shouldUseFPXX( + Args, getToolChain().getTriple(), CPUName, ABIName, + maxis::getMaxisFloatABI(getToolChain().getDriver(), Args))) + CmdArgs.push_back("-mfpxx"); + + // Pass on -mmaxis16 or -mno-maxis16. However, the assembler equivalent of + // -mno-maxis16 is actually -no-maxis16. + if (Arg *A = + Args.getLastArg(options::OPT_maxis16, options::OPT_mno_maxis16)) { + if (A->getOption().matches(options::OPT_maxis16)) { + A->claim(); + A->render(Args, CmdArgs); + } else { + A->claim(); + CmdArgs.push_back("-no-maxis16"); + } + } + + Args.AddLastArg(CmdArgs, options::OPT_mmicromaxis, + options::OPT_mno_micromaxis); + Args.AddLastArg(CmdArgs, options::OPT_mdsp, options::OPT_mno_dsp); + Args.AddLastArg(CmdArgs, options::OPT_mdspr2, options::OPT_mno_dspr2); + + if (Arg *A = Args.getLastArg(options::OPT_mmsa, options::OPT_mno_msa)) { + // Do not use AddLastArg because not all versions of MAXIS assembler + // support -mmsa / -mno-msa options. + if (A->getOption().matches(options::OPT_mmsa)) + CmdArgs.push_back(Args.MakeArgString("-mmsa")); + } + + Args.AddLastArg(CmdArgs, options::OPT_mhard_float, + options::OPT_msoft_float); + + Args.AddLastArg(CmdArgs, options::OPT_mdouble_float, + options::OPT_msingle_float); + + Args.AddLastArg(CmdArgs, options::OPT_modd_spreg, + options::OPT_mno_odd_spreg); + + AddAssemblerKPIC(getToolChain(), Args, CmdArgs); + break; + } + case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -834,6 +932,28 @@ static bool isArmOrThumbArch(llvm::Triple::ArchType Arch) { return Arch == llvm::Triple::arm || Arch == llvm::Triple::thumb; } +static bool isMaxis32(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::maxis || Arch == llvm::Triple::maxisel; +} + +static bool isMaxis64(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::maxis64 || Arch == llvm::Triple::maxis64el; +} + +static bool isMaxisEL(llvm::Triple::ArchType Arch) { + return Arch == llvm::Triple::maxisel || Arch == llvm::Triple::maxis64el; +} + +static bool isMaxis16(const ArgList &Args) { + Arg *A = Args.getLastArg(options::OPT_maxis16, options::OPT_mno_maxis16); + return A && A->getOption().matches(options::OPT_maxis16); +} + +static bool isMicroMaxis(const ArgList &Args) { + Arg *A = Args.getLastArg(options::OPT_mmicromaxis, options::OPT_mno_micromaxis); + return A && A->getOption().matches(options::OPT_mmicromaxis); +} + static bool isMips32(llvm::Triple::ArchType Arch) { return Arch == llvm::Triple::mips || Arch == llvm::Triple::mipsel; } @@ -860,6 +980,505 @@ static Multilib makeMultilib(StringRef commonSuffix) { return Multilib(commonSuffix, commonSuffix, commonSuffix); } +static bool findMaxisCsMultilibs(const Multilib::flags_list &Flags, + FilterNonExistent &NonExistent, + DetectedMultilibs &Result) { + // Check for Code Sourcery toolchain multilibs + MultilibSet CSMaxisMultilibs; + { + auto MArchMaxis16 = makeMultilib("/maxis16").flag("+m32").flag("+maxis16"); + + auto MArchMicroMaxis = + makeMultilib("/micromaxis").flag("+m32").flag("+mmicromaxis"); + + auto MArchDefault = makeMultilib("").flag("-maxis16").flag("-mmicromaxis"); + + auto UCLibc = makeMultilib("/uclibc").flag("+muclibc"); + + auto SoftFloat = makeMultilib("/soft-float").flag("+msoft-float"); + + auto Nan2008 = makeMultilib("/nan2008").flag("+mnan=2008"); + + auto DefaultFloat = + makeMultilib("").flag("-msoft-float").flag("-mnan=2008"); + + auto BigEndian = makeMultilib("").flag("+EB").flag("-EL"); + + auto LittleEndian = makeMultilib("/el").flag("+EL").flag("-EB"); + + // Note that this one's osSuffix is "" + auto MAbi64 = makeMultilib("") + .gccSuffix("/64") + .includeSuffix("/64") + .flag("+mabi=n64") + .flag("-mabi=n32") + .flag("-m32"); + + CSMaxisMultilibs = + MultilibSet() + .Either(MArchMaxis16, MArchMicroMaxis, MArchDefault) + .Maybe(UCLibc) + .Either(SoftFloat, Nan2008, DefaultFloat) + .FilterOut("/micromaxis/nan2008") + .FilterOut("/maxis16/nan2008") + .Either(BigEndian, LittleEndian) + .Maybe(MAbi64) + .FilterOut("/maxis16.*/64") + .FilterOut("/micromaxis.*/64") + .FilterOut(NonExistent) + .setIncludeDirsCallback([](const Multilib &M) { + std::vector Dirs({"/include"}); + if (StringRef(M.includeSuffix()).startswith("/uclibc")) + Dirs.push_back( + "/../../../../maxis-linux-gnu/libc/uclibc/usr/include"); + else + Dirs.push_back("/../../../../maxis-linux-gnu/libc/usr/include"); + return Dirs; + }); + } + + MultilibSet DebianMaxisMultilibs; + { + Multilib MAbiN32 = + Multilib().gccSuffix("/n32").includeSuffix("/n32").flag("+mabi=n32"); + + Multilib M64 = Multilib() + .gccSuffix("/64") + .includeSuffix("/64") + .flag("+m64") + .flag("-m32") + .flag("-mabi=n32"); + + Multilib M32 = Multilib().flag("-m64").flag("+m32").flag("-mabi=n32"); + + DebianMaxisMultilibs = + MultilibSet().Either(M32, M64, MAbiN32).FilterOut(NonExistent); + } + + // Sort candidates. Toolchain that best meets the directories tree goes first. + // Then select the first toolchains matches command line flags. + MultilibSet *Candidates[] = {&CSMaxisMultilibs, &DebianMaxisMultilibs}; + if (CSMaxisMultilibs.size() < DebianMaxisMultilibs.size()) + std::iter_swap(Candidates, Candidates + 1); + for (const MultilibSet *Candidate : Candidates) { + if (Candidate->select(Flags, Result.SelectedMultilib)) { + if (Candidate == &DebianMaxisMultilibs) + Result.BiarchSibling = Multilib(); + Result.Multilibs = *Candidate; + return true; + } + } + return false; +} + +static bool findMaxisAndroidMultilibs(vfs::FileSystem &VFS, StringRef Path, + const Multilib::flags_list &Flags, + FilterNonExistent &NonExistent, + DetectedMultilibs &Result) { + + MultilibSet AndroidMaxisMultilibs = + MultilibSet() + .Maybe(Multilib("/maxis-r2").flag("+march=maxis32r2")) + .Maybe(Multilib("/maxis-r6").flag("+march=maxis32r6")) + .FilterOut(NonExistent); + + MultilibSet AndroidMaxiselMultilibs = + MultilibSet() + .Either(Multilib().flag("+march=maxis32"), + Multilib("/maxis-r2", "", "/maxis-r2").flag("+march=maxis32r2"), + Multilib("/maxis-r6", "", "/maxis-r6").flag("+march=maxis32r6")) + .FilterOut(NonExistent); + + MultilibSet AndroidMaxis64elMultilibs = + MultilibSet() + .Either( + Multilib().flag("+march=maxis64r6"), + Multilib("/32/maxis-r1", "", "/maxis-r1").flag("+march=maxis32"), + Multilib("/32/maxis-r2", "", "/maxis-r2").flag("+march=maxis32r2"), + Multilib("/32/maxis-r6", "", "/maxis-r6").flag("+march=maxis32r6")) + .FilterOut(NonExistent); + + MultilibSet *MS = &AndroidMaxisMultilibs; + if (VFS.exists(Path + "/maxis-r6")) + MS = &AndroidMaxiselMultilibs; + else if (VFS.exists(Path + "/32")) + MS = &AndroidMaxis64elMultilibs; + if (MS->select(Flags, Result.SelectedMultilib)) { + Result.Multilibs = *MS; + return true; + } + return false; +} + +static bool findMaxisMuslMultilibs(const Multilib::flags_list &Flags, + FilterNonExistent &NonExistent, + DetectedMultilibs &Result) { + // Musl toolchain multilibs + MultilibSet MuslMaxisMultilibs; + { + auto MArchMaxisR2 = makeMultilib("") + .osSuffix("/maxis-r2-hard-musl") + .flag("+EB") + .flag("-EL") + .flag("+march=maxis32r2"); + + auto MArchMaxiselR2 = makeMultilib("/maxisel-r2-hard-musl") + .flag("-EB") + .flag("+EL") + .flag("+march=maxis32r2"); + + MuslMaxisMultilibs = MultilibSet().Either(MArchMaxisR2, MArchMaxiselR2); + + // Specify the callback that computes the include directories. + MuslMaxisMultilibs.setIncludeDirsCallback([](const Multilib &M) { + return std::vector( + {"/../sysroot" + M.osSuffix() + "/usr/include"}); + }); + } + if (MuslMaxisMultilibs.select(Flags, Result.SelectedMultilib)) { + Result.Multilibs = MuslMaxisMultilibs; + return true; + } + return false; +} + +static bool findMaxisMtiMultilibs(const Multilib::flags_list &Flags, + FilterNonExistent &NonExistent, + DetectedMultilibs &Result) { + // CodeScape MTI toolchain v1.2 and early. + MultilibSet MtiMaxisMultilibsV1; + { + auto MArchMaxis32 = makeMultilib("/maxis32") + .flag("+m32") + .flag("-m64") + .flag("-mmicromaxis") + .flag("+march=maxis32"); + + auto MArchMicroMaxis = makeMultilib("/micromaxis") + .flag("+m32") + .flag("-m64") + .flag("+mmicromaxis"); + + auto MArchMaxis64r2 = makeMultilib("/maxis64r2") + .flag("-m32") + .flag("+m64") + .flag("+march=maxis64r2"); + + auto MArchMaxis64 = makeMultilib("/maxis64").flag("-m32").flag("+m64").flag( + "-march=maxis64r2"); + + auto MArchDefault = makeMultilib("") + .flag("+m32") + .flag("-m64") + .flag("-mmicromaxis") + .flag("+march=maxis32r2"); + + auto Maxis16 = makeMultilib("/maxis16").flag("+maxis16"); + + auto UCLibc = makeMultilib("/uclibc").flag("+muclibc"); + + auto MAbi64 = + makeMultilib("/64").flag("+mabi=n64").flag("-mabi=n32").flag("-m32"); + + auto BigEndian = makeMultilib("").flag("+EB").flag("-EL"); + + auto LittleEndian = makeMultilib("/el").flag("+EL").flag("-EB"); + + auto SoftFloat = makeMultilib("/sof").flag("+msoft-float"); + + auto Nan2008 = makeMultilib("/nan2008").flag("+mnan=2008"); + + MtiMaxisMultilibsV1 = + MultilibSet() + .Either(MArchMaxis32, MArchMicroMaxis, MArchMaxis64r2, MArchMaxis64, + MArchDefault) + .Maybe(UCLibc) + .Maybe(Maxis16) + .FilterOut("/maxis64/maxis16") + .FilterOut("/maxis64r2/maxis16") + .FilterOut("/micromaxis/maxis16") + .Maybe(MAbi64) + .FilterOut("/micromaxis/64") + .FilterOut("/maxis32/64") + .FilterOut("^/64") + .FilterOut("/maxis16/64") + .Either(BigEndian, LittleEndian) + .Maybe(SoftFloat) + .Maybe(Nan2008) + .FilterOut(".*sof/nan2008") + .FilterOut(NonExistent) + .setIncludeDirsCallback([](const Multilib &M) { + std::vector Dirs({"/include"}); + if (StringRef(M.includeSuffix()).startswith("/uclibc")) + Dirs.push_back("/../../../../sysroot/uclibc/usr/include"); + else + Dirs.push_back("/../../../../sysroot/usr/include"); + return Dirs; + }); + } + + // CodeScape IMG toolchain starting from v1.3. + MultilibSet MtiMaxisMultilibsV2; + { + auto BeHard = makeMultilib("/maxis-r2-hard") + .flag("+EB") + .flag("-msoft-float") + .flag("-mnan=2008") + .flag("-muclibc"); + auto BeSoft = makeMultilib("/maxis-r2-soft") + .flag("+EB") + .flag("+msoft-float") + .flag("-mnan=2008"); + auto ElHard = makeMultilib("/maxisel-r2-hard") + .flag("+EL") + .flag("-msoft-float") + .flag("-mnan=2008") + .flag("-muclibc"); + auto ElSoft = makeMultilib("/maxisel-r2-soft") + .flag("+EL") + .flag("+msoft-float") + .flag("-mnan=2008") + .flag("-mmicromaxis"); + auto BeHardNan = makeMultilib("/maxis-r2-hard-nan2008") + .flag("+EB") + .flag("-msoft-float") + .flag("+mnan=2008") + .flag("-muclibc"); + auto ElHardNan = makeMultilib("/maxisel-r2-hard-nan2008") + .flag("+EL") + .flag("-msoft-float") + .flag("+mnan=2008") + .flag("-muclibc") + .flag("-mmicromaxis"); + auto BeHardNanUclibc = makeMultilib("/maxis-r2-hard-nan2008-uclibc") + .flag("+EB") + .flag("-msoft-float") + .flag("+mnan=2008") + .flag("+muclibc"); + auto ElHardNanUclibc = makeMultilib("/maxisel-r2-hard-nan2008-uclibc") + .flag("+EL") + .flag("-msoft-float") + .flag("+mnan=2008") + .flag("+muclibc"); + auto BeHardUclibc = makeMultilib("/maxis-r2-hard-uclibc") + .flag("+EB") + .flag("-msoft-float") + .flag("-mnan=2008") + .flag("+muclibc"); + auto ElHardUclibc = makeMultilib("/maxisel-r2-hard-uclibc") + .flag("+EL") + .flag("-msoft-float") + .flag("-mnan=2008") + .flag("+muclibc"); + auto ElMicroHardNan = makeMultilib("/micromaxisel-r2-hard-nan2008") + .flag("+EL") + .flag("-msoft-float") + .flag("+mnan=2008") + .flag("+mmicromaxis"); + auto ElMicroSoft = makeMultilib("/micromaxisel-r2-soft") + .flag("+EL") + .flag("+msoft-float") + .flag("-mnan=2008") + .flag("+mmicromaxis"); + + auto O32 = + makeMultilib("/lib").osSuffix("").flag("-mabi=n32").flag("-mabi=n64"); + auto N32 = + makeMultilib("/lib32").osSuffix("").flag("+mabi=n32").flag("-mabi=n64"); + auto N64 = + makeMultilib("/lib64").osSuffix("").flag("-mabi=n32").flag("+mabi=n64"); + + MtiMaxisMultilibsV2 = + MultilibSet() + .Either({BeHard, BeSoft, ElHard, ElSoft, BeHardNan, ElHardNan, + BeHardNanUclibc, ElHardNanUclibc, BeHardUclibc, + ElHardUclibc, ElMicroHardNan, ElMicroSoft}) + .Either(O32, N32, N64) + .FilterOut(NonExistent) + .setIncludeDirsCallback([](const Multilib &M) { + return std::vector({"/../../../../sysroot" + + M.includeSuffix() + + "/../usr/include"}); + }) + .setFilePathsCallback([](const Multilib &M) { + return std::vector( + {"/../../../../maxis-mti-linux-gnu/lib" + M.gccSuffix()}); + }); + } + for (auto Candidate : {&MtiMaxisMultilibsV1, &MtiMaxisMultilibsV2}) { + if (Candidate->select(Flags, Result.SelectedMultilib)) { + Result.Multilibs = *Candidate; + return true; + } + } + return false; +} + +static bool findMaxisImgMultilibs(const Multilib::flags_list &Flags, + FilterNonExistent &NonExistent, + DetectedMultilibs &Result) { + // CodeScape IMG toolchain v1.2 and early. + MultilibSet ImgMultilibsV1; + { + auto Maxis64r6 = makeMultilib("/maxis64r6").flag("+m64").flag("-m32"); + + auto LittleEndian = makeMultilib("/el").flag("+EL").flag("-EB"); + + auto MAbi64 = + makeMultilib("/64").flag("+mabi=n64").flag("-mabi=n32").flag("-m32"); + + ImgMultilibsV1 = + MultilibSet() + .Maybe(Maxis64r6) + .Maybe(MAbi64) + .Maybe(LittleEndian) + .FilterOut(NonExistent) + .setIncludeDirsCallback([](const Multilib &M) { + return std::vector( + {"/include", "/../../../../sysroot/usr/include"}); + }); + } + + // CodeScape IMG toolchain starting from v1.3. + MultilibSet ImgMultilibsV2; + { + auto BeHard = makeMultilib("/maxis-r6-hard") + .flag("+EB") + .flag("-msoft-float") + .flag("-mmicromaxis"); + auto BeSoft = makeMultilib("/maxis-r6-soft") + .flag("+EB") + .flag("+msoft-float") + .flag("-mmicromaxis"); + auto ElHard = makeMultilib("/maxisel-r6-hard") + .flag("+EL") + .flag("-msoft-float") + .flag("-mmicromaxis"); + auto ElSoft = makeMultilib("/maxisel-r6-soft") + .flag("+EL") + .flag("+msoft-float") + .flag("-mmicromaxis"); + auto BeMicroHard = makeMultilib("/micromaxis-r6-hard") + .flag("+EB") + .flag("-msoft-float") + .flag("+mmicromaxis"); + auto BeMicroSoft = makeMultilib("/micromaxis-r6-soft") + .flag("+EB") + .flag("+msoft-float") + .flag("+mmicromaxis"); + auto ElMicroHard = makeMultilib("/micromaxisel-r6-hard") + .flag("+EL") + .flag("-msoft-float") + .flag("+mmicromaxis"); + auto ElMicroSoft = makeMultilib("/micromaxisel-r6-soft") + .flag("+EL") + .flag("+msoft-float") + .flag("+mmicromaxis"); + + auto O32 = + makeMultilib("/lib").osSuffix("").flag("-mabi=n32").flag("-mabi=n64"); + auto N32 = + makeMultilib("/lib32").osSuffix("").flag("+mabi=n32").flag("-mabi=n64"); + auto N64 = + makeMultilib("/lib64").osSuffix("").flag("-mabi=n32").flag("+mabi=n64"); + + ImgMultilibsV2 = + MultilibSet() + .Either({BeHard, BeSoft, ElHard, ElSoft, BeMicroHard, BeMicroSoft, + ElMicroHard, ElMicroSoft}) + .Either(O32, N32, N64) + .FilterOut(NonExistent) + .setIncludeDirsCallback([](const Multilib &M) { + return std::vector({"/../../../../sysroot" + + M.includeSuffix() + + "/../usr/include"}); + }) + .setFilePathsCallback([](const Multilib &M) { + return std::vector( + {"/../../../../maxis-img-linux-gnu/lib" + M.gccSuffix()}); + }); + } + for (auto Candidate : {&ImgMultilibsV1, &ImgMultilibsV2}) { + if (Candidate->select(Flags, Result.SelectedMultilib)) { + Result.Multilibs = *Candidate; + return true; + } + } + return false; +} + +bool clang::driver::findMAXISMultilibs(const Driver &D, + const llvm::Triple &TargetTriple, + StringRef Path, const ArgList &Args, + DetectedMultilibs &Result) { + FilterNonExistent NonExistent(Path, "/crtbegin.o", D.getVFS()); + + StringRef CPUName; + StringRef ABIName; + tools::maxis::getMaxisCPUAndABI(Args, TargetTriple, CPUName, ABIName); + + llvm::Triple::ArchType TargetArch = TargetTriple.getArch(); + + Multilib::flags_list Flags; + addMultilibFlag(isMaxis32(TargetArch), "m32", Flags); + addMultilibFlag(isMaxis64(TargetArch), "m64", Flags); + addMultilibFlag(isMaxis16(Args), "maxis16", Flags); + addMultilibFlag(CPUName == "maxis32", "march=maxis32", Flags); + addMultilibFlag(CPUName == "maxis32r2" || CPUName == "maxis32r3" || + CPUName == "maxis32r5" || CPUName == "p5600", + "march=maxis32r2", Flags); + addMultilibFlag(CPUName == "maxis32r6", "march=maxis32r6", Flags); + addMultilibFlag(CPUName == "maxis64", "march=maxis64", Flags); + addMultilibFlag(CPUName == "maxis64r2" || CPUName == "maxis64r3" || + CPUName == "maxis64r5" || CPUName == "octeon", + "march=maxis64r2", Flags); + addMultilibFlag(CPUName == "maxis64r6", "march=maxis64r6", Flags); + addMultilibFlag(isMicroMaxis(Args), "mmicromaxis", Flags); + addMultilibFlag(tools::maxis::isUCLibc(Args), "muclibc", Flags); + addMultilibFlag(tools::maxis::isNaN2008(Args, TargetTriple), "mnan=2008", + Flags); + addMultilibFlag(ABIName == "n32", "mabi=n32", Flags); + addMultilibFlag(ABIName == "n64", "mabi=n64", Flags); + addMultilibFlag(isSoftFloatABI(Args), "msoft-float", Flags); + addMultilibFlag(!isSoftFloatABI(Args), "mhard-float", Flags); + addMultilibFlag(isMaxisEL(TargetArch), "EL", Flags); + addMultilibFlag(!isMaxisEL(TargetArch), "EB", Flags); + + if (TargetTriple.isAndroid()) + return findMaxisAndroidMultilibs(D.getVFS(), Path, Flags, NonExistent, + Result); + + if (TargetTriple.getVendor() == llvm::Triple::MaxisTechnologies && + TargetTriple.getOS() == llvm::Triple::Linux && + TargetTriple.getEnvironment() == llvm::Triple::UnknownEnvironment) + return findMaxisMuslMultilibs(Flags, NonExistent, Result); + + if (TargetTriple.getVendor() == llvm::Triple::MaxisTechnologies && + TargetTriple.getOS() == llvm::Triple::Linux && + TargetTriple.isGNUEnvironment()) + return findMaxisMtiMultilibs(Flags, NonExistent, Result); + + if (TargetTriple.getVendor() == llvm::Triple::ImaginationTechnologies && + TargetTriple.getOS() == llvm::Triple::Linux && + TargetTriple.isGNUEnvironment()) + return findMaxisImgMultilibs(Flags, NonExistent, Result); + + if (findMaxisCsMultilibs(Flags, NonExistent, Result)) + return true; + + // Fallback to the regular toolchain-tree structure. + Multilib Default; + Result.Multilibs.push_back(Default); + Result.Multilibs.FilterOut(NonExistent); + + if (Result.Multilibs.select(Flags, Result.SelectedMultilib)) { + Result.BiarchSibling = Multilib(); + return true; + } + + return false; +} + static bool findMipsCsMultilibs(const Multilib::flags_list &Flags, FilterNonExistent &NonExistent, DetectedMultilibs &Result) { @@ -1745,6 +2364,31 @@ bool Generic_GCC::GCCInstallationDetector::getBiarchSibling(Multilib &M) const { "i486-slackware-linux", "i686-montavista-linux", "i686-linux-android", "i586-linux-gnu"}; + static const char *const MAXISLibDirs[] = {"/lib"}; + static const char *const MAXISTriples[] = {"maxis-linux-gnu", "maxis-mti-linux", + "maxis-mti-linux-gnu", + "maxis-img-linux-gnu"}; + static const char *const MAXISELLibDirs[] = {"/lib"}; + static const char *const MAXISELTriples[] = {"maxisel-linux-gnu", + "maxis-img-linux-gnu"}; + + static const char *const MAXIS64LibDirs[] = {"/lib64", "/lib"}; + static const char *const MAXIS64Triples[] = { + "maxis64-linux-gnu", "maxis-mti-linux-gnu", "maxis-img-linux-gnu", + "maxis64-linux-gnuabi64"}; + static const char *const MAXIS64ELLibDirs[] = {"/lib64", "/lib"}; + static const char *const MAXIS64ELTriples[] = { + "maxis64el-linux-gnu", "maxis-mti-linux-gnu", "maxis-img-linux-gnu", + "maxis64el-linux-gnuabi64"}; + + static const char *const MAXISELAndroidLibDirs[] = {"/lib", "/libr2", + "/libr6"}; + static const char *const MAXISELAndroidTriples[] = {"maxisel-linux-android"}; + static const char *const MAXIS64ELAndroidLibDirs[] = {"/lib64", "/lib", + "/libr2", "/libr6"}; + static const char *const MAXIS64ELAndroidTriples[] = { + "maxis64el-linux-android"}; + static const char *const MIPSLibDirs[] = {"/lib"}; static const char *const MIPSTriples[] = {"mips-linux-gnu", "mips-mti-linux", "mips-mti-linux-gnu", @@ -1863,6 +2507,55 @@ bool Generic_GCC::GCCInstallationDetector::getBiarchSibling(Multilib &M) const { BiarchTripleAliases.append(begin(X86_64Triples), end(X86_64Triples)); } break; + case llvm::Triple::maxis: + LibDirs.append(begin(MAXISLibDirs), end(MAXISLibDirs)); + TripleAliases.append(begin(MAXISTriples), end(MAXISTriples)); + BiarchLibDirs.append(begin(MAXIS64LibDirs), end(MAXIS64LibDirs)); + BiarchTripleAliases.append(begin(MAXIS64Triples), end(MAXIS64Triples)); + break; + case llvm::Triple::maxisel: + if (TargetTriple.isAndroid()) { + LibDirs.append(begin(MAXISELAndroidLibDirs), end(MAXISELAndroidLibDirs)); + TripleAliases.append(begin(MAXISELAndroidTriples), + end(MAXISELAndroidTriples)); + BiarchLibDirs.append(begin(MAXIS64ELAndroidLibDirs), + end(MAXIS64ELAndroidLibDirs)); + BiarchTripleAliases.append(begin(MAXIS64ELAndroidTriples), + end(MAXIS64ELAndroidTriples)); + + } else { + LibDirs.append(begin(MAXISELLibDirs), end(MAXISELLibDirs)); + TripleAliases.append(begin(MAXISELTriples), end(MAXISELTriples)); + TripleAliases.append(begin(MAXISTriples), end(MAXISTriples)); + BiarchLibDirs.append(begin(MAXIS64ELLibDirs), end(MAXIS64ELLibDirs)); + BiarchTripleAliases.append(begin(MAXIS64ELTriples), end(MAXIS64ELTriples)); + } + break; + case llvm::Triple::maxis64: + LibDirs.append(begin(MAXIS64LibDirs), end(MAXIS64LibDirs)); + TripleAliases.append(begin(MAXIS64Triples), end(MAXIS64Triples)); + BiarchLibDirs.append(begin(MAXISLibDirs), end(MAXISLibDirs)); + BiarchTripleAliases.append(begin(MAXISTriples), end(MAXISTriples)); + break; + case llvm::Triple::maxis64el: + if (TargetTriple.isAndroid()) { + LibDirs.append(begin(MAXIS64ELAndroidLibDirs), + end(MAXIS64ELAndroidLibDirs)); + TripleAliases.append(begin(MAXIS64ELAndroidTriples), + end(MAXIS64ELAndroidTriples)); + BiarchLibDirs.append(begin(MAXISELAndroidLibDirs), + end(MAXISELAndroidLibDirs)); + BiarchTripleAliases.append(begin(MAXISELAndroidTriples), + end(MAXISELAndroidTriples)); + + } else { + LibDirs.append(begin(MAXIS64ELLibDirs), end(MAXIS64ELLibDirs)); + TripleAliases.append(begin(MAXIS64ELTriples), end(MAXIS64ELTriples)); + BiarchLibDirs.append(begin(MAXISELLibDirs), end(MAXISELLibDirs)); + BiarchTripleAliases.append(begin(MAXISELTriples), end(MAXISELTriples)); + BiarchTripleAliases.append(begin(MAXISTriples), end(MAXISTriples)); + } + break; case llvm::Triple::mips: LibDirs.append(begin(MIPSLibDirs), end(MIPSLibDirs)); TripleAliases.append(begin(MIPSTriples), end(MIPSTriples)); @@ -2017,11 +2710,14 @@ bool Generic_GCC::GCCInstallationDetector::ScanGCCForMultilibs( DetectedMultilibs Detected; // Android standalone toolchain could have multilibs for ARM and Thumb. - // Debian mips multilibs behave more like the rest of the biarch ones, + // Debian maxis/mips multilibs behave more like the rest of the biarch ones, // so handle them there if (isArmOrThumbArch(TargetArch) && TargetTriple.isAndroid()) { // It should also work without multilibs in a simplified toolchain. findAndroidArmMultilibs(D, TargetTriple, Path, Args, Detected); + } else if (tools::isMaxisArch(TargetArch)) { + if (!findMAXISMultilibs(D, TargetTriple, Path, Args, Detected)) + return false; } else if (tools::isMipsArch(TargetArch)) { if (!findMIPSMultilibs(D, TargetTriple, Path, Args, Detected)) return false; @@ -2210,6 +2906,8 @@ bool Generic_GCC::isPICDefault() const { case llvm::Triple::ppc64: case llvm::Triple::ppc64le: return !getTriple().isOSBinFormatMachO() && !getTriple().isMacOSX(); + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: case llvm::Triple::mips64: case llvm::Triple::mips64el: return true; @@ -2241,13 +2939,17 @@ bool Generic_GCC::IsIntegratedAssemblerDefault() const { case llvm::Triple::ppc64: case llvm::Triple::ppc64le: case llvm::Triple::systemz: + case llvm::Triple::maxis: + case llvm::Triple::maxisel: case llvm::Triple::mips: case llvm::Triple::mipsel: return true; + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: case llvm::Triple::mips64: case llvm::Triple::mips64el: - // Enabled for Debian and Android mips64/mipsel, as they can precisely - // identify the ABI in use (Debian) or only use N64 for MIPS64 (Android). + // Enabled for Debian and Android maxis64/maxisel/mips64/mipsel, as they can precisely + // identify the ABI in use (Debian) or only use N64 for MAXIS64/MIPS64 (Android). // Other targets are unable to distinguish N32 from N64. if (getTriple().getEnvironment() == llvm::Triple::GNUABI64 || getTriple().isAndroid()) @@ -2379,6 +3081,8 @@ void Generic_ELF::addClangTargetOptions(const ArgList &DriverArgs, ((!GCCInstallation.isValid() || !V.isOlderThan(4, 7, 0)) || getTriple().isAndroid())) || getTriple().getOS() == llvm::Triple::NaCl || + (getTriple().getVendor() == llvm::Triple::MaxisTechnologies && + !getTriple().hasEnvironment()) || (getTriple().getVendor() == llvm::Triple::MipsTechnologies && !getTriple().hasEnvironment()) || getTriple().getOS() == llvm::Triple::Solaris; diff --git a/tools/clang/lib/Driver/ToolChains/Gnu.h b/tools/clang/lib/Driver/ToolChains/Gnu.h index f29342b9..d4ede055 100644 --- a/tools/clang/lib/Driver/ToolChains/Gnu.h +++ b/tools/clang/lib/Driver/ToolChains/Gnu.h @@ -30,6 +30,10 @@ struct DetectedMultilibs { llvm::Optional BiarchSibling; }; +bool findMAXISMultilibs(const Driver &D, const llvm::Triple &TargetTriple, + StringRef Path, const llvm::opt::ArgList &Args, + DetectedMultilibs &Result); + bool findMIPSMultilibs(const Driver &D, const llvm::Triple &TargetTriple, StringRef Path, const llvm::opt::ArgList &Args, DetectedMultilibs &Result); diff --git a/tools/clang/lib/Driver/ToolChains/Linux.cpp b/tools/clang/lib/Driver/ToolChains/Linux.cpp index 1301cdf1..0c4d444a 100644 --- a/tools/clang/lib/Driver/ToolChains/Linux.cpp +++ b/tools/clang/lib/Driver/ToolChains/Linux.cpp @@ -9,6 +9,7 @@ #include "Linux.h" #include "Arch/ARM.h" +#include "Arch/Maxis.h" #include "Arch/Mips.h" #include "Arch/PPC.h" #include "CommonArgs.h" @@ -90,6 +91,26 @@ static std::string getMultiarchTriple(const Driver &D, if (D.getVFS().exists(SysRoot + "/lib/aarch64_be-linux-gnu")) return "aarch64_be-linux-gnu"; break; + case llvm::Triple::maxis: + if (D.getVFS().exists(SysRoot + "/lib/maxis-linux-gnu")) + return "maxis-linux-gnu"; + break; + case llvm::Triple::maxisel: + if (D.getVFS().exists(SysRoot + "/lib/maxisel-linux-gnu")) + return "maxisel-linux-gnu"; + break; + case llvm::Triple::maxis64: + if (D.getVFS().exists(SysRoot + "/lib/maxis64-linux-gnu")) + return "maxis64-linux-gnu"; + if (D.getVFS().exists(SysRoot + "/lib/maxis64-linux-gnuabi64")) + return "maxis64-linux-gnuabi64"; + break; + case llvm::Triple::maxis64el: + if (D.getVFS().exists(SysRoot + "/lib/maxis64el-linux-gnu")) + return "maxis64el-linux-gnu"; + if (D.getVFS().exists(SysRoot + "/lib/maxis64el-linux-gnuabi64")) + return "maxis64el-linux-gnuabi64"; + break; case llvm::Triple::mips: if (D.getVFS().exists(SysRoot + "/lib/mips-linux-gnu")) return "mips-linux-gnu"; @@ -141,7 +162,23 @@ static std::string getMultiarchTriple(const Driver &D, } static StringRef getOSLibDir(const llvm::Triple &Triple, const ArgList &Args) { - if (tools::isMipsArch(Triple.getArch())) { + if (tools::isMaxisArch(Triple.getArch())) { + if (Triple.isAndroid()) { + StringRef CPUName; + StringRef ABIName; + tools::maxis::getMaxisCPUAndABI(Args, Triple, CPUName, ABIName); + if (CPUName == "maxis32r6") + return "libr6"; + if (CPUName == "maxis32r2") + return "libr2"; + } + // lib32 directory has a special meaning on MAXIS targets. + // It contains N32 ABI binaries. Use this folder if produce + // code for N32 ABI only. + if (tools::maxis::hasMaxisAbiArg(Args, "n32")) + return "lib32"; + return Triple.isArch32Bit() ? "lib" : "lib64"; + } else if (tools::isMipsArch(Triple.getArch())) { if (Triple.isAndroid()) { StringRef CPUName; StringRef ABIName; @@ -224,19 +261,22 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) ExtraOpts.push_back("-X"); const bool IsAndroid = Triple.isAndroid(); + const bool IsMaxis = tools::isMaxisArch(Arch); const bool IsMips = tools::isMipsArch(Arch); const bool IsHexagon = Arch == llvm::Triple::hexagon; if (IsMips && !SysRoot.empty()) ExtraOpts.push_back("--sysroot=" + SysRoot); + if (IsMaxis && !SysRoot.empty()) + ExtraOpts.push_back("--sysroot=" + SysRoot); - // Do not use 'gnu' hash style for Mips targets because .gnu.hash - // and the MIPS ABI require .dynsym to be sorted in different ways. - // .gnu.hash needs symbols to be grouped by hash code whereas the MIPS + // Do not use 'gnu' hash style for Maxis/Mips targets because .gnu.hash + // and the MAXIS/MIPS ABI require .dynsym to be sorted in different ways. + // .gnu.hash needs symbols to be grouped by hash code whereas the MAXIS/MIPS // ABI requires a mapping between the GOT and the symbol table. // Android loader does not support .gnu.hash. // Hexagon linker/loader does not support .gnu.hash - if (!IsMips && !IsAndroid && !IsHexagon) { + if (!IsMaxis && !IsMips && !IsAndroid && !IsHexagon) { if (Distro.IsRedhat() || Distro.IsOpenSUSE() || Distro.IsAlpineLinux() || (Distro.IsUbuntu() && Distro >= Distro::UbuntuMaverick)) ExtraOpts.push_back("--hash-style=gnu"); @@ -277,7 +317,7 @@ Linux::Linux(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) addMultilibsFilePaths(D, Multilibs, Multilib, GCCInstallation.getInstallPath(), Paths); - // Sourcery CodeBench MIPS toolchain holds some libraries under + // Sourcery CodeBench MAXIS/MIPS toolchain holds some libraries under // a biarch-like suffix of the GCC installation. addPathIfExists(D, GCCInstallation.getInstallPath() + Multilib.gccSuffix(), Paths); @@ -389,10 +429,12 @@ std::string Linux::computeSysRoot() const { if (!getDriver().SysRoot.empty()) return getDriver().SysRoot; - if (!GCCInstallation.isValid() || !tools::isMipsArch(getTriple().getArch())) + if (!GCCInstallation.isValid() + || !tools::isMaxisArch(getTriple().getArch()) + || !tools::isMipsArch(getTriple().getArch())) return std::string(); - // Standalone MIPS toolchains use different names for sysroot folder + // Standalone MAXIS/MIPS toolchains use different names for sysroot folder // and put it into different places. Here we try to check some known // variants. @@ -477,6 +519,26 @@ std::string Linux::getDynamicLinker(const ArgList &Args) const { Loader = HF ? "ld-linux-armhf.so.3" : "ld-linux.so.3"; break; } + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: { + bool LE = (Triple.getArch() == llvm::Triple::maxisel) || + (Triple.getArch() == llvm::Triple::maxis64el); + bool IsNaN2008 = tools::maxis::isNaN2008(Args, Triple); + + LibDir = "lib" + tools::maxis::getMaxisABILibSuffix(Args, Triple); + + if (tools::maxis::isUCLibc(Args)) + Loader = IsNaN2008 ? "ld-uClibc-maxisn8.so.0" : "ld-uClibc.so.0"; + else if (!Triple.hasEnvironment() && + Triple.getVendor() == llvm::Triple::VendorType::MaxisTechnologies) + Loader = LE ? "ld-musl-maxisel.so.1" : "ld-musl-maxis.so.1"; + else + Loader = IsNaN2008 ? "ld-linux-maxisn8.so.1" : "ld.so.1"; + + break; + } case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -615,6 +677,14 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, "/usr/include/armeb-linux-gnueabi"}; const StringRef ARMEBHFMultiarchIncludeDirs[] = { "/usr/include/armeb-linux-gnueabihf"}; + const StringRef MAXISMultiarchIncludeDirs[] = {"/usr/include/maxis-linux-gnu"}; + const StringRef MAXISELMultiarchIncludeDirs[] = { + "/usr/include/maxisel-linux-gnu"}; + const StringRef MAXIS64MultiarchIncludeDirs[] = { + "/usr/include/maxis64-linux-gnu", "/usr/include/maxis64-linux-gnuabi64"}; + const StringRef MAXIS64ELMultiarchIncludeDirs[] = { + "/usr/include/maxis64el-linux-gnu", + "/usr/include/maxis64el-linux-gnuabi64"}; const StringRef MIPSMultiarchIncludeDirs[] = {"/usr/include/mips-linux-gnu"}; const StringRef MIPSELMultiarchIncludeDirs[] = { "/usr/include/mipsel-linux-gnu"}; @@ -661,6 +731,18 @@ void Linux::AddClangSystemIncludeArgs(const ArgList &DriverArgs, else MultiarchIncludeDirs = ARMEBMultiarchIncludeDirs; break; + case llvm::Triple::maxis: + MultiarchIncludeDirs = MAXISMultiarchIncludeDirs; + break; + case llvm::Triple::maxisel: + MultiarchIncludeDirs = MAXISELMultiarchIncludeDirs; + break; + case llvm::Triple::maxis64: + MultiarchIncludeDirs = MAXIS64MultiarchIncludeDirs; + break; + case llvm::Triple::maxis64el: + MultiarchIncludeDirs = MAXIS64ELMultiarchIncludeDirs; + break; case llvm::Triple::mips: MultiarchIncludeDirs = MIPSMultiarchIncludeDirs; break; @@ -823,6 +905,8 @@ bool Linux::isPIEDefault() const { SanitizerMask Linux::getSupportedSanitizers() const { const bool IsX86 = getTriple().getArch() == llvm::Triple::x86; const bool IsX86_64 = getTriple().getArch() == llvm::Triple::x86_64; + const bool IsMAXIS64 = getTriple().getArch() == llvm::Triple::maxis64 || + getTriple().getArch() == llvm::Triple::maxis64el; const bool IsMIPS64 = getTriple().getArch() == llvm::Triple::mips64 || getTriple().getArch() == llvm::Triple::mips64el; const bool IsPowerPC64 = getTriple().getArch() == llvm::Triple::ppc64 || @@ -840,19 +924,19 @@ SanitizerMask Linux::getSupportedSanitizers() const { Res |= SanitizerKind::KernelAddress; Res |= SanitizerKind::Vptr; Res |= SanitizerKind::SafeStack; - if (IsX86_64 || IsMIPS64 || IsAArch64) + if (IsX86_64 || IsMAXIS64 || IsMIPS64 || IsAArch64) Res |= SanitizerKind::DataFlow; - if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64) + if (IsX86_64 || IsMAXIS64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64) Res |= SanitizerKind::Leak; - if (IsX86_64 || IsMIPS64 || IsAArch64 || IsPowerPC64) + if (IsX86_64 || IsMAXIS64 || IsMIPS64 || IsAArch64 || IsPowerPC64) Res |= SanitizerKind::Thread; - if (IsX86_64 || IsMIPS64 || IsPowerPC64 || IsAArch64) + if (IsX86_64 || IsMAXIS64 || IsMIPS64 || IsPowerPC64 || IsAArch64) Res |= SanitizerKind::Memory; - if (IsX86_64 || IsMIPS64) + if (IsX86_64 || IsMAXIS64 || IsMIPS64) Res |= SanitizerKind::Efficiency; if (IsX86 || IsX86_64) Res |= SanitizerKind::Function; - if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch) + if (IsX86_64 || IsMAXIS64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch) Res |= SanitizerKind::Scudo; if (IsAArch64) Res |= SanitizerKind::HWAddress; diff --git a/tools/clang/lib/Driver/ToolChains/MaxisLinux.cpp b/tools/clang/lib/Driver/ToolChains/MaxisLinux.cpp new file mode 100644 index 00000000..d361c86e --- /dev/null +++ b/tools/clang/lib/Driver/ToolChains/MaxisLinux.cpp @@ -0,0 +1,128 @@ +//===--- Maxis.cpp - Maxis ToolChain Implementations --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MaxisLinux.h" +#include "Arch/Maxis.h" +#include "CommonArgs.h" +#include "clang/Config/config.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" + +using namespace clang::driver; +using namespace clang::driver::toolchains; +using namespace clang; +using namespace llvm::opt; + +/// Maxis Toolchain +MaxisLLVMToolChain::MaxisLLVMToolChain(const Driver &D, + const llvm::Triple &Triple, + const ArgList &Args) + : Linux(D, Triple, Args) { + // Select the correct multilib according to the given arguments. + DetectedMultilibs Result; + findMAXISMultilibs(D, Triple, "", Args, Result); + Multilibs = Result.Multilibs; + SelectedMultilib = Result.SelectedMultilib; + + // Find out the library suffix based on the ABI. + LibSuffix = tools::maxis::getMaxisABILibSuffix(Args, Triple); + getFilePaths().clear(); + getFilePaths().push_back(computeSysRoot() + "/usr/lib" + LibSuffix); +} + +void MaxisLLVMToolChain::AddClangSystemIncludeArgs( + const ArgList &DriverArgs, ArgStringList &CC1Args) const { + if (DriverArgs.hasArg(clang::driver::options::OPT_nostdinc)) + return; + + const Driver &D = getDriver(); + + if (!DriverArgs.hasArg(options::OPT_nobuiltininc)) { + SmallString<128> P(D.ResourceDir); + llvm::sys::path::append(P, "include"); + addSystemInclude(DriverArgs, CC1Args, P); + } + + if (DriverArgs.hasArg(options::OPT_nostdlibinc)) + return; + + const auto &Callback = Multilibs.includeDirsCallback(); + if (Callback) { + for (const auto &Path : Callback(SelectedMultilib)) + addExternCSystemIncludeIfExists(DriverArgs, CC1Args, + D.getInstalledDir() + Path); + } +} + +Tool *MaxisLLVMToolChain::buildLinker() const { + return new tools::gnutools::Linker(*this); +} + +std::string MaxisLLVMToolChain::computeSysRoot() const { + if (!getDriver().SysRoot.empty()) + return getDriver().SysRoot + SelectedMultilib.osSuffix(); + + const std::string InstalledDir(getDriver().getInstalledDir()); + std::string SysRootPath = + InstalledDir + "/../sysroot" + SelectedMultilib.osSuffix(); + if (llvm::sys::fs::exists(SysRootPath)) + return SysRootPath; + + return std::string(); +} + +ToolChain::CXXStdlibType +MaxisLLVMToolChain::GetCXXStdlibType(const ArgList &Args) const { + Arg *A = Args.getLastArg(options::OPT_stdlib_EQ); + if (A) { + StringRef Value = A->getValue(); + if (Value != "libc++") + getDriver().Diag(clang::diag::err_drv_invalid_stdlib_name) + << A->getAsString(Args); + } + + return ToolChain::CST_Libcxx; +} + +std::string MaxisLLVMToolChain::findLibCxxIncludePath() const { + if (const auto &Callback = Multilibs.includeDirsCallback()) { + for (std::string Path : Callback(SelectedMultilib)) { + Path = getDriver().getInstalledDir() + Path + "/c++/v1"; + if (llvm::sys::fs::exists(Path)) { + return Path; + } + } + } + return ""; +} + +void MaxisLLVMToolChain::AddCXXStdlibLibArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + assert((GetCXXStdlibType(Args) == ToolChain::CST_Libcxx) && + "Only -lc++ (aka libxx) is supported in this toolchain."); + + CmdArgs.push_back("-lc++"); + CmdArgs.push_back("-lc++abi"); + CmdArgs.push_back("-lunwind"); +} + +std::string MaxisLLVMToolChain::getCompilerRT(const ArgList &Args, + StringRef Component, + bool Shared) const { + SmallString<128> Path(getDriver().ResourceDir); + llvm::sys::path::append(Path, SelectedMultilib.osSuffix(), "lib" + LibSuffix, + getOS()); + llvm::sys::path::append(Path, Twine("libclang_rt." + Component + "-" + + "maxis" + (Shared ? ".so" : ".a"))); + return Path.str(); +} diff --git a/tools/clang/lib/Driver/ToolChains/MaxisLinux.h b/tools/clang/lib/Driver/ToolChains/MaxisLinux.h new file mode 100644 index 00000000..a2701231 --- /dev/null +++ b/tools/clang/lib/Driver/ToolChains/MaxisLinux.h @@ -0,0 +1,62 @@ +//===--- Maxis.h - Maxis ToolChain Implementations ----------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MAXIS_LINUX_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MAXIS_LINUX_H + +#include "Linux.h" +#include "clang/Driver/ToolChain.h" + +namespace clang { +namespace driver { +namespace toolchains { + +class LLVM_LIBRARY_VISIBILITY MaxisLLVMToolChain : public Linux { +protected: + Tool *buildLinker() const override; + +public: + MaxisLLVMToolChain(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args); + + void + AddClangSystemIncludeArgs(const llvm::opt::ArgList &DriverArgs, + llvm::opt::ArgStringList &CC1Args) const override; + + CXXStdlibType GetCXXStdlibType(const llvm::opt::ArgList &Args) const override; + + std::string findLibCxxIncludePath() const override; + + void AddCXXStdlibLibArgs(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const override; + + std::string getCompilerRT(const llvm::opt::ArgList &Args, StringRef Component, + bool Shared = false) const override; + + std::string computeSysRoot() const override; + + RuntimeLibType GetDefaultRuntimeLibType() const override { + return GCCInstallation.isValid() ? RuntimeLibType::RLT_Libgcc + : RuntimeLibType::RLT_CompilerRT; + } + + const char *getDefaultLinker() const override { + return "lld"; + } + +private: + Multilib SelectedMultilib; + std::string LibSuffix; +}; + +} // end namespace toolchains +} // end namespace driver +} // end namespace clang + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_MAXIS_LINUX_H diff --git a/tools/clang/lib/Driver/ToolChains/NaCl.cpp b/tools/clang/lib/Driver/ToolChains/NaCl.cpp index 128478d6..115c3d87 100644 --- a/tools/clang/lib/Driver/ToolChains/NaCl.cpp +++ b/tools/clang/lib/Driver/ToolChains/NaCl.cpp @@ -93,6 +93,8 @@ void nacltools::Linker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("armelf_nacl"); else if (Arch == llvm::Triple::x86_64) CmdArgs.push_back("elf_x86_64_nacl"); + else if (Arch == llvm::Triple::maxisel) + CmdArgs.push_back("maxiselelf_nacl"); else if (Arch == llvm::Triple::mipsel) CmdArgs.push_back("mipselelf_nacl"); else @@ -154,11 +156,12 @@ void nacltools::Linker::ConstructJob(Compilation &C, const JobAction &JA, // in the group for C++. if (Args.hasArg(options::OPT_pthread) || Args.hasArg(options::OPT_pthreads) || D.CCCIsCXX()) { - // Gold, used by Mips, handles nested groups differently than ld, and + // Gold, used by Maxis/Mips, handles nested groups differently than ld, and // without '-lnacl' it prefers symbols from libpthread.a over libnacl.a, // which is not a desired behaviour here. // See https://sourceware.org/ml/binutils/2015-03/msg00034.html - if (getToolChain().getArch() == llvm::Triple::mipsel) + if (getToolChain().getArch() == llvm::Triple::maxisel + || getToolChain().getArch() == llvm::Triple::mipsel) CmdArgs.push_back("-lnacl"); CmdArgs.push_back("-lpthread"); @@ -172,10 +175,11 @@ void nacltools::Linker::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back("-lgcc_s"); CmdArgs.push_back("--no-as-needed"); - // Mips needs to create and use pnacl_legacy library that contains + // Maxis/Mips needs to create and use pnacl_legacy library that contains // definitions from bitcode/pnaclmm.c and definitions for // __nacl_tp_tls_offset() and __nacl_tp_tdb_offset(). - if (getToolChain().getArch() == llvm::Triple::mipsel) + if (getToolChain().getArch() == llvm::Triple::maxisel + || getToolChain().getArch() == llvm::Triple::mipsel) CmdArgs.push_back("-lpnacl_legacy"); CmdArgs.push_back("--end-group"); @@ -239,6 +243,12 @@ NaClToolChain::NaClToolChain(const Driver &D, const llvm::Triple &Triple, prog_paths.push_back(ProgPath + "arm-nacl/bin"); file_paths.push_back(ToolPath + "arm-nacl"); break; + case llvm::Triple::maxisel: + file_paths.push_back(FilePath + "maxisel-nacl/lib"); + file_paths.push_back(FilePath + "maxisel-nacl/usr/lib"); + prog_paths.push_back(ProgPath + "bin"); + file_paths.push_back(ToolPath + "maxisel-nacl"); + break; case llvm::Triple::mipsel: file_paths.push_back(FilePath + "mipsel-nacl/lib"); file_paths.push_back(FilePath + "mipsel-nacl/usr/lib"); @@ -287,6 +297,9 @@ void NaClToolChain::AddClangSystemIncludeArgs(const ArgList &DriverArgs, case llvm::Triple::x86_64: llvm::sys::path::append(P, "x86_64-nacl/usr/include"); break; + case llvm::Triple::maxisel: + llvm::sys::path::append(P, "maxisel-nacl/usr/include"); + break; case llvm::Triple::mipsel: llvm::sys::path::append(P, "mipsel-nacl/usr/include"); break; @@ -323,6 +336,9 @@ std::string NaClToolChain::findLibCxxIncludePath() const { case llvm::Triple::x86_64: llvm::sys::path::append(P, "x86_64-nacl/include/c++/v1"); return P.str(); + case llvm::Triple::maxisel: + llvm::sys::path::append(P, "maxisel-nacl/include/c++/v1"); + return P.str(); case llvm::Triple::mipsel: llvm::sys::path::append(P, "mipsel-nacl/include/c++/v1"); return P.str(); diff --git a/tools/clang/lib/Driver/ToolChains/NaCl.h b/tools/clang/lib/Driver/ToolChains/NaCl.h index 31af3a53..6af669cb 100644 --- a/tools/clang/lib/Driver/ToolChains/NaCl.h +++ b/tools/clang/lib/Driver/ToolChains/NaCl.h @@ -61,7 +61,8 @@ class LLVM_LIBRARY_VISIBILITY NaClToolChain : public Generic_ELF { llvm::opt::ArgStringList &CmdArgs) const override; bool IsIntegratedAssemblerDefault() const override { - return getTriple().getArch() == llvm::Triple::mipsel; + return getTriple().getArch() == llvm::Triple::maxisel + || getTriple().getArch() == llvm::Triple::mipsel; } // Get the path to the file containing NaCl's ARM macros. diff --git a/tools/clang/lib/Driver/ToolChains/NetBSD.cpp b/tools/clang/lib/Driver/ToolChains/NetBSD.cpp index 0db6578f..1dd463b2 100644 --- a/tools/clang/lib/Driver/ToolChains/NetBSD.cpp +++ b/tools/clang/lib/Driver/ToolChains/NetBSD.cpp @@ -9,6 +9,7 @@ #include "NetBSD.h" #include "Arch/ARM.h" +#include "Arch/Maxis.h" #include "Arch/Mips.h" #include "Arch/Sparc.h" #include "CommonArgs.h" @@ -50,6 +51,30 @@ void netbsd::Assembler::ConstructJob(Compilation &C, const JobAction &JA, break; } + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: { + StringRef CPUName; + StringRef ABIName; + maxis::getMaxisCPUAndABI(Args, getToolChain().getTriple(), CPUName, ABIName); + + CmdArgs.push_back("-march"); + CmdArgs.push_back(CPUName.data()); + + CmdArgs.push_back("-mabi"); + CmdArgs.push_back(maxis::getGnuCompatibleMaxisABIName(ABIName).data()); + + if (getToolChain().getArch() == llvm::Triple::maxis || + getToolChain().getArch() == llvm::Triple::maxis64) + CmdArgs.push_back("-EB"); + else + CmdArgs.push_back("-EL"); + + AddAssemblerKPIC(getToolChain(), Args, CmdArgs); + break; + } + case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -175,6 +200,22 @@ void netbsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, break; } break; + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + if (maxis::hasMaxisAbiArg(Args, "32")) { + CmdArgs.push_back("-m"); + if (getToolChain().getArch() == llvm::Triple::maxis64) + CmdArgs.push_back("elf32btsmip"); + else + CmdArgs.push_back("elf32ltsmip"); + } else if (maxis::hasMaxisAbiArg(Args, "64")) { + CmdArgs.push_back("-m"); + if (getToolChain().getArch() == llvm::Triple::maxis64) + CmdArgs.push_back("elf64btsmip"); + else + CmdArgs.push_back("elf64ltsmip"); + } + break; case llvm::Triple::mips64: case llvm::Triple::mips64el: if (mips::hasMipsAbiArg(Args, "32")) { @@ -353,6 +394,13 @@ NetBSD::NetBSD(const Driver &D, const llvm::Triple &Triple, const ArgList &Args) break; } break; + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + if (tools::maxis::hasMaxisAbiArg(Args, "o32")) + getFilePaths().push_back("=/usr/lib/o32"); + else if (tools::maxis::hasMaxisAbiArg(Args, "64")) + getFilePaths().push_back("=/usr/lib/64"); + break; case llvm::Triple::mips64: case llvm::Triple::mips64el: if (tools::mips::hasMipsAbiArg(Args, "o32")) diff --git a/tools/clang/lib/Driver/ToolChains/OpenBSD.cpp b/tools/clang/lib/Driver/ToolChains/OpenBSD.cpp index fbb84a62..7982abf9 100644 --- a/tools/clang/lib/Driver/ToolChains/OpenBSD.cpp +++ b/tools/clang/lib/Driver/ToolChains/OpenBSD.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "OpenBSD.h" +#include "Arch/Maxis.h" #include "Arch/Mips.h" #include "Arch/Sparc.h" #include "CommonArgs.h" @@ -58,6 +59,24 @@ void openbsd::Assembler::ConstructJob(Compilation &C, const JobAction &JA, break; } + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: { + StringRef CPUName; + StringRef ABIName; + maxis::getMaxisCPUAndABI(Args, getToolChain().getTriple(), CPUName, ABIName); + + CmdArgs.push_back("-mabi"); + CmdArgs.push_back(maxis::getGnuCompatibleMaxisABIName(ABIName).data()); + + if (getToolChain().getArch() == llvm::Triple::maxis64) + CmdArgs.push_back("-EB"); + else + CmdArgs.push_back("-EL"); + + AddAssemblerKPIC(getToolChain(), Args, CmdArgs); + break; + } + case llvm::Triple::mips64: case llvm::Triple::mips64el: { StringRef CPUName; @@ -75,7 +94,7 @@ void openbsd::Assembler::ConstructJob(Compilation &C, const JobAction &JA, AddAssemblerKPIC(getToolChain(), Args, CmdArgs); break; } - + default: break; } @@ -108,9 +127,11 @@ void openbsd::Linker::ConstructJob(Compilation &C, const JobAction &JA, // handled somewhere else. Args.ClaimAllArgs(options::OPT_w); - if (getToolChain().getArch() == llvm::Triple::mips64) + if (getToolChain().getArch() == llvm::Triple::maxis64 || + getToolChain().getArch() == llvm::Triple::mips64) CmdArgs.push_back("-EB"); - else if (getToolChain().getArch() == llvm::Triple::mips64el) + else if (getToolChain().getArch() == llvm::Triple::maxis64el || + getToolChain().getArch() == llvm::Triple::mips64el) CmdArgs.push_back("-EL"); if (!Args.hasArg(options::OPT_nostdlib, options::OPT_shared)) { diff --git a/tools/clang/lib/Driver/XRayArgs.cpp b/tools/clang/lib/Driver/XRayArgs.cpp index 232bacd5..d78e6834 100644 --- a/tools/clang/lib/Driver/XRayArgs.cpp +++ b/tools/clang/lib/Driver/XRayArgs.cpp @@ -40,6 +40,10 @@ XRayArgs::XRayArgs(const ToolChain &TC, const ArgList &Args) { case llvm::Triple::arm: case llvm::Triple::aarch64: case llvm::Triple::ppc64le: + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: diff --git a/tools/clang/lib/Headers/msa.h b/tools/clang/lib/Headers/msa.h index da680f5c..36b82f7e 100644 --- a/tools/clang/lib/Headers/msa.h +++ b/tools/clang/lib/Headers/msa.h @@ -1,4 +1,4 @@ -/*===---- msa.h - MIPS MSA intrinsics --------------------------------------=== +/*===---- msa.h - MAXIS/MIPS MSA intrinsics --------------------------------------=== * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,7 +24,7 @@ #ifndef _MSA_H #define _MSA_H 1 -#if defined(__mips_msa) +#if defined(__maxis_msa) || defined(__mips_msa) typedef signed char v16i8 __attribute__((vector_size(16), aligned(16))); typedef signed char v16i8_b __attribute__((vector_size(16), aligned(1))); typedef unsigned char v16u8 __attribute__((vector_size(16), aligned(16))); @@ -580,4 +580,5 @@ typedef double v2f64_d __attribute__ ((vector_size(16), aligned(8))); #define __msa_cast_to_scalar_float __builtin_msa_cast_to_scalar_float #define __msa_cast_to_scalar_double __builtin_msa_cast_to_scalar_double #endif /* defined(__mips_msa) */ + #endif /* _MSA_H */ diff --git a/tools/clang/lib/Sema/SemaChecking.cpp b/tools/clang/lib/Sema/SemaChecking.cpp index 57af6b86..1fe1c0ba 100644 --- a/tools/clang/lib/Sema/SemaChecking.cpp +++ b/tools/clang/lib/Sema/SemaChecking.cpp @@ -1244,6 +1244,13 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, if (CheckAArch64BuiltinFunctionCall(BuiltinID, TheCall)) return ExprError(); break; + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + if (CheckMaxisBuiltinFunctionCall(BuiltinID, TheCall)) + return ExprError(); + break; case llvm::Triple::mips: case llvm::Triple::mipsel: case llvm::Triple::mips64: @@ -1618,6 +1625,186 @@ bool Sema::CheckAArch64BuiltinFunctionCall(unsigned BuiltinID, return SemaBuiltinConstantArgRange(TheCall, i, l, u + l); } +// CheckMaxisBuiltinFunctionCall - Checks the constant value passed to the +// intrinsic is correct. The switch statement is ordered by DSP, MSA. The +// ordering for DSP is unspecified. MSA is ordered by the data format used +// by the underlying instruction i.e., df/m, df/n and then by size. +// +// FIXME: The size tests here should instead be tablegen'd along with the +// definitions from include/clang/Basic/BuiltinsMaxis.def. +// FIXME: GCC is strict on signedness for some of these intrinsics, we should +// be too. +bool Sema::CheckMaxisBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { + unsigned i = 0, l = 0, u = 0, m = 0; + switch (BuiltinID) { + default: return false; + case Maxis::BI__builtin_maxis_wrdsp: i = 1; l = 0; u = 63; break; + case Maxis::BI__builtin_maxis_rddsp: i = 0; l = 0; u = 63; break; + case Maxis::BI__builtin_maxis_append: i = 2; l = 0; u = 31; break; + case Maxis::BI__builtin_maxis_balign: i = 2; l = 0; u = 3; break; + case Maxis::BI__builtin_maxis_precr_sra_ph_w: i = 2; l = 0; u = 31; break; + case Maxis::BI__builtin_maxis_precr_sra_r_ph_w: i = 2; l = 0; u = 31; break; + case Maxis::BI__builtin_maxis_prepend: i = 2; l = 0; u = 31; break; + // MSA instrinsics. Instructions (which the intrinsics maps to) which use the + // df/m field. + // These intrinsics take an unsigned 3 bit immediate. + case Maxis::BI__builtin_msa_bclri_b: + case Maxis::BI__builtin_msa_bnegi_b: + case Maxis::BI__builtin_msa_bseti_b: + case Maxis::BI__builtin_msa_sat_s_b: + case Maxis::BI__builtin_msa_sat_u_b: + case Maxis::BI__builtin_msa_slli_b: + case Maxis::BI__builtin_msa_srai_b: + case Maxis::BI__builtin_msa_srari_b: + case Maxis::BI__builtin_msa_srli_b: + case Maxis::BI__builtin_msa_srlri_b: i = 1; l = 0; u = 7; break; + case Maxis::BI__builtin_msa_binsli_b: + case Maxis::BI__builtin_msa_binsri_b: i = 2; l = 0; u = 7; break; + // These intrinsics take an unsigned 4 bit immediate. + case Maxis::BI__builtin_msa_bclri_h: + case Maxis::BI__builtin_msa_bnegi_h: + case Maxis::BI__builtin_msa_bseti_h: + case Maxis::BI__builtin_msa_sat_s_h: + case Maxis::BI__builtin_msa_sat_u_h: + case Maxis::BI__builtin_msa_slli_h: + case Maxis::BI__builtin_msa_srai_h: + case Maxis::BI__builtin_msa_srari_h: + case Maxis::BI__builtin_msa_srli_h: + case Maxis::BI__builtin_msa_srlri_h: i = 1; l = 0; u = 15; break; + case Maxis::BI__builtin_msa_binsli_h: + case Maxis::BI__builtin_msa_binsri_h: i = 2; l = 0; u = 15; break; + // These intrinsics take an unsigned 5 bit immedate. + // The first block of intrinsics actually have an unsigned 5 bit field, + // not a df/n field. + case Maxis::BI__builtin_msa_clei_u_b: + case Maxis::BI__builtin_msa_clei_u_h: + case Maxis::BI__builtin_msa_clei_u_w: + case Maxis::BI__builtin_msa_clei_u_d: + case Maxis::BI__builtin_msa_clti_u_b: + case Maxis::BI__builtin_msa_clti_u_h: + case Maxis::BI__builtin_msa_clti_u_w: + case Maxis::BI__builtin_msa_clti_u_d: + case Maxis::BI__builtin_msa_maxi_u_b: + case Maxis::BI__builtin_msa_maxi_u_h: + case Maxis::BI__builtin_msa_maxi_u_w: + case Maxis::BI__builtin_msa_maxi_u_d: + case Maxis::BI__builtin_msa_mini_u_b: + case Maxis::BI__builtin_msa_mini_u_h: + case Maxis::BI__builtin_msa_mini_u_w: + case Maxis::BI__builtin_msa_mini_u_d: + case Maxis::BI__builtin_msa_addvi_b: + case Maxis::BI__builtin_msa_addvi_h: + case Maxis::BI__builtin_msa_addvi_w: + case Maxis::BI__builtin_msa_addvi_d: + case Maxis::BI__builtin_msa_bclri_w: + case Maxis::BI__builtin_msa_bnegi_w: + case Maxis::BI__builtin_msa_bseti_w: + case Maxis::BI__builtin_msa_sat_s_w: + case Maxis::BI__builtin_msa_sat_u_w: + case Maxis::BI__builtin_msa_slli_w: + case Maxis::BI__builtin_msa_srai_w: + case Maxis::BI__builtin_msa_srari_w: + case Maxis::BI__builtin_msa_srli_w: + case Maxis::BI__builtin_msa_srlri_w: + case Maxis::BI__builtin_msa_subvi_b: + case Maxis::BI__builtin_msa_subvi_h: + case Maxis::BI__builtin_msa_subvi_w: + case Maxis::BI__builtin_msa_subvi_d: i = 1; l = 0; u = 31; break; + case Maxis::BI__builtin_msa_binsli_w: + case Maxis::BI__builtin_msa_binsri_w: i = 2; l = 0; u = 31; break; + // These intrinsics take an unsigned 6 bit immediate. + case Maxis::BI__builtin_msa_bclri_d: + case Maxis::BI__builtin_msa_bnegi_d: + case Maxis::BI__builtin_msa_bseti_d: + case Maxis::BI__builtin_msa_sat_s_d: + case Maxis::BI__builtin_msa_sat_u_d: + case Maxis::BI__builtin_msa_slli_d: + case Maxis::BI__builtin_msa_srai_d: + case Maxis::BI__builtin_msa_srari_d: + case Maxis::BI__builtin_msa_srli_d: + case Maxis::BI__builtin_msa_srlri_d: i = 1; l = 0; u = 63; break; + case Maxis::BI__builtin_msa_binsli_d: + case Maxis::BI__builtin_msa_binsri_d: i = 2; l = 0; u = 63; break; + // These intrinsics take a signed 5 bit immediate. + case Maxis::BI__builtin_msa_ceqi_b: + case Maxis::BI__builtin_msa_ceqi_h: + case Maxis::BI__builtin_msa_ceqi_w: + case Maxis::BI__builtin_msa_ceqi_d: + case Maxis::BI__builtin_msa_clti_s_b: + case Maxis::BI__builtin_msa_clti_s_h: + case Maxis::BI__builtin_msa_clti_s_w: + case Maxis::BI__builtin_msa_clti_s_d: + case Maxis::BI__builtin_msa_clei_s_b: + case Maxis::BI__builtin_msa_clei_s_h: + case Maxis::BI__builtin_msa_clei_s_w: + case Maxis::BI__builtin_msa_clei_s_d: + case Maxis::BI__builtin_msa_maxi_s_b: + case Maxis::BI__builtin_msa_maxi_s_h: + case Maxis::BI__builtin_msa_maxi_s_w: + case Maxis::BI__builtin_msa_maxi_s_d: + case Maxis::BI__builtin_msa_mini_s_b: + case Maxis::BI__builtin_msa_mini_s_h: + case Maxis::BI__builtin_msa_mini_s_w: + case Maxis::BI__builtin_msa_mini_s_d: i = 1; l = -16; u = 15; break; + // These intrinsics take an unsigned 8 bit immediate. + case Maxis::BI__builtin_msa_andi_b: + case Maxis::BI__builtin_msa_nori_b: + case Maxis::BI__builtin_msa_ori_b: + case Maxis::BI__builtin_msa_shf_b: + case Maxis::BI__builtin_msa_shf_h: + case Maxis::BI__builtin_msa_shf_w: + case Maxis::BI__builtin_msa_xori_b: i = 1; l = 0; u = 255; break; + case Maxis::BI__builtin_msa_bseli_b: + case Maxis::BI__builtin_msa_bmnzi_b: + case Maxis::BI__builtin_msa_bmzi_b: i = 2; l = 0; u = 255; break; + // df/n format + // These intrinsics take an unsigned 4 bit immediate. + case Maxis::BI__builtin_msa_copy_s_b: + case Maxis::BI__builtin_msa_copy_u_b: + case Maxis::BI__builtin_msa_insve_b: + case Maxis::BI__builtin_msa_splati_b: i = 1; l = 0; u = 15; break; + case Maxis::BI__builtin_msa_sldi_b: i = 2; l = 0; u = 15; break; + // These intrinsics take an unsigned 3 bit immediate. + case Maxis::BI__builtin_msa_copy_s_h: + case Maxis::BI__builtin_msa_copy_u_h: + case Maxis::BI__builtin_msa_insve_h: + case Maxis::BI__builtin_msa_splati_h: i = 1; l = 0; u = 7; break; + case Maxis::BI__builtin_msa_sldi_h: i = 2; l = 0; u = 7; break; + // These intrinsics take an unsigned 2 bit immediate. + case Maxis::BI__builtin_msa_copy_s_w: + case Maxis::BI__builtin_msa_copy_u_w: + case Maxis::BI__builtin_msa_insve_w: + case Maxis::BI__builtin_msa_splati_w: i = 1; l = 0; u = 3; break; + case Maxis::BI__builtin_msa_sldi_w: i = 2; l = 0; u = 3; break; + // These intrinsics take an unsigned 1 bit immediate. + case Maxis::BI__builtin_msa_copy_s_d: + case Maxis::BI__builtin_msa_copy_u_d: + case Maxis::BI__builtin_msa_insve_d: + case Maxis::BI__builtin_msa_splati_d: i = 1; l = 0; u = 1; break; + case Maxis::BI__builtin_msa_sldi_d: i = 2; l = 0; u = 1; break; + // Memory offsets and immediate loads. + // These intrinsics take a signed 10 bit immediate. + case Maxis::BI__builtin_msa_ldi_b: i = 0; l = -128; u = 255; break; + case Maxis::BI__builtin_msa_ldi_h: + case Maxis::BI__builtin_msa_ldi_w: + case Maxis::BI__builtin_msa_ldi_d: i = 0; l = -512; u = 511; break; + case Maxis::BI__builtin_msa_ld_b: i = 1; l = -512; u = 511; m = 16; break; + case Maxis::BI__builtin_msa_ld_h: i = 1; l = -1024; u = 1022; m = 16; break; + case Maxis::BI__builtin_msa_ld_w: i = 1; l = -2048; u = 2044; m = 16; break; + case Maxis::BI__builtin_msa_ld_d: i = 1; l = -4096; u = 4088; m = 16; break; + case Maxis::BI__builtin_msa_st_b: i = 2; l = -512; u = 511; m = 16; break; + case Maxis::BI__builtin_msa_st_h: i = 2; l = -1024; u = 1022; m = 16; break; + case Maxis::BI__builtin_msa_st_w: i = 2; l = -2048; u = 2044; m = 16; break; + case Maxis::BI__builtin_msa_st_d: i = 2; l = -4096; u = 4088; m = 16; break; + } + + if (!m) + return SemaBuiltinConstantArgRange(TheCall, i, l, u); + + return SemaBuiltinConstantArgRange(TheCall, i, l, u) || + SemaBuiltinConstantArgMultiple(TheCall, i, m); +} + // CheckMipsBuiltinFunctionCall - Checks the constant value passed to the // intrinsic is correct. The switch statement is ordered by DSP, MSA. The // ordering for DSP is unspecified. MSA is ordered by the data format used diff --git a/tools/clang/lib/Sema/SemaDeclAttr.cpp b/tools/clang/lib/Sema/SemaDeclAttr.cpp index 21fe46ad..272a51f0 100644 --- a/tools/clang/lib/Sema/SemaDeclAttr.cpp +++ b/tools/clang/lib/Sema/SemaDeclAttr.cpp @@ -5282,6 +5282,65 @@ static void handleMSP430InterruptAttr(Sema &S, Decl *D, D->addAttr(UsedAttr::CreateImplicit(S.Context)); } +static void handleMaxisInterruptAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + // Only one optional argument permitted. + if (Attr.getNumArgs() > 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) + << Attr.getName() << 1; + return; + } + + StringRef Str; + SourceLocation ArgLoc; + + if (Attr.getNumArgs() == 0) + Str = ""; + else if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str, &ArgLoc)) + return; + + // Semantic checks for a function with the 'interrupt' attribute for MAXIS: + // a) Must be a function. + // b) Must have no parameters. + // c) Must have the 'void' return type. + // d) Cannot have the 'maxis16' attribute, as that instruction set + // lacks the 'eret' instruction. + // e) The attribute itself must either have no argument or one of the + // valid interrupt types, see [MaxisInterruptDocs]. + + if (!isFunctionOrMethod(D)) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'interrupt'" << ExpectedFunctionOrMethod; + return; + } + + if (hasFunctionProto(D) && getFunctionOrMethodNumParams(D) != 0) { + S.Diag(D->getLocation(), diag::warn_maxis_interrupt_attribute) + << 0; + return; + } + + if (!getFunctionOrMethodResultType(D)->isVoidType()) { + S.Diag(D->getLocation(), diag::warn_maxis_interrupt_attribute) + << 1; + return; + } + + if (checkAttrMutualExclusion(S, D, Attr.getRange(), + Attr.getName())) + return; + + MaxisInterruptAttr::InterruptType Kind; + if (!MaxisInterruptAttr::ConvertStrToInterruptType(Str, Kind)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_type_not_supported) + << Attr.getName() << "'" + std::string(Str) + "'"; + return; + } + + D->addAttr(::new (S.Context) MaxisInterruptAttr( + Attr.getLoc(), S.Context, Kind, Attr.getAttributeSpellingListIndex())); +} + static void handleMipsInterruptAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Only one optional argument permitted. diff --git a/tools/lld/ELF/Arch/Maxis.cpp b/tools/lld/ELF/Arch/Maxis.cpp new file mode 100644 index 00000000..02a25443 --- /dev/null +++ b/tools/lld/ELF/Arch/Maxis.cpp @@ -0,0 +1,682 @@ +//===- MAXIS.cpp -----------------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "OutputSections.h" +#include "Symbols.h" +#include "SyntheticSections.h" +#include "Target.h" +#include "Thunks.h" +#include "lld/Common/ErrorHandler.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/Endian.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::support::endian; +using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; + +namespace { +template class MAXIS final : public TargetInfo { +public: + MAXIS(); + uint32_t calcEFlags() const override; + RelExpr getRelExpr(RelType Type, const Symbol &S, + const uint8_t *Loc) const override; + int64_t getImplicitAddend(const uint8_t *Buf, RelType Type) const override; + bool isPicRel(RelType Type) const override; + RelType getDynRel(RelType Type) const override; + void writeGotPlt(uint8_t *Buf, const Symbol &S) const override; + void writePltHeader(uint8_t *Buf) const override; + void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, + int32_t Index, unsigned RelOff) const override; + bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, + uint64_t BranchAddr, const Symbol &S) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + bool usesOnlyLowPageBits(RelType Type) const override; +}; +} // namespace + +template MAXIS::MAXIS() { + GotPltHeaderEntriesNum = 2; + DefaultMaxPageSize = 65536; + GotEntrySize = sizeof(typename ELFT::uint); + GotPltEntrySize = sizeof(typename ELFT::uint); + PltEntrySize = 16; + PltHeaderSize = 32; + CopyRel = R_MAXIS_COPY; + PltRel = R_MAXIS_JUMP_SLOT; + NeedsThunks = true; + TrapInstr = 0xefefefef; + + if (ELFT::Is64Bits) { + RelativeRel = (R_MAXIS_64 << 8) | R_MAXIS_REL32; + TlsGotRel = R_MAXIS_TLS_TPREL64; + TlsModuleIndexRel = R_MAXIS_TLS_DTPMOD64; + TlsOffsetRel = R_MAXIS_TLS_DTPREL64; + } else { + RelativeRel = R_MAXIS_REL32; + TlsGotRel = R_MAXIS_TLS_TPREL32; + TlsModuleIndexRel = R_MAXIS_TLS_DTPMOD32; + TlsOffsetRel = R_MAXIS_TLS_DTPREL32; + } +} + +template uint32_t MAXIS::calcEFlags() const { + return calcMaxisEFlags(); +} + +template +RelExpr MAXIS::getRelExpr(RelType Type, const Symbol &S, + const uint8_t *Loc) const { + // See comment in the calculateMaxisRelChain. + if (ELFT::Is64Bits || Config->MaxisN32Abi) + Type &= 0xff; + + switch (Type) { + case R_MAXIS_JALR: + case R_MICROMAXIS_JALR: + return R_HINT; + case R_MAXIS_GPREL16: + case R_MAXIS_GPREL32: + case R_MICROMAXIS_GPREL16: + case R_MICROMAXIS_GPREL7_S2: + return R_MAXIS_GOTREL; + case R_MAXIS_26: + case R_MICROMAXIS_26_S1: + return R_PLT; + case R_MICROMAXIS_PC26_S1: + return R_PLT_PC; + case R_MAXIS_HI16: + case R_MAXIS_LO16: + case R_MAXIS_HIGHER: + case R_MAXIS_HIGHEST: + case R_MICROMAXIS_HI16: + case R_MICROMAXIS_LO16: + case R_MICROMAXIS_HIGHER: + case R_MICROMAXIS_HIGHEST: + // R_MAXIS_HI16/R_MAXIS_LO16 relocations against _gp_disp calculate + // offset between start of function and 'gp' value which by default + // equal to the start of .got section. In that case we consider these + // relocations as relative. + if (&S == ElfSym::MaxisGpDisp) + return R_MAXIS_GOT_GP_PC; + if (&S == ElfSym::MaxisLocalGp) + return R_MAXIS_GOT_GP; + LLVM_FALLTHROUGH; + case R_MAXIS_32: + case R_MAXIS_64: + case R_MAXIS_GOT_OFST: + case R_MAXIS_SUB: + case R_MAXIS_TLS_DTPREL_HI16: + case R_MAXIS_TLS_DTPREL_LO16: + case R_MAXIS_TLS_DTPREL32: + case R_MAXIS_TLS_DTPREL64: + case R_MAXIS_TLS_TPREL_HI16: + case R_MAXIS_TLS_TPREL_LO16: + case R_MAXIS_TLS_TPREL32: + case R_MAXIS_TLS_TPREL64: + case R_MICROMAXIS_GOT_OFST: + case R_MICROMAXIS_SUB: + case R_MICROMAXIS_TLS_DTPREL_HI16: + case R_MICROMAXIS_TLS_DTPREL_LO16: + case R_MICROMAXIS_TLS_TPREL_HI16: + case R_MICROMAXIS_TLS_TPREL_LO16: + return R_ABS; + case R_MAXIS_PC32: + case R_MAXIS_PC16: + case R_MAXIS_PC19_S2: + case R_MAXIS_PC21_S2: + case R_MAXIS_PC26_S2: + case R_MAXIS_PCHI16: + case R_MAXIS_PCLO16: + case R_MICROMAXIS_PC7_S1: + case R_MICROMAXIS_PC10_S1: + case R_MICROMAXIS_PC16_S1: + case R_MICROMAXIS_PC18_S3: + case R_MICROMAXIS_PC19_S2: + case R_MICROMAXIS_PC23_S2: + case R_MICROMAXIS_PC21_S1: + return R_PC; + case R_MAXIS_GOT16: + case R_MICROMAXIS_GOT16: + if (S.isLocal()) + return R_MAXIS_GOT_LOCAL_PAGE; + LLVM_FALLTHROUGH; + case R_MAXIS_CALL16: + case R_MAXIS_GOT_DISP: + case R_MAXIS_TLS_GOTTPREL: + case R_MICROMAXIS_CALL16: + case R_MICROMAXIS_GOT_DISP: + case R_MICROMAXIS_TLS_GOTTPREL: + return R_MAXIS_GOT_OFF; + case R_MAXIS_CALL_HI16: + case R_MAXIS_CALL_LO16: + case R_MAXIS_GOT_HI16: + case R_MAXIS_GOT_LO16: + case R_MICROMAXIS_CALL_HI16: + case R_MICROMAXIS_CALL_LO16: + case R_MICROMAXIS_GOT_HI16: + case R_MICROMAXIS_GOT_LO16: + return R_MAXIS_GOT_OFF32; + case R_MAXIS_GOT_PAGE: + case R_MICROMAXIS_GOT_PAGE: + return R_MAXIS_GOT_LOCAL_PAGE; + case R_MAXIS_TLS_GD: + case R_MICROMAXIS_TLS_GD: + return R_MAXIS_TLSGD; + case R_MAXIS_TLS_LDM: + case R_MICROMAXIS_TLS_LDM: + return R_MAXIS_TLSLD; + case R_MAXIS_NONE: + return R_NONE; + default: + return R_INVALID; + } +} + +template bool MAXIS::isPicRel(RelType Type) const { + return Type == R_MAXIS_32 || Type == R_MAXIS_64; +} + +template RelType MAXIS::getDynRel(RelType Type) const { + return RelativeRel; +} + +template +void MAXIS::writeGotPlt(uint8_t *Buf, const Symbol &) const { + uint64_t VA = InX::Plt->getVA(); + if (isMicroMaxis()) + VA |= 1; + write32(Buf, VA); +} + +template static uint32_t readShuffle(const uint8_t *Loc) { + // The major opcode of a microMAXIS instruction needs to appear + // in the first 16-bit word (lowest address) for efficient hardware + // decode so that it knows if the instruction is 16-bit or 32-bit + // as early as possible. To do so, little-endian binaries keep 16-bit + // words in a big-endian order. That is why we have to swap these + // words to get a correct value. + uint32_t V = read32(Loc); + if (E == support::little) + return (V << 16) | (V >> 16); + return V; +} + +template +static void writeRelocation(uint8_t *Loc, uint64_t V, uint8_t BitsSize, + uint8_t Shift) { + uint32_t Instr = read32(Loc); + uint32_t Mask = 0xffffffff >> (32 - BitsSize); + uint32_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); + write32(Loc, Data); +} + +template +static void writeMicroRelocation32(uint8_t *Loc, uint64_t V, uint8_t BitsSize, + uint8_t Shift) { + // See comments in readShuffle for purpose of this code. + uint16_t *Words = (uint16_t *)Loc; + if (E == support::little) + std::swap(Words[0], Words[1]); + + writeRelocation(Loc, V, BitsSize, Shift); + + if (E == support::little) + std::swap(Words[0], Words[1]); +} + +template +static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize, + uint8_t Shift) { + uint16_t Instr = read16(Loc); + uint16_t Mask = 0xffff >> (16 - BitsSize); + uint16_t Data = (Instr & ~Mask) | ((V >> Shift) & Mask); + write16(Loc, Data); +} + +template void MAXIS::writePltHeader(uint8_t *Buf) const { + const endianness E = ELFT::TargetEndianness; + if (isMicroMaxis()) { + uint64_t GotPlt = InX::GotPlt->getVA(); + uint64_t Plt = InX::Plt->getVA(); + // Overwrite trap instructions written by Writer::writeTrapInstr. + memset(Buf, 0, PltHeaderSize); + + write16(Buf, isMaxisR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . + write16(Buf + 4, 0xff23); // lw $25, 0($3) + write16(Buf + 8, 0x0535); // subu16 $2, $2, $3 + write16(Buf + 10, 0x2525); // srl16 $2, $2, 2 + write16(Buf + 12, 0x3302); // addiu $24, $2, -2 + write16(Buf + 14, 0xfffe); + write16(Buf + 16, 0x0dff); // move $15, $31 + if (isMaxisR6()) { + write16(Buf + 18, 0x0f83); // move $28, $3 + write16(Buf + 20, 0x472b); // jalrc $25 + write16(Buf + 22, 0x0c00); // nop + relocateOne(Buf, R_MICROMAXIS_PC19_S2, GotPlt - Plt); + } else { + write16(Buf + 18, 0x45f9); // jalrc $25 + write16(Buf + 20, 0x0f83); // move $28, $3 + write16(Buf + 22, 0x0c00); // nop + relocateOne(Buf, R_MICROMAXIS_PC23_S2, GotPlt - Plt); + } + return; + } + + if (Config->MaxisN32Abi) { + write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) + write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(Buf + 16, 0x03e07825); // move $15, $31 + write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 + } else if (ELFT::Is64Bits) { + write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(Buf + 4, 0xddd90000); // ld $25, %lo(&GOTPLT[0])($14) + write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 + write32(Buf + 16, 0x03e07825); // move $15, $31 + write32(Buf + 20, 0x0018c0c2); // srl $24, $24, 3 + } else { + write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 + write32(Buf + 16, 0x03e07825); // move $15, $31 + write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 + } + + write32(Buf + 24, 0x0320f809); // jalr $25 + write32(Buf + 28, 0x2718fffe); // subu $24, $24, 2 + + uint64_t GotPlt = InX::GotPlt->getVA(); + writeRelocation(Buf, GotPlt + 0x8000, 16, 16); + writeRelocation(Buf + 4, GotPlt, 16, 0); + writeRelocation(Buf + 8, GotPlt, 16, 0); +} + +template +void MAXIS::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + const endianness E = ELFT::TargetEndianness; + if (isMicroMaxis()) { + // Overwrite trap instructions written by Writer::writeTrapInstr. + memset(Buf, 0, PltEntrySize); + + if (isMaxisR6()) { + write16(Buf, 0x7840); // addiupc $2, (GOTPLT) - . + write16(Buf + 4, 0xff22); // lw $25, 0($2) + write16(Buf + 8, 0x0f02); // move $24, $2 + write16(Buf + 10, 0x4723); // jrc $25 / jr16 $25 + relocateOne(Buf, R_MICROMAXIS_PC19_S2, GotPltEntryAddr - PltEntryAddr); + } else { + write16(Buf, 0x7900); // addiupc $2, (GOTPLT) - . + write16(Buf + 4, 0xff22); // lw $25, 0($2) + write16(Buf + 8, 0x4599); // jrc $25 / jr16 $25 + write16(Buf + 10, 0x0f02); // move $24, $2 + relocateOne(Buf, R_MICROMAXIS_PC23_S2, GotPltEntryAddr - PltEntryAddr); + } + return; + } + + write32(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) + write32(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15) + write32(Buf + 8, isMaxisR6() ? 0x03200009 : 0x03200008); // jr $25 + write32(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) + writeRelocation(Buf, GotPltEntryAddr + 0x8000, 16, 16); + writeRelocation(Buf + 4, GotPltEntryAddr, 16, 0); + writeRelocation(Buf + 12, GotPltEntryAddr, 16, 0); +} + +template +bool MAXIS::needsThunk(RelExpr Expr, RelType Type, const InputFile *File, + uint64_t BranchAddr, const Symbol &S) const { + // Any MAXIS PIC code function is invoked with its address in register $t9. + // So if we have a branch instruction from non-PIC code to the PIC one + // we cannot make the jump directly and need to create a small stubs + // to save the target function address. + // See page 3-38 ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf + if (Type != R_MAXIS_26 && Type != R_MICROMAXIS_26_S1 && + Type != R_MICROMAXIS_PC26_S1) + return false; + auto *F = dyn_cast_or_null>(File); + if (!F) + return false; + // If current file has PIC code, LA25 stub is not required. + if (F->getObj().getHeader()->e_flags & EF_MAXIS_PIC) + return false; + auto *D = dyn_cast(&S); + // LA25 is required if target file has PIC code + // or target symbol is a PIC symbol. + return D && isMaxisPIC(D); +} + +template +int64_t MAXIS::getImplicitAddend(const uint8_t *Buf, RelType Type) const { + const endianness E = ELFT::TargetEndianness; + switch (Type) { + case R_MAXIS_32: + case R_MAXIS_GPREL32: + case R_MAXIS_TLS_DTPREL32: + case R_MAXIS_TLS_TPREL32: + return SignExtend64<32>(read32(Buf)); + case R_MAXIS_26: + // FIXME (simon): If the relocation target symbol is not a PLT entry + // we should use another expression for calculation: + // ((A << 2) | (P & 0xf0000000)) >> 2 + return SignExtend64<28>(read32(Buf) << 2); + case R_MAXIS_GOT16: + case R_MAXIS_HI16: + case R_MAXIS_PCHI16: + return SignExtend64<16>(read32(Buf)) << 16; + case R_MAXIS_GPREL16: + case R_MAXIS_LO16: + case R_MAXIS_PCLO16: + case R_MAXIS_TLS_DTPREL_HI16: + case R_MAXIS_TLS_DTPREL_LO16: + case R_MAXIS_TLS_TPREL_HI16: + case R_MAXIS_TLS_TPREL_LO16: + return SignExtend64<16>(read32(Buf)); + case R_MICROMAXIS_GOT16: + case R_MICROMAXIS_HI16: + return SignExtend64<16>(readShuffle(Buf)) << 16; + case R_MICROMAXIS_GPREL16: + case R_MICROMAXIS_LO16: + case R_MICROMAXIS_TLS_DTPREL_HI16: + case R_MICROMAXIS_TLS_DTPREL_LO16: + case R_MICROMAXIS_TLS_TPREL_HI16: + case R_MICROMAXIS_TLS_TPREL_LO16: + return SignExtend64<16>(readShuffle(Buf)); + case R_MICROMAXIS_GPREL7_S2: + return SignExtend64<9>(readShuffle(Buf) << 2); + case R_MAXIS_PC16: + return SignExtend64<18>(read32(Buf) << 2); + case R_MAXIS_PC19_S2: + return SignExtend64<21>(read32(Buf) << 2); + case R_MAXIS_PC21_S2: + return SignExtend64<23>(read32(Buf) << 2); + case R_MAXIS_PC26_S2: + return SignExtend64<28>(read32(Buf) << 2); + case R_MAXIS_PC32: + return SignExtend64<32>(read32(Buf)); + case R_MICROMAXIS_26_S1: + return SignExtend64<27>(readShuffle(Buf) << 1); + case R_MICROMAXIS_PC7_S1: + return SignExtend64<8>(read16(Buf) << 1); + case R_MICROMAXIS_PC10_S1: + return SignExtend64<11>(read16(Buf) << 1); + case R_MICROMAXIS_PC16_S1: + return SignExtend64<17>(readShuffle(Buf) << 1); + case R_MICROMAXIS_PC18_S3: + return SignExtend64<21>(readShuffle(Buf) << 3); + case R_MICROMAXIS_PC19_S2: + return SignExtend64<21>(readShuffle(Buf) << 2); + case R_MICROMAXIS_PC21_S1: + return SignExtend64<22>(readShuffle(Buf) << 1); + case R_MICROMAXIS_PC23_S2: + return SignExtend64<25>(readShuffle(Buf) << 2); + case R_MICROMAXIS_PC26_S1: + return SignExtend64<27>(readShuffle(Buf) << 1); + default: + return 0; + } +} + +static std::pair +calculateMaxisRelChain(uint8_t *Loc, RelType Type, uint64_t Val) { + // MAXIS N64 ABI packs multiple relocations into the single relocation + // record. In general, all up to three relocations can have arbitrary + // types. In fact, Clang and GCC uses only a few combinations. For now, + // we support two of them. That is allow to pass at least all LLVM + // test suite cases. + // / R_MAXIS_SUB / R_MAXIS_HI16 | R_MAXIS_LO16 + // / R_MAXIS_64 / R_MAXIS_NONE + // The first relocation is a 'real' relocation which is calculated + // using the corresponding symbol's value. The second and the third + // relocations used to modify result of the first one: extend it to + // 64-bit, extract high or low part etc. For details, see part 2.9 Relocation + // at the https://dmz-portal.maxis.com/mw/images/8/82/007-4658-001.pdf + RelType Type2 = (Type >> 8) & 0xff; + RelType Type3 = (Type >> 16) & 0xff; + if (Type2 == R_MAXIS_NONE && Type3 == R_MAXIS_NONE) + return std::make_pair(Type, Val); + if (Type2 == R_MAXIS_64 && Type3 == R_MAXIS_NONE) + return std::make_pair(Type2, Val); + if (Type2 == R_MAXIS_SUB && (Type3 == R_MAXIS_HI16 || Type3 == R_MAXIS_LO16)) + return std::make_pair(Type3, -Val); + if (Type2 == R_MICROMAXIS_SUB && + (Type3 == R_MICROMAXIS_HI16 || Type3 == R_MICROMAXIS_LO16)) + return std::make_pair(Type3, -Val); + error(getErrorLocation(Loc) + "unsupported relocations combination " + + Twine(Type)); + return std::make_pair(Type & 0xff, Val); +} + +template +void MAXIS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { + const endianness E = ELFT::TargetEndianness; + + // Thread pointer and DRP offsets from the start of TLS data area. + // https://www.linux-maxis.org/wiki/NPTL + if (Type == R_MAXIS_TLS_DTPREL_HI16 || Type == R_MAXIS_TLS_DTPREL_LO16 || + Type == R_MAXIS_TLS_DTPREL32 || Type == R_MAXIS_TLS_DTPREL64 || + Type == R_MICROMAXIS_TLS_DTPREL_HI16 || + Type == R_MICROMAXIS_TLS_DTPREL_LO16) { + Val -= 0x8000; + } else if (Type == R_MAXIS_TLS_TPREL_HI16 || Type == R_MAXIS_TLS_TPREL_LO16 || + Type == R_MAXIS_TLS_TPREL32 || Type == R_MAXIS_TLS_TPREL64 || + Type == R_MICROMAXIS_TLS_TPREL_HI16 || + Type == R_MICROMAXIS_TLS_TPREL_LO16) { + Val -= 0x7000; + } + + if (ELFT::Is64Bits || Config->MaxisN32Abi) + std::tie(Type, Val) = calculateMaxisRelChain(Loc, Type, Val); + + switch (Type) { + case R_MAXIS_32: + case R_MAXIS_GPREL32: + case R_MAXIS_TLS_DTPREL32: + case R_MAXIS_TLS_TPREL32: + write32(Loc, Val); + break; + case R_MAXIS_64: + case R_MAXIS_TLS_DTPREL64: + case R_MAXIS_TLS_TPREL64: + write64(Loc, Val); + break; + case R_MAXIS_26: + writeRelocation(Loc, Val, 26, 2); + break; + case R_MAXIS_GOT16: + // The R_MAXIS_GOT16 relocation's value in "relocatable" linking mode + // is updated addend (not a GOT index). In that case write high 16 bits + // to store a correct addend value. + if (Config->Relocatable) { + writeRelocation(Loc, Val + 0x8000, 16, 16); + } else { + checkInt<16>(Loc, Val, Type); + writeRelocation(Loc, Val, 16, 0); + } + break; + case R_MICROMAXIS_GOT16: + if (Config->Relocatable) { + writeMicroRelocation32(Loc, Val + 0x8000, 16, 16); + } else { + checkInt<16>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 16, 0); + } + break; + case R_MAXIS_CALL16: + case R_MAXIS_GOT_DISP: + case R_MAXIS_GOT_PAGE: + case R_MAXIS_GPREL16: + case R_MAXIS_TLS_GD: + case R_MAXIS_TLS_GOTTPREL: + case R_MAXIS_TLS_LDM: + checkInt<16>(Loc, Val, Type); + LLVM_FALLTHROUGH; + case R_MAXIS_CALL_LO16: + case R_MAXIS_GOT_LO16: + case R_MAXIS_GOT_OFST: + case R_MAXIS_LO16: + case R_MAXIS_PCLO16: + case R_MAXIS_TLS_DTPREL_LO16: + case R_MAXIS_TLS_TPREL_LO16: + writeRelocation(Loc, Val, 16, 0); + break; + case R_MICROMAXIS_GOT_DISP: + case R_MICROMAXIS_GOT_PAGE: + case R_MICROMAXIS_GPREL16: + case R_MICROMAXIS_TLS_GD: + case R_MICROMAXIS_TLS_LDM: + checkInt<16>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 16, 0); + break; + case R_MICROMAXIS_CALL16: + case R_MICROMAXIS_CALL_LO16: + case R_MICROMAXIS_GOT_OFST: + case R_MICROMAXIS_LO16: + case R_MICROMAXIS_TLS_DTPREL_LO16: + case R_MICROMAXIS_TLS_GOTTPREL: + case R_MICROMAXIS_TLS_TPREL_LO16: + writeMicroRelocation32(Loc, Val, 16, 0); + break; + case R_MICROMAXIS_GPREL7_S2: + checkInt<7>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 7, 2); + break; + case R_MAXIS_CALL_HI16: + case R_MAXIS_GOT_HI16: + case R_MAXIS_HI16: + case R_MAXIS_PCHI16: + case R_MAXIS_TLS_DTPREL_HI16: + case R_MAXIS_TLS_TPREL_HI16: + writeRelocation(Loc, Val + 0x8000, 16, 16); + break; + case R_MICROMAXIS_CALL_HI16: + case R_MICROMAXIS_GOT_HI16: + case R_MICROMAXIS_HI16: + case R_MICROMAXIS_TLS_DTPREL_HI16: + case R_MICROMAXIS_TLS_TPREL_HI16: + writeMicroRelocation32(Loc, Val + 0x8000, 16, 16); + break; + case R_MAXIS_HIGHER: + writeRelocation(Loc, Val + 0x80008000, 16, 32); + break; + case R_MAXIS_HIGHEST: + writeRelocation(Loc, Val + 0x800080008000, 16, 48); + break; + case R_MICROMAXIS_HIGHER: + writeMicroRelocation32(Loc, Val + 0x80008000, 16, 32); + break; + case R_MICROMAXIS_HIGHEST: + writeMicroRelocation32(Loc, Val + 0x800080008000, 16, 48); + break; + case R_MAXIS_JALR: + case R_MICROMAXIS_JALR: + // Ignore this optimization relocation for now + break; + case R_MAXIS_PC16: + checkAlignment<4>(Loc, Val, Type); + checkInt<18>(Loc, Val, Type); + writeRelocation(Loc, Val, 16, 2); + break; + case R_MAXIS_PC19_S2: + checkAlignment<4>(Loc, Val, Type); + checkInt<21>(Loc, Val, Type); + writeRelocation(Loc, Val, 19, 2); + break; + case R_MAXIS_PC21_S2: + checkAlignment<4>(Loc, Val, Type); + checkInt<23>(Loc, Val, Type); + writeRelocation(Loc, Val, 21, 2); + break; + case R_MAXIS_PC26_S2: + checkAlignment<4>(Loc, Val, Type); + checkInt<28>(Loc, Val, Type); + writeRelocation(Loc, Val, 26, 2); + break; + case R_MAXIS_PC32: + writeRelocation(Loc, Val, 32, 0); + break; + case R_MICROMAXIS_26_S1: + case R_MICROMAXIS_PC26_S1: + checkInt<27>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 26, 1); + break; + case R_MICROMAXIS_PC7_S1: + checkInt<8>(Loc, Val, Type); + writeMicroRelocation16(Loc, Val, 7, 1); + break; + case R_MICROMAXIS_PC10_S1: + checkInt<11>(Loc, Val, Type); + writeMicroRelocation16(Loc, Val, 10, 1); + break; + case R_MICROMAXIS_PC16_S1: + checkInt<17>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 16, 1); + break; + case R_MICROMAXIS_PC18_S3: + checkInt<21>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 18, 3); + break; + case R_MICROMAXIS_PC19_S2: + checkInt<21>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 19, 2); + break; + case R_MICROMAXIS_PC21_S1: + checkInt<22>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 21, 1); + break; + case R_MICROMAXIS_PC23_S2: + checkInt<25>(Loc, Val, Type); + writeMicroRelocation32(Loc, Val, 23, 2); + break; + default: + error(getErrorLocation(Loc) + "unrecognized reloc " + Twine(Type)); + } +} + +template bool MAXIS::usesOnlyLowPageBits(RelType Type) const { + return Type == R_MAXIS_LO16 || Type == R_MAXIS_GOT_OFST || + Type == R_MICROMAXIS_LO16 || Type == R_MICROMAXIS_GOT_OFST; +} + +// Return true if the symbol is a PIC function. +template bool elf::isMaxisPIC(const Defined *Sym) { + typedef typename ELFT::Ehdr Elf_Ehdr; + if (!Sym->Section || !Sym->isFunc()) + return false; + + auto *Sec = cast(Sym->Section); + const Elf_Ehdr *Hdr = Sec->template getFile()->getObj().getHeader(); + return (Sym->StOther & STO_MAXIS_MAXIS16) == STO_MAXIS_PIC || + (Hdr->e_flags & EF_MAXIS_PIC); +} + +template TargetInfo *elf::getMaxisTargetInfo() { + static MAXIS Target; + return &Target; +} + +template TargetInfo *elf::getMaxisTargetInfo(); +template TargetInfo *elf::getMaxisTargetInfo(); +template TargetInfo *elf::getMaxisTargetInfo(); +template TargetInfo *elf::getMaxisTargetInfo(); + +template bool elf::isMaxisPIC(const Defined *); +template bool elf::isMaxisPIC(const Defined *); +template bool elf::isMaxisPIC(const Defined *); +template bool elf::isMaxisPIC(const Defined *); diff --git a/tools/lld/ELF/Arch/MaxisArchTree.cpp b/tools/lld/ELF/Arch/MaxisArchTree.cpp new file mode 100644 index 00000000..30bf3ead --- /dev/null +++ b/tools/lld/ELF/Arch/MaxisArchTree.cpp @@ -0,0 +1,383 @@ +//===- MaxisArchTree.cpp --------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This file contains a helper function for the Writer. +// +//===---------------------------------------------------------------------===// + +#include "InputFiles.h" +#include "SymbolTable.h" +#include "Writer.h" + +#include "lld/Common/ErrorHandler.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/MaxisABIFlags.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::ELF; + +using namespace lld; +using namespace lld::elf; + +namespace { +struct ArchTreeEdge { + uint32_t Child; + uint32_t Parent; +}; + +struct FileFlags { + InputFile *File; + uint32_t Flags; +}; +} // namespace + +static StringRef getAbiName(uint32_t Flags) { + switch (Flags) { + case 0: + return "n64"; + case EF_MAXIS_ABI2: + return "n32"; + case EF_MAXIS_ABI_O32: + return "o32"; + case EF_MAXIS_ABI_O64: + return "o64"; + case EF_MAXIS_ABI_EABI32: + return "eabi32"; + case EF_MAXIS_ABI_EABI64: + return "eabi64"; + default: + return "unknown"; + } +} + +static StringRef getNanName(bool IsNan2008) { + return IsNan2008 ? "2008" : "legacy"; +} + +static StringRef getFpName(bool IsFp64) { return IsFp64 ? "64" : "32"; } + +static void checkFlags(ArrayRef Files) { + uint32_t ABI = Files[0].Flags & (EF_MAXIS_ABI | EF_MAXIS_ABI2); + bool Nan = Files[0].Flags & EF_MAXIS_NAN2008; + bool Fp = Files[0].Flags & EF_MAXIS_FP64; + + for (const FileFlags &F : Files.slice(1)) { + uint32_t ABI2 = F.Flags & (EF_MAXIS_ABI | EF_MAXIS_ABI2); + if (ABI != ABI2) + error("target ABI '" + getAbiName(ABI) + "' is incompatible with '" + + getAbiName(ABI2) + "': " + toString(F.File)); + + bool Nan2 = F.Flags & EF_MAXIS_NAN2008; + if (Nan != Nan2) + error("target -mnan=" + getNanName(Nan) + " is incompatible with -mnan=" + + getNanName(Nan2) + ": " + toString(F.File)); + + bool Fp2 = F.Flags & EF_MAXIS_FP64; + if (Fp != Fp2) + error("target -mfp" + getFpName(Fp) + " is incompatible with -mfp" + + getFpName(Fp2) + ": " + toString(F.File)); + } +} + +static uint32_t getMiscFlags(ArrayRef Files) { + uint32_t Ret = 0; + for (const FileFlags &F : Files) + Ret |= F.Flags & + (EF_MAXIS_ABI | EF_MAXIS_ABI2 | EF_MAXIS_ARCH_ASE | EF_MAXIS_NOREORDER | + EF_MAXIS_MICROMAXIS | EF_MAXIS_NAN2008 | EF_MAXIS_32BITMODE); + return Ret; +} + +static uint32_t getPicFlags(ArrayRef Files) { + // Check PIC/non-PIC compatibility. + bool IsPic = Files[0].Flags & (EF_MAXIS_PIC | EF_MAXIS_CPIC); + for (const FileFlags &F : Files.slice(1)) { + bool IsPic2 = F.Flags & (EF_MAXIS_PIC | EF_MAXIS_CPIC); + if (IsPic && !IsPic2) + warn("linking abicalls code " + toString(Files[0].File) + + " with non-abicalls file: " + toString(F.File)); + if (!IsPic && IsPic2) + warn("linking non-abicalls code " + toString(Files[0].File) + + " with abicalls file: " + toString(F.File)); + } + + // Compute the result PIC/non-PIC flag. + uint32_t Ret = Files[0].Flags & (EF_MAXIS_PIC | EF_MAXIS_CPIC); + for (const FileFlags &F : Files.slice(1)) + Ret &= F.Flags & (EF_MAXIS_PIC | EF_MAXIS_CPIC); + + // PIC code is inherently CPIC and may not set CPIC flag explicitly. + if (Ret & EF_MAXIS_PIC) + Ret |= EF_MAXIS_CPIC; + return Ret; +} + +static ArchTreeEdge ArchTree[] = { + // MAXIS32R6 and MAXIS64R6 are not compatible with other extensions + // MAXIS64R2 extensions. + {EF_MAXIS_ARCH_64R2 | EF_MAXIS_MACH_OCTEON3, EF_MAXIS_ARCH_64R2}, + {EF_MAXIS_ARCH_64R2 | EF_MAXIS_MACH_OCTEON2, EF_MAXIS_ARCH_64R2}, + {EF_MAXIS_ARCH_64R2 | EF_MAXIS_MACH_OCTEON, EF_MAXIS_ARCH_64R2}, + {EF_MAXIS_ARCH_64R2 | EF_MAXIS_MACH_LS3A, EF_MAXIS_ARCH_64R2}, + // MAXIS64 extensions. + {EF_MAXIS_ARCH_64 | EF_MAXIS_MACH_SB1, EF_MAXIS_ARCH_64}, + {EF_MAXIS_ARCH_64 | EF_MAXIS_MACH_XLR, EF_MAXIS_ARCH_64}, + {EF_MAXIS_ARCH_64R2, EF_MAXIS_ARCH_64}, + // MAXIS V extensions. + {EF_MAXIS_ARCH_64, EF_MAXIS_ARCH_5}, + // R5000 extensions. + {EF_MAXIS_ARCH_4 | EF_MAXIS_MACH_5500, EF_MAXIS_ARCH_4 | EF_MAXIS_MACH_5400}, + // MAXIS IV extensions. + {EF_MAXIS_ARCH_4 | EF_MAXIS_MACH_5400, EF_MAXIS_ARCH_4}, + {EF_MAXIS_ARCH_4 | EF_MAXIS_MACH_9000, EF_MAXIS_ARCH_4}, + {EF_MAXIS_ARCH_5, EF_MAXIS_ARCH_4}, + // VR4100 extensions. + {EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_4111, EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_4100}, + {EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_4120, EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_4100}, + // MAXIS III extensions. + {EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_4010, EF_MAXIS_ARCH_3}, + {EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_4100, EF_MAXIS_ARCH_3}, + {EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_4650, EF_MAXIS_ARCH_3}, + {EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_5900, EF_MAXIS_ARCH_3}, + {EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_LS2E, EF_MAXIS_ARCH_3}, + {EF_MAXIS_ARCH_3 | EF_MAXIS_MACH_LS2F, EF_MAXIS_ARCH_3}, + {EF_MAXIS_ARCH_4, EF_MAXIS_ARCH_3}, + // MAXIS32 extensions. + {EF_MAXIS_ARCH_32R2, EF_MAXIS_ARCH_32}, + // MAXIS II extensions. + {EF_MAXIS_ARCH_3, EF_MAXIS_ARCH_2}, + {EF_MAXIS_ARCH_32, EF_MAXIS_ARCH_2}, + // MAXIS I extensions. + {EF_MAXIS_ARCH_1 | EF_MAXIS_MACH_3900, EF_MAXIS_ARCH_1}, + {EF_MAXIS_ARCH_2, EF_MAXIS_ARCH_1}, +}; + +static bool isArchMatched(uint32_t New, uint32_t Res) { + if (New == Res) + return true; + if (New == EF_MAXIS_ARCH_32 && isArchMatched(EF_MAXIS_ARCH_64, Res)) + return true; + if (New == EF_MAXIS_ARCH_32R2 && isArchMatched(EF_MAXIS_ARCH_64R2, Res)) + return true; + for (const auto &Edge : ArchTree) { + if (Res == Edge.Child) { + Res = Edge.Parent; + if (Res == New) + return true; + } + } + return false; +} + +static StringRef getMachName(uint32_t Flags) { + switch (Flags & EF_MAXIS_MACH) { + case EF_MAXIS_MACH_NONE: + return ""; + case EF_MAXIS_MACH_3900: + return "r3900"; + case EF_MAXIS_MACH_4010: + return "r4010"; + case EF_MAXIS_MACH_4100: + return "r4100"; + case EF_MAXIS_MACH_4650: + return "r4650"; + case EF_MAXIS_MACH_4120: + return "r4120"; + case EF_MAXIS_MACH_4111: + return "r4111"; + case EF_MAXIS_MACH_5400: + return "vr5400"; + case EF_MAXIS_MACH_5900: + return "vr5900"; + case EF_MAXIS_MACH_5500: + return "vr5500"; + case EF_MAXIS_MACH_9000: + return "rm9000"; + case EF_MAXIS_MACH_LS2E: + return "loongson2e"; + case EF_MAXIS_MACH_LS2F: + return "loongson2f"; + case EF_MAXIS_MACH_LS3A: + return "loongson3a"; + case EF_MAXIS_MACH_OCTEON: + return "octeon"; + case EF_MAXIS_MACH_OCTEON2: + return "octeon2"; + case EF_MAXIS_MACH_OCTEON3: + return "octeon3"; + case EF_MAXIS_MACH_SB1: + return "sb1"; + case EF_MAXIS_MACH_XLR: + return "xlr"; + default: + return "unknown machine"; + } +} + +static StringRef getArchName(uint32_t Flags) { + switch (Flags & EF_MAXIS_ARCH) { + case EF_MAXIS_ARCH_1: + return "maxis1"; + case EF_MAXIS_ARCH_2: + return "maxis2"; + case EF_MAXIS_ARCH_3: + return "maxis3"; + case EF_MAXIS_ARCH_4: + return "maxis4"; + case EF_MAXIS_ARCH_5: + return "maxis5"; + case EF_MAXIS_ARCH_32: + return "maxis32"; + case EF_MAXIS_ARCH_64: + return "maxis64"; + case EF_MAXIS_ARCH_32R2: + return "maxis32r2"; + case EF_MAXIS_ARCH_64R2: + return "maxis64r2"; + case EF_MAXIS_ARCH_32R6: + return "maxis32r6"; + case EF_MAXIS_ARCH_64R6: + return "maxis64r6"; + default: + return "unknown arch"; + } +} + +static std::string getFullArchName(uint32_t Flags) { + StringRef Arch = getArchName(Flags); + StringRef Mach = getMachName(Flags); + if (Mach.empty()) + return Arch.str(); + return (Arch + " (" + Mach + ")").str(); +} + +// There are (arguably too) many MAXIS ISAs out there. Their relationships +// can be represented as a forest. If all input files have ISAs which +// reachable by repeated proceeding from the single child to the parent, +// these input files are compatible. In that case we need to return "highest" +// ISA. If there are incompatible input files, we show an error. +// For example, maxis1 is a "parent" of maxis2 and such files are compatible. +// Output file gets EF_MAXIS_ARCH_2 flag. From the other side maxis3 and maxis32 +// are incompatible because nor maxis3 is a parent for misp32, nor maxis32 +// is a parent for maxis3. +static uint32_t getArchFlags(ArrayRef Files) { + uint32_t Ret = Files[0].Flags & (EF_MAXIS_ARCH | EF_MAXIS_MACH); + + for (const FileFlags &F : Files.slice(1)) { + uint32_t New = F.Flags & (EF_MAXIS_ARCH | EF_MAXIS_MACH); + + // Check ISA compatibility. + if (isArchMatched(New, Ret)) + continue; + if (!isArchMatched(Ret, New)) { + error("incompatible target ISA:\n>>> " + toString(Files[0].File) + ": " + + getFullArchName(Ret) + "\n>>> " + toString(F.File) + ": " + + getFullArchName(New)); + return 0; + } + Ret = New; + } + return Ret; +} + +template uint32_t elf::calcMaxisEFlags() { + std::vector V; + for (InputFile *F : ObjectFiles) + V.push_back({F, cast>(F)->getObj().getHeader()->e_flags}); + if (V.empty()) + return 0; + checkFlags(V); + return getMiscFlags(V) | getPicFlags(V) | getArchFlags(V); +} + +static int compareMaxisFpAbi(uint8_t FpA, uint8_t FpB) { + if (FpA == FpB) + return 0; + if (FpB == Maxis::Val_GNU_MAXIS_ABI_FP_ANY) + return 1; + if (FpB == Maxis::Val_GNU_MAXIS_ABI_FP_64A && + FpA == Maxis::Val_GNU_MAXIS_ABI_FP_64) + return 1; + if (FpB != Maxis::Val_GNU_MAXIS_ABI_FP_XX) + return -1; + if (FpA == Maxis::Val_GNU_MAXIS_ABI_FP_DOUBLE || + FpA == Maxis::Val_GNU_MAXIS_ABI_FP_64 || + FpA == Maxis::Val_GNU_MAXIS_ABI_FP_64A) + return 1; + return -1; +} + +static StringRef getMaxisFpAbiName(uint8_t FpAbi) { + switch (FpAbi) { + case Maxis::Val_GNU_MAXIS_ABI_FP_ANY: + return "any"; + case Maxis::Val_GNU_MAXIS_ABI_FP_DOUBLE: + return "-mdouble-float"; + case Maxis::Val_GNU_MAXIS_ABI_FP_SINGLE: + return "-msingle-float"; + case Maxis::Val_GNU_MAXIS_ABI_FP_SOFT: + return "-msoft-float"; + case Maxis::Val_GNU_MAXIS_ABI_FP_OLD_64: + return "-maxis32r2 -mfp64 (old)"; + case Maxis::Val_GNU_MAXIS_ABI_FP_XX: + return "-mfpxx"; + case Maxis::Val_GNU_MAXIS_ABI_FP_64: + return "-mgp32 -mfp64"; + case Maxis::Val_GNU_MAXIS_ABI_FP_64A: + return "-mgp32 -mfp64 -mno-odd-spreg"; + default: + return "unknown"; + } +} + +uint8_t elf::getMaxisFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, + StringRef FileName) { + if (compareMaxisFpAbi(NewFlag, OldFlag) >= 0) + return NewFlag; + if (compareMaxisFpAbi(OldFlag, NewFlag) < 0) + error("target floating point ABI '" + getMaxisFpAbiName(OldFlag) + + "' is incompatible with '" + getMaxisFpAbiName(NewFlag) + + "': " + FileName); + return OldFlag; +} + +template static bool isN32Abi(const InputFile *F) { + if (auto *EF = dyn_cast>(F)) + return EF->getObj().getHeader()->e_flags & EF_MAXIS_ABI2; + return false; +} + +bool elf::isMaxisN32Abi(const InputFile *F) { + switch (Config->EKind) { + case ELF32LEKind: + return isN32Abi(F); + case ELF32BEKind: + return isN32Abi(F); + case ELF64LEKind: + return isN32Abi(F); + case ELF64BEKind: + return isN32Abi(F); + default: + llvm_unreachable("unknown Config->EKind"); + } +} + +bool elf::isMicroMaxis() { return Config->EFlags & EF_MAXIS_MICROMAXIS; } + +bool elf::isMaxisR6() { + uint32_t Arch = Config->EFlags & EF_MAXIS_ARCH; + return Arch == EF_MAXIS_ARCH_32R6 || Arch == EF_MAXIS_ARCH_64R6; +} + +template uint32_t elf::calcMaxisEFlags(); +template uint32_t elf::calcMaxisEFlags(); +template uint32_t elf::calcMaxisEFlags(); +template uint32_t elf::calcMaxisEFlags(); diff --git a/tools/lld/ELF/CMakeLists.txt b/tools/lld/ELF/CMakeLists.txt index 7ec83784..b155a674 100644 --- a/tools/lld/ELF/CMakeLists.txt +++ b/tools/lld/ELF/CMakeLists.txt @@ -12,6 +12,8 @@ add_lld_library(lldELF Arch/AMDGPU.cpp Arch/ARM.cpp Arch/AVR.cpp + Arch/Maxis.cpp + Arch/MaxisArchTree.cpp Arch/Mips.cpp Arch/MipsArchTree.cpp Arch/PPC.cpp diff --git a/tools/lld/ELF/Config.h b/tools/lld/ELF/Config.h index ed425720..61be08fd 100644 --- a/tools/lld/ELF/Config.h +++ b/tools/lld/ELF/Config.h @@ -128,6 +128,7 @@ struct Configuration { bool ICF; bool ICFData; bool MergeArmExidx; + bool MaxisN32Abi = false; bool MipsN32Abi = false; bool NoGnuUnique; bool NoUndefinedVersion; @@ -196,6 +197,20 @@ struct Configuration { // endianness::little if IsLE is true. endianness::big otherwise. llvm::support::endianness Endianness; + // True if the target is the little-endian MAXIS64. + // + // The reason why we have this variable only for the MAXIS is because + // we use this often. Some ELF headers for MAXIS64EL are in a + // mixed-endian (which is horrible and I'd say that's a serious spec + // bug), and we need to know whether we are reading MAXIS ELF files or + // not in various places. + // + // (Note that MAXIS64EL is not a typo for MAXIS64LE. This is the official + // name whatever that means. A fun hypothesis is that "EL" is short for + // little-endian written in the little-endian order, but I don't know + // if that's true.) + bool IsMaxis64EL; + // True if the target is the little-endian MIPS64. // // The reason why we have this variable only for the MIPS is because diff --git a/tools/lld/ELF/Driver.cpp b/tools/lld/ELF/Driver.cpp index 714976ae..47f7aaad 100644 --- a/tools/lld/ELF/Driver.cpp +++ b/tools/lld/ELF/Driver.cpp @@ -116,9 +116,11 @@ static std::tuple parseEmulation(StringRef Emul) { .Cases("aarch64elf", "aarch64linux", {ELF64LEKind, EM_AARCH64}) .Cases("armelf", "armelf_linux_eabi", {ELF32LEKind, EM_ARM}) .Case("elf32_x86_64", {ELF32LEKind, EM_X86_64}) - .Cases("elf32btsmip", "elf32btsmipn32", {ELF32BEKind, EM_MIPS}) + .Cases("elf32btsmaxis", "elf32btsmaxisn32", {ELF32BEKind, EM_MAXIS}) .Cases("elf32ltsmip", "elf32ltsmipn32", {ELF32LEKind, EM_MIPS}) .Case("elf32ppc", {ELF32BEKind, EM_PPC}) + .Case("elf64btsmaxis", {ELF64BEKind, EM_MAXIS}) + .Case("elf64ltsmaxis", {ELF64LEKind, EM_MAXIS}) .Case("elf64btsmip", {ELF64BEKind, EM_MIPS}) .Case("elf64ltsmip", {ELF64LEKind, EM_MIPS}) .Case("elf64ppc", {ELF64BEKind, EM_PPC64}) @@ -265,8 +267,11 @@ static void initLLVM(opt::InputArgList &Args) { // Some command line options or some combinations of them are not allowed. // This function checks for such errors. static void checkOptions(opt::InputArgList &Args) { - // The MIPS ABI as of 2016 does not support the GNU-style symbol lookup + // The MAXIS/MIPS ABI as of 2016 does not support the GNU-style symbol lookup // table which is a relatively new feature. + if (Config->EMachine == EM_MAXIS && Config->GnuHash) + error("the .gnu.hash section is not compatible with the MAXIS target."); + if (Config->EMachine == EM_MIPS && Config->GnuHash) error("the .gnu.hash section is not compatible with the MIPS target."); @@ -712,6 +717,7 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) { StringRef S = Arg->getValue(); std::tie(Config->EKind, Config->EMachine, Config->OSABI) = parseEmulation(S); + Config->MaxisN32Abi = (S == "elf32btsmaxisn32" || S == "elf32ltsmaxisn32"); Config->MipsN32Abi = (S == "elf32btsmipn32" || S == "elf32ltsmipn32"); Config->Emulation = S; } @@ -801,8 +807,9 @@ static void setConfigs() { Config->IsLE = (Kind == ELF32LEKind || Kind == ELF64LEKind); Config->Endianness = Config->IsLE ? support::endianness::little : support::endianness::big; + Config->IsMaxis64EL = (Kind == ELF64LEKind && Machine == EM_MAXIS); Config->IsMips64EL = (Kind == ELF64LEKind && Machine == EM_MIPS); - Config->IsRela = Config->Is64 || IsX32 || Config->MipsN32Abi; + Config->IsRela = Config->Is64 || IsX32 || Config->MaxisN32Abi || Config->MipsN32Abi; Config->Pic = Config->Pie || Config->Shared; Config->Wordsize = Config->Is64 ? 8 : 4; } @@ -880,6 +887,7 @@ void LinkerDriver::inferMachineType() { Config->EKind = F->EKind; Config->EMachine = F->EMachine; Config->OSABI = F->OSABI; + Config->MaxisN32Abi = Config->EMachine == EM_MAXIS && isMaxisN32Abi(F); Config->MipsN32Abi = Config->EMachine == EM_MIPS && isMipsN32Abi(F); return; } @@ -971,7 +979,7 @@ template void LinkerDriver::link(opt::InputArgList &Args) { // If a -hash-style option was not given, set to a default value, // which varies depending on the target. if (!Args.hasArg(OPT_hash_style)) { - if (Config->EMachine == EM_MIPS) + if (Config->EMachine == EM_MAXIS || Config->EMachine == EM_MIPS) Config->SysvHash = true; else Config->SysvHash = Config->GnuHash = true; @@ -992,7 +1000,7 @@ template void LinkerDriver::link(opt::InputArgList &Args) { return; // Use default entry point name if no name was given via the command - // line nor linker scripts. For some reason, MIPS entry point name is + // line nor linker scripts. For some reason, MAXIS/MIPS entry point name is // different from others. Config->WarnMissingEntry = (!Config->Entry.empty() || (!Config->Shared && !Config->Relocatable)); diff --git a/tools/lld/ELF/DriverUtils.cpp b/tools/lld/ELF/DriverUtils.cpp index 2f7c9228..38936f83 100644 --- a/tools/lld/ELF/DriverUtils.cpp +++ b/tools/lld/ELF/DriverUtils.cpp @@ -125,11 +125,11 @@ void elf::printHelp(const char *Argv0) { // shared libraries. Therefore, we need to print out at least "elf". // Here, we print out all the targets that we support. outs() << Argv0 << ": supported targets: " - << "elf32-i386 elf32-iamcu elf32-littlearm elf32-ntradbigmips " - << "elf32-ntradlittlemips elf32-powerpc elf32-tradbigmips " - << "elf32-tradlittlemips elf32-x86-64 " - << "elf64-amdgpu elf64-littleaarch64 elf64-powerpc elf64-tradbigmips " - << "elf64-tradlittlemips elf64-x86-64\n"; + << "elf32-i386 elf32-iamcu elf32-littlearm elf32-ntradbigmaxis elf32-ntradbigmips" + << "elf32-ntradlittlemaxis elf32-ntradlittlemips elf32-powerpc elf32-tradbigmaxis elf32-tradbigmips " + << "elf32-tradlittlemaxis elf32-tradlittlemips elf32-x86-64 " + << "elf64-amdgpu elf64-littleaarch64 elf64-powerpc elf64-tradbigmaxis elf64-tradbigmips " + << "elf64-tradlittlemaxis elf64-tradlittlemips elf64-x86-64\n"; } // Reconstructs command line arguments so that so that you can re-run diff --git a/tools/lld/ELF/GdbIndex.cpp b/tools/lld/ELF/GdbIndex.cpp index d27b57f9..46a2cd9c 100644 --- a/tools/lld/ELF/GdbIndex.cpp +++ b/tools/lld/ELF/GdbIndex.cpp @@ -66,7 +66,9 @@ LLDDwarfObj::findAux(const InputSectionBase &Sec, uint64_t Pos, const RelTy &Rel = *It; const ObjFile *File = Sec.getFile(); - uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); + uint32_t SymIndex = Config->EMachine == llvm::ELF::EM_MAXIS + ? Rel.getSymbol(Config->IsMaxis64EL) + : Rel.getSymbol(Config->IsMips64EL); const typename ELFT::Sym &Sym = File->getELFSyms()[SymIndex]; uint32_t SecIndex = File->getSectionIndex(Sym); diff --git a/tools/lld/ELF/ICF.cpp b/tools/lld/ELF/ICF.cpp index b1e12e05..d8cf8844 100644 --- a/tools/lld/ELF/ICF.cpp +++ b/tools/lld/ELF/ICF.cpp @@ -219,7 +219,8 @@ bool ICF::constantEq(const InputSection *SecA, ArrayRef RA, for (size_t I = 0; I < RA.size(); ++I) { if (RA[I].r_offset != RB[I].r_offset || - RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL)) + (RA[I].getType(Config->IsMaxis64EL) != RB[I].getType(Config->IsMaxis64EL) + || RA[I].getType(Config->IsMips64EL) != RB[I].getType(Config->IsMips64EL))) return false; uint64_t AddA = getAddend(RA[I]); diff --git a/tools/lld/ELF/InputFiles.cpp b/tools/lld/ELF/InputFiles.cpp index ccf4b3d7..1e7cf019 100644 --- a/tools/lld/ELF/InputFiles.cpp +++ b/tools/lld/ELF/InputFiles.cpp @@ -856,7 +856,13 @@ template void SharedFile::parseRest() { continue; } - if (Config->EMachine == EM_MIPS) { + if (Config->EMachine == EM_MAXIS) { + // FIXME: MAXIS BFD linker puts _gp_disp symbol into DSO files + // and incorrectly assigns VER_NDX_LOCAL to this section global + // symbol. Here is a workaround for this bug. + if (Versym && VersymIndex == VER_NDX_LOCAL && Name == "_gp_disp") + continue; + } else if (Config->EMachine == EM_MIPS) { // FIXME: MIPS BFD linker puts _gp_disp symbol into DSO files // and incorrectly assigns VER_NDX_LOCAL to this section global // symbol. Here is a workaround for this bug. @@ -919,6 +925,11 @@ static uint8_t getBitcodeMachineKind(StringRef Path, const Triple &T) { return EM_ARM; case Triple::avr: return EM_AVR; + case Triple::maxis: + case Triple::maxisel: + case Triple::maxis64: + case Triple::maxis64el: + return EM_MAXIS; case Triple::mips: case Triple::mipsel: case Triple::mips64: diff --git a/tools/lld/ELF/InputFiles.h b/tools/lld/ELF/InputFiles.h index dda1de81..bdf7f574 100644 --- a/tools/lld/ELF/InputFiles.h +++ b/tools/lld/ELF/InputFiles.h @@ -178,7 +178,9 @@ template class ObjFile : public ELFFileBase { } template Symbol &getRelocTargetSym(const RelT &Rel) const { - uint32_t SymIndex = Rel.getSymbol(Config->IsMips64EL); + uint32_t SymIndex = Config->EMachine == ::llvm::ELF::EM_MAXIS + ? Rel.getSymbol(Config->IsMaxis64EL) + : Rel.getSymbol(Config->IsMips64EL); return getSymbol(SymIndex); } @@ -188,6 +190,11 @@ template class ObjFile : public ELFFileBase { llvm::Optional getDILineInfo(InputSectionBase *, uint64_t); llvm::Optional> getVariableLoc(StringRef Name); + // MAXIS GP0 value defined by this file. This value represents the gp value + // used to create the relocatable object and required to support + // R_MAXIS_GPREL16 / R_MAXIS_GPREL32 relocations. + uint32_t MaxisGp0 = 0; + // MIPS GP0 value defined by this file. This value represents the gp value // used to create the relocatable object and required to support // R_MIPS_GPREL16 / R_MIPS_GPREL32 relocations. diff --git a/tools/lld/ELF/InputSection.cpp b/tools/lld/ELF/InputSection.cpp index 93baefad..1d4fe5b1 100644 --- a/tools/lld/ELF/InputSection.cpp +++ b/tools/lld/ELF/InputSection.cpp @@ -359,7 +359,9 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { InputSectionBase *Sec = getRelocatedSection(); for (const RelTy &Rel : Rels) { - RelType Type = Rel.getType(Config->IsMips64EL); + RelType Type = Config->EMachine == EM_MAXIS + ? Rel.getType(Config->IsMaxis64EL) + : Rel.getType(Config->IsMips64EL); Symbol &Sym = getFile()->getRelocTargetSym(Rel); auto *P = reinterpret_cast(Buf); @@ -371,9 +373,14 @@ void InputSection::copyRelocations(uint8_t *Buf, ArrayRef Rels) { // Output section VA is zero for -r, so r_offset is an offset within the // section, but for --emit-relocs it is an virtual address. P->r_offset = Sec->getOutputSection()->Addr + Sec->getOffset(Rel.r_offset); - P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type, - Config->IsMips64EL); - + if (Config->EMachine == EM_MAXIS) { + P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type, + Config->IsMaxis64EL); + } else { + P->setSymbolAndType(InX::SymTab->getSymbolIndex(&Sym), Type, + Config->IsMips64EL); + } + if (Sym.Type == STT_SECTION) { // We combine multiple section symbols into only one per // section. This means we have to update the addend. That is @@ -517,6 +524,44 @@ static uint64_t getRelocTargetVA(RelType Type, int64_t A, uint64_t P, case R_NONE: case R_TLSDESC_CALL: llvm_unreachable("cannot relocate hint relocs"); + case R_MAXIS_GOTREL: + return Sym.getVA(A) - InX::MaxisGot->getGp(); + case R_MAXIS_GOT_GP: + return InX::MaxisGot->getGp() + A; + case R_MAXIS_GOT_GP_PC: { + // R_MAXIS_LO16 expression has R_MAXIS_GOT_GP_PC type iif the target + // is _gp_disp symbol. In that case we should use the following + // formula for calculation "AHL + GP - P + 4". For details see p. 4-19 at + // ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf + // microMAXIS variants of these relocations use slightly different + // expressions: AHL + GP - P + 3 for %lo() and AHL + GP - P - 1 for %hi() + // to correctly handle less-sugnificant bit of the microMAXIS symbol. + uint64_t V = InX::MaxisGot->getGp() + A - P; + if (Type == R_MAXIS_LO16 || Type == R_MICROMAXIS_LO16) + V += 4; + if (Type == R_MICROMAXIS_LO16 || Type == R_MICROMAXIS_HI16) + V -= 1; + return V; + } + case R_MAXIS_GOT_LOCAL_PAGE: + // If relocation against MAXIS local symbol requires GOT entry, this entry + // should be initialized by 'page address'. This address is high 16-bits + // of sum the symbol's value and the addend. + return InX::MaxisGot->getVA() + InX::MaxisGot->getPageEntryOffset(Sym, A) - + InX::MaxisGot->getGp(); + case R_MAXIS_GOT_OFF: + case R_MAXIS_GOT_OFF32: + // In case of MAXIS if a GOT relocation has non-zero addend this addend + // should be applied to the GOT entry content not to the GOT entry offset. + // That is why we use separate expression type. + return InX::MaxisGot->getVA() + InX::MaxisGot->getSymEntryOffset(Sym, A) - + InX::MaxisGot->getGp(); + case R_MAXIS_TLSGD: + return InX::MaxisGot->getVA() + InX::MaxisGot->getTlsOffset() + + InX::MaxisGot->getGlobalDynOffset(Sym) - InX::MaxisGot->getGp(); + case R_MAXIS_TLSLD: + return InX::MaxisGot->getVA() + InX::MaxisGot->getTlsOffset() + + InX::MaxisGot->getTlsIndexOff() - InX::MaxisGot->getGp(); case R_MIPS_GOTREL: return Sym.getVA(A) - InX::MipsGot->getGp(); case R_MIPS_GOT_GP: @@ -655,7 +700,9 @@ void InputSection::relocateNonAlloc(uint8_t *Buf, ArrayRef Rels) { const unsigned Bits = sizeof(typename ELFT::uint) * 8; for (const RelTy &Rel : Rels) { - RelType Type = Rel.getType(Config->IsMips64EL); + RelType Type = Config->EMachine == EM_MAXIS + ? Rel.getType(Config->IsMaxis64EL) + : Rel.getType(Config->IsMips64EL); uint64_t Offset = getOffset(Rel.r_offset); uint8_t *BufLoc = Buf + Offset; int64_t Addend = getAddend(Rel); diff --git a/tools/lld/ELF/MarkLive.cpp b/tools/lld/ELF/MarkLive.cpp index 88f558c7..b355e908 100644 --- a/tools/lld/ELF/MarkLive.cpp +++ b/tools/lld/ELF/MarkLive.cpp @@ -45,8 +45,13 @@ using namespace lld::elf; template static typename ELFT::uint getAddend(InputSectionBase &Sec, const typename ELFT::Rel &Rel) { - return Target->getImplicitAddend(Sec.Data.begin() + Rel.r_offset, - Rel.getType(Config->IsMips64EL)); + if (Config->EMachine == EM_MAXIS) { + return Target->getImplicitAddend(Sec.Data.begin() + Rel.r_offset, + Rel.getType(Config->IsMips64EL)); + } else { + return Target->getImplicitAddend(Sec.Data.begin() + Rel.r_offset, + Rel.getType(Config->IsMips64EL)); + } } template diff --git a/tools/lld/ELF/Relocations.cpp b/tools/lld/ELF/Relocations.cpp index 37bc8bcb..91f73431 100644 --- a/tools/lld/ELF/Relocations.cpp +++ b/tools/lld/ELF/Relocations.cpp @@ -80,6 +80,61 @@ static std::string getLocation(InputSectionBase &S, const Symbol &Sym, return Msg + S.getObjMsg(Off); } +// This is a MAXIS-specific rule. +// +// In case of MAXIS GP-relative relocations always resolve to a definition +// in a regular input file, ignoring the one-definition rule. So we, +// for example, should not attempt to create a dynamic relocation even +// if the target symbol is preemptible. There are two two MAXIS GP-relative +// relocations R_MAXIS_GPREL16 and R_MAXIS_GPREL32. But only R_MAXIS_GPREL16 +// can be against a preemptible symbol. +// +// To get MAXIS relocation type we apply 0xff mask. In case of O32 ABI all +// relocation types occupy eight bit. In case of N64 ABI we extract first +// relocation from 3-in-1 packet because only the first relocation can +// be against a real symbol. +static bool isMaxisGprel(RelType Type) { + if (Config->EMachine != EM_MAXIS) + return false; + Type &= 0xff; + return Type == R_MAXIS_GPREL16 || Type == R_MICROMAXIS_GPREL16 || + Type == R_MICROMAXIS_GPREL7_S2; +} + +// This function is similar to the `handleTlsRelocation`. MAXIS does not +// support any relaxations for TLS relocations so by factoring out MAXIS +// handling in to the separate function we can simplify the code and do not +// pollute other `handleTlsRelocation` by MAXIS `ifs` statements. +// Maxis has a custom MaxisGotSection that handles the writing of GOT entries +// without dynamic relocations. +template +static unsigned handleMaxisTlsRelocation(RelType Type, Symbol &Sym, + InputSectionBase &C, uint64_t Offset, + int64_t Addend, RelExpr Expr) { + if (Expr == R_MAXIS_TLSLD) { + if (InX::MaxisGot->addTlsIndex() && Config->Pic) + InX::RelaDyn->addReloc({Target->TlsModuleIndexRel, InX::MaxisGot, + InX::MaxisGot->getTlsIndexOff(), false, nullptr, + 0}); + C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); + return 1; + } + + if (Expr == R_MAXIS_TLSGD) { + if (InX::MaxisGot->addDynTlsEntry(Sym) && Sym.IsPreemptible) { + uint64_t Off = InX::MaxisGot->getGlobalDynOffset(Sym); + InX::RelaDyn->addReloc( + {Target->TlsModuleIndexRel, InX::MaxisGot, Off, false, &Sym, 0}); + if (Sym.IsPreemptible) + InX::RelaDyn->addReloc({Target->TlsOffsetRel, InX::MaxisGot, + Off + Config->Wordsize, false, &Sym, 0}); + } + C.Relocations.push_back({Expr, Type, Offset, Addend, &Sym}); + return 1; + } + return 0; +} + // This is a MIPS-specific rule. // // In case of MIPS GP-relative relocations always resolve to a definition @@ -206,6 +261,8 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, if (Config->EMachine == EM_ARM) return handleARMTlsRelocation(Type, Sym, C, Offset, Addend, Expr); + if (Config->EMachine == EM_MAXIS) + return handleMaxisTlsRelocation(Type, Sym, C, Offset, Addend, Expr); if (Config->EMachine == EM_MIPS) return handleMipsTlsRelocation(Type, Sym, C, Offset, Addend, Expr); @@ -295,6 +352,31 @@ handleTlsRelocation(RelType Type, Symbol &Sym, InputSectionBase &C, return 0; } +static RelType getMaxisPairType(RelType Type, bool IsLocal) { + switch (Type) { + case R_MAXIS_HI16: + return R_MAXIS_LO16; + case R_MAXIS_GOT16: + // In case of global symbol, the R_MAXIS_GOT16 relocation does not + // have a pair. Each global symbol has a unique entry in the GOT + // and a corresponding instruction with help of the R_MAXIS_GOT16 + // relocation loads an address of the symbol. In case of local + // symbol, the R_MAXIS_GOT16 relocation creates a GOT entry to hold + // the high 16 bits of the symbol's value. A paired R_MAXIS_LO16 + // relocations handle low 16 bits of the address. That allows + // to allocate only one GOT entry for every 64 KBytes of local data. + return IsLocal ? R_MAXIS_LO16 : R_MAXIS_NONE; + case R_MICROMAXIS_GOT16: + return IsLocal ? R_MICROMAXIS_LO16 : R_MAXIS_NONE; + case R_MAXIS_PCHI16: + return R_MAXIS_PCLO16; + case R_MICROMAXIS_HI16: + return R_MICROMAXIS_LO16; + default: + return R_MAXIS_NONE; + } +} + static RelType getMipsPairType(RelType Type, bool IsLocal) { switch (Type) { case R_MIPS_HI16: @@ -343,7 +425,8 @@ static bool needsPlt(RelExpr Expr) { // returns false for TLS variables even though they need GOT, because // TLS variables uses GOT differently than the regular variables. static bool needsGot(RelExpr Expr) { - return isRelExprOneOf(Expr); } @@ -351,7 +434,7 @@ static bool needsGot(RelExpr Expr) { // True if this expression is of the form Sym - X, where X is a position in the // file (PC, or GOT for example). static bool isRelExpr(RelExpr Expr) { - return isRelExprOneOf(Expr); } @@ -367,7 +450,10 @@ static bool isRelExpr(RelExpr Expr) { static bool isStaticLinkTimeConstant(RelExpr E, RelType Type, const Symbol &Sym, InputSectionBase &S, uint64_t RelOff) { // These expressions always compute a constant - if (isRelExprOneOf +static int64_t computeMaxisAddend(const RelTy &Rel, const RelTy *End, + InputSectionBase &Sec, RelExpr Expr, + bool IsLocal) { + if (Expr == R_MAXIS_GOTREL && IsLocal) + return Sec.getFile()->MaxisGp0; + + // The ABI says that the paired relocation is used only for REL. + // See p. 4-17 at ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf + if (RelTy::IsRela) + return 0; + + RelType Type = Rel.getType(Config->IsMaxis64EL); + uint32_t PairTy = getMaxisPairType(Type, IsLocal); + if (PairTy == R_MAXIS_NONE) + return 0; + + const uint8_t *Buf = Sec.Data.data(); + uint32_t SymIndex = Rel.getSymbol(Config->IsMaxis64EL); + + // To make things worse, paired relocations might not be contiguous in + // the relocation table, so we need to do linear search. *sigh* + for (const RelTy *RI = &Rel; RI != End; ++RI) + if (RI->getType(Config->IsMaxis64EL) == PairTy && + RI->getSymbol(Config->IsMaxis64EL) == SymIndex) + return Target->getImplicitAddend(Buf + RI->r_offset, PairTy); + + warn("can't find matching " + toString(PairTy) + " relocation for " + + toString(Type)); + return 0; +} + // MIPS has an odd notion of "paired" relocations to calculate addends. // For example, if a relocation is of R_MIPS_HI16, there must be a // R_MIPS_LO16 relocation after that, and an addend is calculated using @@ -704,7 +826,12 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End, InputSectionBase &Sec, RelExpr Expr, bool IsLocal) { int64_t Addend; - RelType Type = Rel.getType(Config->IsMips64EL); + RelType Type; + if (Config->EMachine == EM_MAXIS) { + Type = Rel.getType(Config->IsMaxis64EL); + } else { + Type = Rel.getType(Config->IsMips64EL); + } if (RelTy::IsRela) { Addend = getAddend(Rel); @@ -715,6 +842,8 @@ static int64_t computeAddend(const RelTy &Rel, const RelTy *End, if (Config->EMachine == EM_PPC64 && Config->Pic && Type == R_PPC64_TOC) Addend += getPPC64TocBase(); + if (Config->EMachine == EM_MAXIS) + Addend += computeMaxisAddend(Rel, End, Sec, Expr, IsLocal); if (Config->EMachine == EM_MIPS) Addend += computeMipsAddend(Rel, End, Sec, Expr, IsLocal); @@ -754,6 +883,21 @@ static bool maybeReportUndefined(Symbol &Sym, InputSectionBase &Sec, return true; } +// MAXIS N32 ABI treats series of successive relocations with the same offset +// as a single relocation. The similar approach used by N64 ABI, but this ABI +// packs all relocations into the single relocation record. Here we emulate +// this for the N32 ABI. Iterate over relocation with the same offset and put +// theirs types into the single bit-set. +template static RelType getMaxisN32RelType(RelTy *&Rel, RelTy *End) { + RelType Type = Rel->getType(Config->IsMaxis64EL); + uint64_t Offset = Rel->r_offset; + + int N = 0; + while (Rel + 1 != End && (Rel + 1)->r_offset == Offset) + Type |= (++Rel)->getType(Config->IsMaxis64EL) << (8 * ++N); + return Type; +} + // MIPS N32 ABI treats series of successive relocations with the same offset // as a single relocation. The similar approach used by N64 ABI, but this ABI // packs all relocations into the single relocation record. Here we emulate @@ -887,8 +1031,16 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef Rels) { for (auto I = Rels.begin(), End = Rels.end(); I != End; ++I) { const RelTy &Rel = *I; Symbol &Sym = Sec.getFile()->getRelocTargetSym(Rel); - RelType Type = Rel.getType(Config->IsMips64EL); + RelType Type; + if (Config->EMachine == EM_MAXIS) { + Type = Rel.getType(Config->IsMaxis64EL); + } else { + Type = Rel.getType(Config->IsMips64EL); + } + // Deal with MAXIS oddity. + if (Config->MaxisN32Abi) + Type = getMaxisN32RelType(I, End); // Deal with MIPS oddity. if (Config->MipsN32Abi) Type = getMipsN32RelType(I, End); @@ -909,8 +1061,14 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef Rels) { if (isRelExprOneOf(Expr)) continue; + // Handle yet another MAXIS-ness. + if (isMaxisGprel(Type)) { + int64_t Addend = computeAddend(Rel, End, Sec, Expr, Sym.isLocal()); + Sec.Relocations.push_back({R_MAXIS_GOTREL, Type, Offset, Addend, &Sym}); + continue; + } // Handle yet another MIPS-ness. - if (isMipsGprel(Type)) { + else if (isMipsGprel(Type)) { int64_t Addend = computeAddend(Rel, End, Sec, Expr, Sym.isLocal()); Sec.Relocations.push_back({R_MIPS_GOTREL, Type, Offset, Addend, &Sym}); continue; @@ -973,7 +1131,19 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef Rels) { // Create a GOT slot if a relocation needs GOT. if (needsGot(Expr)) { - if (Config->EMachine == EM_MIPS) { + if (Config->EMachine == EM_MAXIS) { + // MAXIS ABI has special rules to process GOT entries and doesn't + // require relocation entries for them. A special case is TLS + // relocations. In that case dynamic loader applies dynamic + // relocations to initialize TLS GOT entries. + // See "Global Offset Table" in Chapter 5 in the following document + // for detailed description: + // ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf + InX::MaxisGot->addEntry(Sym, Addend, Expr); + if (Sym.isTls() && Sym.IsPreemptible) + InX::RelaDyn->addReloc({Target->TlsGotRel, InX::MaxisGot, + Sym.getGotOffset(), false, &Sym, 0}); + } else if (Config->EMachine == EM_MIPS) { // MIPS ABI has special rules to process GOT entries and doesn't // require relocation entries for them. A special case is TLS // relocations. In that case dynamic loader applies dynamic @@ -1002,6 +1172,23 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef Rels) { InX::RelaDyn->addReloc( {Target->getDynRel(Type), &Sec, Offset, false, &Sym, Addend}); + // MAXIS ABI turns using of GOT and dynamic relocations inside out. + // While regular ABI uses dynamic relocations to fill up GOT entries + // MAXIS ABI requires dynamic linker to fills up GOT entries using + // specially sorted dynamic symbol table. This affects even dynamic + // relocations against symbols which do not require GOT entries + // creation explicitly, i.e. do not have any GOT-relocations. So if + // a preemptible symbol has a dynamic relocation we anyway have + // to create a GOT entry for it. + // If a non-preemptible symbol has a dynamic relocation against it, + // dynamic linker takes it st_value, adds offset and writes down + // result of the dynamic relocation. In case of preemptible symbol + // dynamic linker performs symbol resolution, writes the symbol value + // to the GOT entry and reads the GOT entry when it needs to perform + // a dynamic relocation. + // ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf p.4-19 + if (Config->EMachine == EM_MAXIS) + InX::MaxisGot->addEntry(Sym, Addend, Expr); // MIPS ABI turns using of GOT and dynamic relocations inside out. // While regular ABI uses dynamic relocations to fill up GOT entries // MIPS ABI requires dynamic linker to fills up GOT entries using @@ -1017,7 +1204,7 @@ static void scanRelocs(InputSectionBase &Sec, ArrayRef Rels) { // to the GOT entry and reads the GOT entry when it needs to perform // a dynamic relocation. // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf p.4-19 - if (Config->EMachine == EM_MIPS) + else if (Config->EMachine == EM_MIPS) InX::MipsGot->addEntry(Sym, Addend, Expr); continue; } @@ -1086,7 +1273,7 @@ template void elf::scanRelocations(InputSectionBase &S) { // Writer.cpp : Iteratively call framework until no more Thunks added // // Thunk placement requirements: -// Mips LA25 thunks. These must be placed immediately before the callee section +// Maxis/Mips LA25 thunks. These must be placed immediately before the callee section // We can assume that the caller is in range of the Thunk. These are modelled // by Thunks that return the section they must precede with // getTargetInputSection(). @@ -1097,7 +1284,7 @@ template void elf::scanRelocations(InputSectionBase &S) { // restrictions. // // Thunk placement algorithm: -// For Mips LA25 ThunkSections; the placement is explicit, it has to be before +// For Maxis/Mips LA25 ThunkSections; the placement is explicit, it has to be before // getTargetInputSection(). // // For thunks that must be placed within range of the caller there are many @@ -1183,7 +1370,7 @@ void ThunkCreator::mergeThunks(ArrayRef OutputSections) { auto *TA = dyn_cast(A); auto *TB = dyn_cast(B); // Check if Thunk is immediately before any specific Target - // InputSection for example Mips LA25 Thunks. + // InputSection for example Maxis/Mips LA25 Thunks. if (TA && TA->getTargetInputSection() == B) return true; if (TA && !TB && !TA->getTargetInputSection()) diff --git a/tools/lld/ELF/Relocations.h b/tools/lld/ELF/Relocations.h index 2cc8adfa..8c0dabd2 100644 --- a/tools/lld/ELF/Relocations.h +++ b/tools/lld/ELF/Relocations.h @@ -43,6 +43,14 @@ enum RelExpr { R_GOT_PAGE_PC, R_GOT_PC, R_HINT, + R_MAXIS_GOTREL, + R_MAXIS_GOT_GP, + R_MAXIS_GOT_GP_PC, + R_MAXIS_GOT_LOCAL_PAGE, + R_MAXIS_GOT_OFF, + R_MAXIS_GOT_OFF32, + R_MAXIS_TLSGD, + R_MAXIS_TLSLD, R_MIPS_GOTREL, R_MIPS_GOT_GP, R_MIPS_GOT_GP_PC, @@ -169,7 +177,7 @@ class ThunkCreator { // Track InputSections that have an inline ThunkSection placed in front // an inline ThunkSection may have control fall through to the section below // so we need to make sure that there is only one of them. - // The Mips LA25 Thunk is an example of an inline ThunkSection. + // The Maxis/Mips LA25 Thunk is an example of an inline ThunkSection. llvm::DenseMap ThunkedSections; }; diff --git a/tools/lld/ELF/SymbolTable.cpp b/tools/lld/ELF/SymbolTable.cpp index c3a00bea..751f8ddd 100644 --- a/tools/lld/ELF/SymbolTable.cpp +++ b/tools/lld/ELF/SymbolTable.cpp @@ -43,15 +43,15 @@ static InputFile *getFirstElf() { // All input object files must be for the same architecture // (e.g. it does not make sense to link x86 object files with -// MIPS object files.) This function checks for that error. +// MAXIS/MIPS object files.) This function checks for that error. static bool isCompatible(InputFile *F) { if (!F->isElf() && !isa(F)) return true; if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) { - if (Config->EMachine != EM_MIPS) + if (Config->EMachine != EM_MAXIS && Config->EMachine != EM_MIPS) return true; - if (isMipsN32Abi(F) == Config->MipsN32Abi) + if (isMaxisN32Abi(F) == Config->MaxisN32Abi || isMipsN32Abi(F) == Config->MipsN32Abi) return true; } diff --git a/tools/lld/ELF/Symbols.cpp b/tools/lld/ELF/Symbols.cpp index 13a91aab..67a07d90 100644 --- a/tools/lld/ELF/Symbols.cpp +++ b/tools/lld/ELF/Symbols.cpp @@ -36,6 +36,9 @@ Defined *ElfSym::Edata2; Defined *ElfSym::End1; Defined *ElfSym::End2; Defined *ElfSym::GlobalOffsetTable; +Defined *ElfSym::MaxisGp; +Defined *ElfSym::MaxisGpDisp; +Defined *ElfSym::MaxisLocalGp; Defined *ElfSym::MipsGp; Defined *ElfSym::MipsGpDisp; Defined *ElfSym::MipsLocalGp; diff --git a/tools/lld/ELF/Symbols.h b/tools/lld/ELF/Symbols.h index 9b720738..ee038421 100644 --- a/tools/lld/ELF/Symbols.h +++ b/tools/lld/ELF/Symbols.h @@ -134,6 +134,7 @@ class Symbol { Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther, uint8_t Type) : Binding(Binding), File(File), SymbolKind(K), NeedsPltAddr(false), + IsInGlobalMaxisGot(false), Is32BitMaxisGot(false), IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false), IsInIgot(false), IsPreemptible(false), Used(!Config->GcSections), Type(Type), StOther(StOther), Name(Name) {} @@ -144,6 +145,12 @@ class Symbol { // True the symbol should point to its PLT entry. // For SharedSymbol only. unsigned NeedsPltAddr : 1; + // True if this symbol has an entry in the global part of MAXIS GOT. + unsigned IsInGlobalMaxisGot : 1; + + // True if this symbol is referenced by 32-bit GOT relocations. + unsigned Is32BitMaxisGot : 1; + // True if this symbol has an entry in the global part of MIPS GOT. unsigned IsInGlobalMipsGot : 1; @@ -330,6 +337,11 @@ struct ElfSym { // the end of the .got. static Defined *GlobalOffsetTable; + // _gp, _gp_disp and __gnu_local_gp symbols. Only for MAXIS. + static Defined *MaxisGp; + static Defined *MaxisGpDisp; + static Defined *MaxisLocalGp; + // _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS. static Defined *MipsGp; static Defined *MipsGpDisp; diff --git a/tools/lld/ELF/SyntheticSections.cpp b/tools/lld/ELF/SyntheticSections.cpp index a5a851f9..4488dc5e 100644 --- a/tools/lld/ELF/SyntheticSections.cpp +++ b/tools/lld/ELF/SyntheticSections.cpp @@ -86,6 +86,175 @@ MergeInputSection *elf::createCommentSection() { getVersion(), ".comment"); } +// .MAXIS.abiflags section. +template +MaxisAbiFlagsSection::MaxisAbiFlagsSection(Elf_Maxis_ABIFlags Flags) + : SyntheticSection(SHF_ALLOC, SHT_MAXIS_ABIFLAGS, 8, ".MAXIS.abiflags"), + Flags(Flags) { + this->Entsize = sizeof(Elf_Maxis_ABIFlags); +} + +template void MaxisAbiFlagsSection::writeTo(uint8_t *Buf) { + memcpy(Buf, &Flags, sizeof(Flags)); +} + +template +MaxisAbiFlagsSection *MaxisAbiFlagsSection::create() { + Elf_Maxis_ABIFlags Flags = {}; + bool Create = false; + + for (InputSectionBase *Sec : InputSections) { + if (Sec->Type != SHT_MAXIS_ABIFLAGS) + continue; + Sec->Live = false; + Create = true; + + std::string Filename = toString(Sec->File); + const size_t Size = Sec->Data.size(); + // Older version of BFD (such as the default FreeBSD linker) concatenate + // .MAXIS.abiflags instead of merging. To allow for this case (or potential + // zero padding) we ignore everything after the first Elf_Maxis_ABIFlags + if (Size < sizeof(Elf_Maxis_ABIFlags)) { + error(Filename + ": invalid size of .MAXIS.abiflags section: got " + + Twine(Size) + " instead of " + Twine(sizeof(Elf_Maxis_ABIFlags))); + return nullptr; + } + auto *S = reinterpret_cast(Sec->Data.data()); + if (S->version != 0) { + error(Filename + ": unexpected .MAXIS.abiflags version " + + Twine(S->version)); + return nullptr; + } + + // LLD checks ISA compatibility in calcMaxisEFlags(). Here we just + // select the highest number of ISA/Rev/Ext. + Flags.isa_level = std::max(Flags.isa_level, S->isa_level); + Flags.isa_rev = std::max(Flags.isa_rev, S->isa_rev); + Flags.isa_ext = std::max(Flags.isa_ext, S->isa_ext); + Flags.gpr_size = std::max(Flags.gpr_size, S->gpr_size); + Flags.cpr1_size = std::max(Flags.cpr1_size, S->cpr1_size); + Flags.cpr2_size = std::max(Flags.cpr2_size, S->cpr2_size); + Flags.ases |= S->ases; + Flags.flags1 |= S->flags1; + Flags.flags2 |= S->flags2; + Flags.fp_abi = elf::getMaxisFpAbiFlag(Flags.fp_abi, S->fp_abi, Filename); + }; + + if (Create) + return make>(Flags); + return nullptr; +} + +// .MAXIS.options section. +template +MaxisOptionsSection::MaxisOptionsSection(Elf_Maxis_RegInfo Reginfo) + : SyntheticSection(SHF_ALLOC, SHT_MAXIS_OPTIONS, 8, ".MAXIS.options"), + Reginfo(Reginfo) { + this->Entsize = sizeof(Elf_Maxis_Options) + sizeof(Elf_Maxis_RegInfo); +} + +template void MaxisOptionsSection::writeTo(uint8_t *Buf) { + auto *Options = reinterpret_cast(Buf); + Options->kind = ODK_REGINFO; + Options->size = getSize(); + + if (!Config->Relocatable) + Reginfo.ri_gp_value = InX::MaxisGot->getGp(); + memcpy(Buf + sizeof(Elf_Maxis_Options), &Reginfo, sizeof(Reginfo)); +} + +template +MaxisOptionsSection *MaxisOptionsSection::create() { + // N64 ABI only. + if (!ELFT::Is64Bits) + return nullptr; + + std::vector Sections; + for (InputSectionBase *Sec : InputSections) + if (Sec->Type == SHT_MAXIS_OPTIONS) + Sections.push_back(Sec); + + if (Sections.empty()) + return nullptr; + + Elf_Maxis_RegInfo Reginfo = {}; + for (InputSectionBase *Sec : Sections) { + Sec->Live = false; + + std::string Filename = toString(Sec->File); + ArrayRef D = Sec->Data; + + while (!D.empty()) { + if (D.size() < sizeof(Elf_Maxis_Options)) { + error(Filename + ": invalid size of .MAXIS.options section"); + break; + } + + auto *Opt = reinterpret_cast(D.data()); + if (Opt->kind == ODK_REGINFO) { + if (Config->Relocatable && Opt->getRegInfo().ri_gp_value) + error(Filename + ": unsupported non-zero ri_gp_value"); + Reginfo.ri_gprmask |= Opt->getRegInfo().ri_gprmask; + Sec->getFile()->MaxisGp0 = Opt->getRegInfo().ri_gp_value; + break; + } + + if (!Opt->size) + fatal(Filename + ": zero option descriptor size"); + D = D.slice(Opt->size); + } + }; + + return make>(Reginfo); +} + +// MAXIS .reginfo section. +template +MaxisReginfoSection::MaxisReginfoSection(Elf_Maxis_RegInfo Reginfo) + : SyntheticSection(SHF_ALLOC, SHT_MAXIS_REGINFO, 4, ".reginfo"), + Reginfo(Reginfo) { + this->Entsize = sizeof(Elf_Maxis_RegInfo); +} + +template void MaxisReginfoSection::writeTo(uint8_t *Buf) { + if (!Config->Relocatable) + Reginfo.ri_gp_value = InX::MaxisGot->getGp(); + memcpy(Buf, &Reginfo, sizeof(Reginfo)); +} + +template +MaxisReginfoSection *MaxisReginfoSection::create() { + // Section should be alive for O32 and N32 ABIs only. + if (ELFT::Is64Bits) + return nullptr; + + std::vector Sections; + for (InputSectionBase *Sec : InputSections) + if (Sec->Type == SHT_MAXIS_REGINFO) + Sections.push_back(Sec); + + if (Sections.empty()) + return nullptr; + + Elf_Maxis_RegInfo Reginfo = {}; + for (InputSectionBase *Sec : Sections) { + Sec->Live = false; + + if (Sec->Data.size() != sizeof(Elf_Maxis_RegInfo)) { + error(toString(Sec->File) + ": invalid size of .reginfo section"); + return nullptr; + } + auto *R = reinterpret_cast(Sec->Data.data()); + if (Config->Relocatable && R->ri_gp_value) + error(toString(Sec->File) + ": unsupported non-zero ri_gp_value"); + + Reginfo.ri_gprmask |= R->ri_gprmask; + Sec->getFile()->MaxisGp0 = R->ri_gp_value; + }; + + return make>(Reginfo); +} + // .MIPS.abiflags section. template MipsAbiFlagsSection::MipsAbiFlagsSection(Elf_Mips_ABIFlags Flags) @@ -639,10 +808,250 @@ void GotSection::writeTo(uint8_t *Buf) { relocateAlloc(Buf - OutSecOff, Buf - OutSecOff + Size); } +MaxisGotSection::MaxisGotSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MAXIS_GPREL, SHT_PROGBITS, 16, + ".got") {} + + +void MaxisGotSection::addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr) { + // For "true" local symbols which can be referenced from the same module + // only compiler creates two instructions for address loading: + // + // lw $8, 0($gp) # R_MAXIS_GOT16 + // addi $8, $8, 0 # R_MAXIS_LO16 + // + // The first instruction loads high 16 bits of the symbol address while + // the second adds an offset. That allows to reduce number of required + // GOT entries because only one global offset table entry is necessary + // for every 64 KBytes of local data. So for local symbols we need to + // allocate number of GOT entries to hold all required "page" addresses. + // + // All global symbols (hidden and regular) considered by compiler uniformly. + // It always generates a single `lw` instruction and R_MAXIS_GOT16 relocation + // to load address of the symbol. So for each such symbol we need to + // allocate dedicated GOT entry to store its address. + // + // If a symbol is preemptible we need help of dynamic linker to get its + // final address. The corresponding GOT entries are allocated in the + // "global" part of GOT. Entries for non preemptible global symbol allocated + // in the "local" part of GOT. + // + // See "Global Offset Table" in Chapter 5: + // ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf + if (Expr == R_MAXIS_GOT_LOCAL_PAGE) { + // At this point we do not know final symbol value so to reduce number + // of allocated GOT entries do the following trick. Save all output + // sections referenced by GOT relocations. Then later in the `finalize` + // method calculate number of "pages" required to cover all saved output + // section and allocate appropriate number of GOT entries. + PageIndexMap.insert({Sym.getOutputSection(), 0}); + return; + } + if (Sym.isTls()) { + // GOT entries created for MAXIS TLS relocations behave like + // almost GOT entries from other ABIs. They go to the end + // of the global offset table. + Sym.GotIndex = TlsEntries.size(); + TlsEntries.push_back(&Sym); + return; + } + auto AddEntry = [&](Symbol &S, uint64_t A, GotEntries &Items) { + if (S.isInGot() && !A) + return; + size_t NewIndex = Items.size(); + if (!EntryIndexMap.insert({{&S, A}, NewIndex}).second) + return; + Items.emplace_back(&S, A); + if (!A) + S.GotIndex = NewIndex; + }; + if (Sym.IsPreemptible) { + // Ignore addends for preemptible symbols. They got single GOT entry anyway. + AddEntry(Sym, 0, GlobalEntries); + Sym.IsInGlobalMaxisGot = true; + } else if (Expr == R_MAXIS_GOT_OFF32) { + AddEntry(Sym, Addend, LocalEntries32); + Sym.Is32BitMaxisGot = true; + } else { + // Hold local GOT entries accessed via a 16-bit index separately. + // That allows to write them in the beginning of the GOT and keep + // their indexes as less as possible to escape relocation's overflow. + AddEntry(Sym, Addend, LocalEntries); + } +} + +bool MaxisGotSection::addDynTlsEntry(Symbol &Sym) { + if (Sym.GlobalDynIndex != -1U) + return false; + Sym.GlobalDynIndex = TlsEntries.size(); + // Global Dynamic TLS entries take two GOT slots. + TlsEntries.push_back(nullptr); + TlsEntries.push_back(&Sym); + return true; +} + +// Reserves TLS entries for a TLS module ID and a TLS block offset. +// In total it takes two GOT slots. +bool MaxisGotSection::addTlsIndex() { + if (TlsIndexOff != uint32_t(-1)) + return false; + TlsIndexOff = TlsEntries.size() * Config->Wordsize; + TlsEntries.push_back(nullptr); + TlsEntries.push_back(nullptr); + return true; +} + +static uint64_t getMaxisPageAddr(uint64_t Addr) { + return (Addr + 0x8000) & ~0xffff; +} + +static uint64_t getMaxisPageCount(uint64_t Size) { + return (Size + 0xfffe) / 0xffff + 1; +} + +uint64_t MaxisGotSection::getPageEntryOffset(const Symbol &B, + int64_t Addend) const { + const OutputSection *OutSec = B.getOutputSection(); + uint64_t SecAddr = getMaxisPageAddr(OutSec->Addr); + uint64_t SymAddr = getMaxisPageAddr(B.getVA(Addend)); + uint64_t Index = PageIndexMap.lookup(OutSec) + (SymAddr - SecAddr) / 0xffff; + assert(Index < PageEntriesNum); + return (HeaderEntriesNum + Index) * Config->Wordsize; +} + +uint64_t MaxisGotSection::getSymEntryOffset(const Symbol &B, + int64_t Addend) const { + // Calculate offset of the GOT entries block: TLS, global, local. + uint64_t Index = HeaderEntriesNum + PageEntriesNum; + if (B.isTls()) + Index += LocalEntries.size() + LocalEntries32.size() + GlobalEntries.size(); + else if (B.IsInGlobalMaxisGot) + Index += LocalEntries.size() + LocalEntries32.size(); + else if (B.Is32BitMaxisGot) + Index += LocalEntries.size(); + // Calculate offset of the GOT entry in the block. + if (B.isInGot()) + Index += B.GotIndex; + else { + auto It = EntryIndexMap.find({&B, Addend}); + assert(It != EntryIndexMap.end()); + Index += It->second; + } + return Index * Config->Wordsize; +} + +uint64_t MaxisGotSection::getTlsOffset() const { + return (getLocalEntriesNum() + GlobalEntries.size()) * Config->Wordsize; +} + +uint64_t MaxisGotSection::getGlobalDynOffset(const Symbol &B) const { + return B.GlobalDynIndex * Config->Wordsize; +} + +const Symbol *MaxisGotSection::getFirstGlobalEntry() const { + return GlobalEntries.empty() ? nullptr : GlobalEntries.front().first; +} + +unsigned MaxisGotSection::getLocalEntriesNum() const { + return HeaderEntriesNum + PageEntriesNum + LocalEntries.size() + + LocalEntries32.size(); +} + +void MaxisGotSection::finalizeContents() { updateAllocSize(); } + +bool MaxisGotSection::updateAllocSize() { + PageEntriesNum = 0; + for (std::pair &P : PageIndexMap) { + // For each output section referenced by GOT page relocations calculate + // and save into PageIndexMap an upper bound of MAXIS GOT entries required + // to store page addresses of local symbols. We assume the worst case - + // each 64kb page of the output section has at least one GOT relocation + // against it. And take in account the case when the section intersects + // page boundaries. + P.second = PageEntriesNum; + PageEntriesNum += getMaxisPageCount(P.first->Size); + } + Size = (getLocalEntriesNum() + GlobalEntries.size() + TlsEntries.size()) * + Config->Wordsize; + return false; +} + +bool MaxisGotSection::empty() const { + // We add the .got section to the result for dynamic MAXIS target because + // its address and properties are mentioned in the .dynamic section. + return Config->Relocatable; +} + +uint64_t MaxisGotSection::getGp() const { return ElfSym::MaxisGp->getVA(0); } + +void MaxisGotSection::writeTo(uint8_t *Buf) { + // Set the MSB of the second GOT slot. This is not required by any + // MAXIS ABI documentation, though. + // + // There is a comment in glibc saying that "The MSB of got[1] of a + // gnu object is set to identify gnu objects," and in GNU gold it + // says "the second entry will be used by some runtime loaders". + // But how this field is being used is unclear. + // + // We are not really willing to mimic other linkers behaviors + // without understanding why they do that, but because all files + // generated by GNU tools have this special GOT value, and because + // we've been doing this for years, it is probably a safe bet to + // keep doing this for now. We really need to revisit this to see + // if we had to do this. + writeUint(Buf + Config->Wordsize, (uint64_t)1 << (Config->Wordsize * 8 - 1)); + Buf += HeaderEntriesNum * Config->Wordsize; + // Write 'page address' entries to the local part of the GOT. + for (std::pair &L : PageIndexMap) { + size_t PageCount = getMaxisPageCount(L.first->Size); + uint64_t FirstPageAddr = getMaxisPageAddr(L.first->Addr); + for (size_t PI = 0; PI < PageCount; ++PI) { + uint8_t *Entry = Buf + (L.second + PI) * Config->Wordsize; + writeUint(Entry, FirstPageAddr + PI * 0x10000); + } + } + Buf += PageEntriesNum * Config->Wordsize; + auto AddEntry = [&](const GotEntry &SA) { + uint8_t *Entry = Buf; + Buf += Config->Wordsize; + const Symbol *Sym = SA.first; + uint64_t VA = Sym->getVA(SA.second); + if (Sym->StOther & STO_MAXIS_MICROMAXIS) + VA |= 1; + writeUint(Entry, VA); + }; + std::for_each(std::begin(LocalEntries), std::end(LocalEntries), AddEntry); + std::for_each(std::begin(LocalEntries32), std::end(LocalEntries32), AddEntry); + std::for_each(std::begin(GlobalEntries), std::end(GlobalEntries), AddEntry); + // Initialize TLS-related GOT entries. If the entry has a corresponding + // dynamic relocations, leave it initialized by zero. Write down adjusted + // TLS symbol's values otherwise. To calculate the adjustments use offsets + // for thread-local storage. + // https://www.linux-maxis.org/wiki/NPTL + if (TlsIndexOff != -1U && !Config->Pic) + writeUint(Buf + TlsIndexOff, 1); + for (const Symbol *B : TlsEntries) { + if (!B || B->IsPreemptible) + continue; + uint64_t VA = B->getVA(); + if (B->GotIndex != -1U) { + uint8_t *Entry = Buf + B->GotIndex * Config->Wordsize; + writeUint(Entry, VA - 0x7000); + } + if (B->GlobalDynIndex != -1U) { + uint8_t *Entry = Buf + B->GlobalDynIndex * Config->Wordsize; + writeUint(Entry, 1); + Entry += Config->Wordsize; + writeUint(Entry, VA - 0x8000); + } + } +} + MipsGotSection::MipsGotSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL, SHT_PROGBITS, 16, ".got") {} + void MipsGotSection::addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr) { // For "true" local symbols which can be referenced from the same module // only compiler creates two instructions for address loading: @@ -966,11 +1375,11 @@ DynamicSection::DynamicSection() ".dynamic") { this->Entsize = ELFT::Is64Bits ? 16 : 8; - // .dynamic section is not writable on MIPS and on Fuchsia OS + // .dynamic section is not writable on MAXIS/MIPS and on Fuchsia OS // which passes -z rodynamic. // See "Special Section" in Chapter 4 in the following document: // ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf - if (Config->EMachine == EM_MIPS || Config->ZRodynamic) + if (Config->EMachine == EM_MAXIS || Config->EMachine == EM_MIPS || Config->ZRodynamic) this->Flags = SHF_ALLOC; // Add strings to .dynstr early so that .dynstr's size will be @@ -1072,10 +1481,10 @@ template void DynamicSection::finalizeContents() { addInt(IsRela ? DT_RELAENT : DT_RELENT, IsRela ? sizeof(Elf_Rela) : sizeof(Elf_Rel)); - // MIPS dynamic loader does not support RELCOUNT tag. + // MAXIS/MIPS dynamic loader does not support RELCOUNT tag. // The problem is in the tight relation between dynamic - // relocations and GOT. So do not emit this tag on MIPS. - if (Config->EMachine != EM_MIPS) { + // relocations and GOT. So do not emit this tag on MAXIS/MIPS. + if (Config->EMachine != EM_MAXIS && Config->EMachine != EM_MIPS) { size_t NumRelativeRels = InX::RelaDyn->getRelativeRelocCount(); if (Config->ZCombreloc && NumRelativeRels) addInt(IsRela ? DT_RELACOUNT : DT_RELCOUNT, NumRelativeRels); @@ -1091,6 +1500,9 @@ template void DynamicSection::finalizeContents() { addInSec(DT_JMPREL, InX::RelaPlt); addSize(DT_PLTRELSZ, InX::RelaPlt->getParent()); switch (Config->EMachine) { + case EM_MAXIS: + addInSec(DT_MAXIS_PLTGOT, InX::GotPlt); + break; case EM_MIPS: addInSec(DT_MIPS_PLTGOT, InX::GotPlt); break; @@ -1147,7 +1559,22 @@ template void DynamicSection::finalizeContents() { addInt(DT_VERNEEDNUM, In::VerNeed->getNeedNum()); } - if (Config->EMachine == EM_MIPS) { + if (Config->EMachine == EM_MAXIS) { + addInt(DT_MAXIS_RLD_VERSION, 1); + addInt(DT_MAXIS_FLAGS, RHF_NOTPOT); + addInt(DT_MAXIS_BASE_ADDRESS, Target->getImageBase()); + addInt(DT_MAXIS_SYMTABNO, InX::DynSymTab->getNumSymbols()); + + add(DT_MAXIS_LOCAL_GOTNO, [] { return InX::MaxisGot->getLocalEntriesNum(); }); + + if (const Symbol *B = InX::MaxisGot->getFirstGlobalEntry()) + addInt(DT_MAXIS_GOTSYM, B->DynsymIndex); + else + addInt(DT_MAXIS_GOTSYM, InX::DynSymTab->getNumSymbols()); + addInSec(DT_PLTGOT, InX::MaxisGot); + if (InX::MaxisRldMap) + addInSec(DT_MAXIS_RLD_MAP, InX::MaxisRldMap); + } else if (Config->EMachine == EM_MIPS) { addInt(DT_MIPS_RLD_VERSION, 1); addInt(DT_MIPS_FLAGS, RHF_NOTPOT); addInt(DT_MIPS_BASE_ADDRESS, Target->getImageBase()); @@ -1224,7 +1651,18 @@ static void encodeDynamicReloc(typename ELFT::Rela *P, if (Config->IsRela) P->r_addend = Rel.getAddend(); P->r_offset = Rel.getOffset(); - if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot) + if (Config->EMachine == EM_MAXIS && Rel.getInputSec() == InX::MaxisGot) + // The MAXIS GOT section contains dynamic relocations that correspond to TLS + // entries. These entries are placed after the global and local sections of + // the GOT. At the point when we create these relocations, the size of the + // global and local sections is unknown, so the offset that we store in the + // TLS entry's DynamicReloc is relative to the start of the TLS section of + // the GOT, rather than being relative to the start of the GOT. This line of + // code adds the size of the global and local sections to the virtual + // address computed by getOffset() in order to adjust it into the TLS + // section. + P->r_offset += InX::MaxisGot->getTlsOffset(); + else if (Config->EMachine == EM_MIPS && Rel.getInputSec() == InX::MipsGot) // The MIPS GOT section contains dynamic relocations that correspond to TLS // entries. These entries are placed after the global and local sections of // the GOT. At the point when we create these relocations, the size of the @@ -1235,7 +1673,12 @@ static void encodeDynamicReloc(typename ELFT::Rela *P, // address computed by getOffset() in order to adjust it into the TLS // section. P->r_offset += InX::MipsGot->getTlsOffset(); - P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL); + + if (Config->EMachine == EM_MAXIS) { + P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMaxis64EL); + } else { + P->setSymbolAndType(Rel.getSymIndex(), Rel.Type, Config->IsMips64EL); + } } template @@ -1249,12 +1692,23 @@ RelocationSection::RelocationSection(StringRef Name, bool Sort) template static bool compRelocations(const RelTy &A, const RelTy &B) { - bool AIsRel = A.getType(Config->IsMips64EL) == Target->RelativeRel; - bool BIsRel = B.getType(Config->IsMips64EL) == Target->RelativeRel; + bool AIsRel; + bool BIsRel; + if (Config->EMachine == EM_MAXIS) { + AIsRel = A.getType(Config->IsMaxis64EL) == Target->RelativeRel; + BIsRel = B.getType(Config->IsMaxis64EL) == Target->RelativeRel; + } else { + AIsRel = A.getType(Config->IsMips64EL) == Target->RelativeRel; + BIsRel = B.getType(Config->IsMips64EL) == Target->RelativeRel; + } if (AIsRel != BIsRel) return AIsRel; - return A.getSymbol(Config->IsMips64EL) < B.getSymbol(Config->IsMips64EL); + if (Config->EMachine == EM_MAXIS) { + return A.getSymbol(Config->IsMaxis64EL) < B.getSymbol(Config->IsMaxis64EL); + } else { + return A.getSymbol(Config->IsMips64EL) < B.getSymbol(Config->IsMips64EL); + } } template void RelocationSection::writeTo(uint8_t *Buf) { @@ -1354,7 +1808,8 @@ bool AndroidPackedRelocationSection::updateAllocSize() { Elf_Rela R; encodeDynamicReloc(&R, Rel); - if (R.getType(Config->IsMips64EL) == Target->RelativeRel) + if (R.getType(Config->IsMaxis64EL) == Target->RelativeRel + || R.getType(Config->IsMips64EL) == Target->RelativeRel) Relatives.push_back(R); else NonRelatives.push_back(R); @@ -1474,6 +1929,22 @@ SymbolTableBaseSection::SymbolTableBaseSection(StringTableSection &StrTabSec) StrTabSec.isDynamic() ? ".dynsym" : ".symtab"), StrTabSec(StrTabSec) {} +// Orders symbols according to their positions in the GOT, +// in compliance with MAXIS ABI rules. +// See "Global Offset Table" in Chapter 5 in the following document +// for detailed description: +// ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf +static bool sortMaxisSymbols(const SymbolTableEntry &L, + const SymbolTableEntry &R) { + // Sort entries related to non-local preemptible symbols by GOT indexes. + // All other entries go to the first part of GOT in arbitrary order. + bool LIsInLocalGot = !L.Sym->IsInGlobalMaxisGot; + bool RIsInLocalGot = !R.Sym->IsInGlobalMaxisGot; + if (LIsInLocalGot || RIsInLocalGot) + return !RIsInLocalGot; + return L.Sym->GotIndex < R.Sym->GotIndex; +} + // Orders symbols according to their positions in the GOT, // in compliance with MIPS ABI rules. // See "Global Offset Table" in Chapter 5 in the following document @@ -1503,6 +1974,8 @@ void SymbolTableBaseSection::finalizeContents() { if (InX::GnuHashTab) { // NB: It also sorts Symbols to meet the GNU hash table requirements. InX::GnuHashTab->addSymbols(Symbols); + } else if (Config->EMachine == EM_MAXIS) { + std::stable_sort(Symbols.begin(), Symbols.end(), sortMaxisSymbols); } else if (Config->EMachine == EM_MIPS) { std::stable_sort(Symbols.begin(), Symbols.end(), sortMipsSymbols); } @@ -1620,11 +2093,39 @@ template void SymbolTableSection::writeTo(uint8_t *Buf) { ++ESym; } + // On MAXIS we need to mark symbol which has a PLT entry and requires + // pointer equality by STO_MAXIS_PLT flag. That is necessary to help + // dynamic linker distinguish such symbols and MAXIS lazy-binding stubs. + // https://sourceware.org/ml/binutils/2008-07/txt00000.txt + if (Config->EMachine == EM_MAXIS) { + auto *ESym = reinterpret_cast(Buf); + + for (SymbolTableEntry &Ent : Symbols) { + Symbol *Sym = Ent.Sym; + if (Sym->isInPlt() && Sym->NeedsPltAddr) + ESym->st_other |= STO_MAXIS_PLT; + if (isMicroMaxis()) { + // Set STO_MAXIS_MICROMAXIS flag and less-significant bit for + // defined microMAXIS symbols and shared symbols with PLT record. + if ((Sym->isDefined() && (Sym->StOther & STO_MAXIS_MICROMAXIS)) || + (Sym->isShared() && Sym->NeedsPltAddr)) { + if (StrTabSec.isDynamic()) + ESym->st_value |= 1; + ESym->st_other |= STO_MAXIS_MICROMAXIS; + } + } + if (Config->Relocatable) + if (auto *D = dyn_cast(Sym)) + if (isMaxisPIC(D)) + ESym->st_other |= STO_MAXIS_PIC; + ++ESym; + } + } // On MIPS we need to mark symbol which has a PLT entry and requires // pointer equality by STO_MIPS_PLT flag. That is necessary to help // dynamic linker distinguish such symbols and MIPS lazy-binding stubs. // https://sourceware.org/ml/binutils/2008-07/txt00000.txt - if (Config->EMachine == EM_MIPS) { + else if (Config->EMachine == EM_MIPS) { auto *ESym = reinterpret_cast(Buf); for (SymbolTableEntry &Ent : Symbols) { @@ -2545,6 +3046,10 @@ void elf::mergeSections() { V.erase(std::remove(V.begin(), V.end(), nullptr), V.end()); } +MaxisRldMapSection::MaxisRldMapSection() + : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, Config->Wordsize, + ".rld_map") {} + MipsRldMapSection::MipsRldMapSection() : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, Config->Wordsize, ".rld_map") {} @@ -2622,6 +3127,8 @@ GotPltSection *InX::GotPlt; GnuHashTableSection *InX::GnuHashTab; HashTableSection *InX::HashTab; IgotPltSection *InX::IgotPlt; +MaxisGotSection *InX::MaxisGot; +MaxisRldMapSection *InX::MaxisRldMap; MipsGotSection *InX::MipsGot; MipsRldMapSection *InX::MipsRldMap; PltSection *InX::Plt; @@ -2648,6 +3155,21 @@ template void PltSection::addEntry(Symbol &Sym); template void PltSection::addEntry(Symbol &Sym); template void PltSection::addEntry(Symbol &Sym); +template class elf::MaxisAbiFlagsSection; +template class elf::MaxisAbiFlagsSection; +template class elf::MaxisAbiFlagsSection; +template class elf::MaxisAbiFlagsSection; + +template class elf::MaxisOptionsSection; +template class elf::MaxisOptionsSection; +template class elf::MaxisOptionsSection; +template class elf::MaxisOptionsSection; + +template class elf::MaxisReginfoSection; +template class elf::MaxisReginfoSection; +template class elf::MaxisReginfoSection; +template class elf::MaxisReginfoSection; + template class elf::MipsAbiFlagsSection; template class elf::MipsAbiFlagsSection; template class elf::MipsAbiFlagsSection; diff --git a/tools/lld/ELF/SyntheticSections.h b/tools/lld/ELF/SyntheticSections.h index a9905905..6b6cf964 100644 --- a/tools/lld/ELF/SyntheticSections.h +++ b/tools/lld/ELF/SyntheticSections.h @@ -165,6 +165,104 @@ class BssSection final : public SyntheticSection { uint64_t Size; }; +class MaxisGotSection final : public SyntheticSection { +public: + MaxisGotSection(); + void writeTo(uint8_t *Buf) override; + size_t getSize() const override { return Size; } + bool updateAllocSize() override; + void finalizeContents() override; + bool empty() const override; + void addEntry(Symbol &Sym, int64_t Addend, RelExpr Expr); + bool addDynTlsEntry(Symbol &Sym); + bool addTlsIndex(); + uint64_t getPageEntryOffset(const Symbol &B, int64_t Addend) const; + uint64_t getSymEntryOffset(const Symbol &B, int64_t Addend) const; + uint64_t getGlobalDynOffset(const Symbol &B) const; + + // Returns the symbol which corresponds to the first entry of the global part + // of GOT on MAXIS platform. It is required to fill up MAXIS-specific dynamic + // table properties. + // Returns nullptr if the global part is empty. + const Symbol *getFirstGlobalEntry() const; + + // Returns the number of entries in the local part of GOT including + // the number of reserved entries. + unsigned getLocalEntriesNum() const; + + // Returns offset of TLS part of the MAXIS GOT table. This part goes + // after 'local' and 'global' entries. + uint64_t getTlsOffset() const; + + uint32_t getTlsIndexOff() const { return TlsIndexOff; } + + uint64_t getGp() const; + +private: + // MAXIS GOT consists of three parts: local, global and tls. Each part + // contains different types of entries. Here is a layout of GOT: + // - Header entries | + // - Page entries | Local part + // - Local entries (16-bit access) | + // - Local entries (32-bit access) | + // - Normal global entries || Global part + // - Reloc-only global entries || + // - TLS entries ||| TLS part + // + // Header: + // Two entries hold predefined value 0x0 and 0x80000000. + // Page entries: + // These entries created by R_MAXIS_GOT_PAGE relocation and R_MAXIS_GOT16 + // relocation against local symbols. They are initialized by higher 16-bit + // of the corresponding symbol's value. So each 64kb of address space + // requires a single GOT entry. + // Local entries (16-bit access): + // These entries created by GOT relocations against global non-preemptible + // symbols so dynamic linker is not necessary to resolve the symbol's + // values. "16-bit access" means that corresponding relocations address + // GOT using 16-bit index. Each unique Symbol-Addend pair has its own + // GOT entry. + // Local entries (32-bit access): + // These entries are the same as above but created by relocations which + // address GOT using 32-bit index (R_MAXIS_GOT_HI16/LO16 etc). + // Normal global entries: + // These entries created by GOT relocations against preemptible global + // symbols. They need to be initialized by dynamic linker and they ordered + // exactly as the corresponding entries in the dynamic symbols table. + // Reloc-only global entries: + // These entries created for symbols that are referenced by dynamic + // relocations R_MAXIS_REL32. These entries are not accessed with gp-relative + // addressing, but MAXIS ABI requires that these entries be present in GOT. + // TLS entries: + // Entries created by TLS relocations. + + // Number of "Header" entries. + static const unsigned HeaderEntriesNum = 2; + // Number of allocated "Page" entries. + uint32_t PageEntriesNum = 0; + // Map output sections referenced by MAXIS GOT relocations + // to the first index of "Page" entries allocated for this section. + llvm::SmallMapVector PageIndexMap; + + typedef std::pair GotEntry; + typedef std::vector GotEntries; + // Map from Symbol-Addend pair to the GOT index. + llvm::DenseMap EntryIndexMap; + // Local entries (16-bit access). + GotEntries LocalEntries; + // Local entries (32-bit access). + GotEntries LocalEntries32; + + // Normal and reloc-only global entries. + GotEntries GlobalEntries; + + // TLS entries. + std::vector TlsEntries; + + uint32_t TlsIndexOff = -1; + uint64_t Size = 0; +}; + class MipsGotSection final : public SyntheticSection { public: MipsGotSection(); @@ -719,6 +817,67 @@ class MergeNoTailSection final : public MergeSyntheticSection { size_t ShardOffsets[NumShards]; }; +// .MAXIS.abiflags section. +template +class MaxisAbiFlagsSection final : public SyntheticSection { + typedef llvm::object::Elf_Maxis_ABIFlags Elf_Maxis_ABIFlags; + +public: + static MaxisAbiFlagsSection *create(); + + MaxisAbiFlagsSection(Elf_Maxis_ABIFlags Flags); + size_t getSize() const override { return sizeof(Elf_Maxis_ABIFlags); } + void writeTo(uint8_t *Buf) override; + +private: + Elf_Maxis_ABIFlags Flags; +}; + +// .MAXIS.options section. +template class MaxisOptionsSection final : public SyntheticSection { + typedef llvm::object::Elf_Maxis_Options Elf_Maxis_Options; + typedef llvm::object::Elf_Maxis_RegInfo Elf_Maxis_RegInfo; + +public: + static MaxisOptionsSection *create(); + + MaxisOptionsSection(Elf_Maxis_RegInfo Reginfo); + void writeTo(uint8_t *Buf) override; + + size_t getSize() const override { + return sizeof(Elf_Maxis_Options) + sizeof(Elf_Maxis_RegInfo); + } + +private: + Elf_Maxis_RegInfo Reginfo; +}; + +// MAXIS .reginfo section. +template class MaxisReginfoSection final : public SyntheticSection { + typedef llvm::object::Elf_Maxis_RegInfo Elf_Maxis_RegInfo; + +public: + static MaxisReginfoSection *create(); + + MaxisReginfoSection(Elf_Maxis_RegInfo Reginfo); + size_t getSize() const override { return sizeof(Elf_Maxis_RegInfo); } + void writeTo(uint8_t *Buf) override; + +private: + Elf_Maxis_RegInfo Reginfo; +}; + +// This is a MAXIS specific section to hold a space within the data segment +// of executable file which is pointed to by the DT_MAXIS_RLD_MAP entry. +// See "Dynamic section" in Chapter 5 in the following document: +// ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf +class MaxisRldMapSection : public SyntheticSection { +public: + MaxisRldMapSection(); + size_t getSize() const override { return Config->Wordsize; } + void writeTo(uint8_t *Buf) override {} +}; + // .MIPS.abiflags section. template class MipsAbiFlagsSection final : public SyntheticSection { @@ -791,7 +950,7 @@ class ARMExidxSentinelSection : public SyntheticSection { }; // A container for one or more linker generated thunks. Instances of these -// thunks including ARM interworking and Mips LA25 PI to non-PI thunks. +// thunks including ARM interworking and Maxis/Mips LA25 PI to non-PI thunks. class ThunkSection : public SyntheticSection { public: // ThunkSection in OS, with desired OutSecOff of Off @@ -837,6 +996,8 @@ struct InX { static GotSection *Got; static GotPltSection *GotPlt; static IgotPltSection *IgotPlt; + static MaxisGotSection *MaxisGot; + static MaxisRldMapSection *MaxisRldMap; static MipsGotSection *MipsGot; static MipsRldMapSection *MipsRldMap; static PltSection *Plt; diff --git a/tools/lld/ELF/Target.cpp b/tools/lld/ELF/Target.cpp index b528fd58..6ea868a7 100644 --- a/tools/lld/ELF/Target.cpp +++ b/tools/lld/ELF/Target.cpp @@ -60,6 +60,19 @@ TargetInfo *elf::getTarget() { return getARMTargetInfo(); case EM_AVR: return getAVRTargetInfo(); + case EM_MAXIS: + switch (Config->EKind) { + case ELF32LEKind: + return getMaxisTargetInfo(); + case ELF32BEKind: + return getMaxisTargetInfo(); + case ELF64LEKind: + return getMaxisTargetInfo(); + case ELF64BEKind: + return getMaxisTargetInfo(); + default: + fatal("unsupported MAXIS target"); + } case EM_MIPS: switch (Config->EKind) { case ELF32LEKind: diff --git a/tools/lld/ELF/Target.h b/tools/lld/ELF/Target.h index 1f58adba..b00cbaa8 100644 --- a/tools/lld/ELF/Target.h +++ b/tools/lld/ELF/Target.h @@ -132,6 +132,7 @@ TargetInfo *getSPARCV9TargetInfo(); TargetInfo *getX32TargetInfo(); TargetInfo *getX86TargetInfo(); TargetInfo *getX86_64TargetInfo(); +template TargetInfo *getMaxisTargetInfo(); template TargetInfo *getMipsTargetInfo(); std::string getErrorLocation(const uint8_t *Loc); @@ -142,6 +143,7 @@ uint64_t getAArch64Page(uint64_t Expr); extern TargetInfo *Target; TargetInfo *getTarget(); +template bool isMaxisPIC(const Defined *Sym); template bool isMipsPIC(const Defined *Sym); static inline void reportRangeError(uint8_t *Loc, RelType Type, const Twine &V, diff --git a/tools/lld/ELF/Thunks.cpp b/tools/lld/ELF/Thunks.cpp index b0bbf6da..3ea65c4e 100644 --- a/tools/lld/ELF/Thunks.cpp +++ b/tools/lld/ELF/Thunks.cpp @@ -11,7 +11,7 @@ // // A thunk is a small piece of code written after an input section // which is used to jump between "incompatible" functions -// such as MIPS PIC and non-PIC or ARM non-Thumb and Thumb functions. +// such as MAXIS/MIPS PIC and non-PIC or ARM non-Thumb and Thumb functions. // // If a jump target is too far and its address doesn't fit to a // short jump instruction, we need to create a thunk too, but we @@ -107,6 +107,39 @@ class ThumbV7PILongThunk final : public Thunk { bool isCompatibleWith(RelType Type) const override; }; +// MAXIS LA25 thunk +class MaxisThunk final : public Thunk { +public: + MaxisThunk(Symbol &Dest) : Thunk(Dest) {} + + uint32_t size() const override { return 16; } + void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + void addSymbols(ThunkSection &IS) override; + InputSection *getTargetInputSection() const override; +}; + +// microMAXIS R2-R5 LA25 thunk +class MicroMaxisThunk final : public Thunk { +public: + MicroMaxisThunk(Symbol &Dest) : Thunk(Dest) {} + + uint32_t size() const override { return 14; } + void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + void addSymbols(ThunkSection &IS) override; + InputSection *getTargetInputSection() const override; +}; + +// microMAXIS R6 LA25 thunk +class MicroMaxisR6Thunk final : public Thunk { +public: + MicroMaxisR6Thunk(Symbol &Dest) : Thunk(Dest) {} + + uint32_t size() const override { return 12; } + void writeTo(uint8_t *Buf, ThunkSection &IS) const override; + void addSymbols(ThunkSection &IS) override; + InputSection *getTargetInputSection() const override; +}; + // MIPS LA25 thunk class MipsThunk final : public Thunk { public: @@ -304,6 +337,78 @@ bool ThumbV7PILongThunk::isCompatibleWith(RelType Type) const { return Type != R_ARM_JUMP24 && Type != R_ARM_PC24 && Type != R_ARM_PLT32; } +// Write MAXIS LA25 thunk code to call PIC function from the non-PIC one. +void MaxisThunk::writeTo(uint8_t *Buf, ThunkSection &) const { + uint64_t S = Destination.getVA(); + write32(Buf, 0x3c190000, Config->Endianness); // lui $25, %hi(func) + write32(Buf + 4, 0x08000000 | (S >> 2), Config->Endianness); // j func + write32(Buf + 8, 0x27390000, Config->Endianness); // addiu $25, $25, %lo(func) + write32(Buf + 12, 0x00000000, Config->Endianness); // nop + Target->relocateOne(Buf, R_MAXIS_HI16, S); + Target->relocateOne(Buf + 8, R_MAXIS_LO16, S); +} + +void MaxisThunk::addSymbols(ThunkSection &IS) { + ThunkSym = + addSyntheticLocal(Saver.save("__LA25Thunk_" + Destination.getName()), + STT_FUNC, Offset, size(), IS); +} + +InputSection *MaxisThunk::getTargetInputSection() const { + auto &DR = cast(Destination); + return dyn_cast(DR.Section); +} + +// Write microMAXIS R2-R5 LA25 thunk code +// to call PIC function from the non-PIC one. +void MicroMaxisThunk::writeTo(uint8_t *Buf, ThunkSection &) const { + uint64_t S = Destination.getVA() | 1; + write16(Buf, 0x41b9, Config->Endianness); // lui $25, %hi(func) + write16(Buf + 4, 0xd400, Config->Endianness); // j func + write16(Buf + 8, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func) + write16(Buf + 12, 0x0c00, Config->Endianness); // nop + Target->relocateOne(Buf, R_MICROMAXIS_HI16, S); + Target->relocateOne(Buf + 4, R_MICROMAXIS_26_S1, S); + Target->relocateOne(Buf + 8, R_MICROMAXIS_LO16, S); +} + +void MicroMaxisThunk::addSymbols(ThunkSection &IS) { + ThunkSym = + addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()), + STT_FUNC, Offset, size(), IS); + ThunkSym->StOther |= STO_MAXIS_MICROMAXIS; +} + +InputSection *MicroMaxisThunk::getTargetInputSection() const { + auto &DR = cast(Destination); + return dyn_cast(DR.Section); +} + +// Write microMAXIS R6 LA25 thunk code +// to call PIC function from the non-PIC one. +void MicroMaxisR6Thunk::writeTo(uint8_t *Buf, ThunkSection &) const { + uint64_t S = Destination.getVA() | 1; + uint64_t P = ThunkSym->getVA(); + write16(Buf, 0x1320, Config->Endianness); // lui $25, %hi(func) + write16(Buf + 4, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func) + write16(Buf + 8, 0x9400, Config->Endianness); // bc func + Target->relocateOne(Buf, R_MICROMAXIS_HI16, S); + Target->relocateOne(Buf + 4, R_MICROMAXIS_LO16, S); + Target->relocateOne(Buf + 8, R_MICROMAXIS_PC26_S1, S - P - 12); +} + +void MicroMaxisR6Thunk::addSymbols(ThunkSection &IS) { + ThunkSym = + addSyntheticLocal(Saver.save("__microLA25Thunk_" + Destination.getName()), + STT_FUNC, Offset, size(), IS); + ThunkSym->StOther |= STO_MAXIS_MICROMAXIS; +} + +InputSection *MicroMaxisR6Thunk::getTargetInputSection() const { + auto &DR = cast(Destination); + return dyn_cast(DR.Section); +} + // Write MIPS LA25 thunk code to call PIC function from the non-PIC one. void MipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const { uint64_t S = Destination.getVA(); @@ -411,6 +516,14 @@ static Thunk *addThunkArm(RelType Reloc, Symbol &S) { fatal("unrecognized relocation type"); } +static Thunk *addThunkMaxis(RelType Type, Symbol &S) { + if ((S.StOther & STO_MAXIS_MICROMAXIS) && isMaxisR6()) + return make(S); + if (S.StOther & STO_MAXIS_MICROMAXIS) + return make(S); + return make(S); +} + static Thunk *addThunkMips(RelType Type, Symbol &S) { if ((S.StOther & STO_MIPS_MICROMIPS) && isMipsR6()) return make(S); @@ -424,9 +537,11 @@ Thunk *addThunk(RelType Type, Symbol &S) { return addThunkAArch64(Type, S); else if (Config->EMachine == EM_ARM) return addThunkArm(Type, S); + else if (Config->EMachine == EM_MAXIS) + return addThunkMaxis(Type, S); else if (Config->EMachine == EM_MIPS) return addThunkMips(Type, S); - llvm_unreachable("add Thunk only supported for ARM and Mips"); + llvm_unreachable("add Thunk only supported for ARM and Maxis/Mips"); return nullptr; } diff --git a/tools/lld/ELF/Thunks.h b/tools/lld/ELF/Thunks.h index 828fac0b..34e266f3 100644 --- a/tools/lld/ELF/Thunks.h +++ b/tools/lld/ELF/Thunks.h @@ -54,7 +54,7 @@ class Thunk { }; // For a Relocation to symbol S create a Thunk to be added to a synthetic -// ThunkSection. At present there are implementations for ARM and Mips Thunks. +// ThunkSection. At present there are implementations for ARM and Maxis/Mips Thunks. Thunk *addThunk(RelType Type, Symbol &S); } // namespace elf diff --git a/tools/lld/ELF/Writer.cpp b/tools/lld/ELF/Writer.cpp index 1c3b6495..4887c528 100644 --- a/tools/lld/ELF/Writer.cpp +++ b/tools/lld/ELF/Writer.cpp @@ -171,7 +171,28 @@ static Defined *addOptionalRegular(StringRef Name, SectionBase *Sec, // The linker is expected to define some symbols depending on // the linking result. This function defines such symbols. void elf::addReservedSymbols() { - if (Config->EMachine == EM_MIPS) { + if (Config->EMachine == EM_MAXIS) { + // Define _gp for MAXIS. st_value of _gp symbol will be updated by Writer + // so that it points to an absolute address which by default is relative + // to GOT. Default offset is 0x7ff0. + // See "Global Data Symbols" in Chapter 6 in the following document: + // ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf + ElfSym::MaxisGp = Symtab->addAbsolute("_gp", STV_HIDDEN, STB_GLOBAL); + + // On MAXIS O32 ABI, _gp_disp is a magic symbol designates offset between + // start of function and 'gp' pointer into GOT. + if (Symtab->find("_gp_disp")) + ElfSym::MaxisGpDisp = + Symtab->addAbsolute("_gp_disp", STV_HIDDEN, STB_GLOBAL); + + // The __gnu_local_gp is a magic symbol equal to the current value of 'gp' + // pointer. This symbol is used in the code generated by .cpload pseudo-op + // in case of using -mno-shared option. + // https://sourceware.org/ml/binutils/2004-12/msg00094.html + if (Symtab->find("__gnu_local_gp")) + ElfSym::MaxisLocalGp = + Symtab->addAbsolute("__gnu_local_gp", STV_HIDDEN, STB_GLOBAL); + } else if (Config->EMachine == EM_MIPS) { // Define _gp for MIPS. st_value of _gp symbol will be updated by Writer // so that it points to an absolute address which by default is relative // to GOT. Default offset is 0x7ff0. @@ -286,8 +307,21 @@ template static void createSyntheticSections() { HasDataRelRo ? ".data.rel.ro.bss" : ".bss.rel.ro", 0, 1); Add(InX::BssRelRo); + // Add MAXIS-specific sections. + if (Config->EMachine == EM_MAXIS) { + if (!Config->Shared && Config->HasDynSymTab) { + InX::MaxisRldMap = make(); + Add(InX::MaxisRldMap); + } + if (auto *Sec = MaxisAbiFlagsSection::create()) + Add(Sec); + if (auto *Sec = MaxisOptionsSection::create()) + Add(Sec); + if (auto *Sec = MaxisReginfoSection::create()) + Add(Sec); + } // Add MIPS-specific sections. - if (Config->EMachine == EM_MIPS) { + else if (Config->EMachine == EM_MIPS) { if (!Config->Shared && Config->HasDynSymTab) { InX::MipsRldMap = make(); Add(InX::MipsRldMap); @@ -330,9 +364,12 @@ template static void createSyntheticSections() { Add(InX::RelaDyn); } - // Add .got. MIPS' .got is so different from the other archs, + // Add .got. MAXIS/MIPS' .got is so different from the other archs, // it has its own class. - if (Config->EMachine == EM_MIPS) { + if (Config->EMachine == EM_MAXIS) { + InX::MaxisGot = make(); + Add(InX::MaxisGot); + } else if (Config->EMachine == EM_MIPS) { InX::MipsGot = make(); Add(InX::MipsGot); } else { @@ -669,6 +706,8 @@ static bool isRelroSection(const OutputSection *Sec) { // * It is easy to check if a give branch was taken. // * It is easy two see how similar two ranks are (see getRankProximity). enum RankFlags { + RF_MAXIS_GPREL = 1 << 18, + RF_MAXIS_NOT_GOT = 1 << 17, RF_NOT_ADDR_SET = 1 << 16, RF_NOT_INTERP = 1 << 15, RF_NOT_ALLOC = 1 << 14, @@ -791,7 +830,15 @@ static unsigned getSectionRank(const OutputSection *Sec) { if (Name == ".branch_lt") Rank |= RF_PPC_BRANCH_LT; } - if (Config->EMachine == EM_MIPS) { + if (Config->EMachine == EM_MAXIS) { + // All sections with SHF_MAXIS_GPREL flag should be grouped together + // because data in these sections is addressable with a gp relative address. + if (Sec->Flags & SHF_MAXIS_GPREL) + Rank |= RF_MAXIS_GPREL; + + if (Sec->Name != ".got") + Rank |= RF_MAXIS_NOT_GOT; + } else if (Config->EMachine == EM_MIPS) { // All sections with SHF_MIPS_GPREL flag should be grouped together // because data in these sections is addressable with a gp relative address. if (Sec->Flags & SHF_MIPS_GPREL) @@ -866,8 +913,10 @@ template void Writer::setReservedSymbolSections() { // The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to // be at some offset from the base of the .got section, usually 0 or the end // of the .got - InputSection *GotSection = InX::MipsGot ? cast(InX::MipsGot) - : cast(InX::Got); + InputSection *GotSection = InX::MaxisGot ? cast(InX::MaxisGot) + : InX::MipsGot + ? cast(InX::MipsGot) + : cast(InX::Got); ElfSym::GlobalOffsetTable->Section = GotSection; } @@ -915,9 +964,19 @@ template void Writer::setReservedSymbolSections() { if (ElfSym::Bss) ElfSym::Bss->Section = findSection(".bss"); - // Setup MIPS _gp_disp/__gnu_local_gp symbols which should + // Setup MAXIS/MIPS _gp_disp/__gnu_local_gp symbols which should // be equal to the _gp symbol's value. - if (ElfSym::MipsGp) { + if (ElfSym::MaxisGp) { + // Find GP-relative section with the lowest address + // and use this address to calculate default _gp value. + for (OutputSection *OS : OutputSections) { + if (OS->Flags & SHF_MAXIS_GPREL) { + ElfSym::MaxisGp->Section = OS; + ElfSym::MaxisGp->Value = 0x7ff0; + break; + } + } + } else if (ElfSym::MipsGp) { // Find GP-relative section with the lowest address // and use this address to calculate default _gp value. for (OutputSection *OS : OutputSections) { @@ -1464,7 +1523,7 @@ template void Writer::finalizeSections() { // Binary and relocatable output does not have PHDRS. // The headers have to be created before finalize as that can influence the - // image base and the dynamic section on mips includes the image base. + // image base and the dynamic section on maxis/mips includes the image base. if (!Config->Relocatable && !Config->OFormatBinary) { Phdrs = Script->hasPhdrsCommands() ? Script->createPhdrs() : createPhdrs(); addPtArmExid(Phdrs); @@ -1480,7 +1539,7 @@ template void Writer::finalizeSections() { applySynthetic( {InX::DynSymTab, InX::Bss, InX::BssRelRo, InX::GnuHashTab, InX::HashTab, InX::SymTab, InX::ShStrTab, InX::StrTab, - In::VerDef, InX::DynStrTab, InX::Got, InX::MipsGot, + In::VerDef, InX::DynStrTab, InX::Got, InX::MaxisGot, InX::MipsGot, InX::IgotPlt, InX::GotPlt, InX::RelaDyn, InX::RelaIplt, InX::RelaPlt, InX::Plt, InX::Iplt, InX::EhFrameHdr, In::VerSym, In::VerNeed, InX::Dynamic}, @@ -1512,7 +1571,9 @@ template void Writer::finalizeSections() { Script->assignAddresses(); Changed |= A64P.createFixes(); } - if (InX::MipsGot) + if (InX::MaxisGot) + InX::MaxisGot->updateAllocSize(); + else if (InX::MipsGot) InX::MipsGot->updateAllocSize(); Changed |= InX::RelaDyn->updateAllocSize(); } while (Changed); diff --git a/tools/lld/ELF/Writer.h b/tools/lld/ELF/Writer.h index f48f9d1e..64d25da6 100644 --- a/tools/lld/ELF/Writer.h +++ b/tools/lld/ELF/Writer.h @@ -56,6 +56,15 @@ struct PhdrEntry { void addReservedSymbols(); llvm::StringRef getOutputSectionName(InputSectionBase *S); +template uint32_t calcMaxisEFlags(); + +uint8_t getMaxisFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, + llvm::StringRef FileName); + +bool isMaxisN32Abi(const InputFile *F); +bool isMicroMaxis(); +bool isMaxisR6(); + template uint32_t calcMipsEFlags(); uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, diff --git a/tools/lld/docs/ReleaseNotes.rst b/tools/lld/docs/ReleaseNotes.rst index ede0b2dc..801b478d 100644 --- a/tools/lld/docs/ReleaseNotes.rst +++ b/tools/lld/docs/ReleaseNotes.rst @@ -59,9 +59,9 @@ ELF Improvements * lld can now generate thunks for out of range branches. -* MIPS port now generates all output dynamic relocations using Elf_Rel format only. +* MAXIS/MIPS port now generates all output dynamic relocations using Elf_Rel format only. -* Added handling of the R_MIPS_26 relocation in case of N32/N64 ABIs and +* Added handling of the R_MAXIS_26/R_MIPS_26 relocation in case of N32/N64 ABIs and generating proper PLT entries. * The following options have been added: ``--icf=none`` ``-z muldefs`` diff --git a/tools/lld/docs/index.rst b/tools/lld/docs/index.rst index f4bf3be9..3583ecb3 100644 --- a/tools/lld/docs/index.rst +++ b/tools/lld/docs/index.rst @@ -34,9 +34,9 @@ Features gold linker. Your milage may vary, though. - It supports various CPUs/ABIs including x86-64, x86, x32, AArch64, - ARM, MIPS 32/64 big/little-endian, PowerPC, PowerPC 64 and AMDGPU. + ARM, MAXIS/MIPS 32/64 big/little-endian, PowerPC, PowerPC 64 and AMDGPU. Among these, x86-64 is the most well-supported target and have - reached production quality. AArch64 and MIPS seem decent too. x86 + reached production quality. AArch64 and MAXIS/MIPS seem decent too. x86 should be OK but not well tested yet. ARM support is being developed actively. @@ -148,7 +148,7 @@ Here is a brief project history of the ELF and COFF ports. - July 2015: The new ELF port was developed based on the COFF linker architecture. -- September 2015: The first patches to support MIPS and AArch64 landed. +- September 2015: The first patches to support MAXIS/MIPS and AArch64 landed. - October 2015: Succeeded to self-host the ELF port. We have noticed that the linker was faster than the GNU linkers, but we weren't sure diff --git a/tools/lld/include/lld/Core/DefinedAtom.h b/tools/lld/include/lld/Core/DefinedAtom.h index 6229d67e..e5d3551c 100644 --- a/tools/lld/include/lld/Core/DefinedAtom.h +++ b/tools/lld/include/lld/Core/DefinedAtom.h @@ -189,6 +189,11 @@ class DefinedAtom : public Atom { // Attributes describe a code model used by the atom. enum CodeModel { codeNA, // no specific code model + // MAXIS code models + codeMaxisPIC, // PIC function in a PIC / non-PIC mixed file + codeMaxisMicro, // microMAXIS instruction encoding + codeMaxisMicroPIC, // microMAXIS instruction encoding + PIC + codeMaxis16, // MAXIS-16 instruction encoding // MIPS code models codeMipsPIC, // PIC function in a PIC / non-PIC mixed file codeMipsMicro, // microMIPS instruction encoding diff --git a/tools/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp b/tools/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp index 59548684..c041830d 100644 --- a/tools/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp +++ b/tools/lld/lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -349,6 +349,10 @@ template <> struct ScalarEnumerationTraits { template <> struct ScalarEnumerationTraits { static void enumeration(IO &io, lld::DefinedAtom::CodeModel &value) { io.enumCase(value, "none", lld::DefinedAtom::codeNA); + io.enumCase(value, "maxis-pic", lld::DefinedAtom::codeMaxisPIC); + io.enumCase(value, "maxis-micro", lld::DefinedAtom::codeMaxisMicro); + io.enumCase(value, "maxis-micro-pic", lld::DefinedAtom::codeMaxisMicroPIC); + io.enumCase(value, "maxis-16", lld::DefinedAtom::codeMaxis16); io.enumCase(value, "mips-pic", lld::DefinedAtom::codeMipsPIC); io.enumCase(value, "mips-micro", lld::DefinedAtom::codeMipsMicro); io.enumCase(value, "mips-micro-pic", lld::DefinedAtom::codeMipsMicroPIC); diff --git a/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp index efbf3bc2..ebbe3141 100644 --- a/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp +++ b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp @@ -257,12 +257,12 @@ extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(int *argc, // // Examples: // - // Fuzz the big-endian MIPS32R6 disassembler using 100,000 inputs of up to + // Fuzz the big-endian MAXIS/MIPS32R6 disassembler using 100,000 inputs of up to // 4-bytes each and use the contents of ./corpus as the test corpus: // llvm-mc-fuzzer -triple mips-linux-gnu -mcpu=mips32r6 -disassemble \ // -fuzzer-args -max_len=4 -runs=100000 ./corpus // - // Infinitely fuzz the little-endian MIPS64R2 disassembler with the MSA + // Infinitely fuzz the little-endian MAXIS/MIPS64R2 disassembler with the MSA // feature enabled using up to 64-byte inputs: // llvm-mc-fuzzer -triple mipsel-linux-gnu -mcpu=mips64r2 -mattr=msa \ // -disassemble -fuzzer-args ./corpus diff --git a/tools/llvm-mc-disassemble-fuzzer/llvm-mc-disassemble-fuzzer.cpp b/tools/llvm-mc-disassemble-fuzzer/llvm-mc-disassemble-fuzzer.cpp index 36d1f7ca..f3a75461 100644 --- a/tools/llvm-mc-disassemble-fuzzer/llvm-mc-disassemble-fuzzer.cpp +++ b/tools/llvm-mc-disassemble-fuzzer/llvm-mc-disassemble-fuzzer.cpp @@ -86,12 +86,12 @@ extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize(int *argc, // // Examples: // - // Fuzz the big-endian MIPS32R6 disassembler using 100,000 inputs of up to + // Fuzz the big-endian MAXIS/MIPS32R6 disassembler using 100,000 inputs of up to // 4-bytes each and use the contents of ./corpus as the test corpus: // llvm-mc-fuzzer -triple mips-linux-gnu -mcpu=mips32r6 -disassemble \ // -fuzzer-args -max_len=4 -runs=100000 ./corpus // - // Infinitely fuzz the little-endian MIPS64R2 disassembler with the MSA + // Infinitely fuzz the little-endian MAXIS/MIPS64R2 disassembler with the MSA // feature enabled using up to 64-byte inputs: // llvm-mc-fuzzer -triple mipsel-linux-gnu -mcpu=mips64r2 -mattr=msa \ // -disassemble -fuzzer-args ./corpus diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 3a911242..fa4a4d83 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -748,6 +748,7 @@ static std::error_code getRelocationValueString(const ELFObjectFile *Obj, case ELF::EM_IAMCU: case ELF::EM_ARM: case ELF::EM_HEXAGON: + case ELF::EM_MAXIS: case ELF::EM_MIPS: case ELF::EM_BPF: case ELF::EM_RISCV: diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index 40a0e46e..f6596bac 100644 --- a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -219,6 +219,15 @@ static std::string formatMachineType(CPUType Cpu) { RETURN_CASE(CPUType, Pentium, "intel pentium"); RETURN_CASE(CPUType, PentiumPro, "intel pentium pro"); RETURN_CASE(CPUType, Pentium3, "intel pentium 3"); + RETURN_CASE(CPUType, MAXIS, "maxis"); + RETURN_CASE(CPUType, MAXIS16, "maxis-16"); + RETURN_CASE(CPUType, MAXIS32, "maxis-32"); + RETURN_CASE(CPUType, MAXIS64, "maxis-64"); + RETURN_CASE(CPUType, MAXISI, "maxis i"); + RETURN_CASE(CPUType, MAXISII, "maxis ii"); + RETURN_CASE(CPUType, MAXISIII, "maxis iii"); + RETURN_CASE(CPUType, MAXISIV, "maxis iv"); + RETURN_CASE(CPUType, MAXISV, "maxis v"); RETURN_CASE(CPUType, MIPS, "mips"); RETURN_CASE(CPUType, MIPS16, "mips-16"); RETURN_CASE(CPUType, MIPS32, "mips-32"); diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp index fae89920..d75da856 100644 --- a/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -87,6 +87,7 @@ static std::string formatCallingConvention(CallingConvention Convention) { RETURN_CASE(CallingConvention, Generic, "generic"); RETURN_CASE(CallingConvention, Inline, "inline"); RETURN_CASE(CallingConvention, M32RCall, "m32rcall"); + RETURN_CASE(CallingConvention, MaxisCall, "maxiscall"); RETURN_CASE(CallingConvention, MipsCall, "mipscall"); RETURN_CASE(CallingConvention, NearC, "cdecl"); RETURN_CASE(CallingConvention, NearFast, "fastcall"); diff --git a/tools/llvm-pdbutil/PdbYaml.cpp b/tools/llvm-pdbutil/PdbYaml.cpp index eb39708a..0b34a4c3 100644 --- a/tools/llvm-pdbutil/PdbYaml.cpp +++ b/tools/llvm-pdbutil/PdbYaml.cpp @@ -42,6 +42,9 @@ template <> struct ScalarEnumerationTraits { io.enumCase(Value, "x86", PDB_Machine::x86); io.enumCase(Value, "Ia64", PDB_Machine::Ia64); io.enumCase(Value, "M32R", PDB_Machine::M32R); + io.enumCase(Value, "Maxis16", PDB_Machine::Maxis16); + io.enumCase(Value, "MaxisFpu", PDB_Machine::MaxisFpu); + io.enumCase(Value, "MaxisFpu16", PDB_Machine::MaxisFpu16); io.enumCase(Value, "Mips16", PDB_Machine::Mips16); io.enumCase(Value, "MipsFpu", PDB_Machine::MipsFpu); io.enumCase(Value, "MipsFpu16", PDB_Machine::MipsFpu16); @@ -50,6 +53,7 @@ template <> struct ScalarEnumerationTraits { io.enumCase(Value, "SH3", PDB_Machine::SH3); io.enumCase(Value, "SH3DSP", PDB_Machine::SH3DSP); io.enumCase(Value, "Thumb", PDB_Machine::Thumb); + io.enumCase(Value, "WceMaxisV2", PDB_Machine::WceMaxisV2); io.enumCase(Value, "WceMipsV2", PDB_Machine::WceMipsV2); } }; diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 0e76e75c..16a70598 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -338,6 +338,9 @@ static const EnumEntry ImageFileMachineType[] = { LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_I386 ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_IA64 ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_M32R ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MAXIS16 ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MAXISFPU ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MAXISFPU16), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPS16 ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPSFPU ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_MIPSFPU16), @@ -349,6 +352,7 @@ static const EnumEntry ImageFileMachineType[] = { LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH4 ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_SH5 ), LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_THUMB ), + LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_WCEMAXISV2) LLVM_READOBJ_ENUM_ENT(COFF, IMAGE_FILE_MACHINE_WCEMIPSV2) }; diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 5605eaea..8fcb3435 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -44,6 +44,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/MathExtras.h" +#include "llvm/Support/MaxisABIFlags.h" #include "llvm/Support/MipsABIFlags.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" @@ -153,6 +154,11 @@ class ELFDumper : public ObjDumper { void printGroupSections() override; void printAttributes() override; + void printMaxisPLTGOT() override; + void printMaxisABIFlags() override; + void printMaxisReginfo() override; + void printMaxisOptions() override; + void printMipsPLTGOT() override; void printMipsABIFlags() override; void printMipsReginfo() override; @@ -291,6 +297,7 @@ void ELFDumper::printSymbolsHelper(bool IsDynamic) const { ELFDumperStyle->printSymbol(Obj, &Sym, Syms.begin(), StrTable, IsDynamic); } +template class MaxisGOTParser; template class MipsGOTParser; template class DumpStyle { @@ -316,6 +323,8 @@ template class DumpStyle { virtual void printProgramHeaders(const ELFFile *Obj) = 0; virtual void printHashHistogram(const ELFFile *Obj) = 0; virtual void printNotes(const ELFFile *Obj) = 0; + virtual void printMaxisGOT(const MaxisGOTParser &Parser) = 0; + virtual void printMaxisPLT(const MaxisGOTParser &Parser) = 0; virtual void printMipsGOT(const MipsGOTParser &Parser) = 0; virtual void printMipsPLT(const MipsGOTParser &Parser) = 0; const ELFDumper *dumper() const { return Dumper; } @@ -345,6 +354,8 @@ template class GNUStyle : public DumpStyle { void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile *Obj) override; void printNotes(const ELFFile *Obj) override; + void printMaxisGOT(const MaxisGOTParser &Parser) override; + void printMaxisPLT(const MaxisGOTParser &Parser) override; void printMipsGOT(const MipsGOTParser &Parser) override; void printMipsPLT(const MipsGOTParser &Parser) override; @@ -405,6 +416,8 @@ template class LLVMStyle : public DumpStyle { void printProgramHeaders(const ELFO *Obj) override; void printHashHistogram(const ELFFile *Obj) override; void printNotes(const ELFFile *Obj) override; + void printMaxisGOT(const MaxisGOTParser &Parser) override; + void printMaxisPLT(const MaxisGOTParser &Parser) override; void printMipsGOT(const MipsGOTParser &Parser) override; void printMipsPLT(const MipsGOTParser &Parser) override; @@ -858,8 +871,10 @@ static const EnumEntry ElfMachineType[] = { ENUM_ENT(EM_88K, "MC88000"), ENUM_ENT(EM_IAMCU, "EM_IAMCU"), ENUM_ENT(EM_860, "Intel 80860"), + ENUM_ENT(EM_MAXIS, "MAXIS R3000"), ENUM_ENT(EM_MIPS, "MIPS R3000"), ENUM_ENT(EM_S370, "IBM System/370"), + ENUM_ENT(EM_MAXIS_RS3_LE, "MAXIS R3000 little-endian"), ENUM_ENT(EM_MIPS_RS3_LE, "MIPS R3000 little-endian"), ENUM_ENT(EM_PARISC, "HPPA"), ENUM_ENT(EM_VPP500, "Fujitsu VPP500"), @@ -884,6 +899,7 @@ static const EnumEntry ElfMachineType[] = { ENUM_ENT(EM_H8S, "Hitachi H8S"), ENUM_ENT(EM_H8_500, "Hitachi H8/500"), ENUM_ENT(EM_IA_64, "Intel IA-64"), + ENUM_ENT(EM_MAXIS_X, "Stanford MAXIS-X"), ENUM_ENT(EM_MIPS_X, "Stanford MIPS-X"), ENUM_ENT(EM_COLDFIRE, "Motorola Coldfire"), ENUM_ENT(EM_68HC12, "Motorola MC68HC12 Microcontroller"), @@ -1075,6 +1091,17 @@ static const EnumEntry ElfHexagonSectionFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, SHF_HEX_GPREL) }; +static const EnumEntry ElfMaxisSectionFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MAXIS_NODUPES), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MAXIS_NAMES ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MAXIS_LOCAL ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MAXIS_NOSTRIP), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MAXIS_GPREL ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MAXIS_MERGE ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MAXIS_ADDR ), + LLVM_READOBJ_ENUM_ENT(ELF, SHF_MAXIS_STRING ) +}; + static const EnumEntry ElfMipsSectionFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NODUPES), LLVM_READOBJ_ENUM_ENT(ELF, SHF_MIPS_NAMES ), @@ -1129,6 +1156,15 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) { switch (Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX); } + case ELF::EM_MAXIS: + case ELF::EM_MAXIS_RS3_LE: + switch (Type) { + LLVM_READOBJ_ENUM_CASE(ELF, PT_MAXIS_REGINFO); + LLVM_READOBJ_ENUM_CASE(ELF, PT_MAXIS_RTPROC); + LLVM_READOBJ_ENUM_CASE(ELF, PT_MAXIS_OPTIONS); + LLVM_READOBJ_ENUM_CASE(ELF, PT_MAXIS_ABIFLAGS); + } + } case ELF::EM_MIPS: case ELF::EM_MIPS_RS3_LE: switch (Type) { @@ -1184,6 +1220,20 @@ static std::string getElfPtType(unsigned Arch, unsigned Type) { if (Type == ELF::PT_ARM_EXIDX) return "EXIDX"; return ""; + case ELF::EM_MAXIS: + case ELF::EM_MAXIS_RS3_LE: + switch (Type) { + case PT_MAXIS_REGINFO: + return "REGINFO"; + case PT_MAXIS_RTPROC: + return "RTPROC"; + case PT_MAXIS_OPTIONS: + return "OPTIONS"; + case PT_MAXIS_ABIFLAGS: + return "ABIFLAGS"; + } + return ""; + } case ELF::EM_MIPS: case ELF::EM_MIPS_RS3_LE: switch (Type) { @@ -1208,6 +1258,52 @@ static const EnumEntry ElfSegmentFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, PF_R) }; +static const EnumEntry ElfHeaderMaxisFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_NOREORDER), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_PIC), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_CPIC), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ABI2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_32BITMODE), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_FP64), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_NAN2008), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ABI_O32), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ABI_O64), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ABI_EABI32), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ABI_EABI64), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_3900), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_4010), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_4100), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_4650), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_4120), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_4111), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_SB1), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_OCTEON), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_XLR), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_OCTEON2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_OCTEON3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_5400), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_5900), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_5500), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_9000), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_LS2E), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_LS2F), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MACH_LS3A), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_MICROMAXIS), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_ASE_M16), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_ASE_MDMX), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_1), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_3), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_4), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_5), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_32), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_64), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_32R2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_64R2), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_32R6), + LLVM_READOBJ_ENUM_ENT(ELF, EF_MAXIS_ARCH_64R6) +}; + static const EnumEntry ElfHeaderMipsFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_NOREORDER), LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_PIC), @@ -1274,6 +1370,37 @@ static const EnumEntry ElfSymOtherFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, STV_PROTECTED) }; +static const EnumEntry ElfMaxisSymOtherFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, STO_MAXIS_OPTIONAL), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MAXIS_PLT), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MAXIS_PIC), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MAXIS_MICROMAXIS) +}; + +static const EnumEntry ElfMaxis16SymOtherFlags[] = { + LLVM_READOBJ_ENUM_ENT(ELF, STO_MAXIS_OPTIONAL), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MAXIS_PLT), + LLVM_READOBJ_ENUM_ENT(ELF, STO_MAXIS_MAXIS16) +}; + +static const char *getElfMaxisOptionsOdkType(unsigned Odk) { + switch (Odk) { + LLVM_READOBJ_ENUM_CASE(ELF, ODK_NULL); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_REGINFO); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_EXCEPTIONS); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_PAD); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWPATCH); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_FILL); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_TAGS); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWAND); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_HWOR); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_GP_GROUP); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_IDENT); + LLVM_READOBJ_ENUM_CASE(ELF, ODK_PAGESIZE); + default: + return "Unknown"; + } +} static const EnumEntry ElfMipsSymOtherFlags[] = { LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_OPTIONAL), LLVM_READOBJ_ENUM_ENT(ELF, STO_MIPS_PLT), @@ -1512,6 +1639,21 @@ static const char *getTypeString(unsigned Arch, uint64_t Type) { LLVM_READOBJ_TYPE_CASE(HEXAGON_VER); LLVM_READOBJ_TYPE_CASE(HEXAGON_PLT); } + case EM_MAXIS: + switch (Type) { + LLVM_READOBJ_TYPE_CASE(MAXIS_RLD_MAP_REL); + LLVM_READOBJ_TYPE_CASE(MAXIS_RLD_VERSION); + LLVM_READOBJ_TYPE_CASE(MAXIS_FLAGS); + LLVM_READOBJ_TYPE_CASE(MAXIS_BASE_ADDRESS); + LLVM_READOBJ_TYPE_CASE(MAXIS_LOCAL_GOTNO); + LLVM_READOBJ_TYPE_CASE(MAXIS_SYMTABNO); + LLVM_READOBJ_TYPE_CASE(MAXIS_UNREFEXTNO); + LLVM_READOBJ_TYPE_CASE(MAXIS_GOTSYM); + LLVM_READOBJ_TYPE_CASE(MAXIS_RLD_MAP); + LLVM_READOBJ_TYPE_CASE(MAXIS_PLTGOT); + LLVM_READOBJ_TYPE_CASE(MAXIS_OPTIONS); + } + } case EM_MIPS: switch (Type) { LLVM_READOBJ_TYPE_CASE(MIPS_RLD_MAP_REL); @@ -1623,6 +1765,25 @@ static const EnumEntry ElfDynamicDTFlags1[] = { LLVM_READOBJ_DT_FLAG_ENT(DF_1, SINGLETON) }; +static const EnumEntry ElfDynamicDTMaxisFlags[] = { + LLVM_READOBJ_DT_FLAG_ENT(RHF, NONE), + LLVM_READOBJ_DT_FLAG_ENT(RHF, QUICKSTART), + LLVM_READOBJ_DT_FLAG_ENT(RHF, NOTPOT), + LLVM_READOBJ_DT_FLAG_ENT(RHS, NO_LIBRARY_REPLACEMENT), + LLVM_READOBJ_DT_FLAG_ENT(RHF, NO_MOVE), + LLVM_READOBJ_DT_FLAG_ENT(RHF, SGI_ONLY), + LLVM_READOBJ_DT_FLAG_ENT(RHF, GUARANTEE_INIT), + LLVM_READOBJ_DT_FLAG_ENT(RHF, DELTA_C_PLUS_PLUS), + LLVM_READOBJ_DT_FLAG_ENT(RHF, GUARANTEE_START_INIT), + LLVM_READOBJ_DT_FLAG_ENT(RHF, PIXIE), + LLVM_READOBJ_DT_FLAG_ENT(RHF, DEFAULT_DELAY_LOAD), + LLVM_READOBJ_DT_FLAG_ENT(RHF, REQUICKSTART), + LLVM_READOBJ_DT_FLAG_ENT(RHF, REQUICKSTARTED), + LLVM_READOBJ_DT_FLAG_ENT(RHF, CORD), + LLVM_READOBJ_DT_FLAG_ENT(RHF, NO_UNRES_UNDEF), + LLVM_READOBJ_DT_FLAG_ENT(RHF, RLD_ORDER_SAFE) +}; + static const EnumEntry ElfDynamicDTMipsFlags[] = { LLVM_READOBJ_DT_FLAG_ENT(RHF, NONE), LLVM_READOBJ_DT_FLAG_ENT(RHF, QUICKSTART), @@ -1706,6 +1867,12 @@ void ELFDumper::printValue(uint64_t Type, uint64_t Value) { case DT_VERSYM: case DT_GNU_HASH: case DT_NULL: + case DT_MAXIS_BASE_ADDRESS: + case DT_MAXIS_GOTSYM: + case DT_MAXIS_RLD_MAP: + case DT_MAXIS_RLD_MAP_REL: + case DT_MAXIS_PLTGOT: + case DT_MAXIS_OPTIONS: case DT_MIPS_BASE_ADDRESS: case DT_MIPS_GOTSYM: case DT_MIPS_RLD_MAP: @@ -1718,6 +1885,10 @@ void ELFDumper::printValue(uint64_t Type, uint64_t Value) { case DT_RELCOUNT: case DT_VERDEFNUM: case DT_VERNEEDNUM: + case DT_MAXIS_RLD_VERSION: + case DT_MAXIS_LOCAL_GOTNO: + case DT_MAXIS_SYMTABNO: + case DT_MAXIS_UNREFEXTNO: case DT_MIPS_RLD_VERSION: case DT_MIPS_LOCAL_GOTNO: case DT_MIPS_SYMTABNO: @@ -1754,6 +1925,9 @@ void ELFDumper::printValue(uint64_t Type, uint64_t Value) { case DT_RUNPATH: OS << getDynamicString(Value); break; + case DT_MAXIS_FLAGS: + printFlags(Value, makeArrayRef(ElfDynamicDTMaxisFlags), OS); + break; case DT_MIPS_FLAGS: printFlags(Value, makeArrayRef(ElfDynamicDTMipsFlags), OS); break; @@ -1913,6 +2087,56 @@ template <> void ELFDumper>::printAttributes() { } } +template class MaxisGOTParser { +public: + TYPEDEF_ELF_TYPES(ELFT) + using Entry = typename ELFO::Elf_Addr; + using Entries = ArrayRef; + + const bool IsStatic; + const ELFO * const Obj; + + MaxisGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms); + + bool hasGot() const { return !GotEntries.empty(); } + bool hasPlt() const { return !PltEntries.empty(); } + + uint64_t getGp() const; + + const Entry *getGotLazyResolver() const; + const Entry *getGotModulePointer() const; + const Entry *getPltLazyResolver() const; + const Entry *getPltModulePointer() const; + + Entries getLocalEntries() const; + Entries getGlobalEntries() const; + Entries getOtherEntries() const; + Entries getPltEntries() const; + + uint64_t getGotAddress(const Entry * E) const; + int64_t getGotOffset(const Entry * E) const; + const Elf_Sym *getGotSym(const Entry *E) const; + + uint64_t getPltAddress(const Entry * E) const; + const Elf_Sym *getPltSym(const Entry *E) const; + + StringRef getPltStrTable() const { return PltStrTable; } + +private: + const Elf_Shdr *GotSec; + size_t LocalNum; + size_t GlobalNum; + + const Elf_Shdr *PltSec; + const Elf_Shdr *PltRelSec; + const Elf_Shdr *PltSymTable; + Elf_Sym_Range GotDynSyms; + StringRef PltStrTable; + + Entries GotEntries; + Entries PltEntries; +}; + template class MipsGOTParser { public: TYPEDEF_ELF_TYPES(ELFT) @@ -1965,6 +2189,386 @@ template class MipsGOTParser { } // end anonymous namespace +template +MaxisGOTParser::MaxisGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, + Elf_Sym_Range DynSyms) + : IsStatic(DynTable.empty()), Obj(Obj), GotSec(nullptr), LocalNum(0), + GlobalNum(0), PltSec(nullptr), PltRelSec(nullptr), PltSymTable(nullptr) { + // See "Global Offset Table" in Chapter 5 in the following document + // for detailed GOT description. + // ftp://www.linux-maxis.org/pub/linux/maxis/doc/ABI/maxisabi.pdf + + // Find static GOT secton. + if (IsStatic) { + GotSec = findSectionByName(*Obj, ".got"); + if (!GotSec) + reportError("Cannot find .got section"); + + ArrayRef Content = unwrapOrError(Obj->getSectionContents(GotSec)); + GotEntries = Entries(reinterpret_cast(Content.data()), + Content.size() / sizeof(Entry)); + LocalNum = GotEntries.size(); + return; + } + + // Lookup dynamic table tags which define GOT/PLT layouts. + Optional DtPltGot; + Optional DtLocalGotNum; + Optional DtGotSym; + Optional DtMaxisPltGot; + Optional DtJmpRel; + for (const auto &Entry : DynTable) { + switch (Entry.getTag()) { + case ELF::DT_PLTGOT: + DtPltGot = Entry.getVal(); + break; + case ELF::DT_MAXIS_LOCAL_GOTNO: + DtLocalGotNum = Entry.getVal(); + break; + case ELF::DT_MAXIS_GOTSYM: + DtGotSym = Entry.getVal(); + break; + case ELF::DT_MAXIS_PLTGOT: + DtMaxisPltGot = Entry.getVal(); + break; + case ELF::DT_JMPREL: + DtJmpRel = Entry.getVal(); + break; + } + } + + // Find dynamic GOT section. + if (DtPltGot || DtLocalGotNum || DtGotSym) { + if (!DtPltGot) + report_fatal_error("Cannot find PLTGOT dynamic table tag."); + if (!DtLocalGotNum) + report_fatal_error("Cannot find MAXIS_LOCAL_GOTNO dynamic table tag."); + if (!DtGotSym) + report_fatal_error("Cannot find MAXIS_GOTSYM dynamic table tag."); + + size_t DynSymTotal = DynSyms.size(); + if (*DtGotSym > DynSymTotal) + reportError("MAXIS_GOTSYM exceeds a number of dynamic symbols"); + + GotSec = findNotEmptySectionByAddress(Obj, *DtPltGot); + if (!GotSec) + reportError("There is no not empty GOT section at 0x" + + Twine::utohexstr(*DtPltGot)); + + LocalNum = *DtLocalGotNum; + GlobalNum = DynSymTotal - *DtGotSym; + + ArrayRef Content = unwrapOrError(Obj->getSectionContents(GotSec)); + GotEntries = Entries(reinterpret_cast(Content.data()), + Content.size() / sizeof(Entry)); + GotDynSyms = DynSyms.drop_front(*DtGotSym); + } + + // Find PLT section. + if (DtMaxisPltGot || DtJmpRel) { + if (!DtMaxisPltGot) + report_fatal_error("Cannot find MAXIS_PLTGOT dynamic table tag."); + if (!DtJmpRel) + report_fatal_error("Cannot find JMPREL dynamic table tag."); + + PltSec = findNotEmptySectionByAddress(Obj, *DtMaxisPltGot); + if (!PltSec) + report_fatal_error("There is no not empty PLTGOT section at 0x " + + Twine::utohexstr(*DtMaxisPltGot)); + + PltRelSec = findNotEmptySectionByAddress(Obj, *DtJmpRel); + if (!PltRelSec) + report_fatal_error("There is no not empty RELPLT section at 0x" + + Twine::utohexstr(*DtJmpRel)); + + ArrayRef PltContent = + unwrapOrError(Obj->getSectionContents(PltSec)); + PltEntries = Entries(reinterpret_cast(PltContent.data()), + PltContent.size() / sizeof(Entry)); + + PltSymTable = unwrapOrError(Obj->getSection(PltRelSec->sh_link)); + PltStrTable = unwrapOrError(Obj->getStringTableForSymtab(*PltSymTable)); + } +} + +template uint64_t MaxisGOTParser::getGp() const { + return GotSec->sh_addr + 0x7ff0; +} + +template +const typename MaxisGOTParser::Entry * +MaxisGOTParser::getGotLazyResolver() const { + return LocalNum > 0 ? &GotEntries[0] : nullptr; +} + +template +const typename MaxisGOTParser::Entry * +MaxisGOTParser::getGotModulePointer() const { + if (LocalNum < 2) + return nullptr; + const Entry &E = GotEntries[1]; + if ((E >> (sizeof(Entry) * 8 - 1)) == 0) + return nullptr; + return &E; +} + +template +typename MaxisGOTParser::Entries +MaxisGOTParser::getLocalEntries() const { + size_t Skip = getGotModulePointer() ? 2 : 1; + if (LocalNum - Skip <= 0) + return Entries(); + return GotEntries.slice(Skip, LocalNum - Skip); +} + +template +typename MaxisGOTParser::Entries +MaxisGOTParser::getGlobalEntries() const { + if (GlobalNum == 0) + return Entries(); + return GotEntries.slice(LocalNum, GlobalNum); +} + +template +typename MaxisGOTParser::Entries +MaxisGOTParser::getOtherEntries() const { + size_t OtherNum = GotEntries.size() - LocalNum - GlobalNum; + if (OtherNum == 0) + return Entries(); + return GotEntries.slice(LocalNum + GlobalNum, OtherNum); +} + +template +uint64_t MaxisGOTParser::getGotAddress(const Entry *E) const { + int64_t Offset = std::distance(GotEntries.data(), E) * sizeof(Entry); + return GotSec->sh_addr + Offset; +} + +template +int64_t MaxisGOTParser::getGotOffset(const Entry *E) const { + int64_t Offset = std::distance(GotEntries.data(), E) * sizeof(Entry); + return Offset - 0x7ff0; +} + +template +const typename MaxisGOTParser::Elf_Sym * +MaxisGOTParser::getGotSym(const Entry *E) const { + int64_t Offset = std::distance(GotEntries.data(), E); + return &GotDynSyms[Offset - LocalNum]; +} + +template +const typename MaxisGOTParser::Entry * +MaxisGOTParser::getPltLazyResolver() const { + return PltEntries.empty() ? nullptr : &PltEntries[0]; +} + +template +const typename MaxisGOTParser::Entry * +MaxisGOTParser::getPltModulePointer() const { + return PltEntries.size() < 2 ? nullptr : &PltEntries[1]; +} + +template +typename MaxisGOTParser::Entries +MaxisGOTParser::getPltEntries() const { + if (PltEntries.size() <= 2) + return Entries(); + return PltEntries.slice(2, PltEntries.size() - 2); +} + +template +uint64_t MaxisGOTParser::getPltAddress(const Entry *E) const { + int64_t Offset = std::distance(PltEntries.data(), E) * sizeof(Entry); + return PltSec->sh_addr + Offset; +} + +template +const typename MaxisGOTParser::Elf_Sym * +MaxisGOTParser::getPltSym(const Entry *E) const { + int64_t Offset = std::distance(getPltEntries().data(), E); + if (PltRelSec->sh_type == ELF::SHT_REL) { + Elf_Rel_Range Rels = unwrapOrError(Obj->rels(PltRelSec)); + return unwrapOrError(Obj->getRelocationSymbol(&Rels[Offset], PltSymTable)); + } else { + Elf_Rela_Range Rels = unwrapOrError(Obj->relas(PltRelSec)); + return unwrapOrError(Obj->getRelocationSymbol(&Rels[Offset], PltSymTable)); + } +} + +template void ELFDumper::printMaxisPLTGOT() { + if (Obj->getHeader()->e_machine != EM_MAXIS) + reportError("MAXIS PLT GOT is available for MAXIS targets only"); + + MaxisGOTParser Parser(Obj, dynamic_table(), dynamic_symbols()); + if (Parser.hasGot()) + ELFDumperStyle->printMaxisGOT(Parser); + if (Parser.hasPlt()) + ELFDumperStyle->printMaxisPLT(Parser); +} + +static const EnumEntry ElfMaxisISAExtType[] = { + {"None", Maxis::AFL_EXT_NONE}, + {"Broadcom SB-1", Maxis::AFL_EXT_SB1}, + {"Cavium Networks Octeon", Maxis::AFL_EXT_OCTEON}, + {"Cavium Networks Octeon2", Maxis::AFL_EXT_OCTEON2}, + {"Cavium Networks OcteonP", Maxis::AFL_EXT_OCTEONP}, + {"Cavium Networks Octeon3", Maxis::AFL_EXT_OCTEON3}, + {"LSI R4010", Maxis::AFL_EXT_4010}, + {"Loongson 2E", Maxis::AFL_EXT_LOONGSON_2E}, + {"Loongson 2F", Maxis::AFL_EXT_LOONGSON_2F}, + {"Loongson 3A", Maxis::AFL_EXT_LOONGSON_3A}, + {"MAXIS R4650", Maxis::AFL_EXT_4650}, + {"MAXIS R5900", Maxis::AFL_EXT_5900}, + {"MAXIS R10000", Maxis::AFL_EXT_10000}, + {"NEC VR4100", Maxis::AFL_EXT_4100}, + {"NEC VR4111/VR4181", Maxis::AFL_EXT_4111}, + {"NEC VR4120", Maxis::AFL_EXT_4120}, + {"NEC VR5400", Maxis::AFL_EXT_5400}, + {"NEC VR5500", Maxis::AFL_EXT_5500}, + {"RMI Xlr", Maxis::AFL_EXT_XLR}, + {"Toshiba R3900", Maxis::AFL_EXT_3900} +}; + +static const EnumEntry ElfMaxisASEFlags[] = { + {"DSP", Maxis::AFL_ASE_DSP}, + {"DSPR2", Maxis::AFL_ASE_DSPR2}, + {"Enhanced VA Scheme", Maxis::AFL_ASE_EVA}, + {"MCU", Maxis::AFL_ASE_MCU}, + {"MDMX", Maxis::AFL_ASE_MDMX}, + {"MAXIS-3D", Maxis::AFL_ASE_MAXIS3D}, + {"MT", Maxis::AFL_ASE_MT}, + {"SmartMAXIS", Maxis::AFL_ASE_SMARTMAXIS}, + {"VZ", Maxis::AFL_ASE_VIRT}, + {"MSA", Maxis::AFL_ASE_MSA}, + {"MAXIS16", Maxis::AFL_ASE_MAXIS16}, + {"microMAXIS", Maxis::AFL_ASE_MICROMAXIS}, + {"XPA", Maxis::AFL_ASE_XPA} +}; + +static const EnumEntry ElfMaxisFpABIType[] = { + {"Hard or soft float", Maxis::Val_GNU_MAXIS_ABI_FP_ANY}, + {"Hard float (double precision)", Maxis::Val_GNU_MAXIS_ABI_FP_DOUBLE}, + {"Hard float (single precision)", Maxis::Val_GNU_MAXIS_ABI_FP_SINGLE}, + {"Soft float", Maxis::Val_GNU_MAXIS_ABI_FP_SOFT}, + {"Hard float (MAXIS32r2 64-bit FPU 12 callee-saved)", + Maxis::Val_GNU_MAXIS_ABI_FP_OLD_64}, + {"Hard float (32-bit CPU, Any FPU)", Maxis::Val_GNU_MAXIS_ABI_FP_XX}, + {"Hard float (32-bit CPU, 64-bit FPU)", Maxis::Val_GNU_MAXIS_ABI_FP_64}, + {"Hard float compat (32-bit CPU, 64-bit FPU)", + Maxis::Val_GNU_MAXIS_ABI_FP_64A} +}; + +static const EnumEntry ElfMaxisFlags1[] { + {"ODDSPREG", Maxis::AFL_FLAGS1_ODDSPREG}, +}; + +static int getMaxisRegisterSize(uint8_t Flag) { + switch (Flag) { + case Maxis::AFL_REG_NONE: + return 0; + case Maxis::AFL_REG_32: + return 32; + case Maxis::AFL_REG_64: + return 64; + case Maxis::AFL_REG_128: + return 128; + default: + return -1; + } +} + +template void ELFDumper::printMaxisABIFlags() { + const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MAXIS.abiflags"); + if (!Shdr) { + W.startLine() << "There is no .MAXIS.abiflags section in the file.\n"; + return; + } + ArrayRef Sec = unwrapOrError(Obj->getSectionContents(Shdr)); + if (Sec.size() != sizeof(Elf_Maxis_ABIFlags)) { + W.startLine() << "The .MAXIS.abiflags section has a wrong size.\n"; + return; + } + + auto *Flags = reinterpret_cast *>(Sec.data()); + + raw_ostream &OS = W.getOStream(); + DictScope GS(W, "MAXIS ABI Flags"); + + W.printNumber("Version", Flags->version); + W.startLine() << "ISA: "; + if (Flags->isa_rev <= 1) + OS << format("MAXIS%u", Flags->isa_level); + else + OS << format("MAXIS%ur%u", Flags->isa_level, Flags->isa_rev); + OS << "\n"; + W.printEnum("ISA Extension", Flags->isa_ext, makeArrayRef(ElfMaxisISAExtType)); + W.printFlags("ASEs", Flags->ases, makeArrayRef(ElfMaxisASEFlags)); + W.printEnum("FP ABI", Flags->fp_abi, makeArrayRef(ElfMaxisFpABIType)); + W.printNumber("GPR size", getMaxisRegisterSize(Flags->gpr_size)); + W.printNumber("CPR1 size", getMaxisRegisterSize(Flags->cpr1_size)); + W.printNumber("CPR2 size", getMaxisRegisterSize(Flags->cpr2_size)); + W.printFlags("Flags 1", Flags->flags1, makeArrayRef(ElfMaxisFlags1)); + W.printHex("Flags 2", Flags->flags2); +} + +template +static void printMaxisReginfoData(ScopedPrinter &W, + const Elf_Maxis_RegInfo &Reginfo) { + W.printHex("GP", Reginfo.ri_gp_value); + W.printHex("General Mask", Reginfo.ri_gprmask); + W.printHex("Co-Proc Mask0", Reginfo.ri_cprmask[0]); + W.printHex("Co-Proc Mask1", Reginfo.ri_cprmask[1]); + W.printHex("Co-Proc Mask2", Reginfo.ri_cprmask[2]); + W.printHex("Co-Proc Mask3", Reginfo.ri_cprmask[3]); +} + +template void ELFDumper::printMaxisReginfo() { + const Elf_Shdr *Shdr = findSectionByName(*Obj, ".reginfo"); + if (!Shdr) { + W.startLine() << "There is no .reginfo section in the file.\n"; + return; + } + ArrayRef Sec = unwrapOrError(Obj->getSectionContents(Shdr)); + if (Sec.size() != sizeof(Elf_Maxis_RegInfo)) { + W.startLine() << "The .reginfo section has a wrong size.\n"; + return; + } + + DictScope GS(W, "MAXIS RegInfo"); + auto *Reginfo = reinterpret_cast *>(Sec.data()); + printMaxisReginfoData(W, *Reginfo); +} + +template void ELFDumper::printMaxisOptions() { + const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MAXIS.options"); + if (!Shdr) { + W.startLine() << "There is no .MAXIS.options section in the file.\n"; + return; + } + + DictScope GS(W, "MAXIS Options"); + + ArrayRef Sec = unwrapOrError(Obj->getSectionContents(Shdr)); + while (!Sec.empty()) { + if (Sec.size() < sizeof(Elf_Maxis_Options)) { + W.startLine() << "The .MAXIS.options section has a wrong size.\n"; + return; + } + auto *O = reinterpret_cast *>(Sec.data()); + DictScope GS(W, getElfMaxisOptionsOdkType(O->kind)); + switch (O->kind) { + case ODK_REGINFO: + printMaxisReginfoData(W, O->getRegInfo()); + break; + default: + W.startLine() << "Unsupported MAXIS options tag.\n"; + break; + } + Sec = Sec.slice(O->size); + } +} + template MipsGOTParser::MipsGOTParser(const ELFO *Obj, Elf_Dyn_Range DynTable, Elf_Sym_Range DynSyms) @@ -2531,7 +3135,11 @@ void GNUStyle::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab, // First two fields are bit width dependent. The rest of them are after are // fixed width. Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias}; - Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); + if (Obj->getHeader()->e_machine == EM_MAXIS) { + Obj->getRelocationTypeName(R.getType(Obj->isMaxis64EL()), RelocName); + } else { + Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); + } Sym = unwrapOrError(Obj->getRelocationSymbol(&R, SymTab)); if (Sym && Sym->getType() == ELF::STT_SECTION) { const Elf_Shdr *Sec = unwrapOrError( @@ -2646,6 +3254,19 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { case SHT_X86_64_UNWIND: return "X86_64_UNWIND"; } + case EM_MAXIS: + case EM_MAXIS_RS3_LE: + switch (Type) { + case SHT_MAXIS_REGINFO: + return "MAXIS_REGINFO"; + case SHT_MAXIS_OPTIONS: + return "MAXIS_OPTIONS"; + case SHT_MAXIS_ABIFLAGS: + return "MAXIS_ABIFLAGS"; + case SHT_MAXIS_DWARF: + return "SHT_MAXIS_DWARF"; + } + } case EM_MIPS: case EM_MIPS_RS3_LE: switch (Type) { @@ -3159,9 +3780,18 @@ void GNUStyle::printDynamicRelocation(const ELFO *Obj, Elf_Rela R, // fixed width. Field Fields[5] = {0, 10 + Bias, 19 + 2 * Bias, 42 + 2 * Bias, 53 + 2 * Bias}; - uint32_t SymIndex = R.getSymbol(Obj->isMips64EL()); - const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; - Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); + uint32_t SymIndex; + if (Obj->getHeader()->e_machine == EM_MAXIS) { + SymIndex = R.getSymbol(Obj->isMaxis64EL()); + } else { + SymIndex = R.getSymbol(Obj->isMips64EL()); + } + const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; + if (Obj->getHeader()->e_machine == EM_MAXIS) { + Obj->getRelocationTypeName(R.getType(Obj->isMaxis64EL()), RelocName); + } else { + Obj->getRelocationTypeName(R.getType(Obj->isMips64EL()), RelocName); + } SymbolName = unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable())); std::string Addend, Info, Offset, Value; @@ -3558,6 +4188,119 @@ void GNUStyle::printNotes(const ELFFile *Obj) { } } +template +void GNUStyle::printMaxisGOT(const MaxisGOTParser &Parser) { + size_t Bias = ELFT::Is64Bits ? 8 : 0; + auto PrintEntry = [&](const Elf_Addr *E, StringRef Purpose) { + OS.PadToColumn(2); + OS << format_hex_no_prefix(Parser.getGotAddress(E), 8 + Bias); + OS.PadToColumn(11 + Bias); + OS << format_decimal(Parser.getGotOffset(E), 6) << "(gp)"; + OS.PadToColumn(22 + Bias); + OS << format_hex_no_prefix(*E, 8 + Bias); + OS.PadToColumn(31 + 2 * Bias); + OS << Purpose << "\n"; + }; + + OS << (Parser.IsStatic ? "Static GOT:\n" : "Primary GOT:\n"); + OS << " Canonical gp value: " + << format_hex_no_prefix(Parser.getGp(), 8 + Bias) << "\n\n"; + + OS << " Reserved entries:\n"; + OS << " Address Access Initial Purpose\n"; + PrintEntry(Parser.getGotLazyResolver(), "Lazy resolver"); + if (Parser.getGotModulePointer()) + PrintEntry(Parser.getGotModulePointer(), "Module pointer (GNU extension)"); + + if (!Parser.getLocalEntries().empty()) { + OS << "\n"; + OS << " Local entries:\n"; + OS << " Address Access Initial\n"; + for (auto &E : Parser.getLocalEntries()) + PrintEntry(&E, ""); + } + + if (Parser.IsStatic) + return; + + if (!Parser.getGlobalEntries().empty()) { + OS << "\n"; + OS << " Global entries:\n"; + OS << " Address Access Initial Sym.Val. Type Ndx Name\n"; + for (auto &E : Parser.getGlobalEntries()) { + const Elf_Sym *Sym = Parser.getGotSym(&E); + std::string SymName = this->dumper()->getFullSymbolName( + Sym, this->dumper()->getDynamicStringTable(), false); + + OS.PadToColumn(2); + OS << to_string(format_hex_no_prefix(Parser.getGotAddress(&E), 8 + Bias)); + OS.PadToColumn(11 + Bias); + OS << to_string(format_decimal(Parser.getGotOffset(&E), 6)) + "(gp)"; + OS.PadToColumn(22 + Bias); + OS << to_string(format_hex_no_prefix(E, 8 + Bias)); + OS.PadToColumn(31 + 2 * Bias); + OS << to_string(format_hex_no_prefix(Sym->st_value, 8 + Bias)); + OS.PadToColumn(40 + 3 * Bias); + OS << printEnum(Sym->getType(), makeArrayRef(ElfSymbolTypes)); + OS.PadToColumn(48 + 3 * Bias); + OS << getSymbolSectionNdx(Parser.Obj, Sym, + this->dumper()->dynamic_symbols().begin()); + OS.PadToColumn(52 + 3 * Bias); + OS << SymName << "\n"; + } + } + + if (!Parser.getOtherEntries().empty()) + OS << "\n Number of TLS and multi-GOT entries " + << Parser.getOtherEntries().size() << "\n"; +} + +template +void GNUStyle::printMaxisPLT(const MaxisGOTParser &Parser) { + size_t Bias = ELFT::Is64Bits ? 8 : 0; + auto PrintEntry = [&](const Elf_Addr *E, StringRef Purpose) { + OS.PadToColumn(2); + OS << format_hex_no_prefix(Parser.getGotAddress(E), 8 + Bias); + OS.PadToColumn(11 + Bias); + OS << format_hex_no_prefix(*E, 8 + Bias); + OS.PadToColumn(20 + 2 * Bias); + OS << Purpose << "\n"; + }; + + OS << "PLT GOT:\n\n"; + + OS << " Reserved entries:\n"; + OS << " Address Initial Purpose\n"; + PrintEntry(Parser.getPltLazyResolver(), "PLT lazy resolver"); + if (Parser.getPltModulePointer()) + PrintEntry(Parser.getGotModulePointer(), "Module pointer"); + + if (!Parser.getPltEntries().empty()) { + OS << "\n"; + OS << " Entries:\n"; + OS << " Address Initial Sym.Val. Type Ndx Name\n"; + for (auto &E : Parser.getPltEntries()) { + const Elf_Sym *Sym = Parser.getPltSym(&E); + std::string SymName = this->dumper()->getFullSymbolName( + Sym, this->dumper()->getDynamicStringTable(), false); + + OS.PadToColumn(2); + OS << to_string(format_hex_no_prefix(Parser.getGotAddress(&E), 8 + Bias)); + OS.PadToColumn(11 + Bias); + OS << to_string(format_hex_no_prefix(E, 8 + Bias)); + OS.PadToColumn(20 + 2 * Bias); + OS << to_string(format_hex_no_prefix(Sym->st_value, 8 + Bias)); + OS.PadToColumn(29 + 3 * Bias); + OS << printEnum(Sym->getType(), makeArrayRef(ElfSymbolTypes)); + OS.PadToColumn(37 + 3 * Bias); + OS << getSymbolSectionNdx(Parser.Obj, Sym, + this->dumper()->dynamic_symbols().begin()); + OS.PadToColumn(41 + 3 * Bias); + OS << SymName << "\n"; + } + } +} + template void GNUStyle::printMipsGOT(const MipsGOTParser &Parser) { size_t Bias = ELFT::Is64Bits ? 8 : 0; @@ -3709,7 +4452,11 @@ template void LLVMStyle::printFileHeaders(const ELFO *Obj) { W.printHex("Entry", e->e_entry); W.printHex("ProgramHeaderOffset", e->e_phoff); W.printHex("SectionHeaderOffset", e->e_shoff); - if (e->e_machine == EM_MIPS) + if (e->e_machine == EM_MAXIS) + W.printFlags("Flags", e->e_flags, makeArrayRef(ElfHeaderMaxisFlags), + unsigned(ELF::EF_MAXIS_ARCH), unsigned(ELF::EF_MAXIS_ABI), + unsigned(ELF::EF_MAXIS_MACH)); + else if (e->e_machine == EM_MIPS) W.printFlags("Flags", e->e_flags, makeArrayRef(ElfHeaderMipsFlags), unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI), unsigned(ELF::EF_MIPS_MACH)); @@ -3815,7 +4562,11 @@ template void LLVMStyle::printRelocation(const ELFO *Obj, Elf_Rela Rel, const Elf_Shdr *SymTab) { SmallString<32> RelocName; - Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); + if (Obj->getHeader()->e_machine == EM_MAXIS) { + Obj->getRelocationTypeName(Rel.getType(Obj->isMaxis64EL()), RelocName); + } else { + Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); + } StringRef TargetName; const Elf_Sym *Sym = unwrapOrError(Obj->getRelocationSymbol(&Rel, SymTab)); if (Sym && Sym->getType() == ELF::STT_SECTION) { @@ -3830,9 +4581,15 @@ void LLVMStyle::printRelocation(const ELFO *Obj, Elf_Rela Rel, if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); W.printHex("Offset", Rel.r_offset); - W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL())); - W.printNumber("Symbol", !TargetName.empty() ? TargetName : "-", - Rel.getSymbol(Obj->isMips64EL())); + if (Obj->getHeader()->e_machine == EM_MAXIS) { + W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMaxis64EL())); + W.printNumber("Symbol", !TargetName.empty() ? TargetName : "-", + Rel.getSymbol(Obj->isMaxis64EL())); + } else { + W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL())); + W.printNumber("Symbol", !TargetName.empty() ? TargetName : "-", + Rel.getSymbol(Obj->isMips64EL())); + } W.printHex("Addend", Rel.r_addend); } else { raw_ostream &OS = W.startLine(); @@ -3870,6 +4627,10 @@ template void LLVMStyle::printSections(const ELFO *Obj) { std::begin(ElfHexagonSectionFlags), std::end(ElfHexagonSectionFlags)); break; + case EM_MAXIS: + SectionFlags.insert(SectionFlags.end(), std::begin(ElfMaxisSectionFlags), + std::end(ElfMaxisSectionFlags)); + break; case EM_MIPS: SectionFlags.insert(SectionFlags.end(), std::begin(ElfMipsSectionFlags), std::end(ElfMipsSectionFlags)); @@ -3950,7 +4711,19 @@ void LLVMStyle::printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, else { std::vector> SymOtherFlags(std::begin(ElfSymOtherFlags), std::end(ElfSymOtherFlags)); - if (Obj->getHeader()->e_machine == EM_MIPS) { + if (Obj->getHeader()->e_machine == EM_MAXIS) { + // Someones in their infinite wisdom decided to make STO_MAXIS_MAXIS16 + // flag overlapped with other ST_MAXIS_xxx flags. So consider both + // cases separately. + if ((Symbol->st_other & STO_MAXIS_MAXIS16) == STO_MAXIS_MAXIS16) + SymOtherFlags.insert(SymOtherFlags.end(), + std::begin(ElfMaxis16SymOtherFlags), + std::end(ElfMaxis16SymOtherFlags)); + else + SymOtherFlags.insert(SymOtherFlags.end(), + std::begin(ElfMaxisSymOtherFlags), + std::end(ElfMaxisSymOtherFlags)); + } else if (Obj->getHeader()->e_machine == EM_MIPS) { // Someones in their infinite wisdom decided to make STO_MIPS_MIPS16 // flag overlapped with other ST_MIPS_xxx flags. So consider both // cases separately. @@ -4017,16 +4790,29 @@ void LLVMStyle::printDynamicRelocations(const ELFO *Obj) { template void LLVMStyle::printDynamicRelocation(const ELFO *Obj, Elf_Rela Rel) { SmallString<32> RelocName; - Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); + if (Obj->getHeader()->e_machine == EM_MAXIS) { + Obj->getRelocationTypeName(Rel.getType(Obj->isMaxis64EL()), RelocName); + } else { + Obj->getRelocationTypeName(Rel.getType(Obj->isMips64EL()), RelocName); + } StringRef SymbolName; - uint32_t SymIndex = Rel.getSymbol(Obj->isMips64EL()); + uint32_t SymIndex; + if (Obj->getHeader()->e_machine == EM_MAXIS) { + SymIndex = Rel.getSymbol(Obj->isMaxis64EL()); + } else { + SymIndex = Rel.getSymbol(Obj->isMips64EL()); + } const Elf_Sym *Sym = this->dumper()->dynamic_symbols().begin() + SymIndex; SymbolName = unwrapOrError(Sym->getName(this->dumper()->getDynamicStringTable())); if (opts::ExpandRelocs) { DictScope Group(W, "Relocation"); W.printHex("Offset", Rel.r_offset); - W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL())); + if (Obj->getHeader()->e_machine == EM_MAXIS) { + W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMaxis64EL())); + } else { + W.printNumber("Type", RelocName, (int)Rel.getType(Obj->isMips64EL())); + } W.printString("Symbol", !SymbolName.empty() ? SymbolName : "-"); W.printHex("Addend", Rel.r_addend); } else { @@ -4066,6 +4852,117 @@ void LLVMStyle::printNotes(const ELFFile *Obj) { W.startLine() << "printNotes not implemented!\n"; } +template +void LLVMStyle::printMaxisGOT(const MaxisGOTParser &Parser) { + auto PrintEntry = [&](const Elf_Addr *E) { + W.printHex("Address", Parser.getGotAddress(E)); + W.printNumber("Access", Parser.getGotOffset(E)); + W.printHex("Initial", *E); + }; + + DictScope GS(W, Parser.IsStatic ? "Static GOT" : "Primary GOT"); + + W.printHex("Canonical gp value", Parser.getGp()); + { + ListScope RS(W, "Reserved entries"); + { + DictScope D(W, "Entry"); + PrintEntry(Parser.getGotLazyResolver()); + W.printString("Purpose", StringRef("Lazy resolver")); + } + + if (Parser.getGotModulePointer()) { + DictScope D(W, "Entry"); + PrintEntry(Parser.getGotModulePointer()); + W.printString("Purpose", StringRef("Module pointer (GNU extension)")); + } + } + { + ListScope LS(W, "Local entries"); + for (auto &E : Parser.getLocalEntries()) { + DictScope D(W, "Entry"); + PrintEntry(&E); + } + } + + if (Parser.IsStatic) + return; + + { + ListScope GS(W, "Global entries"); + for (auto &E : Parser.getGlobalEntries()) { + DictScope D(W, "Entry"); + + PrintEntry(&E); + + const Elf_Sym *Sym = Parser.getGotSym(&E); + W.printHex("Value", Sym->st_value); + W.printEnum("Type", Sym->getType(), makeArrayRef(ElfSymbolTypes)); + + unsigned SectionIndex = 0; + StringRef SectionName; + this->dumper()->getSectionNameIndex( + Sym, this->dumper()->dynamic_symbols().begin(), SectionName, + SectionIndex); + W.printHex("Section", SectionName, SectionIndex); + + std::string SymName = this->dumper()->getFullSymbolName( + Sym, this->dumper()->getDynamicStringTable(), true); + W.printNumber("Name", SymName, Sym->st_name); + } + } + + W.printNumber("Number of TLS and multi-GOT entries", + uint64_t(Parser.getOtherEntries().size())); +} + +template +void LLVMStyle::printMaxisPLT(const MaxisGOTParser &Parser) { + auto PrintEntry = [&](const Elf_Addr *E) { + W.printHex("Address", Parser.getPltAddress(E)); + W.printHex("Initial", *E); + }; + + DictScope GS(W, "PLT GOT"); + + { + ListScope RS(W, "Reserved entries"); + { + DictScope D(W, "Entry"); + PrintEntry(Parser.getPltLazyResolver()); + W.printString("Purpose", StringRef("PLT lazy resolver")); + } + + if (auto E = Parser.getPltModulePointer()) { + DictScope D(W, "Entry"); + PrintEntry(E); + W.printString("Purpose", StringRef("Module pointer")); + } + } + { + ListScope LS(W, "Entries"); + for (auto &E : Parser.getPltEntries()) { + DictScope D(W, "Entry"); + PrintEntry(&E); + + const Elf_Sym *Sym = Parser.getPltSym(&E); + W.printHex("Value", Sym->st_value); + W.printEnum("Type", Sym->getType(), makeArrayRef(ElfSymbolTypes)); + + unsigned SectionIndex = 0; + StringRef SectionName; + this->dumper()->getSectionNameIndex( + Sym, this->dumper()->dynamic_symbols().begin(), SectionName, + SectionIndex); + W.printHex("Section", SectionName, SectionIndex); + + std::string SymName = + this->dumper()->getFullSymbolName(Sym, Parser.getPltStrTable(), true); + W.printNumber("Name", SymName, Sym->st_name); + } + } +} + template void LLVMStyle::printMipsGOT(const MipsGOTParser &Parser) { auto PrintEntry = [&](const Elf_Addr *E) { diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index c5b331d9..4c828499 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -52,6 +52,12 @@ class ObjDumper { // Only implemented for ARM ELF at this time. virtual void printAttributes() { } + // Only implemented for MAXIS ELF at this time. + virtual void printMaxisPLTGOT() { } + virtual void printMaxisABIFlags() { } + virtual void printMaxisReginfo() { } + virtual void printMaxisOptions() { } + // Only implemented for MIPS ELF at this time. virtual void printMipsPLTGOT() { } virtual void printMipsABIFlags() { } diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index c0765827..dd6c3eb4 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -179,6 +179,23 @@ namespace opts { cl::alias ARMAttributesShort("a", cl::desc("Alias for --arm-attributes"), cl::aliasopt(ARMAttributes)); + // -maxis-plt-got + cl::opt + MaxisPLTGOT("maxis-plt-got", + cl::desc("Display the MAXIS GOT and PLT GOT sections")); + + // -maxis-abi-flags + cl::opt MaxisABIFlags("maxis-abi-flags", + cl::desc("Display the MAXIS.abiflags section")); + + // -maxis-reginfo + cl::opt MaxisReginfo("maxis-reginfo", + cl::desc("Display the MAXIS .reginfo section")); + + // -maxis-options + cl::opt MaxisOptions("maxis-options", + cl::desc("Display the MAXIS .MAXIS.options section")); + // -mips-plt-got cl::opt MipsPLTGOT("mips-plt-got", @@ -332,6 +349,18 @@ static void reportError(StringRef Input, Error Err) { reportError(ErrMsg); } +static bool isMaxisArch(unsigned Arch) { + switch (Arch) { + case llvm::Triple::maxis: + case llvm::Triple::maxisel: + case llvm::Triple::maxis64: + case llvm::Triple::maxis64el: + return true; + default: + return false; + } +} + static bool isMipsArch(unsigned Arch) { switch (Arch) { case llvm::Triple::mips: @@ -343,6 +372,7 @@ static bool isMipsArch(unsigned Arch) { return false; } } + namespace { struct ReadObjTypeTableBuilder { ReadObjTypeTableBuilder() @@ -421,6 +451,16 @@ static void dumpObject(const ObjectFile *Obj) { if (Obj->getArch() == llvm::Triple::arm) if (opts::ARMAttributes) Dumper->printAttributes(); + if (isMaxisArch(Obj->getArch())) { + if (opts::MaxisPLTGOT) + Dumper->printMaxisPLTGOT(); + if (opts::MaxisABIFlags) + Dumper->printMaxisABIFlags(); + if (opts::MaxisReginfo) + Dumper->printMaxisReginfo(); + if (opts::MaxisOptions) + Dumper->printMaxisOptions(); + } if (isMipsArch(Obj->getArch())) { if (opts::MipsPLTGOT) Dumper->printMipsPLTGOT(); diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 840ddbab..053f4722 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -63,6 +63,7 @@ namespace opts { extern llvm::cl::opt CodeView; extern llvm::cl::opt CodeViewSubsectionBytes; extern llvm::cl::opt ARMAttributes; + extern llvm::cl::opt MaxisPLTGOT; extern llvm::cl::opt MipsPLTGOT; enum OutputStyleTy { LLVM, GNU }; extern llvm::cl::opt Output; diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp index f68bcf4e..f5900360 100644 --- a/tools/obj2yaml/elf2yaml.cpp +++ b/tools/obj2yaml/elf2yaml.cpp @@ -58,6 +58,7 @@ class ELFDumper { dumpContentSection(const Elf_Shdr *Shdr); ErrorOr dumpNoBitsSection(const Elf_Shdr *Shdr); ErrorOr dumpGroup(const Elf_Shdr *Shdr); + ErrorOr dumpMaxisABIFlags(const Elf_Shdr *Shdr); ErrorOr dumpMipsABIFlags(const Elf_Shdr *Shdr); public: @@ -168,6 +169,13 @@ template ErrorOr ELFDumper::dump() { Y->Sections.push_back(std::unique_ptr(G.get())); break; } + case ELF::SHT_MAXIS_ABIFLAGS: { + ErrorOr G = dumpMaxisABIFlags(&Sec); + if (std::error_code EC = G.getError()) + return EC; + Y->Sections.push_back(std::unique_ptr(G.get())); + break; + } case ELF::SHT_MIPS_ABIFLAGS: { ErrorOr G = dumpMipsABIFlags(&Sec); if (std::error_code EC = G.getError()) @@ -278,7 +286,11 @@ template std::error_code ELFDumper::dumpRelocation(const RelT *Rel, const Elf_Shdr *SymTab, ELFYAML::Relocation &R) { - R.Type = Rel->getType(Obj.isMips64EL()); + if (Obj.getHeader()->e_machine == EM_MAXIS) { + R.Type = Rel->getType(Obj.isMaxis64EL()); + } else { + R.Type = Rel->getType(Obj.isMips64EL()); + } R.Offset = Rel->r_offset; R.Addend = 0; @@ -486,6 +498,35 @@ ErrorOr ELFDumper::dumpGroup(const Elf_Shdr *Shdr) { return S.release(); } +template +ErrorOr +ELFDumper::dumpMaxisABIFlags(const Elf_Shdr *Shdr) { + assert(Shdr->sh_type == ELF::SHT_MAXIS_ABIFLAGS && + "Section type is not SHT_MAXIS_ABIFLAGS"); + auto S = make_unique(); + if (std::error_code EC = dumpCommonSection(Shdr, *S)) + return EC; + + auto ContentOrErr = Obj.getSectionContents(Shdr); + if (!ContentOrErr) + return errorToErrorCode(ContentOrErr.takeError()); + + auto *Flags = reinterpret_cast *>( + ContentOrErr.get().data()); + S->Version = Flags->version; + S->ISALevel = Flags->isa_level; + S->ISARevision = Flags->isa_rev; + S->GPRSize = Flags->gpr_size; + S->CPR1Size = Flags->cpr1_size; + S->CPR2Size = Flags->cpr2_size; + S->FpABI = Flags->fp_abi; + S->ISAExtension = Flags->isa_ext; + S->ASEs = Flags->ases; + S->Flags1 = Flags->flags1; + S->Flags2 = Flags->flags2; + return S.release(); +} + template ErrorOr ELFDumper::dumpMipsABIFlags(const Elf_Shdr *Shdr) { diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp index 21648469..b939ab0f 100644 --- a/tools/yaml2obj/yaml2elf.cpp +++ b/tools/yaml2obj/yaml2elf.cpp @@ -154,6 +154,9 @@ class ELFState { ContiguousBlobAccumulator &CBA); bool writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::Group &Group, ContiguousBlobAccumulator &CBA); + bool writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::MaxisABIFlags &Section, + ContiguousBlobAccumulator &CBA); bool writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::MipsABIFlags &Section, ContiguousBlobAccumulator &CBA); @@ -279,6 +282,9 @@ bool ELFState::initSectionHeaders(std::vector &SHeaders, SHeader.sh_info = SymIdx; if (!writeSectionContent(SHeader, *S, CBA)) return false; + } else if (auto S = dyn_cast(Sec.get())) { + if (!writeSectionContent(SHeader, *S, CBA)) + return false; } else if (auto S = dyn_cast(Sec.get())) { if (!writeSectionContent(SHeader, *S, CBA)) return false; @@ -462,6 +468,12 @@ ELFState::writeSectionContent(Elf_Shdr &SHeader, SHeader.sh_size = Section.Size; } +static bool isMaxis64EL(const ELFYAML::Object &Doc) { + return Doc.Header.Machine == ELFYAML::ELF_EM(llvm::ELF::EM_MAXIS) && + Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64) && + Doc.Header.Data == ELFYAML::ELF_ELFDATA(ELF::ELFDATA2LSB); +} + static bool isMips64EL(const ELFYAML::Object &Doc) { return Doc.Header.Machine == ELFYAML::ELF_EM(llvm::ELF::EM_MIPS) && Doc.Header.Class == ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64) && @@ -496,13 +508,21 @@ ELFState::writeSectionContent(Elf_Shdr &SHeader, zero(REntry); REntry.r_offset = Rel.Offset; REntry.r_addend = Rel.Addend; - REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); + if (Doc.Header.Machine == ELFYAML::ELF_EM(llvm::ELF::EM_MAXIS)) { + REntry.setSymbolAndType(SymIdx, Rel.Type, isMaxis64EL(Doc)); + } else { + REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); + } OS.write((const char *)&REntry, sizeof(REntry)); } else { Elf_Rel REntry; zero(REntry); REntry.r_offset = Rel.Offset; - REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); + if (Doc.Header.Machine == ELFYAML::ELF_EM(llvm::ELF::EM_MAXIS)) { + REntry.setSymbolAndType(SymIdx, Rel.Type, isMaxis64EL(Doc)); + } else { + REntry.setSymbolAndType(SymIdx, Rel.Type, isMips64EL(Doc)); + } OS.write((const char *)&REntry, sizeof(REntry)); } } @@ -539,6 +559,35 @@ bool ELFState::writeSectionContent(Elf_Shdr &SHeader, return true; } +template +bool ELFState::writeSectionContent(Elf_Shdr &SHeader, + const ELFYAML::MaxisABIFlags &Section, + ContiguousBlobAccumulator &CBA) { + assert(Section.Type == llvm::ELF::SHT_MAXIS_ABIFLAGS && + "Section type is not SHT_MAXIS_ABIFLAGS"); + + object::Elf_Maxis_ABIFlags Flags; + zero(Flags); + SHeader.sh_entsize = sizeof(Flags); + SHeader.sh_size = SHeader.sh_entsize; + + auto &OS = CBA.getOSAndAlignedOffset(SHeader.sh_offset, SHeader.sh_addralign); + Flags.version = Section.Version; + Flags.isa_level = Section.ISALevel; + Flags.isa_rev = Section.ISARevision; + Flags.gpr_size = Section.GPRSize; + Flags.cpr1_size = Section.CPR1Size; + Flags.cpr2_size = Section.CPR2Size; + Flags.fp_abi = Section.FpABI; + Flags.isa_ext = Section.ISAExtension; + Flags.ases = Section.ASEs; + Flags.flags1 = Section.Flags1; + Flags.flags2 = Section.Flags2; + OS.write((const char *)&Flags, sizeof(Flags)); + + return true; +} + template bool ELFState::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::MipsABIFlags &Section, diff --git a/utils/TableGen/AsmMatcherEmitter.cpp b/utils/TableGen/AsmMatcherEmitter.cpp index f2d304bf..c71a6650 100644 --- a/utils/TableGen/AsmMatcherEmitter.cpp +++ b/utils/TableGen/AsmMatcherEmitter.cpp @@ -3017,7 +3017,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { Info.SubtargetFeatures, OS); // Emit the function to match a register name to number. - // This should be omitted for Mips target + // This should be omitted for Maxis/Mips target if (AsmParser->getValueAsBit("ShouldEmitMatchRegisterName")) emitMatchRegisterName(Target, AsmParser, OS); diff --git a/utils/TableGen/CodeGenRegisters.cpp b/utils/TableGen/CodeGenRegisters.cpp index a6b0a4be..a4a81a97 100644 --- a/utils/TableGen/CodeGenRegisters.cpp +++ b/utils/TableGen/CodeGenRegisters.cpp @@ -1100,7 +1100,7 @@ CodeGenRegBank::CodeGenRegBank(RecordKeeper &Records, // Compute register name map. for (auto &Reg : Registers) // FIXME: This could just be RegistersByName[name] = register, except that - // causes some failures in MIPS - perhaps they have duplicate register name + // causes some failures in MAXIS/MIPS - perhaps they have duplicate register name // entries? (or maybe there's a reason for it - I don't know much about this // code, just drive-by refactoring) RegistersByName.insert( diff --git a/utils/TableGen/GlobalISelEmitter.cpp b/utils/TableGen/GlobalISelEmitter.cpp index c7d662db..6ce4bd19 100644 --- a/utils/TableGen/GlobalISelEmitter.cpp +++ b/utils/TableGen/GlobalISelEmitter.cpp @@ -3013,7 +3013,7 @@ Expected GlobalISelEmitter::importExplicitUseRenderer( // Similarly, imm is an operator in TreePatternNode's view but must be // rendered as operands. // FIXME: The target should be able to choose sign-extended when appropriate - // (e.g. on Mips). + // (e.g. on Maxis/Mips). if (DstChild->getOperator()->getName() == "imm") { DstMIBuilder.addRenderer(DstChild->getName()); return InsertPt;