diff --git a/README.md b/README.md index a54dd151970c..1273ba17c2fa 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,122 @@ -# Capstone's LLVM with refactored TableGen backends +# The LLVM Compiler Infrastructure -This LLVM version has the purpose to generate code for the -[Capstone disassembler](https://github.com/capstone-engine/capstone). +This directory and its sub-directories contain the source code for LLVM, +a toolkit for the construction of highly optimized compilers, +optimizers, and run-time environments. -It refactors the TableGen emitter backends, so they can emit C code -in addition to the C++ code they normally emit. +The README briefly describes how to get started with building LLVM. +For more information on how to contribute to the LLVM project, please +take a look at the +[Contributing to LLVM](https://llvm.org/docs/Contributing.html) guide. -Please note that within LLVM we speak of a `Target` if we refer to an architecture. +## Getting Started with the LLVM System -## Code generation +Taken from [here](https://llvm.org/docs/GettingStarted.html). -### Relevant files +### Overview -The TableGen emitter backends are located in `llvm/utils/TableGen/`. +Welcome to the LLVM project! -The target definition files (`.td`) define the -instructions, operands, features and other things. This is the source of all our information. -If something is wrongly defined there, it will be wrong in the generated files. -You can find the `td` files in `llvm/lib/Target//`. +The LLVM project has multiple components. The core of the project is +itself called "LLVM". This contains all of the tools, libraries, and header +files needed to process intermediate representations and convert them into +object files. Tools include an assembler, disassembler, bitcode analyzer, and +bitcode optimizer. It also contains basic regression tests. -### Code generation overview +C-like languages use the [Clang](http://clang.llvm.org/) frontend. This +component compiles C, C++, Objective-C, and Objective-C++ code into LLVM bitcode +-- and from there into object files, using LLVM. -Generating code for a target has 6 steps: +Other components include: +the [libc++ C++ standard library](https://libcxx.llvm.org), +the [LLD linker](https://lld.llvm.org), and more. -``` - 5 6 - ┌──────────┐ ┌──────────┐ - │Printer │ │CS .inc │ - 1 2 3 4 ┌──►│Capstone ├─────►│files │ -┌───────┐ ┌───────────┐ ┌───────────┐ ┌──────────┐ │ └──────────┘ └──────────┘ -│ .td │ │ │ │ │ │ Code- │ │ -│ files ├────►│ TableGen ├────►│ CodeGen ├────►│ Emitter │◄─┤ -└───────┘ └──────┬────┘ └───────────┘ └──────────┘ │ - │ ▲ │ ┌──────────┐ ┌──────────┐ - └─────────────────────────────────┘ └──►│Printer ├─────►│LLVM .inc │ - │LLVM │ │files │ - └──────────┘ └──────────┘ -``` +### Getting the Source Code and Building LLVM -1. LLVM targets are defined in `.td` files. They describe instructions, operands, -features and other properties. +The LLVM Getting Started documentation may be out of date. The [Clang +Getting Started](http://clang.llvm.org/get_started.html) page might have more +accurate information. -2. [LLVM TableGen](https://llvm.org/docs/TableGen/index.html) parses these files -and converts them to an internal representation of [Classes, Records, DAGs](https://llvm.org/docs/TableGen/ProgRef.html) - and other types. +This is an example work-flow and configuration to get and build the LLVM source: -3. In the second step a TableGen component called [CodeGen](https://llvm.org/docs/CodeGenerator.html) -abstracts this even further. -The result is a representation which is _not_ specific to any target -(e.g. the `CodeGenInstruction` class can represent a machine instruction of any target). +1. Checkout LLVM (including related sub-projects like Clang): -4. Different code emitter backends use the result of the former two components to -generated code. + * ``git clone https://github.com/llvm/llvm-project.git`` -5. Whenever the emitter emits code it calls a `Printer`. Either the `PrinterCapstone` to emit C or `PrinterLLVM` to emit C++. -Which one is controlled by the `--printerLang=[CCS,C++]` option passed to `llvm-tblgen`. + * Or, on windows, ``git clone --config core.autocrlf=false + https://github.com/llvm/llvm-project.git`` -6. After the emitter backend is done, the `Printer` writes the `output_stream` content into the `.inc` files. +2. Configure and build LLVM and Clang: -### Emitter backends and their use cases + * ``cd llvm-project`` -We use the following emitter backends + * ``cmake -S llvm -B build -G [options]`` -| Name | Generated Code | Note | -|------|----------------|------| -| AsmMatcherEmitter | Mapping tables for Capstone | | -| AsmWriterEmitter | State machine to decode the asm-string for a `MCInst` | | -| DecoderEmitter | State machine which decodes bytes to a `MCInst`. | | -| InstrInfoEmitter | Tables with instruction information (instruction enum, instr. operand information...) | | -| RegisterInfoEmitter | Tables with register information (register enum, register type info...) | | -| SubtargetEmitter | Table about the target features. | | -| SearchableTablesEmitter | Usually used to generate tables and decoding functions for system operands. | **1.** Not all targets use this. | + Some common build system generators are: -## Developer notes + * ``Ninja`` --- for generating [Ninja](https://ninja-build.org) + build files. Most llvm developers use Ninja. + * ``Unix Makefiles`` --- for generating make-compatible parallel makefiles. + * ``Visual Studio`` --- for generating Visual Studio projects and + solutions. + * ``Xcode`` --- for generating Xcode projects. -- If you find C++ code within the generated files you need to extend `PrinterCapstone::translateToC()`. -If this still doesn't fix the problem, the code snipped wasn't passed through `translateToC()` before emitting. -So you need to figure out where this specific code snipped is printed and pass it to `translateToC()`. + Some common options: -- If the mapping files miss operand types or access information, then the `.td` files are incomplete (happens surprisingly often). -You need to search for the instruction or operands with missing or incorrect values and fix them. - ``` - Wrong access attributes for: - - Registers, Immediates: The instructions defines "out" and "in" operands incorrectly. - - Memory: The "mayLoad" or "mayStore" variable is not set for the instruction. + * ``-DLLVM_ENABLE_PROJECTS='...'`` and ``-DLLVM_ENABLE_RUNTIMES='...'`` --- + semicolon-separated list of the LLVM sub-projects and runtimes you'd like to + additionally build. ``LLVM_ENABLE_PROJECTS`` can include any of: clang, + clang-tools-extra, cross-project-tests, flang, libc, libclc, lld, lldb, + mlir, openmp, polly, or pstl. ``LLVM_ENABLE_RUNTIMES`` can include any of + libcxx, libcxxabi, libunwind, compiler-rt, libc or openmp. Some runtime + projects can be specified either in ``LLVM_ENABLE_PROJECTS`` or in + ``LLVM_ENABLE_RUNTIMES``. - Operand type is invalid: - - The "OperandType" variable is unset for this operand. - ``` + For example, to build LLVM, Clang, libcxx, and libcxxabi, use + ``-DLLVM_ENABLE_PROJECTS="clang" -DLLVM_ENABLE_RUNTIMES="libcxx;libcxxabi"``. -- If certain target features (e.g. architecture extensions) were removed from upstream LLVM or you want to add your own, -checkout [DeprecatedFeatures.md](DeprecatedFeatures.md). \ No newline at end of file + * ``-DCMAKE_INSTALL_PREFIX=directory`` --- Specify for *directory* the full + path name of where you want the LLVM tools and libraries to be installed + (default ``/usr/local``). Be careful if you install runtime libraries: if + your system uses those provided by LLVM (like libc++ or libc++abi), you + must not overwrite your system's copy of those libraries, since that + could render your system unusable. In general, using something like + ``/usr`` is not advised, but ``/usr/local`` is fine. + + * ``-DCMAKE_BUILD_TYPE=type`` --- Valid options for *type* are Debug, + Release, RelWithDebInfo, and MinSizeRel. Default is Debug. + + * ``-DLLVM_ENABLE_ASSERTIONS=On`` --- Compile with assertion checks enabled + (default is Yes for Debug builds, No for all other build types). + + * ``cmake --build build [-- [options] ]`` or your build system specified above + directly. + + * The default target (i.e. ``ninja`` or ``make``) will build all of LLVM. + + * The ``check-all`` target (i.e. ``ninja check-all``) will run the + regression tests to ensure everything is in working order. + + * CMake will generate targets for each tool and library, and most + LLVM sub-projects generate their own ``check-`` target. + + * Running a serial build will be **slow**. To improve speed, try running a + parallel build. That's done by default in Ninja; for ``make``, use the option + ``-j NNN``, where ``NNN`` is the number of parallel jobs to run. + In most cases, you get the best performance if you specify the number of CPU threads you have. + On some Unix systems, you can specify this with ``-j$(nproc)``. + + * For more information see [CMake](https://llvm.org/docs/CMake.html). + +Consult the +[Getting Started with LLVM](https://llvm.org/docs/GettingStarted.html#getting-started-with-llvm) +page for detailed information on configuring and compiling LLVM. You can visit +[Directory Layout](https://llvm.org/docs/GettingStarted.html#directory-layout) +to learn about the layout of the source code tree. + +## Getting in touch + +Join [LLVM Discourse forums](https://discourse.llvm.org/), [discord chat](https://discord.gg/xS7Z362) or #llvm IRC channel on [OFTC](https://oftc.net/). + +The LLVM project has adopted a [code of conduct](https://llvm.org/docs/CodeOfConduct.html) for +participants to all modes of communication within the project. diff --git a/llvm/CMakeLists.txt b/llvm/CMakeLists.txt index 8e9bc28001c5..2b9315c22ed9 100644 --- a/llvm/CMakeLists.txt +++ b/llvm/CMakeLists.txt @@ -22,7 +22,7 @@ if(NOT DEFINED LLVM_VERSION_MINOR) set(LLVM_VERSION_MINOR 0) endif() if(NOT DEFINED LLVM_VERSION_PATCH) - set(LLVM_VERSION_PATCH 4) + set(LLVM_VERSION_PATCH 6) endif() if(NOT DEFINED LLVM_VERSION_SUFFIX) set(LLVM_VERSION_SUFFIX) diff --git a/llvm/cmake/modules/AddLLVM.cmake b/llvm/cmake/modules/AddLLVM.cmake index 94fc83db9344..93e6d67551de 100644 --- a/llvm/cmake/modules/AddLLVM.cmake +++ b/llvm/cmake/modules/AddLLVM.cmake @@ -2320,7 +2320,8 @@ function(llvm_setup_rpath name) # FIXME: update this when there is better solution. set(_install_rpath "${LLVM_LIBRARY_OUTPUT_INTDIR}" "${CMAKE_INSTALL_PREFIX}/lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) elseif(UNIX) - set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) + set(_build_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}" ${extra_libdir}) + set(_install_rpath "\$ORIGIN/../lib${LLVM_LIBDIR_SUFFIX}") if(${CMAKE_SYSTEM_NAME} MATCHES "(FreeBSD|DragonFly)") set_property(TARGET ${name} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-z,origin ") @@ -2334,9 +2335,16 @@ function(llvm_setup_rpath name) return() endif() - # Enable BUILD_WITH_INSTALL_RPATH unless CMAKE_BUILD_RPATH is set. + # Enable BUILD_WITH_INSTALL_RPATH unless CMAKE_BUILD_RPATH is set and not + # building for macOS or AIX, as those platforms seemingly require it. + # On AIX, the tool chain doesn't support modifying rpaths/libpaths for XCOFF + # on install at the moment, so BUILD_WITH_INSTALL_RPATH is required. if("${CMAKE_BUILD_RPATH}" STREQUAL "") - set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH ON) + if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin|AIX") + set_property(TARGET ${name} PROPERTY BUILD_WITH_INSTALL_RPATH ON) + else() + set_property(TARGET ${name} APPEND PROPERTY BUILD_RPATH "${_build_rpath}") + endif() endif() set_target_properties(${name} PROPERTIES diff --git a/llvm/cmake/modules/LLVM-Config.cmake b/llvm/cmake/modules/LLVM-Config.cmake index 37eab12e7460..ae082c6ae520 100644 --- a/llvm/cmake/modules/LLVM-Config.cmake +++ b/llvm/cmake/modules/LLVM-Config.cmake @@ -1,3 +1,6 @@ +cmake_policy(PUSH) +cmake_policy(SET CMP0057 NEW) + function(get_system_libs return_var) message(AUTHOR_WARNING "get_system_libs no longer needed") set(${return_var} "" PARENT_SCOPE) @@ -343,3 +346,5 @@ function(explicit_map_components_to_libraries out_libs) endforeach(c) set(${out_libs} ${result} PARENT_SCOPE) endfunction(explicit_map_components_to_libraries) + +cmake_policy(POP) diff --git a/llvm/include/llvm/Analysis/AliasAnalysis.h b/llvm/include/llvm/Analysis/AliasAnalysis.h index 953e15e358f1..8ac6e7dac63e 100644 --- a/llvm/include/llvm/Analysis/AliasAnalysis.h +++ b/llvm/include/llvm/Analysis/AliasAnalysis.h @@ -116,6 +116,15 @@ class AliasResult { operator Kind() const { return static_cast(Alias); } + bool operator==(const AliasResult &Other) const { + return Alias == Other.Alias && HasOffset == Other.HasOffset && + Offset == Other.Offset; + } + bool operator!=(const AliasResult &Other) const { return !(*this == Other); } + + bool operator==(Kind K) const { return Alias == K; } + bool operator!=(Kind K) const { return !(*this == K); } + constexpr bool hasOffset() const { return HasOffset; } constexpr int32_t getOffset() const { assert(HasOffset && "No offset!"); diff --git a/llvm/include/llvm/Analysis/TargetLibraryInfo.h b/llvm/include/llvm/Analysis/TargetLibraryInfo.h index 8fcfbdbd6665..951945f7b765 100644 --- a/llvm/include/llvm/Analysis/TargetLibraryInfo.h +++ b/llvm/include/llvm/Analysis/TargetLibraryInfo.h @@ -408,14 +408,14 @@ class TargetLibraryInfo { ShouldExtI32Param = true; ShouldExtI32Return = true; } - // Mips and riscv64, on the other hand, needs signext on i32 parameters - // corresponding to both signed and unsigned ints. - if (T.isMIPS() || T.isRISCV64()) { + // LoongArch, Mips, and riscv64, on the other hand, need signext on i32 + // parameters corresponding to both signed and unsigned ints. + if (T.isLoongArch() || T.isMIPS() || T.isRISCV64()) { ShouldSignExtI32Param = true; } - // riscv64 needs signext on i32 returns corresponding to both signed and - // unsigned ints. - if (T.isRISCV64()) { + // LoongArch and riscv64 need signext on i32 returns corresponding to both + // signed and unsigned ints. + if (T.isLoongArch() || T.isRISCV64()) { ShouldSignExtI32Return = true; } } diff --git a/llvm/include/llvm/Support/Compiler.h b/llvm/include/llvm/Support/Compiler.h index 21d6493c746a..cf330662cf4b 100644 --- a/llvm/include/llvm/Support/Compiler.h +++ b/llvm/include/llvm/Support/Compiler.h @@ -139,7 +139,7 @@ #define LLVM_ATTRIBUTE_USED #endif -#if defined(__clang__) && !defined(__INTELLISENSE__) +#if defined(__clang__) #define LLVM_DEPRECATED(MSG, FIX) __attribute__((deprecated(MSG, FIX))) #else #define LLVM_DEPRECATED(MSG, FIX) [[deprecated(MSG)]] diff --git a/llvm/include/llvm/TableGen/StringMatcher.h b/llvm/include/llvm/TableGen/StringMatcher.h index a83e3e9ad731..795b7a6d41dc 100644 --- a/llvm/include/llvm/TableGen/StringMatcher.h +++ b/llvm/include/llvm/TableGen/StringMatcher.h @@ -13,7 +13,6 @@ #ifndef LLVM_TABLEGEN_STRINGMATCHER_H #define LLVM_TABLEGEN_STRINGMATCHER_H -#include "PrinterTypes.h" #include "llvm/ADT/StringRef.h" #include #include @@ -36,26 +35,18 @@ class StringMatcher { StringRef StrVariableName; const std::vector &Matches; raw_ostream &OS; - PrinterLanguage PL; public: StringMatcher(StringRef strVariableName, const std::vector &matches, raw_ostream &os) - : StrVariableName(strVariableName), Matches(matches), OS(os), PL(PRINTER_LANG_CPP) {} - StringMatcher(StringRef strVariableName, - const std::vector &matches, raw_ostream &os, PrinterLanguage PL) - : StrVariableName(strVariableName), Matches(matches), OS(os), PL(PL) {} + : StrVariableName(strVariableName), Matches(matches), OS(os) {} void Emit(unsigned Indent = 0, bool IgnoreDuplicates = false) const; - void EmitCPP(unsigned Indent = 0, bool IgnoreDuplicates = false) const; private: bool EmitStringMatcherForChar(const std::vector &Matches, unsigned CharNo, unsigned IndentCount, bool IgnoreDuplicates) const; - bool EmitStringMatcherForCharCPP(const std::vector &Matches, - unsigned CharNo, unsigned IndentCount, - bool IgnoreDuplicates) const; }; } // end namespace llvm diff --git a/llvm/include/llvm/TableGen/StringToOffsetTable.h b/llvm/include/llvm/TableGen/StringToOffsetTable.h index 9ac496fb41fc..7fcf20abed61 100644 --- a/llvm/include/llvm/TableGen/StringToOffsetTable.h +++ b/llvm/include/llvm/TableGen/StringToOffsetTable.h @@ -9,12 +9,10 @@ #ifndef LLVM_TABLEGEN_STRINGTOOFFSETTABLE_H #define LLVM_TABLEGEN_STRINGTOOFFSETTABLE_H -#include "PrinterTypes.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/TableGen/Error.h" #include namespace llvm { @@ -24,14 +22,10 @@ namespace llvm { /// It can then output this string blob and use indexes into the string to /// reference each piece. class StringToOffsetTable { - PrinterLanguage PL; StringMap StringOffset; std::string AggregateString; public: - StringToOffsetTable() : PL(PRINTER_LANG_CPP) {}; - StringToOffsetTable(PrinterLanguage PL) : PL(PL) {}; - bool Empty() const { return StringOffset.empty(); } unsigned GetOrAddStringOffset(StringRef Str, bool appendZero = true) { @@ -48,16 +42,6 @@ class StringToOffsetTable { } void EmitString(raw_ostream &O) { - switch(PL) { - default: - PrintFatalNote("No StringToOffsetTable method defined to emit the selected language.\n"); - case PRINTER_LANG_CPP: - EmitStringCPP(O); - break; - } - } - - void EmitStringCPP(raw_ostream &O) { // Escape the string. SmallString<256> Str; raw_svector_ostream(Str).write_escaped(AggregateString); diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp index 8d4c8802f71c..3de4efb5ba22 100644 --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -1724,12 +1724,9 @@ bool TargetLowering::SimplifyDemandedBits( unsigned InnerBits = InnerVT.getScalarSizeInBits(); if (ShAmt < InnerBits && DemandedBits.getActiveBits() <= InnerBits && isTypeDesirableForOp(ISD::SHL, InnerVT)) { - EVT ShTy = getShiftAmountTy(InnerVT, DL); - if (!APInt(BitWidth, ShAmt).isIntN(ShTy.getSizeInBits())) - ShTy = InnerVT; - SDValue NarrowShl = - TLO.DAG.getNode(ISD::SHL, dl, InnerVT, InnerOp, - TLO.DAG.getConstant(ShAmt, dl, ShTy)); + SDValue NarrowShl = TLO.DAG.getNode( + ISD::SHL, dl, InnerVT, InnerOp, + TLO.DAG.getShiftAmountConstant(ShAmt, InnerVT, dl)); return TLO.CombineTo( Op, TLO.DAG.getNode(ISD::ANY_EXTEND, dl, VT, NarrowShl)); } diff --git a/llvm/lib/Support/BLAKE3/CMakeLists.txt b/llvm/lib/Support/BLAKE3/CMakeLists.txt index ae75c9ce9a4f..85fe4f6f4206 100644 --- a/llvm/lib/Support/BLAKE3/CMakeLists.txt +++ b/llvm/lib/Support/BLAKE3/CMakeLists.txt @@ -10,7 +10,6 @@ if (LLVM_DISABLE_ASSEMBLY_FILES) else() set(CAN_USE_ASSEMBLER TRUE) endif() -set(CAN_USE_ASSEMBLER FALSE) macro(disable_blake3_x86_simd) add_compile_definitions(BLAKE3_NO_AVX512 BLAKE3_NO_AVX2 BLAKE3_NO_SSE41 BLAKE3_NO_SSE2) diff --git a/llvm/lib/TableGen/CMakeLists.txt b/llvm/lib/TableGen/CMakeLists.txt index 1db225125bd5..c550840f147d 100644 --- a/llvm/lib/TableGen/CMakeLists.txt +++ b/llvm/lib/TableGen/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMTableGen Parser.cpp Record.cpp SetTheory.cpp + StringMatcher.cpp TableGenBackend.cpp TableGenBackendSkeleton.cpp TGLexer.cpp diff --git a/llvm/lib/TableGen/StringMatcher.cpp b/llvm/lib/TableGen/StringMatcher.cpp new file mode 100644 index 000000000000..c169b4e0a362 --- /dev/null +++ b/llvm/lib/TableGen/StringMatcher.cpp @@ -0,0 +1,155 @@ +//===- StringMatcher.cpp - Generate a matcher for input strings -----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the StringMatcher class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include +#include +#include +#include +#include + +using namespace llvm; + +/// FindFirstNonCommonLetter - Find the first character in the keys of the +/// string pairs that is not shared across the whole set of strings. All +/// strings are assumed to have the same length. +static unsigned +FindFirstNonCommonLetter(const std::vector &Matches) { + assert(!Matches.empty()); + for (unsigned i = 0, e = Matches[0]->first.size(); i != e; ++i) { + // Check to see if letter i is the same across the set. + char Letter = Matches[0]->first[i]; + + for (const StringMatcher::StringPair *Match : Matches) + if (Match->first[i] != Letter) + return i; + } + + return Matches[0]->first.size(); +} + +/// EmitStringMatcherForChar - Given a set of strings that are known to be the +/// same length and whose characters leading up to CharNo are the same, emit +/// code to verify that CharNo and later are the same. +/// +/// \return - True if control can leave the emitted code fragment. +bool StringMatcher::EmitStringMatcherForChar( + const std::vector &Matches, unsigned CharNo, + unsigned IndentCount, bool IgnoreDuplicates) const { + assert(!Matches.empty() && "Must have at least one string to match!"); + std::string Indent(IndentCount * 2 + 4, ' '); + + // If we have verified that the entire string matches, we're done: output the + // matching code. + if (CharNo == Matches[0]->first.size()) { + if (Matches.size() > 1 && !IgnoreDuplicates) + report_fatal_error("Had duplicate keys to match on"); + + // If the to-execute code has \n's in it, indent each subsequent line. + StringRef Code = Matches[0]->second; + + std::pair Split = Code.split('\n'); + OS << Indent << Split.first << "\t // \"" << Matches[0]->first << "\"\n"; + + Code = Split.second; + while (!Code.empty()) { + Split = Code.split('\n'); + OS << Indent << Split.first << "\n"; + Code = Split.second; + } + return false; + } + + // Bucket the matches by the character we are comparing. + std::map> MatchesByLetter; + + for (const StringPair *Match : Matches) + MatchesByLetter[Match->first[CharNo]].push_back(Match); + + // If we have exactly one bucket to match, see how many characters are common + // across the whole set and match all of them at once. + if (MatchesByLetter.size() == 1) { + unsigned FirstNonCommonLetter = FindFirstNonCommonLetter(Matches); + unsigned NumChars = FirstNonCommonLetter-CharNo; + + // Emit code to break out if the prefix doesn't match. + if (NumChars == 1) { + // Do the comparison with if (Str[1] != 'f') + // FIXME: Need to escape general characters. + OS << Indent << "if (" << StrVariableName << "[" << CharNo << "] != '" + << Matches[0]->first[CharNo] << "')\n"; + OS << Indent << " break;\n"; + } else { + // Do the comparison with if memcmp(Str.data()+1, "foo", 3). + // FIXME: Need to escape general strings. + OS << Indent << "if (memcmp(" << StrVariableName << ".data()+" << CharNo + << ", \"" << Matches[0]->first.substr(CharNo, NumChars) << "\", " + << NumChars << ") != 0)\n"; + OS << Indent << " break;\n"; + } + + return EmitStringMatcherForChar(Matches, FirstNonCommonLetter, IndentCount, + IgnoreDuplicates); + } + + // Otherwise, we have multiple possible things, emit a switch on the + // character. + OS << Indent << "switch (" << StrVariableName << "[" << CharNo << "]) {\n"; + OS << Indent << "default: break;\n"; + + for (const auto &LI : MatchesByLetter) { + // TODO: escape hard stuff (like \n) if we ever care about it. + OS << Indent << "case '" << LI.first << "':\t // " << LI.second.size() + << " string"; + if (LI.second.size() != 1) + OS << 's'; + OS << " to match.\n"; + if (EmitStringMatcherForChar(LI.second, CharNo + 1, IndentCount + 1, + IgnoreDuplicates)) + OS << Indent << " break;\n"; + } + + OS << Indent << "}\n"; + return true; +} + +/// Emit - Top level entry point. +/// +void StringMatcher::Emit(unsigned Indent, bool IgnoreDuplicates) const { + // If nothing to match, just fall through. + if (Matches.empty()) return; + + // First level categorization: group strings by length. + std::map> MatchesByLength; + + for (const StringPair &Match : Matches) + MatchesByLength[Match.first.size()].push_back(&Match); + + // Output a switch statement on length and categorize the elements within each + // bin. + OS.indent(Indent*2+2) << "switch (" << StrVariableName << ".size()) {\n"; + OS.indent(Indent*2+2) << "default: break;\n"; + + for (const auto &LI : MatchesByLength) { + OS.indent(Indent * 2 + 2) + << "case " << LI.first << ":\t // " << LI.second.size() << " string" + << (LI.second.size() == 1 ? "" : "s") << " to match.\n"; + if (EmitStringMatcherForChar(LI.second, 0, Indent, IgnoreDuplicates)) + OS.indent(Indent*2+4) << "break;\n"; + } + + OS.indent(Indent*2+2) << "}\n"; +} diff --git a/llvm/lib/Target/ARM/ARMInstrFormats.td b/llvm/lib/Target/ARM/ARMInstrFormats.td index 781cab78eba4..14e315534570 100644 --- a/llvm/lib/Target/ARM/ARMInstrFormats.td +++ b/llvm/lib/Target/ARM/ARMInstrFormats.td @@ -669,7 +669,6 @@ class AIldr_ex_or_acq opcod, bits<2> opcod2, dag oops, dag iops, InstrIt let Inst{11-10} = 0b11; let Inst{9-8} = opcod2; let Inst{7-0} = 0b10011111; - let mayLoad = 1; } class AIstr_ex_or_rel opcod, bits<2> opcod2, dag oops, dag iops, InstrItinClass itin, string opc, string asm, list pattern> @@ -685,7 +684,6 @@ class AIstr_ex_or_rel opcod, bits<2> opcod2, dag oops, dag iops, InstrIt let Inst{9-8} = opcod2; let Inst{7-4} = 0b1001; let Inst{3-0} = Rt; - let mayStore = 1; } // Atomic load/store instructions class AIldrex opcod, dag oops, dag iops, InstrItinClass itin, @@ -697,7 +695,6 @@ class AIstrex opcod, dag oops, dag iops, InstrItinClass itin, : AIstr_ex_or_rel { bits<4> Rd; let Inst{15-12} = Rd; - let mayLoad = 1; } // Exclusive load/store instructions @@ -795,8 +792,6 @@ class AI2ldstidx op, bit op20, dag oops, dag iops, Format f, let Inst{11-8} = addr{7-4}; // imm7_4/zero let Inst{7-4} = op; let Inst{3-0} = addr{3-0}; // imm3_0/Rm - let mayLoad = op20; - let mayStore = 0; let DecoderMethod = "DecodeAddrMode3Instruction"; } @@ -891,8 +881,6 @@ class AI3ldstidx op, bit op20, bit isPre, dag oops, dag iops, let Inst{20} = op20; // L bit let Inst{15-12} = Rt; // Rt let Inst{7-4} = op; - let mayLoad = op20; - let mayStore = !if(op20, 0, 1); } // FIXME: Merge with the above class when addrmode2 gets used for LDR, LDRB @@ -915,8 +903,6 @@ class AI3ldstidxT op, bit isLoad, dag oops, dag iops, let Inst{19-16} = addr; // Rn let Inst{15-12} = Rt; // Rt let Inst{7-4} = op; - let mayLoad = isLoad; - let mayStore = !if(isLoad, 0, 1); } // stores @@ -938,7 +924,6 @@ class AI3str op, dag oops, dag iops, Format f, InstrItinClass itin, let Inst{7-4} = op; let Inst{3-0} = addr{3-0}; // imm3_0/Rm let DecoderMethod = "DecodeAddrMode3Instruction"; - let mayStore = 1; } // addrmode4 instructions @@ -1405,8 +1390,8 @@ class ThumbXI pattern, AddrMode am = AddrModeNone, string constraints = ""> - : Thumb2I; + string opc, string asm, list pattern, AddrMode am = AddrModeNone> + : Thumb2I; class T2Ii12 pattern> : Thumb2I; diff --git a/llvm/lib/Target/ARM/ARMInstrInfo.td b/llvm/lib/Target/ARM/ARMInstrInfo.td index 41c408b429b0..f5415c5b5895 100644 --- a/llvm/lib/Target/ARM/ARMInstrInfo.td +++ b/llvm/lib/Target/ARM/ARMInstrInfo.td @@ -2748,11 +2748,6 @@ class RFEI let Inst{20} = 1; let Inst{19-16} = Rn; let Inst{15-0} = 0xa00; - - let OutOperandList = !cond(!eq(wb, 0b1): (outs GPR:$Rn_wb), - true: (outs)); - let Constraints = !cond(!eq(wb, 0b1): "$Rn_wb = $Rn", - true: ""); } def RFEDA : RFEI<0, "rfeda\t$Rn"> { @@ -2813,12 +2808,13 @@ def ERET : ABI<0b0001, (outs), (ins), NoItinerary, "eret", "", []>, // Load -let mayLoad = 1 in { defm LDR : AI_ldr1<0, "ldr", IIC_iLoad_r, IIC_iLoad_si, load>; defm LDRB : AI_ldr1nopc<1, "ldrb", IIC_iLoad_bh_r, IIC_iLoad_bh_si, zextloadi8>; -} +defm STR : AI_str1<0, "str", IIC_iStore_r, IIC_iStore_si, store>; +defm STRB : AI_str1nopc<1, "strb", IIC_iStore_bh_r, IIC_iStore_bh_si, + truncstorei8>; // Special LDR for loads from non-pc-relative constpools. let canFoldAsLoad = 1, mayLoad = 1, hasSideEffects = 0, @@ -2834,7 +2830,6 @@ def LDRcp : AI2ldst<0b010, 1, 0, (outs GPR:$Rt), (ins addrmode_imm12:$addr), let Inst{11-0} = addr{11-0}; // imm12 } -let mayLoad = 1 in { // Loads with zero extension def LDRH : AI3ld<0b1011, 1, (outs GPR:$Rt), (ins addrmode3:$addr), LdMiscFrm, IIC_iLoad_bh_r, "ldrh", "\t$Rt, $addr", @@ -2848,7 +2843,6 @@ def LDRSH : AI3ld<0b1111, 1, (outs GPR:$Rt), (ins addrmode3:$addr), LdMiscFrm, def LDRSB : AI3ld<0b1101, 1, (outs GPR:$Rt), (ins addrmode3:$addr), LdMiscFrm, IIC_iLoad_bh_r, "ldrsb", "\t$Rt, $addr", [(set GPR:$Rt, (sextloadi8 addrmode3:$addr))]>; -} // mayLoad = 1 let mayLoad = 1, hasSideEffects = 0, hasExtraDefRegAllocReq = 1 in { // Load doubleword @@ -2865,7 +2859,6 @@ def LOADDUAL : ARMPseudoInst<(outs GPRPairOp:$Rt), (ins addrmode3:$addr), } } -let mayLoad = 1 in { def LDA : AIldracq<0b00, (outs GPR:$Rt), (ins addr_offset_none:$addr), NoItinerary, "lda", "\t$Rt, $addr", []>; def LDAB : AIldracq<0b10, (outs GPR:$Rt), (ins addr_offset_none:$addr), @@ -3127,16 +3120,9 @@ def LDRConstPool : ARMAsmPseudo<"ldr${q} $Rt, $immediate", (ins const_pool_asm_imm:$immediate, pred:$q), (outs GPR:$Rt)>; -} // mayLoad = 1 // Store -let mayStore = 1 in { - -defm STR : AI_str1<0, "str", IIC_iStore_r, IIC_iStore_si, store>; -defm STRB : AI_str1nopc<1, "strb", IIC_iStore_bh_r, IIC_iStore_bh_si, - truncstorei8>; - // Stores with truncate def STRH : AI3str<0b1011, (outs), (ins GPR:$Rt, addrmode3:$addr), StMiscFrm, IIC_iStore_bh_r, "strh", "\t$Rt, $addr", @@ -3230,7 +3216,6 @@ let mayStore = 1, hasSideEffects = 0 in { defm STR : AI2_stridx<0, "str", IIC_iStore_iu, IIC_iStore_ru>; defm STRB : AI2_stridx<1, "strb", IIC_iStore_bh_iu, IIC_iStore_bh_ru>; } -} // mayStore = 1 def : ARMPat<(post_store GPR:$Rt, addr_offset_none:$addr, am2offset_reg:$offset), @@ -3249,7 +3234,6 @@ def : ARMPat<(post_truncsti8 GPR:$Rt, addr_offset_none:$addr, (STRB_POST_IMM GPR:$Rt, addr_offset_none:$addr, am2offset_imm:$offset)>; -let mayStore = 1 in { // Pseudo-instructions for pattern matching the pre-indexed stores. We can't // put the patterns on the instruction definitions directly as ISel wants // the address base and offset to be separate operands, not a single @@ -3466,7 +3450,6 @@ def STLB : AIstrrel<0b10, (outs), (ins GPR:$Rt, addr_offset_none:$addr), NoItinerary, "stlb", "\t$Rt, $addr", []>; def STLH : AIstrrel<0b11, (outs), (ins GPR:$Rt, addr_offset_none:$addr), NoItinerary, "stlh", "\t$Rt, $addr", []>; -} // mayStore = 1 //===----------------------------------------------------------------------===// // Load / store multiple Instructions. @@ -5437,17 +5420,17 @@ def CDP2 : ABXI<0b1110, (outs), (ins p_imm:$cop, imm0_15:$opc1, } class ACI pattern, string cstrs = "", IndexMode im = IndexModeNone, + list pattern, IndexMode im = IndexModeNone, AddrMode am = AddrModeNone> : I { + opc, asm, "", pattern> { let Inst{27-25} = 0b110; } class ACInoP pattern, string constraints = "", IndexMode im = IndexModeNone, + list pattern, IndexMode im = IndexModeNone, AddrMode am = AddrModeNone> : InoP { + opc, asm, "", pattern> { let Inst{31-28} = 0b1111; let Inst{27-25} = 0b110; } @@ -5455,7 +5438,7 @@ class ACInoP pattern> { def _OFFSET : ACI<(outs), (ins p_imm:$cop, c_imm:$CRd, addrmode5:$addr), - asm, "\t$cop, $CRd, $addr", pattern, "", IndexModeNone, + asm, "\t$cop, $CRd, $addr", pattern, IndexModeNone, AddrMode5> { bits<13> addr; bits<4> cop; @@ -5471,8 +5454,8 @@ multiclass LdStCop pattern> { let Inst{7-0} = addr{7-0}; let DecoderMethod = "DecodeCopMemInstruction"; } - def _PRE : ACI<(outs GPR:$Rn_wb), (ins p_imm:$cop, c_imm:$CRd, addrmode5_pre:$addr), - asm, "\t$cop, $CRd, $addr!", [], "$addr.base = $Rn_wb", IndexModePre> { + def _PRE : ACI<(outs), (ins p_imm:$cop, c_imm:$CRd, addrmode5_pre:$addr), + asm, "\t$cop, $CRd, $addr!", [], IndexModePre> { bits<13> addr; bits<4> cop; bits<4> CRd; @@ -5489,7 +5472,7 @@ multiclass LdStCop pattern> { } def _POST: ACI<(outs), (ins p_imm:$cop, c_imm:$CRd, addr_offset_none:$addr, postidx_imm8s4:$offset), - asm, "\t$cop, $CRd, $addr, $offset", [], "", IndexModePost> { + asm, "\t$cop, $CRd, $addr, $offset", [], IndexModePost> { bits<9> offset; bits<4> addr; bits<4> cop; @@ -5527,7 +5510,7 @@ multiclass LdStCop pattern> { } multiclass LdSt2Cop pattern> { def _OFFSET : ACInoP<(outs), (ins p_imm:$cop, c_imm:$CRd, addrmode5:$addr), - asm, "\t$cop, $CRd, $addr", pattern, "", IndexModeNone, + asm, "\t$cop, $CRd, $addr", pattern, IndexModeNone, AddrMode5> { bits<13> addr; bits<4> cop; @@ -5543,8 +5526,8 @@ multiclass LdSt2Cop pattern> { let Inst{7-0} = addr{7-0}; let DecoderMethod = "DecodeCopMemInstruction"; } - def _PRE : ACInoP<(outs GPR:$Rn_wb), (ins p_imm:$cop, c_imm:$CRd, addrmode5_pre:$addr), - asm, "\t$cop, $CRd, $addr!", [], "$addr.base = $Rn_wb", IndexModePre> { + def _PRE : ACInoP<(outs), (ins p_imm:$cop, c_imm:$CRd, addrmode5_pre:$addr), + asm, "\t$cop, $CRd, $addr!", [], IndexModePre> { bits<13> addr; bits<4> cop; bits<4> CRd; @@ -5561,7 +5544,7 @@ multiclass LdSt2Cop pattern> { } def _POST: ACInoP<(outs), (ins p_imm:$cop, c_imm:$CRd, addr_offset_none:$addr, postidx_imm8s4:$offset), - asm, "\t$cop, $CRd, $addr, $offset", [], "", IndexModePost> { + asm, "\t$cop, $CRd, $addr, $offset", [], IndexModePost> { bits<9> offset; bits<4> addr; bits<4> cop; @@ -5598,19 +5581,15 @@ multiclass LdSt2Cop pattern> { } } -let mayLoad = 1 in { defm LDC : LdStCop <1, 0, "ldc", [(int_arm_ldc timm:$cop, timm:$CRd, addrmode5:$addr)]>; defm LDCL : LdStCop <1, 1, "ldcl", [(int_arm_ldcl timm:$cop, timm:$CRd, addrmode5:$addr)]>; defm LDC2 : LdSt2Cop<1, 0, "ldc2", [(int_arm_ldc2 timm:$cop, timm:$CRd, addrmode5:$addr)]>, Requires<[IsARM,PreV8]>; defm LDC2L : LdSt2Cop<1, 1, "ldc2l", [(int_arm_ldc2l timm:$cop, timm:$CRd, addrmode5:$addr)]>, Requires<[IsARM,PreV8]>; -} -let mayStore = 1 in { defm STC : LdStCop <0, 0, "stc", [(int_arm_stc timm:$cop, timm:$CRd, addrmode5:$addr)]>; defm STCL : LdStCop <0, 1, "stcl", [(int_arm_stcl timm:$cop, timm:$CRd, addrmode5:$addr)]>; defm STC2 : LdSt2Cop<0, 0, "stc2", [(int_arm_stc2 timm:$cop, timm:$CRd, addrmode5:$addr)]>, Requires<[IsARM,PreV8]>; defm STC2L : LdSt2Cop<0, 1, "stc2l", [(int_arm_stc2l timm:$cop, timm:$CRd, addrmode5:$addr)]>, Requires<[IsARM,PreV8]>; -} } // DecoderNamespace = "CoProc" diff --git a/llvm/lib/Target/ARM/ARMInstrMVE.td b/llvm/lib/Target/ARM/ARMInstrMVE.td index 5aeb9a8070f2..2cc470fdf474 100644 --- a/llvm/lib/Target/ARM/ARMInstrMVE.td +++ b/llvm/lib/Target/ARM/ARMInstrMVE.td @@ -6181,8 +6181,8 @@ class MVE_VLDRSTR_base, Sched<[WriteVLD4]>; } // mayLoad = 1, hasSideEffects = 0, hasExtraDefRegAllocReq = 1 -let mayLoad = 1 in { // Classes for VLD*LN pseudo-instructions with multi-register operands. // These are expanded to real instructions after register allocation. class VLDQLNPseudo @@ -1073,7 +1072,6 @@ def VLD1LNd32 : VLD1LN32<0b1000, {?,0,?,?}, "32", v2i32, load> { def VLD1LNq8Pseudo : VLD1QLNPseudo; def VLD1LNq16Pseudo : VLD1QLNPseudo; def VLD1LNq32Pseudo : VLD1QLNPseudo; -} // mayLoad = 1 let Predicates = [HasNEON] in { def : Pat<(vector_insert (v4f16 DPR:$src), @@ -1368,7 +1366,6 @@ def VLD4LNq32Pseudo_UPD : VLDQQQQLNWBPseudo, Sched<[WriteVLD2]>; } // mayLoad = 1, hasSideEffects = 0, hasExtraDefRegAllocReq = 1 -let mayLoad = 1 in { // VLD1DUP : Vector Load (single element to all lanes) class VLD1DUP op7_4, string Dt, ValueType Ty, PatFrag LoadOp, Operand AddrMode> @@ -1388,14 +1385,12 @@ def VLD1DUPd16 : VLD1DUP<{0,1,0,?}, "16", v4i16, extloadi16, addrmode6dupalign16>; def VLD1DUPd32 : VLD1DUP<{1,0,0,?}, "32", v2i32, load, addrmode6dupalign32>; -} // mayLoad = 1 let Predicates = [HasNEON] in { def : Pat<(v2f32 (ARMvdup (f32 (load addrmode6dup:$addr)))), (VLD1DUPd32 addrmode6:$addr)>; } -let mayLoad = 1 in { class VLD1QDUP op7_4, string Dt, ValueType Ty, PatFrag LoadOp, Operand AddrMode> : NLdSt<1, 0b10, 0b1100, op7_4, (outs VecListDPairAllLanes:$Vd), @@ -1414,7 +1409,6 @@ def VLD1DUPq16 : VLD1QDUP<{0,1,1,?}, "16", v8i16, extloadi16, addrmode6dupalign16>; def VLD1DUPq32 : VLD1QDUP<{1,0,1,?}, "32", v4i32, load, addrmode6dupalign32>; -} // mayLoad = 1 let Predicates = [HasNEON] in { def : Pat<(v4f32 (ARMvdup (f32 (load addrmode6dup:$addr)))), @@ -2128,7 +2122,6 @@ def VST4q32oddPseudo_UPD : VSTQQQQWBPseudo, Sched<[WriteVST4]>; } // mayStore = 1, hasSideEffects = 0, hasExtraSrcRegAllocReq = 1 -let mayStore = 1 in { // Classes for VST*LN pseudo-instructions with multi-register operands. // These are expanded to real instructions after register allocation. class VSTQLNPseudo @@ -2189,7 +2182,6 @@ def VST1LNd32 : VST1LN<0b1000, {?,0,?,?}, "32", v2i32, store, extractelt, def VST1LNq8Pseudo : VST1QLNPseudo; def VST1LNq16Pseudo : VST1QLNPseudo; def VST1LNq32Pseudo : VST1QLNPseudo; -} // mayStore = 1 let Predicates = [HasNEON] in { def : Pat<(store (extractelt (v2f32 DPR:$src), imm:$lane), addrmode6:$addr), @@ -2203,7 +2195,6 @@ def : Pat<(store (extractelt (v8f16 QPR:$src), imm:$lane), addrmode6:$addr), (VST1LNq16Pseudo addrmode6:$addr, QPR:$src, imm:$lane)>; } -let mayStore = 1 in { // ...with address register writeback: class VST1LNWB op11_8, bits<4> op7_4, string Dt, ValueType Ty, PatFrag StoreOp, SDNode ExtractOp, Operand AdrMode> @@ -2241,7 +2232,6 @@ def VST1LNd32_UPD : VST1LNWB<0b1000, {?,0,?,?}, "32", v2i32, post_store, def VST1LNq8Pseudo_UPD : VST1QLNWBPseudo; def VST1LNq16Pseudo_UPD : VST1QLNWBPseudo; def VST1LNq32Pseudo_UPD : VST1QLNWBPseudo; -} // mayStore = 1 let mayStore = 1, hasSideEffects = 0, hasExtraSrcRegAllocReq = 1 in { @@ -8158,7 +8148,6 @@ def : NEONInstAlias<"vand${p}.i32 $Vd, $imm", (VBICiv4i32 QPR:$Vd, nImmSplatNotI32:$imm, pred:$p)>; -let mayLoad = 1 in { // VLD1 single-lane pseudo-instructions. These need special handling for // the lane index that an InstAlias can't handle, so we use these instead. def VLD1LNdAsm_8 : NEONDataTypeAsmPseudoInst<"vld1${p}", ".8", "$list, $addr", @@ -8195,9 +8184,8 @@ def VLD1LNdWB_register_Asm_32 : NEONDataTypeAsmPseudoInst<"vld1${p}", ".32", "$list, $addr, $Rm", (ins VecListOneDWordIndexed:$list, addrmode6align32:$addr, rGPR:$Rm, pred:$p)>; -} // mayLoad = 1 -let mayStore = 1 in { + // VST1 single-lane pseudo-instructions. These need special handling for // the lane index that an InstAlias can't handle, so we use these instead. def VST1LNdAsm_8 : NEONDataTypeAsmPseudoInst<"vst1${p}", ".8", "$list, $addr", @@ -8234,9 +8222,7 @@ def VST1LNdWB_register_Asm_32 : NEONDataTypeAsmPseudoInst<"vst1${p}", ".32", "$list, $addr, $Rm", (ins VecListOneDWordIndexed:$list, addrmode6align32:$addr, rGPR:$Rm, pred:$p)>; -} // mayStore = 1 -let mayLoad = 1 in { // VLD2 single-lane pseudo-instructions. These need special handling for // the lane index that an InstAlias can't handle, so we use these instead. def VLD2LNdAsm_8 : NEONDataTypeAsmPseudoInst<"vld2${p}", ".8", "$list, $addr", @@ -8294,9 +8280,8 @@ def VLD2LNqWB_register_Asm_32 : NEONDataTypeAsmPseudoInst<"vld2${p}", ".32", "$list, $addr, $Rm", (ins VecListTwoQWordIndexed:$list, addrmode6align64:$addr, rGPR:$Rm, pred:$p)>; -} // mayLoad = 1 -let mayStore = 1 in { + // VST2 single-lane pseudo-instructions. These need special handling for // the lane index that an InstAlias can't handle, so we use these instead. def VST2LNdAsm_8 : NEONDataTypeAsmPseudoInst<"vst2${p}", ".8", "$list, $addr", @@ -8355,9 +8340,7 @@ def VST2LNqWB_register_Asm_32 : NEONDataTypeAsmPseudoInst<"vst2${p}", ".32", "$list, $addr, $Rm", (ins VecListTwoQWordIndexed:$list, addrmode6align64:$addr, rGPR:$Rm, pred:$p)>; -} // mayStore = 1 -let mayLoad = 1 in { // VLD3 all-lanes pseudo-instructions. These need special handling for // the lane index that an InstAlias can't handle, so we use these instead. def VLD3DUPdAsm_8 : NEONDataTypeAsmPseudoInst<"vld3${p}", ".8", "$list, $addr", @@ -8546,9 +8529,7 @@ def VLD3qWB_register_Asm_32 : NEONDataTypeAsmPseudoInst<"vld3${p}", ".32", "$list, $addr, $Rm", (ins VecListThreeQ:$list, addrmode6align64:$addr, rGPR:$Rm, pred:$p)>; -} // mayLoad = 1 -let mayStore = 1 in { // VST3 single-lane pseudo-instructions. These need special handling for // the lane index that an InstAlias can't handle, so we use these instead. def VST3LNdAsm_8 : NEONDataTypeAsmPseudoInst<"vst3${p}", ".8", "$list, $addr", @@ -8667,9 +8648,7 @@ def VST3qWB_register_Asm_32 : NEONDataTypeAsmPseudoInst<"vst3${p}", ".32", "$list, $addr, $Rm", (ins VecListThreeQ:$list, addrmode6align64:$addr, rGPR:$Rm, pred:$p)>; -} // mayStore = 1 -let mayLoad = 1 in { // VLD4 all-lanes pseudo-instructions. These need special handling for // the lane index that an InstAlias can't handle, so we use these instead. def VLD4DUPdAsm_8 : NEONDataTypeAsmPseudoInst<"vld4${p}", ".8", "$list, $addr", @@ -8872,9 +8851,7 @@ def VLD4qWB_register_Asm_32 : NEONDataTypeAsmPseudoInst<"vld4${p}", ".32", "$list, $addr, $Rm", (ins VecListFourQ:$list, addrmode6align64or128or256:$addr, rGPR:$Rm, pred:$p)>; -} // mayLoad = 1 -let mayStore = 1 in { // VST4 single-lane pseudo-instructions. These need special handling for // the lane index that an InstAlias can't handle, so we use these instead. def VST4LNdAsm_8 : NEONDataTypeAsmPseudoInst<"vst4${p}", ".8", "$list, $addr", @@ -9005,7 +8982,6 @@ def VST4qWB_register_Asm_32 : NEONDataTypeAsmPseudoInst<"vst4${p}", ".32", "$list, $addr, $Rm", (ins VecListFourQ:$list, addrmode6align64or128or256:$addr, rGPR:$Rm, pred:$p)>; -} // mayStore = 1 // VMOV/VMVN takes an optional datatype suffix defm : NEONDTAnyInstAlias<"vmov${p}", "$Vd, $Vm", diff --git a/llvm/lib/Target/ARM/ARMInstrThumb.td b/llvm/lib/Target/ARM/ARMInstrThumb.td index 70fdb82f2c98..8f7039a327b3 100644 --- a/llvm/lib/Target/ARM/ARMInstrThumb.td +++ b/llvm/lib/Target/ARM/ARMInstrThumb.td @@ -688,7 +688,6 @@ def tTRAP : TI<(outs), (ins), IIC_Br, // PC-relative loads need to be matched first as constant pool accesses need to // always be PC-relative. We do this using AddedComplexity, as the pattern is // simpler than the patterns of the other load instructions. -let mayLoad = 1 in { let canFoldAsLoad = 1, isReMaterializable = 1, AddedComplexity = 10 in def tLDRpci : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_pc:$addr), IIC_iLoad_i, "ldr", "\t$Rt, $addr", @@ -699,7 +698,6 @@ def tLDRpci : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_pc:$addr), IIC_iLoad_i, bits<8> addr; let Inst{10-8} = Rt; let Inst{7-0} = addr; - let mayLoad = 1; } // SP-relative loads should be matched before standard immediate-offset loads as @@ -713,7 +711,6 @@ def tLDRspi : T1pIs<(outs tGPR:$Rt), (ins t_addrmode_sp:$addr), IIC_iLoad_i, bits<8> addr; let Inst{10-8} = Rt; let Inst{7-0} = addr; - let mayLoad = 1; } // Loads: reg/reg and reg/imm5 @@ -787,10 +784,8 @@ def tLDRSH : // A8.6.84 AddrModeT1_2, IIC_iLoad_bh_r, "ldrsh", "\t$Rt, $addr", [(set tGPR:$Rt, (sextloadi16 t_addrmode_rr_sext:$addr))]>, Sched<[WriteLd]>; -} // mayLoad = 1 -let mayStore = 1 in { def tSTRspi : T1pIs<(outs), (ins tGPR:$Rt, t_addrmode_sp:$addr), IIC_iStore_i, "str", "\t$Rt, $addr", [(store tGPR:$Rt, t_addrmode_sp:$addr)]>, @@ -799,7 +794,6 @@ def tSTRspi : T1pIs<(outs), (ins tGPR:$Rt, t_addrmode_sp:$addr), IIC_iStore_i, bits<8> addr; let Inst{10-8} = Rt; let Inst{7-0} = addr; - let mayStore = 1; } // A8.6.194 & A8.6.192 @@ -819,7 +813,6 @@ defm tSTRH : thumb_st_rr_ri_enc<0b001, 0b1000, t_addrmode_rr, t_addrmode_is2, AddrModeT1_2, IIC_iStore_bh_r, IIC_iStore_bh_i, "strh", truncstorei16>, Sched<[WriteST]>; -} // mayStore = 1 //===----------------------------------------------------------------------===// @@ -1506,7 +1499,7 @@ def tLEApcrelJT : tPseudoInst<(outs tGPR:$Rd), // Thumb-1 doesn't have the TBB or TBH instructions, but we can synthesize them // and make use of the same compressed jump table format as Thumb-2. -let mayLoad = 1, Size = 2, isBranch = 1, isTerminator = 1, isBarrier = 1, +let Size = 2, isBranch = 1, isTerminator = 1, isBarrier = 1, isIndirectBranch = 1, isNotDuplicable = 1 in { def tTBB_JT : tPseudoInst<(outs), (ins tGPRwithpc:$base, tGPR:$index, i32imm:$jt, i32imm:$pclbl), 0, diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td b/llvm/lib/Target/ARM/ARMInstrThumb2.td index 7f1328ad7fff..521cb0695384 100644 --- a/llvm/lib/Target/ARM/ARMInstrThumb2.td +++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td @@ -1572,7 +1572,6 @@ def t2LDRSH_POST : T2Ipostldst<1, 0b01, 1, 0, (outs GPR:$Rt, GPR:$Rn_wb), // F5.1.72 LDR (immediate) T4 // .w suffixes; Constraints can't be used on t2InstAlias to describe // "$Rn = $Rn_wb" on POST or "$addr.base = $Rn_wb" on PRE. -let mayLoad = 1 in { def t2LDR_PRE_imm : t2AsmPseudo<"ldr${p}.w $Rt, $addr!", (ins GPR:$Rt, t2addrmode_imm8_pre:$addr, pred:$p)>; def t2LDR_POST_imm : t2AsmPseudo<"ldr${p}.w $Rt, $Rn, $imm", @@ -1634,16 +1633,13 @@ def t2LDAB : T2Ildacq<0b1101, 0b00, (outs rGPR:$Rt), def t2LDAH : T2Ildacq<0b1101, 0b01, (outs rGPR:$Rt), (ins addr_offset_none:$addr), "ldah", "\t$Rt, $addr", []>, Sched<[WriteLd]>; -} // mayLoad = 1 -let mayStore = 1 in { // Store defm t2STR :T2I_st<0b10,"str", IIC_iStore_i, IIC_iStore_si, GPR, store>; defm t2STRB:T2I_st<0b00,"strb", IIC_iStore_bh_i, IIC_iStore_bh_si, rGPR, truncstorei8>; defm t2STRH:T2I_st<0b01,"strh", IIC_iStore_bh_i, IIC_iStore_bh_si, rGPR, truncstorei16>; -} // Store doubleword let mayStore = 1, hasSideEffects = 0, hasExtraSrcRegAllocReq = 1 in @@ -1678,7 +1674,6 @@ def t2STRB_PRE : T2Ipreldst<0, 0b00, 0, 1, (outs GPRnopc:$Rn_wb), Sched<[WriteST]>; } // mayStore = 1, hasSideEffects = 0 -let mayStore = 1 in { def t2STR_POST : T2Ipostldst<0, 0b10, 0, 0, (outs GPRnopc:$Rn_wb), (ins GPRnopc:$Rt, addr_offset_none:$Rn, t2am_imm8_offset:$offset), @@ -1773,7 +1768,6 @@ class T2IstT type, string opc, InstrItinClass ii> def t2STRT : T2IstT<0b10, "strt", IIC_iStore_i>; def t2STRBT : T2IstT<0b00, "strbt", IIC_iStore_bh_i>; def t2STRHT : T2IstT<0b01, "strht", IIC_iStore_bh_i>; -} // mayStore = 1 // ldrd / strd pre / post variants @@ -1806,7 +1800,6 @@ def t2STRD_POST : T2Ii8s4post<0, 1, 0, (outs GPR:$wb), IIC_iStore_d_ru, "strd", "\t$Rt, $Rt2, $addr$imm", "$addr.base = $wb", []>, Sched<[WriteST]>; -let mayStore = 1 in { class T2Istrrel bit54, dag oops, dag iops, string opc, string asm, list pattern> : Thumb2I; def t2STLH : T2Istrrel<0b01, (outs), (ins rGPR:$Rt, addr_offset_none:$addr), "stlh", "\t$Rt, $addr", []>; -} // mayStore = 1 // T2Ipl (Preload Data/Instruction) signals the memory system of possible future // data/instruction access. @@ -1901,11 +1893,9 @@ multiclass T2Ipl write, bits<1> instr, string opc> { } } -let mayLoad = 1 in { defm t2PLD : T2Ipl<0, 0, "pld">, Requires<[IsThumb2]>; defm t2PLDW : T2Ipl<1, 0, "pldw">, Requires<[IsThumb2,HasV7,HasMP]>; defm t2PLI : T2Ipl<0, 1, "pli">, Requires<[IsThumb2,HasV7]>; -} // mayLoad = 1 // PLD/PLDW/PLI aliases w/ the optional .w suffix def : t2InstAlias<"pld${p}.w\t$addr", @@ -1937,7 +1927,6 @@ def : InstAlias<"pli${p}.w\t$addr", // pci variant is very similar to i12, but supports negative offsets // from the PC. Only PLD and PLI have pci variants (not PLDW) -let mayLoad = 1 in { class T2Iplpci inst, string opc> : T2Ipc<(outs), (ins t2ldrlabel:$addr), IIC_Preload, opc, "\t$addr", [(ARMPreload (ARMWrapper tconstpool:$addr), @@ -1957,7 +1946,6 @@ class T2Iplpci inst, string opc> : T2Ipc<(outs), (ins t2ldrlabel:$addr), def t2PLDpci : T2Iplpci<0, "pld">, Requires<[IsThumb2]>; def t2PLIpci : T2Iplpci<1, "pli">, Requires<[IsThumb2,HasV7]>; -} // mayLoad = 1 def : t2InstAlias<"pld${p}.w $addr", (t2PLDpci t2ldrlabel:$addr, pred:$p)>; @@ -1981,7 +1969,6 @@ def : InstAlias<"pli${p}.w $addr", // Load / store multiple Instructions. // -let mayLoad = 1 in multiclass thumb2_ld_mult { def IA : @@ -3901,7 +3888,6 @@ def t2BR_JT : t2basePseudoInst<(outs), Sched<[WriteBr]>; // FIXME: Add a case that can be predicated. -let mayLoad = 1 in { def t2TBB_JT : t2PseudoInst<(outs), (ins GPR:$base, GPR:$index, i32imm:$jt, i32imm:$pclbl), 0, IIC_Br, []>, Sched<[WriteBr]>; @@ -3935,7 +3921,6 @@ def t2TBH : T2I<(outs), (ins (addrmode_tbh $Rn, $Rm):$addr), IIC_Br, let DecoderMethod = "DecodeThumbTableBranch"; } -} // mayLoad } // isNotDuplicable, isIndirectBranch } // isBranch, isTerminator, isBarrier @@ -4317,8 +4302,8 @@ def t2ABS : PseudoInst<(outs rGPR:$dst), (ins rGPR:$src), // Coprocessor load/store -- for disassembly only // class T2CI op31_28, dag oops, dag iops, string opc, string asm, - list pattern, AddrMode am = AddrModeNone, string constraints = ""> - : T2I { + list pattern, AddrMode am = AddrModeNone> + : T2I { let Inst{31-28} = op31_28; let Inst{27-25} = 0b110; } @@ -4342,8 +4327,8 @@ multiclass t2LdStCop op31_28, bit load, bit Dbit, string asm, list let DecoderMethod = "DecodeCopMemInstruction"; } def _PRE : T2CI { + (outs), (ins p_imm:$cop, c_imm:$CRd, addrmode5_pre:$addr), + asm, "\t$cop, $CRd, $addr!", []> { bits<13> addr; bits<4> cop; bits<4> CRd; @@ -4399,20 +4384,16 @@ multiclass t2LdStCop op31_28, bit load, bit Dbit, string asm, list } let DecoderNamespace = "Thumb2CoProc" in { -let mayLoad = 1 in { defm t2LDC : t2LdStCop<0b1110, 1, 0, "ldc", [(int_arm_ldc timm:$cop, timm:$CRd, addrmode5:$addr)]>; defm t2LDCL : t2LdStCop<0b1110, 1, 1, "ldcl", [(int_arm_ldcl timm:$cop, timm:$CRd, addrmode5:$addr)]>; defm t2LDC2 : t2LdStCop<0b1111, 1, 0, "ldc2", [(int_arm_ldc2 timm:$cop, timm:$CRd, addrmode5:$addr)]>, Requires<[PreV8,IsThumb2]>; defm t2LDC2L : t2LdStCop<0b1111, 1, 1, "ldc2l", [(int_arm_ldc2l timm:$cop, timm:$CRd, addrmode5:$addr)]>, Requires<[PreV8,IsThumb2]>; -} -let mayStore = 1 in { defm t2STC : t2LdStCop<0b1110, 0, 0, "stc", [(int_arm_stc timm:$cop, timm:$CRd, addrmode5:$addr)]>; defm t2STCL : t2LdStCop<0b1110, 0, 1, "stcl", [(int_arm_stcl timm:$cop, timm:$CRd, addrmode5:$addr)]>; defm t2STC2 : t2LdStCop<0b1111, 0, 0, "stc2", [(int_arm_stc2 timm:$cop, timm:$CRd, addrmode5:$addr)]>, Requires<[PreV8,IsThumb2]>; defm t2STC2L : t2LdStCop<0b1111, 0, 1, "stc2l", [(int_arm_stc2l timm:$cop, timm:$CRd, addrmode5:$addr)]>, Requires<[PreV8,IsThumb2]>; } -} //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/ARM/ARMInstrVFP.td b/llvm/lib/Target/ARM/ARMInstrVFP.td index 00c42c95c344..c1fecf3673a0 100644 --- a/llvm/lib/Target/ARM/ARMInstrVFP.td +++ b/llvm/lib/Target/ARM/ARMInstrVFP.td @@ -149,7 +149,7 @@ def fbits16 : Operand { // Load / store Instructions. // -let mayLoad = 1, canFoldAsLoad = 1, isReMaterializable = 1 in { +let canFoldAsLoad = 1, isReMaterializable = 1 in { def VLDRD : ADI5<0b1101, 0b01, (outs DPR:$Dd), (ins addrmode5:$addr), IIC_fpLoad64, "vldr", "\t$Dd, $addr", @@ -171,7 +171,7 @@ def VLDRH : AHI5<0b1101, 0b01, (outs HPR:$Sd), (ins addrmode5fp16:$addr), [(set HPR:$Sd, (f16 (alignedload16 addrmode5fp16:$addr)))]>, Requires<[HasFPRegs16]>; -} // End of 'mayLoad = 1, let canFoldAsLoad = 1, isReMaterializable = 1 in' +} // End of 'let canFoldAsLoad = 1, isReMaterializable = 1 in' def : Pat<(bf16 (alignedload16 addrmode5fp16:$addr)), (VLDRH addrmode5fp16:$addr)> { @@ -186,7 +186,6 @@ def : Pat<(bf16 (alignedload16 t2addrmode_imm12:$addr)), let Predicates = [HasNoFPRegs16, IsThumb]; } -let mayStore = 1 in { def VSTRD : ADI5<0b1101, 0b00, (outs), (ins DPR:$Dd, addrmode5:$addr), IIC_fpStore64, "vstr", "\t$Dd, $addr", [(alignedstore32 (f64 DPR:$Dd), addrmode5:$addr)]>, @@ -206,7 +205,6 @@ def VSTRH : AHI5<0b1101, 0b00, (outs), (ins HPR:$Sd, addrmode5fp16:$addr), IIC_fpStore16, "vstr", ".16\t$Sd, $addr", [(alignedstore16 (f16 HPR:$Sd), addrmode5fp16:$addr)]>, Requires<[HasFPRegs16]>; -} // mayStore = 1 def : Pat<(alignedstore16 (bf16 HPR:$Sd), addrmode5fp16:$addr), (VSTRH (bf16 HPR:$Sd), addrmode5fp16:$addr)> { @@ -236,8 +234,6 @@ multiclass vfp_ldst_multgetSubtargetInfo().getFeatureBits(); @@ -1948,10 +1943,6 @@ static DecodeStatus DecodeCopMemInstruction(MCInst &Inst, unsigned Insn, if (featureBits[ARM::HasV8Ops] && (coproc != 14)) return MCDisassembler::Fail; - if (IsPreIndexed) - // Dummy operand for Rn_wb. - MCOperand_CreateImm0(Inst, (0)); - Inst.addOperand(MCOperand::createImm(coproc)); Inst.addOperand(MCOperand::createImm(CRd)); if (!Check(S, DecodeGPRRegisterClass(Inst, Rn, Address, Decoder))) diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp index 202fc473f9e4..609a383426d6 100644 --- a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp +++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp @@ -1628,7 +1628,7 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM, ISD::UADDO, ISD::SSUBO, ISD::USUBO, ISD::SMUL_LOHI, ISD::UMUL_LOHI, // Logical/bit: ISD::AND, ISD::OR, ISD::XOR, ISD::ROTL, ISD::ROTR, - ISD::CTPOP, ISD::CTLZ, ISD::CTTZ, + ISD::CTPOP, ISD::CTLZ, ISD::CTTZ, ISD::BSWAP, ISD::BITREVERSE, // Floating point arithmetic/math functions: ISD::FADD, ISD::FSUB, ISD::FMUL, ISD::FMA, ISD::FDIV, ISD::FREM, ISD::FNEG, ISD::FABS, ISD::FSQRT, ISD::FSIN, @@ -1701,8 +1701,11 @@ HexagonTargetLowering::HexagonTargetLowering(const TargetMachine &TM, setOperationAction(ISD::OR, NativeVT, Legal); setOperationAction(ISD::XOR, NativeVT, Legal); - if (NativeVT.getVectorElementType() != MVT::i1) + if (NativeVT.getVectorElementType() != MVT::i1) { setOperationAction(ISD::SPLAT_VECTOR, NativeVT, Legal); + setOperationAction(ISD::BSWAP, NativeVT, Legal); + setOperationAction(ISD::BITREVERSE, NativeVT, Legal); + } } for (MVT VT : {MVT::v8i8, MVT::v4i16, MVT::v2i32}) { diff --git a/llvm/lib/Target/Hexagon/HexagonPatterns.td b/llvm/lib/Target/Hexagon/HexagonPatterns.td index a75ac0e1378e..375e519a6848 100644 --- a/llvm/lib/Target/Hexagon/HexagonPatterns.td +++ b/llvm/lib/Target/Hexagon/HexagonPatterns.td @@ -117,8 +117,8 @@ def usat: PatFrag<(ops node:$V, node:$Ty), (HexagonUSAT node:$V, node:$Ty)>; // Pattern fragments to extract the low and high subregisters from a // 64-bit value. -def LoReg: OutPatFrag<(ops node:$Rs), (EXTRACT_SUBREG (i64 $Rs), isub_lo)>; -def HiReg: OutPatFrag<(ops node:$Rs), (EXTRACT_SUBREG (i64 $Rs), isub_hi)>; +def LoReg: OutPatFrag<(ops node:$Rs), (EXTRACT_SUBREG $Rs, isub_lo)>; +def HiReg: OutPatFrag<(ops node:$Rs), (EXTRACT_SUBREG $Rs, isub_hi)>; def IsOrAdd: PatFrag<(ops node:$A, node:$B), (or node:$A, node:$B), [{ return isOrEquivalentToAdd(N); @@ -1123,6 +1123,12 @@ def: Pat<(bswap I32:$Rs), (A2_swiz I32:$Rs)>; def: Pat<(bswap I64:$Rss), (Combinew (A2_swiz (LoReg $Rss)), (A2_swiz (HiReg $Rss)))>; +def: Pat<(bswap V2I16:$Rs), (A2_combine_lh (A2_swiz $Rs), (A2_swiz $Rs))>; +def: Pat<(bswap V2I32:$Rs), (Combinew (A2_swiz (HiReg $Rs)), + (A2_swiz (LoReg $Rs)))>; +def: Pat<(bswap V4I16:$Rs), (A2_orp (S2_lsr_i_vh $Rs, 8), + (S2_asl_i_vh $Rs, 8))>; + def: Pat<(shl s6_0ImmPred:$s6, I32:$Rt), (S4_lsli imm:$s6, I32:$Rt)>; def: Pat<(shl I32:$Rs, (i32 16)), (A2_aslh I32:$Rs)>; def: Pat<(sra I32:$Rs, (i32 16)), (A2_asrh I32:$Rs)>; @@ -1854,6 +1860,20 @@ def: Pat<(i32 (ctpop I32:$Rs)), (S5_popcountp (A4_combineir 0, I32:$Rs))>; def: Pat<(bitreverse I32:$Rs), (S2_brev I32:$Rs)>; def: Pat<(bitreverse I64:$Rss), (S2_brevp I64:$Rss)>; +def: Pat<(bitreverse V4I8:$Rs), (A2_swiz (S2_brev $Rs))>; +def: Pat<(bitreverse V8I8:$Rs), (Combinew (A2_swiz (LoReg (S2_brevp $Rs))), + (A2_swiz (HiReg (S2_brevp $Rs))))>; +def: Pat<(bitreverse V2I16:$Rs), (A2_combine_lh (S2_brev $Rs), + (S2_brev $Rs))>; +def: Pat<(bitreverse V4I16:$Rs), + (Combinew (A2_combine_lh (LoReg (S2_brevp $Rs)), + (LoReg (S2_brevp $Rs))), + (A2_combine_lh (HiReg (S2_brevp $Rs)), + (HiReg (S2_brevp $Rs))))>; +def: Pat<(bitreverse V2I32:$Rs), + (Combinew (i32 (LoReg (S2_brevp $Rs))), + (i32 (HiReg (S2_brevp $Rs))))>; + let AddedComplexity = 20 in { // Complexity greater than and/or/xor def: Pat<(and I32:$Rs, IsNPow2_32:$V), (S2_clrbit_i IntRegs:$Rs, (LogN2_32 $V))>; diff --git a/llvm/lib/Target/PowerPC/PPC.td b/llvm/lib/Target/PowerPC/PPC.td index 5ef8f1bc3e7f..d05723461103 100644 --- a/llvm/lib/Target/PowerPC/PPC.td +++ b/llvm/lib/Target/PowerPC/PPC.td @@ -327,11 +327,6 @@ def FeaturePredictableSelectIsExpensive : def FeatureFastMFLR : SubtargetFeature<"fast-MFLR", "HasFastMFLR", "true", "MFLR is a fast instruction">; -#define CAPSTONE_DEPR_FEATURE -include "PPCDeprecated.td" -#define CAPSTONE_PS_FEATURE -include "PPCInstrPairedSingle.td" - // Since new processors generally contain a superset of features of those that // came before them, the idea is to make implementations of new processors // less error prone and easier to read. @@ -667,9 +662,6 @@ def : ProcessorModel<"ppc64", G5Model, FeatureMFTB]>; def : ProcessorModel<"ppc64le", P8Model, ProcessorFeatures.P8Features>; -#define CAPSTONE_PS_PROCESSOR -include "PPCInstrPairedSingle.td" - //===----------------------------------------------------------------------===// // Calling Conventions //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td index 0ed5b63ee5f0..4335891cd483 100644 --- a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td +++ b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td @@ -1243,7 +1243,6 @@ def : InstAlias<"mtspefscr $Rx", (MTSPR8 512, g8rc:$Rx)>; // Sign extending loads. -let mayLoad = 1 in { let PPC970_Unit = 2 in { let Interpretation64Bit = 1, isCodeGenOnly = 1 in def LHA8: DForm_1<42, (outs g8rc:$rD), (ins memri:$src), @@ -1264,8 +1263,6 @@ def LWAX : XForm_1_memOp<31, 341, (outs g8rc:$rD), (ins memrr:$src), "lwax $rD, $src", IIC_LdStLHA, [(set i64:$rD, (sextloadi32 XForm:$src))]>, isPPC64, PPC970_DGroup_Cracked, SExt32To64; -} - // For fast-isel: let isCodeGenOnly = 1, mayLoad = 1, hasSideEffects = 0 in { def LWA_32 : DSForm_1<58, 2, (outs gprc:$rD), (ins memrix:$src), @@ -1369,7 +1366,7 @@ def LWZUX8 : XForm_1_memOp<31, 55, (outs g8rc:$rD, ptr_rc_nor0:$ea_result), // Full 8-byte loads. -let PPC970_Unit = 2, mayLoad = 1 in { +let PPC970_Unit = 2 in { def LD : DSForm_1<58, 0, (outs g8rc:$rD), (ins memrix:$src), "ld $rD, $src", IIC_LdStLD, [(set i64:$rD, (load DSForm:$src))]>, isPPC64; @@ -1605,7 +1602,7 @@ def PADDIdtprel : PPCEmitTimePseudo<(outs g8rc:$rD), (ins g8rc_nox0:$reg, s16imm isPPC64; let PPC970_Unit = 2 in { -let Interpretation64Bit = 1, isCodeGenOnly = 1, mayStore = 1 in { +let Interpretation64Bit = 1, isCodeGenOnly = 1 in { // Truncating stores. def STB8 : DForm_1<38, (outs), (ins g8rc:$rS, memri:$src), "stb $rS, $src", IIC_LdStStore, @@ -1631,7 +1628,6 @@ def STWX8 : XForm_8_memOp<31, 151, (outs), (ins g8rc:$rS, memrr:$dst), } // Interpretation64Bit // Normal 8-byte stores. -let mayStore = 1 in { def STD : DSForm_1<62, 0, (outs), (ins g8rc:$rS, memrix:$dst), "std $rS, $dst", IIC_LdStSTD, [(store i64:$rS, DSForm:$dst)]>, isPPC64; @@ -1646,7 +1642,6 @@ def STDBRX: XForm_8_memOp<31, 660, (outs), (ins g8rc:$rS, memrr:$dst), [(PPCstbrx i64:$rS, ForceXForm:$dst, i64)]>, isPPC64, PPC970_DGroup_Cracked; } -} let mayStore = 1, hasNoSchedulingInfo = 1 in { // Normal 16-byte stores. diff --git a/llvm/lib/Target/PowerPC/PPCInstrAltivec.td b/llvm/lib/Target/PowerPC/PPCInstrAltivec.td index 9fefbf8f6054..9236b8fea773 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrAltivec.td +++ b/llvm/lib/Target/PowerPC/PPCInstrAltivec.td @@ -426,7 +426,6 @@ def LVXL : XForm_1_memOp<31, 359, (outs vrrc:$vD), (ins memrr:$src), [(set v4i32:$vD, (int_ppc_altivec_lvxl ForceXForm:$src))]>; } -let mayLoad = 1 in { def LVSL : XForm_1_memOp<31, 6, (outs vrrc:$vD), (ins memrr:$src), "lvsl $vD, $src", IIC_LdStLoad, [(set v16i8:$vD, (int_ppc_altivec_lvsl ForceXForm:$src))]>, @@ -435,7 +434,6 @@ def LVSR : XForm_1_memOp<31, 38, (outs vrrc:$vD), (ins memrr:$src), "lvsr $vD, $src", IIC_LdStLoad, [(set v16i8:$vD, (int_ppc_altivec_lvsr ForceXForm:$src))]>, PPC970_Unit_LSU; -} let PPC970_Unit = 2, mayStore = 1, mayLoad = 0 in { // Stores. def STVEBX: XForm_8_memOp<31, 135, (outs), (ins vrrc:$rS, memrr:$dst), diff --git a/llvm/lib/Target/PowerPC/PPCInstrFormats.td b/llvm/lib/Target/PowerPC/PPCInstrFormats.td index 6d0f39d22cc2..f0f8d6ebcf09 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrFormats.td +++ b/llvm/lib/Target/PowerPC/PPCInstrFormats.td @@ -2208,7 +2208,3 @@ class PPCPostRAExpPseudo pattern> class PseudoXFormMemOp pattern> : PPCPostRAExpPseudo, XFormMemOp; -#define CAPSTONE_PS_FORMATS -include "PPCInstrPairedSingle.td" -#define CAPSTONE_DEPR_FORMATS -include "PPCDeprecated.td" diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.td b/llvm/lib/Target/PowerPC/PPCInstrInfo.td index 0043040fa689..1551f3f32841 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.td +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.td @@ -697,66 +697,39 @@ def PDForm : ComplexPattern; //===----------------------------------------------------------------------===// // PowerPC Instruction Predicate Definitions. -def In32BitMode : Predicate<"!Subtarget->isPPC64()">, - AssemblerPredicate<(all_of (not Feature64Bit)), "64bit">; -def In64BitMode : Predicate<"Subtarget->isPPC64()">, - AssemblerPredicate<(all_of Feature64Bit), "64bit">; -def IsBookE : Predicate<"Subtarget->isBookE()">, - AssemblerPredicate<(all_of FeatureBookE), "booke">; -def IsNotBookE : Predicate<"!Subtarget->isBookE()">, - AssemblerPredicate<(all_of (not FeatureBookE)), "notbooke">; -def HasOnlyMSYNC : Predicate<"Subtarget->hasOnlyMSYNC()">, - AssemblerPredicate<(all_of FeatureMSYNC), "msync">; -def HasSYNC : Predicate<"!Subtarget->hasOnlyMSYNC()">, - AssemblerPredicate<(all_of (not FeatureMSYNC)), "sync">; -def IsPPC4xx : Predicate<"Subtarget->isPPC4xx()">, - AssemblerPredicate<(all_of FeaturePPC4xx), "ppc4xx">; -def IsPPC6xx : Predicate<"Subtarget->isPPC6xx()">, - AssemblerPredicate<(all_of FeaturePPC6xx), "ppc6xx">; -def IsE500 : Predicate<"Subtarget->isE500()">, - AssemblerPredicate<(all_of FeatureE500), "e500">; -def HasSPE : Predicate<"Subtarget->hasSPE()">, - AssemblerPredicate<(all_of FeatureSPE), "spe">; -def HasICBT : Predicate<"Subtarget->hasICBT()">, - AssemblerPredicate<(all_of FeatureICBT), "icbt">; -def HasPartwordAtomics : Predicate<"Subtarget->hasPartwordAtomics()">, - AssemblerPredicate<(all_of FeaturePartwordAtomic), "partwordatomic">; -def HasQuadwordAtomics : Predicate<"Subtarget->hasQuadwordAtomics()">, - AssemblerPredicate<(all_of FeatureQuadwordAtomic), "quadwordatomic">; +def In32BitMode : Predicate<"!Subtarget->isPPC64()">; +def In64BitMode : Predicate<"Subtarget->isPPC64()">; +def IsBookE : Predicate<"Subtarget->isBookE()">; +def IsNotBookE : Predicate<"!Subtarget->isBookE()">; +def HasOnlyMSYNC : Predicate<"Subtarget->hasOnlyMSYNC()">; +def HasSYNC : Predicate<"!Subtarget->hasOnlyMSYNC()">; +def IsPPC4xx : Predicate<"Subtarget->isPPC4xx()">; +def IsPPC6xx : Predicate<"Subtarget->isPPC6xx()">; +def IsE500 : Predicate<"Subtarget->isE500()">; +def HasSPE : Predicate<"Subtarget->hasSPE()">; +def HasICBT : Predicate<"Subtarget->hasICBT()">; +def HasPartwordAtomics : Predicate<"Subtarget->hasPartwordAtomics()">; +def HasQuadwordAtomics : Predicate<"Subtarget->hasQuadwordAtomics()">; def NoNaNsFPMath - : Predicate<"Subtarget->getTargetMachine().Options.NoNaNsFPMath">, - AssemblerPredicate<(all_of (not FeatureFPU)), "notfpu">; + : Predicate<"Subtarget->getTargetMachine().Options.NoNaNsFPMath">; def NaNsFPMath - : Predicate<"!Subtarget->getTargetMachine().Options.NoNaNsFPMath">, - AssemblerPredicate<(all_of FeatureFPU), "fpu">; -def HasBPERMD : Predicate<"Subtarget->hasBPERMD()">, - AssemblerPredicate<(all_of FeatureBPERMD), "bpermd">; -def HasExtDiv : Predicate<"Subtarget->hasExtDiv()">, - AssemblerPredicate<(all_of FeatureFPU), "fpu">; -def IsISA2_06 : Predicate<"Subtarget->isISA2_06()">, - AssemblerPredicate<(all_of FeatureBPERMD), "bpermd">; -def IsISA2_07 : Predicate<"Subtarget->isISA2_07()">, - AssemblerPredicate<(all_of FeatureExtDiv), "extdiv">; -def IsISA3_0 : Predicate<"Subtarget->isISA3_0()">, - AssemblerPredicate<(all_of FeatureISA2_06), "isa2_06">; -def HasFPU : Predicate<"Subtarget->hasFPU()">, - AssemblerPredicate<(all_of FeatureISA2_07), "isa2_07">; -def PCRelativeMemops : Predicate<"Subtarget->hasPCRelativeMemops()">, - AssemblerPredicate<(all_of FeatureISA3_0), "isa3_0">; -def IsNotISA3_1 : Predicate<"!Subtarget->isISA3_1()">, - AssemblerPredicate<(all_of (not FeatureISA3_1)), "notisa3_1">; + : Predicate<"!Subtarget->getTargetMachine().Options.NoNaNsFPMath">; +def HasBPERMD : Predicate<"Subtarget->hasBPERMD()">; +def HasExtDiv : Predicate<"Subtarget->hasExtDiv()">; +def IsISA2_06 : Predicate<"Subtarget->isISA2_06()">; +def IsISA2_07 : Predicate<"Subtarget->isISA2_07()">; +def IsISA3_0 : Predicate<"Subtarget->isISA3_0()">; +def HasFPU : Predicate<"Subtarget->hasFPU()">; +def PCRelativeMemops : Predicate<"Subtarget->hasPCRelativeMemops()">; +def IsNotISA3_1 : Predicate<"!Subtarget->isISA3_1()">; // AIX assembler may not be modern enough to support some extended mne. -def ModernAs: Predicate<"!Subtarget->isAIXABI() || Subtarget->HasModernAIXAs">, +def ModernAs: Predicate<"!Subtarget->isAIXABI() || Subtarget->HasModernAIXAs">, AssemblerPredicate<(any_of (not AIXOS), FeatureModernAIXAs)>; -def IsAIX : Predicate<"Subtarget->isAIXABI()">, - AssemblerPredicate<(all_of AIXOS), "aix">; -def NotAIX : Predicate<"!Subtarget->isAIXABI()">, - AssemblerPredicate<(all_of (not AIXOS)), "notaix">; -def IsISAFuture : Predicate<"Subtarget->isISAFuture()">, - AssemblerPredicate<(all_of FeatureISAFuture), "isafuture">; -def IsNotISAFuture : Predicate<"!Subtarget->isISAFuture()">, - AssemblerPredicate<(all_of (not FeatureISAFuture)), "notisafuture">; +def IsAIX : Predicate<"Subtarget->isAIXABI()">; +def NotAIX : Predicate<"!Subtarget->isAIXABI()">; +def IsISAFuture : Predicate<"Subtarget->isISAFuture()">; +def IsNotISAFuture : Predicate<"!Subtarget->isISAFuture()">; //===----------------------------------------------------------------------===// // PowerPC Multiclass Definitions. @@ -1590,20 +1563,16 @@ def DCBA : DCB_Form<758, 0, (outs), (ins memrr:$dst), "dcba $dst", def DCBI : DCB_Form<470, 0, (outs), (ins memrr:$dst), "dcbi $dst", IIC_LdStDCBF, [(int_ppc_dcbi xoaddr:$dst)]>, PPC970_DGroup_Single; -let mayLoad = 1 in def DCBST : DCB_Form<54, 0, (outs), (ins memrr:$dst), "dcbst $dst", IIC_LdStDCBF, [(int_ppc_dcbst xoaddr:$dst)]>, PPC970_DGroup_Single; -let mayStore = 1 in { def DCBZ : DCB_Form<1014, 0, (outs), (ins memrr:$dst), "dcbz $dst", IIC_LdStDCBF, [(int_ppc_dcbz xoaddr:$dst)]>, PPC970_DGroup_Single; def DCBZL : DCB_Form<1014, 1, (outs), (ins memrr:$dst), "dcbzl $dst", IIC_LdStDCBF, [(int_ppc_dcbzl xoaddr:$dst)]>, PPC970_DGroup_Single; -} -let mayLoad = 1 in def DCBF : DCB_Form_hint<86, (outs), (ins u3imm:$TH, memrr:$dst), "dcbf $dst, $TH", IIC_LdStDCBF, []>, PPC970_DGroup_Single; @@ -1621,12 +1590,10 @@ def ICBLC : XForm_icbt<31, 230, (outs), (ins u4imm:$CT, memrr:$src), "icblc $CT, $src", IIC_LdStStore>, Requires<[HasICBT]>; def ICBLQ : XForm_icbt<31, 198, (outs), (ins u4imm:$CT, memrr:$src), "icblq. $CT, $src", IIC_LdStLoad>, Requires<[HasICBT]>; -let mayLoad = 1 in { def ICBT : XForm_icbt<31, 22, (outs), (ins u4imm:$CT, memrr:$src), "icbt $CT, $src", IIC_LdStLoad>, Requires<[HasICBT]>; def ICBTLS : XForm_icbt<31, 486, (outs), (ins u4imm:$CT, memrr:$src), "icbtls $CT, $src", IIC_LdStLoad>, Requires<[HasICBT]>; -} def : Pat<(int_ppc_dcbt xoaddr:$dst), (DCBT 0, xoaddr:$dst)>; @@ -1846,7 +1813,7 @@ def POPCNTB : XForm_11<31, 122, (outs gprc:$rA), (ins gprc:$rS), // // Unindexed (r+i) Loads. -let PPC970_Unit = 2, mayLoad = 1 in { +let PPC970_Unit = 2 in { def LBZ : DForm_1<34, (outs gprc:$rD), (ins memri:$src), "lbz $rD, $src", IIC_LdStLoad, [(set i32:$rD, (zextloadi8 DForm:$src))]>, ZExt32To64, @@ -2057,7 +2024,7 @@ def : Pat<(pre_store f64:$rS, iPTR:$ptrreg, iaddroff:$ptroff), (STFDU $rS, iaddroff:$ptroff, $ptrreg)>; // Indexed (r+r) Stores. -let PPC970_Unit = 2, mayStore = 1 in { +let PPC970_Unit = 2 in { def STBX : XForm_8_memOp<31, 215, (outs), (ins gprc:$rS, memrr:$dst), "stbx $rS, $dst", IIC_LdStStore, [(truncstorei8 i32:$rS, XForm:$dst)]>, @@ -4089,7 +4056,6 @@ def STSWI : XForm_base_r3xo_memOp<31, 725, (outs), def ISYNC : XLForm_2_ext<19, 150, 0, 0, 0, (outs), (ins), "isync", IIC_SprISYNC, []>; -let mayLoad = 1 in def ICBI : XForm_1a<31, 982, (outs), (ins memrr:$src), "icbi $src", IIC_LdStICBI, []>; @@ -4258,7 +4224,6 @@ def NAP : XLForm_1_np<19, 434, (outs), (ins), "nap", IIC_BrB, []>; def ATTN : XForm_attn<0, 256, (outs), (ins), "attn", IIC_BrB>; -let mayLoad = 1 in { def LBZCIX : XForm_base_r3xo_memOp<31, 853, (outs gprc:$RST), (ins gprc:$A, gprc:$B), "lbzcix $RST, $A, $B", IIC_LdStLoad, []>; @@ -4271,9 +4236,7 @@ def LWZCIX : XForm_base_r3xo_memOp<31, 789, (outs gprc:$RST), def LDCIX : XForm_base_r3xo_memOp<31, 885, (outs gprc:$RST), (ins gprc:$A, gprc:$B), "ldcix $RST, $A, $B", IIC_LdStLoad, []>; -} -let mayStore = 1 in { def STBCIX : XForm_base_r3xo_memOp<31, 981, (outs), (ins gprc:$RST, gprc:$A, gprc:$B), "stbcix $RST, $A, $B", IIC_LdStLoad, []>; @@ -4286,11 +4249,9 @@ def STWCIX : XForm_base_r3xo_memOp<31, 917, (outs), def STDCIX : XForm_base_r3xo_memOp<31, 1013, (outs), (ins gprc:$RST, gprc:$A, gprc:$B), "stdcix $RST, $A, $B", IIC_LdStLoad, []>; -} // External PID Load Store Instructions -let mayLoad = 1 in { def LBEPX : XForm_1<31, 95, (outs gprc:$rD), (ins memrr:$src), "lbepx $rD, $src", IIC_LdStLoad, []>, Requires<[IsE500]>; @@ -4306,9 +4267,7 @@ def LHEPX : XForm_1<31, 287, (outs gprc:$rD), (ins memrr:$src), def LWEPX : XForm_1<31, 31, (outs gprc:$rD), (ins memrr:$src), "lwepx $rD, $src", IIC_LdStLoad, []>, Requires<[IsE500]>; -} -let mayStore = 1 in { def STBEPX : XForm_8<31, 223, (outs), (ins gprc:$rS, memrr:$dst), "stbepx $rS, $dst", IIC_LdStStore, []>, Requires<[IsE500]>; @@ -4324,7 +4283,6 @@ def STHEPX : XForm_8<31, 415, (outs), (ins gprc:$rS, memrr:$dst), def STWEPX : XForm_8<31, 159, (outs), (ins gprc:$rS, memrr:$dst), "stwepx $rS, $dst", IIC_LdStStore, []>, Requires<[IsE500]>; -} def DCBFEP : DCB_Form<127, 0, (outs), (ins memrr:$dst), "dcbfep $dst", IIC_LdStDCBF, []>, Requires<[IsE500]>; @@ -4346,7 +4304,6 @@ def DCBZEP : DCB_Form<1023, 0, (outs), (ins memrr:$dst), "dcbzep $dst", def DCBZLEP : DCB_Form<1023, 1, (outs), (ins memrr:$dst), "dcbzlep $dst", IIC_LdStDCBF, []>, Requires<[IsE500]>; -let mayLoad = 1 in def ICBIEP : XForm_1a<31, 991, (outs), (ins memrr:$src), "icbiep $src", IIC_LdStICBI, []>, Requires<[IsE500]>; @@ -4396,11 +4353,9 @@ def DCBTSTCT : PPCAsmPseudo<"dcbtstct $dst, $TH", (ins memrr:$dst, u5imm:$TH)>; def DCBTSTDS : PPCAsmPseudo<"dcbtstds $dst, $TH", (ins memrr:$dst, u5imm:$TH)>; def DCBTSTT : PPCAsmPseudo<"dcbtstt $dst", (ins memrr:$dst)>; -let mayLoad = 1 in { def DCBFx : PPCAsmPseudo<"dcbf $dst", (ins memrr:$dst)>; def DCBFL : PPCAsmPseudo<"dcbfl $dst", (ins memrr:$dst)>; def DCBFLP : PPCAsmPseudo<"dcbflp $dst", (ins memrr:$dst)>; -} def : Pat<(int_ppc_isync), (ISYNC)>; def : Pat<(int_ppc_dcbfl xoaddr:$dst), @@ -4590,7 +4545,6 @@ def : InstAlias<"tlbwehi $RS, $A", (TLBWE2 gprc:$RS, gprc:$A, 0)>, def : InstAlias<"tlbwelo $RS, $A", (TLBWE2 gprc:$RS, gprc:$A, 1)>, Requires<[IsPPC4xx]>; -let mayLoad = 1 in def LAx : PPCAsmPseudo<"la $rA, $addr", (ins gprc:$rA, memri:$addr)>; def SUBI : PPCAsmPseudo<"subi $rA, $rB, $imm", @@ -5206,10 +5160,3 @@ def : Pat<(int_ppc_dcbtt ForceXForm:$dst), def : Pat<(int_ppc_stfiw ForceXForm:$dst, f64:$XT), (STFIWX f64:$XT, ForceXForm:$dst)>; - -#define CAPSTONE_DEPR_INTRINSICS -include "PPCDeprecated.td" -#define CAPSTONE_DEPR_INSTR -#define CAPSTONE_PS_INSTR -include "PPCDeprecated.td" -include "PPCInstrPairedSingle.td" diff --git a/llvm/lib/Target/PowerPC/PPCInstrSPE.td b/llvm/lib/Target/PowerPC/PPCInstrSPE.td index 471cb9058986..1e0cc7f348b6 100644 --- a/llvm/lib/Target/PowerPC/PPCInstrSPE.td +++ b/llvm/lib/Target/PowerPC/PPCInstrSPE.td @@ -460,7 +460,6 @@ def EVFSTSTLT : EVXForm_3<669, (outs crrc:$crD), (ins sperc:$RA, sperc:$RB "evfststlt $crD, $RA, $RB", IIC_VecGeneral, []>; } -let mayLoad = 1 in { def EVLDD : EVXForm_D<769, (outs sperc:$RT), (ins spe8dis:$dst), "evldd $RT, $dst", IIC_LdStLoad, [(set f64:$RT, (load iaddr:$dst))]>; @@ -509,7 +508,6 @@ def EVLWWSPLAT : EVXForm_D<793, (outs sperc:$RT), (ins spe4dis:$dst), "evlwwsplat $RT, $dst", IIC_LdStLoad, []>; def EVLWWSPLATX : EVXForm_1<792, (outs sperc:$RT), (ins memrr:$src), "evlwwsplatx $RT, $src", IIC_LdStLoad, []>; -} // mayLoad = 1 def EVMERGEHI : EVXForm_1<556, (outs sperc:$RT), (ins sperc:$RA, sperc:$RB), "evmergehi $RT, $RA, $RB", IIC_VecGeneral, []>; @@ -753,7 +751,6 @@ def EVSRWU : EVXForm_1<544, (outs sperc:$RT), (ins sperc:$RA, sperc:$RB) "evsrwu $RT, $RA, $RB", IIC_VecGeneral, []>; -let mayStore = 1 in { def EVSTDD : EVXForm_D<801, (outs), (ins sperc:$RT, spe8dis:$dst), "evstdd $RT, $dst", IIC_LdStStore, [(store f64:$RT, iaddr:$dst)]>; @@ -786,7 +783,6 @@ def EVSTWWO : EVXForm_D<829, (outs), (ins sperc:$RT, spe4dis:$dst), "evstwwo $RT, $dst", IIC_LdStStore, []>; def EVSTWWOX : EVXForm_1<828, (outs), (ins sperc:$RT, memrr:$dst), "evstwwox $RT, $dst", IIC_LdStStore, []>; -} // mayStore = 1 def EVSUBFSSIAAW : EVXForm_2<1219, (outs sperc:$RT), (ins sperc:$RA), "evsubfssiaaw $RT, $RA", IIC_VecComplex, []>; @@ -806,7 +802,6 @@ def EVXOR : EVXForm_1<534, (outs sperc:$RT), (ins sperc:$RA, sperc:$RB) []>; let isAsmParserOnly = 1 in { -let mayLoad = 1 in { // Identical to the integer Load/Stores, but to handle floats def SPELWZ : DForm_1<32, (outs spe4rc:$rD), (ins memri:$src), "lwz $rD, $src", IIC_LdStLoad, @@ -814,9 +809,6 @@ def SPELWZ : DForm_1<32, (outs spe4rc:$rD), (ins memri:$src), def SPELWZX : XForm_1<31, 23, (outs spe4rc:$rD), (ins memrr:$src), "lwzx $rD, $src", IIC_LdStLoad, [(set f32:$rD, (load xaddr:$src))]>; -} - -let mayStore = 1 in { def SPESTW : DForm_1<36, (outs), (ins spe4rc:$rS, memri:$src), "stw $rS, $src", IIC_LdStStore, [(store f32:$rS, iaddr:$src)]>; @@ -824,7 +816,6 @@ def SPESTWX : XForm_8<31, 151, (outs), (ins spe4rc:$rS, memrr:$dst), "stwx $rS, $dst", IIC_LdStStore, [(store f32:$rS, xaddr:$dst)]>; } -} } // HasSPE diff --git a/llvm/lib/Target/PowerPC/PPCRegisterInfo.td b/llvm/lib/Target/PowerPC/PPCRegisterInfo.td index 23ba141bc092..700baa5733b4 100644 --- a/llvm/lib/Target/PowerPC/PPCRegisterInfo.td +++ b/llvm/lib/Target/PowerPC/PPCRegisterInfo.td @@ -745,7 +745,6 @@ def absdirectbrtarget : Operand { let PrintMethod = "printAbsBranchOperand"; let EncoderMethod = "getAbsDirectBrEncoding"; let ParserMatchClass = PPCDirectBrAsmOperand; - let OperandType = "OPERAND_IMMEDIATE"; } def PPCCondBrAsmOperand : AsmOperandClass { let Name = "CondBr"; let PredicateMethod = "isCondBr"; @@ -762,7 +761,6 @@ def abscondbrtarget : Operand { let PrintMethod = "printAbsBranchOperand"; let EncoderMethod = "getAbsCondBrEncoding"; let ParserMatchClass = PPCCondBrAsmOperand; - let OperandType = "OPERAND_IMMEDIATE"; } def calltarget : Operand { let PrintMethod = "printBranchOperand"; @@ -775,7 +773,6 @@ def abscalltarget : Operand { let PrintMethod = "printAbsBranchOperand"; let EncoderMethod = "getAbsDirectBrEncoding"; let ParserMatchClass = PPCDirectBrAsmOperand; - let OperandType = "OPERAND_IMMEDIATE"; } def PPCCRBitMaskOperand : AsmOperandClass { let Name = "CRBitMask"; let PredicateMethod = "isCRBitMask"; @@ -802,7 +799,6 @@ def PPCDispRI34Operand : AsmOperandClass { } def dispRI34 : Operand { let ParserMatchClass = PPCDispRI34Operand; - let OperandType = "OPERAND_IMMEDIATE"; } def memri34 : Operand { // memri, imm is a 34-bit value. let PrintMethod = "printMemRegImm34"; @@ -833,7 +829,6 @@ def PPCDispRIOperand : AsmOperandClass { } def dispRI : Operand { let ParserMatchClass = PPCDispRIOperand; - let OperandType = "OPERAND_IMMEDIATE"; } def PPCDispRIXOperand : AsmOperandClass { let Name = "DispRIX"; let PredicateMethod = "isS16ImmX4"; @@ -841,7 +836,6 @@ def PPCDispRIXOperand : AsmOperandClass { } def dispRIX : Operand { let ParserMatchClass = PPCDispRIXOperand; - let OperandType = "OPERAND_IMMEDIATE"; } def PPCDispRIHashOperand : AsmOperandClass { let Name = "DispRIHash"; let PredicateMethod = "isHashImmX8"; @@ -849,7 +843,6 @@ def PPCDispRIHashOperand : AsmOperandClass { } def dispRIHash : Operand { let ParserMatchClass = PPCDispRIHashOperand; - let OperandType = "OPERAND_IMMEDIATE"; } def PPCDispRIX16Operand : AsmOperandClass { let Name = "DispRIX16"; let PredicateMethod = "isS16ImmX16"; @@ -857,7 +850,6 @@ def PPCDispRIX16Operand : AsmOperandClass { } def dispRIX16 : Operand { let ParserMatchClass = PPCDispRIX16Operand; - let OperandType = "OPERAND_IMMEDIATE"; } def PPCDispSPE8Operand : AsmOperandClass { let Name = "DispSPE8"; let PredicateMethod = "isU8ImmX8"; @@ -865,7 +857,6 @@ def PPCDispSPE8Operand : AsmOperandClass { } def dispSPE8 : Operand { let ParserMatchClass = PPCDispSPE8Operand; - let OperandType = "OPERAND_IMMEDIATE"; } def PPCDispSPE4Operand : AsmOperandClass { let Name = "DispSPE4"; let PredicateMethod = "isU7ImmX4"; @@ -873,7 +864,6 @@ def PPCDispSPE4Operand : AsmOperandClass { } def dispSPE4 : Operand { let ParserMatchClass = PPCDispSPE4Operand; - let OperandType = "OPERAND_IMMEDIATE"; } def PPCDispSPE2Operand : AsmOperandClass { let Name = "DispSPE2"; let PredicateMethod = "isU6ImmX2"; @@ -881,7 +871,6 @@ def PPCDispSPE2Operand : AsmOperandClass { } def dispSPE2 : Operand { let ParserMatchClass = PPCDispSPE2Operand; - let OperandType = "OPERAND_IMMEDIATE"; } def memri : Operand { @@ -1071,6 +1060,3 @@ def PPCRegDMRpRCAsmOperand : AsmOperandClass { def dmrp : RegisterOperand { let ParserMatchClass = PPCRegDMRpRCAsmOperand; } - -#define CAPSTONE_DEPR_REGS -include "PPCDeprecated.td" diff --git a/llvm/lib/Target/PowerPC/PPCScheduleP10.td b/llvm/lib/Target/PowerPC/PPCScheduleP10.td index ad466a829a93..f89ef735a367 100644 --- a/llvm/lib/Target/PowerPC/PPCScheduleP10.td +++ b/llvm/lib/Target/PowerPC/PPCScheduleP10.td @@ -36,7 +36,7 @@ def P10Model : SchedMachineModel { let CompleteModel = 1; // Do not support SPE (Signal Procesing Engine) on Power 10. - let UnsupportedFeatures = [HasSPE, IsE500, IsBookE, IsISAFuture, HasQPX, HasPS]; + let UnsupportedFeatures = [HasSPE, IsE500, IsBookE, IsISAFuture]; } let SchedModel = P10Model in { diff --git a/llvm/lib/Target/PowerPC/PPCScheduleP9.td b/llvm/lib/Target/PowerPC/PPCScheduleP9.td index 9ffcd4ddd431..e9f4daa62de3 100644 --- a/llvm/lib/Target/PowerPC/PPCScheduleP9.td +++ b/llvm/lib/Target/PowerPC/PPCScheduleP9.td @@ -43,8 +43,7 @@ def P9Model : SchedMachineModel { // instructions introduced after ISA 3.0. let UnsupportedFeatures = [HasSPE, PrefixInstrs, MMA, PairedVectorMemops, - PCRelativeMemops, IsISA3_1, IsISAFuture, - HasQPX, HasPS]; + PCRelativeMemops, IsISA3_1, IsISAFuture]; } let SchedModel = P9Model in { diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp index 5dca792dc89a..0b3059df124e 100644 --- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp +++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp @@ -8488,11 +8488,13 @@ SystemZTargetLowering::emitMemMemWrapper(MachineInstr &MI, .addReg(RemSrcReg).addImm(SrcDisp); MBB->addSuccessor(AllDoneMBB); MBB = AllDoneMBB; - if (EndMBB) { + if (Opcode != SystemZ::MVC) { EXRL_MIB.addReg(SystemZ::CC, RegState::ImplicitDefine); - MBB->addLiveIn(SystemZ::CC); + if (EndMBB) + MBB->addLiveIn(SystemZ::CC); } } + MF.getProperties().reset(MachineFunctionProperties::Property::NoPHIs); } // Handle any remaining bytes with straight-line code. diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index 1606413c382b..df9aaddd5729 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -1557,7 +1557,19 @@ void X86FrameLowering::emitPrologue(MachineFunction &MF, Fn.arg_size() == 2) { StackSize += 8; MFI.setStackSize(StackSize); - emitSPUpdate(MBB, MBBI, DL, -8, /*InEpilogue=*/false); + + // Update the stack pointer by pushing a register. This is the instruction + // emitted that would be end up being emitted by a call to `emitSPUpdate`. + // Hard-coding the update to a push avoids emitting a second + // `STACKALLOC_W_PROBING` instruction in the save block: We know that stack + // probing isn't needed anyways for an 8-byte update. + // Pushing a register leaves us in a similar situation to a regular + // function call where we know that the address at (rsp-8) is writeable. + // That way we avoid any off-by-ones with stack probing for additional + // stack pointer updates later on. + BuildMI(MBB, MBBI, DL, TII.get(X86::PUSH64r)) + .addReg(X86::RAX, RegState::Undef) + .setMIFlag(MachineInstr::FrameSetup); } // If this is x86-64 and the Red Zone is not disabled, if we are a leaf diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index cf17c51b04fc..e43b33eed470 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -499,7 +499,7 @@ X86TargetLowering::X86TargetLowering(const X86TargetMachine &TM, } if (Subtarget.hasSSEPrefetch() || Subtarget.hasThreeDNow()) - setOperationAction(ISD::PREFETCH , MVT::Other, Legal); + setOperationAction(ISD::PREFETCH , MVT::Other, Custom); setOperationAction(ISD::ATOMIC_FENCE , MVT::Other, Custom); @@ -2195,6 +2195,7 @@ X86TargetLowering::X86TargetLowering(const X86TargetMachine &TM, setOperationAction(ISD::FMUL, VT, Expand); setOperationAction(ISD::FDIV, VT, Expand); setOperationAction(ISD::BUILD_VECTOR, VT, Custom); + setOperationAction(ISD::VECTOR_SHUFFLE, VT, Custom); } addLegalFPImmediate(APFloat::getZero(APFloat::BFloat())); } @@ -2207,6 +2208,7 @@ X86TargetLowering::X86TargetLowering(const X86TargetMachine &TM, setOperationAction(ISD::FMUL, MVT::v32bf16, Expand); setOperationAction(ISD::FDIV, MVT::v32bf16, Expand); setOperationAction(ISD::BUILD_VECTOR, MVT::v32bf16, Custom); + setOperationAction(ISD::VECTOR_SHUFFLE, MVT::v32bf16, Custom); } if (!Subtarget.useSoftFloat() && Subtarget.hasVLX()) { @@ -18773,11 +18775,11 @@ static SDValue lower256BitShuffle(const SDLoc &DL, ArrayRef Mask, MVT VT, return DAG.getBitcast(VT, DAG.getVectorShuffle(FpVT, DL, V1, V2, Mask)); } - if (VT == MVT::v16f16) { - V1 = DAG.getBitcast(MVT::v16i16, V1); - V2 = DAG.getBitcast(MVT::v16i16, V2); - return DAG.getBitcast(MVT::v16f16, - DAG.getVectorShuffle(MVT::v16i16, DL, V1, V2, Mask)); + if (VT == MVT::v16f16 || VT.getVectorElementType() == MVT::bf16) { + MVT IVT = VT.changeVectorElementTypeToInteger(); + V1 = DAG.getBitcast(IVT, V1); + V2 = DAG.getBitcast(IVT, V2); + return DAG.getBitcast(VT, DAG.getVectorShuffle(IVT, DL, V1, V2, Mask)); } switch (VT.SimpleTy) { @@ -33093,6 +33095,18 @@ static SDValue LowerCVTPS2PH(SDValue Op, SelectionDAG &DAG) { return DAG.getNode(ISD::CONCAT_VECTORS, dl, VT, Lo, Hi); } +static SDValue LowerPREFETCH(SDValue Op, const X86Subtarget &Subtarget, + SelectionDAG &DAG) { + unsigned IsData = cast(Op.getOperand(4))->getZExtValue(); + + // We don't support non-data prefetch without PREFETCHI. + // Just preserve the chain. + if (!IsData && !Subtarget.hasPREFETCHI()) + return Op.getOperand(0); + + return Op; +} + static StringRef getInstrStrFromOpNo(const SmallVectorImpl &AsmStrs, unsigned OpNo) { const APInt Operand(32, OpNo); @@ -33294,6 +33308,7 @@ SDValue X86TargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const { case ISD::GC_TRANSITION_END: return LowerGC_TRANSITION(Op, DAG); case ISD::ADDRSPACECAST: return LowerADDRSPACECAST(Op, DAG); case X86ISD::CVTPS2PH: return LowerCVTPS2PH(Op, DAG); + case ISD::PREFETCH: return LowerPREFETCH(Op, Subtarget, DAG); } } diff --git a/llvm/lib/Target/X86/X86InstrAVX512.td b/llvm/lib/Target/X86/X86InstrAVX512.td index 6da4dd2b942c..888e69ac4ac0 100644 --- a/llvm/lib/Target/X86/X86InstrAVX512.td +++ b/llvm/lib/Target/X86/X86InstrAVX512.td @@ -12969,6 +12969,27 @@ let Predicates = [HasBF16, HasVLX] in { (VCVTNEPS2BF16Z256rr VR256X:$src)>; def : Pat<(v8bf16 (int_x86_vcvtneps2bf16256 (loadv8f32 addr:$src))), (VCVTNEPS2BF16Z256rm addr:$src)>; + + def : Pat<(v8bf16 (X86VBroadcastld16 addr:$src)), + (VPBROADCASTWZ128rm addr:$src)>; + def : Pat<(v16bf16 (X86VBroadcastld16 addr:$src)), + (VPBROADCASTWZ256rm addr:$src)>; + + def : Pat<(v8bf16 (X86VBroadcast (v8bf16 VR128X:$src))), + (VPBROADCASTWZ128rr VR128X:$src)>; + def : Pat<(v16bf16 (X86VBroadcast (v8bf16 VR128X:$src))), + (VPBROADCASTWZ256rr VR128X:$src)>; + + // TODO: No scalar broadcast due to we don't support legal scalar bf16 so far. +} + +let Predicates = [HasBF16] in { + def : Pat<(v32bf16 (X86VBroadcastld16 addr:$src)), + (VPBROADCASTWZrm addr:$src)>; + + def : Pat<(v32bf16 (X86VBroadcast (v8bf16 VR128X:$src))), + (VPBROADCASTWZrr VR128X:$src)>; + // TODO: No scalar broadcast due to we don't support legal scalar bf16 so far. } let Constraints = "$src1 = $dst" in { diff --git a/llvm/test/CodeGen/Hexagon/bitmanip.ll b/llvm/test/CodeGen/Hexagon/bitmanip.ll index 2044a2fdd083..9ce7f0576506 100644 --- a/llvm/test/CodeGen/Hexagon/bitmanip.ll +++ b/llvm/test/CodeGen/Hexagon/bitmanip.ll @@ -1,135 +1,370 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 ; RUN: llc -march=hexagon < %s | FileCheck %s -; CHECK-LABEL: popcount_16 -; CHECK: zxth -; CHECK: popcount -define i16 @popcount_16(i16 %p) #0 { - %t = call i16 @llvm.ctpop.i16(i16 %p) #0 - ret i16 %t +define i16 @popcount_i16(i16 %a0) #0 { +; CHECK-LABEL: popcount_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: r0 = zxth(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = popcount(r1:0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.ctpop.i16(i16 %a0) #1 + ret i16 %v0 } -; CHECK-LABEL: popcount_32 -; CHECK: popcount -define i32 @popcount_32(i32 %p) #0 { - %t = call i32 @llvm.ctpop.i32(i32 %p) #0 - ret i32 %t +define i32 @popcount_i32(i32 %a0) #0 { +; CHECK-LABEL: popcount_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = popcount(r1:0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.ctpop.i32(i32 %a0) #1 + ret i32 %v0 } -; CHECK-LABEL: popcount_64 -; CHECK: popcount -define i64 @popcount_64(i64 %p) #0 { - %t = call i64 @llvm.ctpop.i64(i64 %p) #0 - ret i64 %t +define i64 @popcount_i64(i64 %a0) #0 { +; CHECK-LABEL: popcount_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = popcount(r1:0) +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.ctpop.i64(i64 %a0) #1 + ret i64 %v0 } -; CHECK-LABEL: ctlz_16 -; CHECK: [[REG0:r[0-9]+]] = zxth -; CHECK: [[REG1:r[0-9]+]] = cl0([[REG0]]) -; CHECK: add([[REG1]],#-16) -define i16 @ctlz_16(i16 %p) #0 { - %t = call i16 @llvm.ctlz.i16(i16 %p, i1 true) #0 - ret i16 %t +define i16 @ctlz_i16(i16 %a0) #0 { +; CHECK-LABEL: ctlz_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = zxth(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = cl0(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = add(r0,#-16) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.ctlz.i16(i16 %a0, i1 true) #1 + ret i16 %v0 } -; CHECK-LABEL: ctlz_32 -; CHECK: cl0 -define i32 @ctlz_32(i32 %p) #0 { - %t = call i32 @llvm.ctlz.i32(i32 %p, i1 true) #0 - ret i32 %t +define i32 @ctlz_i32(i32 %a0) #0 { +; CHECK-LABEL: ctlz_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = cl0(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.ctlz.i32(i32 %a0, i1 true) #1 + ret i32 %v0 } -; CHECK-LABEL: ctlz_64 -; CHECK: cl0 -define i64 @ctlz_64(i64 %p) #0 { - %t = call i64 @llvm.ctlz.i64(i64 %p, i1 true) #0 - ret i64 %t +define i64 @ctlz_i64(i64 %a0) #0 { +; CHECK-LABEL: ctlz_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = cl0(r1:0) +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.ctlz.i64(i64 %a0, i1 true) #1 + ret i64 %v0 } -; CHECK-LABEL: cttz_16 -; CHECK: ct0 -define i16 @cttz_16(i16 %p) #0 { - %t = call i16 @llvm.cttz.i16(i16 %p, i1 true) #0 - ret i16 %t +define i16 @cttz_i16(i16 %a0) #0 { +; CHECK-LABEL: cttz_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = ct0(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.cttz.i16(i16 %a0, i1 true) #1 + ret i16 %v0 } -; CHECK-LABEL: cttz_32 -; CHECK: ct0 -define i32 @cttz_32(i32 %p) #0 { - %t = call i32 @llvm.cttz.i32(i32 %p, i1 true) #0 - ret i32 %t +define i32 @cttz_i32(i32 %a0) #0 { +; CHECK-LABEL: cttz_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = ct0(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.cttz.i32(i32 %a0, i1 true) #1 + ret i32 %v0 } -; CHECK-LABEL: cttz_64 -; CHECK: ct0 -define i64 @cttz_64(i64 %p) #0 { - %t = call i64 @llvm.cttz.i64(i64 %p, i1 true) #0 - ret i64 %t +define i64 @cttz_i64(i64 %a0) #0 { +; CHECK-LABEL: cttz_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = ct0(r1:0) +; CHECK-NEXT: r1 = #0 +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.cttz.i64(i64 %a0, i1 true) #1 + ret i64 %v0 } -; CHECK-LABEL: brev_16 -; CHECK: [[REG:r[0-9]+]] = brev -; CHECK: lsr([[REG]],#16) -define i16 @brev_16(i16 %p) #0 { - %t = call i16 @llvm.bitreverse.i16(i16 %p) #0 - ret i16 %t +define i16 @bswap_i16(i16 %a0) #0 { +; CHECK-LABEL: bswap_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = lsr(r0,#16) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.bswap.i16(i16 %a0) #1 + ret i16 %v0 } -; CHECK-LABEL: brev_32 -; CHECK: brev -define i32 @brev_32(i32 %p) #0 { - %t = call i32 @llvm.bitreverse.i32(i32 %p) #0 - ret i32 %t +define i32 @bswap_i32(i32 %a0) #0 { +; CHECK-LABEL: bswap_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.bswap.i32(i32 %a0) #1 + ret i32 %v0 } -; CHECK-LABEL: brev_64 -; CHECK: brev -define i64 @brev_64(i64 %p) #0 { - %t = call i64 @llvm.bitreverse.i64(i64 %p) #0 - ret i64 %t +define i64 @bswap_i64(i64 %a0) #0 { +; CHECK-LABEL: bswap_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r2 = swiz(r1) +; CHECK-NEXT: r3 = swiz(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1:0 = combine(r3,r2) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.bswap.i64(i64 %a0) #1 + ret i64 %v0 } -; CHECK-LABEL: bswap_16 -; CHECK: [[REG:r[0-9]+]] = swiz -; CHECK: lsr([[REG]],#16) -define i16 @bswap_16(i16 %p) #0 { - %t = call i16 @llvm.bswap.i16(i16 %p) #0 - ret i16 %t +define <2 x i16> @bswap_v2i16(<2 x i16> %a0) #0 { +; CHECK-LABEL: bswap_v2i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = combine(r0.l,r0.h) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <2 x i16> @llvm.bswap.v2i16(<2 x i16> %a0) + ret <2 x i16> %v0 } -; CHECK-LABEL: bswap_32 -; CHECK: swiz -define i32 @bswap_32(i32 %p) #0 { - %t = call i32 @llvm.bswap.i32(i32 %p) #0 - ret i32 %t +define <4 x i16> @bswap_v4i16(<4 x i16> %a0) #0 { +; CHECK-LABEL: bswap_v4i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r3:2 = vlsrh(r1:0,#8) +; CHECK-NEXT: r5:4 = vaslh(r1:0,#8) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1:0 = or(r3:2,r5:4) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <4 x i16> @llvm.bswap.v4i16(<4 x i16> %a0) + ret <4 x i16> %v0 } -; CHECK-LABEL: bswap_64 -; CHECK: swiz -; CHECK: swiz -; CHECK: combine -define i64 @bswap_64(i64 %p) #0 { - %t = call i64 @llvm.bswap.i64(i64 %p) #0 - ret i64 %t +define <2 x i32> @bswap_v2i32(<2 x i32> %a0) #0 { +; CHECK-LABEL: bswap_v2i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: r1 = swiz(r1) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <2 x i32> @llvm.bswap.v2i32(<2 x i32> %a0) + ret <2 x i32> %v0 } -declare i16 @llvm.ctpop.i16(i16) #0 -declare i32 @llvm.ctpop.i32(i32) #0 -declare i64 @llvm.ctpop.i64(i64) #0 +define i16 @brev_i16(i16 %a0) #0 { +; CHECK-LABEL: brev_i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = brev(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = lsr(r0,#16) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i16 @llvm.bitreverse.i16(i16 %a0) #1 + ret i16 %v0 +} + +define i32 @brev_i32(i32 %a0) #0 { +; CHECK-LABEL: brev_i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = brev(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i32 @llvm.bitreverse.i32(i32 %a0) #1 + ret i32 %v0 +} + +define i64 @brev_i64(i64 %a0) #0 { +; CHECK-LABEL: brev_i64: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r1:0 = brev(r1:0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call i64 @llvm.bitreverse.i64(i64 %a0) #1 + ret i64 %v0 +} + +define <4 x i8> @brev_v4i8(<4 x i8> %a0) #0 { +; CHECK-LABEL: brev_v4i8: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = brev(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r0) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <4 x i8> @llvm.bitreverse.v4i8(<4 x i8> %a0) + ret <4 x i8> %v0 +} + +define <8 x i8> @brev_v8i8(<8 x i8> %a0) #0 { +; CHECK-LABEL: brev_v8i8: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r3:2 = brev(r1:0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = swiz(r3) +; CHECK-NEXT: r1 = swiz(r2) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <8 x i8> @llvm.bitreverse.v8i8(<8 x i8> %a0) + ret <8 x i8> %v0 +} + +define <2 x i16> @brev_v2i16(<2 x i16> %a0) #0 { +; CHECK-LABEL: brev_v2i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r0 = brev(r0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = combine(r0.l,r0.h) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <2 x i16> @llvm.bitreverse.v2i16(<2 x i16> %a0) + ret <2 x i16> %v0 +} + +define <4 x i16> @brev_v4i16(<4 x i16> %a0) #0 { +; CHECK-LABEL: brev_v4i16: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r3:2 = brev(r1:0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r0 = combine(r3.l,r3.h) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: r1 = combine(r2.l,r2.h) +; CHECK-NEXT: } + %v0 = tail call <4 x i16> @llvm.bitreverse.v4i16(<4 x i16> %a0) + ret <4 x i16> %v0 +} + +define <2 x i32> @brev_v2i32(<2 x i32> %a0) #0 { +; CHECK-LABEL: brev_v2i32: +; CHECK: .cfi_startproc +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: { +; CHECK-NEXT: r3:2 = brev(r1:0) +; CHECK-NEXT: } +; CHECK-NEXT: { +; CHECK-NEXT: r1:0 = combine(r2,r3) +; CHECK-NEXT: jumpr r31 +; CHECK-NEXT: } + %v0 = tail call <2 x i32> @llvm.bitreverse.v2i32(<2 x i32> %a0) + ret <2 x i32> %v0 +} + + +declare i16 @llvm.ctpop.i16(i16) #1 +declare i32 @llvm.ctpop.i32(i32) #1 +declare i64 @llvm.ctpop.i64(i64) #1 + +declare i16 @llvm.ctlz.i16(i16, i1) #1 +declare i32 @llvm.ctlz.i32(i32, i1) #1 +declare i64 @llvm.ctlz.i64(i64, i1) #1 + +declare i16 @llvm.cttz.i16(i16, i1) #1 +declare i32 @llvm.cttz.i32(i32, i1) #1 +declare i64 @llvm.cttz.i64(i64, i1) #1 + +declare i16 @llvm.bswap.i16(i16) #1 +declare i32 @llvm.bswap.i32(i32) #1 +declare i64 @llvm.bswap.i64(i64) #1 + +declare <2 x i16> @llvm.bswap.v2i16(<2 x i16>) #1 +declare <4 x i16> @llvm.bswap.v4i16(<4 x i16>) #1 +declare <2 x i32> @llvm.bswap.v2i32(<2 x i32>) #1 + +declare i16 @llvm.bitreverse.i16(i16) #1 +declare i32 @llvm.bitreverse.i32(i32) #1 +declare i64 @llvm.bitreverse.i64(i64) #1 -declare i16 @llvm.ctlz.i16(i16, i1) #0 -declare i32 @llvm.ctlz.i32(i32, i1) #0 -declare i64 @llvm.ctlz.i64(i64, i1) #0 +declare <4 x i8> @llvm.bitreverse.v4i8(<4 x i8>) #1 +declare <8 x i8> @llvm.bitreverse.v8i8(<8 x i8>) #1 -declare i16 @llvm.cttz.i16(i16, i1) #0 -declare i32 @llvm.cttz.i32(i32, i1) #0 -declare i64 @llvm.cttz.i64(i64, i1) #0 +declare <2 x i16> @llvm.bitreverse.v2i16(<2 x i16>) #1 +declare <4 x i16> @llvm.bitreverse.v4i16(<4 x i16>) #1 +declare <2 x i32> @llvm.bitreverse.v2i32(<2 x i32>) #1 -declare i16 @llvm.bitreverse.i16(i16) #0 -declare i32 @llvm.bitreverse.i32(i32) #0 -declare i64 @llvm.bitreverse.i64(i64) #0 -declare i16 @llvm.bswap.i16(i16) #0 -declare i32 @llvm.bswap.i32(i32) #0 -declare i64 @llvm.bswap.i64(i64) #0 +attributes #0 = { "target-features"="+v68,-long-calls" } +attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) } -attributes #0 = { nounwind readnone } diff --git a/llvm/test/CodeGen/RISCV/rvv/pr61561.ll b/llvm/test/CodeGen/RISCV/rvv/pr61561.ll new file mode 100644 index 000000000000..1478e8bfd3c6 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/rvv/pr61561.ll @@ -0,0 +1,33 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc < %s -mtriple=riscv64 -mattr=+v | FileCheck %s + +define @foo(ptr %p) { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: +; CHECK-NEXT: vl1re16.v v8, (a0) +; CHECK-NEXT: vsetvli a0, zero, e16, m1, ta, ma +; CHECK-NEXT: vsll.vi v8, v8, 3 +; CHECK-NEXT: vsetvli zero, zero, e32, m2, ta, ma +; CHECK-NEXT: vzext.vf2 v10, v8 +; CHECK-NEXT: li a0, 248 +; CHECK-NEXT: vand.vx v8, v10, a0 +; CHECK-NEXT: lui a0, 4 +; CHECK-NEXT: vmv.v.x v10, a0 +; CHECK-NEXT: lui a0, 1 +; CHECK-NEXT: addiw a0, a0, -361 +; CHECK-NEXT: vmacc.vx v10, a0, v8 +; CHECK-NEXT: vsetvli zero, zero, e16, m1, ta, ma +; CHECK-NEXT: vnsrl.wi v8, v10, 15 +; CHECK-NEXT: vsetvli zero, zero, e8, mf2, ta, ma +; CHECK-NEXT: vnsrl.wi v8, v8, 0 +; CHECK-NEXT: ret + %i13 = load , ptr %p, align 2 + %i14 = zext %i13 to + %i15 = shl nuw nsw %i14, shufflevector ( insertelement ( poison, i32 3, i64 0), poison, zeroinitializer) + %i16 = and %i15, shufflevector ( insertelement ( poison, i32 248, i64 0), poison, zeroinitializer) + %i17 = mul nuw nsw %i16, shufflevector ( insertelement ( poison, i32 3735, i64 0), poison, zeroinitializer) + %i18 = add nuw nsw %i17, shufflevector ( insertelement ( poison, i32 16384, i64 0), poison, zeroinitializer) + %i21 = lshr %i18, shufflevector ( insertelement ( poison, i32 15, i64 0), poison, zeroinitializer) + %i22 = trunc %i21 to + ret %i22 +} diff --git a/llvm/test/CodeGen/SystemZ/memmem-exrl-cc.mir b/llvm/test/CodeGen/SystemZ/memmem-exrl-cc.mir new file mode 100644 index 000000000000..972314152bde --- /dev/null +++ b/llvm/test/CodeGen/SystemZ/memmem-exrl-cc.mir @@ -0,0 +1,126 @@ +# RUN: llc -mtriple=s390x-linux-gnu -mcpu=z15 -O3 -start-before=finalize-isel %s -o - \ +# RUN: -verify-machineinstrs | FileCheck %s +# +# Test that the exrl used for the remainder of a memset (after xc loop) is +# not rescheduled incorrectly in a way that interfers with CC liveness. + +--- | + define void @fun(ptr %Src, ptr %Dst, i64 %Len) { ret void } +... + +# CHECK-LABEL: fun: + +# CHECK: exrl %r4, .Ltmp0 +# CHECK-NEXT: cgije + +# CHECK: exrl %r4, .Ltmp1 +# CHECK-NEXT: tmll +# CHECK-NEXT: jne + +# CHECK: .Ltmp1: +# CHECK-NEXT: xc 0(1,%r3), 0(%r2) +# CHECK-NEXT: .Ltmp0: +# CHECK-NEXT: xc 0(1,%r14), 0(%r14) +--- +name: fun +alignment: 16 +tracksRegLiveness: true +registers: + - { id: 0, class: grx32bit } + - { id: 1, class: addr64bit } + - { id: 2, class: addr64bit } + - { id: 3, class: gr64bit } + - { id: 4, class: gr64bit } + - { id: 5, class: grx32bit } + - { id: 6, class: addr64bit } + - { id: 7, class: addr64bit } + - { id: 8, class: gr64bit } + - { id: 9, class: gr128bit } + - { id: 10, class: gr64bit } + - { id: 11, class: gr32bit } + - { id: 12, class: gr32bit } + - { id: 13, class: gr32bit } + - { id: 14, class: addr64bit } + - { id: 15, class: gr64bit } + - { id: 16, class: gr64bit } + - { id: 17, class: addr64bit } + - { id: 18, class: gr64bit } + - { id: 19, class: vr128bit } +liveins: + - { reg: '$r2d', virtual-reg: '%1' } + - { reg: '$r3d', virtual-reg: '%2' } + - { reg: '$r4d', virtual-reg: '%3' } +frameInfo: + maxAlignment: 1 +machineFunctionInfo: {} +jumpTable: + kind: block-address + entries: + - id: 0 + blocks: [ '%bb.1', '%bb.2', '%bb.5', '%bb.6', '%bb.7', '%bb.7', + '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', '%bb.7', + '%bb.2', '%bb.5', '%bb.6', '%bb.7', '%bb.1', '%bb.2', + '%bb.7', '%bb.7', '%bb.5' ] +body: | + bb.0: + liveins: $r2d, $r3d, $r4d + + %3:gr64bit = COPY $r4d + %2:addr64bit = COPY $r3d + %1:addr64bit = COPY $r2d + %4:gr64bit = LGHI 0 + + bb.8: + successors: %bb.1(0x00000000), %bb.2(0x00000000), %bb.5(0x00000000), %bb.6(0x00000000), %bb.7(0x80000000) + + %6:addr64bit = SLLG %4, $noreg, 3 + %7:addr64bit = LARL %jump-table.0 + BI killed %6, 0, killed %7 :: (load (s64) from jump-table) + + bb.1: + successors: + + + bb.2: + successors: %bb.4(0x30000000), %bb.3(0x50000000) + + %8:gr64bit = LG %1, 0, $noreg + %9:gr128bit = FLOGR killed %8, implicit-def dead $cc + %10:gr64bit = COPY %9.subreg_h64 + %11:gr32bit = COPY %10.subreg_l32 + %12:gr32bit = LHIMux 64 + %13:gr32bit = nuw nsw SRK killed %12, killed %11, implicit-def dead $cc + %0:grx32bit = COPY %13 + %14:addr64bit = AGHIK %3, -1, implicit-def dead $cc + XCReg %1, 0, %1, 0, %14, implicit-def dead $cc + XCReg %2, 0, %1, 0, %14, implicit-def dead $cc + TMLMux %13, 7, implicit-def $cc + BRC 15, 8, %bb.4, implicit $cc + J %bb.3 + + bb.3: + successors: + + %16:gr64bit = IMPLICIT_DEF + %15:gr64bit = INSERT_SUBREG %16, %0, %subreg.subreg_l32 + %18:gr64bit = IMPLICIT_DEF + %17:addr64bit = RISBGN %18, killed %15, 54, 185, 3 + %19:vr128bit = VGBM 0 + VST killed %19, killed %17, 16, $noreg + + bb.4: + successors: + + + bb.5: + successors: + + + bb.6: + successors: + + + bb.7: + Return + +... diff --git a/llvm/test/CodeGen/X86/avx512bf16-vl-intrinsics.ll b/llvm/test/CodeGen/X86/avx512bf16-vl-intrinsics.ll index 3cdc5de871e2..40b512d68be8 100644 --- a/llvm/test/CodeGen/X86/avx512bf16-vl-intrinsics.ll +++ b/llvm/test/CodeGen/X86/avx512bf16-vl-intrinsics.ll @@ -356,3 +356,49 @@ entry: %2 = select <4 x i1> %1, <4 x float> %0, <4 x float> %E ret <4 x float> %2 } + +define <16 x i16> @test_no_vbroadcast1() { +; CHECK-LABEL: test_no_vbroadcast1: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: vcvtneps2bf16 %xmm0, %xmm0 # encoding: [0x62,0xf2,0x7e,0x08,0x72,0xc0] +; CHECK-NEXT: vpbroadcastw %xmm0, %ymm0 # EVEX TO VEX Compression encoding: [0xc4,0xe2,0x7d,0x79,0xc0] +; CHECK-NEXT: ret{{[l|q]}} # encoding: [0xc3] +entry: + %0 = tail call <8 x bfloat> @llvm.x86.avx512bf16.mask.cvtneps2bf16.128(<4 x float> poison, <8 x bfloat> zeroinitializer, <4 x i1> ) + %1 = bitcast <8 x bfloat> %0 to <8 x i16> + %2 = shufflevector <8 x i16> %1, <8 x i16> undef, <16 x i32> zeroinitializer + ret <16 x i16> %2 +} + +;; FIXME: This should generate the same output as above, but let's fix the crash first. +define <16 x bfloat> @test_no_vbroadcast2() nounwind { +; X86-LABEL: test_no_vbroadcast2: +; X86: # %bb.0: # %entry +; X86-NEXT: pushl %ebp # encoding: [0x55] +; X86-NEXT: movl %esp, %ebp # encoding: [0x89,0xe5] +; X86-NEXT: andl $-32, %esp # encoding: [0x83,0xe4,0xe0] +; X86-NEXT: subl $64, %esp # encoding: [0x83,0xec,0x40] +; X86-NEXT: vcvtneps2bf16 %xmm0, %xmm0 # encoding: [0x62,0xf2,0x7e,0x08,0x72,0xc0] +; X86-NEXT: vmovaps %xmm0, (%esp) # EVEX TO VEX Compression encoding: [0xc5,0xf8,0x29,0x04,0x24] +; X86-NEXT: vpbroadcastw (%esp), %ymm0 # EVEX TO VEX Compression encoding: [0xc4,0xe2,0x7d,0x79,0x04,0x24] +; X86-NEXT: movl %ebp, %esp # encoding: [0x89,0xec] +; X86-NEXT: popl %ebp # encoding: [0x5d] +; X86-NEXT: retl # encoding: [0xc3] +; +; X64-LABEL: test_no_vbroadcast2: +; X64: # %bb.0: # %entry +; X64-NEXT: pushq %rbp # encoding: [0x55] +; X64-NEXT: movq %rsp, %rbp # encoding: [0x48,0x89,0xe5] +; X64-NEXT: andq $-32, %rsp # encoding: [0x48,0x83,0xe4,0xe0] +; X64-NEXT: subq $64, %rsp # encoding: [0x48,0x83,0xec,0x40] +; X64-NEXT: vcvtneps2bf16 %xmm0, %xmm0 # encoding: [0x62,0xf2,0x7e,0x08,0x72,0xc0] +; X64-NEXT: vmovaps %xmm0, (%rsp) # EVEX TO VEX Compression encoding: [0xc5,0xf8,0x29,0x04,0x24] +; X64-NEXT: vpbroadcastw (%rsp), %ymm0 # EVEX TO VEX Compression encoding: [0xc4,0xe2,0x7d,0x79,0x04,0x24] +; X64-NEXT: movq %rbp, %rsp # encoding: [0x48,0x89,0xec] +; X64-NEXT: popq %rbp # encoding: [0x5d] +; X64-NEXT: retq # encoding: [0xc3] +entry: + %0 = tail call <8 x bfloat> @llvm.x86.avx512bf16.mask.cvtneps2bf16.128(<4 x float> poison, <8 x bfloat> zeroinitializer, <4 x i1> ) + %1 = shufflevector <8 x bfloat> %0, <8 x bfloat> undef, <16 x i32> zeroinitializer + ret <16 x bfloat> %1 +} diff --git a/llvm/test/CodeGen/X86/prefetchi.ll b/llvm/test/CodeGen/X86/prefetchi.ll index 8f97e077e535..442819ea20d0 100644 --- a/llvm/test/CodeGen/X86/prefetchi.ll +++ b/llvm/test/CodeGen/X86/prefetchi.ll @@ -1,5 +1,6 @@ ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -mtriple=x86_64-- -mattr=+prefetchi | FileCheck %s +; RUN: llc < %s -mtriple=x86_64-- | FileCheck %s --check-prefix=NOPREFETCHI define dso_local void @t(ptr %ptr) nounwind { ; CHECK-LABEL: t: @@ -9,6 +10,10 @@ define dso_local void @t(ptr %ptr) nounwind { ; CHECK-NEXT: prefetchit1 t(%rip) ; CHECK-NEXT: prefetchit0 ext(%rip) ; CHECK-NEXT: retq +; +; NOPREFETCHI-LABEL: t: +; NOPREFETCHI: # %bb.0: # %entry +; NOPREFETCHI-NEXT: retq entry: tail call void @llvm.prefetch(ptr %ptr, i32 0, i32 2, i32 0) tail call void @llvm.prefetch(ptr %ptr, i32 0, i32 3, i32 0) diff --git a/llvm/test/CodeGen/X86/x86-64-intrcc.ll b/llvm/test/CodeGen/X86/x86-64-intrcc.ll index 1e1e01214c90..443d4c2fa464 100644 --- a/llvm/test/CodeGen/X86/x86-64-intrcc.ll +++ b/llvm/test/CodeGen/X86/x86-64-intrcc.ll @@ -174,5 +174,22 @@ entry: ret void } +define x86_intrcc void @test_stack_allocation(ptr byval(%struct.interrupt_frame) %frame, i64 %err) #1 { + ; CHECK-LABEL: test_stack_allocation: + ; CHECK: # %bb.0: # %entry + + ;; Ensure that STACKALLOC_W_PROBING isn't emitted. + ; CHECK-NOT: # fixed size alloca with probing + ;; Ensure that stack space is allocated. + ; CHECK: subq $280, %rsp +entry: + %some_allocation = alloca i64 + ;; Call a un-inlineable function to ensure the allocation isn't put in the red zone. + call void @external_function(ptr %some_allocation) + ret void +} + +declare void @external_function(ptr) attributes #0 = { nounwind "frame-pointer"="all" } +attributes #1 = { nounwind "probe-stack"="inline-asm" } diff --git a/llvm/test/Instrumentation/AddressSanitizer/experiment.ll b/llvm/test/Instrumentation/AddressSanitizer/experiment.ll index 5d1c5a2d5014..e18df618ebca 100644 --- a/llvm/test/Instrumentation/AddressSanitizer/experiment.ll +++ b/llvm/test/Instrumentation/AddressSanitizer/experiment.ll @@ -3,7 +3,8 @@ ; RUN: opt < %s -passes=asan -asan-force-experiment=42 -S | FileCheck %s ; RUN: opt < %s -passes=asan -asan-force-experiment=42 -S -mtriple=s390x-unknown-linux | FileCheck %s --check-prefix=EXT ; RUN: opt < %s -passes=asan -asan-force-experiment=42 -S -mtriple=mips-linux-gnu | FileCheck %s --check-prefix=MIPS_EXT -; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target +; RUN: opt < %s -passes=asan -asan-force-experiment=42 -S -mtriple=loongarch64-unknown-linux-gnu | FileCheck %s --check-prefix=LA_EXT +; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target, loongarch-registered-target target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-unknown-linux-gnu" @@ -119,15 +120,19 @@ entry: ; CHECK: declare void @__asan_report_exp_load_n(i64, i64, i32) ; EXT: declare void @__asan_report_exp_load_n(i64, i64, i32 zeroext) ; MIPS_EXT: declare void @__asan_report_exp_load_n(i64, i64, i32 signext) +; LA_EXT: declare void @__asan_report_exp_load_n(i64, i64, i32 signext) ; CHECK: declare void @__asan_exp_loadN(i64, i64, i32) ; EXT: declare void @__asan_exp_loadN(i64, i64, i32 zeroext) ; MIPS_EXT: declare void @__asan_exp_loadN(i64, i64, i32 signext) +; LA_EXT: declare void @__asan_exp_loadN(i64, i64, i32 signext) ; CHECK: declare void @__asan_report_exp_load1(i64, i32) ; EXT: declare void @__asan_report_exp_load1(i64, i32 zeroext) ; MIPS_EXT: declare void @__asan_report_exp_load1(i64, i32 signext) +; LA_EXT: declare void @__asan_report_exp_load1(i64, i32 signext) ; CHECK: declare void @__asan_exp_load1(i64, i32) ; EXT: declare void @__asan_exp_load1(i64, i32 zeroext) ; MIPS_EXT: declare void @__asan_exp_load1(i64, i32 signext) +; LA_EXT: declare void @__asan_exp_load1(i64, i32 signext) diff --git a/llvm/test/Instrumentation/AddressSanitizer/mem-intrinsics.ll b/llvm/test/Instrumentation/AddressSanitizer/mem-intrinsics.ll index c9d3132a777c..67ef8d1cd675 100644 --- a/llvm/test/Instrumentation/AddressSanitizer/mem-intrinsics.ll +++ b/llvm/test/Instrumentation/AddressSanitizer/mem-intrinsics.ll @@ -5,7 +5,8 @@ ; RUN: opt < %s -passes=asan -asan-kernel -asan-kernel-mem-intrinsic-prefix -S | FileCheck --check-prefixes=CHECK,CHECK-PREFIX %s ; RUN: opt < %s -passes=asan -S -mtriple=s390x-unknown-linux | FileCheck --check-prefix=EXT %s ; RUN: opt < %s -passes=asan -S -mtriple=mips-linux-gnu | FileCheck --check-prefix=MIPS_EXT %s -; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target +; RUN: opt < %s -passes=asan -S -mtriple=loongarch64-unknown-linux-gnu | FileCheck --check-prefix=LA_EXT %s +; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target, loongarch-registered-target target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64" target triple = "x86_64-unknown-linux-gnu" @@ -80,3 +81,4 @@ define void @memintr_element_atomic_test(ptr %a, ptr %b) nounwind uwtable saniti ; CHECK-PREFIX: declare ptr @__asan_memset(ptr, i32, i64) ; EXT: declare ptr @__asan_memset(ptr, i32 zeroext, i64) ; MIPS_EXT: declare ptr @__asan_memset(ptr, i32 signext, i64) +; LA_EXT: declare ptr @__asan_memset(ptr, i32 signext, i64) diff --git a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll index 0312aa1592f8..76afc4bf007c 100644 --- a/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll +++ b/llvm/test/Instrumentation/ThreadSanitizer/atomic.ll @@ -1,7 +1,8 @@ ; RUN: opt < %s -passes=tsan -S | FileCheck %s ; RUN: opt < %s -passes=tsan -S -mtriple=s390x-unknown-linux | FileCheck --check-prefix=EXT %s ; RUN: opt < %s -passes=tsan -S -mtriple=mips-linux-gnu | FileCheck --check-prefix=MIPS_EXT %s -; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target +; RUN: opt < %s -passes=tsan -S -mtriple=loongarch64-unknown-linux-gnu | FileCheck --check-prefix=LA_EXT %s +; REQUIRES: x86-registered-target, systemz-registered-target, mips-registered-target, loongarch-registered-target ; Check that atomic memory operations are converted to calls into ThreadSanitizer runtime. target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" @@ -2102,38 +2103,47 @@ entry: ; CHECK: declare void @__tsan_atomic32_store(ptr, i32, i32) ; EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext) ; MIPS_EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext) +; LA_EXT: declare void @__tsan_atomic32_store(ptr, i32 signext, i32 signext) ; CHECK: declare i32 @__tsan_atomic32_compare_exchange_val(ptr, i32, i32, i32, i32) ; EXT: declare signext i32 @__tsan_atomic32_compare_exchange_val(ptr, i32 signext, i32 signext, i32 signext, i32 signext) ; MIPS_EXT: declare i32 @__tsan_atomic32_compare_exchange_val(ptr, i32 signext, i32 signext, i32 signext, i32 signext) +; LA_EXT: declare signext i32 @__tsan_atomic32_compare_exchange_val(ptr, i32 signext, i32 signext, i32 signext, i32 signext) ; CHECK: declare i64 @__tsan_atomic64_load(ptr, i32) ; EXT: declare i64 @__tsan_atomic64_load(ptr, i32 signext) ; MIPS_EXT: declare i64 @__tsan_atomic64_load(ptr, i32 signext) +; LA_EXT: declare i64 @__tsan_atomic64_load(ptr, i32 signext) ; CHECK: declare void @__tsan_atomic64_store(ptr, i64, i32) ; EXT: declare void @__tsan_atomic64_store(ptr, i64, i32 signext) ; MIPS_EXT: declare void @__tsan_atomic64_store(ptr, i64, i32 signext) +; LA_EXT: declare void @__tsan_atomic64_store(ptr, i64, i32 signext) ; CHECK: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32) ; EXT: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32 signext) ; MIPS_EXT: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32 signext) +; LA_EXT: declare i64 @__tsan_atomic64_fetch_add(ptr, i64, i32 signext) ; CHECK: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32, i32) ; EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext) ; MIPS_EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext) +; LA_EXT: declare i64 @__tsan_atomic64_compare_exchange_val(ptr, i64, i64, i32 signext, i32 signext) ; CHECK: declare void @__tsan_atomic_thread_fence(i32) ; EXT: declare void @__tsan_atomic_thread_fence(i32 signext) ; MIPS_EXT: declare void @__tsan_atomic_thread_fence(i32 signext) +; LA_EXT: declare void @__tsan_atomic_thread_fence(i32 signext) ; CHECK: declare void @__tsan_atomic_signal_fence(i32) ; EXT: declare void @__tsan_atomic_signal_fence(i32 signext) ; MIPS_EXT: declare void @__tsan_atomic_signal_fence(i32 signext) +; LA_EXT: declare void @__tsan_atomic_signal_fence(i32 signext) ; CHECK: declare ptr @__tsan_memset(ptr, i32, i64) ; EXT: declare ptr @__tsan_memset(ptr, i32 signext, i64) ; MIPS_EXT: declare ptr @__tsan_memset(ptr, i32 signext, i64) +; LA_EXT: declare ptr @__tsan_memset(ptr, i32 signext, i64) !llvm.module.flags = !{!0, !1, !2} !llvm.dbg.cu = !{!8} diff --git a/llvm/test/Transforms/GVN/pr63019.ll b/llvm/test/Transforms/GVN/pr63019.ll new file mode 100644 index 000000000000..f2628a32be5e --- /dev/null +++ b/llvm/test/Transforms/GVN/pr63019.ll @@ -0,0 +1,41 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2 +; RUN: opt -S -passes=gvn < %s | FileCheck %s + +; Make sure the two offsets from the phi don't get merged incorrectly. +define i8 @test(i1 %c, i64 %offset, ptr %ptr) { +; CHECK-LABEL: define i8 @test +; CHECK-SAME: (i1 [[C:%.*]], i64 [[OFFSET:%.*]], ptr [[PTR:%.*]]) { +; CHECK-NEXT: start: +; CHECK-NEXT: [[ALLOCA:%.*]] = alloca [8 x i8], align 8 +; CHECK-NEXT: store i64 1234605616436508552, ptr [[ALLOCA]], align 8 +; CHECK-NEXT: [[GEP_2:%.*]] = getelementptr i8, ptr [[ALLOCA]], i64 2 +; CHECK-NEXT: [[GEP_UNKNOWN:%.*]] = getelementptr i8, ptr [[ALLOCA]], i64 [[OFFSET]] +; CHECK-NEXT: br i1 [[C]], label [[JOIN:%.*]], label [[IF:%.*]] +; CHECK: if: +; CHECK-NEXT: br label [[JOIN]] +; CHECK: join: +; CHECK-NEXT: [[PHI:%.*]] = phi ptr [ [[GEP_UNKNOWN]], [[START:%.*]] ], [ [[GEP_2]], [[IF]] ] +; CHECK-NEXT: store i8 0, ptr [[ALLOCA]], align 8 +; CHECK-NEXT: [[LOAD1:%.*]] = load i64, ptr [[ALLOCA]], align 8 +; CHECK-NEXT: store i64 [[LOAD1]], ptr [[PTR]], align 8 +; CHECK-NEXT: [[LOAD2:%.*]] = load i8, ptr [[PHI]], align 1 +; CHECK-NEXT: ret i8 [[LOAD2]] +; +start: + %alloca = alloca [8 x i8], align 8 + store i64 u0x1122334455667788, ptr %alloca, align 8 + %gep.2 = getelementptr i8, ptr %alloca, i64 2 + %gep.unknown = getelementptr i8, ptr %alloca, i64 %offset + br i1 %c, label %join, label %if + +if: + br label %join + +join: + %phi = phi ptr [ %gep.unknown, %start ], [ %gep.2, %if ] + store i8 0, ptr %alloca, align 8 + %load1 = load i64, ptr %alloca, align 8 + store i64 %load1, ptr %ptr, align 8 + %load2 = load i8, ptr %phi, align 1 + ret i8 %load2 +} diff --git a/llvm/test/tools/llvm-mca/X86/Generic/no-duplicate-symbols.s b/llvm/test/tools/llvm-mca/X86/Generic/no-duplicate-symbols.s new file mode 100644 index 000000000000..8b8144c08b85 --- /dev/null +++ b/llvm/test/tools/llvm-mca/X86/Generic/no-duplicate-symbols.s @@ -0,0 +1,8 @@ +# RUN: llvm-mca -mtriple=x86_64-unknown-unknown -mcpu=x86-64 < %s 2>&1 | FileCheck %s + +# This test checks that https://github.com/llvm/llvm-project/issues/62528 is resolved. +foo: + pushq %rbp + +# CHECK-NOT: :4:1: error: symbol 'foo' is already defined + diff --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp index 73c341891ab7..33adf15fccaf 100644 --- a/llvm/tools/llvm-mca/llvm-mca.cpp +++ b/llvm/tools/llvm-mca/llvm-mca.cpp @@ -401,11 +401,6 @@ int main(int argc, char **argv) { // Tell SrcMgr about this buffer, which is what the parser will pick up. SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc()); - MCContext Ctx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); - std::unique_ptr MOFI( - TheTarget->createMCObjectFileInfo(Ctx, /*PIC=*/false)); - Ctx.setObjectFileInfo(MOFI.get()); - std::unique_ptr BOS; std::unique_ptr MCII(TheTarget->createMCInstrInfo()); @@ -433,7 +428,11 @@ int main(int argc, char **argv) { } // Parse the input and create CodeRegions that llvm-mca can analyze. - mca::AsmAnalysisRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, + MCContext ACtx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); + std::unique_ptr AMOFI( + TheTarget->createMCObjectFileInfo(ACtx, /*PIC=*/false)); + ACtx.setObjectFileInfo(AMOFI.get()); + mca::AsmAnalysisRegionGenerator CRG(*TheTarget, SrcMgr, ACtx, *MAI, *STI, *MCII); Expected RegionsOrErr = CRG.parseAnalysisRegions(std::move(IPtemp)); @@ -471,7 +470,11 @@ int main(int argc, char **argv) { // Parse the input and create InstrumentRegion that llvm-mca // can use to improve analysis. - mca::AsmInstrumentRegionGenerator IRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, + MCContext ICtx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); + std::unique_ptr IMOFI( + TheTarget->createMCObjectFileInfo(ICtx, /*PIC=*/false)); + ICtx.setObjectFileInfo(IMOFI.get()); + mca::AsmInstrumentRegionGenerator IRG(*TheTarget, SrcMgr, ICtx, *MAI, *STI, *MCII, *IM); Expected InstrumentRegionsOrErr = IRG.parseInstrumentRegions(std::move(IPtemp)); @@ -547,7 +550,7 @@ int main(int argc, char **argv) { unsigned RegionIdx = 0; std::unique_ptr MCE( - TheTarget->createMCCodeEmitter(*MCII, Ctx)); + TheTarget->createMCCodeEmitter(*MCII, ACtx)); assert(MCE && "Unable to create code emitter!"); std::unique_ptr MAB(TheTarget->createMCAsmBackend( diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp index 1aa62483e9a8..c13e5b5deff6 100644 --- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp +++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp @@ -95,8 +95,30 @@ // //===----------------------------------------------------------------------===// -#include "AsmMatcherEmitterTypes.h" -#include "Printer.h" +#include "CodeGenInstruction.h" +#include "CodeGenTarget.h" +#include "SubtargetFeatureInfo.h" +#include "Types.h" +#include "llvm/ADT/CachedHashString.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/StringMatcher.h" +#include "llvm/TableGen/StringToOffsetTable.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include +#include +#include +#include using namespace llvm; @@ -109,42 +131,124 @@ static cl::opt cl::desc("Only match instructions with the given prefix"), cl::cat(AsmMatcherEmitterCat)); -#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +namespace { +class AsmMatcherInfo; + +// Register sets are used as keys in some second-order sets TableGen creates +// when generating its data structures. This means that the order of two +// RegisterSets can be seen in the outputted AsmMatcher tables occasionally, and +// can even affect compiler output (at least seen in diagnostics produced when +// all matches fail). So we use a type that sorts them consistently. +typedef std::set RegisterSet; class AsmMatcherEmitter { RecordKeeper &Records; - PrinterLLVM &PI; - public: - AsmMatcherEmitter(RecordKeeper &R, PrinterLLVM &PI) : Records(R), PI(PI) {} + AsmMatcherEmitter(RecordKeeper &R) : Records(R) {} - void run(); + void run(raw_ostream &o); }; -// -// ClassInfo implementation -// +/// ClassInfo - Helper class for storing the information about a particular +/// class of operands which can be matched. +struct ClassInfo { + enum ClassInfoKind { + /// Invalid kind, for use as a sentinel value. + Invalid = 0, + + /// The class for a particular token. + Token, + + /// The (first) register class, subsequent register classes are + /// RegisterClass0+1, and so on. + RegisterClass0, + + /// The (first) user defined class, subsequent user defined classes are + /// UserClass0+1, and so on. + UserClass0 = 1<<16 + }; + + /// Kind - The class kind, which is either a predefined kind, or (UserClass0 + + /// N) for the Nth user defined class. + unsigned Kind; + + /// SuperClasses - The super classes of this class. Note that for simplicities + /// sake user operands only record their immediate super class, while register + /// operands include all superclasses. + std::vector SuperClasses; + + /// Name - The full class name, suitable for use in an enum. + std::string Name; + + /// ClassName - The unadorned generic name for this class (e.g., Token). + std::string ClassName; -bool ClassInfo::isRelatedTo(const ClassInfo &RHS) const { + /// ValueName - The name of the value this class represents; for a token this + /// is the literal token string, for an operand it is the TableGen class (or + /// empty if this is a derived class). + std::string ValueName; + + /// PredicateMethod - The name of the operand method to test whether the + /// operand matches this class; this is not valid for Token or register kinds. + std::string PredicateMethod; + + /// RenderMethod - The name of the operand method to add this operand to an + /// MCInst; this is not valid for Token or register kinds. + std::string RenderMethod; + + /// ParserMethod - The name of the operand method to do a target specific + /// parsing on the operand. + std::string ParserMethod; + + /// For register classes: the records for all the registers in this class. + RegisterSet Registers; + + /// For custom match classes: the diagnostic kind for when the predicate fails. + std::string DiagnosticType; + + /// For custom match classes: the diagnostic string for when the predicate fails. + std::string DiagnosticString; + + /// Is this operand optional and not always required. + bool IsOptional; + + /// DefaultMethod - The name of the method that returns the default operand + /// for optional operand + std::string DefaultMethod; + +public: + /// isRegisterClass() - Check if this is a register class. + bool isRegisterClass() const { + return Kind >= RegisterClass0 && Kind < UserClass0; + } + + /// isUserClass() - Check if this is a user defined class. + bool isUserClass() const { + return Kind >= UserClass0; + } + + /// isRelatedTo - Check whether this class is "related" to \p RHS. Classes + /// are related if they are in the same class hierarchy. + bool isRelatedTo(const ClassInfo &RHS) const { // Tokens are only related to tokens. if (Kind == Token || RHS.Kind == Token) - return Kind == Token && RHS.Kind == Token; - + return Kind == Token && RHS.Kind == Token; + // Registers classes are only related to registers classes, and only if // their intersection is non-empty. if (isRegisterClass() || RHS.isRegisterClass()) { - if (!isRegisterClass() || !RHS.isRegisterClass()) - return false; - - RegisterSet Tmp; - std::insert_iterator II(Tmp, Tmp.begin()); - std::set_intersection(Registers.begin(), Registers.end(), - RHS.Registers.begin(), RHS.Registers.end(), II, - LessRecordByID()); - - return !Tmp.empty(); - } - + if (!isRegisterClass() || !RHS.isRegisterClass()) + return false; + + RegisterSet Tmp; + std::insert_iterator II(Tmp, Tmp.begin()); + std::set_intersection(Registers.begin(), Registers.end(), + RHS.Registers.begin(), RHS.Registers.end(), + II, LessRecordByID()); + + return !Tmp.empty(); + } + // Otherwise we have two users operands; they are related if they are in the // same class hierarchy. // @@ -153,98 +257,102 @@ bool ClassInfo::isRelatedTo(const ClassInfo &RHS) const { assert(isUserClass() && RHS.isUserClass() && "Unexpected class!"); const ClassInfo *Root = this; while (!Root->SuperClasses.empty()) - Root = Root->SuperClasses.front(); - + Root = Root->SuperClasses.front(); + const ClassInfo *RHSRoot = &RHS; while (!RHSRoot->SuperClasses.empty()) - RHSRoot = RHSRoot->SuperClasses.front(); - + RHSRoot = RHSRoot->SuperClasses.front(); + return Root == RHSRoot; -} + } -bool ClassInfo::isSubsetOf(const ClassInfo &RHS) const { + /// isSubsetOf - Test whether this class is a subset of \p RHS. + bool isSubsetOf(const ClassInfo &RHS) const { // This is a subset of RHS if it is the same class... if (this == &RHS) - return true; - + return true; + // ... or if any of its super classes are a subset of RHS. SmallVector Worklist(SuperClasses.begin(), SuperClasses.end()); SmallPtrSet Visited; while (!Worklist.empty()) { - auto *CI = Worklist.pop_back_val(); - if (CI == &RHS) - return true; - for (auto *Super : CI->SuperClasses) - if (Visited.insert(Super).second) - Worklist.push_back(Super); - } - + auto *CI = Worklist.pop_back_val(); + if (CI == &RHS) + return true; + for (auto *Super : CI->SuperClasses) + if (Visited.insert(Super).second) + Worklist.push_back(Super); + } + return false; -} + } -int ClassInfo::getTreeDepth() const { + int getTreeDepth() const { int Depth = 0; const ClassInfo *Root = this; while (!Root->SuperClasses.empty()) { - Depth++; - Root = Root->SuperClasses.front(); + Depth++; + Root = Root->SuperClasses.front(); } return Depth; -} + } -const ClassInfo *ClassInfo::findRoot() const { + const ClassInfo *findRoot() const { const ClassInfo *Root = this; while (!Root->SuperClasses.empty()) - Root = Root->SuperClasses.front(); + Root = Root->SuperClasses.front(); return Root; -} + } -bool ClassInfo::operator<(const ClassInfo &RHS) const { + /// Compare two classes. This does not produce a total ordering, but does + /// guarantee that subclasses are sorted before their parents, and that the + /// ordering is transitive. + bool operator<(const ClassInfo &RHS) const { if (this == &RHS) - return false; + return false; // First, enforce the ordering between the three different types of class. // Tokens sort before registers, which sort before user classes. if (Kind == Token) { - if (RHS.Kind != Token) - return true; - assert(RHS.Kind == Token); + if (RHS.Kind != Token) + return true; + assert(RHS.Kind == Token); } else if (isRegisterClass()) { - if (RHS.Kind == Token) - return false; - else if (RHS.isUserClass()) - return true; - assert(RHS.isRegisterClass()); + if (RHS.Kind == Token) + return false; + else if (RHS.isUserClass()) + return true; + assert(RHS.isRegisterClass()); } else if (isUserClass()) { - if (!RHS.isUserClass()) - return false; - assert(RHS.isUserClass()); + if (!RHS.isUserClass()) + return false; + assert(RHS.isUserClass()); } else { - llvm_unreachable("Unknown ClassInfoKind"); + llvm_unreachable("Unknown ClassInfoKind"); } if (Kind == Token || isUserClass()) { - // Related tokens and user classes get sorted by depth in the inheritence - // tree (so that subclasses are before their parents). - if (isRelatedTo(RHS)) { - if (getTreeDepth() > RHS.getTreeDepth()) - return true; - if (getTreeDepth() < RHS.getTreeDepth()) - return false; + // Related tokens and user classes get sorted by depth in the inheritence + // tree (so that subclasses are before their parents). + if (isRelatedTo(RHS)) { + if (getTreeDepth() > RHS.getTreeDepth()) + return true; + if (getTreeDepth() < RHS.getTreeDepth()) + return false; } else { - // Unrelated tokens and user classes are ordered by the name of their - // root nodes, so that there is a consistent ordering between - // unconnected trees. - return findRoot()->ValueName < RHS.findRoot()->ValueName; + // Unrelated tokens and user classes are ordered by the name of their + // root nodes, so that there is a consistent ordering between + // unconnected trees. + return findRoot()->ValueName < RHS.findRoot()->ValueName; } } else if (isRegisterClass()) { - // For register sets, sort by number of registers. This guarantees that - // a set will always sort before all of it's strict supersets. - if (Registers.size() != RHS.Registers.size()) - return Registers.size() < RHS.Registers.size(); + // For register sets, sort by number of registers. This guarantees that + // a set will always sort before all of it's strict supersets. + if (Registers.size() != RHS.Registers.size()) + return Registers.size() < RHS.Registers.size(); } else { - llvm_unreachable("Unknown ClassInfoKind"); + llvm_unreachable("Unknown ClassInfoKind"); } // FIXME: We should be able to just return false here, as we only need a @@ -253,49 +361,274 @@ bool ClassInfo::operator<(const ClassInfo &RHS) const { // accidentally rely on this behaviour, so it will have to stay like this // until they are fixed. return ValueName < RHS.ValueName; -} + } +}; -// -// MatchableInfo implementation -// -int MatchableInfo::findAsmOperand(StringRef N, int SubOpIdx) const { +class AsmVariantInfo { +public: + StringRef RegisterPrefix; + StringRef TokenizingCharacters; + StringRef SeparatorCharacters; + StringRef BreakCharacters; + StringRef Name; + int AsmVariantNo; +}; + +/// MatchableInfo - Helper class for storing the necessary information for an +/// instruction or alias which is capable of being matched. +struct MatchableInfo { + struct AsmOperand { + /// Token - This is the token that the operand came from. + StringRef Token; + + /// The unique class instance this operand should match. + ClassInfo *Class; + + /// The operand name this is, if anything. + StringRef SrcOpName; + + /// The operand name this is, before renaming for tied operands. + StringRef OrigSrcOpName; + + /// The suboperand index within SrcOpName, or -1 for the entire operand. + int SubOpIdx; + + /// Whether the token is "isolated", i.e., it is preceded and followed + /// by separators. + bool IsIsolatedToken; + + /// Register record if this token is singleton register. + Record *SingletonReg; + + explicit AsmOperand(bool IsIsolatedToken, StringRef T) + : Token(T), Class(nullptr), SubOpIdx(-1), + IsIsolatedToken(IsIsolatedToken), SingletonReg(nullptr) {} + }; + + /// ResOperand - This represents a single operand in the result instruction + /// generated by the match. In cases (like addressing modes) where a single + /// assembler operand expands to multiple MCOperands, this represents the + /// single assembler operand, not the MCOperand. + struct ResOperand { + enum { + /// RenderAsmOperand - This represents an operand result that is + /// generated by calling the render method on the assembly operand. The + /// corresponding AsmOperand is specified by AsmOperandNum. + RenderAsmOperand, + + /// TiedOperand - This represents a result operand that is a duplicate of + /// a previous result operand. + TiedOperand, + + /// ImmOperand - This represents an immediate value that is dumped into + /// the operand. + ImmOperand, + + /// RegOperand - This represents a fixed register that is dumped in. + RegOperand + } Kind; + + /// Tuple containing the index of the (earlier) result operand that should + /// be copied from, as well as the indices of the corresponding (parsed) + /// operands in the asm string. + struct TiedOperandsTuple { + unsigned ResOpnd; + unsigned SrcOpnd1Idx; + unsigned SrcOpnd2Idx; + }; + + union { + /// This is the operand # in the AsmOperands list that this should be + /// copied from. + unsigned AsmOperandNum; + + /// Description of tied operands. + TiedOperandsTuple TiedOperands; + + /// ImmVal - This is the immediate value added to the instruction. + int64_t ImmVal; + + /// Register - This is the register record. + Record *Register; + }; + + /// MINumOperands - The number of MCInst operands populated by this + /// operand. + unsigned MINumOperands; + + static ResOperand getRenderedOp(unsigned AsmOpNum, unsigned NumOperands) { + ResOperand X; + X.Kind = RenderAsmOperand; + X.AsmOperandNum = AsmOpNum; + X.MINumOperands = NumOperands; + return X; + } + + static ResOperand getTiedOp(unsigned TiedOperandNum, unsigned SrcOperand1, + unsigned SrcOperand2) { + ResOperand X; + X.Kind = TiedOperand; + X.TiedOperands = { TiedOperandNum, SrcOperand1, SrcOperand2 }; + X.MINumOperands = 1; + return X; + } + + static ResOperand getImmOp(int64_t Val) { + ResOperand X; + X.Kind = ImmOperand; + X.ImmVal = Val; + X.MINumOperands = 1; + return X; + } + + static ResOperand getRegOp(Record *Reg) { + ResOperand X; + X.Kind = RegOperand; + X.Register = Reg; + X.MINumOperands = 1; + return X; + } + }; + + /// AsmVariantID - Target's assembly syntax variant no. + int AsmVariantID; + + /// AsmString - The assembly string for this instruction (with variants + /// removed), e.g. "movsx $src, $dst". + std::string AsmString; + + /// TheDef - This is the definition of the instruction or InstAlias that this + /// matchable came from. + Record *const TheDef; + + /// DefRec - This is the definition that it came from. + PointerUnion DefRec; + + const CodeGenInstruction *getResultInst() const { + if (DefRec.is()) + return DefRec.get(); + return DefRec.get()->ResultInst; + } + + /// ResOperands - This is the operand list that should be built for the result + /// MCInst. + SmallVector ResOperands; + + /// Mnemonic - This is the first token of the matched instruction, its + /// mnemonic. + StringRef Mnemonic; + + /// AsmOperands - The textual operands that this instruction matches, + /// annotated with a class and where in the OperandList they were defined. + /// This directly corresponds to the tokenized AsmString after the mnemonic is + /// removed. + SmallVector AsmOperands; + + /// Predicates - The required subtarget features to match this instruction. + SmallVector RequiredFeatures; + + /// ConversionFnKind - The enum value which is passed to the generated + /// convertToMCInst to convert parsed operands into an MCInst for this + /// function. + std::string ConversionFnKind; + + /// If this instruction is deprecated in some form. + bool HasDeprecation; + + /// If this is an alias, this is use to determine whether or not to using + /// the conversion function defined by the instruction's AsmMatchConverter + /// or to use the function generated by the alias. + bool UseInstAsmMatchConverter; + + MatchableInfo(const CodeGenInstruction &CGI) + : AsmVariantID(0), AsmString(CGI.AsmString), TheDef(CGI.TheDef), DefRec(&CGI), + UseInstAsmMatchConverter(true) { + } + + MatchableInfo(std::unique_ptr Alias) + : AsmVariantID(0), AsmString(Alias->AsmString), TheDef(Alias->TheDef), + DefRec(Alias.release()), + UseInstAsmMatchConverter( + TheDef->getValueAsBit("UseInstAsmMatchConverter")) { + } + + // Could remove this and the dtor if PointerUnion supported unique_ptr + // elements with a dynamic failure/assertion (like the one below) in the case + // where it was copied while being in an owning state. + MatchableInfo(const MatchableInfo &RHS) + : AsmVariantID(RHS.AsmVariantID), AsmString(RHS.AsmString), + TheDef(RHS.TheDef), DefRec(RHS.DefRec), ResOperands(RHS.ResOperands), + Mnemonic(RHS.Mnemonic), AsmOperands(RHS.AsmOperands), + RequiredFeatures(RHS.RequiredFeatures), + ConversionFnKind(RHS.ConversionFnKind), + HasDeprecation(RHS.HasDeprecation), + UseInstAsmMatchConverter(RHS.UseInstAsmMatchConverter) { + assert(!DefRec.is()); + } + + ~MatchableInfo() { + delete DefRec.dyn_cast(); + } + + // Two-operand aliases clone from the main matchable, but mark the second + // operand as a tied operand of the first for purposes of the assembler. + void formTwoOperandAlias(StringRef Constraint); + + void initialize(const AsmMatcherInfo &Info, + SmallPtrSetImpl &SingletonRegisters, + AsmVariantInfo const &Variant, + bool HasMnemonicFirst); + + /// validate - Return true if this matchable is a valid thing to match against + /// and perform a bunch of validity checking. + bool validate(StringRef CommentDelimiter, bool IsAlias) const; + + /// findAsmOperand - Find the AsmOperand with the specified name and + /// suboperand index. + int findAsmOperand(StringRef N, int SubOpIdx) const { auto I = find_if(AsmOperands, [&](const AsmOperand &Op) { - return Op.SrcOpName == N && Op.SubOpIdx == SubOpIdx; + return Op.SrcOpName == N && Op.SubOpIdx == SubOpIdx; }); return (I != AsmOperands.end()) ? I - AsmOperands.begin() : -1; -} + } -int MatchableInfo::findAsmOperandNamed(StringRef N, int LastIdx) const { + /// findAsmOperandNamed - Find the first AsmOperand with the specified name. + /// This does not check the suboperand index. + int findAsmOperandNamed(StringRef N, int LastIdx = -1) const { auto I = llvm::find_if(llvm::drop_begin(AsmOperands, LastIdx + 1), [&](const AsmOperand &Op) { return Op.SrcOpName == N; }); return (I != AsmOperands.end()) ? I - AsmOperands.begin() : -1; -} + } -int MatchableInfo::findAsmOperandOriginallyNamed(StringRef N) const { - auto I = find_if(AsmOperands, [&](const AsmOperand &Op) { - return Op.OrigSrcOpName == N; - }); + int findAsmOperandOriginallyNamed(StringRef N) const { + auto I = + find_if(AsmOperands, + [&](const AsmOperand &Op) { return Op.OrigSrcOpName == N; }); return (I != AsmOperands.end()) ? I - AsmOperands.begin() : -1; -} + } + + void buildInstructionResultOperands(); + void buildAliasResultOperands(bool AliasConstraintsAreChecked); -bool MatchableInfo::operator<(const MatchableInfo &RHS) const { + /// operator< - Compare two matchables. + bool operator<(const MatchableInfo &RHS) const { // The primary comparator is the instruction mnemonic. if (int Cmp = Mnemonic.compare_insensitive(RHS.Mnemonic)) - return Cmp == -1; - + return Cmp == -1; + if (AsmOperands.size() != RHS.AsmOperands.size()) - return AsmOperands.size() < RHS.AsmOperands.size(); - + return AsmOperands.size() < RHS.AsmOperands.size(); + // Compare lexicographically by operand. The matcher validates that other // orderings wouldn't be ambiguous using \see couldMatchAmbiguouslyWith(). for (unsigned i = 0, e = AsmOperands.size(); i != e; ++i) { - if (*AsmOperands[i].Class < *RHS.AsmOperands[i].Class) - return true; - if (*RHS.AsmOperands[i].Class < *AsmOperands[i].Class) - return false; + if (*AsmOperands[i].Class < *RHS.AsmOperands[i].Class) + return true; + if (*RHS.AsmOperands[i].Class < *AsmOperands[i].Class) + return false; } - + // Give matches that require more features higher precedence. This is useful // because we cannot define AssemblerPredicates with the negation of // processor features. For example, ARM v6 "nop" may be either a HINT or @@ -303,8 +636,8 @@ bool MatchableInfo::operator<(const MatchableInfo &RHS) const { // predicate MOV under "NoV6", but HINT will always match first because it // requires V6 while MOV does not. if (RequiredFeatures.size() != RHS.RequiredFeatures.size()) - return RequiredFeatures.size() > RHS.RequiredFeatures.size(); - + return RequiredFeatures.size() > RHS.RequiredFeatures.size(); + // For X86 AVX/AVX512 instructions, we prefer vex encoding because the // vex encoding size is smaller. Since X86InstrSSE.td is included ahead // of X86InstrAVX512.td, the AVX instruction ID is less than AVX512 ID. @@ -312,77 +645,169 @@ bool MatchableInfo::operator<(const MatchableInfo &RHS) const { // matching table. if (TheDef->isSubClassOf("Instruction") && TheDef->getValueAsBit("HasPositionOrder")) - return TheDef->getID() < RHS.TheDef->getID(); - + return TheDef->getID() < RHS.TheDef->getID(); + return false; -} + } -bool MatchableInfo::couldMatchAmbiguouslyWith(const MatchableInfo &RHS) const { + /// couldMatchAmbiguouslyWith - Check whether this matchable could + /// ambiguously match the same set of operands as \p RHS (without being a + /// strictly superior match). + bool couldMatchAmbiguouslyWith(const MatchableInfo &RHS) const { // The primary comparator is the instruction mnemonic. if (Mnemonic != RHS.Mnemonic) - return false; - + return false; + // Different variants can't conflict. if (AsmVariantID != RHS.AsmVariantID) - return false; - + return false; + // The number of operands is unambiguous. if (AsmOperands.size() != RHS.AsmOperands.size()) - return false; - + return false; + // Otherwise, make sure the ordering of the two instructions is unambiguous // by checking that either (a) a token or operand kind discriminates them, // or (b) the ordering among equivalent kinds is consistent. - + // Tokens and operand kinds are unambiguous (assuming a correct target // specific parser). for (unsigned i = 0, e = AsmOperands.size(); i != e; ++i) - if (AsmOperands[i].Class->Kind != RHS.AsmOperands[i].Class->Kind || - AsmOperands[i].Class->Kind == ClassInfo::Token) - if (*AsmOperands[i].Class < *RHS.AsmOperands[i].Class || - *RHS.AsmOperands[i].Class < *AsmOperands[i].Class) - return false; - + if (AsmOperands[i].Class->Kind != RHS.AsmOperands[i].Class->Kind || + AsmOperands[i].Class->Kind == ClassInfo::Token) + if (*AsmOperands[i].Class < *RHS.AsmOperands[i].Class || + *RHS.AsmOperands[i].Class < *AsmOperands[i].Class) + return false; + // Otherwise, this operand could commute if all operands are equivalent, or // there is a pair of operands that compare less than and a pair that // compare greater than. bool HasLT = false, HasGT = false; for (unsigned i = 0, e = AsmOperands.size(); i != e; ++i) { - if (*AsmOperands[i].Class < *RHS.AsmOperands[i].Class) - HasLT = true; - if (*RHS.AsmOperands[i].Class < *AsmOperands[i].Class) - HasGT = true; + if (*AsmOperands[i].Class < *RHS.AsmOperands[i].Class) + HasLT = true; + if (*RHS.AsmOperands[i].Class < *AsmOperands[i].Class) + HasGT = true; } - + return HasLT == HasGT; -} + } -// -// OperandMatchEntry implementation -// -OperandMatchEntry OperandMatchEntry::create(const MatchableInfo *mi, ClassInfo *ci, - unsigned opMask) { + void dump() const; + +private: + void tokenizeAsmString(AsmMatcherInfo const &Info, + AsmVariantInfo const &Variant); + void addAsmOperand(StringRef Token, bool IsIsolatedToken = false); +}; + +struct OperandMatchEntry { + unsigned OperandMask; + const MatchableInfo* MI; + ClassInfo *CI; + + static OperandMatchEntry create(const MatchableInfo *mi, ClassInfo *ci, + unsigned opMask) { OperandMatchEntry X; X.OperandMask = opMask; X.CI = ci; X.MI = mi; return X; -} + } +}; -// -// AsmMatcherInfo implementations -// -const SubtargetFeatureInfo *AsmMatcherInfo::getSubtargetFeature(Record *Def) const { +class AsmMatcherInfo { +public: + /// Tracked Records + RecordKeeper &Records; + + /// The tablegen AsmParser record. + Record *AsmParser; + + /// Target - The target information. + CodeGenTarget &Target; + + /// The classes which are needed for matching. + std::forward_list Classes; + + /// The information on the matchables to match. + std::vector> Matchables; + + /// Info for custom matching operands by user defined methods. + std::vector OperandMatchInfo; + + /// Map of Register records to their class information. + typedef std::map RegisterClassesTy; + RegisterClassesTy RegisterClasses; + + /// Map of Predicate records to their subtarget information. + std::map SubtargetFeatures; + + /// Map of AsmOperandClass records to their class information. + std::map AsmOperandClasses; + + /// Map of RegisterClass records to their class information. + std::map RegisterClassClasses; + +private: + /// Map of token to class information which has already been constructed. + std::map TokenClasses; + +private: + /// getTokenClass - Lookup or create the class for the given token. + ClassInfo *getTokenClass(StringRef Token); + + /// getOperandClass - Lookup or create the class for the given operand. + ClassInfo *getOperandClass(const CGIOperandList::OperandInfo &OI, + int SubOpIdx); + ClassInfo *getOperandClass(Record *Rec, int SubOpIdx); + + /// buildRegisterClasses - Build the ClassInfo* instances for register + /// classes. + void buildRegisterClasses(SmallPtrSetImpl &SingletonRegisters); + + /// buildOperandClasses - Build the ClassInfo* instances for user defined + /// operand classes. + void buildOperandClasses(); + + void buildInstructionOperandReference(MatchableInfo *II, StringRef OpName, + unsigned AsmOpIdx); + void buildAliasOperandReference(MatchableInfo *II, StringRef OpName, + MatchableInfo::AsmOperand &Op); + +public: + AsmMatcherInfo(Record *AsmParser, + CodeGenTarget &Target, + RecordKeeper &Records); + + /// Construct the various tables used during matching. + void buildInfo(); + + /// buildOperandMatchInfo - Build the necessary information to handle user + /// defined operand parsing methods. + void buildOperandMatchInfo(); + + /// getSubtargetFeature - Lookup or create the subtarget feature info for the + /// given operand. + const SubtargetFeatureInfo *getSubtargetFeature(Record *Def) const { assert(Def->isSubClassOf("Predicate") && "Invalid predicate type!"); const auto &I = SubtargetFeatures.find(Def); return I == SubtargetFeatures.end() ? nullptr : &I->second; -} + } -bool AsmMatcherInfo::hasOptionalOperands() const { -return any_of(Classes, - [](const ClassInfo &Class) { return Class.IsOptional; }); -} + RecordKeeper &getRecords() const { + return Records; + } + + bool hasOptionalOperands() const { + return any_of(Classes, + [](const ClassInfo &Class) { return Class.IsOptional; }); + } +}; +} // end anonymous namespace + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void MatchableInfo::dump() const { errs() << TheDef->getName() << " -- " << "flattened:\"" << AsmString <<"\"\n"; @@ -676,13 +1101,7 @@ bool MatchableInfo::validate(StringRef CommentDelimiter, bool IsAlias) const { << "ignoring instruction with tied operand '" << Tok << "'\n"; }); - // Capstone: There are ARM instructions like: - // vst2${p}.32 {$Vd[$lane], $src2[$lane]}, $Rn$Rm - // The $lane operand would prevent this instruction to be added to a - // Matchable. This would mean that these instructions - // get no mappable info. - // So we only care about this. - return true; + return false; } } @@ -1527,7 +1946,7 @@ static unsigned emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, std::vector> &Infos, bool HasMnemonicFirst, bool HasOptionalOperands, - PrinterLLVM &PI) { + raw_ostream &OS) { SmallSetVector OperandConversionKinds; SmallSetVector InstructionConversionKinds; std::vector > ConversionTable; @@ -1536,17 +1955,86 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, // TargetOperandClass - This is the target's operand class, like X86Operand. std::string TargetOperandClass = Target.getName().str() + "Operand"; + // Write the convert function to a separate stream, so we can drop it after + // the enum. We'll build up the conversion handlers for the individual + // operand types opportunistically as we encounter them. + std::string ConvertFnBody; + raw_string_ostream CvtOS(ConvertFnBody); // Start the unified conversion function. - size_t MaxNumOperands = 0; - for (const auto &MI : Infos) { - MaxNumOperands = std::max(MaxNumOperands, MI->AsmOperands.size()); + if (HasOptionalOperands) { + CvtOS << "void " << Target.getName() << ClassName << "::\n" + << "convertToMCInst(unsigned Kind, MCInst &Inst, " + << "unsigned Opcode,\n" + << " const OperandVector &Operands,\n" + << " const SmallBitVector &OptionalOperandsMask) {\n"; + } else { + CvtOS << "void " << Target.getName() << ClassName << "::\n" + << "convertToMCInst(unsigned Kind, MCInst &Inst, " + << "unsigned Opcode,\n" + << " const OperandVector &Operands) {\n"; } - PI.asmMatcherEmitConversionFunctionI(Target.getName(), ClassName, - TargetOperandClass, HasOptionalOperands, - MaxNumOperands); - + CvtOS << " assert(Kind < CVT_NUM_SIGNATURES && \"Invalid signature!\");\n"; + CvtOS << " const uint8_t *Converter = ConversionTable[Kind];\n"; + if (HasOptionalOperands) { + size_t MaxNumOperands = 0; + for (const auto &MI : Infos) { + MaxNumOperands = std::max(MaxNumOperands, MI->AsmOperands.size()); + } + CvtOS << " unsigned DefaultsOffset[" << (MaxNumOperands + 1) + << "] = { 0 };\n"; + CvtOS << " assert(OptionalOperandsMask.size() == " << (MaxNumOperands) + << ");\n"; + CvtOS << " for (unsigned i = 0, NumDefaults = 0; i < " << (MaxNumOperands) + << "; ++i) {\n"; + CvtOS << " DefaultsOffset[i + 1] = NumDefaults;\n"; + CvtOS << " NumDefaults += (OptionalOperandsMask[i] ? 1 : 0);\n"; + CvtOS << " }\n"; + } + CvtOS << " unsigned OpIdx;\n"; + CvtOS << " Inst.setOpcode(Opcode);\n"; + CvtOS << " for (const uint8_t *p = Converter; *p; p += 2) {\n"; + if (HasOptionalOperands) { + CvtOS << " OpIdx = *(p + 1) - DefaultsOffset[*(p + 1)];\n"; + } else { + CvtOS << " OpIdx = *(p + 1);\n"; + } + CvtOS << " switch (*p) {\n"; + CvtOS << " default: llvm_unreachable(\"invalid conversion entry!\");\n"; + CvtOS << " case CVT_Reg:\n"; + CvtOS << " static_cast<" << TargetOperandClass + << " &>(*Operands[OpIdx]).addRegOperands(Inst, 1);\n"; + CvtOS << " break;\n"; + CvtOS << " case CVT_Tied: {\n"; + CvtOS << " assert(OpIdx < (size_t)(std::end(TiedAsmOperandTable) -\n"; + CvtOS << " std::begin(TiedAsmOperandTable)) &&\n"; + CvtOS << " \"Tied operand not found\");\n"; + CvtOS << " unsigned TiedResOpnd = TiedAsmOperandTable[OpIdx][0];\n"; + CvtOS << " if (TiedResOpnd != (uint8_t)-1)\n"; + CvtOS << " Inst.addOperand(Inst.getOperand(TiedResOpnd));\n"; + CvtOS << " break;\n"; + CvtOS << " }\n"; + + std::string OperandFnBody; + raw_string_ostream OpOS(OperandFnBody); // Start the operand number lookup function. - PI.asmMatcherEmitOperandFunctionI(Target.getName(), ClassName); + OpOS << "void " << Target.getName() << ClassName << "::\n" + << "convertToMapAndConstraints(unsigned Kind,\n"; + OpOS.indent(27); + OpOS << "const OperandVector &Operands) {\n" + << " assert(Kind < CVT_NUM_SIGNATURES && \"Invalid signature!\");\n" + << " unsigned NumMCOperands = 0;\n" + << " const uint8_t *Converter = ConversionTable[Kind];\n" + << " for (const uint8_t *p = Converter; *p; p += 2) {\n" + << " switch (*p) {\n" + << " default: llvm_unreachable(\"invalid conversion entry!\");\n" + << " case CVT_Reg:\n" + << " Operands[*(p + 1)]->setMCOperandNum(NumMCOperands);\n" + << " Operands[*(p + 1)]->setConstraint(\"r\");\n" + << " ++NumMCOperands;\n" + << " break;\n" + << " case CVT_Tied:\n" + << " ++NumMCOperands;\n" + << " break;\n"; // Pre-populate the operand conversion kinds with the standard always // available entries. @@ -1582,9 +2070,10 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, ConversionTable.back().push_back(CVT_Done); // Add the handler to the conversion driver function. - PI.asmMatcherEmitConversionFunctionII( - getEnumNameForToken(AsmMatchConverter), - AsmMatchConverter); + CvtOS << " case CVT_" + << getEnumNameForToken(AsmMatchConverter) << ":\n" + << " " << AsmMatchConverter << "(Inst, Operands);\n" + << " break;\n"; // FIXME: Handle the operand number lookup for custom match functions. continue; @@ -1641,12 +2130,37 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, // This is a new operand kind. Add a handler for it to the // converter driver. - PI.asmMatcherEmitConversionFunctionIII( - Name, TargetOperandClass, HasOptionalOperands, - Op, OpInfo); + CvtOS << " case " << Name << ":\n"; + if (Op.Class->IsOptional) { + // If optional operand is not present in actual instruction then we + // should call its DefaultMethod before RenderMethod + assert(HasOptionalOperands); + CvtOS << " if (OptionalOperandsMask[*(p + 1) - 1]) {\n" + << " " << Op.Class->DefaultMethod << "()" + << "->" << Op.Class->RenderMethod << "(Inst, " + << OpInfo.MINumOperands << ");\n" + << " } else {\n" + << " static_cast<" << TargetOperandClass + << " &>(*Operands[OpIdx])." << Op.Class->RenderMethod + << "(Inst, " << OpInfo.MINumOperands << ");\n" + << " }\n"; + } else { + CvtOS << " static_cast<" << TargetOperandClass + << " &>(*Operands[OpIdx])." << Op.Class->RenderMethod + << "(Inst, " << OpInfo.MINumOperands << ");\n"; + } + CvtOS << " break;\n"; // Add a handler for the operand number lookup. - PI.asmMatcherEmitOperandFunctionII(Name, Op, OpInfo); + OpOS << " case " << Name << ":\n" + << " Operands[*(p + 1)]->setMCOperandNum(NumMCOperands);\n"; + + if (Op.Class->isRegisterClass()) + OpOS << " Operands[*(p + 1)]->setConstraint(\"r\");\n"; + else + OpOS << " Operands[*(p + 1)]->setConstraint(\"m\");\n"; + OpOS << " NumMCOperands += " << OpInfo.MINumOperands << ";\n" + << " break;\n"; break; } case MatchableInfo::ResOperand::TiedOperand: { @@ -1690,9 +2204,15 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, if (!IsNewConverter) break; - PI.asmMatcherEmitConversionFunctionIV(Name, Val); - PI.asmMatcherEmitOperandFunctionIII(Name); + CvtOS << " case " << Name << ":\n" + << " Inst.addOperand(MCOperand::createImm(" << Val << "));\n" + << " break;\n"; + OpOS << " case " << Name << ":\n" + << " Operands[*(p + 1)]->setMCOperandNum(NumMCOperands);\n" + << " Operands[*(p + 1)]->setConstraint(\"\");\n" + << " ++NumMCOperands;\n" + << " break;\n"; break; } case MatchableInfo::ResOperand::RegOperand: { @@ -1715,8 +2235,15 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, if (!IsNewConverter) break; - PI.asmMatcherEmitConversionFunctionV(Name, Reg); - PI.asmMatcherEmitOperandFunctionIV(Name); + CvtOS << " case " << Name << ":\n" + << " Inst.addOperand(MCOperand::createReg(" << Reg << "));\n" + << " break;\n"; + + OpOS << " case " << Name << ":\n" + << " Operands[*(p + 1)]->setMCOperandNum(NumMCOperands);\n" + << " Operands[*(p + 1)]->setConstraint(\"m\");\n" + << " ++NumMCOperands;\n" + << " break;\n"; } } } @@ -1737,10 +2264,10 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, } // Finish up the converter driver function. - PI.asmMatcherEmitConversionFunctionVI(); + CvtOS << " }\n }\n}\n\n"; // Finish up the operand number lookup function. - PI.asmMatcherEmitOperandFunctionV(); + OpOS << " }\n }\n}\n\n"; // Output a static table for tied operands. if (TiedOperandsEnumMap.size()) { @@ -1751,33 +2278,80 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, "an 8bit offset from the conversion table, where index " "'255' is reserved as operand not to be copied."); - PI.asmMatcherEmitTiedOperandEnum(TiedOperandsEnumMap); - - PI.asmMatcherEmitTiedOpTable(TiedOperandsEnumMap); + OS << "enum {\n"; + for (auto &KV : TiedOperandsEnumMap) { + OS << " " << KV.second << ",\n"; + } + OS << "};\n\n"; + + OS << "static const uint8_t TiedAsmOperandTable[][3] = {\n"; + for (auto &KV : TiedOperandsEnumMap) { + OS << " /* " << KV.second << " */ { " + << utostr(std::get<0>(KV.first)) << ", " + << utostr(std::get<1>(KV.first)) << ", " + << utostr(std::get<2>(KV.first)) << " },\n"; + } + OS << "};\n\n"; } else - PI.asmMatcherEmitTiedOpEmptyTable(); + OS << "static const uint8_t TiedAsmOperandTable[][3] = " + "{ /* empty */ {0, 0, 0} };\n\n"; - PI.emitNamespace("", true); + OS << "namespace {\n"; // Output the operand conversion kind enum. - PI.asmMatcherEmitOperandConvKindEnum(OperandConversionKinds); + OS << "enum OperatorConversionKind {\n"; + for (const auto &Converter : OperandConversionKinds) + OS << " " << Converter << ",\n"; + OS << " CVT_NUM_CONVERTERS\n"; + OS << "};\n\n"; // Output the instruction conversion kind enum. - PI.asmMatcherEmitInstrConvKindEnum(InstructionConversionKinds); + OS << "enum InstructionConversionKind {\n"; + for (const auto &Signature : InstructionConversionKinds) + OS << " " << Signature << ",\n"; + OS << " CVT_NUM_SIGNATURES\n"; + OS << "};\n\n"; - PI.emitNamespace("", false); + OS << "} // end anonymous namespace\n\n"; // Output the conversion table. - PI.asmMatcherEmitConversionTable(MaxRowLength, - ConversionTable, - InstructionConversionKinds, - OperandConversionKinds, - TiedOperandsEnumMap); + OS << "static const uint8_t ConversionTable[CVT_NUM_SIGNATURES][" + << MaxRowLength << "] = {\n"; + + for (unsigned Row = 0, ERow = ConversionTable.size(); Row != ERow; ++Row) { + assert(ConversionTable[Row].size() % 2 == 0 && "bad conversion row!"); + OS << " // " << InstructionConversionKinds[Row] << "\n"; + OS << " { "; + for (unsigned i = 0, e = ConversionTable[Row].size(); i != e; i += 2) { + OS << OperandConversionKinds[ConversionTable[Row][i]] << ", "; + if (OperandConversionKinds[ConversionTable[Row][i]] != + CachedHashString("CVT_Tied")) { + OS << (unsigned)(ConversionTable[Row][i + 1]) << ", "; + continue; + } + + // For a tied operand, emit a reference to the TiedAsmOperandTable + // that contains the operand to copy, and the parsed operands to + // check for their tied constraints. + auto Key = std::make_tuple((uint8_t)ConversionTable[Row][i + 1], + (uint8_t)ConversionTable[Row][i + 2], + (uint8_t)ConversionTable[Row][i + 3]); + auto TiedOpndEnum = TiedOperandsEnumMap.find(Key); + assert(TiedOpndEnum != TiedOperandsEnumMap.end() && + "No record for tied operand pair"); + OS << TiedOpndEnum->second << ", "; + i += 2; + } + OS << "CVT_Done },\n"; + } + + OS << "};\n\n"; // Spit out the conversion driver function. - PI.asmMatcherWriteCvtOSToOS(); + OS << CvtOS.str(); + // Spit out the operand number lookup function. - PI.asmMatcherWriteOpOSToOS(); + OS << OpOS.str(); return ConversionTable.size(); } @@ -1785,29 +2359,179 @@ emitConvertFuncs(CodeGenTarget &Target, StringRef ClassName, /// emitMatchClassEnumeration - Emit the enumeration for match class kinds. static void emitMatchClassEnumeration(CodeGenTarget &Target, std::forward_list &Infos, - PrinterLLVM const &PI) { - PI.emitNamespace("", true); - PI.asmMatcherEmitMatchClassKindEnum(Infos); - PI.emitNamespace("", false); + raw_ostream &OS) { + OS << "namespace {\n\n"; + + OS << "/// MatchClassKind - The kinds of classes which participate in\n" + << "/// instruction matching.\n"; + OS << "enum MatchClassKind {\n"; + OS << " InvalidMatchClass = 0,\n"; + OS << " OptionalMatchClass = 1,\n"; + ClassInfo::ClassInfoKind LastKind = ClassInfo::Token; + StringRef LastName = "OptionalMatchClass"; + for (const auto &CI : Infos) { + if (LastKind == ClassInfo::Token && CI.Kind != ClassInfo::Token) { + OS << " MCK_LAST_TOKEN = " << LastName << ",\n"; + } else if (LastKind < ClassInfo::UserClass0 && + CI.Kind >= ClassInfo::UserClass0) { + OS << " MCK_LAST_REGISTER = " << LastName << ",\n"; + } + LastKind = (ClassInfo::ClassInfoKind)CI.Kind; + LastName = CI.Name; + + OS << " " << CI.Name << ", // "; + if (CI.Kind == ClassInfo::Token) { + OS << "'" << CI.ValueName << "'\n"; + } else if (CI.isRegisterClass()) { + if (!CI.ValueName.empty()) + OS << "register class '" << CI.ValueName << "'\n"; + else + OS << "derived register class\n"; + } else { + OS << "user defined class '" << CI.ValueName << "'\n"; + } + } + OS << " NumMatchClassKinds\n"; + OS << "};\n\n"; + + OS << "} // end anonymous namespace\n\n"; } /// emitMatchClassDiagStrings - Emit a function to get the diagnostic text to be /// used when an assembly operand does not match the expected operand class. -static void emitOperandMatchErrorDiagStrings(AsmMatcherInfo &Info, PrinterLLVM const &PI) { +static void emitOperandMatchErrorDiagStrings(AsmMatcherInfo &Info, raw_ostream &OS) { // If the target does not use DiagnosticString for any operands, don't emit // an unused function. if (llvm::all_of(Info.Classes, [](const ClassInfo &CI) { return CI.DiagnosticString.empty(); })) return; - PI.asmMatcherEmitMatchClassDiagStrings(Info); + + OS << "static const char *getMatchKindDiag(" << Info.Target.getName() + << "AsmParser::" << Info.Target.getName() + << "MatchResultTy MatchResult) {\n"; + OS << " switch (MatchResult) {\n"; + + for (const auto &CI: Info.Classes) { + if (!CI.DiagnosticString.empty()) { + assert(!CI.DiagnosticType.empty() && + "DiagnosticString set without DiagnosticType"); + OS << " case " << Info.Target.getName() + << "AsmParser::Match_" << CI.DiagnosticType << ":\n"; + OS << " return \"" << CI.DiagnosticString << "\";\n"; + } + } + + OS << " default:\n"; + OS << " return nullptr;\n"; + + OS << " }\n"; + OS << "}\n\n"; +} + +static void emitRegisterMatchErrorFunc(AsmMatcherInfo &Info, raw_ostream &OS) { + OS << "static unsigned getDiagKindFromRegisterClass(MatchClassKind " + "RegisterClass) {\n"; + if (none_of(Info.Classes, [](const ClassInfo &CI) { + return CI.isRegisterClass() && !CI.DiagnosticType.empty(); + })) { + OS << " return MCTargetAsmParser::Match_InvalidOperand;\n"; + } else { + OS << " switch (RegisterClass) {\n"; + for (const auto &CI: Info.Classes) { + if (CI.isRegisterClass() && !CI.DiagnosticType.empty()) { + OS << " case " << CI.Name << ":\n"; + OS << " return " << Info.Target.getName() << "AsmParser::Match_" + << CI.DiagnosticType << ";\n"; + } + } + + OS << " default:\n"; + OS << " return MCTargetAsmParser::Match_InvalidOperand;\n"; + + OS << " }\n"; + } + OS << "}\n\n"; +} + +/// emitValidateOperandClass - Emit the function to validate an operand class. +static void emitValidateOperandClass(AsmMatcherInfo &Info, + raw_ostream &OS) { + OS << "static unsigned validateOperandClass(MCParsedAsmOperand &GOp, " + << "MatchClassKind Kind) {\n"; + OS << " " << Info.Target.getName() << "Operand &Operand = (" + << Info.Target.getName() << "Operand &)GOp;\n"; + + // The InvalidMatchClass is not to match any operand. + OS << " if (Kind == InvalidMatchClass)\n"; + OS << " return MCTargetAsmParser::Match_InvalidOperand;\n\n"; + + // Check for Token operands first. + // FIXME: Use a more specific diagnostic type. + OS << " if (Operand.isToken() && Kind <= MCK_LAST_TOKEN)\n"; + OS << " return isSubclass(matchTokenString(Operand.getToken()), Kind) ?\n" + << " MCTargetAsmParser::Match_Success :\n" + << " MCTargetAsmParser::Match_InvalidOperand;\n\n"; + + // Check the user classes. We don't care what order since we're only + // actually matching against one of them. + OS << " switch (Kind) {\n" + " default: break;\n"; + for (const auto &CI : Info.Classes) { + if (!CI.isUserClass()) + continue; + + OS << " // '" << CI.ClassName << "' class\n"; + OS << " case " << CI.Name << ": {\n"; + OS << " DiagnosticPredicate DP(Operand." << CI.PredicateMethod + << "());\n"; + OS << " if (DP.isMatch())\n"; + OS << " return MCTargetAsmParser::Match_Success;\n"; + if (!CI.DiagnosticType.empty()) { + OS << " if (DP.isNearMatch())\n"; + OS << " return " << Info.Target.getName() << "AsmParser::Match_" + << CI.DiagnosticType << ";\n"; + OS << " break;\n"; + } + else + OS << " break;\n"; + OS << " }\n"; + } + OS << " } // end switch (Kind)\n\n"; + + // Check for register operands, including sub-classes. + OS << " if (Operand.isReg()) {\n"; + OS << " MatchClassKind OpKind;\n"; + OS << " switch (Operand.getReg()) {\n"; + OS << " default: OpKind = InvalidMatchClass; break;\n"; + for (const auto &RC : Info.RegisterClasses) + OS << " case " << RC.first->getValueAsString("Namespace") << "::" + << RC.first->getName() << ": OpKind = " << RC.second->Name + << "; break;\n"; + OS << " }\n"; + OS << " return isSubclass(OpKind, Kind) ? " + << "(unsigned)MCTargetAsmParser::Match_Success :\n " + << " getDiagKindFromRegisterClass(Kind);\n }\n\n"; + + // Expected operand is a register, but actual is not. + OS << " if (Kind > MCK_LAST_TOKEN && Kind <= MCK_LAST_REGISTER)\n"; + OS << " return getDiagKindFromRegisterClass(Kind);\n\n"; + + // Generic fallthrough match failure case for operands that don't have + // specialized diagnostic types. + OS << " return MCTargetAsmParser::Match_InvalidOperand;\n"; + OS << "}\n\n"; } /// emitIsSubclass - Emit the subclass predicate function. static void emitIsSubclass(CodeGenTarget &Target, std::forward_list &Infos, - PrinterLLVM const &PI) { - PI.asmMatcherEmitIsSubclassI(); + raw_ostream &OS) { + OS << "/// isSubclass - Compute whether \\p A is a subclass of \\p B.\n"; + OS << "static bool isSubclass(MatchClassKind A, MatchClassKind B) {\n"; + OS << " if (A == B)\n"; + OS << " return true;\n\n"; + bool EmittedSwitch = false; for (const auto &A : Infos) { std::vector SuperClasses; @@ -1822,26 +2546,47 @@ static void emitIsSubclass(CodeGenTarget &Target, continue; // If this is the first SuperClass, emit the switch header. - EmittedSwitch = PI.asmMatcherEmitIsSubclassII(EmittedSwitch, A.Name); + if (!EmittedSwitch) { + OS << " switch (A) {\n"; + OS << " default:\n"; + OS << " return false;\n"; + EmittedSwitch = true; + } + + OS << "\n case " << A.Name << ":\n"; if (SuperClasses.size() == 1) { - PI.asmMatcherEmitIsSubclassIII(SuperClasses.back()); + OS << " return B == " << SuperClasses.back() << ";\n"; continue; } - PI.asmMatcherEmitIsSubclassIV(SuperClasses); + if (!SuperClasses.empty()) { + OS << " switch (B) {\n"; + OS << " default: return false;\n"; + for (StringRef SC : SuperClasses) + OS << " case " << SC << ": return true;\n"; + OS << " }\n"; + } else { + // No case statement to emit + OS << " return false;\n"; + } } // If there were case statements emitted into the string stream write the // default. - PI.asmMatcherEmitIsSubclassV(EmittedSwitch); + if (EmittedSwitch) + OS << " }\n"; + else + OS << " return false;\n"; + + OS << "}\n\n"; } /// emitMatchTokenString - Emit the function to match a token string to the /// appropriate match class value. static void emitMatchTokenString(CodeGenTarget &Target, std::forward_list &Infos, - PrinterLLVM const &PI) { + raw_ostream &OS) { // Construct the match list. std::vector Matches; for (const auto &CI : Infos) { @@ -1849,13 +2594,18 @@ static void emitMatchTokenString(CodeGenTarget &Target, Matches.emplace_back(CI.ValueName, "return " + CI.Name + ";"); } - PI.asmMatcherEmitMatchTokenString(Matches); + OS << "static MatchClassKind matchTokenString(StringRef Name) {\n"; + + StringMatcher("Name", Matches, OS).Emit(); + + OS << " return InvalidMatchClass;\n"; + OS << "}\n\n"; } /// emitMatchRegisterName - Emit the function to match a string to the target /// specific register enum. static void emitMatchRegisterName(CodeGenTarget &Target, Record *AsmParser, - PrinterLLVM const &PI) { + raw_ostream &OS) { // Construct the match list. std::vector Matches; const auto &Regs = Target.getRegBank().getRegisters(); @@ -1867,13 +2617,20 @@ static void emitMatchRegisterName(CodeGenTarget &Target, Record *AsmParser, "return " + utostr(Reg.EnumValue) + ";"); } - PI.asmMatcherEmitMatchRegisterName(AsmParser, Matches); + OS << "static unsigned MatchRegisterName(StringRef Name) {\n"; + + bool IgnoreDuplicates = + AsmParser->getValueAsBit("AllowDuplicateRegisterNames"); + StringMatcher("Name", Matches, OS).Emit(0, IgnoreDuplicates); + + OS << " return 0;\n"; + OS << "}\n\n"; } /// Emit the function to match a string to the target /// specific register enum. static void emitMatchRegisterAltName(CodeGenTarget &Target, Record *AsmParser, - PrinterLLVM const &PI) { + raw_ostream &OS) { // Construct the match list. std::vector Matches; const auto &Regs = Target.getRegBank().getRegisters(); @@ -1892,11 +2649,19 @@ static void emitMatchRegisterAltName(CodeGenTarget &Target, Record *AsmParser, "return " + utostr(Reg.EnumValue) + ";"); } } - PI.asmMatcherEmitMatchRegisterAltName(AsmParser, Matches); + + OS << "static unsigned MatchRegisterAltName(StringRef Name) {\n"; + + bool IgnoreDuplicates = + AsmParser->getValueAsBit("AllowDuplicateRegisterNames"); + StringMatcher("Name", Matches, OS).Emit(0, IgnoreDuplicates); + + OS << " return 0;\n"; + OS << "}\n\n"; } /// emitOperandDiagnosticTypes - Emit the operand matching diagnostic types. -static void emitOperandDiagnosticTypes(AsmMatcherInfo &Info, PrinterLLVM const &PI) { +static void emitOperandDiagnosticTypes(AsmMatcherInfo &Info, raw_ostream &OS) { // Get the set of diagnostic types from all of the operand classes. std::set Types; for (const auto &OpClassEntry : Info.AsmOperandClasses) { @@ -1911,7 +2676,32 @@ static void emitOperandDiagnosticTypes(AsmMatcherInfo &Info, PrinterLLVM const & if (Types.empty()) return; // Now emit the enum entries. - PI.asmMatcherEmitOperandDiagTypes(Types); + for (StringRef Type : Types) + OS << " Match_" << Type << ",\n"; + OS << " END_OPERAND_DIAGNOSTIC_TYPES\n"; +} + +/// emitGetSubtargetFeatureName - Emit the helper function to get the +/// user-level name for a subtarget feature. +static void emitGetSubtargetFeatureName(AsmMatcherInfo &Info, raw_ostream &OS) { + OS << "// User-level names for subtarget features that participate in\n" + << "// instruction matching.\n" + << "static const char *getSubtargetFeatureName(uint64_t Val) {\n"; + if (!Info.SubtargetFeatures.empty()) { + OS << " switch(Val) {\n"; + for (const auto &SF : Info.SubtargetFeatures) { + const SubtargetFeatureInfo &SFI = SF.second; + // FIXME: Totally just a placeholder name to get the algorithm working. + OS << " case " << SFI.getEnumBitName() << ": return \"" + << SFI.TheDef->getValueAsString("PredicateName") << "\";\n"; + } + OS << " default: return \"(unknown)\";\n"; + OS << " }\n"; + } else { + // Nothing to emit, so skip the switch + OS << " return \"(unknown)\";\n"; + } + OS << "}\n\n"; } static std::string GetAliasRequiredFeatures(Record *R, @@ -1938,7 +2728,7 @@ static std::string GetAliasRequiredFeatures(Record *R, return Result; } -static void emitMnemonicAliasVariant(PrinterLLVM const &PI, const AsmMatcherInfo &Info, +static void emitMnemonicAliasVariant(raw_ostream &OS,const AsmMatcherInfo &Info, std::vector &Aliases, unsigned Indent = 0, StringRef AsmParserVariantName = StringRef()){ @@ -1993,24 +2783,33 @@ static void emitMnemonicAliasVariant(PrinterLLVM const &PI, const AsmMatcherInfo if (R->getValueAsString("ToMnemonic") == AliasEntry.first) PrintFatalError(R->getLoc(), "MnemonicAlias to the same string"); - PI.asmMatcherAppendMnemonicAlias(R, FeatureMask, MatchCode); + if (!MatchCode.empty()) + MatchCode += "else "; + MatchCode += "if (" + FeatureMask + ")\n"; + MatchCode += " Mnemonic = \""; + MatchCode += R->getValueAsString("ToMnemonic").lower(); + MatchCode += "\";\n"; } if (AliasWithNoPredicate != -1) { Record *R = ToVec[AliasWithNoPredicate]; - PI.asmMatcherAppendMnemonic(R, MatchCode); + if (!MatchCode.empty()) + MatchCode += "else\n "; + MatchCode += "Mnemonic = \""; + MatchCode += R->getValueAsString("ToMnemonic").lower(); + MatchCode += "\";\n"; } - PI.asmMatcherAppendMnemonicAliasEnd(MatchCode); + MatchCode += "return;"; Cases.push_back(std::make_pair(AliasEntry.first, MatchCode)); } - PI.asmMatcherEmitMnemonicAliasVariant(Cases, Indent); + StringMatcher("Mnemonic", Cases, OS).Emit(Indent); } /// emitMnemonicAliases - If the target has any MnemonicAlias<> definitions, /// emit a function for them and return true, otherwise return false. -static bool emitMnemonicAliases(PrinterLLVM const &PI, const AsmMatcherInfo &Info, +static bool emitMnemonicAliases(raw_ostream &OS, const AsmMatcherInfo &Info, CodeGenTarget &Target) { // Ignore aliases when match-prefix is set. if (!MatchPrefix.empty()) @@ -2020,29 +2819,385 @@ static bool emitMnemonicAliases(PrinterLLVM const &PI, const AsmMatcherInfo &Inf Info.getRecords().getAllDerivedDefinitions("MnemonicAlias"); if (Aliases.empty()) return false; - PI.asmMatcherEmitApplyMnemonicAliasesI(); + OS << "static void applyMnemonicAliases(StringRef &Mnemonic, " + "const FeatureBitset &Features, unsigned VariantID) {\n"; + OS << " switch (VariantID) {\n"; unsigned VariantCount = Target.getAsmParserVariantCount(); for (unsigned VC = 0; VC != VariantCount; ++VC) { Record *AsmVariant = Target.getAsmParserVariant(VC); int AsmParserVariantNo = AsmVariant->getValueAsInt("Variant"); StringRef AsmParserVariantName = AsmVariant->getValueAsString("Name"); - PI.asmMatcherEmitApplyMnemonicAliasesII(AsmParserVariantNo); - emitMnemonicAliasVariant(PI, Info, Aliases, /*Indent=*/2, + OS << " case " << AsmParserVariantNo << ":\n"; + emitMnemonicAliasVariant(OS, Info, Aliases, /*Indent=*/2, AsmParserVariantName); - PI.asmMatcherEmitApplyMnemonicAliasesIII(); + OS << " break;\n"; } - PI.asmMatcherEmitApplyMnemonicAliasesIV(); + OS << " }\n"; // Emit aliases that apply to all variants. - emitMnemonicAliasVariant(PI, Info, Aliases); + emitMnemonicAliasVariant(OS, Info, Aliases); - PI.asmMatcherEmitApplyMnemonicAliasesV(); + OS << "}\n\n"; return true; } -void AsmMatcherEmitter::run() { - PI.asmMatcherEmitSourceFileHeader("Assembly Matcher Source Fragment"); +static void +emitCustomOperandParsing(raw_ostream &OS, CodeGenTarget &Target, + const AsmMatcherInfo &Info, StringRef ClassName, + StringToOffsetTable &StringTable, + unsigned MaxMnemonicIndex, unsigned MaxFeaturesIndex, + bool HasMnemonicFirst, const Record &AsmParser) { + unsigned MaxMask = 0; + for (const OperandMatchEntry &OMI : Info.OperandMatchInfo) { + MaxMask |= OMI.OperandMask; + } + + // Emit the static custom operand parsing table; + OS << "namespace {\n"; + OS << " struct OperandMatchEntry {\n"; + OS << " " << getMinimalTypeForRange(MaxMnemonicIndex) + << " Mnemonic;\n"; + OS << " " << getMinimalTypeForRange(MaxMask) + << " OperandMask;\n"; + OS << " " << getMinimalTypeForRange(std::distance( + Info.Classes.begin(), Info.Classes.end())) << " Class;\n"; + OS << " " << getMinimalTypeForRange(MaxFeaturesIndex) + << " RequiredFeaturesIdx;\n\n"; + OS << " StringRef getMnemonic() const {\n"; + OS << " return StringRef(MnemonicTable + Mnemonic + 1,\n"; + OS << " MnemonicTable[Mnemonic]);\n"; + OS << " }\n"; + OS << " };\n\n"; + + OS << " // Predicate for searching for an opcode.\n"; + OS << " struct LessOpcodeOperand {\n"; + OS << " bool operator()(const OperandMatchEntry &LHS, StringRef RHS) {\n"; + OS << " return LHS.getMnemonic() < RHS;\n"; + OS << " }\n"; + OS << " bool operator()(StringRef LHS, const OperandMatchEntry &RHS) {\n"; + OS << " return LHS < RHS.getMnemonic();\n"; + OS << " }\n"; + OS << " bool operator()(const OperandMatchEntry &LHS,"; + OS << " const OperandMatchEntry &RHS) {\n"; + OS << " return LHS.getMnemonic() < RHS.getMnemonic();\n"; + OS << " }\n"; + OS << " };\n"; + + OS << "} // end anonymous namespace\n\n"; + + OS << "static const OperandMatchEntry OperandMatchTable[" + << Info.OperandMatchInfo.size() << "] = {\n"; + + OS << " /* Operand List Mnemonic, Mask, Operand Class, Features */\n"; + for (const OperandMatchEntry &OMI : Info.OperandMatchInfo) { + const MatchableInfo &II = *OMI.MI; + + OS << " { "; + + // Store a pascal-style length byte in the mnemonic. + std::string LenMnemonic = char(II.Mnemonic.size()) + II.Mnemonic.lower(); + OS << StringTable.GetOrAddStringOffset(LenMnemonic, false) + << " /* " << II.Mnemonic << " */, "; + + OS << OMI.OperandMask; + OS << " /* "; + ListSeparator LS; + for (int i = 0, e = 31; i !=e; ++i) + if (OMI.OperandMask & (1 << i)) + OS << LS << i; + OS << " */, "; + + OS << OMI.CI->Name; + + // Write the required features mask. + OS << ", AMFBS"; + if (II.RequiredFeatures.empty()) + OS << "_None"; + else + for (unsigned i = 0, e = II.RequiredFeatures.size(); i != e; ++i) + OS << '_' << II.RequiredFeatures[i]->TheDef->getName(); + + OS << " },\n"; + } + OS << "};\n\n"; + + // Emit the operand class switch to call the correct custom parser for + // the found operand class. + OS << "OperandMatchResultTy " << Target.getName() << ClassName << "::\n" + << "tryCustomParseOperand(OperandVector" + << " &Operands,\n unsigned MCK) {\n\n" + << " switch(MCK) {\n"; + + for (const auto &CI : Info.Classes) { + if (CI.ParserMethod.empty()) + continue; + OS << " case " << CI.Name << ":\n" + << " return " << CI.ParserMethod << "(Operands);\n"; + } + + OS << " default:\n"; + OS << " return MatchOperand_NoMatch;\n"; + OS << " }\n"; + OS << " return MatchOperand_NoMatch;\n"; + OS << "}\n\n"; + + // Emit the static custom operand parser. This code is very similar with + // the other matcher. Also use MatchResultTy here just in case we go for + // a better error handling. + OS << "OperandMatchResultTy " << Target.getName() << ClassName << "::\n" + << "MatchOperandParserImpl(OperandVector" + << " &Operands,\n StringRef Mnemonic,\n" + << " bool ParseForAllFeatures) {\n"; + + // Emit code to get the available features. + OS << " // Get the current feature set.\n"; + OS << " const FeatureBitset &AvailableFeatures = getAvailableFeatures();\n\n"; + + OS << " // Get the next operand index.\n"; + OS << " unsigned NextOpNum = Operands.size()" + << (HasMnemonicFirst ? " - 1" : "") << ";\n"; + + // Emit code to search the table. + OS << " // Search the table.\n"; + if (HasMnemonicFirst) { + OS << " auto MnemonicRange =\n"; + OS << " std::equal_range(std::begin(OperandMatchTable), " + "std::end(OperandMatchTable),\n"; + OS << " Mnemonic, LessOpcodeOperand());\n\n"; + } else { + OS << " auto MnemonicRange = std::make_pair(std::begin(OperandMatchTable)," + " std::end(OperandMatchTable));\n"; + OS << " if (!Mnemonic.empty())\n"; + OS << " MnemonicRange =\n"; + OS << " std::equal_range(std::begin(OperandMatchTable), " + "std::end(OperandMatchTable),\n"; + OS << " Mnemonic, LessOpcodeOperand());\n\n"; + } + + OS << " if (MnemonicRange.first == MnemonicRange.second)\n"; + OS << " return MatchOperand_NoMatch;\n\n"; + + OS << " for (const OperandMatchEntry *it = MnemonicRange.first,\n" + << " *ie = MnemonicRange.second; it != ie; ++it) {\n"; + + OS << " // equal_range guarantees that instruction mnemonic matches.\n"; + OS << " assert(Mnemonic == it->getMnemonic());\n\n"; + + // Emit check that the required features are available. + OS << " // check if the available features match\n"; + OS << " const FeatureBitset &RequiredFeatures = " + "FeatureBitsets[it->RequiredFeaturesIdx];\n"; + OS << " if (!ParseForAllFeatures && (AvailableFeatures & " + "RequiredFeatures) != RequiredFeatures)\n"; + OS << " continue;\n\n"; + + // Emit check to ensure the operand number matches. + OS << " // check if the operand in question has a custom parser.\n"; + OS << " if (!(it->OperandMask & (1 << NextOpNum)))\n"; + OS << " continue;\n\n"; + + // Emit call to the custom parser method + StringRef ParserName = AsmParser.getValueAsString("OperandParserMethod"); + if (ParserName.empty()) + ParserName = "tryCustomParseOperand"; + OS << " // call custom parse method to handle the operand\n"; + OS << " OperandMatchResultTy Result = " << ParserName + << "(Operands, it->Class);\n"; + OS << " if (Result != MatchOperand_NoMatch)\n"; + OS << " return Result;\n"; + OS << " }\n\n"; + + OS << " // Okay, we had no match.\n"; + OS << " return MatchOperand_NoMatch;\n"; + OS << "}\n\n"; +} + +static void emitAsmTiedOperandConstraints(CodeGenTarget &Target, + AsmMatcherInfo &Info, + raw_ostream &OS) { + std::string AsmParserName = + std::string(Info.AsmParser->getValueAsString("AsmParserClassName")); + OS << "static bool "; + OS << "checkAsmTiedOperandConstraints(const " << Target.getName() + << AsmParserName << "&AsmParser,\n"; + OS << " unsigned Kind,\n"; + OS << " const OperandVector &Operands,\n"; + OS << " uint64_t &ErrorInfo) {\n"; + OS << " assert(Kind < CVT_NUM_SIGNATURES && \"Invalid signature!\");\n"; + OS << " const uint8_t *Converter = ConversionTable[Kind];\n"; + OS << " for (const uint8_t *p = Converter; *p; p += 2) {\n"; + OS << " switch (*p) {\n"; + OS << " case CVT_Tied: {\n"; + OS << " unsigned OpIdx = *(p + 1);\n"; + OS << " assert(OpIdx < (size_t)(std::end(TiedAsmOperandTable) -\n"; + OS << " std::begin(TiedAsmOperandTable)) &&\n"; + OS << " \"Tied operand not found\");\n"; + OS << " unsigned OpndNum1 = TiedAsmOperandTable[OpIdx][1];\n"; + OS << " unsigned OpndNum2 = TiedAsmOperandTable[OpIdx][2];\n"; + OS << " if (OpndNum1 != OpndNum2) {\n"; + OS << " auto &SrcOp1 = Operands[OpndNum1];\n"; + OS << " auto &SrcOp2 = Operands[OpndNum2];\n"; + OS << " if (!AsmParser.areEqualRegs(*SrcOp1, *SrcOp2)) {\n"; + OS << " ErrorInfo = OpndNum2;\n"; + OS << " return false;\n"; + OS << " }\n"; + OS << " }\n"; + OS << " break;\n"; + OS << " }\n"; + OS << " default:\n"; + OS << " break;\n"; + OS << " }\n"; + OS << " }\n"; + OS << " return true;\n"; + OS << "}\n\n"; +} + +static void emitMnemonicSpellChecker(raw_ostream &OS, CodeGenTarget &Target, + unsigned VariantCount) { + OS << "static std::string " << Target.getName() + << "MnemonicSpellCheck(StringRef S, const FeatureBitset &FBS," + << " unsigned VariantID) {\n"; + if (!VariantCount) + OS << " return \"\";"; + else { + OS << " const unsigned MaxEditDist = 2;\n"; + OS << " std::vector Candidates;\n"; + OS << " StringRef Prev = \"\";\n\n"; + + OS << " // Find the appropriate table for this asm variant.\n"; + OS << " const MatchEntry *Start, *End;\n"; + OS << " switch (VariantID) {\n"; + OS << " default: llvm_unreachable(\"invalid variant!\");\n"; + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); + OS << " case " << AsmVariantNo << ": Start = std::begin(MatchTable" << VC + << "); End = std::end(MatchTable" << VC << "); break;\n"; + } + OS << " }\n\n"; + OS << " for (auto I = Start; I < End; I++) {\n"; + OS << " // Ignore unsupported instructions.\n"; + OS << " const FeatureBitset &RequiredFeatures = " + "FeatureBitsets[I->RequiredFeaturesIdx];\n"; + OS << " if ((FBS & RequiredFeatures) != RequiredFeatures)\n"; + OS << " continue;\n"; + OS << "\n"; + OS << " StringRef T = I->getMnemonic();\n"; + OS << " // Avoid recomputing the edit distance for the same string.\n"; + OS << " if (T.equals(Prev))\n"; + OS << " continue;\n"; + OS << "\n"; + OS << " Prev = T;\n"; + OS << " unsigned Dist = S.edit_distance(T, false, MaxEditDist);\n"; + OS << " if (Dist <= MaxEditDist)\n"; + OS << " Candidates.push_back(T);\n"; + OS << " }\n"; + OS << "\n"; + OS << " if (Candidates.empty())\n"; + OS << " return \"\";\n"; + OS << "\n"; + OS << " std::string Res = \", did you mean: \";\n"; + OS << " unsigned i = 0;\n"; + OS << " for (; i < Candidates.size() - 1; i++)\n"; + OS << " Res += Candidates[i].str() + \", \";\n"; + OS << " return Res + Candidates[i].str() + \"?\";\n"; + } + OS << "}\n"; + OS << "\n"; +} + +static void emitMnemonicChecker(raw_ostream &OS, + CodeGenTarget &Target, + unsigned VariantCount, + bool HasMnemonicFirst, + bool HasMnemonicAliases) { + OS << "static bool " << Target.getName() + << "CheckMnemonic(StringRef Mnemonic,\n"; + OS << " " + << "const FeatureBitset &AvailableFeatures,\n"; + OS << " " + << "unsigned VariantID) {\n"; + + if (!VariantCount) { + OS << " return false;\n"; + } else { + if (HasMnemonicAliases) { + OS << " // Process all MnemonicAliases to remap the mnemonic.\n"; + OS << " applyMnemonicAliases(Mnemonic, AvailableFeatures, VariantID);"; + OS << "\n\n"; + } + OS << " // Find the appropriate table for this asm variant.\n"; + OS << " const MatchEntry *Start, *End;\n"; + OS << " switch (VariantID) {\n"; + OS << " default: llvm_unreachable(\"invalid variant!\");\n"; + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); + OS << " case " << AsmVariantNo << ": Start = std::begin(MatchTable" << VC + << "); End = std::end(MatchTable" << VC << "); break;\n"; + } + OS << " }\n\n"; + + OS << " // Search the table.\n"; + if (HasMnemonicFirst) { + OS << " auto MnemonicRange = " + "std::equal_range(Start, End, Mnemonic, LessOpcode());\n\n"; + } else { + OS << " auto MnemonicRange = std::make_pair(Start, End);\n"; + OS << " unsigned SIndex = Mnemonic.empty() ? 0 : 1;\n"; + OS << " if (!Mnemonic.empty())\n"; + OS << " MnemonicRange = " + << "std::equal_range(Start, End, Mnemonic.lower(), LessOpcode());\n\n"; + } + + OS << " if (MnemonicRange.first == MnemonicRange.second)\n"; + OS << " return false;\n\n"; + + OS << " for (const MatchEntry *it = MnemonicRange.first, " + << "*ie = MnemonicRange.second;\n"; + OS << " it != ie; ++it) {\n"; + OS << " const FeatureBitset &RequiredFeatures =\n"; + OS << " FeatureBitsets[it->RequiredFeaturesIdx];\n"; + OS << " if ((AvailableFeatures & RequiredFeatures) == "; + OS << "RequiredFeatures)\n"; + OS << " return true;\n"; + OS << " }\n"; + OS << " return false;\n"; + } + OS << "}\n"; + OS << "\n"; +} + +// Emit a function mapping match classes to strings, for debugging. +static void emitMatchClassKindNames(std::forward_list &Infos, + raw_ostream &OS) { + OS << "#ifndef NDEBUG\n"; + OS << "const char *getMatchClassName(MatchClassKind Kind) {\n"; + OS << " switch (Kind) {\n"; + + OS << " case InvalidMatchClass: return \"InvalidMatchClass\";\n"; + OS << " case OptionalMatchClass: return \"OptionalMatchClass\";\n"; + for (const auto &CI : Infos) { + OS << " case " << CI.Name << ": return \"" << CI.Name << "\";\n"; + } + OS << " case NumMatchClassKinds: return \"NumMatchClassKinds\";\n"; + + OS << " }\n"; + OS << " llvm_unreachable(\"unhandled MatchClassKind!\");\n"; + OS << "}\n\n"; + OS << "#endif // NDEBUG\n"; +} + +static std::string +getNameForFeatureBitset(const std::vector &FeatureBitset) { + std::string Name = "AMFBS"; + for (const auto &Feature : FeatureBitset) + Name += ("_" + Feature->getName()).str(); + return Name; +} + +void AsmMatcherEmitter::run(raw_ostream &OS) { CodeGenTarget Target(Records); Record *AsmParser = Target.getAsmParser(); StringRef ClassName = AsmParser->getValueAsString("AsmParserClassName"); @@ -2109,72 +3264,131 @@ void AsmMatcherEmitter::run() { // Write the output. // Information for the class declaration. - PI.emitIncludeToggle("GET_ASSEMBLER_HEADER", true); - PI.asmMatcherEmitDeclarations(HasOptionalOperands, ReportMultipleNearMisses, !Info.OperandMatchInfo.empty()); - PI.emitIncludeToggle("GET_ASSEMBLER_HEADER", false); + OS << "\n#ifdef GET_ASSEMBLER_HEADER\n"; + OS << "#undef GET_ASSEMBLER_HEADER\n"; + OS << " // This should be included into the middle of the declaration of\n"; + OS << " // your subclasses implementation of MCTargetAsmParser.\n"; + OS << " FeatureBitset ComputeAvailableFeatures(const FeatureBitset &FB) const;\n"; + if (HasOptionalOperands) { + OS << " void convertToMCInst(unsigned Kind, MCInst &Inst, " + << "unsigned Opcode,\n" + << " const OperandVector &Operands,\n" + << " const SmallBitVector &OptionalOperandsMask);\n"; + } else { + OS << " void convertToMCInst(unsigned Kind, MCInst &Inst, " + << "unsigned Opcode,\n" + << " const OperandVector &Operands);\n"; + } + OS << " void convertToMapAndConstraints(unsigned Kind,\n "; + OS << " const OperandVector &Operands) override;\n"; + OS << " unsigned MatchInstructionImpl(const OperandVector &Operands,\n" + << " MCInst &Inst,\n"; + if (ReportMultipleNearMisses) + OS << " SmallVectorImpl *NearMisses,\n"; + else + OS << " uint64_t &ErrorInfo,\n" + << " FeatureBitset &MissingFeatures,\n"; + OS << " bool matchingInlineAsm,\n" + << " unsigned VariantID = 0);\n"; + if (!ReportMultipleNearMisses) + OS << " unsigned MatchInstructionImpl(const OperandVector &Operands,\n" + << " MCInst &Inst,\n" + << " uint64_t &ErrorInfo,\n" + << " bool matchingInlineAsm,\n" + << " unsigned VariantID = 0) {\n" + << " FeatureBitset MissingFeatures;\n" + << " return MatchInstructionImpl(Operands, Inst, ErrorInfo, MissingFeatures,\n" + << " matchingInlineAsm, VariantID);\n" + << " }\n\n"; + + + if (!Info.OperandMatchInfo.empty()) { + OS << " OperandMatchResultTy MatchOperandParserImpl(\n"; + OS << " OperandVector &Operands,\n"; + OS << " StringRef Mnemonic,\n"; + OS << " bool ParseForAllFeatures = false);\n"; + + OS << " OperandMatchResultTy tryCustomParseOperand(\n"; + OS << " OperandVector &Operands,\n"; + OS << " unsigned MCK);\n\n"; + } + + OS << "#endif // GET_ASSEMBLER_HEADER_INFO\n\n"; // Emit the operand match diagnostic enum names. - PI.emitIncludeToggle("GET_OPERAND_DIAGNOSTIC_TYPES", true); - emitOperandDiagnosticTypes(Info, PI); - PI.emitIncludeToggle("GET_OPERAND_DIAGNOSTIC_TYPES", false); + OS << "\n#ifdef GET_OPERAND_DIAGNOSTIC_TYPES\n"; + OS << "#undef GET_OPERAND_DIAGNOSTIC_TYPES\n\n"; + emitOperandDiagnosticTypes(Info, OS); + OS << "#endif // GET_OPERAND_DIAGNOSTIC_TYPES\n\n"; + + OS << "\n#ifdef GET_REGISTER_MATCHER\n"; + OS << "#undef GET_REGISTER_MATCHER\n\n"; - PI.emitIncludeToggle("GET_REGISTER_MATCHER", true); // Emit the subtarget feature enumeration. - PI.asmMatcherEmitSTFBitEnum(Info); + SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration( + Info.SubtargetFeatures, OS); // Emit the function to match a register name to number. // This should be omitted for Mips target if (AsmParser->getValueAsBit("ShouldEmitMatchRegisterName")) - emitMatchRegisterName(Target, AsmParser, PI); + emitMatchRegisterName(Target, AsmParser, OS); if (AsmParser->getValueAsBit("ShouldEmitMatchRegisterAltName")) - emitMatchRegisterAltName(Target, AsmParser, PI); - PI.emitIncludeToggle("GET_REGISTER_MATCHER", false); + emitMatchRegisterAltName(Target, AsmParser, OS); + + OS << "#endif // GET_REGISTER_MATCHER\n\n"; + + OS << "\n#ifdef GET_SUBTARGET_FEATURE_NAME\n"; + OS << "#undef GET_SUBTARGET_FEATURE_NAME\n\n"; - PI.emitIncludeToggle("GET_SUBTARGET_FEATURE_NAME", true); // Generate the helper function to get the names for subtarget features. - PI.asmMatcherEmitGetSubtargetFeatureName(Info.SubtargetFeatures); - PI.emitIncludeToggle("GET_SUBTARGET_FEATURE_NAME", false); + emitGetSubtargetFeatureName(Info, OS); + + OS << "#endif // GET_SUBTARGET_FEATURE_NAME\n\n"; + + OS << "\n#ifdef GET_MATCHER_IMPLEMENTATION\n"; + OS << "#undef GET_MATCHER_IMPLEMENTATION\n\n"; - PI.emitIncludeToggle("GET_MATCHER_IMPLEMENTATION", true); // Generate the function that remaps for mnemonic aliases. - bool HasMnemonicAliases = emitMnemonicAliases(PI, Info, Target); + bool HasMnemonicAliases = emitMnemonicAliases(OS, Info, Target); // Generate the convertToMCInst function to convert operands into an MCInst. // Also, generate the convertToMapAndConstraints function for MS-style inline // assembly. The latter doesn't actually generate a MCInst. unsigned NumConverters = emitConvertFuncs(Target, ClassName, Info.Matchables, HasMnemonicFirst, - HasOptionalOperands, PI); + HasOptionalOperands, OS); // Emit the enumeration for classes which participate in matching. - emitMatchClassEnumeration(Target, Info.Classes, PI); + emitMatchClassEnumeration(Target, Info.Classes, OS); // Emit a function to get the user-visible string to describe an operand // match failure in diagnostics. - emitOperandMatchErrorDiagStrings(Info, PI); + emitOperandMatchErrorDiagStrings(Info, OS); // Emit a function to map register classes to operand match failure codes. - PI.asmMatcherEmitRegisterMatchErrorFunc(Info); + emitRegisterMatchErrorFunc(Info, OS); // Emit the routine to match token strings to their match class. - emitMatchTokenString(Target, Info.Classes, PI); + emitMatchTokenString(Target, Info.Classes, OS); // Emit the subclass predicate routine. - emitIsSubclass(Target, Info.Classes, PI); + emitIsSubclass(Target, Info.Classes, OS); // Emit the routine to validate an operand against a match class. - PI.asmMatcherEmitValidateOperandClass(Info); + emitValidateOperandClass(Info, OS); - PI.asmMatcherEmitMatchClassKindNames(Info.Classes); + emitMatchClassKindNames(Info.Classes, OS); // Emit the available features compute function. - PI.asmMatcherEmitComputeAssemblerAvailableFeatures(Info, ClassName); + SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures( + Info.Target.getName(), ClassName, "ComputeAvailableFeatures", + Info.SubtargetFeatures, OS); if (!ReportMultipleNearMisses) - PI.asmMatcherEmitAsmTiedOperandConstraints(Target, Info); + emitAsmTiedOperandConstraints(Target, Info, OS); - StringToOffsetTable StringTable(PrinterLLVM::getLanguage()); + StringToOffsetTable StringTable; size_t MaxNumOperands = 0; unsigned MaxMnemonicIndex = 0; @@ -2189,7 +3403,9 @@ void AsmMatcherEmitter::run() { StringTable.GetOrAddStringOffset(LenMnemonic, false)); } - PI.asmMatcherEmitMnemonicTable(StringTable); + OS << "static const char MnemonicTable[] =\n"; + StringTable.EmitString(OS); + OS << ";\n\n"; std::vector> FeatureBitsets; for (const auto &MI : Info.Matchables) { @@ -2217,8 +3433,29 @@ void AsmMatcherEmitter::run() { FeatureBitsets.erase( std::unique(FeatureBitsets.begin(), FeatureBitsets.end()), FeatureBitsets.end()); - PI.asmMatcherEmitFeatureBitsetEnum(FeatureBitsets); - PI.asmMatcherEmitFeatureBitsets(FeatureBitsets, Info); + OS << "// Feature bitsets.\n" + << "enum : " << getMinimalTypeForRange(FeatureBitsets.size()) << " {\n" + << " AMFBS_None,\n"; + for (const auto &FeatureBitset : FeatureBitsets) { + if (FeatureBitset.empty()) + continue; + OS << " " << getNameForFeatureBitset(FeatureBitset) << ",\n"; + } + OS << "};\n\n" + << "static constexpr FeatureBitset FeatureBitsets[] = {\n" + << " {}, // AMFBS_None\n"; + for (const auto &FeatureBitset : FeatureBitsets) { + if (FeatureBitset.empty()) + continue; + OS << " {"; + for (const auto &Feature : FeatureBitset) { + const auto &I = Info.SubtargetFeatures.find(Feature); + assert(I != Info.SubtargetFeatures.end() && "Didn't import predicate?"); + OS << I->second.getEnumBitName() << ", "; + } + OS << "},\n"; + } + OS << "};\n\n"; // Emit the static match table; unused classes get initialized to 0 which is // guaranteed to be InvalidMatchClass. @@ -2230,64 +3467,543 @@ void AsmMatcherEmitter::run() { // order the match kinds appropriately (putting mnemonics last), then we // should only end up using a few bits for each class, especially the ones // following the mnemonic. - PI.emitNamespace("", true); - PI.asmMatcherEmitMatchEntryStruct(MaxMnemonicIndex, NumConverters, MaxNumOperands, - FeatureBitsets, Info); - PI.emitNamespace("", false); + OS << "namespace {\n"; + OS << " struct MatchEntry {\n"; + OS << " " << getMinimalTypeForRange(MaxMnemonicIndex) + << " Mnemonic;\n"; + OS << " uint16_t Opcode;\n"; + OS << " " << getMinimalTypeForRange(NumConverters) + << " ConvertFn;\n"; + OS << " " << getMinimalTypeForRange(FeatureBitsets.size()) + << " RequiredFeaturesIdx;\n"; + OS << " " << getMinimalTypeForRange( + std::distance(Info.Classes.begin(), Info.Classes.end())) + << " Classes[" << MaxNumOperands << "];\n"; + OS << " StringRef getMnemonic() const {\n"; + OS << " return StringRef(MnemonicTable + Mnemonic + 1,\n"; + OS << " MnemonicTable[Mnemonic]);\n"; + OS << " }\n"; + OS << " };\n\n"; + + OS << " // Predicate for searching for an opcode.\n"; + OS << " struct LessOpcode {\n"; + OS << " bool operator()(const MatchEntry &LHS, StringRef RHS) {\n"; + OS << " return LHS.getMnemonic() < RHS;\n"; + OS << " }\n"; + OS << " bool operator()(StringRef LHS, const MatchEntry &RHS) {\n"; + OS << " return LHS < RHS.getMnemonic();\n"; + OS << " }\n"; + OS << " bool operator()(const MatchEntry &LHS, const MatchEntry &RHS) {\n"; + OS << " return LHS.getMnemonic() < RHS.getMnemonic();\n"; + OS << " }\n"; + OS << " };\n"; + + OS << "} // end anonymous namespace\n\n"; unsigned VariantCount = Target.getAsmParserVariantCount(); - PI.asmMatcherEmitMatchTable(Target, Info, StringTable, VariantCount); + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); - PI.asmMatcherEmitIncludes(); + OS << "static const MatchEntry MatchTable" << VC << "[] = {\n"; + + for (const auto &MI : Info.Matchables) { + if (MI->AsmVariantID != AsmVariantNo) + continue; + + // Store a pascal-style length byte in the mnemonic. + std::string LenMnemonic = + char(MI->Mnemonic.size()) + MI->Mnemonic.lower(); + OS << " { " << StringTable.GetOrAddStringOffset(LenMnemonic, false) + << " /* " << MI->Mnemonic << " */, " + << Target.getInstNamespace() << "::" + << MI->getResultInst()->TheDef->getName() << ", " + << MI->ConversionFnKind << ", "; + + // Write the required features mask. + OS << "AMFBS"; + if (MI->RequiredFeatures.empty()) + OS << "_None"; + else + for (unsigned i = 0, e = MI->RequiredFeatures.size(); i != e; ++i) + OS << '_' << MI->RequiredFeatures[i]->TheDef->getName(); + + OS << ", { "; + ListSeparator LS; + for (const MatchableInfo::AsmOperand &Op : MI->AsmOperands) + OS << LS << Op.Class->Name; + OS << " }, },\n"; + } + + OS << "};\n\n"; + } + + OS << "#include \"llvm/Support/Debug.h\"\n"; + OS << "#include \"llvm/Support/Format.h\"\n\n"; // Finally, build the match function. - PI.asmMatcherEmitMatchFunction( - Target, AsmParser, ClassName, HasMnemonicFirst, HasOptionalOperands, - ReportMultipleNearMisses, HasMnemonicAliases, MaxNumOperands, - HasDeprecation, VariantCount); + OS << "unsigned " << Target.getName() << ClassName << "::\n" + << "MatchInstructionImpl(const OperandVector &Operands,\n"; + OS << " MCInst &Inst,\n"; + if (ReportMultipleNearMisses) + OS << " SmallVectorImpl *NearMisses,\n"; + else + OS << " uint64_t &ErrorInfo,\n" + << " FeatureBitset &MissingFeatures,\n"; + OS << " bool matchingInlineAsm, unsigned VariantID) {\n"; + + if (!ReportMultipleNearMisses) { + OS << " // Eliminate obvious mismatches.\n"; + OS << " if (Operands.size() > " + << (MaxNumOperands + HasMnemonicFirst) << ") {\n"; + OS << " ErrorInfo = " + << (MaxNumOperands + HasMnemonicFirst) << ";\n"; + OS << " return Match_InvalidOperand;\n"; + OS << " }\n\n"; + } - if (!Info.OperandMatchInfo.empty()) { - unsigned MaxMask = 0; - for (const OperandMatchEntry &OMI : Info.OperandMatchInfo) { - MaxMask |= OMI.OperandMask; - } - PI.asmMatcherEmitCustomOperandParsing(MaxMask, Target, Info, ClassName, - StringTable, MaxMnemonicIndex, FeatureBitsets.size(), - HasMnemonicFirst, *AsmParser); + // Emit code to get the available features. + OS << " // Get the current feature set.\n"; + OS << " const FeatureBitset &AvailableFeatures = getAvailableFeatures();\n\n"; + + OS << " // Get the instruction mnemonic, which is the first token.\n"; + if (HasMnemonicFirst) { + OS << " StringRef Mnemonic = ((" << Target.getName() + << "Operand &)*Operands[0]).getToken();\n\n"; + } else { + OS << " StringRef Mnemonic;\n"; + OS << " if (Operands[0]->isToken())\n"; + OS << " Mnemonic = ((" << Target.getName() + << "Operand &)*Operands[0]).getToken();\n\n"; } - PI.emitIncludeToggle("GET_MATCHER_IMPLEMENTATION", false); - PI.emitIncludeToggle("GET_MNEMONIC_SPELL_CHECKER", true); - PI.asmMatcherEmitMnemonicSpellChecker(Target, VariantCount); - PI.emitIncludeToggle("GET_MNEMONIC_SPELL_CHECKER", false); + if (HasMnemonicAliases) { + OS << " // Process all MnemonicAliases to remap the mnemonic.\n"; + OS << " applyMnemonicAliases(Mnemonic, AvailableFeatures, VariantID);\n\n"; + } + + // Emit code to compute the class list for this operand vector. + if (!ReportMultipleNearMisses) { + OS << " // Some state to try to produce better error messages.\n"; + OS << " bool HadMatchOtherThanFeatures = false;\n"; + OS << " bool HadMatchOtherThanPredicate = false;\n"; + OS << " unsigned RetCode = Match_InvalidOperand;\n"; + OS << " MissingFeatures.set();\n"; + OS << " // Set ErrorInfo to the operand that mismatches if it is\n"; + OS << " // wrong for all instances of the instruction.\n"; + OS << " ErrorInfo = ~0ULL;\n"; + } - PI.emitIncludeToggle("GET_MNEMONIC_CHECKER", true); - PI.asmMatcherEmitMnemonicChecker(Target, VariantCount, + if (HasOptionalOperands) { + OS << " SmallBitVector OptionalOperandsMask(" << MaxNumOperands << ");\n"; + } + + // Emit code to search the table. + OS << " // Find the appropriate table for this asm variant.\n"; + OS << " const MatchEntry *Start, *End;\n"; + OS << " switch (VariantID) {\n"; + OS << " default: llvm_unreachable(\"invalid variant!\");\n"; + for (unsigned VC = 0; VC != VariantCount; ++VC) { + Record *AsmVariant = Target.getAsmParserVariant(VC); + int AsmVariantNo = AsmVariant->getValueAsInt("Variant"); + OS << " case " << AsmVariantNo << ": Start = std::begin(MatchTable" << VC + << "); End = std::end(MatchTable" << VC << "); break;\n"; + } + OS << " }\n"; + + OS << " // Search the table.\n"; + if (HasMnemonicFirst) { + OS << " auto MnemonicRange = " + "std::equal_range(Start, End, Mnemonic, LessOpcode());\n\n"; + } else { + OS << " auto MnemonicRange = std::make_pair(Start, End);\n"; + OS << " unsigned SIndex = Mnemonic.empty() ? 0 : 1;\n"; + OS << " if (!Mnemonic.empty())\n"; + OS << " MnemonicRange = " + "std::equal_range(Start, End, Mnemonic.lower(), LessOpcode());\n\n"; + } + + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"AsmMatcher: found \" <<\n" + << " std::distance(MnemonicRange.first, MnemonicRange.second) <<\n" + << " \" encodings with mnemonic '\" << Mnemonic << \"'\\n\");\n\n"; + + OS << " // Return a more specific error code if no mnemonics match.\n"; + OS << " if (MnemonicRange.first == MnemonicRange.second)\n"; + OS << " return Match_MnemonicFail;\n\n"; + + OS << " for (const MatchEntry *it = MnemonicRange.first, " + << "*ie = MnemonicRange.second;\n"; + OS << " it != ie; ++it) {\n"; + OS << " const FeatureBitset &RequiredFeatures = " + "FeatureBitsets[it->RequiredFeaturesIdx];\n"; + OS << " bool HasRequiredFeatures =\n"; + OS << " (AvailableFeatures & RequiredFeatures) == RequiredFeatures;\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"Trying to match opcode \"\n"; + OS << " << MII.getName(it->Opcode) << \"\\n\");\n"; + + if (ReportMultipleNearMisses) { + OS << " // Some state to record ways in which this instruction did not match.\n"; + OS << " NearMissInfo OperandNearMiss = NearMissInfo::getSuccess();\n"; + OS << " NearMissInfo FeaturesNearMiss = NearMissInfo::getSuccess();\n"; + OS << " NearMissInfo EarlyPredicateNearMiss = NearMissInfo::getSuccess();\n"; + OS << " NearMissInfo LatePredicateNearMiss = NearMissInfo::getSuccess();\n"; + OS << " bool MultipleInvalidOperands = false;\n"; + } + + if (HasMnemonicFirst) { + OS << " // equal_range guarantees that instruction mnemonic matches.\n"; + OS << " assert(Mnemonic == it->getMnemonic());\n"; + } + + // Emit check that the subclasses match. + if (!ReportMultipleNearMisses) + OS << " bool OperandsValid = true;\n"; + if (HasOptionalOperands) { + OS << " OptionalOperandsMask.reset(0, " << MaxNumOperands << ");\n"; + } + OS << " for (unsigned FormalIdx = " << (HasMnemonicFirst ? "0" : "SIndex") + << ", ActualIdx = " << (HasMnemonicFirst ? "1" : "SIndex") + << "; FormalIdx != " << MaxNumOperands << "; ++FormalIdx) {\n"; + OS << " auto Formal = " + << "static_cast(it->Classes[FormalIdx]);\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\",\n"; + OS << " dbgs() << \" Matching formal operand class \" << getMatchClassName(Formal)\n"; + OS << " << \" against actual operand at index \" << ActualIdx);\n"; + OS << " if (ActualIdx < Operands.size())\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \" (\";\n"; + OS << " Operands[ActualIdx]->print(dbgs()); dbgs() << \"): \");\n"; + OS << " else\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \": \");\n"; + OS << " if (ActualIdx >= Operands.size()) {\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"actual operand " + "index out of range\\n\");\n"; + if (ReportMultipleNearMisses) { + OS << " bool ThisOperandValid = (Formal == " <<"InvalidMatchClass) || " + "isSubclass(Formal, OptionalMatchClass);\n"; + OS << " if (!ThisOperandValid) {\n"; + OS << " if (!OperandNearMiss) {\n"; + OS << " // Record info about match failure for later use.\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"recording too-few-operands near miss\\n\");\n"; + OS << " OperandNearMiss =\n"; + OS << " NearMissInfo::getTooFewOperands(Formal, it->Opcode);\n"; + OS << " } else if (OperandNearMiss.getKind() != NearMissInfo::NearMissTooFewOperands) {\n"; + OS << " // If more than one operand is invalid, give up on this match entry.\n"; + OS << " DEBUG_WITH_TYPE(\n"; + OS << " \"asm-matcher\",\n"; + OS << " dbgs() << \"second invalid operand, giving up on this opcode\\n\");\n"; + OS << " MultipleInvalidOperands = true;\n"; + OS << " break;\n"; + OS << " }\n"; + OS << " } else {\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"but formal " + "operand not required\\n\");\n"; + OS << " }\n"; + OS << " continue;\n"; + } else { + OS << " if (Formal == InvalidMatchClass) {\n"; + if (HasOptionalOperands) { + OS << " OptionalOperandsMask.set(FormalIdx, " << MaxNumOperands + << ");\n"; + } + OS << " break;\n"; + OS << " }\n"; + OS << " if (isSubclass(Formal, OptionalMatchClass)) {\n"; + if (HasOptionalOperands) { + OS << " OptionalOperandsMask.set(FormalIdx);\n"; + } + OS << " continue;\n"; + OS << " }\n"; + OS << " OperandsValid = false;\n"; + OS << " ErrorInfo = ActualIdx;\n"; + OS << " break;\n"; + } + OS << " }\n"; + OS << " MCParsedAsmOperand &Actual = *Operands[ActualIdx];\n"; + OS << " unsigned Diag = validateOperandClass(Actual, Formal);\n"; + OS << " if (Diag == Match_Success) {\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\",\n"; + OS << " dbgs() << \"match success using generic matcher\\n\");\n"; + OS << " ++ActualIdx;\n"; + OS << " continue;\n"; + OS << " }\n"; + OS << " // If the generic handler indicates an invalid operand\n"; + OS << " // failure, check for a special case.\n"; + OS << " if (Diag != Match_Success) {\n"; + OS << " unsigned TargetDiag = validateTargetOperandClass(Actual, Formal);\n"; + OS << " if (TargetDiag == Match_Success) {\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\",\n"; + OS << " dbgs() << \"match success using target matcher\\n\");\n"; + OS << " ++ActualIdx;\n"; + OS << " continue;\n"; + OS << " }\n"; + OS << " // If the target matcher returned a specific error code use\n"; + OS << " // that, else use the one from the generic matcher.\n"; + OS << " if (TargetDiag != Match_InvalidOperand && " + "HasRequiredFeatures)\n"; + OS << " Diag = TargetDiag;\n"; + OS << " }\n"; + OS << " // If current formal operand wasn't matched and it is optional\n" + << " // then try to match next formal operand\n"; + OS << " if (Diag == Match_InvalidOperand " + << "&& isSubclass(Formal, OptionalMatchClass)) {\n"; + if (HasOptionalOperands) { + OS << " OptionalOperandsMask.set(FormalIdx);\n"; + } + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"ignoring optional operand\\n\");\n"; + OS << " continue;\n"; + OS << " }\n"; + + if (ReportMultipleNearMisses) { + OS << " if (!OperandNearMiss) {\n"; + OS << " // If this is the first invalid operand we have seen, record some\n"; + OS << " // information about it.\n"; + OS << " DEBUG_WITH_TYPE(\n"; + OS << " \"asm-matcher\",\n"; + OS << " dbgs()\n"; + OS << " << \"operand match failed, recording near-miss with diag code \"\n"; + OS << " << Diag << \"\\n\");\n"; + OS << " OperandNearMiss =\n"; + OS << " NearMissInfo::getMissedOperand(Diag, Formal, it->Opcode, ActualIdx);\n"; + OS << " ++ActualIdx;\n"; + OS << " } else {\n"; + OS << " // If more than one operand is invalid, give up on this match entry.\n"; + OS << " DEBUG_WITH_TYPE(\n"; + OS << " \"asm-matcher\",\n"; + OS << " dbgs() << \"second operand mismatch, skipping this opcode\\n\");\n"; + OS << " MultipleInvalidOperands = true;\n"; + OS << " break;\n"; + OS << " }\n"; + OS << " }\n\n"; + } else { + OS << " // If this operand is broken for all of the instances of this\n"; + OS << " // mnemonic, keep track of it so we can report loc info.\n"; + OS << " // If we already had a match that only failed due to a\n"; + OS << " // target predicate, that diagnostic is preferred.\n"; + OS << " if (!HadMatchOtherThanPredicate &&\n"; + OS << " (it == MnemonicRange.first || ErrorInfo <= ActualIdx)) {\n"; + OS << " if (HasRequiredFeatures && (ErrorInfo != ActualIdx || Diag " + "!= Match_InvalidOperand))\n"; + OS << " RetCode = Diag;\n"; + OS << " ErrorInfo = ActualIdx;\n"; + OS << " }\n"; + OS << " // Otherwise, just reject this instance of the mnemonic.\n"; + OS << " OperandsValid = false;\n"; + OS << " break;\n"; + OS << " }\n\n"; + } + + if (ReportMultipleNearMisses) + OS << " if (MultipleInvalidOperands) {\n"; + else + OS << " if (!OperandsValid) {\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"Opcode result: multiple \"\n"; + OS << " \"operand mismatches, ignoring \"\n"; + OS << " \"this opcode\\n\");\n"; + OS << " continue;\n"; + OS << " }\n"; + + // Emit check that the required features are available. + OS << " if (!HasRequiredFeatures) {\n"; + if (!ReportMultipleNearMisses) + OS << " HadMatchOtherThanFeatures = true;\n"; + OS << " FeatureBitset NewMissingFeatures = RequiredFeatures & " + "~AvailableFeatures;\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"Missing target features:\";\n"; + OS << " for (unsigned I = 0, E = NewMissingFeatures.size(); I != E; ++I)\n"; + OS << " if (NewMissingFeatures[I])\n"; + OS << " dbgs() << ' ' << I;\n"; + OS << " dbgs() << \"\\n\");\n"; + if (ReportMultipleNearMisses) { + OS << " FeaturesNearMiss = NearMissInfo::getMissedFeature(NewMissingFeatures);\n"; + } else { + OS << " if (NewMissingFeatures.count() <=\n" + " MissingFeatures.count())\n"; + OS << " MissingFeatures = NewMissingFeatures;\n"; + OS << " continue;\n"; + } + OS << " }\n"; + OS << "\n"; + OS << " Inst.clear();\n\n"; + OS << " Inst.setOpcode(it->Opcode);\n"; + // Verify the instruction with the target-specific match predicate function. + OS << " // We have a potential match but have not rendered the operands.\n" + << " // Check the target predicate to handle any context sensitive\n" + " // constraints.\n" + << " // For example, Ties that are referenced multiple times must be\n" + " // checked here to ensure the input is the same for each match\n" + " // constraints. If we leave it any later the ties will have been\n" + " // canonicalized\n" + << " unsigned MatchResult;\n" + << " if ((MatchResult = checkEarlyTargetMatchPredicate(Inst, " + "Operands)) != Match_Success) {\n" + << " Inst.clear();\n"; + OS << " DEBUG_WITH_TYPE(\n"; + OS << " \"asm-matcher\",\n"; + OS << " dbgs() << \"Early target match predicate failed with diag code \"\n"; + OS << " << MatchResult << \"\\n\");\n"; + if (ReportMultipleNearMisses) { + OS << " EarlyPredicateNearMiss = NearMissInfo::getMissedPredicate(MatchResult);\n"; + } else { + OS << " RetCode = MatchResult;\n" + << " HadMatchOtherThanPredicate = true;\n" + << " continue;\n"; + } + OS << " }\n\n"; + + if (ReportMultipleNearMisses) { + OS << " // If we did not successfully match the operands, then we can't convert to\n"; + OS << " // an MCInst, so bail out on this instruction variant now.\n"; + OS << " if (OperandNearMiss) {\n"; + OS << " // If the operand mismatch was the only problem, reprrt it as a near-miss.\n"; + OS << " if (NearMisses && !FeaturesNearMiss && !EarlyPredicateNearMiss) {\n"; + OS << " DEBUG_WITH_TYPE(\n"; + OS << " \"asm-matcher\",\n"; + OS << " dbgs()\n"; + OS << " << \"Opcode result: one mismatched operand, adding near-miss\\n\");\n"; + OS << " NearMisses->push_back(OperandNearMiss);\n"; + OS << " } else {\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"Opcode result: multiple \"\n"; + OS << " \"types of mismatch, so not \"\n"; + OS << " \"reporting near-miss\\n\");\n"; + OS << " }\n"; + OS << " continue;\n"; + OS << " }\n\n"; + } + + OS << " if (matchingInlineAsm) {\n"; + OS << " convertToMapAndConstraints(it->ConvertFn, Operands);\n"; + if (!ReportMultipleNearMisses) { + OS << " if (!checkAsmTiedOperandConstraints(*this, it->ConvertFn, " + "Operands, ErrorInfo))\n"; + OS << " return Match_InvalidTiedOperand;\n"; + OS << "\n"; + } + OS << " return Match_Success;\n"; + OS << " }\n\n"; + OS << " // We have selected a definite instruction, convert the parsed\n" + << " // operands into the appropriate MCInst.\n"; + if (HasOptionalOperands) { + OS << " convertToMCInst(it->ConvertFn, Inst, it->Opcode, Operands,\n" + << " OptionalOperandsMask);\n"; + } else { + OS << " convertToMCInst(it->ConvertFn, Inst, it->Opcode, Operands);\n"; + } + OS << "\n"; + + // Verify the instruction with the target-specific match predicate function. + OS << " // We have a potential match. Check the target predicate to\n" + << " // handle any context sensitive constraints.\n" + << " if ((MatchResult = checkTargetMatchPredicate(Inst)) !=" + << " Match_Success) {\n" + << " DEBUG_WITH_TYPE(\"asm-matcher\",\n" + << " dbgs() << \"Target match predicate failed with diag code \"\n" + << " << MatchResult << \"\\n\");\n" + << " Inst.clear();\n"; + if (ReportMultipleNearMisses) { + OS << " LatePredicateNearMiss = NearMissInfo::getMissedPredicate(MatchResult);\n"; + } else { + OS << " RetCode = MatchResult;\n" + << " HadMatchOtherThanPredicate = true;\n" + << " continue;\n"; + } + OS << " }\n\n"; + + if (ReportMultipleNearMisses) { + OS << " int NumNearMisses = ((int)(bool)OperandNearMiss +\n"; + OS << " (int)(bool)FeaturesNearMiss +\n"; + OS << " (int)(bool)EarlyPredicateNearMiss +\n"; + OS << " (int)(bool)LatePredicateNearMiss);\n"; + OS << " if (NumNearMisses == 1) {\n"; + OS << " // We had exactly one type of near-miss, so add that to the list.\n"; + OS << " assert(!OperandNearMiss && \"OperandNearMiss was handled earlier\");\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"Opcode result: found one type of \"\n"; + OS << " \"mismatch, so reporting a \"\n"; + OS << " \"near-miss\\n\");\n"; + OS << " if (NearMisses && FeaturesNearMiss)\n"; + OS << " NearMisses->push_back(FeaturesNearMiss);\n"; + OS << " else if (NearMisses && EarlyPredicateNearMiss)\n"; + OS << " NearMisses->push_back(EarlyPredicateNearMiss);\n"; + OS << " else if (NearMisses && LatePredicateNearMiss)\n"; + OS << " NearMisses->push_back(LatePredicateNearMiss);\n"; + OS << "\n"; + OS << " continue;\n"; + OS << " } else if (NumNearMisses > 1) {\n"; + OS << " // This instruction missed in more than one way, so ignore it.\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"Opcode result: multiple \"\n"; + OS << " \"types of mismatch, so not \"\n"; + OS << " \"reporting near-miss\\n\");\n"; + OS << " continue;\n"; + OS << " }\n"; + } + + // Call the post-processing function, if used. + StringRef InsnCleanupFn = AsmParser->getValueAsString("AsmParserInstCleanup"); + if (!InsnCleanupFn.empty()) + OS << " " << InsnCleanupFn << "(Inst);\n"; + + if (HasDeprecation) { + OS << " std::string Info;\n"; + OS << " if (!getParser().getTargetParser().getTargetOptions().MCNoDeprecatedWarn &&\n"; + OS << " MII.getDeprecatedInfo(Inst, getSTI(), Info)) {\n"; + OS << " SMLoc Loc = ((" << Target.getName() + << "Operand &)*Operands[0]).getStartLoc();\n"; + OS << " getParser().Warning(Loc, Info, std::nullopt);\n"; + OS << " }\n"; + } + + if (!ReportMultipleNearMisses) { + OS << " if (!checkAsmTiedOperandConstraints(*this, it->ConvertFn, " + "Operands, ErrorInfo))\n"; + OS << " return Match_InvalidTiedOperand;\n"; + OS << "\n"; + } + + OS << " DEBUG_WITH_TYPE(\n"; + OS << " \"asm-matcher\",\n"; + OS << " dbgs() << \"Opcode result: complete match, selecting this opcode\\n\");\n"; + OS << " return Match_Success;\n"; + OS << " }\n\n"; + + if (ReportMultipleNearMisses) { + OS << " // No instruction variants matched exactly.\n"; + OS << " return Match_NearMisses;\n"; + } else { + OS << " // Okay, we had no match. Try to return a useful error code.\n"; + OS << " if (HadMatchOtherThanPredicate || !HadMatchOtherThanFeatures)\n"; + OS << " return RetCode;\n\n"; + OS << " ErrorInfo = 0;\n"; + OS << " return Match_MissingFeature;\n"; + } + OS << "}\n\n"; + + if (!Info.OperandMatchInfo.empty()) + emitCustomOperandParsing(OS, Target, Info, ClassName, StringTable, + MaxMnemonicIndex, FeatureBitsets.size(), + HasMnemonicFirst, *AsmParser); + + OS << "#endif // GET_MATCHER_IMPLEMENTATION\n\n"; + + OS << "\n#ifdef GET_MNEMONIC_SPELL_CHECKER\n"; + OS << "#undef GET_MNEMONIC_SPELL_CHECKER\n\n"; + + emitMnemonicSpellChecker(OS, Target, VariantCount); + + OS << "#endif // GET_MNEMONIC_SPELL_CHECKER\n\n"; + + OS << "\n#ifdef GET_MNEMONIC_CHECKER\n"; + OS << "#undef GET_MNEMONIC_CHECKER\n\n"; + + emitMnemonicChecker(OS, Target, VariantCount, HasMnemonicFirst, HasMnemonicAliases); - PI.emitIncludeToggle("GET_MNEMONIC_CHECKER", false); + + OS << "#endif // GET_MNEMONIC_CHECKER\n\n"; } namespace llvm { void EmitAsmMatcher(RecordKeeper &RK, raw_ostream &OS) { - formatted_raw_ostream FOS(OS); - PrinterLanguage const PLang = PrinterLLVM::getLanguage(); - PrinterLLVM *PI = nullptr; - switch (PLang) { - default: - PrintFatalNote( - "AsmMatcher backend does not support the selected ouput language."); - return; - case PRINTER_LANG_CPP: - PI = new PrinterLLVM(FOS); - break; - case PRINTER_LANG_CAPSTONE_C: - PI = new PrinterCapstone(FOS); - break; - } - - AsmMatcherEmitter(RK, *PI).run(); - delete PI; + emitSourceFileHeader("Assembly Matcher Source Fragment", OS); + AsmMatcherEmitter(RK).run(OS); } } // end namespace llvm diff --git a/llvm/utils/TableGen/AsmWriterEmitter.cpp b/llvm/utils/TableGen/AsmWriterEmitter.cpp index 639ddc901e7c..f2e4d15a2c75 100644 --- a/llvm/utils/TableGen/AsmWriterEmitter.cpp +++ b/llvm/utils/TableGen/AsmWriterEmitter.cpp @@ -15,7 +15,6 @@ #include "CodeGenInstruction.h" #include "CodeGenRegisters.h" #include "CodeGenTarget.h" -#include "Printer.h" #include "SequenceToOffsetTable.h" #include "Types.h" #include "llvm/ADT/ArrayRef.h" @@ -57,27 +56,24 @@ namespace { class AsmWriterEmitter { RecordKeeper &Records; CodeGenTarget Target; - PrinterLLVM &PI; ArrayRef NumberedInstructions; std::vector Instructions; public: - AsmWriterEmitter(RecordKeeper &R, PrinterLLVM &PI); + AsmWriterEmitter(RecordKeeper &R); - void run(); + void run(raw_ostream &o); private: void EmitGetMnemonic( + raw_ostream &o, std::vector> &TableDrivenOperandPrinters, unsigned &BitsLeft, unsigned &AsmStrBits); void EmitPrintInstruction( + raw_ostream &o, std::vector> &TableDrivenOperandPrinters, unsigned &BitsLeft, unsigned &AsmStrBits); - void EmitInstructions(std::vector &Insts, - bool PassSubtarget); - void EmitGetRegisterName(); - void EmitRegisterNameString(StringRef AltName, - const std::deque &Registers); - void EmitPrintAliasInstruction(); + void EmitGetRegisterName(raw_ostream &o); + void EmitPrintAliasInstruction(raw_ostream &O); void FindUniqueOperandCommands(std::vector &UOC, std::vector> &InstIdxs, @@ -87,10 +83,30 @@ class AsmWriterEmitter { } // end anonymous namespace +static void PrintCases(std::vector> &OpsToPrint, raw_ostream &O, + bool PassSubtarget) { + O << " case " << OpsToPrint.back().first << ":"; + AsmWriterOperand TheOp = OpsToPrint.back().second; + OpsToPrint.pop_back(); + + // Check to see if any other operands are identical in this list, and if so, + // emit a case label for them. + for (unsigned i = OpsToPrint.size(); i != 0; --i) + if (OpsToPrint[i-1].second == TheOp) { + O << "\n case " << OpsToPrint[i-1].first << ":"; + OpsToPrint.erase(OpsToPrint.begin()+i-1); + } + + // Finally, emit the code. + O << "\n " << TheOp.getCode(PassSubtarget); + O << "\n break;\n"; +} + /// EmitInstructions - Emit the last instruction in the vector and any other /// instructions that are suitably similar to it. -void AsmWriterEmitter::EmitInstructions(std::vector &Insts, - bool PassSubtarget) { +static void EmitInstructions(std::vector &Insts, + raw_ostream &O, bool PassSubtarget) { AsmWriterInst FirstInst = Insts.back(); Insts.pop_back(); @@ -110,11 +126,39 @@ void AsmWriterEmitter::EmitInstructions(std::vector &Insts, } } } - PI.asmWriterEmitInstruction(FirstInst, - SimilarInsts, - DifferingOperand, - PassSubtarget); + O << " case " << FirstInst.CGI->Namespace << "::" + << FirstInst.CGI->TheDef->getName() << ":\n"; + for (const AsmWriterInst &AWI : SimilarInsts) + O << " case " << AWI.CGI->Namespace << "::" + << AWI.CGI->TheDef->getName() << ":\n"; + for (unsigned i = 0, e = FirstInst.Operands.size(); i != e; ++i) { + if (i != DifferingOperand) { + // If the operand is the same for all instructions, just print it. + O << " " << FirstInst.Operands[i].getCode(PassSubtarget); + } else { + // If this is the operand that varies between all of the instructions, + // emit a switch for just this operand now. + O << " switch (MI->getOpcode()) {\n"; + O << " default: llvm_unreachable(\"Unexpected opcode.\");\n"; + std::vector> OpsToPrint; + OpsToPrint.push_back(std::make_pair(FirstInst.CGI->Namespace.str() + "::" + + FirstInst.CGI->TheDef->getName().str(), + FirstInst.Operands[i])); + + for (const AsmWriterInst &AWI : SimilarInsts) { + OpsToPrint.push_back(std::make_pair(AWI.CGI->Namespace.str()+"::" + + AWI.CGI->TheDef->getName().str(), + AWI.Operands[i])); + } + std::reverse(OpsToPrint.begin(), OpsToPrint.end()); + while (!OpsToPrint.empty()) + PrintCases(OpsToPrint, O, PassSubtarget); + O << " }"; + } + O << "\n"; + } + O << " break;\n"; } void AsmWriterEmitter:: @@ -249,16 +293,21 @@ static void UnescapeAliasString(std::string &Str) { } void AsmWriterEmitter::EmitGetMnemonic( + raw_ostream &O, std::vector> &TableDrivenOperandPrinters, unsigned &BitsLeft, unsigned &AsmStrBits) { Record *AsmWriter = Target.getAsmWriter(); StringRef ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); bool PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget"); - PI.asmWriterEmitGetMnemonic(Target.getName().str(), ClassName); + O << "/// getMnemonic - This method is automatically generated by " + "tablegen\n" + "/// from the instruction set description.\n" + "std::pair " + << Target.getName() << ClassName << "::getMnemonic(const MCInst *MI) {\n"; // Build an aggregate string, and build a table of offsets into it. - SequenceToOffsetTable StringTable(PrinterLLVM::getLanguage(), true); + SequenceToOffsetTable StringTable; /// OpcodeInfo - This encodes the index of the string to use for the first /// chunk of the output as well as indices used for operand printing. @@ -351,28 +400,129 @@ void AsmWriterEmitter::EmitGetMnemonic( } // Emit the string table itself. - PI.asmWriterEmitAsmStrs(StringTable); - PI.asmWriterEmitMnemonicDecodeTable(OpcodeInfoBits, - BitsLeft, - AsmStrBits, - NumberedInstructions, - OpcodeInfo); + StringTable.emitStringLiteralDef(O, " static const char AsmStrs[]"); + + // Emit the lookup tables in pieces to minimize wasted bytes. + unsigned BytesNeeded = ((OpcodeInfoBits - BitsLeft) + 7) / 8; + unsigned Table = 0, Shift = 0; + SmallString<128> BitsString; + raw_svector_ostream BitsOS(BitsString); + // If the total bits is more than 32-bits we need to use a 64-bit type. + BitsOS << " uint" << ((BitsLeft < (OpcodeInfoBits - 32)) ? 64 : 32) + << "_t Bits = 0;\n"; + while (BytesNeeded != 0) { + // Figure out how big this table section needs to be, but no bigger than 4. + unsigned TableSize = std::min(llvm::bit_floor(BytesNeeded), 4u); + BytesNeeded -= TableSize; + TableSize *= 8; // Convert to bits; + uint64_t Mask = (1ULL << TableSize) - 1; + O << " static const uint" << TableSize << "_t OpInfo" << Table + << "[] = {\n"; + for (unsigned i = 0, e = NumberedInstructions.size(); i != e; ++i) { + O << " " << ((OpcodeInfo[i] >> Shift) & Mask) << "U,\t// " + << NumberedInstructions[i]->TheDef->getName() << "\n"; + } + O << " };\n\n"; + // Emit string to combine the individual table lookups. + BitsOS << " Bits |= "; + // If the total bits is more than 32-bits we need to use a 64-bit type. + if (BitsLeft < (OpcodeInfoBits - 32)) + BitsOS << "(uint64_t)"; + BitsOS << "OpInfo" << Table << "[MI->getOpcode()] << " << Shift << ";\n"; + // Prepare the shift for the next iteration and increment the table count. + Shift += TableSize; + ++Table; + } + + O << " // Emit the opcode for the instruction.\n"; + O << BitsString; + + // Return mnemonic string and bits. + O << " return {AsmStrs+(Bits & " << (1 << AsmStrBits) - 1 + << ")-1, Bits};\n\n"; + + O << "}\n"; } /// EmitPrintInstruction - Generate the code for the "printInstruction" method /// implementation. Destroys all instances of AsmWriterInst information, by /// clearing the Instructions vector. void AsmWriterEmitter::EmitPrintInstruction( + raw_ostream &O, std::vector> &TableDrivenOperandPrinters, unsigned &BitsLeft, unsigned &AsmStrBits) { + const unsigned OpcodeInfoBits = 64; Record *AsmWriter = Target.getAsmWriter(); StringRef ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); bool PassSubtarget = AsmWriter->getValueAsInt("PassSubtarget"); - PI.asmWriterEmitPrintInstruction(Target.getName().str(), - TableDrivenOperandPrinters, - BitsLeft, AsmStrBits, - ClassName, PassSubtarget); + // This function has some huge switch statements that causing excessive + // compile time in LLVM profile instrumenation build. This print function + // usually is not frequently called in compilation. Here we disable the + // profile instrumenation for this function. + O << "/// printInstruction - This method is automatically generated by " + "tablegen\n" + "/// from the instruction set description.\n" + "LLVM_NO_PROFILE_INSTRUMENT_FUNCTION\n" + "void " + << Target.getName() << ClassName + << "::printInstruction(const MCInst *MI, uint64_t Address, " + << (PassSubtarget ? "const MCSubtargetInfo &STI, " : "") + << "raw_ostream &O) {\n"; + + // Emit the initial tab character. + O << " O << \"\\t\";\n\n"; + + // Emit the starting string. + O << " auto MnemonicInfo = getMnemonic(MI);\n\n"; + O << " O << MnemonicInfo.first;\n\n"; + + O << " uint" << ((BitsLeft < (OpcodeInfoBits - 32)) ? 64 : 32) + << "_t Bits = MnemonicInfo.second;\n" + << " assert(Bits != 0 && \"Cannot print this instruction.\");\n"; + + // Output the table driven operand information. + BitsLeft = OpcodeInfoBits-AsmStrBits; + for (unsigned i = 0, e = TableDrivenOperandPrinters.size(); i != e; ++i) { + std::vector &Commands = TableDrivenOperandPrinters[i]; + + // Compute the number of bits we need to represent these cases, this is + // ceil(log2(numentries)). + unsigned NumBits = Log2_32_Ceil(Commands.size()); + assert(NumBits <= BitsLeft && "consistency error"); + + // Emit code to extract this field from Bits. + O << "\n // Fragment " << i << " encoded into " << NumBits + << " bits for " << Commands.size() << " unique commands.\n"; + + if (Commands.size() == 2) { + // Emit two possibilitys with if/else. + O << " if ((Bits >> " + << (OpcodeInfoBits-BitsLeft) << ") & " + << ((1 << NumBits)-1) << ") {\n" + << Commands[1] + << " } else {\n" + << Commands[0] + << " }\n\n"; + } else if (Commands.size() == 1) { + // Emit a single possibility. + O << Commands[0] << "\n\n"; + } else { + O << " switch ((Bits >> " + << (OpcodeInfoBits-BitsLeft) << ") & " + << ((1 << NumBits)-1) << ") {\n" + << " default: llvm_unreachable(\"Invalid command number.\");\n"; + + // Print out all the cases. + for (unsigned j = 0, e = Commands.size(); j != e; ++j) { + O << " case " << j << ":\n"; + O << Commands[j]; + O << " break;\n"; + } + O << " }\n\n"; + } + BitsLeft -= NumBits; + } // Okay, delete instructions with no operand info left. llvm::erase_if(Instructions, @@ -389,20 +539,21 @@ void AsmWriterEmitter::EmitPrintInstruction( // instructions. if (!Instructions.empty()) { // Find the opcode # of inline asm. - PI.asmWriterEmitInstrSwitch(); + O << " switch (MI->getOpcode()) {\n"; + O << " default: llvm_unreachable(\"Unexpected opcode.\");\n"; while (!Instructions.empty()) - EmitInstructions(Instructions, PassSubtarget); + EmitInstructions(Instructions, O, PassSubtarget); - PI.asmWriterEmitCompoundClosure(2, true, false); + O << " }\n"; } - PI.asmWriterEmitCompoundClosure(0, true, false); + O << "}\n"; } -void AsmWriterEmitter::EmitRegisterNameString( - StringRef AltName, - const std::deque &Registers) { - SequenceToOffsetTable StringTable(PrinterLLVM::getLanguage()); +static void +emitRegisterNameString(raw_ostream &O, StringRef AltName, + const std::deque &Registers) { + SequenceToOffsetTable StringTable; SmallVector AsmNames(Registers.size()); unsigned i = 0; for (const auto &Reg : Registers) { @@ -439,11 +590,21 @@ void AsmWriterEmitter::EmitRegisterNameString( } StringTable.layout(); - PI.asmWriterEmitStringLiteralDef(StringTable, AltName); - PI.asmWriterEmitRegAsmOffsets(Registers.size(), AsmNames, StringTable, AltName); + StringTable.emitStringLiteralDef(O, Twine(" static const char AsmStrs") + + AltName + "[]"); + + O << " static const " << getMinimalTypeForRange(StringTable.size() - 1, 32) + << " RegAsmOffset" << AltName << "[] = {"; + for (unsigned i = 0, e = Registers.size(); i != e; ++i) { + if ((i % 14) == 0) + O << "\n "; + O << StringTable.get(AsmNames[i]) << ", "; + } + O << "\n };\n" + << "\n"; } -void AsmWriterEmitter::EmitGetRegisterName() { +void AsmWriterEmitter::EmitGetRegisterName(raw_ostream &O) { Record *AsmWriter = Target.getAsmWriter(); StringRef ClassName = AsmWriter->getValueAsString("AsmWriterClassName"); const auto &Registers = Target.getRegBank().getRegisters(); @@ -451,18 +612,57 @@ void AsmWriterEmitter::EmitGetRegisterName() { bool hasAltNames = AltNameIndices.size() > 1; StringRef Namespace = Registers.front().TheDef->getValueAsString("Namespace"); - PI.asmWriterEmitGetRegNameAssert(Target.getName().str(), - ClassName, - hasAltNames, - Registers.size()); + O << + "\n\n/// getRegisterName - This method is automatically generated by tblgen\n" + "/// from the register set description. This returns the assembler name\n" + "/// for the specified register.\n" + "const char *" << Target.getName() << ClassName << "::"; + if (hasAltNames) + O << "\ngetRegisterName(MCRegister Reg, unsigned AltIdx) {\n"; + else + O << "getRegisterName(MCRegister Reg) {\n"; + O << " unsigned RegNo = Reg.id();\n" + << " assert(RegNo && RegNo < " << (Registers.size() + 1) + << " && \"Invalid register number!\");\n" + << "\n"; if (hasAltNames) { for (const Record *R : AltNameIndices) - EmitRegisterNameString(R->getName(), Registers); + emitRegisterNameString(O, R->getName(), Registers); } else - EmitRegisterNameString("", Registers); + emitRegisterNameString(O, "", Registers); - PI.asmWriterEmitAltIdxSwitch(hasAltNames, AltNameIndices, Namespace); + if (hasAltNames) { + O << " switch(AltIdx) {\n" + << " default: llvm_unreachable(\"Invalid register alt name index!\");\n"; + for (const Record *R : AltNameIndices) { + StringRef AltName = R->getName(); + O << " case "; + if (!Namespace.empty()) + O << Namespace << "::"; + O << AltName << ":\n"; + if (R->isValueUnset("FallbackRegAltNameIndex")) + O << " assert(*(AsmStrs" << AltName << "+RegAsmOffset" << AltName + << "[RegNo-1]) &&\n" + << " \"Invalid alt name index for register!\");\n"; + else { + O << " if (!*(AsmStrs" << AltName << "+RegAsmOffset" << AltName + << "[RegNo-1]))\n" + << " return getRegisterName(RegNo, "; + if (!Namespace.empty()) + O << Namespace << "::"; + O << R->getValueAsDef("FallbackRegAltNameIndex")->getName() << ");\n"; + } + O << " return AsmStrs" << AltName << "+RegAsmOffset" << AltName + << "[RegNo-1];\n"; + } + O << " }\n"; + } else { + O << " assert (*(AsmStrs+RegAsmOffset[RegNo-1]) &&\n" + << " \"Invalid alt name index for register!\");\n" + << " return AsmStrs+RegAsmOffset[RegNo-1];\n"; + } + O << "}\n"; } namespace { @@ -599,10 +799,11 @@ struct AliasPriorityComparator { } // end anonymous namespace -void AsmWriterEmitter::EmitPrintAliasInstruction() { +void AsmWriterEmitter::EmitPrintAliasInstruction(raw_ostream &O) { Record *AsmWriter = Target.getAsmWriter(); - PI.emitIncludeToggle("PRINT_ALIAS_INSTR", true); + O << "\n#ifdef PRINT_ALIAS_INSTR\n"; + O << "#undef PRINT_ALIAS_INSTR\n\n"; ////////////////////////////// // Gather information about aliases we need to print @@ -689,7 +890,7 @@ void AsmWriterEmitter::EmitPrintAliasInstruction() { // Ignore unchecked result operands. while (IAP.getCondCount() < MIOpNum) - IAP.addCond(PI.asmWriterGetPatCondKIgnore()); + IAP.addCond("AliasPatternCond::K_Ignore, 0"); const CodeGenInstAlias::ResultOperand &RO = CGA.ResultOperands[i]; @@ -727,11 +928,11 @@ void AsmWriterEmitter::EmitPrintAliasInstruction() { if (R->isSubClassOf("RegisterOperand")) R = R->getValueAsDef("RegClass"); IAP.addCond(std::string( - formatv(PI.asmWriterGetPatCondKRegClass(), + formatv("AliasPatternCond::K_RegClass, {0}::{1}RegClassID", Namespace, R->getName()))); } else { IAP.addCond(std::string(formatv( - PI.asmWriterGetPatCondKTiedReg(), IAP.getOpIndex(ROName)))); + "AliasPatternCond::K_TiedReg, {0}", IAP.getOpIndex(ROName)))); } } else { // Assume all printable operands are desired for now. This can be @@ -749,7 +950,7 @@ void AsmWriterEmitter::EmitPrintAliasInstruction() { break; // No conditions on this operand at all } IAP.addCond( - std::string(formatv(PI.asmWriterGetPatCondKCustom(), Entry))); + std::string(formatv("AliasPatternCond::K_Custom, {0}", Entry))); } break; } @@ -762,19 +963,19 @@ void AsmWriterEmitter::EmitPrintAliasInstruction() { PrintFatalError("Matching an alias with an immediate out of the " "range of int32_t is not supported"); IAP.addCond(std::string( - formatv(PI.asmWriterGetPatCondKImm(), Imm32))); + formatv("AliasPatternCond::K_Imm, uint32_t({0})", Imm32))); break; } case CodeGenInstAlias::ResultOperand::K_Reg: if (!CGA.ResultOperands[i].getRegister()) { IAP.addCond(std::string(formatv( - PI.asmWriterGetPatCondKNoReg(), Namespace))); + "AliasPatternCond::K_Reg, {0}::NoRegister", Namespace))); break; } StringRef Reg = CGA.ResultOperands[i].getRegister()->getName(); IAP.addCond(std::string( - formatv(PI.asmWriterGetPatCondKReg(), Namespace, Reg))); + formatv("AliasPatternCond::K_Reg, {0}::{1}", Namespace, Reg))); break; } @@ -825,13 +1026,13 @@ void AsmWriterEmitter::EmitPrintAliasInstruction() { PrintFatalError(R->getLoc(), "Invalid AssemblerCondDag!"); IAP.addCond(std::string(formatv( - PI.asmWriterGetPatCondKFeature(), IsOr ? "Or" : "", + "AliasPatternCond::K_{0}{1}Feature, {2}::{3}", IsOr ? "Or" : "", IsNeg ? "Neg" : "", Namespace, Arg->getAsString()))); } // If an AssemblerPredicate with ors is used, note end of list should // these be combined. if (IsOr) - IAP.addCond(PI.asmWriterGetPatCondKEndOrFeature()); + IAP.addCond("AliasPatternCond::K_EndOrFeatures, 0"); } IAPrinterMap[Aliases.first].push_back(std::move(IAP)); @@ -842,6 +1043,15 @@ void AsmWriterEmitter::EmitPrintAliasInstruction() { // Write out the printAliasInstr function ////////////////////////////// + std::string Header; + raw_string_ostream HeaderO(Header); + + HeaderO << "bool " << Target.getName() << ClassName + << "::printAliasInstr(const MCInst" + << " *MI, uint64_t Address, " + << (PassSubtarget ? "const MCSubtargetInfo &STI, " : "") + << "raw_ostream &OS) {\n"; + std::string PatternsForOpcode; raw_string_ostream OpcodeO(PatternsForOpcode); @@ -886,15 +1096,15 @@ void AsmWriterEmitter::EmitPrintAliasInstruction() { unsigned PatternStart = PatternCount; // Insert the pattern start and opcode in the pattern list for debugging. - PatternO << formatv(PI.asmWriterGetPatOpcStart(), It->first, PatternStart); + PatternO << formatv(" // {0} - {1}\n", It->first, PatternStart); for (IAPrinter *IAP : UniqueIAPs) { // Start each condition list with a comment of the resulting pattern that // we're trying to match. unsigned CondStart = CondCount; - CondO << formatv(PI.asmWriterGetCondPatStart(), IAP->getResult(), CondStart); + CondO << formatv(" // {0} - {1}\n", IAP->getResult(), CondStart); for (const auto &Cond : IAP->getConds()) - CondO << PI.asmWriterGetCond(Cond); + CondO << " {" << Cond << "},\n"; CondCount += IAP->getCondCount(); // After operands have been examined, re-encode the alias string with @@ -910,52 +1120,167 @@ void AsmWriterEmitter::EmitPrintAliasInstruction() { } unsigned AsmStrOffset = Insertion.first->second; - PatternO << formatv(PI.asmWriterGetPatternFormat(), AsmStrOffset, + PatternO << formatv(" {{{0}, {1}, {2}, {3} },\n", AsmStrOffset, CondStart, IAP->getNumMIOps(), IAP->getCondCount()); ++PatternCount; } - OpcodeO << formatv(PI.asmWriterGetOpcodeFormat(), It->first, PatternStart, + OpcodeO << formatv(" {{{0}, {1}, {2} },\n", It->first, PatternStart, PatternCount - PatternStart); } if (OpcodeO.str().empty()) { - PI.asmWriterEmitPrintAliasInstrHeader(Target.getName().str(), ClassName, PassSubtarget); - PI.asmWriterEmitPrintAliasInstrBodyRetFalse(); - PI.emitIncludeToggle("PRINT_ALIAS_INSTR", false, false); + O << HeaderO.str(); + O << " return false;\n"; + O << "}\n\n"; + O << "#endif // PRINT_ALIAS_INSTR\n"; return; } // Forward declare the validation method if needed. if (!MCOpPredicates.empty()) - PI.asmWriterEmitDeclValid(Target.getName().str(), ClassName); - - PI.asmWriterEmitPrintAliasInstrHeader(Target.getName().str(), - ClassName, - PassSubtarget); - PI.asmWriterEmitPrintAliasInstrBody(OpcodeO, - PatternO, - CondO, - AsmStrings, - MCOpPredicates, - Target.getName().str(), - ClassName, - PassSubtarget); + O << "static bool " << Target.getName() << ClassName + << "ValidateMCOperand(const MCOperand &MCOp,\n" + << " const MCSubtargetInfo &STI,\n" + << " unsigned PredicateIndex);\n"; + + O << HeaderO.str(); + O.indent(2) << "static const PatternsForOpcode OpToPatterns[] = {\n"; + O << OpcodeO.str(); + O.indent(2) << "};\n\n"; + O.indent(2) << "static const AliasPattern Patterns[] = {\n"; + O << PatternO.str(); + O.indent(2) << "};\n\n"; + O.indent(2) << "static const AliasPatternCond Conds[] = {\n"; + O << CondO.str(); + O.indent(2) << "};\n\n"; + O.indent(2) << "static const char AsmStrings[] =\n"; + for (const auto &P : AsmStrings) { + O.indent(4) << "/* " << P.first << " */ \"" << P.second << "\\0\"\n"; + } + + O.indent(2) << ";\n\n"; + + // Assert that the opcode table is sorted. Use a static local constructor to + // ensure that the check only happens once on first run. + O << "#ifndef NDEBUG\n"; + O.indent(2) << "static struct SortCheck {\n"; + O.indent(2) << " SortCheck(ArrayRef OpToPatterns) {\n"; + O.indent(2) << " assert(std::is_sorted(\n"; + O.indent(2) << " OpToPatterns.begin(), OpToPatterns.end(),\n"; + O.indent(2) << " [](const PatternsForOpcode &L, const " + "PatternsForOpcode &R) {\n"; + O.indent(2) << " return L.Opcode < R.Opcode;\n"; + O.indent(2) << " }) &&\n"; + O.indent(2) << " \"tablegen failed to sort opcode patterns\");\n"; + O.indent(2) << " }\n"; + O.indent(2) << "} sortCheckVar(OpToPatterns);\n"; + O << "#endif\n\n"; + + O.indent(2) << "AliasMatchingData M {\n"; + O.indent(2) << " ArrayRef(OpToPatterns),\n"; + O.indent(2) << " ArrayRef(Patterns),\n"; + O.indent(2) << " ArrayRef(Conds),\n"; + O.indent(2) << " StringRef(AsmStrings, std::size(AsmStrings)),\n"; + if (MCOpPredicates.empty()) + O.indent(2) << " nullptr,\n"; + else + O.indent(2) << " &" << Target.getName() << ClassName << "ValidateMCOperand,\n"; + O.indent(2) << "};\n"; + + O.indent(2) << "const char *AsmString = matchAliasPatterns(MI, " + << (PassSubtarget ? "&STI" : "nullptr") << ", M);\n"; + O.indent(2) << "if (!AsmString) return false;\n\n"; + + // Code that prints the alias, replacing the operands with the ones from the + // MCInst. + O << " unsigned I = 0;\n"; + O << " while (AsmString[I] != ' ' && AsmString[I] != '\\t' &&\n"; + O << " AsmString[I] != '$' && AsmString[I] != '\\0')\n"; + O << " ++I;\n"; + O << " OS << '\\t' << StringRef(AsmString, I);\n"; + + O << " if (AsmString[I] != '\\0') {\n"; + O << " if (AsmString[I] == ' ' || AsmString[I] == '\\t') {\n"; + O << " OS << '\\t';\n"; + O << " ++I;\n"; + O << " }\n"; + O << " do {\n"; + O << " if (AsmString[I] == '$') {\n"; + O << " ++I;\n"; + O << " if (AsmString[I] == (char)0xff) {\n"; + O << " ++I;\n"; + O << " int OpIdx = AsmString[I++] - 1;\n"; + O << " int PrintMethodIdx = AsmString[I++] - 1;\n"; + O << " printCustomAliasOperand(MI, Address, OpIdx, PrintMethodIdx, "; + O << (PassSubtarget ? "STI, " : ""); + O << "OS);\n"; + O << " } else\n"; + O << " printOperand(MI, unsigned(AsmString[I++]) - 1, "; + O << (PassSubtarget ? "STI, " : ""); + O << "OS);\n"; + O << " } else {\n"; + O << " OS << AsmString[I++];\n"; + O << " }\n"; + O << " } while (AsmString[I] != '\\0');\n"; + O << " }\n\n"; + + O << " return true;\n"; + O << "}\n\n"; ////////////////////////////// // Write out the printCustomAliasOperand function ////////////////////////////// - PI.asmWriterEmitPrintAliasOp(Target.getName().str(), - ClassName, - PrintMethods, - PassSubtarget); + O << "void " << Target.getName() << ClassName << "::" + << "printCustomAliasOperand(\n" + << " const MCInst *MI, uint64_t Address, unsigned OpIdx,\n" + << " unsigned PrintMethodIdx,\n" + << (PassSubtarget ? " const MCSubtargetInfo &STI,\n" : "") + << " raw_ostream &OS) {\n"; + if (PrintMethods.empty()) + O << " llvm_unreachable(\"Unknown PrintMethod kind\");\n"; + else { + O << " switch (PrintMethodIdx) {\n" + << " default:\n" + << " llvm_unreachable(\"Unknown PrintMethod kind\");\n" + << " break;\n"; + + for (unsigned i = 0; i < PrintMethods.size(); ++i) { + O << " case " << i << ":\n" + << " " << PrintMethods[i].first << "(MI, " + << (PrintMethods[i].second ? "Address, " : "") << "OpIdx, " + << (PassSubtarget ? "STI, " : "") << "OS);\n" + << " break;\n"; + } + O << " }\n"; + } + O << "}\n\n"; + + if (!MCOpPredicates.empty()) { + O << "static bool " << Target.getName() << ClassName + << "ValidateMCOperand(const MCOperand &MCOp,\n" + << " const MCSubtargetInfo &STI,\n" + << " unsigned PredicateIndex) {\n" + << " switch (PredicateIndex) {\n" + << " default:\n" + << " llvm_unreachable(\"Unknown MCOperandPredicate kind\");\n" + << " break;\n"; + + for (unsigned i = 0; i < MCOpPredicates.size(); ++i) { + StringRef MCOpPred = MCOpPredicates[i]->getValueAsString("MCOperandPredicate"); + O << " case " << i + 1 << ": {\n" + << MCOpPred.data() << "\n" + << " }\n"; + } + O << " }\n" + << "}\n\n"; + } - PI.asmWriterEmitPrintMC(Target.getName().str(), ClassName, MCOpPredicates); - PI.emitIncludeToggle("PRINT_ALIAS_INSTR", false, false); + O << "#endif // PRINT_ALIAS_INSTR\n"; } -AsmWriterEmitter::AsmWriterEmitter(RecordKeeper &R, PrinterLLVM &PI) : Records(R), Target(R), PI(PI) { +AsmWriterEmitter::AsmWriterEmitter(RecordKeeper &R) : Records(R), Target(R) { Record *AsmWriter = Target.getAsmWriter(); unsigned Variant = AsmWriter->getValueAsInt("Variant"); @@ -969,35 +1294,21 @@ AsmWriterEmitter::AsmWriterEmitter(RecordKeeper &R, PrinterLLVM &PI) : Records(R } } -void AsmWriterEmitter::run() { +void AsmWriterEmitter::run(raw_ostream &O) { std::vector> TableDrivenOperandPrinters; unsigned BitsLeft = 0; unsigned AsmStrBits = 0; - - PI.asmWriterEmitSourceFileHeader(); - EmitGetMnemonic(TableDrivenOperandPrinters, BitsLeft, AsmStrBits); - EmitPrintInstruction(TableDrivenOperandPrinters, BitsLeft, AsmStrBits); - EmitGetRegisterName(); - EmitPrintAliasInstruction(); + EmitGetMnemonic(O, TableDrivenOperandPrinters, BitsLeft, AsmStrBits); + EmitPrintInstruction(O, TableDrivenOperandPrinters, BitsLeft, AsmStrBits); + EmitGetRegisterName(O); + EmitPrintAliasInstruction(O); } namespace llvm { void EmitAsmWriter(RecordKeeper &RK, raw_ostream &OS) { - CodeGenTarget CGTarget(RK); - PrinterLanguage const PL = PrinterLLVM::getLanguage(); - PrinterLLVM *PI; - - formatted_raw_ostream FOS(OS); - if (PL == PRINTER_LANG_CPP) { - PI = new PrinterLLVM(FOS, CGTarget.getName().str()); - } else if (PL == PRINTER_LANG_CAPSTONE_C) { - PI = new PrinterCapstone(FOS, CGTarget.getName().str()); - } else { - llvm_unreachable("AsmWriterEmitter does not support the given output language."); - } - AsmWriterEmitter(RK, *PI).run(); - delete PI; + emitSourceFileHeader("Assembly Writer Source Fragment", OS); + AsmWriterEmitter(RK).run(OS); } } // end namespace llvm diff --git a/llvm/utils/TableGen/AsmWriterInst.cpp b/llvm/utils/TableGen/AsmWriterInst.cpp index 2d4b7f4da3ec..4a78108d6f4a 100644 --- a/llvm/utils/TableGen/AsmWriterInst.cpp +++ b/llvm/utils/TableGen/AsmWriterInst.cpp @@ -13,11 +13,9 @@ #include "AsmWriterInst.h" #include "CodeGenInstruction.h" #include "CodeGenTarget.h" -#include "Printer.h" #include "llvm/ADT/StringExtras.h" #include "llvm/TableGen/Error.h" #include "llvm/TableGen/Record.h" -#include using namespace llvm; @@ -25,38 +23,15 @@ static bool isIdentChar(char C) { return isAlnum(C) || C == '_'; } std::string AsmWriterOperand::getCode(bool PassSubtarget) const { if (OperandType == isLiteralTextOperand) { - std::string Res; - if (Str.size() == 1) { - Res = "SStream_concat1(O, '" + Str + "');"; - return Res; - } - - Res = "SStream_concat0(O, \"" + Str + "\");"; - return Res; + if (Str.size() == 1) + return "O << '" + Str + "';"; + return "O << \"" + Str + "\";"; } if (OperandType == isLiteralStatementOperand) return Str; - bool LangCS = PrinterLLVM::getLanguage() == PRINTER_LANG_CAPSTONE_C; - PassSubtarget = LangCS ? false : PassSubtarget; - - std::string Result; - if (LangCS && PCRel) { - // Those two functions have two different signatures which is not supported - // in C. For the PCRel version (gets the Address as parameter), we add an - // "Addr" to the name. - if (Str.find("printOperand") == 0) - Result = Str + "Addr"; - else if (Str.find("printAdrLabelOperand") == 0) { - unsigned TemplArgsIdx = Str.find("<"); - Result = Str.substr(0, TemplArgsIdx) + "Addr" + Str.substr(TemplArgsIdx); - } - } else - Result = Str; - - Result = Result + "(MI"; - + std::string Result = Str + "(MI"; if (PCRel) Result += ", Address"; if (MIOpNo != ~0U) diff --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt index 19ce3674f9ee..aa16e7e894ac 100644 --- a/llvm/utils/TableGen/CMakeLists.txt +++ b/llvm/utils/TableGen/CMakeLists.txt @@ -41,8 +41,6 @@ add_tablegen(llvm-tblgen LLVM OptParserEmitter.cpp OptRSTEmitter.cpp PredicateExpander.cpp - PrinterLLVM.cpp - PrinterCapstone.cpp PseudoLoweringEmitter.cpp CompressInstEmitter.cpp RegisterBankEmitter.cpp @@ -50,7 +48,6 @@ add_tablegen(llvm-tblgen LLVM RISCVTargetDefEmitter.cpp SDNodeProperties.cpp SearchableTableEmitter.cpp - StringMatcher.cpp SubtargetEmitter.cpp SubtargetFeatureInfo.cpp TableGen.cpp diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp index 16abfc0af892..b7240f01300c 100644 --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -17,7 +17,6 @@ #include "CodeGenInstruction.h" #include "CodeGenIntrinsics.h" #include "CodeGenSchedule.h" -#include "Printer.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/CommandLine.h" #include "llvm/TableGen/Error.h" @@ -273,11 +272,7 @@ std::string llvm::getQualifiedName(const Record *R) { Namespace = std::string(R->getValueAsString("Namespace")); if (Namespace.empty()) return std::string(R->getName()); - - if (PrinterLLVM::getLanguage() == PRINTER_LANG_CAPSTONE_C) - return Namespace + "_" + R->getName().str(); - else - return Namespace + "::" + R->getName().str(); + return Namespace + "::" + R->getName().str(); } diff --git a/llvm/utils/TableGen/DecoderEmitter.cpp b/llvm/utils/TableGen/DecoderEmitter.cpp index b341fd0415a6..8f816744370c 100644 --- a/llvm/utils/TableGen/DecoderEmitter.cpp +++ b/llvm/utils/TableGen/DecoderEmitter.cpp @@ -13,9 +13,7 @@ #include "CodeGenInstruction.h" #include "CodeGenTarget.h" -#include "DecoderEmitterTypes.h" #include "InfoByHwMode.h" -#include "Printer.h" #include "VarLenCodeEmitterGen.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/ArrayRef.h" @@ -58,38 +56,117 @@ STATISTIC(NumInstructions, "Number of instructions considered"); STATISTIC(NumEncodingsSupported, "Number of encodings supported"); STATISTIC(NumEncodingsOmitted, "Number of encodings omitted"); +struct EncodingField { + unsigned Base, Width, Offset; + EncodingField(unsigned B, unsigned W, unsigned O) + : Base(B), Width(W), Offset(O) { } +}; + +struct OperandInfo { + std::vector Fields; + std::string Decoder; + bool HasCompleteDecoder; + uint64_t InitValue; + + OperandInfo(std::string D, bool HCD) + : Decoder(std::move(D)), HasCompleteDecoder(HCD), InitValue(0) {} + + void addField(unsigned Base, unsigned Width, unsigned Offset) { + Fields.push_back(EncodingField(Base, Width, Offset)); + } + + unsigned numFields() const { return Fields.size(); } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return Fields.begin(); } + const_iterator end() const { return Fields.end(); } +}; + +typedef std::vector DecoderTable; +typedef uint32_t DecoderFixup; +typedef std::vector FixupList; +typedef std::vector FixupScopeList; +typedef SmallSetVector PredicateSet; +typedef SmallSetVector DecoderSet; +struct DecoderTableInfo { + DecoderTable Table; + FixupScopeList FixupStack; + PredicateSet Predicates; + DecoderSet Decoders; +}; + +struct EncodingAndInst { + const Record *EncodingDef; + const CodeGenInstruction *Inst; + StringRef HwModeName; + + EncodingAndInst(const Record *EncodingDef, const CodeGenInstruction *Inst, + StringRef HwModeName = "") + : EncodingDef(EncodingDef), Inst(Inst), HwModeName(HwModeName) {} +}; + +struct EncodingIDAndOpcode { + unsigned EncodingID; + unsigned Opcode; + + EncodingIDAndOpcode() : EncodingID(0), Opcode(0) {} + EncodingIDAndOpcode(unsigned EncodingID, unsigned Opcode) + : EncodingID(EncodingID), Opcode(Opcode) {} +}; + +raw_ostream &operator<<(raw_ostream &OS, const EncodingAndInst &Value) { + if (Value.EncodingDef != Value.Inst->TheDef) + OS << Value.EncodingDef->getName() << ":"; + OS << Value.Inst->TheDef->getName(); + return OS; +} + class DecoderEmitter { RecordKeeper &RK; - const PrinterLLVM &PI; std::vector NumberedEncodings; - ArrayRef NumberedInstructions; - DenseMap IndexOfInstruction; - // The opcode map. - // It maps the DecoderNamespace (e.g. ARM Thumb16) and instruction sizes - // to the encodings which belong into this subset. - std::map, std::vector> - OpcMap; - std::map> Operands; - std::vector InstrLen; - public: - // Defaults preserved here for documentation, even though they aren't - // strictly necessary given the way that this is currently being called. - DecoderEmitter(RecordKeeper &R, const PrinterLLVM &PI) - : RK(R), PI(PI), NumberedInstructions(nullptr), Target(R) {} - void retrieveHwModeEncodings(); - unsigned fillOpcMap(bool const IsVarLenInst); + DecoderEmitter(RecordKeeper &R, std::string PredicateNamespace) + : RK(R), Target(R), PredicateNamespace(std::move(PredicateNamespace)) {} + + // Emit the decoder state machine table. + void emitTable(formatted_raw_ostream &o, DecoderTable &Table, + unsigned Indentation, unsigned BitWidth, + StringRef Namespace) const; + void emitInstrLenTable(formatted_raw_ostream &OS, + std::vector &InstrLen) const; + void emitPredicateFunction(formatted_raw_ostream &OS, + PredicateSet &Predicates, + unsigned Indentation) const; + void emitDecoderFunction(formatted_raw_ostream &OS, + DecoderSet &Decoders, + unsigned Indentation) const; // run - Output the code emitter - void run(); + void run(raw_ostream &o); private: CodeGenTarget Target; + +public: + std::string PredicateNamespace; }; } // end anonymous namespace +// The set (BIT_TRUE, BIT_FALSE, BIT_UNSET) represents a ternary logic system +// for a bit value. +// +// BIT_UNFILTERED is used as the init value for a filter position. It is used +// only for filter processings. +typedef enum { + BIT_TRUE, // '1' + BIT_FALSE, // '0' + BIT_UNSET, // '?' + BIT_UNFILTERED // unfiltered +} bit_value_t; + static bool ValueSet(bit_value_t V) { return (V == BIT_TRUE || V == BIT_FALSE); } @@ -262,6 +339,16 @@ class Filter { } // end anonymous namespace +// These are states of our finite state machines used in FilterChooser's +// filterProcessor() which produces the filter candidates to use. +typedef enum { + ATTR_NONE, + ATTR_FILTERED, + ATTR_ALL_SET, + ATTR_ALL_UNSET, + ATTR_MIXED +} bitAttr_t; + /// FilterChooser - FilterChooser chooses the best filter among a set of Filters /// in order to perform the decoding of instructions at the current level. /// @@ -283,15 +370,12 @@ class FilterChooser { protected: friend class Filter; - // The Printer to emit source code from. - const PrinterLLVM &PI; - // Vector of codegen instructions to choose our filter. ArrayRef AllInstructions; // Vector of uid's for this filter chooser to work on. - // The first member of the pair is the opcode id being decoded, the second - // is the opcode id that should be emitted. + // The first member of the pair is the opcode id being decoded, the second is + // the opcode id that should be emitted. const std::vector &Opcodes; // Lookup table for the operand decoding of instructions. @@ -313,25 +397,28 @@ class FilterChooser { // Width of instructions unsigned BitWidth; + // Parent emitter + const DecoderEmitter *Emitter; + public: - FilterChooser(const PrinterLLVM &PI, ArrayRef Insts, + FilterChooser(ArrayRef Insts, const std::vector &IDs, const std::map> &Ops, - unsigned BW) - : PI(PI), AllInstructions(Insts), Opcodes(IDs), Operands(Ops), + unsigned BW, const DecoderEmitter *E) + : AllInstructions(Insts), Opcodes(IDs), Operands(Ops), FilterBitValues(BW, BIT_UNFILTERED), Parent(nullptr), BestIndex(-1), - BitWidth(BW) { + BitWidth(BW), Emitter(E) { doFilter(); } - FilterChooser(const PrinterLLVM &PI, ArrayRef Insts, + FilterChooser(ArrayRef Insts, const std::vector &IDs, const std::map> &Ops, const std::vector &ParentFilterBitValues, - const FilterChooser &Parent) - : PI(Parent.PI), AllInstructions(Insts), Opcodes(IDs), Operands(Ops), - FilterBitValues(ParentFilterBitValues), Parent(&Parent), BestIndex(-1), - BitWidth(Parent.BitWidth) { + const FilterChooser &parent) + : AllInstructions(Insts), Opcodes(IDs), Operands(Ops), + FilterBitValues(ParentFilterBitValues), Parent(&parent), BestIndex(-1), + BitWidth(parent.BitWidth), Emitter(parent.Emitter) { doFilter(); } @@ -342,35 +429,59 @@ class FilterChooser { protected: // Populates the insn given the uid. - void insnWithID(insn_t &Insn, unsigned Opcode) const; + void insnWithID(insn_t &Insn, unsigned Opcode) const { + BitsInit &Bits = getBitsField(*AllInstructions[Opcode].EncodingDef, "Inst"); + Insn.resize(BitWidth > Bits.getNumBits() ? BitWidth : Bits.getNumBits(), + BIT_UNSET); + // We may have a SoftFail bitmask, which specifies a mask where an encoding + // may differ from the value in "Inst" and yet still be valid, but the + // disassembler should return SoftFail instead of Success. + // + // This is used for marking UNPREDICTABLE instructions in the ARM world. + const RecordVal *RV = + AllInstructions[Opcode].EncodingDef->getValue("SoftFail"); + const BitsInit *SFBits = RV ? dyn_cast(RV->getValue()) : nullptr; + for (unsigned i = 0; i < Bits.getNumBits(); ++i) { + if (SFBits && bitFromBits(*SFBits, i) == BIT_TRUE) + Insn[i] = BIT_UNSET; + else + Insn[i] = bitFromBits(Bits, i); + } + } // Emit the name of the encoding/instruction pair. - void emitNameWithID(raw_ostream &ErrOS, unsigned Opcode) const; + void emitNameWithID(raw_ostream &OS, unsigned Opcode) const { + const Record *EncodingDef = AllInstructions[Opcode].EncodingDef; + const Record *InstDef = AllInstructions[Opcode].Inst->TheDef; + if (EncodingDef != InstDef) + OS << EncodingDef->getName() << ":"; + OS << InstDef->getName(); + } - // Populates the field of the insn given the start position and the number - // of consecutive bits to scan for. + // Populates the field of the insn given the start position and the number of + // consecutive bits to scan for. // // Returns false if there exists any uninitialized bit value in the range. // Returns true, otherwise. bool fieldFromInsn(uint64_t &Field, insn_t &Insn, unsigned StartBit, unsigned NumBits) const; - /// dumpFilterArray - dumpFilterArray prints out debugging info for the - /// given filter array as a series of chars. - void dumpFilterArray(raw_ostream &ErrOS, - const std::vector &Filter) const; + /// dumpFilterArray - dumpFilterArray prints out debugging info for the given + /// filter array as a series of chars. + void dumpFilterArray(raw_ostream &o, + const std::vector & filter) const; /// dumpStack - dumpStack traverses the filter chooser chain and calls /// dumpFilterArray on each filter chooser up to the top level one. - void dumpStack(raw_ostream &ErrOS, const char *Prefix) const; + void dumpStack(raw_ostream &o, const char *prefix) const; Filter &bestFilter() { assert(BestIndex != -1 && "BestIndex not set"); return Filters[BestIndex]; } - bool positionFiltered(unsigned I) const { - return ValueSet(FilterBitValues[I]); + bool PositionFiltered(unsigned i) const { + return ValueSet(FilterBitValues[i]); } // Calculates the island(s) needed to decode the instruction. @@ -382,11 +493,20 @@ class FilterChooser { std::vector &FieldVals, const insn_t &Insn) const; + // Emits code to check the Predicates member of an instruction are true. + // Returns true if predicate matches were emitted, false otherwise. + bool emitPredicateMatch(raw_ostream &o, unsigned &Indentation, + unsigned Opc) const; + bool emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp, + raw_ostream &OS) const; + bool doesOpcodeNeedPredicate(unsigned Opc) const; unsigned getPredicateIndex(DecoderTableInfo &TableInfo, StringRef P) const; - void emitPredicateTableEntry(DecoderTableInfo &TableInfo, unsigned Opc) const; + void emitPredicateTableEntry(DecoderTableInfo &TableInfo, + unsigned Opc) const; - void emitSoftFailTableEntry(DecoderTableInfo &TableInfo, unsigned Opc) const; + void emitSoftFailTableEntry(DecoderTableInfo &TableInfo, + unsigned Opc) const; // Emits table entries to decode the singleton. void emitSingletonTableEntry(DecoderTableInfo &TableInfo, @@ -396,22 +516,26 @@ class FilterChooser { void emitSingletonTableEntry(DecoderTableInfo &TableInfo, const Filter &Best) const; - void emitDecoder(raw_ostream &DecoderOS, unsigned Opc, + void emitBinaryParser(raw_ostream &o, unsigned &Indentation, + const OperandInfo &OpInfo, + bool &OpHasCompleteDecoder) const; + + void emitDecoder(raw_ostream &OS, unsigned Indentation, unsigned Opc, bool &HasCompleteDecoder) const; unsigned getDecoderIndex(DecoderSet &Decoders, unsigned Opc, bool &HasCompleteDecoder) const; // Assign a single filter and run with it. - void runSingleFilter(unsigned StartBit, unsigned NumBit, bool Mixed); + void runSingleFilter(unsigned startBit, unsigned numBit, bool mixed); // reportRegion is a helper function for filterProcessor to mark a region as // eligible for use as a filter region. void reportRegion(bitAttr_t RA, unsigned StartBit, unsigned BitIndex, bool AllowMixed); - // FilterProcessor scans the well-known encoding bits of the instructions - // and builds up a list of candidate filters. It chooses the best filter - // and recursively descends down the decoding tree. + // FilterProcessor scans the well-known encoding bits of the instructions and + // builds up a list of candidate filters. It chooses the best filter and + // recursively descends down the decoding tree. bool filterProcessor(bool AllowMixed, bool Greedy = true); // Decides on the best configuration of filter(s) to use in order to decode @@ -493,11 +617,9 @@ void Filter::recurse() { // Delegates to an inferior filter chooser for further processing on this // group of instructions whose segment values are variable. - FilterChooserMap.insert(std::make_pair( - NO_FIXED_SEGMENTS_SENTINEL, - std::make_unique(Owner->PI, Owner->AllInstructions, - VariableInstructions, Owner->Operands, - BitValueArray, *Owner))); + FilterChooserMap.insert(std::make_pair(NO_FIXED_SEGMENTS_SENTINEL, + std::make_unique(Owner->AllInstructions, + VariableInstructions, Owner->Operands, BitValueArray, *Owner))); } // No need to recurse for a singleton filtered instruction. @@ -508,11 +630,11 @@ void Filter::recurse() { } // Otherwise, create sub choosers. - for (const auto &InstSubset: FilteredInstructions) { + for (const auto &Inst : FilteredInstructions) { // Marks all the segment positions with either BIT_TRUE or BIT_FALSE. for (unsigned bitIndex = 0; bitIndex < NumBits; ++bitIndex) { - if (InstSubset.first & (1ULL << bitIndex)) + if (Inst.first & (1ULL << bitIndex)) BitValueArray[StartBit + bitIndex] = BIT_TRUE; else BitValueArray[StartBit + bitIndex] = BIT_FALSE; @@ -521,9 +643,9 @@ void Filter::recurse() { // Delegates to an inferior filter chooser for further processing on this // category of instructions. FilterChooserMap.insert(std::make_pair( - InstSubset.first, std::make_unique( - Owner->PI, Owner->AllInstructions, InstSubset.second, - Owner->Operands, BitValueArray, *Owner))); + Inst.first, std::make_unique( + Owner->AllInstructions, Inst.second, + Owner->Operands, BitValueArray, *Owner))); } } @@ -637,35 +759,259 @@ unsigned Filter::usefulness() const { // // ////////////////////////////////// -// Populates the insn given the uid. -void FilterChooser::insnWithID(insn_t &Insn, unsigned Opcode) const { - BitsInit const &Bits = - getBitsField(*AllInstructions[Opcode].EncodingDef, "Inst"); - Insn.resize(BitWidth > Bits.getNumBits() ? BitWidth : Bits.getNumBits(), - BIT_UNSET); - // We may have a SoftFail bitmask, which specifies a mask where an encoding - // may differ from the value in "Inst" and yet still be valid, but the - // disassembler should return SoftFail instead of Success. - // - // This is used for marking UNPREDICTABLE instructions in the ARM world. - const RecordVal *RV = - AllInstructions[Opcode].EncodingDef->getValue("SoftFail"); - const BitsInit *SFBits = RV ? dyn_cast(RV->getValue()) : nullptr; - for (unsigned I = 0; I < Bits.getNumBits(); ++I) { - if (SFBits && bitFromBits(*SFBits, I) == BIT_TRUE) - Insn[I] = BIT_UNSET; - else - Insn[I] = bitFromBits(Bits, I); +// Emit the decoder state machine table. +void DecoderEmitter::emitTable(formatted_raw_ostream &OS, DecoderTable &Table, + unsigned Indentation, unsigned BitWidth, + StringRef Namespace) const { + OS.indent(Indentation) << "static const uint8_t DecoderTable" << Namespace + << BitWidth << "[] = {\n"; + + Indentation += 2; + + // FIXME: We may be able to use the NumToSkip values to recover + // appropriate indentation levels. + DecoderTable::const_iterator I = Table.begin(); + DecoderTable::const_iterator E = Table.end(); + while (I != E) { + assert (I < E && "incomplete decode table entry!"); + + uint64_t Pos = I - Table.begin(); + OS << "/* " << Pos << " */"; + OS.PadToColumn(12); + + switch (*I) { + default: + PrintFatalError("invalid decode table opcode"); + case MCD::OPC_ExtractField: { + ++I; + unsigned Start = *I++; + unsigned Len = *I++; + OS.indent(Indentation) << "MCD::OPC_ExtractField, " << Start << ", " + << Len << ", // Inst{"; + if (Len > 1) + OS << (Start + Len - 1) << "-"; + OS << Start << "} ...\n"; + break; + } + case MCD::OPC_FilterValue: { + ++I; + OS.indent(Indentation) << "MCD::OPC_FilterValue, "; + // The filter value is ULEB128 encoded. + while (*I >= 128) + OS << (unsigned)*I++ << ", "; + OS << (unsigned)*I++ << ", "; + + // 24-bit numtoskip value. + uint8_t Byte = *I++; + uint32_t NumToSkip = Byte; + OS << (unsigned)Byte << ", "; + Byte = *I++; + OS << (unsigned)Byte << ", "; + NumToSkip |= Byte << 8; + Byte = *I++; + OS << utostr(Byte) << ", "; + NumToSkip |= Byte << 16; + OS << "// Skip to: " << ((I - Table.begin()) + NumToSkip) << "\n"; + break; + } + case MCD::OPC_CheckField: { + ++I; + unsigned Start = *I++; + unsigned Len = *I++; + OS.indent(Indentation) << "MCD::OPC_CheckField, " << Start << ", " + << Len << ", ";// << Val << ", " << NumToSkip << ",\n"; + // ULEB128 encoded field value. + for (; *I >= 128; ++I) + OS << (unsigned)*I << ", "; + OS << (unsigned)*I++ << ", "; + // 24-bit numtoskip value. + uint8_t Byte = *I++; + uint32_t NumToSkip = Byte; + OS << (unsigned)Byte << ", "; + Byte = *I++; + OS << (unsigned)Byte << ", "; + NumToSkip |= Byte << 8; + Byte = *I++; + OS << utostr(Byte) << ", "; + NumToSkip |= Byte << 16; + OS << "// Skip to: " << ((I - Table.begin()) + NumToSkip) << "\n"; + break; + } + case MCD::OPC_CheckPredicate: { + ++I; + OS.indent(Indentation) << "MCD::OPC_CheckPredicate, "; + for (; *I >= 128; ++I) + OS << (unsigned)*I << ", "; + OS << (unsigned)*I++ << ", "; + + // 24-bit numtoskip value. + uint8_t Byte = *I++; + uint32_t NumToSkip = Byte; + OS << (unsigned)Byte << ", "; + Byte = *I++; + OS << (unsigned)Byte << ", "; + NumToSkip |= Byte << 8; + Byte = *I++; + OS << utostr(Byte) << ", "; + NumToSkip |= Byte << 16; + OS << "// Skip to: " << ((I - Table.begin()) + NumToSkip) << "\n"; + break; + } + case MCD::OPC_Decode: + case MCD::OPC_TryDecode: { + bool IsTry = *I == MCD::OPC_TryDecode; + ++I; + // Extract the ULEB128 encoded Opcode to a buffer. + uint8_t Buffer[16], *p = Buffer; + while ((*p++ = *I++) >= 128) + assert((p - Buffer) <= (ptrdiff_t)sizeof(Buffer) + && "ULEB128 value too large!"); + // Decode the Opcode value. + unsigned Opc = decodeULEB128(Buffer); + OS.indent(Indentation) << "MCD::OPC_" << (IsTry ? "Try" : "") + << "Decode, "; + for (p = Buffer; *p >= 128; ++p) + OS << (unsigned)*p << ", "; + OS << (unsigned)*p << ", "; + + // Decoder index. + for (; *I >= 128; ++I) + OS << (unsigned)*I << ", "; + OS << (unsigned)*I++ << ", "; + + if (!IsTry) { + OS << "// Opcode: " << NumberedEncodings[Opc] << "\n"; + break; + } + + // Fallthrough for OPC_TryDecode. + + // 24-bit numtoskip value. + uint8_t Byte = *I++; + uint32_t NumToSkip = Byte; + OS << (unsigned)Byte << ", "; + Byte = *I++; + OS << (unsigned)Byte << ", "; + NumToSkip |= Byte << 8; + Byte = *I++; + OS << utostr(Byte) << ", "; + NumToSkip |= Byte << 16; + + OS << "// Opcode: " << NumberedEncodings[Opc] + << ", skip to: " << ((I - Table.begin()) + NumToSkip) << "\n"; + break; + } + case MCD::OPC_SoftFail: { + ++I; + OS.indent(Indentation) << "MCD::OPC_SoftFail"; + // Positive mask + uint64_t Value = 0; + unsigned Shift = 0; + do { + OS << ", " << (unsigned)*I; + Value += (*I & 0x7f) << Shift; + Shift += 7; + } while (*I++ >= 128); + if (Value > 127) { + OS << " /* 0x"; + OS.write_hex(Value); + OS << " */"; + } + // Negative mask + Value = 0; + Shift = 0; + do { + OS << ", " << (unsigned)*I; + Value += (*I & 0x7f) << Shift; + Shift += 7; + } while (*I++ >= 128); + if (Value > 127) { + OS << " /* 0x"; + OS.write_hex(Value); + OS << " */"; + } + OS << ",\n"; + break; + } + case MCD::OPC_Fail: { + ++I; + OS.indent(Indentation) << "MCD::OPC_Fail,\n"; + break; + } + } + } + OS.indent(Indentation) << "0\n"; + + Indentation -= 2; + + OS.indent(Indentation) << "};\n\n"; +} + +void DecoderEmitter::emitInstrLenTable(formatted_raw_ostream &OS, + std::vector &InstrLen) const { + OS << "static const uint8_t InstrLenTable[] = {\n"; + for (unsigned &Len : InstrLen) { + OS << Len << ",\n"; + } + OS << "};\n\n"; +} + +void DecoderEmitter::emitPredicateFunction(formatted_raw_ostream &OS, + PredicateSet &Predicates, + unsigned Indentation) const { + // The predicate function is just a big switch statement based on the + // input predicate index. + OS.indent(Indentation) << "static bool checkDecoderPredicate(unsigned Idx, " + << "const FeatureBitset &Bits) {\n"; + Indentation += 2; + if (!Predicates.empty()) { + OS.indent(Indentation) << "switch (Idx) {\n"; + OS.indent(Indentation) << "default: llvm_unreachable(\"Invalid index!\");\n"; + unsigned Index = 0; + for (const auto &Predicate : Predicates) { + OS.indent(Indentation) << "case " << Index++ << ":\n"; + OS.indent(Indentation+2) << "return (" << Predicate << ");\n"; + } + OS.indent(Indentation) << "}\n"; + } else { + // No case statement to emit + OS.indent(Indentation) << "llvm_unreachable(\"Invalid index!\");\n"; } + Indentation -= 2; + OS.indent(Indentation) << "}\n\n"; } -// Emit the name of the encoding/instruction pair. -void FilterChooser::emitNameWithID(raw_ostream &ErrOS, unsigned Opcode) const { - const Record *EncodingDef = AllInstructions[Opcode].EncodingDef; - const Record *InstDef = AllInstructions[Opcode].Inst->TheDef; - if (EncodingDef != InstDef) - ErrOS << EncodingDef->getName() << ":"; - ErrOS << InstDef->getName(); +void DecoderEmitter::emitDecoderFunction(formatted_raw_ostream &OS, + DecoderSet &Decoders, + unsigned Indentation) const { + // The decoder function is just a big switch statement based on the + // input decoder index. + OS.indent(Indentation) << "template \n"; + OS.indent(Indentation) << "static DecodeStatus decodeToMCInst(DecodeStatus S," + << " unsigned Idx, InsnType insn, MCInst &MI,\n"; + OS.indent(Indentation) + << " uint64_t " + << "Address, const MCDisassembler *Decoder, bool &DecodeComplete) {\n"; + Indentation += 2; + OS.indent(Indentation) << "DecodeComplete = true;\n"; + // TODO: When InsnType is large, using uint64_t limits all fields to 64 bits + // It would be better for emitBinaryParser to use a 64-bit tmp whenever + // possible but fall back to an InsnType-sized tmp for truly large fields. + OS.indent(Indentation) << "using TmpType = " + "std::conditional_t::" + "value, InsnType, uint64_t>;\n"; + OS.indent(Indentation) << "TmpType tmp;\n"; + OS.indent(Indentation) << "switch (Idx) {\n"; + OS.indent(Indentation) << "default: llvm_unreachable(\"Invalid index!\");\n"; + unsigned Index = 0; + for (const auto &Decoder : Decoders) { + OS.indent(Indentation) << "case " << Index++ << ":\n"; + OS << Decoder; + OS.indent(Indentation+2) << "return S;\n"; + } + OS.indent(Indentation) << "}\n"; + Indentation -= 2; + OS.indent(Indentation) << "}\n\n"; } // Populates the field of the insn given the start position and the number of @@ -743,7 +1089,7 @@ unsigned FilterChooser::getIslands(std::vector &StartBits, for (unsigned i = 0; i < BitWidth; ++i) { int64_t Val = Value(Insn[i]); - bool Filtered = positionFiltered(i); + bool Filtered = PositionFiltered(i); switch (State) { default: llvm_unreachable("Unreachable code!"); case 0: @@ -783,22 +1129,66 @@ unsigned FilterChooser::getIslands(std::vector &StartBits, return Num; } -void FilterChooser::emitDecoder(raw_ostream &DecoderOS, unsigned Opc, - bool &HasCompleteDecoder) const { +void FilterChooser::emitBinaryParser(raw_ostream &o, unsigned &Indentation, + const OperandInfo &OpInfo, + bool &OpHasCompleteDecoder) const { + const std::string &Decoder = OpInfo.Decoder; + + bool UseInsertBits = OpInfo.numFields() != 1 || OpInfo.InitValue != 0; + + if (UseInsertBits) { + o.indent(Indentation) << "tmp = 0x"; + o.write_hex(OpInfo.InitValue); + o << ";\n"; + } + + for (const EncodingField &EF : OpInfo) { + o.indent(Indentation); + if (UseInsertBits) + o << "insertBits(tmp, "; + else + o << "tmp = "; + o << "fieldFromInstruction(insn, " << EF.Base << ", " << EF.Width << ')'; + if (UseInsertBits) + o << ", " << EF.Offset << ", " << EF.Width << ')'; + else if (EF.Offset != 0) + o << " << " << EF.Offset; + o << ";\n"; + } + + if (Decoder != "") { + OpHasCompleteDecoder = OpInfo.HasCompleteDecoder; + o.indent(Indentation) << "if (!Check(S, " << Decoder + << "(MI, tmp, Address, Decoder))) { " + << (OpHasCompleteDecoder ? "" + : "DecodeComplete = false; ") + << "return MCDisassembler::Fail; }\n"; + } else { + OpHasCompleteDecoder = true; + o.indent(Indentation) << "MI.addOperand(MCOperand::createImm(tmp));\n"; + } +} + +void FilterChooser::emitDecoder(raw_ostream &OS, unsigned Indentation, + unsigned Opc, bool &HasCompleteDecoder) const { HasCompleteDecoder = true; for (const auto &Op : Operands.find(Opc)->second) { // If a custom instruction decoder was specified, use that. if (Op.numFields() == 0 && !Op.Decoder.empty()) { HasCompleteDecoder = Op.HasCompleteDecoder; - PI.decoderEmitterEmitOpDecoder(DecoderOS, Op); + OS.indent(Indentation) + << "if (!Check(S, " << Op.Decoder + << "(MI, insn, Address, Decoder))) { " + << (HasCompleteDecoder ? "" : "DecodeComplete = false; ") + << "return MCDisassembler::Fail; }\n"; break; } - PI.decoderEmitterEmitOpBinaryParser(DecoderOS, Op); - - // If a custom decoder was set the flag decides otherwise its true. - HasCompleteDecoder = Op.Decoder != "" ? Op.HasCompleteDecoder : true; + bool OpHasCompleteDecoder; + emitBinaryParser(OS, Indentation, Op, OpHasCompleteDecoder); + if (!OpHasCompleteDecoder) + HasCompleteDecoder = false; } } @@ -807,8 +1197,11 @@ unsigned FilterChooser::getDecoderIndex(DecoderSet &Decoders, bool &HasCompleteDecoder) const { // Build up the predicate string. SmallString<256> Decoder; + // FIXME: emitDecoder() function can take a buffer directly rather than + // a stream. raw_svector_ostream S(Decoder); - emitDecoder(S, Opc, HasCompleteDecoder); + unsigned I = 4; + emitDecoder(S, I, Opc, HasCompleteDecoder); // Using the full decoder string as the key value here is a bit // heavyweight, but is effective. If the string comparisons become a @@ -823,6 +1216,63 @@ unsigned FilterChooser::getDecoderIndex(DecoderSet &Decoders, return (unsigned)(P - Decoders.begin()); } +// If ParenIfBinOp is true, print a surrounding () if Val uses && or ||. +bool FilterChooser::emitPredicateMatchAux(const Init &Val, bool ParenIfBinOp, + raw_ostream &OS) const { + if (auto *D = dyn_cast(&Val)) { + if (!D->getDef()->isSubClassOf("SubtargetFeature")) + return true; + OS << "Bits[" << Emitter->PredicateNamespace << "::" << D->getAsString() + << "]"; + return false; + } + if (auto *D = dyn_cast(&Val)) { + std::string Op = D->getOperator()->getAsString(); + if (Op == "not" && D->getNumArgs() == 1) { + OS << '!'; + return emitPredicateMatchAux(*D->getArg(0), true, OS); + } + if ((Op == "any_of" || Op == "all_of") && D->getNumArgs() > 0) { + bool Paren = D->getNumArgs() > 1 && std::exchange(ParenIfBinOp, true); + if (Paren) + OS << '('; + ListSeparator LS(Op == "any_of" ? " || " : " && "); + for (auto *Arg : D->getArgs()) { + OS << LS; + if (emitPredicateMatchAux(*Arg, ParenIfBinOp, OS)) + return true; + } + if (Paren) + OS << ')'; + return false; + } + } + return true; +} + +bool FilterChooser::emitPredicateMatch(raw_ostream &o, unsigned &Indentation, + unsigned Opc) const { + ListInit *Predicates = + AllInstructions[Opc].EncodingDef->getValueAsListInit("Predicates"); + bool IsFirstEmission = true; + for (unsigned i = 0; i < Predicates->size(); ++i) { + Record *Pred = Predicates->getElementAsRecord(i); + if (!Pred->getValue("AssemblerMatcherPredicate")) + continue; + + if (!isa(Pred->getValue("AssemblerCondDag")->getValue())) + continue; + + if (!IsFirstEmission) + o << " && "; + if (emitPredicateMatchAux(*Pred->getValueAsDag("AssemblerCondDag"), + Predicates->size() > 1, o)) + PrintFatalError(Pred->getLoc(), "Invalid AssemblerCondDag!"); + IsFirstEmission = false; + } + return !Predicates->empty(); +} + bool FilterChooser::doesOpcodeNeedPredicate(unsigned Opc) const { ListInit *Predicates = AllInstructions[Opc].EncodingDef->getValueAsListInit("Predicates"); @@ -859,22 +1309,23 @@ void FilterChooser::emitPredicateTableEntry(DecoderTableInfo &TableInfo, // Build up the predicate string. SmallString<256> Predicate; + // FIXME: emitPredicateMatch() functions can take a buffer directly rather + // than a stream. raw_svector_ostream PS(Predicate); - const ListInit *Predicates = - AllInstructions[Opc].EncodingDef->getValueAsListInit("Predicates"); - PI.decoderEmitterEmitPredicateMatch(PS, Predicates, Opc); + unsigned I = 0; + emitPredicateMatch(PS, I, Opc); // Figure out the index into the predicate table for the predicate just // computed. - unsigned const PIdx = getPredicateIndex(TableInfo, PS.str()); + unsigned PIdx = getPredicateIndex(TableInfo, PS.str()); SmallString<16> PBytes; raw_svector_ostream S(PBytes); encodeULEB128(PIdx, S); TableInfo.Table.push_back(MCD::OPC_CheckPredicate); // Predicate index - for (unsigned I = 0, E = PBytes.size(); I != E; ++I) - TableInfo.Table.push_back(PBytes[I]); + for (unsigned i = 0, e = PBytes.size(); i != e; ++i) + TableInfo.Table.push_back(PBytes[i]); // Push location for NumToSkip backpatching. TableInfo.FixupStack.back().push_back(TableInfo.Table.size()); TableInfo.Table.push_back(0); @@ -1860,11 +2311,312 @@ populateInstruction(CodeGenTarget &Target, const Record &EncodingDef, return Bits.getNumBits(); } -/// Encodings of instructions might differ if the Hardware Mode is different as -/// well. Here we add all possible encodings. -void DecoderEmitter::retrieveHwModeEncodings() { - std::set HwModeNames; +// emitFieldFromInstruction - Emit the templated helper function +// fieldFromInstruction(). +// On Windows we make sure that this function is not inlined when +// using the VS compiler. It has a bug which causes the function +// to be optimized out in some circumstances. See llvm.org/pr38292 +static void emitFieldFromInstruction(formatted_raw_ostream &OS) { + OS << "// Helper functions for extracting fields from encoded instructions.\n" + << "// InsnType must either be integral or an APInt-like object that " + "must:\n" + << "// * be default-constructible and copy-constructible\n" + << "// * be constructible from an APInt (this can be private)\n" + << "// * Support insertBits(bits, startBit, numBits)\n" + << "// * Support extractBitsAsZExtValue(numBits, startBit)\n" + << "// * Support the ~, &, ==, and != operators with other objects of " + "the same type\n" + << "// * Support the != and bitwise & with uint64_t\n" + << "// * Support put (<<) to raw_ostream&\n" + << "template \n" + << "#if defined(_MSC_VER) && !defined(__clang__)\n" + << "__declspec(noinline)\n" + << "#endif\n" + << "static std::enable_if_t::value, InsnType>\n" + << "fieldFromInstruction(const InsnType &insn, unsigned startBit,\n" + << " unsigned numBits) {\n" + << " assert(startBit + numBits <= 64 && \"Cannot support >64-bit " + "extractions!\");\n" + << " assert(startBit + numBits <= (sizeof(InsnType) * 8) &&\n" + << " \"Instruction field out of bounds!\");\n" + << " InsnType fieldMask;\n" + << " if (numBits == sizeof(InsnType) * 8)\n" + << " fieldMask = (InsnType)(-1LL);\n" + << " else\n" + << " fieldMask = (((InsnType)1 << numBits) - 1) << startBit;\n" + << " return (insn & fieldMask) >> startBit;\n" + << "}\n" + << "\n" + << "template \n" + << "static std::enable_if_t::value, " + "uint64_t>\n" + << "fieldFromInstruction(const InsnType &insn, unsigned startBit,\n" + << " unsigned numBits) {\n" + << " return insn.extractBitsAsZExtValue(numBits, startBit);\n" + << "}\n\n"; +} + +// emitInsertBits - Emit the templated helper function insertBits(). +static void emitInsertBits(formatted_raw_ostream &OS) { + OS << "// Helper function for inserting bits extracted from an encoded " + "instruction into\n" + << "// a field.\n" + << "template \n" + << "static std::enable_if_t::value>\n" + << "insertBits(InsnType &field, InsnType bits, unsigned startBit, " + "unsigned numBits) {\n" + << " assert(startBit + numBits <= sizeof field * 8);\n" + << " field |= (InsnType)bits << startBit;\n" + << "}\n" + << "\n" + << "template \n" + << "static std::enable_if_t::value>\n" + << "insertBits(InsnType &field, uint64_t bits, unsigned startBit, " + "unsigned numBits) {\n" + << " field.insertBits(bits, startBit, numBits);\n" + << "}\n\n"; +} +// emitDecodeInstruction - Emit the templated helper function +// decodeInstruction(). +static void emitDecodeInstruction(formatted_raw_ostream &OS, + bool IsVarLenInst) { + OS << "template \n" + << "static DecodeStatus decodeInstruction(const uint8_t DecodeTable[], " + "MCInst &MI,\n" + << " InsnType insn, uint64_t " + "Address,\n" + << " const MCDisassembler *DisAsm,\n" + << " const MCSubtargetInfo &STI"; + if (IsVarLenInst) { + OS << ",\n" + << " llvm::function_ref makeUp"; + } + OS << ") {\n" + << " const FeatureBitset &Bits = STI.getFeatureBits();\n" + << "\n" + << " const uint8_t *Ptr = DecodeTable;\n" + << " uint64_t CurFieldValue = 0;\n" + << " DecodeStatus S = MCDisassembler::Success;\n" + << " while (true) {\n" + << " ptrdiff_t Loc = Ptr - DecodeTable;\n" + << " switch (*Ptr) {\n" + << " default:\n" + << " errs() << Loc << \": Unexpected decode table opcode!\\n\";\n" + << " return MCDisassembler::Fail;\n" + << " case MCD::OPC_ExtractField: {\n" + << " unsigned Start = *++Ptr;\n" + << " unsigned Len = *++Ptr;\n" + << " ++Ptr;\n"; + if (IsVarLenInst) + OS << " makeUp(insn, Start + Len);\n"; + OS << " CurFieldValue = fieldFromInstruction(insn, Start, Len);\n" + << " LLVM_DEBUG(dbgs() << Loc << \": OPC_ExtractField(\" << Start << " + "\", \"\n" + << " << Len << \"): \" << CurFieldValue << \"\\n\");\n" + << " break;\n" + << " }\n" + << " case MCD::OPC_FilterValue: {\n" + << " // Decode the field value.\n" + << " unsigned Len;\n" + << " uint64_t Val = decodeULEB128(++Ptr, &Len);\n" + << " Ptr += Len;\n" + << " // NumToSkip is a plain 24-bit integer.\n" + << " unsigned NumToSkip = *Ptr++;\n" + << " NumToSkip |= (*Ptr++) << 8;\n" + << " NumToSkip |= (*Ptr++) << 16;\n" + << "\n" + << " // Perform the filter operation.\n" + << " if (Val != CurFieldValue)\n" + << " Ptr += NumToSkip;\n" + << " LLVM_DEBUG(dbgs() << Loc << \": OPC_FilterValue(\" << Val << " + "\", \" << NumToSkip\n" + << " << \"): \" << ((Val != CurFieldValue) ? \"FAIL:\" " + ": \"PASS:\")\n" + << " << \" continuing at \" << (Ptr - DecodeTable) << " + "\"\\n\");\n" + << "\n" + << " break;\n" + << " }\n" + << " case MCD::OPC_CheckField: {\n" + << " unsigned Start = *++Ptr;\n" + << " unsigned Len = *++Ptr;\n"; + if (IsVarLenInst) + OS << " makeUp(insn, Start + Len);\n"; + OS << " uint64_t FieldValue = fieldFromInstruction(insn, Start, Len);\n" + << " // Decode the field value.\n" + << " unsigned PtrLen = 0;\n" + << " uint64_t ExpectedValue = decodeULEB128(++Ptr, &PtrLen);\n" + << " Ptr += PtrLen;\n" + << " // NumToSkip is a plain 24-bit integer.\n" + << " unsigned NumToSkip = *Ptr++;\n" + << " NumToSkip |= (*Ptr++) << 8;\n" + << " NumToSkip |= (*Ptr++) << 16;\n" + << "\n" + << " // If the actual and expected values don't match, skip.\n" + << " if (ExpectedValue != FieldValue)\n" + << " Ptr += NumToSkip;\n" + << " LLVM_DEBUG(dbgs() << Loc << \": OPC_CheckField(\" << Start << " + "\", \"\n" + << " << Len << \", \" << ExpectedValue << \", \" << " + "NumToSkip\n" + << " << \"): FieldValue = \" << FieldValue << \", " + "ExpectedValue = \"\n" + << " << ExpectedValue << \": \"\n" + << " << ((ExpectedValue == FieldValue) ? \"PASS\\n\" : " + "\"FAIL\\n\"));\n" + << " break;\n" + << " }\n" + << " case MCD::OPC_CheckPredicate: {\n" + << " unsigned Len;\n" + << " // Decode the Predicate Index value.\n" + << " unsigned PIdx = decodeULEB128(++Ptr, &Len);\n" + << " Ptr += Len;\n" + << " // NumToSkip is a plain 24-bit integer.\n" + << " unsigned NumToSkip = *Ptr++;\n" + << " NumToSkip |= (*Ptr++) << 8;\n" + << " NumToSkip |= (*Ptr++) << 16;\n" + << " // Check the predicate.\n" + << " bool Pred;\n" + << " if (!(Pred = checkDecoderPredicate(PIdx, Bits)))\n" + << " Ptr += NumToSkip;\n" + << " (void)Pred;\n" + << " LLVM_DEBUG(dbgs() << Loc << \": OPC_CheckPredicate(\" << PIdx " + "<< \"): \"\n" + << " << (Pred ? \"PASS\\n\" : \"FAIL\\n\"));\n" + << "\n" + << " break;\n" + << " }\n" + << " case MCD::OPC_Decode: {\n" + << " unsigned Len;\n" + << " // Decode the Opcode value.\n" + << " unsigned Opc = decodeULEB128(++Ptr, &Len);\n" + << " Ptr += Len;\n" + << " unsigned DecodeIdx = decodeULEB128(Ptr, &Len);\n" + << " Ptr += Len;\n" + << "\n" + << " MI.clear();\n" + << " MI.setOpcode(Opc);\n" + << " bool DecodeComplete;\n"; + if (IsVarLenInst) { + OS << " Len = InstrLenTable[Opc];\n" + << " makeUp(insn, Len);\n"; + } + OS << " S = decodeToMCInst(S, DecodeIdx, insn, MI, Address, DisAsm, " + "DecodeComplete);\n" + << " assert(DecodeComplete);\n" + << "\n" + << " LLVM_DEBUG(dbgs() << Loc << \": OPC_Decode: opcode \" << Opc\n" + << " << \", using decoder \" << DecodeIdx << \": \"\n" + << " << (S != MCDisassembler::Fail ? \"PASS\" : " + "\"FAIL\") << \"\\n\");\n" + << " return S;\n" + << " }\n" + << " case MCD::OPC_TryDecode: {\n" + << " unsigned Len;\n" + << " // Decode the Opcode value.\n" + << " unsigned Opc = decodeULEB128(++Ptr, &Len);\n" + << " Ptr += Len;\n" + << " unsigned DecodeIdx = decodeULEB128(Ptr, &Len);\n" + << " Ptr += Len;\n" + << " // NumToSkip is a plain 24-bit integer.\n" + << " unsigned NumToSkip = *Ptr++;\n" + << " NumToSkip |= (*Ptr++) << 8;\n" + << " NumToSkip |= (*Ptr++) << 16;\n" + << "\n" + << " // Perform the decode operation.\n" + << " MCInst TmpMI;\n" + << " TmpMI.setOpcode(Opc);\n" + << " bool DecodeComplete;\n" + << " S = decodeToMCInst(S, DecodeIdx, insn, TmpMI, Address, DisAsm, " + "DecodeComplete);\n" + << " LLVM_DEBUG(dbgs() << Loc << \": OPC_TryDecode: opcode \" << " + "Opc\n" + << " << \", using decoder \" << DecodeIdx << \": \");\n" + << "\n" + << " if (DecodeComplete) {\n" + << " // Decoding complete.\n" + << " LLVM_DEBUG(dbgs() << (S != MCDisassembler::Fail ? \"PASS\" : " + "\"FAIL\") << \"\\n\");\n" + << " MI = TmpMI;\n" + << " return S;\n" + << " } else {\n" + << " assert(S == MCDisassembler::Fail);\n" + << " // If the decoding was incomplete, skip.\n" + << " Ptr += NumToSkip;\n" + << " LLVM_DEBUG(dbgs() << \"FAIL: continuing at \" << (Ptr - " + "DecodeTable) << \"\\n\");\n" + << " // Reset decode status. This also drops a SoftFail status " + "that could be\n" + << " // set before the decode attempt.\n" + << " S = MCDisassembler::Success;\n" + << " }\n" + << " break;\n" + << " }\n" + << " case MCD::OPC_SoftFail: {\n" + << " // Decode the mask values.\n" + << " unsigned Len;\n" + << " uint64_t PositiveMask = decodeULEB128(++Ptr, &Len);\n" + << " Ptr += Len;\n" + << " uint64_t NegativeMask = decodeULEB128(Ptr, &Len);\n" + << " Ptr += Len;\n" + << " bool Fail = (insn & PositiveMask) != 0 || (~insn & " + "NegativeMask) != 0;\n" + << " if (Fail)\n" + << " S = MCDisassembler::SoftFail;\n" + << " LLVM_DEBUG(dbgs() << Loc << \": OPC_SoftFail: \" << (Fail ? " + "\"FAIL\\n\" : \"PASS\\n\"));\n" + << " break;\n" + << " }\n" + << " case MCD::OPC_Fail: {\n" + << " LLVM_DEBUG(dbgs() << Loc << \": OPC_Fail\\n\");\n" + << " return MCDisassembler::Fail;\n" + << " }\n" + << " }\n" + << " }\n" + << " llvm_unreachable(\"bogosity detected in disassembler state " + "machine!\");\n" + << "}\n\n"; +} + +// Helper to propagate SoftFail status. Returns false if the status is Fail; +// callers are expected to early-exit in that condition. (Note, the '&' operator +// is correct to propagate the values of this enum; see comment on 'enum +// DecodeStatus'.) +static void emitCheck(formatted_raw_ostream &OS) { + OS << "static bool Check(DecodeStatus &Out, DecodeStatus In) {\n" + << " Out = static_cast(Out & In);\n" + << " return Out != MCDisassembler::Fail;\n" + << "}\n\n"; +} + +// Emits disassembler code for instruction decoding. +void DecoderEmitter::run(raw_ostream &o) { + formatted_raw_ostream OS(o); + OS << "#include \"llvm/MC/MCInst.h\"\n"; + OS << "#include \"llvm/MC/MCSubtargetInfo.h\"\n"; + OS << "#include \"llvm/MC/SubtargetFeature.h\"\n"; + OS << "#include \"llvm/Support/DataTypes.h\"\n"; + OS << "#include \"llvm/Support/Debug.h\"\n"; + OS << "#include \"llvm/Support/LEB128.h\"\n"; + OS << "#include \"llvm/Support/raw_ostream.h\"\n"; + OS << "#include \n"; + OS << '\n'; + OS << "namespace llvm {\n\n"; + + emitFieldFromInstruction(OS); + emitInsertBits(OS); + emitCheck(OS); + + Target.reverseBitsForLittleEndianEncoding(); + + // Parameterize the decoders based on namespace and instruction width. + std::set HwModeNames; + const auto &NumberedInstructions = Target.getInstructionsByEnumValue(); + NumberedEncodings.reserve(NumberedInstructions.size()); + DenseMap IndexOfInstruction; // First, collect all HwModes referenced by the target. for (const auto &NumberedInstruction : NumberedInstructions) { IndexOfInstruction[NumberedInstruction->TheDef] = NumberedEncodings.size(); @@ -1873,7 +2625,7 @@ void DecoderEmitter::retrieveHwModeEncodings() { NumberedInstruction->TheDef->getValue("EncodingInfos")) { if (auto *DI = dyn_cast_or_null(RV->getValue())) { const CodeGenHwModes &HWM = Target.getHwModes(); - EncodingInfoByHwMode const EBM(DI->getDef(), HWM); + EncodingInfoByHwMode EBM(DI->getDef(), HWM); for (auto &KV : EBM) HwModeNames.insert(HWM.getMode(KV.first).Name); } @@ -1906,18 +2658,28 @@ void DecoderEmitter::retrieveHwModeEncodings() { NumberedEncodings.emplace_back(NumberedInstruction->TheDef, NumberedInstruction, HwModeName); } -} + for (const auto &NumberedAlias : RK.getAllDerivedDefinitions("AdditionalEncoding")) + NumberedEncodings.emplace_back( + NumberedAlias, + &Target.getInstruction(NumberedAlias->getValueAsDef("AliasOf"))); -/// Fills the Opcode Map with the encodings of the different decoder spaces. -/// It returns the maximum length of all variable length instructions. -/// Or 0 if no variable length is in the set (\p IsVarLenInst = false) -unsigned DecoderEmitter::fillOpcMap(bool const IsVarLenInst) { + std::map, std::vector> + OpcMap; + std::map> Operands; + std::vector InstrLen; + + bool IsVarLenInst = + any_of(NumberedInstructions, [](const CodeGenInstruction *CGI) { + RecordVal *RV = CGI->TheDef->getValue("Inst"); + return RV && isa(RV->getValue()); + }); unsigned MaxInstLen = 0; - for (unsigned I = 0; I < NumberedEncodings.size(); ++I) { - const Record *EncodingDef = NumberedEncodings[I].EncodingDef; - const CodeGenInstruction *Inst = NumberedEncodings[I].Inst; + + for (unsigned i = 0; i < NumberedEncodings.size(); ++i) { + const Record *EncodingDef = NumberedEncodings[i].EncodingDef; + const CodeGenInstruction *Inst = NumberedEncodings[i].Inst; const Record *Def = Inst->TheDef; - unsigned const Size = EncodingDef->getValueAsInt("Size"); + unsigned Size = EncodingDef->getValueAsInt("Size"); if (Def->getValueAsString("Namespace") == "TargetOpcode" || Def->getValueAsBit("isPseudo") || Def->getValueAsBit("isAsmParserOnly") || @@ -1926,7 +2688,7 @@ unsigned DecoderEmitter::fillOpcMap(bool const IsVarLenInst) { continue; } - if (I < NumberedInstructions.size()) + if (i < NumberedInstructions.size()) NumInstructions++; NumEncodings++; @@ -1936,64 +2698,31 @@ unsigned DecoderEmitter::fillOpcMap(bool const IsVarLenInst) { if (IsVarLenInst) InstrLen.resize(NumberedInstructions.size(), 0); - if (unsigned const Len = populateInstruction(Target, *EncodingDef, *Inst, I, - Operands, IsVarLenInst)) { + if (unsigned Len = populateInstruction(Target, *EncodingDef, *Inst, i, + Operands, IsVarLenInst)) { if (IsVarLenInst) { MaxInstLen = std::max(MaxInstLen, Len); - InstrLen[I] = Len; + InstrLen[i] = Len; } std::string DecoderNamespace = std::string(EncodingDef->getValueAsString("DecoderNamespace")); - if (!NumberedEncodings[I].HwModeName.empty()) + if (!NumberedEncodings[i].HwModeName.empty()) DecoderNamespace += - std::string("_") + NumberedEncodings[I].HwModeName.str(); + std::string("_") + NumberedEncodings[i].HwModeName.str(); OpcMap[std::make_pair(DecoderNamespace, Size)].emplace_back( - I, IndexOfInstruction.find(Def)->second); + i, IndexOfInstruction.find(Def)->second); } else { NumEncodingsOmitted++; } } - return MaxInstLen; -} - -// Emits disassembler code for instruction decoding. -void DecoderEmitter::run() { - PI.decoderEmitterEmitSourceFileHeader(); - PI.decoderEmitterEmitIncludes(); - PI.emitNamespace("llvm", true); - PI.decoderEmitterEmitFieldFromInstruction(); - PI.decoderEmitterEmitInsertBits(); - - Target.reverseBitsForLittleEndianEncoding(); - - // Parameterize the decoders based on namespace and instruction width. - NumberedInstructions = Target.getInstructionsByEnumValue(); - NumberedEncodings.reserve(NumberedInstructions.size()); - - retrieveHwModeEncodings(); - - for (const auto &NumberedAlias : - RK.getAllDerivedDefinitions("AdditionalEncoding")) - NumberedEncodings.emplace_back( - NumberedAlias, - &Target.getInstruction(NumberedAlias->getValueAsDef("AliasOf"))); - - bool const IsVarLenInst = - any_of(NumberedInstructions, [](const CodeGenInstruction *CGI) { - RecordVal *RV = CGI->TheDef->getValue("Inst"); - return RV && isa(RV->getValue()); - }); - - unsigned const MaxInstLen = fillOpcMap(IsVarLenInst); - // Build the state machine for instruction decoding. DecoderTableInfo TableInfo; for (const auto &Opc : OpcMap) { // Emit the decoder for this namespace+width combination. - ArrayRef const NumberedEncodingsRef( + ArrayRef NumberedEncodingsRef( NumberedEncodings.data(), NumberedEncodings.size()); - FilterChooser const FC(PI, NumberedEncodingsRef, Opc.second, Operands, - IsVarLenInst ? MaxInstLen : 8 * Opc.first.second); + FilterChooser FC(NumberedEncodingsRef, Opc.second, Operands, + IsVarLenInst ? MaxInstLen : 8 * Opc.first.second, this); // The decode table is cleared for each top level decoder function. The // predicates and decoders themselves, however, are shared across all @@ -2014,115 +2743,31 @@ void DecoderEmitter::run() { TableInfo.Table.push_back(MCD::OPC_Fail); // Print the table to the output stream. - PI.decoderEmitterEmitTable(TableInfo.Table, FC.getBitWidth(), Opc.first.first, - NumberedEncodings); - PI.flushOS(); + emitTable(OS, TableInfo.Table, 0, FC.getBitWidth(), Opc.first.first); } // For variable instruction, we emit a instruction length table // to let the decoder know how long the instructions are. // You can see example usage in M68k's disassembler. if (IsVarLenInst) - PI.decoderEmitterEmitInstrLenTable(InstrLen); + emitInstrLenTable(OS, InstrLen); // Emit the predicate function. - PI.decoderEmitterEmitPredicateFunction(TableInfo.Predicates, 0); + emitPredicateFunction(OS, TableInfo.Predicates, 0); // Emit the decoder function. - PI.decoderEmitterEmitDecoderFunction(TableInfo.Decoders, 0); + emitDecoderFunction(OS, TableInfo.Decoders, 0); // Emit the main entry point for the decoder, decodeInstruction(). - PI.decoderEmitterEmitDecodeInstruction(IsVarLenInst); - PI.emitNamespace("llvm", false); -} - -static void setPrinterParameters(CodeGenTarget &Target, PrinterLanguage PL, - std::string &PredicateNamespace, - std::string &GPrefix, std::string &GPostfix, - std::string &ROK, std::string &RFail, - std::string &L) { - // ARM and Thumb have a CHECK() macro to deal with DecodeStatuses. - if (Target.getName() == "ARM" || Target.getName() == "Thumb" || - Target.getName() == "AArch64" || Target.getName() == "ARM64") { - PredicateNamespace = std::string(Target.getName()); - - if (PredicateNamespace == "Thumb") - PredicateNamespace = "ARM"; - - switch (PL) { - default: - PrintFatalNote("DecoderEmitter does not support the given output language."); - case llvm::PRINTER_LANG_CPP: - GPrefix = "if (!Check(S, "; - L = " MCDisassembler::DecodeStatus S = " - "MCDisassembler::Success;\n(void)S;"; - break; - case llvm::PRINTER_LANG_CAPSTONE_C: - GPrefix = "if (!Check(&S, "; - L = " MCDisassembler_DecodeStatus S = " - "MCDisassembler_Success;\n(void)S;"; - break; - } - GPostfix = "))"; - } else { - PredicateNamespace = Target.getName().str(); - GPrefix = "if ("; - L = ""; - - switch (PL) { - default: - PrintFatalNote("DecoderEmitter does not support the given output language."); - case llvm::PRINTER_LANG_CPP: - GPostfix = " == MCDisassembler::Fail)"; - break; - case llvm::PRINTER_LANG_CAPSTONE_C: - GPostfix = " == MCDisassembler_Fail)"; - break; - } - } - - ROK = "S"; + emitDecodeInstruction(OS, IsVarLenInst); - switch (PL) { - default: - PrintFatalNote("DecoderEmitter does not support the given output language."); - case llvm::PRINTER_LANG_CPP: - RFail = "MCDisassembler::Fail"; - break; - case llvm::PRINTER_LANG_CAPSTONE_C: - RFail = "MCDisassembler_Fail"; - break; - } + OS << "\n} // end namespace llvm\n"; } namespace llvm { -void EmitDecoder(RecordKeeper &RK, raw_ostream &OS, CodeGenTarget &Target) { - formatted_raw_ostream FOS(OS); - PrinterLLVM *PI; - std::string PredicateNamespace; - std::string GPrefix; - std::string GPostfix; - std::string ROK; - std::string RFail; - std::string L; - PrinterLanguage const PL = PrinterLLVM::getLanguage(); - - setPrinterParameters(Target, PL, PredicateNamespace, GPrefix, GPostfix, - ROK, RFail, L); - - if (PL == PRINTER_LANG_CPP) { - PI = new PrinterLLVM(FOS, PredicateNamespace, - GPrefix, GPostfix, ROK, RFail, - L, Target.getName().str()); - } else if (PL == PRINTER_LANG_CAPSTONE_C) { - PI = new PrinterCapstone(FOS, PredicateNamespace, - GPrefix, GPostfix, ROK, RFail, - L, Target.getName().str()); - } else { - PrintFatalNote("DecoderEmitter does not support the given output language."); - } - DecoderEmitter(RK, *PI).run(); - delete PI; +void EmitDecoder(RecordKeeper &RK, raw_ostream &OS, + const std::string &PredicateNamespace) { + DecoderEmitter(RK, PredicateNamespace).run(OS); } } // end namespace llvm diff --git a/llvm/utils/TableGen/DisassemblerEmitter.cpp b/llvm/utils/TableGen/DisassemblerEmitter.cpp index 789ca19ccb57..dfa4b30ee569 100644 --- a/llvm/utils/TableGen/DisassemblerEmitter.cpp +++ b/llvm/utils/TableGen/DisassemblerEmitter.cpp @@ -7,7 +7,6 @@ //===----------------------------------------------------------------------===// #include "CodeGenTarget.h" -#include "Printer.h" #include "WebAssemblyDisassemblerEmitter.h" #include "X86DisassemblerTables.h" #include "X86RecognizableInstr.h" @@ -96,18 +95,19 @@ using namespace llvm::X86Disassembler; namespace llvm { -void EmitDecoder(RecordKeeper &RK, raw_ostream &OS, CodeGenTarget &Target); +extern void EmitDecoder(RecordKeeper &RK, raw_ostream &OS, + const std::string &PredicateNamespace); void EmitDisassembler(RecordKeeper &Records, raw_ostream &OS) { CodeGenTarget Target(Records); + emitSourceFileHeader(" * " + Target.getName().str() + " Disassembler", OS); // X86 uses a custom disassembler. if (Target.getName() == "X86") { - emitSourceFileHeader(" * " + Target.getName().str() + " Disassembler", OS); DisassemblerTables Tables; - ArrayRef numberedInstructions = - Target.getInstructionsByEnumValue(); + ArrayRef numberedInstructions = + Target.getInstructionsByEnumValue(); for (unsigned i = 0, e = numberedInstructions.size(); i != e; ++i) RecognizableInstr::processInstr(Tables, *numberedInstructions[i], i); @@ -125,11 +125,14 @@ void EmitDisassembler(RecordKeeper &Records, raw_ostream &OS) { // below (which depends on a Size table-gen Record), and also uses a custom // disassembler. if (Target.getName() == "WebAssembly") { - emitSourceFileHeader(" * " + Target.getName().str() + " Disassembler", OS); emitWebAssemblyDisassemblerTables(OS, Target.getInstructionsByEnumValue()); return; } - EmitDecoder(Records, OS, Target); + + std::string PredicateNamespace = std::string(Target.getName()); + if (PredicateNamespace == "Thumb") + PredicateNamespace = "ARM"; + EmitDecoder(Records, OS, PredicateNamespace); } } // end namespace llvm diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp index afa8d8eed004..564c3ed64e26 100644 --- a/llvm/utils/TableGen/InstrInfoEmitter.cpp +++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp @@ -11,8 +11,30 @@ // //===----------------------------------------------------------------------===// -#include "InstrInfoEmitterTypes.h" -#include "Printer.h" +#include "CodeGenDAGPatterns.h" +#include "CodeGenInstruction.h" +#include "CodeGenSchedule.h" +#include "CodeGenTarget.h" +#include "PredicateExpander.h" +#include "SequenceToOffsetTable.h" +#include "SubtargetFeatureInfo.h" +#include "TableGenBackends.h" +#include "Types.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include +#include +#include +#include +#include +#include using namespace llvm; @@ -26,63 +48,80 @@ namespace { class InstrInfoEmitter { RecordKeeper &Records; - PrinterLLVM &PI; CodeGenDAGPatterns CDP; const CodeGenSchedModels &SchedModels; public: - InstrInfoEmitter(RecordKeeper &R, PrinterLLVM &PI): - Records(R), PI(PI), CDP(R), SchedModels(CDP.getTargetInfo().getSchedModels()) {} + InstrInfoEmitter(RecordKeeper &R): + Records(R), CDP(R), SchedModels(CDP.getTargetInfo().getSchedModels()) {} // run - Output the instruction set description. - void run(); + void run(raw_ostream &OS); private: - void emitEnums(); + void emitEnums(raw_ostream &OS); + + typedef std::map, unsigned> OperandInfoMapTy; + + /// The keys of this map are maps which have OpName enum values as their keys + /// and instruction operand indices as their values. The values of this map + /// are lists of instruction names. + typedef std::map, + std::vector> OpNameMapTy; + typedef std::map::iterator StrUintMapIter; /// Generate member functions in the target-specific GenInstrInfo class. /// /// This method is used to custom expand TIIPredicate definitions. /// See file llvm/Target/TargetInstPredicates.td for a description of what is /// a TIIPredicate and how to use it. - void emitTIIHelperMethods(StringRef TargetName, + void emitTIIHelperMethods(raw_ostream &OS, StringRef TargetName, bool ExpandDefinition = true); /// Expand TIIPredicate definitions to functions that accept a const MCInst /// reference. - void emitMCIIHelperMethods(StringRef TargetName); + void emitMCIIHelperMethods(raw_ostream &OS, StringRef TargetName); /// Write verifyInstructionPredicates methods. - void emitFeatureVerifier(const CodeGenTarget &Target); + void emitFeatureVerifier(raw_ostream &OS, const CodeGenTarget &Target); void emitRecord(const CodeGenInstruction &Inst, unsigned Num, Record *InstrInfo, std::map, unsigned> &EL, - const OperandInfoMapTy &OpInf); + const OperandInfoMapTy &OpInfo, + raw_ostream &OS); void emitOperandTypeMappings( - const CodeGenTarget &Target, + raw_ostream &OS, const CodeGenTarget &Target, ArrayRef NumberedInstructions); void initOperandMapData( ArrayRef NumberedInstructions, StringRef Namespace, std::map &Operands, OpNameMapTy &OperandMap); - void emitOperandNameMappings(const CodeGenTarget &Target, + void emitOperandNameMappings(raw_ostream &OS, const CodeGenTarget &Target, ArrayRef NumberedInstructions); void emitLogicalOperandSizeMappings( - StringRef Namespace, + raw_ostream &OS, StringRef Namespace, ArrayRef NumberedInstructions); void emitLogicalOperandTypeMappings( - StringRef Namespace, + raw_ostream &OS, StringRef Namespace, ArrayRef NumberedInstructions); // Operand information. - void EmitOperandInfo(OperandInfoMapTy &OperandInfoIDs); + void EmitOperandInfo(raw_ostream &OS, OperandInfoMapTy &OperandInfoIDs); std::vector GetOperandInfo(const CodeGenInstruction &Inst); }; } // end anonymous namespace +static void PrintDefList(const std::vector &Uses, + unsigned Num, raw_ostream &OS) { + OS << "static const MCPhysReg ImplicitList" << Num << "[] = { "; + for (auto [Idx, U] : enumerate(Uses)) + OS << (Idx ? ", " : "") << getQualifiedName(U); + OS << " };\n"; +} + //===----------------------------------------------------------------------===// // Operand Info Emission. //===----------------------------------------------------------------------===// @@ -118,7 +157,57 @@ InstrInfoEmitter::GetOperandInfo(const CodeGenInstruction &Inst) { Record *OpR = OperandList[j].Rec; std::string Res; - PI.instrInfoSetOperandInfoStr(Res, OpR, Op, Op.Constraints[j]); + if (OpR->isSubClassOf("RegisterOperand")) + OpR = OpR->getValueAsDef("RegClass"); + if (OpR->isSubClassOf("RegisterClass")) + Res += getQualifiedName(OpR) + "RegClassID, "; + else if (OpR->isSubClassOf("PointerLikeRegClass")) + Res += utostr(OpR->getValueAsInt("RegClassKind")) + ", "; + else + // -1 means the operand does not have a fixed register class. + Res += "-1, "; + + // Fill in applicable flags. + Res += "0"; + + // Ptr value whose register class is resolved via callback. + if (OpR->isSubClassOf("PointerLikeRegClass")) + Res += "|(1<isSubClassOf("PredicateOp")) + Res += "|(1<isSubClassOf("OptionalDefOperand")) + Res += "|(1<isSubClassOf("BranchTargetOperand")) + Res += "|(1<()] = ++OperandListNum; - PI.emitString("\n"); + OS << "\n"; const CodeGenTarget &Target = CDP.getTargetInfo(); for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { std::vector OperandInfo = GetOperandInfo(*Inst); @@ -140,7 +229,10 @@ void InstrInfoEmitter::EmitOperandInfo( if (N != 0) continue; N = ++OperandListNum; - PI.instrInfoEmitOperandInfoTable(OperandInfo, N); + OS << "static const MCOperandInfo OperandInfo" << N << "[] = { "; + for (const std::string &Info : OperandInfo) + OS << "{ " << Info << " }, "; + OS << "};\n"; } } @@ -170,8 +262,8 @@ void InstrInfoEmitter::initOperandMapData( } OpList[I->second] = Info.MIOperandNo; } - OperandMap[OpList].push_back( - PI.instrInfoGetInstMapEntry(Namespace, Inst->TheDef->getName())); + OperandMap[OpList].push_back(Namespace.str() + "::" + + Inst->TheDef->getName().str()); } } @@ -186,7 +278,7 @@ void InstrInfoEmitter::initOperandMapData( /// - A function called getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx) /// for looking up the operand index for an instruction, given a value from /// OpName enum -void InstrInfoEmitter::emitOperandNameMappings( +void InstrInfoEmitter::emitOperandNameMappings(raw_ostream &OS, const CodeGenTarget &Target, ArrayRef NumberedInstructions) { StringRef Namespace = Target.getInstNamespace(); @@ -198,30 +290,68 @@ void InstrInfoEmitter::emitOperandNameMappings( initOperandMapData(NumberedInstructions, Namespace, Operands, OperandMap); - PI.emitIncludeToggle("GET_INSTRINFO_OPERAND_ENUM", true); - PI.emitNamespace("llvm", true); - PI.emitNamespace(Namespace.str(), true); - PI.emitNamespace(OpNameNS, true); - PI.instrInfoEmitOperandEnum(Operands); - PI.emitNamespace(OpNameNS, false); - PI.emitNamespace(Namespace.str(), false); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_INSTRINFO_OPERAND_ENUM", false); - - PI.emitIncludeToggle("GET_INSTRINFO_NAMED_OPS", true); - PI.emitNamespace("llvm", true); - PI.emitNamespace(Namespace.str(), true); - PI.instrInfoEmitGetNamedOperandIdx(Operands, OperandMap); - PI.emitNamespace(Namespace.str(), false); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_INSTRINFO_NAMED_OPS", false); + OS << "#ifdef GET_INSTRINFO_OPERAND_ENUM\n"; + OS << "#undef GET_INSTRINFO_OPERAND_ENUM\n"; + OS << "namespace llvm {\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "namespace " << OpNameNS << " {\n"; + OS << "enum {\n"; + for (const auto &Op : Operands) + OS << " " << Op.first << " = " << Op.second << ",\n"; + + OS << " OPERAND_LAST"; + OS << "\n};\n"; + OS << "} // end namespace OpName\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif //GET_INSTRINFO_OPERAND_ENUM\n\n"; + + OS << "#ifdef GET_INSTRINFO_NAMED_OPS\n"; + OS << "#undef GET_INSTRINFO_NAMED_OPS\n"; + OS << "namespace llvm {\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "LLVM_READONLY\n"; + OS << "int16_t getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx) {\n"; + if (!Operands.empty()) { + OS << " static const int16_t OperandMap [][" << Operands.size() + << "] = {\n"; + for (const auto &Entry : OperandMap) { + const std::map &OpList = Entry.first; + OS << "{"; + + // Emit a row of the OperandMap table + for (unsigned i = 0, e = Operands.size(); i != e; ++i) + OS << (OpList.count(i) == 0 ? -1 : (int)OpList.find(i)->second) << ", "; + + OS << "},\n"; + } + OS << "};\n"; + + OS << " switch(Opcode) {\n"; + unsigned TableIndex = 0; + for (const auto &Entry : OperandMap) { + for (const std::string &Name : Entry.second) + OS << " case " << Name << ":\n"; + + OS << " return OperandMap[" << TableIndex++ << "][NamedIdx];\n"; + } + OS << " default: return -1;\n"; + OS << " }\n"; + } else { + // There are no operands, so no need to emit anything + OS << " return -1;\n"; + } + OS << "}\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif //GET_INSTRINFO_NAMED_OPS\n\n"; } /// Generate an enum for all the operand types for this target, under the /// llvm::TargetNamespace::OpTypes namespace. /// Operand types are all definitions derived of the Operand Target.td class. void InstrInfoEmitter::emitOperandTypeMappings( - const CodeGenTarget &Target, + raw_ostream &OS, const CodeGenTarget &Target, ArrayRef NumberedInstructions) { StringRef Namespace = Target.getInstNamespace(); @@ -231,33 +361,38 @@ void InstrInfoEmitter::emitOperandTypeMappings( std::vector RegisterClasses = Records.getAllDerivedDefinitions("RegisterClass"); - PI.emitIncludeToggle("GET_INSTRINFO_OPERAND_TYPES_ENUM", true); - PI.emitNamespace("llvm", true); - PI.emitNamespace(Namespace.str(), true); - PI.emitNamespace("OpTypes", true); - PI.instrInfoEmitOpTypeEnumPartI(); + OS << "#ifdef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; + OS << "#undef GET_INSTRINFO_OPERAND_TYPES_ENUM\n"; + OS << "namespace llvm {\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "namespace OpTypes {\n"; + OS << "enum OperandType {\n"; unsigned EnumVal = 0; for (const std::vector *RecordsToAdd : {&Operands, &RegisterOperands, &RegisterClasses}) { for (const Record *Op : *RecordsToAdd) { if (!Op->isAnonymous()) - PI.instrInfoEmitOpTypeEnumPartII(Op->getName(), EnumVal); + OS << " " << Op->getName() << " = " << EnumVal << ",\n"; ++EnumVal; } } - PI.instrInfoEmitOpTypeEnumPartIII(); - PI.emitNamespace("OpTypes", false); - PI.emitNamespace(Namespace.str(), false); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_INSTRINFO_OPERAND_TYPES_ENUM", false); - - PI.emitIncludeToggle("GET_INSTRINFO_OPERAND_TYPE", true); - PI.emitNamespace("llvm", true); - PI.emitNamespace(Namespace.str(), true); - PI.instrInfoEmitGetOpTypeHdr(); - + OS << " OPERAND_TYPE_LIST_END" << "\n};\n"; + OS << "} // end namespace OpTypes\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif // GET_INSTRINFO_OPERAND_TYPES_ENUM\n\n"; + + OS << "#ifdef GET_INSTRINFO_OPERAND_TYPE\n"; + OS << "#undef GET_INSTRINFO_OPERAND_TYPE\n"; + OS << "namespace llvm {\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "LLVM_READONLY\n"; + OS << "static int getOperandType(uint16_t Opcode, uint16_t OpIdx) {\n"; + auto getInstrName = [&](int I) -> StringRef { + return NumberedInstructions[I]->TheDef->getName(); + }; // TODO: Factor out duplicate operand lists to compress the tables. if (!NumberedInstructions.empty()) { std::vector OperandOffsets; @@ -284,7 +419,14 @@ void InstrInfoEmitter::emitOperandTypeMappings( // Size the unsigned integer offset to save space. assert(OperandRecords.size() <= UINT32_MAX && "Too many operands for offset table"); - PI.instrInfoEmitOpTypeOffsetTable(OperandOffsets, OperandRecords.size(), NumberedInstructions); + OS << ((OperandRecords.size() <= UINT16_MAX) ? " const uint16_t" + : " const uint32_t"); + OS << " Offsets[] = {\n"; + for (int I = 0, E = OperandOffsets.size(); I != E; ++I) { + OS << " /* " << getInstrName(I) << " */\n"; + OS << " " << OperandOffsets[I] << ",\n"; + } + OS << " };\n"; // Add an entry for the end so that we don't need to special case it below. OperandOffsets.push_back(OperandRecords.size()); @@ -293,23 +435,44 @@ void InstrInfoEmitter::emitOperandTypeMappings( // Size the signed integer operand type to save space. assert(EnumVal <= INT16_MAX && "Too many operand types for operand types table"); - PI.instrInfoEmitOpcodeOpTypesTable(EnumVal, - OperandRecords, - OperandOffsets, - NumberedInstructions); - PI.instrInfoEmitGetOpTypeReturn(); + OS << "\n using namespace OpTypes;\n"; + OS << ((EnumVal <= INT8_MAX) ? " const int8_t" : " const int16_t"); + OS << " OpcodeOperandTypes[] = {\n "; + for (int I = 0, E = OperandRecords.size(), CurOffset = 0; I != E; ++I) { + // We print each Opcode's operands in its own row. + if (I == OperandOffsets[CurOffset]) { + OS << "\n /* " << getInstrName(CurOffset) << " */\n "; + while (OperandOffsets[++CurOffset] == I) + OS << "/* " << getInstrName(CurOffset) << " */\n "; + } + Record *OpR = OperandRecords[I]; + if ((OpR->isSubClassOf("Operand") || + OpR->isSubClassOf("RegisterOperand") || + OpR->isSubClassOf("RegisterClass")) && + !OpR->isAnonymous()) + OS << OpR->getName(); + else + OS << -1; + OS << ", "; + } + OS << "\n };\n"; + + OS << " return OpcodeOperandTypes[Offsets[Opcode] + OpIdx];\n"; } else { - PI.instrInfoEmitGetOpTypeUnreachable(); + OS << " llvm_unreachable(\"No instructions defined\");\n"; } - PI.instrInfoEmitGetOpTypeEnd(); - PI.emitNamespace(Namespace.str(), false); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_INSTRINFO_OPERAND_TYPE", false); - - PI.emitIncludeToggle("GET_INSTRINFO_MEM_OPERAND_SIZE", true); - PI.emitNamespace("llvm", true); - PI.emitNamespace(Namespace.str(), true); - PI.instrInfoEmitGetMemOpSizeHdr(); + OS << "}\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif // GET_INSTRINFO_OPERAND_TYPE\n\n"; + + OS << "#ifdef GET_INSTRINFO_MEM_OPERAND_SIZE\n"; + OS << "#undef GET_INSTRINFO_MEM_OPERAND_SIZE\n"; + OS << "namespace llvm {\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "LLVM_READONLY\n"; + OS << "static int getMemOperandSize(int OpType) {\n"; + OS << " switch (OpType) {\n"; std::map> SizeToOperandName; for (const Record *Op : Operands) { if (!Op->isSubClassOf("X86MemOperand")) @@ -317,14 +480,20 @@ void InstrInfoEmitter::emitOperandTypeMappings( if (int Size = Op->getValueAsInt("Size")) SizeToOperandName[Size].push_back(Op->getName()); } - PI.instrInfoEmitGetOpMemSizeTbl(SizeToOperandName); - PI.emitNamespace(Namespace.str(), false); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_INSTRINFO_MEM_OPERAND_SIZE", false); + OS << " default: return 0;\n"; + for (auto KV : SizeToOperandName) { + for (const StringRef &OperandName : KV.second) + OS << " case OpTypes::" << OperandName << ":\n"; + OS << " return " << KV.first << ";\n\n"; + } + OS << " }\n}\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif // GET_INSTRINFO_MEM_OPERAND_SIZE\n\n"; } void InstrInfoEmitter::emitLogicalOperandSizeMappings( - StringRef Namespace, + raw_ostream &OS, StringRef Namespace, ArrayRef NumberedInstructions) { std::map, unsigned> LogicalOpSizeMap; @@ -349,36 +518,69 @@ void InstrInfoEmitter::emitLogicalOperandSizeMappings( auto I = LogicalOpSizeMap.insert({LogicalOpList, LogicalOpSizeMap.size()}).first; InstMap[I->second].push_back( - PI.instrInfoGetInstMapEntry(Namespace, Inst->TheDef->getName())); + (Namespace + "::" + Inst->TheDef->getName()).str()); } - PI.emitIncludeToggle("GET_INSTRINFO_LOGICAL_OPERAND_SIZE_MAP", true); - PI.emitNamespace("llvm", true); - PI.emitNamespace(Namespace.str(), true); - PI.instrInfoEmitGetLogicalOpSizeHdr(); + OS << "#ifdef GET_INSTRINFO_LOGICAL_OPERAND_SIZE_MAP\n"; + OS << "#undef GET_INSTRINFO_LOGICAL_OPERAND_SIZE_MAP\n"; + OS << "namespace llvm {\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "LLVM_READONLY static unsigned\n"; + OS << "getLogicalOperandSize(uint16_t Opcode, uint16_t LogicalOpIdx) {\n"; if (!InstMap.empty()) { std::vector *> LogicalOpSizeList( LogicalOpSizeMap.size()); for (auto &P : LogicalOpSizeMap) { LogicalOpSizeList[P.second] = &P.first; } - PI.instrInfoEmitGetLogicalOpSizeTable(LogicalOpListSize, LogicalOpSizeList); - - PI.instrInfoEmitGetLogicalOpSizeSwitch(InstMap); + OS << " static const unsigned SizeMap[][" << LogicalOpListSize + << "] = {\n"; + for (auto &R : LogicalOpSizeList) { + const auto &Row = *R; + OS << " {"; + int i; + for (i = 0; i < static_cast(Row.size()); ++i) { + OS << Row[i] << ", "; + } + for (; i < static_cast(LogicalOpListSize); ++i) { + OS << "0, "; + } + OS << "}, "; + OS << "\n"; + } + OS << " };\n"; + + OS << " switch (Opcode) {\n"; + OS << " default: return LogicalOpIdx;\n"; + for (auto &P : InstMap) { + auto OpMapIdx = P.first; + const auto &Insts = P.second; + for (const auto &Inst : Insts) { + OS << " case " << Inst << ":\n"; + } + OS << " return SizeMap[" << OpMapIdx << "][LogicalOpIdx];\n"; + } + OS << " }\n"; } else { - PI.instrInfoEmitGetLogicalOpSizeReturn(); + OS << " return LogicalOpIdx;\n"; } - PI.instrInfoEmitGetLogicalOpSizeEnd(); - - PI.instrInfoEmitGetLogicalOpIdx(); - - PI.emitNamespace(Namespace.str(), false); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_INSTRINFO_LOGICAL_OPERAND_SIZE_MAP", false); + OS << "}\n"; + + OS << "LLVM_READONLY static inline unsigned\n"; + OS << "getLogicalOperandIdx(uint16_t Opcode, uint16_t LogicalOpIdx) {\n"; + OS << " auto S = 0U;\n"; + OS << " for (auto i = 0U; i < LogicalOpIdx; ++i)\n"; + OS << " S += getLogicalOperandSize(Opcode, i);\n"; + OS << " return S;\n"; + OS << "}\n"; + + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif // GET_INSTRINFO_LOGICAL_OPERAND_SIZE_MAP\n\n"; } void InstrInfoEmitter::emitLogicalOperandTypeMappings( - StringRef Namespace, + raw_ostream &OS, StringRef Namespace, ArrayRef NumberedInstructions) { std::map, unsigned> LogicalOpTypeMap; @@ -398,7 +600,7 @@ void InstrInfoEmitter::emitLogicalOperandTypeMappings( OpR->isSubClassOf("RegisterClass")) && !OpR->isAnonymous()) { LogicalOpTypeList.push_back( - PI.instrInfoGetOpTypeListEntry(Namespace, Op.Rec->getName())); + (Namespace + "::OpTypes::" + Op.Rec->getName()).str()); } else { LogicalOpTypeList.push_back("-1"); } @@ -409,82 +611,146 @@ void InstrInfoEmitter::emitLogicalOperandTypeMappings( LogicalOpTypeMap.insert({LogicalOpTypeList, LogicalOpTypeMap.size()}) .first; InstMap[I->second].push_back( - PI.instrInfoGetInstMapEntry(Namespace, Inst->TheDef->getName())); + (Namespace + "::" + Inst->TheDef->getName()).str()); } - PI.emitIncludeToggle("GET_INSTRINFO_LOGICAL_OPERAND_TYPE_MAP", true); - PI.emitNamespace("llvm", true); - PI.emitNamespace(Namespace.str(), true); - PI.instrInfoEmitGetLogicalOpTypeHdr(); + OS << "#ifdef GET_INSTRINFO_LOGICAL_OPERAND_TYPE_MAP\n"; + OS << "#undef GET_INSTRINFO_LOGICAL_OPERAND_TYPE_MAP\n"; + OS << "namespace llvm {\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "LLVM_READONLY static int\n"; + OS << "getLogicalOperandType(uint16_t Opcode, uint16_t LogicalOpIdx) {\n"; if (!InstMap.empty()) { std::vector *> LogicalOpTypeList( LogicalOpTypeMap.size()); for (auto &P : LogicalOpTypeMap) { LogicalOpTypeList[P.second] = &P.first; } - PI.instrInfoEmitGetLogicalOpTypeTable(OpTypeListSize, - LogicalOpTypeList); - PI.instrInfoEmitGetLogicalOpTypeSwitch(InstMap); + OS << " static const int TypeMap[][" << OpTypeListSize << "] = {\n"; + for (int r = 0, rs = LogicalOpTypeList.size(); r < rs; ++r) { + const auto &Row = *LogicalOpTypeList[r]; + OS << " {"; + int i, s = Row.size(); + for (i = 0; i < s; ++i) { + if (i > 0) + OS << ", "; + OS << Row[i]; + } + for (; i < static_cast(OpTypeListSize); ++i) { + if (i > 0) + OS << ", "; + OS << "-1"; + } + OS << "}"; + if (r != rs - 1) + OS << ","; + OS << "\n"; + } + OS << " };\n"; + + OS << " switch (Opcode) {\n"; + OS << " default: return -1;\n"; + for (auto &P : InstMap) { + auto OpMapIdx = P.first; + const auto &Insts = P.second; + for (const auto &Inst : Insts) { + OS << " case " << Inst << ":\n"; + } + OS << " return TypeMap[" << OpMapIdx << "][LogicalOpIdx];\n"; + } + OS << " }\n"; } else { - PI.instrInfoEmitGetLogicalOpTypeReturn(); + OS << " return -1;\n"; } - PI.instrInfoEmitGetLogicalOpTypeEnd(); - PI.emitNamespace(Namespace.str(), false); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_INSTRINFO_LOGICAL_OPERAND_TYPE_MAP", false); + OS << "}\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif // GET_INSTRINFO_LOGICAL_OPERAND_TYPE_MAP\n\n"; } -void InstrInfoEmitter::emitMCIIHelperMethods( +void InstrInfoEmitter::emitMCIIHelperMethods(raw_ostream &OS, StringRef TargetName) { RecVec TIIPredicates = Records.getAllDerivedDefinitions("TIIPredicate"); - PI.emitIncludeToggle("GET_INSTRINFO_MC_HELPER_DECLS", true); - PI.emitNamespace("llvm", true); - PI.instrInfoEmitDeclareMCInstFeatureClasses(); + OS << "#ifdef GET_INSTRINFO_MC_HELPER_DECLS\n"; + OS << "#undef GET_INSTRINFO_MC_HELPER_DECLS\n\n"; + + OS << "namespace llvm {\n"; + OS << "class MCInst;\n"; + OS << "class FeatureBitset;\n\n"; + + OS << "namespace " << TargetName << "_MC {\n\n"; - PI.emitNamespace(TargetName.str() + "_MC", true); + for (const Record *Rec : TIIPredicates) { + OS << "bool " << Rec->getValueAsString("FunctionName") + << "(const MCInst &MI);\n"; + } + + OS << "void verifyInstructionPredicates(unsigned Opcode, const FeatureBitset " + "&Features);\n"; + + OS << "\n} // end namespace " << TargetName << "_MC\n"; + OS << "} // end namespace llvm\n\n"; - PI.instrInfoEmitPredFcnDecl(TIIPredicates); + OS << "#endif // GET_INSTRINFO_MC_HELPER_DECLS\n\n"; - PI.emitNamespace(TargetName.str() + "_MC", false); - PI.emitNamespace("llvm", false); + OS << "#ifdef GET_INSTRINFO_MC_HELPERS\n"; + OS << "#undef GET_INSTRINFO_MC_HELPERS\n\n"; - PI.emitIncludeToggle("GET_INSTRINFO_MC_HELPER_DECLS", false); + OS << "namespace llvm {\n"; + OS << "namespace " << TargetName << "_MC {\n\n"; - PI.emitIncludeToggle("GET_INSTRINFO_MC_HELPERS", true); + PredicateExpander PE(TargetName); + PE.setExpandForMC(true); - PI.emitNamespace("llvm", true); - PI.emitNamespace(TargetName.str() + "_MC", true); + for (const Record *Rec : TIIPredicates) { + OS << "bool " << Rec->getValueAsString("FunctionName"); + OS << "(const MCInst &MI) {\n"; + + OS.indent(PE.getIndentLevel() * 2); + PE.expandStatement(OS, Rec->getValueAsDef("Body")); + OS << "\n}\n\n"; + } - PI.instrInfoEmitPredFcnImpl(TargetName, TIIPredicates); + OS << "} // end namespace " << TargetName << "_MC\n"; + OS << "} // end namespace llvm\n\n"; - PI.emitNamespace(TargetName.str() + "_MC", false); - PI.emitNamespace("llvm", false); + OS << "#endif // GET_GENISTRINFO_MC_HELPERS\n\n"; +} - PI.emitIncludeToggle("GET_INSTRINFO_MC_HELPERS", false); +static std::string +getNameForFeatureBitset(const std::vector &FeatureBitset) { + std::string Name = "CEFBS"; + for (const auto &Feature : FeatureBitset) + Name += ("_" + Feature->getName()).str(); + return Name; } -void InstrInfoEmitter::emitFeatureVerifier( +void InstrInfoEmitter::emitFeatureVerifier(raw_ostream &OS, const CodeGenTarget &Target) { const auto &All = SubtargetFeatureInfo::getAll(Records); std::map SubtargetFeatures; SubtargetFeatures.insert(All.begin(), All.end()); - PI.emitIncludeToggle("ENABLE_INSTR_PREDICATE_VERIFIER", true); - PI.instrInfoEmitInstrPredVerifierIncludes(); + OS << "#ifdef ENABLE_INSTR_PREDICATE_VERIFIER\n" + << "#undef ENABLE_INSTR_PREDICATE_VERIFIER\n" + << "#include \n\n"; - PI.emitNamespace("llvm", true); - PI.emitNamespace(Target.getName().str() + "_MC", true); + OS << "namespace llvm {\n"; + OS << "namespace " << Target.getName() << "_MC {\n\n"; // Emit the subtarget feature enumeration. - PI.instrInfoEmitSubtargetFeatureBitEnumeration(SubtargetFeatures); + SubtargetFeatureInfo::emitSubtargetFeatureBitEnumeration(SubtargetFeatures, + OS); // Emit the name table for error messages. - PI.instrInfoEmitEmitSTFNameTable(SubtargetFeatures); + OS << "#ifndef NDEBUG\n"; + SubtargetFeatureInfo::emitNameTable(SubtargetFeatures, OS); + OS << "#endif // NDEBUG\n\n"; // Emit the available features compute function. - PI.instrInfoEmitComputeAssemblerAvailableFeatures(Target.getName(), - SubtargetFeatures); + SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures( + Target.getName(), "", "computeAvailableFeatures", SubtargetFeatures, OS); std::vector> FeatureBitsets; for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { @@ -513,28 +779,107 @@ void InstrInfoEmitter::emitFeatureVerifier( FeatureBitsets.erase( std::unique(FeatureBitsets.begin(), FeatureBitsets.end()), FeatureBitsets.end()); - PI.emitIfNotDef("NDEBUG", true); - PI.instrInfoEmitFeatureBitsEnum(FeatureBitsets); - PI.instrInfoEmitFeatureBitsArray(FeatureBitsets, SubtargetFeatures); - PI.emitIfNotDef("NDEBUG", false); + OS << "#ifndef NDEBUG\n" + << "// Feature bitsets.\n" + << "enum : " << getMinimalTypeForRange(FeatureBitsets.size()) << " {\n" + << " CEFBS_None,\n"; + for (const auto &FeatureBitset : FeatureBitsets) { + if (FeatureBitset.empty()) + continue; + OS << " " << getNameForFeatureBitset(FeatureBitset) << ",\n"; + } + OS << "};\n\n" + << "static constexpr FeatureBitset FeatureBitsets[] = {\n" + << " {}, // CEFBS_None\n"; + for (const auto &FeatureBitset : FeatureBitsets) { + if (FeatureBitset.empty()) + continue; + OS << " {"; + for (const auto &Feature : FeatureBitset) { + const auto &I = SubtargetFeatures.find(Feature); + assert(I != SubtargetFeatures.end() && "Didn't import predicate?"); + OS << I->second.getEnumBitName() << ", "; + } + OS << "},\n"; + } + OS << "};\n" + << "#endif // NDEBUG\n\n"; // Emit the predicate verifier. - PI.instrInfoEmitPredVerifier(FeatureBitsets, SubtargetFeatures, Target); - PI.emitNamespace(Target.getName().str() + "_MC", false); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("ENABLE_INSTR_PREDICATE_VERIFIER", false); + OS << "void verifyInstructionPredicates(\n" + << " unsigned Opcode, const FeatureBitset &Features) {\n" + << "#ifndef NDEBUG\n" + << " static " << getMinimalTypeForRange(FeatureBitsets.size()) + << " RequiredFeaturesRefs[] = {\n"; + unsigned InstIdx = 0; + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) { + OS << " CEFBS"; + unsigned NumPredicates = 0; + for (Record *Predicate : Inst->TheDef->getValueAsListOfDefs("Predicates")) { + const auto &I = SubtargetFeatures.find(Predicate); + if (I != SubtargetFeatures.end()) { + OS << '_' << I->second.TheDef->getName(); + NumPredicates++; + } + } + if (!NumPredicates) + OS << "_None"; + OS << ", // " << Inst->TheDef->getName() << " = " << InstIdx << "\n"; + InstIdx++; + } + OS << " };\n\n"; + OS << " assert(Opcode < " << InstIdx << ");\n"; + OS << " FeatureBitset AvailableFeatures = " + "computeAvailableFeatures(Features);\n"; + OS << " const FeatureBitset &RequiredFeatures = " + "FeatureBitsets[RequiredFeaturesRefs[Opcode]];\n"; + OS << " FeatureBitset MissingFeatures =\n" + << " (AvailableFeatures & RequiredFeatures) ^\n" + << " RequiredFeatures;\n" + << " if (MissingFeatures.any()) {\n" + << " std::ostringstream Msg;\n" + << " Msg << \"Attempting to emit \" << &" << Target.getName() + << "InstrNameData[" << Target.getName() << "InstrNameIndices[Opcode]]\n" + << " << \" instruction but the \";\n" + << " for (unsigned i = 0, e = MissingFeatures.size(); i != e; ++i)\n" + << " if (MissingFeatures.test(i))\n" + << " Msg << SubtargetFeatureNames[i] << \" \";\n" + << " Msg << \"predicate(s) are not met\";\n" + << " report_fatal_error(Msg.str().c_str());\n" + << " }\n" + << "#endif // NDEBUG\n"; + OS << "}\n"; + OS << "} // end namespace " << Target.getName() << "_MC\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif // ENABLE_INSTR_PREDICATE_VERIFIER\n\n"; } -void InstrInfoEmitter::emitTIIHelperMethods( +void InstrInfoEmitter::emitTIIHelperMethods(raw_ostream &OS, StringRef TargetName, bool ExpandDefinition) { RecVec TIIPredicates = Records.getAllDerivedDefinitions("TIIPredicate"); if (TIIPredicates.empty()) return; - PI.instrInfoEmitTIIPredicates(TargetName, - TIIPredicates, - ExpandDefinition); + PredicateExpander PE(TargetName); + PE.setExpandForMC(false); + + for (const Record *Rec : TIIPredicates) { + OS << (ExpandDefinition ? "" : "static ") << "bool "; + if (ExpandDefinition) + OS << TargetName << "InstrInfo::"; + OS << Rec->getValueAsString("FunctionName"); + OS << "(const MachineInstr &MI)"; + if (!ExpandDefinition) { + OS << ";\n"; + continue; + } + + OS << " {\n"; + OS.indent(PE.getIndentLevel() * 2); + PE.expandStatement(OS, Rec->getValueAsDef("Body")); + OS << "\n}\n\n"; + } } //===----------------------------------------------------------------------===// @@ -542,12 +887,14 @@ void InstrInfoEmitter::emitTIIHelperMethods( //===----------------------------------------------------------------------===// // run - Emit the main instruction description records for the target... -void InstrInfoEmitter::run() { - PI.instrInfoEmitSourceFileHeader(); - emitEnums(); +void InstrInfoEmitter::run(raw_ostream &OS) { + emitSourceFileHeader("Target Instruction Enum Values and Descriptors", OS); + emitEnums(OS); - PI.emitIncludeToggle("GET_INSTRINFO_MC_DESC", true); - PI.emitNamespace("llvm", true); + OS << "#ifdef GET_INSTRINFO_MC_DESC\n"; + OS << "#undef GET_INSTRINFO_MC_DESC\n"; + + OS << "namespace llvm {\n\n"; CodeGenTarget &Target = CDP.getTargetInfo(); const std::string &TargetName = std::string(Target.getName()); @@ -566,7 +913,7 @@ void InstrInfoEmitter::run() { unsigned &IL = EmittedLists[ImplicitOps]; if (!IL) { IL = ++ListNumber; - PI.instrInfoPrintDefList(ImplicitOps, IL, getQualifiedName); + PrintDefList(ImplicitOps, IL, OS); } } } @@ -575,31 +922,40 @@ void InstrInfoEmitter::run() { // Emit all of the operand info records. Records.startTimer("Emit operand info"); - EmitOperandInfo(OperandInfoIDs); + EmitOperandInfo(OS, OperandInfoIDs); - // Emit all of the MCInstrDesc records in ascending ENUM ordering. + // Emit all of the MCInstrDesc records in reverse ENUM ordering. Records.startTimer("Emit InstrDesc records"); - PI.instrInfoEmitMCInstrDescHdr(TargetName); + OS << "\nextern const MCInstrDesc " << TargetName << "Insts[] = {\n"; ArrayRef NumberedInstructions = Target.getInstructionsByEnumValue(); SequenceToOffsetTable InstrNames; - // CAPSTONE: Do not reverse NumberedInstructions. - // We access them via the opcode. They must ascend for this. - unsigned Num = 0; - for (const CodeGenInstruction *Inst : NumberedInstructions) { + unsigned Num = NumberedInstructions.size(); + for (const CodeGenInstruction *Inst : reverse(NumberedInstructions)) { // Keep a list of the instruction names. InstrNames.add(std::string(Inst->TheDef->getName())); // Emit the record into the table. - emitRecord(*Inst, Num++, InstrInfo, EmittedLists, OperandInfoIDs); + emitRecord(*Inst, --Num, InstrInfo, EmittedLists, OperandInfoIDs, OS); } - PI.instrInfoEmitMCInstrDescEnd(); + OS << "};\n\n"; + // Emit the array of instruction names. Records.startTimer("Emit instruction names"); InstrNames.layout(); - PI.instrInfoEmitStringLiteralDef(TargetName, InstrNames); + InstrNames.emitStringLiteralDef(OS, Twine("extern const char ") + TargetName + + "InstrNameData[]"); - PI.instrInfoEmitInstrNameIndices(TargetName, NumberedInstructions, InstrNames); + OS << "extern const unsigned " << TargetName <<"InstrNameIndices[] = {"; + Num = 0; + for (const CodeGenInstruction *Inst : NumberedInstructions) { + // Newline every eight entries. + if (Num % 8 == 0) + OS << "\n "; + OS << InstrNames.get(std::string(Inst->TheDef->getName())) << "U, "; + ++Num; + } + OS << "\n};\n\n"; bool HasDeprecationFeatures = llvm::any_of(NumberedInstructions, [](const CodeGenInstruction *Inst) { @@ -607,10 +963,21 @@ void InstrInfoEmitter::run() { !Inst->DeprecatedReason.empty(); }); if (HasDeprecationFeatures) { - PI.instrInfoEmitInstrDeprFeatures(TargetName, - Target.getInstNamespace().str(), - NumberedInstructions, - InstrNames); + OS << "extern const uint8_t " << TargetName + << "InstrDeprecationFeatures[] = {"; + Num = 0; + for (const CodeGenInstruction *Inst : NumberedInstructions) { + if (Num % 8 == 0) + OS << "\n "; + if (!Inst->HasComplexDeprecationPredicate && + !Inst->DeprecatedReason.empty()) + OS << Target.getInstNamespace() << "::" << Inst->DeprecatedReason + << ", "; + else + OS << "uint8_t(-1), "; + ++Num; + } + OS << "\n};\n\n"; } bool HasComplexDeprecationInfos = @@ -618,90 +985,187 @@ void InstrInfoEmitter::run() { return Inst->HasComplexDeprecationPredicate; }); if (HasComplexDeprecationInfos) { - PI.instrInfoEmitInstrComplexDeprInfos(TargetName, - NumberedInstructions); + OS << "extern const MCInstrInfo::ComplexDeprecationPredicate " << TargetName + << "InstrComplexDeprecationInfos[] = {"; + Num = 0; + for (const CodeGenInstruction *Inst : NumberedInstructions) { + if (Num % 8 == 0) + OS << "\n "; + if (Inst->HasComplexDeprecationPredicate) + // Emit a function pointer to the complex predicate method. + OS << "&get" << Inst->DeprecatedReason << "DeprecationInfo, "; + else + OS << "nullptr, "; + ++Num; + } + OS << "\n};\n\n"; } // MCInstrInfo initialization routine. Records.startTimer("Emit initialization routine"); - PI.instrInfoEmitMCInstrInfoInitRoutine(TargetName, - NumberedInstructions.size(), - HasDeprecationFeatures, - HasComplexDeprecationInfos); - - PI.emitNamespace("llvm", false); - - PI.emitIncludeToggle("GET_INSTRINFO_MC_DESC", false); + OS << "static inline void Init" << TargetName + << "MCInstrInfo(MCInstrInfo *II) {\n"; + OS << " II->InitMCInstrInfo(" << TargetName << "Insts, " << TargetName + << "InstrNameIndices, " << TargetName << "InstrNameData, "; + if (HasDeprecationFeatures) + OS << TargetName << "InstrDeprecationFeatures, "; + else + OS << "nullptr, "; + if (HasComplexDeprecationInfos) + OS << TargetName << "InstrComplexDeprecationInfos, "; + else + OS << "nullptr, "; + OS << NumberedInstructions.size() << ");\n}\n\n"; + + OS << "} // end namespace llvm\n"; + + OS << "#endif // GET_INSTRINFO_MC_DESC\n\n"; // Create a TargetInstrInfo subclass to hide the MC layer initialization. - PI.emitIncludeToggle("GET_INSTRINFO_HEADER", true); + OS << "#ifdef GET_INSTRINFO_HEADER\n"; + OS << "#undef GET_INSTRINFO_HEADER\n"; std::string ClassName = TargetName + "GenInstrInfo"; - PI.emitNamespace("llvm", true); - PI.instrInfoEmitClassStruct(ClassName); - PI.emitNamespace("llvm", false); - - PI.emitIncludeToggle("GET_INSTRINFO_HEADER", false); - - PI.emitIncludeToggle("GET_INSTRINFO_HELPER_DECLS", true); - emitTIIHelperMethods(TargetName, /* ExpandDefinition = */ false); - PI.emitString("\n"); - PI.emitIncludeToggle("GET_INSTRINFO_HELPER_DECLS", false); - - PI.emitIncludeToggle("GET_INSTRINFO_HELPERS", true); - emitTIIHelperMethods(TargetName, /* ExpandDefinition = */ true); - PI.emitIncludeToggle("GET_INSTRINFO_HELPERS", false); - - PI.emitIncludeToggle("GET_INSTRINFO_CTOR_DTOR", true); - - PI.emitNamespace("llvm", true); - PI.instrInfoEmitExternArrays(TargetName, - HasDeprecationFeatures, - HasComplexDeprecationInfos); - PI.instrInfoEmitMCInstrInfoInit(TargetName, - ClassName, - NumberedInstructions.size(), - HasDeprecationFeatures, - HasComplexDeprecationInfos); - PI.emitNamespace("llvm", false); - - PI.emitIncludeToggle("GET_INSTRINFO_CTOR_DTOR", false); + OS << "namespace llvm {\n"; + OS << "struct " << ClassName << " : public TargetInstrInfo {\n" + << " explicit " << ClassName + << "(unsigned CFSetupOpcode = ~0u, unsigned CFDestroyOpcode = ~0u, " + "unsigned CatchRetOpcode = ~0u, unsigned ReturnOpcode = ~0u);\n" + << " ~" << ClassName << "() override = default;\n"; + + + OS << "\n};\n} // end namespace llvm\n"; + + OS << "#endif // GET_INSTRINFO_HEADER\n\n"; + + OS << "#ifdef GET_INSTRINFO_HELPER_DECLS\n"; + OS << "#undef GET_INSTRINFO_HELPER_DECLS\n\n"; + emitTIIHelperMethods(OS, TargetName, /* ExpandDefinition = */ false); + OS << "\n"; + OS << "#endif // GET_INSTRINFO_HELPER_DECLS\n\n"; + + OS << "#ifdef GET_INSTRINFO_HELPERS\n"; + OS << "#undef GET_INSTRINFO_HELPERS\n\n"; + emitTIIHelperMethods(OS, TargetName, /* ExpandDefinition = */ true); + OS << "#endif // GET_INSTRINFO_HELPERS\n\n"; + + OS << "#ifdef GET_INSTRINFO_CTOR_DTOR\n"; + OS << "#undef GET_INSTRINFO_CTOR_DTOR\n"; + + OS << "namespace llvm {\n"; + OS << "extern const MCInstrDesc " << TargetName << "Insts[];\n"; + OS << "extern const unsigned " << TargetName << "InstrNameIndices[];\n"; + OS << "extern const char " << TargetName << "InstrNameData[];\n"; + if (HasDeprecationFeatures) + OS << "extern const uint8_t " << TargetName + << "InstrDeprecationFeatures[];\n"; + if (HasComplexDeprecationInfos) + OS << "extern const MCInstrInfo::ComplexDeprecationPredicate " << TargetName + << "InstrComplexDeprecationInfos[];\n"; + OS << ClassName << "::" << ClassName + << "(unsigned CFSetupOpcode, unsigned CFDestroyOpcode, unsigned " + "CatchRetOpcode, unsigned ReturnOpcode)\n" + << " : TargetInstrInfo(CFSetupOpcode, CFDestroyOpcode, CatchRetOpcode, " + "ReturnOpcode) {\n" + << " InitMCInstrInfo(" << TargetName << "Insts, " << TargetName + << "InstrNameIndices, " << TargetName << "InstrNameData, "; + if (HasDeprecationFeatures) + OS << TargetName << "InstrDeprecationFeatures, "; + else + OS << "nullptr, "; + if (HasComplexDeprecationInfos) + OS << TargetName << "InstrComplexDeprecationInfos, "; + else + OS << "nullptr, "; + OS << NumberedInstructions.size() << ");\n}\n"; + OS << "} // end namespace llvm\n"; + + OS << "#endif // GET_INSTRINFO_CTOR_DTOR\n\n"; Records.startTimer("Emit operand name mappings"); - emitOperandNameMappings(Target, NumberedInstructions); + emitOperandNameMappings(OS, Target, NumberedInstructions); Records.startTimer("Emit operand type mappings"); - emitOperandTypeMappings(Target, NumberedInstructions); + emitOperandTypeMappings(OS, Target, NumberedInstructions); Records.startTimer("Emit logical operand size mappings"); - emitLogicalOperandSizeMappings(TargetName, NumberedInstructions); + emitLogicalOperandSizeMappings(OS, TargetName, NumberedInstructions); Records.startTimer("Emit logical operand type mappings"); - emitLogicalOperandTypeMappings(TargetName, NumberedInstructions); + emitLogicalOperandTypeMappings(OS, TargetName, NumberedInstructions); Records.startTimer("Emit helper methods"); - emitMCIIHelperMethods(TargetName); + emitMCIIHelperMethods(OS, TargetName); Records.startTimer("Emit verifier methods"); - emitFeatureVerifier(Target); + emitFeatureVerifier(OS, Target); } void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, Record *InstrInfo, std::map, unsigned> &EmittedLists, - const OperandInfoMapTy &OpInfo) { + const OperandInfoMapTy &OpInfo, + raw_ostream &OS) { int MinOperands = 0; if (!Inst.Operands.empty()) // Each logical operand can be multiple MI operands. MinOperands = Inst.Operands.back().MIOperandNo + Inst.Operands.back().MINumOperands; - PI.instrInfoEmitRecord(SchedModels, Inst, Num, MinOperands); + OS << " { "; + OS << Num << ",\t" << MinOperands << ",\t" + << Inst.Operands.NumDefs << ",\t" + << Inst.TheDef->getValueAsInt("Size") << ",\t" + << SchedModels.getSchedClassIdx(Inst) << ",\t" + << Inst.ImplicitUses.size() << ",\t" + << Inst.ImplicitDefs.size() << ",\t0"; CodeGenTarget &Target = CDP.getTargetInfo(); // Emit all of the target independent flags... - PI.instrInfoEmitTargetIndepFlags(Inst, Target.getAllowRegisterRenaming()); + if (Inst.isPreISelOpcode) OS << "|(1ULL<getValueAsBitsInit("TSFlags"); @@ -715,51 +1179,79 @@ void InstrInfoEmitter::emitRecord(const CodeGenInstruction &Inst, unsigned Num, PrintFatalError(Inst.TheDef->getLoc(), "Invalid TSFlags bit in " + Inst.TheDef->getName()); } - PI.instrInfoEmitTSFFlags(Value); + OS << ", 0x"; + OS.write_hex(Value); + OS << "ULL, "; // Emit the implicit use/def list... std::vector ImplicitOps = Inst.ImplicitUses; llvm::append_range(ImplicitOps, Inst.ImplicitDefs); - PI.instrInfoEmitUseDefsLists(EmittedLists, ImplicitOps); + if (ImplicitOps.empty()) + OS << "nullptr, "; + else + OS << "ImplicitList" << EmittedLists[ImplicitOps] << ", "; // Emit the operand info. std::vector OperandInfo = GetOperandInfo(Inst); - PI.instrInfoEmitOperandInfo(OperandInfo, OpInfo); + if (OperandInfo.empty()) + OS << "nullptr"; + else + OS << "OperandInfo" << OpInfo.find(OperandInfo)->second; - PI.instrInfoEmitRecordEnd(Num, Inst.TheDef->getName().str()); + OS << " }, // Inst #" << Num << " = " << Inst.TheDef->getName() << "\n"; } // emitEnums - Print out enum values for all of the instructions. -void InstrInfoEmitter::emitEnums() { +void InstrInfoEmitter::emitEnums(raw_ostream &OS) { + OS << "#ifdef GET_INSTRINFO_ENUM\n"; + OS << "#undef GET_INSTRINFO_ENUM\n"; + + OS << "namespace llvm {\n\n"; + const CodeGenTarget &Target = CDP.getTargetInfo(); // We must emit the PHI opcode first... StringRef Namespace = Target.getInstNamespace(); + if (Namespace.empty()) PrintFatalError("No instructions defined!"); - PI.instrInfoEmitEnums(Target, Namespace, SchedModels); + + OS << "namespace " << Namespace << " {\n"; + OS << " enum {\n"; + unsigned Num = 0; + for (const CodeGenInstruction *Inst : Target.getInstructionsByEnumValue()) + OS << " " << Inst->TheDef->getName() << "\t= " << Num++ << ",\n"; + OS << " INSTRUCTION_LIST_END = " << Num << "\n"; + OS << " };\n\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + OS << "#endif // GET_INSTRINFO_ENUM\n\n"; + + OS << "#ifdef GET_INSTRINFO_SCHED_ENUM\n"; + OS << "#undef GET_INSTRINFO_SCHED_ENUM\n"; + OS << "namespace llvm {\n\n"; + OS << "namespace " << Namespace << " {\n"; + OS << "namespace Sched {\n"; + OS << " enum {\n"; + Num = 0; + for (const auto &Class : SchedModels.explicit_classes()) + OS << " " << Class.Name << "\t= " << Num++ << ",\n"; + OS << " SCHED_LIST_END = " << Num << "\n"; + OS << " };\n"; + OS << "} // end namespace Sched\n"; + OS << "} // end namespace " << Namespace << "\n"; + OS << "} // end namespace llvm\n"; + + OS << "#endif // GET_INSTRINFO_SCHED_ENUM\n\n"; } namespace llvm { void EmitInstrInfo(RecordKeeper &RK, raw_ostream &OS) { - formatted_raw_ostream FOS(OS); - PrinterLLVM *PI; - PrinterLanguage const PL = PrinterLLVM::getLanguage(); - - if (PL == PRINTER_LANG_CPP) { - PI = new PrinterLLVM(FOS); - } else if (PL == PRINTER_LANG_CAPSTONE_C) { - PI = new PrinterCapstone(FOS); - } else { - llvm_unreachable("InstrInfoEmitter does not support the given output language."); - } - RK.startTimer("Analyze DAG patterns"); - InstrInfoEmitter(RK, *PI).run(); + InstrInfoEmitter(RK).run(OS); RK.startTimer("Emit map table"); EmitMapTable(RK, OS); - delete PI; } } // end namespace llvm diff --git a/llvm/utils/TableGen/PredicateExpander.cpp b/llvm/utils/TableGen/PredicateExpander.cpp index b2cb900038a7..b129401461b5 100644 --- a/llvm/utils/TableGen/PredicateExpander.cpp +++ b/llvm/utils/TableGen/PredicateExpander.cpp @@ -15,12 +15,12 @@ namespace llvm { -void PredicateExpanderLLVM::expandTrue(raw_ostream &OS) { OS << "true"; } -void PredicateExpanderLLVM::expandFalse(raw_ostream &OS) { OS << "false"; } +void PredicateExpander::expandTrue(raw_ostream &OS) { OS << "true"; } +void PredicateExpander::expandFalse(raw_ostream &OS) { OS << "false"; } -void PredicateExpanderLLVM::expandCheckImmOperand(raw_ostream &OS, int OpIndex, - int ImmVal, - StringRef FunctionMapper) { +void PredicateExpander::expandCheckImmOperand(raw_ostream &OS, int OpIndex, + int ImmVal, + StringRef FunctionMapper) { if (!FunctionMapper.empty()) OS << FunctionMapper << "("; OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex @@ -30,9 +30,9 @@ void PredicateExpanderLLVM::expandCheckImmOperand(raw_ostream &OS, int OpIndex, OS << (shouldNegate() ? " != " : " == ") << ImmVal; } -void PredicateExpanderLLVM::expandCheckImmOperand(raw_ostream &OS, int OpIndex, - StringRef ImmVal, - StringRef FunctionMapper) { +void PredicateExpander::expandCheckImmOperand(raw_ostream &OS, int OpIndex, + StringRef ImmVal, + StringRef FunctionMapper) { if (ImmVal.empty()) expandCheckImmOperandSimple(OS, OpIndex, FunctionMapper); @@ -45,8 +45,9 @@ void PredicateExpanderLLVM::expandCheckImmOperand(raw_ostream &OS, int OpIndex, OS << (shouldNegate() ? " != " : " == ") << ImmVal; } -void PredicateExpanderLLVM::expandCheckImmOperandSimple( - raw_ostream &OS, int OpIndex, StringRef FunctionMapper) { +void PredicateExpander::expandCheckImmOperandSimple(raw_ostream &OS, + int OpIndex, + StringRef FunctionMapper) { if (shouldNegate()) OS << "!"; if (!FunctionMapper.empty()) @@ -57,9 +58,9 @@ void PredicateExpanderLLVM::expandCheckImmOperandSimple( OS << ")"; } -void PredicateExpanderLLVM::expandCheckRegOperand(raw_ostream &OS, int OpIndex, - const Record *Reg, - StringRef FunctionMapper) { +void PredicateExpander::expandCheckRegOperand(raw_ostream &OS, int OpIndex, + const Record *Reg, + StringRef FunctionMapper) { assert(Reg->isSubClassOf("Register") && "Expected a register Record!"); if (!FunctionMapper.empty()) @@ -75,544 +76,10 @@ void PredicateExpanderLLVM::expandCheckRegOperand(raw_ostream &OS, int OpIndex, OS << Reg->getName(); } -void PredicateExpanderLLVM::expandCheckRegOperandSimple( - raw_ostream &OS, int OpIndex, StringRef FunctionMapper) { - if (shouldNegate()) - OS << "!"; - if (!FunctionMapper.empty()) - OS << FunctionMapper << "("; - OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex - << ").getReg()"; - if (!FunctionMapper.empty()) - OS << ")"; -} - -void PredicateExpanderLLVM::expandCheckInvalidRegOperand(raw_ostream &OS, - int OpIndex) { - OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex - << ").getReg() " << (shouldNegate() ? "!= " : "== ") << "0"; -} - -void PredicateExpanderLLVM::expandCheckSameRegOperand(raw_ostream &OS, - int First, int Second) { - OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << First - << ").getReg() " << (shouldNegate() ? "!=" : "==") << " MI" - << (isByRef() ? "." : "->") << "getOperand(" << Second << ").getReg()"; -} - -void PredicateExpanderLLVM::expandCheckNumOperands(raw_ostream &OS, - int NumOps) { - OS << "MI" << (isByRef() ? "." : "->") << "getNumOperands() " - << (shouldNegate() ? "!= " : "== ") << NumOps; -} - -void PredicateExpanderLLVM::expandCheckOpcode(raw_ostream &OS, - const Record *Inst) { - OS << "MI" << (isByRef() ? "." : "->") << "getOpcode() " - << (shouldNegate() ? "!= " : "== ") << Inst->getValueAsString("Namespace") - << "::" << Inst->getName(); -} - -void PredicateExpanderLLVM::expandCheckOpcode(raw_ostream &OS, - const RecVec &Opcodes) { - assert(!Opcodes.empty() && "Expected at least one opcode to check!"); - bool First = true; - - if (Opcodes.size() == 1) { - OS << "( "; - expandCheckOpcode(OS, Opcodes[0]); - OS << " )"; - return; - } - - OS << '('; - increaseIndentLevel(); - for (const Record *Rec : Opcodes) { - OS << '\n'; - OS.indent(getIndentLevel() * 2); - if (!First) - OS << (shouldNegate() ? "&& " : "|| "); - - expandCheckOpcode(OS, Rec); - First = false; - } - - OS << '\n'; - decreaseIndentLevel(); - OS.indent(getIndentLevel() * 2); - OS << ')'; -} - -void PredicateExpanderLLVM::expandCheckPseudo(raw_ostream &OS, - const RecVec &Opcodes) { - if (shouldExpandForMC()) - expandFalse(OS); - else - expandCheckOpcode(OS, Opcodes); -} - -void PredicateExpanderLLVM::expandPredicateSequence(raw_ostream &OS, - const RecVec &Sequence, - bool IsCheckAll) { - assert(!Sequence.empty() && "Found an invalid empty predicate set!"); - if (Sequence.size() == 1) - return expandPredicate(OS, Sequence[0]); - - // Okay, there is more than one predicate in the set. - bool First = true; - OS << (shouldNegate() ? "!(" : "("); - increaseIndentLevel(); - - bool OldValue = shouldNegate(); - setNegatePredicate(false); - for (const Record *Rec : Sequence) { - OS << '\n'; - OS.indent(getIndentLevel() * 2); - if (!First) - OS << (IsCheckAll ? "&& " : "|| "); - expandPredicate(OS, Rec); - First = false; - } - OS << '\n'; - decreaseIndentLevel(); - OS.indent(getIndentLevel() * 2); - OS << ')'; - setNegatePredicate(OldValue); -} - -void PredicateExpanderLLVM::expandTIIFunctionCall(raw_ostream &OS, - StringRef MethodName) { - OS << (shouldNegate() ? "!" : ""); - OS << TargetName << (shouldExpandForMC() ? "_MC::" : "InstrInfo::"); - OS << MethodName << (isByRef() ? "(MI)" : "(*MI)"); -} - -void PredicateExpanderLLVM::expandCheckIsRegOperand(raw_ostream &OS, - int OpIndex) { - OS << (shouldNegate() ? "!" : "") << "MI" << (isByRef() ? "." : "->") - << "getOperand(" << OpIndex << ").isReg() "; -} - -void PredicateExpanderLLVM::expandCheckIsImmOperand(raw_ostream &OS, - int OpIndex) { - OS << (shouldNegate() ? "!" : "") << "MI" << (isByRef() ? "." : "->") - << "getOperand(" << OpIndex << ").isImm() "; -} - -void PredicateExpanderLLVM::expandCheckFunctionPredicateWithTII( - raw_ostream &OS, StringRef MCInstFn, StringRef MachineInstrFn, - StringRef TIIPtr) { - if (!shouldExpandForMC()) { - OS << (TIIPtr.empty() ? "TII" : TIIPtr) << "->" << MachineInstrFn; - OS << (isByRef() ? "(MI)" : "(*MI)"); - return; - } - - OS << MCInstFn << (isByRef() ? "(MI" : "(*MI") << ", MCII)"; -} - -void PredicateExpanderLLVM::expandCheckFunctionPredicate( - raw_ostream &OS, StringRef MCInstFn, StringRef MachineInstrFn) { - OS << (shouldExpandForMC() ? MCInstFn : MachineInstrFn) - << (isByRef() ? "(MI)" : "(*MI)"); -} - -void PredicateExpanderLLVM::expandCheckNonPortable(raw_ostream &OS, - StringRef Code) { - if (shouldExpandForMC()) - return expandFalse(OS); - - OS << '(' << Code << ')'; -} - -void PredicateExpanderLLVM::expandReturnStatement(raw_ostream &OS, - const Record *Rec) { - std::string Buffer; - raw_string_ostream SS(Buffer); - - SS << "return "; - expandPredicate(SS, Rec); - SS << ";"; - OS << Buffer; -} - -void PredicateExpanderLLVM::expandOpcodeSwitchCase(raw_ostream &OS, - const Record *Rec) { - const RecVec &Opcodes = Rec->getValueAsListOfDefs("Opcodes"); - for (const Record *Opcode : Opcodes) { - OS.indent(getIndentLevel() * 2); - OS << "case " << Opcode->getValueAsString("Namespace") - << "::" << Opcode->getName() << ":\n"; - } - - increaseIndentLevel(); - OS.indent(getIndentLevel() * 2); - expandStatement(OS, Rec->getValueAsDef("CaseStmt")); - decreaseIndentLevel(); -} - -void PredicateExpanderLLVM::expandOpcodeSwitchStatement(raw_ostream &OS, - const RecVec &Cases, - const Record *Default) { - std::string Buffer; - raw_string_ostream SS(Buffer); - - SS << "switch(MI" << (isByRef() ? "." : "->") << "getOpcode()) {\n"; - for (const Record *Rec : Cases) { - expandOpcodeSwitchCase(SS, Rec); - SS << '\n'; - } - - // Expand the default case. - SS.indent(getIndentLevel() * 2); - SS << "default:\n"; - - increaseIndentLevel(); - SS.indent(getIndentLevel() * 2); - expandStatement(SS, Default); - decreaseIndentLevel(); - SS << '\n'; - - SS.indent(getIndentLevel() * 2); - SS << "} // end of switch-stmt"; - OS << Buffer; -} - -void PredicateExpanderLLVM::expandStatement(raw_ostream &OS, - const Record *Rec) { - // Assume that padding has been added by the caller. - if (Rec->isSubClassOf("MCOpcodeSwitchStatement")) { - expandOpcodeSwitchStatement(OS, Rec->getValueAsListOfDefs("Cases"), - Rec->getValueAsDef("DefaultCase")); - return; - } - - if (Rec->isSubClassOf("MCReturnStatement")) { - expandReturnStatement(OS, Rec->getValueAsDef("Pred")); - return; - } - - llvm_unreachable("No known rules to expand this MCStatement"); -} - -void PredicateExpanderLLVM::expandPredicate(raw_ostream &OS, - const Record *Rec) { - // Assume that padding has been added by the caller. - if (Rec->isSubClassOf("MCTrue")) { - if (shouldNegate()) - return expandFalse(OS); - return expandTrue(OS); - } - - if (Rec->isSubClassOf("MCFalse")) { - if (shouldNegate()) - return expandTrue(OS); - return expandFalse(OS); - } - - if (Rec->isSubClassOf("CheckNot")) { - flipNegatePredicate(); - expandPredicate(OS, Rec->getValueAsDef("Pred")); - flipNegatePredicate(); - return; - } - - if (Rec->isSubClassOf("CheckIsRegOperand")) - return expandCheckIsRegOperand(OS, Rec->getValueAsInt("OpIndex")); - - if (Rec->isSubClassOf("CheckIsImmOperand")) - return expandCheckIsImmOperand(OS, Rec->getValueAsInt("OpIndex")); - - if (Rec->isSubClassOf("CheckRegOperand")) - return expandCheckRegOperand(OS, Rec->getValueAsInt("OpIndex"), - Rec->getValueAsDef("Reg"), - Rec->getValueAsString("FunctionMapper")); - - if (Rec->isSubClassOf("CheckRegOperandSimple")) - return expandCheckRegOperandSimple(OS, Rec->getValueAsInt("OpIndex"), - Rec->getValueAsString("FunctionMapper")); - - if (Rec->isSubClassOf("CheckInvalidRegOperand")) - return expandCheckInvalidRegOperand(OS, Rec->getValueAsInt("OpIndex")); - - if (Rec->isSubClassOf("CheckImmOperand")) - return expandCheckImmOperand(OS, Rec->getValueAsInt("OpIndex"), - Rec->getValueAsInt("ImmVal"), - Rec->getValueAsString("FunctionMapper")); - - if (Rec->isSubClassOf("CheckImmOperand_s")) - return expandCheckImmOperand(OS, Rec->getValueAsInt("OpIndex"), - Rec->getValueAsString("ImmVal"), - Rec->getValueAsString("FunctionMapper")); - - if (Rec->isSubClassOf("CheckImmOperandSimple")) - return expandCheckImmOperandSimple(OS, Rec->getValueAsInt("OpIndex"), - Rec->getValueAsString("FunctionMapper")); - if (Rec->isSubClassOf("CheckSameRegOperand")) - return expandCheckSameRegOperand(OS, Rec->getValueAsInt("FirstIndex"), - Rec->getValueAsInt("SecondIndex")); - - if (Rec->isSubClassOf("CheckNumOperands")) - return expandCheckNumOperands(OS, Rec->getValueAsInt("NumOps")); - - if (Rec->isSubClassOf("CheckPseudo")) - return expandCheckPseudo(OS, Rec->getValueAsListOfDefs("ValidOpcodes")); - - if (Rec->isSubClassOf("CheckOpcode")) - return expandCheckOpcode(OS, Rec->getValueAsListOfDefs("ValidOpcodes")); - - if (Rec->isSubClassOf("CheckAll")) - return expandPredicateSequence(OS, Rec->getValueAsListOfDefs("Predicates"), - /* AllOf */ true); - - if (Rec->isSubClassOf("CheckAny")) - return expandPredicateSequence(OS, Rec->getValueAsListOfDefs("Predicates"), - /* AllOf */ false); - - if (Rec->isSubClassOf("CheckFunctionPredicate")) { - return expandCheckFunctionPredicate( - OS, Rec->getValueAsString("MCInstFnName"), - Rec->getValueAsString("MachineInstrFnName")); - } - - if (Rec->isSubClassOf("CheckFunctionPredicateWithTII")) { - return expandCheckFunctionPredicateWithTII( - OS, Rec->getValueAsString("MCInstFnName"), - Rec->getValueAsString("MachineInstrFnName"), - Rec->getValueAsString("TIIPtrName")); - } - - if (Rec->isSubClassOf("CheckNonPortable")) - return expandCheckNonPortable(OS, Rec->getValueAsString("CodeBlock")); - - if (Rec->isSubClassOf("TIIPredicate")) - return expandTIIFunctionCall(OS, Rec->getValueAsString("FunctionName")); - - llvm_unreachable("No known rules to expand this MCInstPredicate"); -} - -void PredicateExpanderLLVM::expandHeader(raw_ostream &OS, - const STIPredicateFunction &Fn) { - const Record *Rec = Fn.getDeclaration(); - StringRef FunctionName = Rec->getValueAsString("Name"); - - OS.indent(getIndentLevel() * 2); - OS << "bool "; - if (shouldExpandDefinition()) - OS << getClassPrefix() << "::"; - OS << FunctionName << "("; - if (shouldExpandForMC()) - OS << "const MCInst " << (isByRef() ? "&" : "*") << "MI"; - else - OS << "const MachineInstr " << (isByRef() ? "&" : "*") << "MI"; - if (Rec->getValueAsBit("UpdatesOpcodeMask")) - OS << ", APInt &Mask"; - OS << (shouldExpandForMC() ? ", unsigned ProcessorID) const " : ") const "); - if (shouldExpandDefinition()) { - OS << "{\n"; - return; - } - - if (Rec->getValueAsBit("OverridesBaseClassMember")) - OS << "override"; - OS << ";\n"; -} - -void PredicateExpanderLLVM::expandPrologue(raw_ostream &OS, - const STIPredicateFunction &Fn) { - RecVec Delegates = Fn.getDeclaration()->getValueAsListOfDefs("Delegates"); - bool UpdatesOpcodeMask = - Fn.getDeclaration()->getValueAsBit("UpdatesOpcodeMask"); - - increaseIndentLevel(); - unsigned IndentLevel = getIndentLevel(); - for (const Record *Delegate : Delegates) { - OS.indent(IndentLevel * 2); - OS << "if (" << Delegate->getValueAsString("Name") << "(MI"; - if (UpdatesOpcodeMask) - OS << ", Mask"; - if (shouldExpandForMC()) - OS << ", ProcessorID"; - OS << "))\n"; - OS.indent((1 + IndentLevel) * 2); - OS << "return true;\n\n"; - } - - if (shouldExpandForMC()) - return; - - OS.indent(IndentLevel * 2); - OS << "unsigned ProcessorID = getSchedModel().getProcessorID();\n"; -} - -void PredicateExpanderLLVM::expandOpcodeGroup(raw_ostream &OS, - const OpcodeGroup &Group, - bool ShouldUpdateOpcodeMask) { - const OpcodeInfo &OI = Group.getOpcodeInfo(); - for (const PredicateInfo &PI : OI.getPredicates()) { - const APInt &ProcModelMask = PI.ProcModelMask; - bool FirstProcID = true; - for (unsigned I = 0, E = ProcModelMask.getActiveBits(); I < E; ++I) { - if (!ProcModelMask[I]) - continue; - - if (FirstProcID) { - OS.indent(getIndentLevel() * 2); - OS << "if (ProcessorID == " << I; - } else { - OS << " || ProcessorID == " << I; - } - FirstProcID = false; - } - - OS << ") {\n"; - - increaseIndentLevel(); - OS.indent(getIndentLevel() * 2); - if (ShouldUpdateOpcodeMask) { - if (PI.OperandMask.isZero()) - OS << "Mask.clearAllBits();\n"; - else - OS << "Mask = " << PI.OperandMask << ";\n"; - OS.indent(getIndentLevel() * 2); - } - OS << "return "; - expandPredicate(OS, PI.Predicate); - OS << ";\n"; - decreaseIndentLevel(); - OS.indent(getIndentLevel() * 2); - OS << "}\n"; - } -} - -void PredicateExpanderLLVM::expandBody(raw_ostream &OS, - const STIPredicateFunction &Fn) { - bool UpdatesOpcodeMask = - Fn.getDeclaration()->getValueAsBit("UpdatesOpcodeMask"); - - unsigned IndentLevel = getIndentLevel(); - OS.indent(IndentLevel * 2); - OS << "switch(MI" << (isByRef() ? "." : "->") << "getOpcode()) {\n"; - OS.indent(IndentLevel * 2); - OS << "default:\n"; - OS.indent(IndentLevel * 2); - OS << " break;"; - - for (const OpcodeGroup &Group : Fn.getGroups()) { - for (const Record *Opcode : Group.getOpcodes()) { - OS << '\n'; - OS.indent(IndentLevel * 2); - OS << "case " << getTargetName() << "::" << Opcode->getName() << ":"; - } - - OS << '\n'; - increaseIndentLevel(); - expandOpcodeGroup(OS, Group, UpdatesOpcodeMask); - - OS.indent(getIndentLevel() * 2); - OS << "break;\n"; - decreaseIndentLevel(); - } - - OS.indent(IndentLevel * 2); - OS << "}\n"; -} - -void PredicateExpanderLLVM::expandEpilogue(raw_ostream &OS, - const STIPredicateFunction &Fn) { - OS << '\n'; - OS.indent(getIndentLevel() * 2); - OS << "return "; - expandPredicate(OS, Fn.getDefaultReturnPredicate()); - OS << ";\n"; - - decreaseIndentLevel(); - OS.indent(getIndentLevel() * 2); - StringRef FunctionName = Fn.getDeclaration()->getValueAsString("Name"); - OS << "} // " << ClassPrefix << "::" << FunctionName << "\n\n"; -} - -void PredicateExpanderLLVM::expandSTIPredicate(raw_ostream &OS, - const STIPredicateFunction &Fn) { - const Record *Rec = Fn.getDeclaration(); - if (shouldExpandForMC() && !Rec->getValueAsBit("ExpandForMC")) - return; - - expandHeader(OS, Fn); - if (shouldExpandDefinition()) { - expandPrologue(OS, Fn); - expandBody(OS, Fn); - expandEpilogue(OS, Fn); - } -} - -//--------------- -// Capstone -//--------------- - -void PredicateExpanderCapstone::expandTrue(raw_ostream &OS) { OS << "true"; } -void PredicateExpanderCapstone::expandFalse(raw_ostream &OS) { OS << "false"; } - -void PredicateExpanderCapstone::expandCheckImmOperand(raw_ostream &OS, int OpIndex, - int ImmVal, - StringRef FunctionMapper) { - if (!FunctionMapper.empty()) - OS << FunctionMapper << "("; - OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex - << ").getImm()"; - if (!FunctionMapper.empty()) - OS << ")"; - OS << (shouldNegate() ? " != " : " == ") << ImmVal; -} - -void PredicateExpanderCapstone::expandCheckImmOperand(raw_ostream &OS, int OpIndex, - StringRef ImmVal, - StringRef FunctionMapper) { - if (ImmVal.empty()) - expandCheckImmOperandSimple(OS, OpIndex, FunctionMapper); - - if (!FunctionMapper.empty()) - OS << FunctionMapper << "("; - OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex - << ").getImm()"; - if (!FunctionMapper.empty()) - OS << ")"; - OS << (shouldNegate() ? " != " : " == ") << ImmVal; -} - -void PredicateExpanderCapstone::expandCheckImmOperandSimple( - raw_ostream &OS, int OpIndex, StringRef FunctionMapper) { - if (shouldNegate()) - OS << "!"; - if (!FunctionMapper.empty()) - OS << FunctionMapper << "("; - OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex - << ").getImm()"; - if (!FunctionMapper.empty()) - OS << ")"; -} - -void PredicateExpanderCapstone::expandCheckRegOperand(raw_ostream &OS, int OpIndex, - const Record *Reg, - StringRef FunctionMapper) { - assert(Reg->isSubClassOf("Register") && "Expected a register Record!"); - - if (!FunctionMapper.empty()) - OS << FunctionMapper << "("; - OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex - << ").getReg()"; - if (!FunctionMapper.empty()) - OS << ")"; - OS << (shouldNegate() ? " != " : " == "); - const StringRef Str = Reg->getValueAsString("Namespace"); - if (!Str.empty()) - OS << Str << "::"; - OS << Reg->getName(); -} - -void PredicateExpanderCapstone::expandCheckRegOperandSimple( - raw_ostream &OS, int OpIndex, StringRef FunctionMapper) { +void PredicateExpander::expandCheckRegOperandSimple(raw_ostream &OS, + int OpIndex, + StringRef FunctionMapper) { if (shouldNegate()) OS << "!"; if (!FunctionMapper.empty()) @@ -623,34 +90,32 @@ void PredicateExpanderCapstone::expandCheckRegOperandSimple( OS << ")"; } -void PredicateExpanderCapstone::expandCheckInvalidRegOperand(raw_ostream &OS, - int OpIndex) { +void PredicateExpander::expandCheckInvalidRegOperand(raw_ostream &OS, + int OpIndex) { OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex << ").getReg() " << (shouldNegate() ? "!= " : "== ") << "0"; } -void PredicateExpanderCapstone::expandCheckSameRegOperand(raw_ostream &OS, - int First, int Second) { +void PredicateExpander::expandCheckSameRegOperand(raw_ostream &OS, int First, + int Second) { OS << "MI" << (isByRef() ? "." : "->") << "getOperand(" << First << ").getReg() " << (shouldNegate() ? "!=" : "==") << " MI" << (isByRef() ? "." : "->") << "getOperand(" << Second << ").getReg()"; } -void PredicateExpanderCapstone::expandCheckNumOperands(raw_ostream &OS, - int NumOps) { +void PredicateExpander::expandCheckNumOperands(raw_ostream &OS, int NumOps) { OS << "MI" << (isByRef() ? "." : "->") << "getNumOperands() " << (shouldNegate() ? "!= " : "== ") << NumOps; } -void PredicateExpanderCapstone::expandCheckOpcode(raw_ostream &OS, - const Record *Inst) { +void PredicateExpander::expandCheckOpcode(raw_ostream &OS, const Record *Inst) { OS << "MI" << (isByRef() ? "." : "->") << "getOpcode() " << (shouldNegate() ? "!= " : "== ") << Inst->getValueAsString("Namespace") << "::" << Inst->getName(); } -void PredicateExpanderCapstone::expandCheckOpcode(raw_ostream &OS, - const RecVec &Opcodes) { +void PredicateExpander::expandCheckOpcode(raw_ostream &OS, + const RecVec &Opcodes) { assert(!Opcodes.empty() && "Expected at least one opcode to check!"); bool First = true; @@ -679,17 +144,17 @@ void PredicateExpanderCapstone::expandCheckOpcode(raw_ostream &OS, OS << ')'; } -void PredicateExpanderCapstone::expandCheckPseudo(raw_ostream &OS, - const RecVec &Opcodes) { +void PredicateExpander::expandCheckPseudo(raw_ostream &OS, + const RecVec &Opcodes) { if (shouldExpandForMC()) expandFalse(OS); else expandCheckOpcode(OS, Opcodes); } -void PredicateExpanderCapstone::expandPredicateSequence(raw_ostream &OS, - const RecVec &Sequence, - bool IsCheckAll) { +void PredicateExpander::expandPredicateSequence(raw_ostream &OS, + const RecVec &Sequence, + bool IsCheckAll) { assert(!Sequence.empty() && "Found an invalid empty predicate set!"); if (Sequence.size() == 1) return expandPredicate(OS, Sequence[0]); @@ -716,26 +181,24 @@ void PredicateExpanderCapstone::expandPredicateSequence(raw_ostream &OS, setNegatePredicate(OldValue); } -void PredicateExpanderCapstone::expandTIIFunctionCall(raw_ostream &OS, - StringRef MethodName) { +void PredicateExpander::expandTIIFunctionCall(raw_ostream &OS, + StringRef MethodName) { OS << (shouldNegate() ? "!" : ""); OS << TargetName << (shouldExpandForMC() ? "_MC::" : "InstrInfo::"); OS << MethodName << (isByRef() ? "(MI)" : "(*MI)"); } -void PredicateExpanderCapstone::expandCheckIsRegOperand(raw_ostream &OS, - int OpIndex) { +void PredicateExpander::expandCheckIsRegOperand(raw_ostream &OS, int OpIndex) { OS << (shouldNegate() ? "!" : "") << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex << ").isReg() "; } -void PredicateExpanderCapstone::expandCheckIsImmOperand(raw_ostream &OS, - int OpIndex) { +void PredicateExpander::expandCheckIsImmOperand(raw_ostream &OS, int OpIndex) { OS << (shouldNegate() ? "!" : "") << "MI" << (isByRef() ? "." : "->") << "getOperand(" << OpIndex << ").isImm() "; } -void PredicateExpanderCapstone::expandCheckFunctionPredicateWithTII( +void PredicateExpander::expandCheckFunctionPredicateWithTII( raw_ostream &OS, StringRef MCInstFn, StringRef MachineInstrFn, StringRef TIIPtr) { if (!shouldExpandForMC()) { @@ -747,22 +210,23 @@ void PredicateExpanderCapstone::expandCheckFunctionPredicateWithTII( OS << MCInstFn << (isByRef() ? "(MI" : "(*MI") << ", MCII)"; } -void PredicateExpanderCapstone::expandCheckFunctionPredicate( - raw_ostream &OS, StringRef MCInstFn, StringRef MachineInstrFn) { +void PredicateExpander::expandCheckFunctionPredicate(raw_ostream &OS, + StringRef MCInstFn, + StringRef MachineInstrFn) { OS << (shouldExpandForMC() ? MCInstFn : MachineInstrFn) << (isByRef() ? "(MI)" : "(*MI)"); } -void PredicateExpanderCapstone::expandCheckNonPortable(raw_ostream &OS, - StringRef Code) { +void PredicateExpander::expandCheckNonPortable(raw_ostream &OS, + StringRef Code) { if (shouldExpandForMC()) return expandFalse(OS); OS << '(' << Code << ')'; } -void PredicateExpanderCapstone::expandReturnStatement(raw_ostream &OS, - const Record *Rec) { +void PredicateExpander::expandReturnStatement(raw_ostream &OS, + const Record *Rec) { std::string Buffer; raw_string_ostream SS(Buffer); @@ -772,13 +236,13 @@ void PredicateExpanderCapstone::expandReturnStatement(raw_ostream &OS, OS << Buffer; } -void PredicateExpanderCapstone::expandOpcodeSwitchCase(raw_ostream &OS, - const Record *Rec) { +void PredicateExpander::expandOpcodeSwitchCase(raw_ostream &OS, + const Record *Rec) { const RecVec &Opcodes = Rec->getValueAsListOfDefs("Opcodes"); for (const Record *Opcode : Opcodes) { OS.indent(getIndentLevel() * 2); OS << "case " << Opcode->getValueAsString("Namespace") - << "_" << Opcode->getName() << ":\n"; + << "::" << Opcode->getName() << ":\n"; } increaseIndentLevel(); @@ -787,9 +251,9 @@ void PredicateExpanderCapstone::expandOpcodeSwitchCase(raw_ostream &OS, decreaseIndentLevel(); } -void PredicateExpanderCapstone::expandOpcodeSwitchStatement(raw_ostream &OS, - const RecVec &Cases, - const Record *Default) { +void PredicateExpander::expandOpcodeSwitchStatement(raw_ostream &OS, + const RecVec &Cases, + const Record *Default) { std::string Buffer; raw_string_ostream SS(Buffer); @@ -814,8 +278,7 @@ void PredicateExpanderCapstone::expandOpcodeSwitchStatement(raw_ostream &OS, OS << Buffer; } -void PredicateExpanderCapstone::expandStatement(raw_ostream &OS, - const Record *Rec) { +void PredicateExpander::expandStatement(raw_ostream &OS, const Record *Rec) { // Assume that padding has been added by the caller. if (Rec->isSubClassOf("MCOpcodeSwitchStatement")) { expandOpcodeSwitchStatement(OS, Rec->getValueAsListOfDefs("Cases"), @@ -831,8 +294,7 @@ void PredicateExpanderCapstone::expandStatement(raw_ostream &OS, llvm_unreachable("No known rules to expand this MCStatement"); } -void PredicateExpanderCapstone::expandPredicate(raw_ostream &OS, - const Record *Rec) { +void PredicateExpander::expandPredicate(raw_ostream &OS, const Record *Rec) { // Assume that padding has been added by the caller. if (Rec->isSubClassOf("MCTrue")) { if (shouldNegate()) @@ -928,8 +390,8 @@ void PredicateExpanderCapstone::expandPredicate(raw_ostream &OS, llvm_unreachable("No known rules to expand this MCInstPredicate"); } -void PredicateExpanderCapstone::expandHeader(raw_ostream &OS, - const STIPredicateFunction &Fn) { +void STIPredicateExpander::expandHeader(raw_ostream &OS, + const STIPredicateFunction &Fn) { const Record *Rec = Fn.getDeclaration(); StringRef FunctionName = Rec->getValueAsString("Name"); @@ -955,8 +417,8 @@ void PredicateExpanderCapstone::expandHeader(raw_ostream &OS, OS << ";\n"; } -void PredicateExpanderCapstone::expandPrologue(raw_ostream &OS, - const STIPredicateFunction &Fn) { +void STIPredicateExpander::expandPrologue(raw_ostream &OS, + const STIPredicateFunction &Fn) { RecVec Delegates = Fn.getDeclaration()->getValueAsListOfDefs("Delegates"); bool UpdatesOpcodeMask = Fn.getDeclaration()->getValueAsBit("UpdatesOpcodeMask"); @@ -982,9 +444,8 @@ void PredicateExpanderCapstone::expandPrologue(raw_ostream &OS, OS << "unsigned ProcessorID = getSchedModel().getProcessorID();\n"; } -void PredicateExpanderCapstone::expandOpcodeGroup(raw_ostream &OS, - const OpcodeGroup &Group, - bool ShouldUpdateOpcodeMask) { +void STIPredicateExpander::expandOpcodeGroup(raw_ostream &OS, const OpcodeGroup &Group, + bool ShouldUpdateOpcodeMask) { const OpcodeInfo &OI = Group.getOpcodeInfo(); for (const PredicateInfo &PI : OI.getPredicates()) { const APInt &ProcModelMask = PI.ProcModelMask; @@ -1022,8 +483,8 @@ void PredicateExpanderCapstone::expandOpcodeGroup(raw_ostream &OS, } } -void PredicateExpanderCapstone::expandBody(raw_ostream &OS, - const STIPredicateFunction &Fn) { +void STIPredicateExpander::expandBody(raw_ostream &OS, + const STIPredicateFunction &Fn) { bool UpdatesOpcodeMask = Fn.getDeclaration()->getValueAsBit("UpdatesOpcodeMask"); @@ -1055,8 +516,8 @@ void PredicateExpanderCapstone::expandBody(raw_ostream &OS, OS << "}\n"; } -void PredicateExpanderCapstone::expandEpilogue(raw_ostream &OS, - const STIPredicateFunction &Fn) { +void STIPredicateExpander::expandEpilogue(raw_ostream &OS, + const STIPredicateFunction &Fn) { OS << '\n'; OS.indent(getIndentLevel() * 2); OS << "return "; @@ -1069,8 +530,8 @@ void PredicateExpanderCapstone::expandEpilogue(raw_ostream &OS, OS << "} // " << ClassPrefix << "::" << FunctionName << "\n\n"; } -void PredicateExpanderCapstone::expandSTIPredicate(raw_ostream &OS, - const STIPredicateFunction &Fn) { +void STIPredicateExpander::expandSTIPredicate(raw_ostream &OS, + const STIPredicateFunction &Fn) { const Record *Rec = Fn.getDeclaration(); if (shouldExpandForMC() && !Rec->getValueAsBit("ExpandForMC")) return; diff --git a/llvm/utils/TableGen/PredicateExpander.h b/llvm/utils/TableGen/PredicateExpander.h index 03b590663a07..27f049a715aa 100644 --- a/llvm/utils/TableGen/PredicateExpander.h +++ b/llvm/utils/TableGen/PredicateExpander.h @@ -21,44 +21,23 @@ namespace llvm { -// Forward declarations. -class STIPredicateFunction; -class OpcodeGroup; class raw_ostream; class Record; -class PredicateExpanderLLVM; -class PredicateExpanderCapstone; class PredicateExpander { - - friend PredicateExpanderLLVM; - friend PredicateExpanderCapstone; - bool EmitCallsByRef; bool NegatePredicate; bool ExpandForMC; unsigned IndentLevel; StringRef TargetName; - // STI only - bool ExpandDefinition; - StringRef ClassPrefix; PredicateExpander(const PredicateExpander &) = delete; PredicateExpander &operator=(const PredicateExpander &) = delete; -private: - virtual void expandHeader(raw_ostream &OS, const STIPredicateFunction &Fn) = 0; - virtual void expandPrologue(raw_ostream &OS, const STIPredicateFunction &Fn) = 0; - virtual void expandOpcodeGroup(raw_ostream &OS, const OpcodeGroup &Group, - bool ShouldUpdateOpcodeMask) = 0; - virtual void expandBody(raw_ostream &OS, const STIPredicateFunction &Fn) = 0; - virtual void expandEpilogue(raw_ostream &OS, const STIPredicateFunction &Fn) = 0; - public: PredicateExpander(StringRef Target) : EmitCallsByRef(true), NegatePredicate(false), ExpandForMC(false), - IndentLevel(1U), TargetName(Target), ExpandDefinition(false) {} - virtual ~PredicateExpander() {} + IndentLevel(1U), TargetName(Target) {} bool isByRef() const { return EmitCallsByRef; } bool shouldNegate() const { return NegatePredicate; } bool shouldExpandForMC() const { return ExpandForMC; } @@ -74,161 +53,72 @@ class PredicateExpander { void decreaseIndentLevel() { --IndentLevel; } using RecVec = std::vector; - virtual void expandTrue(raw_ostream &OS) = 0; - virtual void expandFalse(raw_ostream &OS) = 0; - virtual void expandCheckImmOperand(raw_ostream &OS, int OpIndex, int ImmVal, - StringRef FunctionMapper) = 0; - virtual void expandCheckImmOperand(raw_ostream &OS, int OpIndex, - StringRef ImmVal, - StringRef FunctionMapperer) = 0; - virtual void expandCheckImmOperandSimple(raw_ostream &OS, int OpIndex, - StringRef FunctionMapper) = 0; - virtual void expandCheckRegOperand(raw_ostream &OS, int OpIndex, - const Record *Reg, - StringRef FunctionMapper) = 0; - virtual void expandCheckRegOperandSimple(raw_ostream &OS, int OpIndex, - StringRef FunctionMapper) = 0; - virtual void expandCheckSameRegOperand(raw_ostream &OS, int First, - int Second) = 0; - virtual void expandCheckNumOperands(raw_ostream &OS, int NumOps) = 0; - virtual void expandCheckOpcode(raw_ostream &OS, const Record *Inst) = 0; - - virtual void expandCheckPseudo(raw_ostream &OS, const RecVec &Opcodes) = 0; - virtual void expandCheckOpcode(raw_ostream &OS, const RecVec &Opcodes) = 0; - virtual void expandPredicateSequence(raw_ostream &OS, const RecVec &Sequence, - bool IsCheckAll) = 0; - virtual void expandTIIFunctionCall(raw_ostream &OS, StringRef MethodName) = 0; - virtual void expandCheckIsRegOperand(raw_ostream &OS, int OpIndex) = 0; - virtual void expandCheckIsImmOperand(raw_ostream &OS, int OpIndex) = 0; - virtual void expandCheckInvalidRegOperand(raw_ostream &OS, int OpIndex) = 0; - virtual void expandCheckFunctionPredicate(raw_ostream &OS, StringRef MCInstFn, - StringRef MachineInstrFn) = 0; - virtual void expandCheckFunctionPredicateWithTII(raw_ostream &OS, - StringRef MCInstFn, - StringRef MachineInstrFn, - StringRef TIIPtr) = 0; - virtual void expandCheckNonPortable(raw_ostream &OS, StringRef CodeBlock) = 0; - virtual void expandPredicate(raw_ostream &OS, const Record *Rec) = 0; - virtual void expandReturnStatement(raw_ostream &OS, const Record *Rec) = 0; - virtual void expandOpcodeSwitchCase(raw_ostream &OS, const Record *Rec) = 0; - virtual void expandOpcodeSwitchStatement(raw_ostream &OS, const RecVec &Cases, - const Record *Default) = 0; - virtual void expandStatement(raw_ostream &OS, const Record *Rec) = 0; - - // STI only - bool shouldExpandDefinition() const { return ExpandDefinition; } - StringRef getClassPrefix() const { return ClassPrefix; } - void setClassPrefix(StringRef S) { ClassPrefix = S; } - void setExpandDefinition(bool Value) { ExpandDefinition = Value; } - - virtual void expandSTIPredicate(raw_ostream &OS, - const STIPredicateFunction &Fn) = 0; -}; - -class PredicateExpanderLLVM : public PredicateExpander { - using PredicateExpander::PredicateExpander; - - void expandHeader(raw_ostream &OS, const STIPredicateFunction &Fn) override; - void expandPrologue(raw_ostream &OS, const STIPredicateFunction &Fn) override; - void expandOpcodeGroup(raw_ostream &OS, const OpcodeGroup &Group, - bool ShouldUpdateOpcodeMask) override; - void expandBody(raw_ostream &OS, const STIPredicateFunction &Fn) override; - void expandEpilogue(raw_ostream &OS, const STIPredicateFunction &Fn) override; - - void expandTrue(raw_ostream &OS) override; - void expandFalse(raw_ostream &OS) override; + void expandTrue(raw_ostream &OS); + void expandFalse(raw_ostream &OS); void expandCheckImmOperand(raw_ostream &OS, int OpIndex, int ImmVal, - StringRef FunctionMapper) override; + StringRef FunctionMapper); void expandCheckImmOperand(raw_ostream &OS, int OpIndex, StringRef ImmVal, - StringRef FunctionMapperer) override; + StringRef FunctionMapperer); void expandCheckImmOperandSimple(raw_ostream &OS, int OpIndex, - StringRef FunctionMapper) override; + StringRef FunctionMapper); void expandCheckRegOperand(raw_ostream &OS, int OpIndex, const Record *Reg, - StringRef FunctionMapper) override; + StringRef FunctionMapper); void expandCheckRegOperandSimple(raw_ostream &OS, int OpIndex, - StringRef FunctionMapper) override; - void expandCheckSameRegOperand(raw_ostream &OS, int First, - int Second) override; - void expandCheckNumOperands(raw_ostream &OS, int NumOps) override; - void expandCheckOpcode(raw_ostream &OS, const Record *Inst) override; - - void expandCheckPseudo(raw_ostream &OS, const RecVec &Opcodes) override; - void expandCheckOpcode(raw_ostream &OS, const RecVec &Opcodes) override; + StringRef FunctionMapper); + void expandCheckSameRegOperand(raw_ostream &OS, int First, int Second); + void expandCheckNumOperands(raw_ostream &OS, int NumOps); + void expandCheckOpcode(raw_ostream &OS, const Record *Inst); + + void expandCheckPseudo(raw_ostream &OS, const RecVec &Opcodes); + void expandCheckOpcode(raw_ostream &OS, const RecVec &Opcodes); void expandPredicateSequence(raw_ostream &OS, const RecVec &Sequence, - bool IsCheckAll) override; - void expandTIIFunctionCall(raw_ostream &OS, StringRef MethodName) override; - void expandCheckIsRegOperand(raw_ostream &OS, int OpIndex) override; - void expandCheckIsImmOperand(raw_ostream &OS, int OpIndex) override; - void expandCheckInvalidRegOperand(raw_ostream &OS, int OpIndex) override; + bool IsCheckAll); + void expandTIIFunctionCall(raw_ostream &OS, StringRef MethodName); + void expandCheckIsRegOperand(raw_ostream &OS, int OpIndex); + void expandCheckIsImmOperand(raw_ostream &OS, int OpIndex); + void expandCheckInvalidRegOperand(raw_ostream &OS, int OpIndex); void expandCheckFunctionPredicate(raw_ostream &OS, StringRef MCInstFn, - StringRef MachineInstrFn) override; + StringRef MachineInstrFn); void expandCheckFunctionPredicateWithTII(raw_ostream &OS, StringRef MCInstFn, StringRef MachineInstrFn, - StringRef TIIPtr) override; - void expandCheckNonPortable(raw_ostream &OS, StringRef CodeBlock) override; - void expandPredicate(raw_ostream &OS, const Record *Rec) override; - void expandReturnStatement(raw_ostream &OS, const Record *Rec) override; - void expandOpcodeSwitchCase(raw_ostream &OS, const Record *Rec) override; + StringRef TIIPtr); + void expandCheckNonPortable(raw_ostream &OS, StringRef CodeBlock); + void expandPredicate(raw_ostream &OS, const Record *Rec); + void expandReturnStatement(raw_ostream &OS, const Record *Rec); + void expandOpcodeSwitchCase(raw_ostream &OS, const Record *Rec); void expandOpcodeSwitchStatement(raw_ostream &OS, const RecVec &Cases, - const Record *Default) override; - void expandStatement(raw_ostream &OS, const Record *Rec) override; - - // STI only - void expandSTIPredicate(raw_ostream &OS, - const STIPredicateFunction &Fn) override; + const Record *Default); + void expandStatement(raw_ostream &OS, const Record *Rec); }; -class PredicateExpanderCapstone : public PredicateExpander { - using PredicateExpander::PredicateExpander; +// Forward declarations. +class STIPredicateFunction; +class OpcodeGroup; + +class STIPredicateExpander : public PredicateExpander { + StringRef ClassPrefix; + bool ExpandDefinition; + + STIPredicateExpander(const PredicateExpander &) = delete; + STIPredicateExpander &operator=(const PredicateExpander &) = delete; - void expandHeader(raw_ostream &OS, const STIPredicateFunction &Fn) override; - void expandPrologue(raw_ostream &OS, const STIPredicateFunction &Fn) override; + void expandHeader(raw_ostream &OS, const STIPredicateFunction &Fn); + void expandPrologue(raw_ostream &OS, const STIPredicateFunction &Fn); void expandOpcodeGroup(raw_ostream &OS, const OpcodeGroup &Group, - bool ShouldUpdateOpcodeMask) override; - void expandBody(raw_ostream &OS, const STIPredicateFunction &Fn) override; - void expandEpilogue(raw_ostream &OS, const STIPredicateFunction &Fn) override; + bool ShouldUpdateOpcodeMask); + void expandBody(raw_ostream &OS, const STIPredicateFunction &Fn); + void expandEpilogue(raw_ostream &OS, const STIPredicateFunction &Fn); - void expandTrue(raw_ostream &OS) override; - void expandFalse(raw_ostream &OS) override; - void expandCheckImmOperand(raw_ostream &OS, int OpIndex, int ImmVal, - StringRef FunctionMapper) override; - void expandCheckImmOperand(raw_ostream &OS, int OpIndex, StringRef ImmVal, - StringRef FunctionMapperer) override; - void expandCheckImmOperandSimple(raw_ostream &OS, int OpIndex, - StringRef FunctionMapper) override; - void expandCheckRegOperand(raw_ostream &OS, int OpIndex, const Record *Reg, - StringRef FunctionMapper) override; - void expandCheckRegOperandSimple(raw_ostream &OS, int OpIndex, - StringRef FunctionMapper) override; - void expandCheckSameRegOperand(raw_ostream &OS, int First, - int Second) override; - void expandCheckNumOperands(raw_ostream &OS, int NumOps) override; - void expandCheckOpcode(raw_ostream &OS, const Record *Inst) override; - - void expandCheckPseudo(raw_ostream &OS, const RecVec &Opcodes) override; - void expandCheckOpcode(raw_ostream &OS, const RecVec &Opcodes) override; - void expandPredicateSequence(raw_ostream &OS, const RecVec &Sequence, - bool IsCheckAll) override; - void expandTIIFunctionCall(raw_ostream &OS, StringRef MethodName) override; - void expandCheckIsRegOperand(raw_ostream &OS, int OpIndex) override; - void expandCheckIsImmOperand(raw_ostream &OS, int OpIndex) override; - void expandCheckInvalidRegOperand(raw_ostream &OS, int OpIndex) override; - void expandCheckFunctionPredicate(raw_ostream &OS, StringRef MCInstFn, - StringRef MachineInstrFn) override; - void expandCheckFunctionPredicateWithTII(raw_ostream &OS, StringRef MCInstFn, - StringRef MachineInstrFn, - StringRef TIIPtr) override; - void expandCheckNonPortable(raw_ostream &OS, StringRef CodeBlock) override; - void expandPredicate(raw_ostream &OS, const Record *Rec) override; - void expandReturnStatement(raw_ostream &OS, const Record *Rec) override; - void expandOpcodeSwitchCase(raw_ostream &OS, const Record *Rec) override; - void expandOpcodeSwitchStatement(raw_ostream &OS, const RecVec &Cases, - const Record *Default) override; - void expandStatement(raw_ostream &OS, const Record *Rec) override; +public: + STIPredicateExpander(StringRef Target) + : PredicateExpander(Target), ExpandDefinition(false) {} + + bool shouldExpandDefinition() const { return ExpandDefinition; } + StringRef getClassPrefix() const { return ClassPrefix; } + void setClassPrefix(StringRef S) { ClassPrefix = S; } + void setExpandDefinition(bool Value) { ExpandDefinition = Value; } - // STI only - void expandSTIPredicate(raw_ostream &OS, - const STIPredicateFunction &Fn) override; + void expandSTIPredicate(raw_ostream &OS, const STIPredicateFunction &Fn); }; } // namespace llvm diff --git a/llvm/utils/TableGen/RegisterInfoEmitter.cpp b/llvm/utils/TableGen/RegisterInfoEmitter.cpp index 5da20e35e50b..113cebf8a08e 100644 --- a/llvm/utils/TableGen/RegisterInfoEmitter.cpp +++ b/llvm/utils/TableGen/RegisterInfoEmitter.cpp @@ -12,8 +12,35 @@ // //===----------------------------------------------------------------------===// -#include "Printer.h" -#include "RegisterInfoEmitterTypes.h" +#include "CodeGenRegisters.h" +#include "CodeGenTarget.h" +#include "SequenceToOffsetTable.h" +#include "Types.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/SparseBitVector.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/MachineValueType.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/SetTheory.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace llvm; @@ -29,53 +56,180 @@ namespace { class RegisterInfoEmitter { CodeGenTarget Target; RecordKeeper &Records; - PrinterLLVM &PI; public: - RegisterInfoEmitter(RecordKeeper &R, PrinterLLVM &PI) - : Target(R), Records(R), PI(PI) { + RegisterInfoEmitter(RecordKeeper &R) : Target(R), Records(R) { CodeGenRegBank &RegBank = Target.getRegBank(); RegBank.computeDerivedInfo(); } + // runEnums - Print out enum values for all of the registers. + void runEnums(raw_ostream &o, CodeGenTarget &Target, CodeGenRegBank &Bank); + // runMCDesc - Print out MC register descriptions. - void runMCDesc(CodeGenTarget &Target, CodeGenRegBank &Bank); + void runMCDesc(raw_ostream &o, CodeGenTarget &Target, CodeGenRegBank &Bank); // runTargetHeader - Emit a header fragment for the register info emitter. - void runTargetHeader(CodeGenTarget &Target, + void runTargetHeader(raw_ostream &o, CodeGenTarget &Target, CodeGenRegBank &Bank); // runTargetDesc - Output the target register and register file descriptions. - void runTargetDesc(CodeGenTarget &Target, + void runTargetDesc(raw_ostream &o, CodeGenTarget &Target, CodeGenRegBank &Bank); // run - Output the register file description. - void run(); + void run(raw_ostream &o); void debugDump(raw_ostream &OS); private: - void EmitRegMapping(const std::deque &Regs, + void EmitRegMapping(raw_ostream &o, const std::deque &Regs, bool isCtor); - void EmitRegMappingTables(const std::deque &Regs, + void EmitRegMappingTables(raw_ostream &o, + const std::deque &Regs, bool isCtor); - void EmitRegUnitPressure(const CodeGenRegBank &RegBank, + void EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank, const std::string &ClassName); - void emitComposeSubRegIndices(CodeGenRegBank &RegBank, + void emitComposeSubRegIndices(raw_ostream &OS, CodeGenRegBank &RegBank, const std::string &ClassName); - void emitComposeSubRegIndexLaneMask(CodeGenRegBank &RegBank, + void emitComposeSubRegIndexLaneMask(raw_ostream &OS, CodeGenRegBank &RegBank, const std::string &ClassName); }; } // end anonymous namespace +// runEnums - Print out enum values for all of the registers. +void RegisterInfoEmitter::runEnums(raw_ostream &OS, + CodeGenTarget &Target, CodeGenRegBank &Bank) { + const auto &Registers = Bank.getRegisters(); + + // Register enums are stored as uint16_t in the tables. Make sure we'll fit. + assert(Registers.size() <= 0xffff && "Too many regs to fit in tables"); + + StringRef Namespace = Registers.front().TheDef->getValueAsString("Namespace"); + + emitSourceFileHeader("Target Register Enum Values", OS); + + OS << "\n#ifdef GET_REGINFO_ENUM\n"; + OS << "#undef GET_REGINFO_ENUM\n\n"; + + OS << "namespace llvm {\n\n"; + + OS << "class MCRegisterClass;\n" + << "extern const MCRegisterClass " << Target.getName() + << "MCRegisterClasses[];\n\n"; + + if (!Namespace.empty()) + OS << "namespace " << Namespace << " {\n"; + OS << "enum {\n NoRegister,\n"; + + for (const auto &Reg : Registers) + OS << " " << Reg.getName() << " = " << Reg.EnumValue << ",\n"; + assert(Registers.size() == Registers.back().EnumValue && + "Register enum value mismatch!"); + OS << " NUM_TARGET_REGS // " << Registers.size()+1 << "\n"; + OS << "};\n"; + if (!Namespace.empty()) + OS << "} // end namespace " << Namespace << "\n"; + + const auto &RegisterClasses = Bank.getRegClasses(); + if (!RegisterClasses.empty()) { + + // RegisterClass enums are stored as uint16_t in the tables. + assert(RegisterClasses.size() <= 0xffff && + "Too many register classes to fit in tables"); + + OS << "\n// Register classes\n\n"; + if (!Namespace.empty()) + OS << "namespace " << Namespace << " {\n"; + OS << "enum {\n"; + for (const auto &RC : RegisterClasses) + OS << " " << RC.getName() << "RegClassID" + << " = " << RC.EnumValue << ",\n"; + OS << "\n};\n"; + if (!Namespace.empty()) + OS << "} // end namespace " << Namespace << "\n\n"; + } + + const std::vector &RegAltNameIndices = Target.getRegAltNameIndices(); + // If the only definition is the default NoRegAltName, we don't need to + // emit anything. + if (RegAltNameIndices.size() > 1) { + OS << "\n// Register alternate name indices\n\n"; + if (!Namespace.empty()) + OS << "namespace " << Namespace << " {\n"; + OS << "enum {\n"; + for (unsigned i = 0, e = RegAltNameIndices.size(); i != e; ++i) + OS << " " << RegAltNameIndices[i]->getName() << ",\t// " << i << "\n"; + OS << " NUM_TARGET_REG_ALT_NAMES = " << RegAltNameIndices.size() << "\n"; + OS << "};\n"; + if (!Namespace.empty()) + OS << "} // end namespace " << Namespace << "\n\n"; + } + + auto &SubRegIndices = Bank.getSubRegIndices(); + if (!SubRegIndices.empty()) { + OS << "\n// Subregister indices\n\n"; + std::string Namespace = SubRegIndices.front().getNamespace(); + if (!Namespace.empty()) + OS << "namespace " << Namespace << " {\n"; + OS << "enum : uint16_t {\n NoSubRegister,\n"; + unsigned i = 0; + for (const auto &Idx : SubRegIndices) + OS << " " << Idx.getName() << ",\t// " << ++i << "\n"; + OS << " NUM_TARGET_SUBREGS\n};\n"; + if (!Namespace.empty()) + OS << "} // end namespace " << Namespace << "\n\n"; + } + + OS << "// Register pressure sets enum.\n"; + if (!Namespace.empty()) + OS << "namespace " << Namespace << " {\n"; + OS << "enum RegisterPressureSets {\n"; + unsigned NumSets = Bank.getNumRegPressureSets(); + for (unsigned i = 0; i < NumSets; ++i ) { + const RegUnitSet &RegUnits = Bank.getRegSetAt(i); + OS << " " << RegUnits.Name << " = " << i << ",\n"; + } + OS << "};\n"; + if (!Namespace.empty()) + OS << "} // end namespace " << Namespace << '\n'; + OS << '\n'; + + OS << "} // end namespace llvm\n\n"; + OS << "#endif // GET_REGINFO_ENUM\n\n"; +} + +static void printInt(raw_ostream &OS, int Val) { + OS << Val; +} + void RegisterInfoEmitter:: -EmitRegUnitPressure(const CodeGenRegBank &RegBank, +EmitRegUnitPressure(raw_ostream &OS, const CodeGenRegBank &RegBank, const std::string &ClassName) { unsigned NumRCs = RegBank.getRegClasses().size(); unsigned NumSets = RegBank.getNumRegPressureSets(); - PI.regInfoEmitRegClassWeight(RegBank, ClassName); + OS << "/// Get the weight in units of pressure for this register class.\n" + << "const RegClassWeight &" << ClassName << "::\n" + << "getRegClassWeight(const TargetRegisterClass *RC) const {\n" + << " static const RegClassWeight RCWeightTable[] = {\n"; + for (const auto &RC : RegBank.getRegClasses()) { + const CodeGenRegister::Vec &Regs = RC.getMembers(); + OS << " {" << RC.getWeight(RegBank) << ", "; + if (Regs.empty() || RC.Artificial) + OS << '0'; + else { + std::vector RegUnits; + RC.buildRegUnitSet(RegBank, RegUnits); + OS << RegBank.getRegUnitSetWeight(RegUnits); + } + OS << "}, \t// " << RC.getName() << "\n"; + } + OS << " };\n" + << " return RCWeightTable[RC->getID()];\n" + << "}\n\n"; + // Reasonable targets (not ARMv7) have unit weight for all units, so don't // bother generating a table. bool RegUnitsHaveUnitWeight = true; @@ -84,11 +238,64 @@ EmitRegUnitPressure(const CodeGenRegBank &RegBank, if (RegBank.getRegUnit(UnitIdx).Weight > 1) RegUnitsHaveUnitWeight = false; } - PI.regInfoEmitRegUnitWeight(RegBank, ClassName, RegUnitsHaveUnitWeight); - PI.regInfoEmitGetNumRegPressureSets(ClassName, NumSets); - PI.regInfoEmitGetRegPressureTables(RegBank, ClassName, NumSets); + OS << "/// Get the weight in units of pressure for this register unit.\n" + << "unsigned " << ClassName << "::\n" + << "getRegUnitWeight(unsigned RegUnit) const {\n" + << " assert(RegUnit < " << RegBank.getNumNativeRegUnits() + << " && \"invalid register unit\");\n"; + if (!RegUnitsHaveUnitWeight) { + OS << " static const uint8_t RUWeightTable[] = {\n "; + for (unsigned UnitIdx = 0, UnitEnd = RegBank.getNumNativeRegUnits(); + UnitIdx < UnitEnd; ++UnitIdx) { + const RegUnit &RU = RegBank.getRegUnit(UnitIdx); + assert(RU.Weight < 256 && "RegUnit too heavy"); + OS << RU.Weight << ", "; + } + OS << "};\n" + << " return RUWeightTable[RegUnit];\n"; + } + else { + OS << " // All register units have unit weight.\n" + << " return 1;\n"; + } + OS << "}\n\n"; + + OS << "\n" + << "// Get the number of dimensions of register pressure.\n" + << "unsigned " << ClassName << "::getNumRegPressureSets() const {\n" + << " return " << NumSets << ";\n}\n\n"; + + OS << "// Get the name of this register unit pressure set.\n" + << "const char *" << ClassName << "::\n" + << "getRegPressureSetName(unsigned Idx) const {\n" + << " static const char *PressureNameTable[] = {\n"; + unsigned MaxRegUnitWeight = 0; + for (unsigned i = 0; i < NumSets; ++i ) { + const RegUnitSet &RegUnits = RegBank.getRegSetAt(i); + MaxRegUnitWeight = std::max(MaxRegUnitWeight, RegUnits.Weight); + OS << " \"" << RegUnits.Name << "\",\n"; + } + OS << " };\n" + << " return PressureNameTable[Idx];\n" + << "}\n\n"; + + OS << "// Get the register unit pressure limit for this dimension.\n" + << "// This limit must be adjusted dynamically for reserved registers.\n" + << "unsigned " << ClassName << "::\n" + << "getRegPressureSetLimit(const MachineFunction &MF, unsigned Idx) const " + "{\n" + << " static const " << getMinimalTypeForRange(MaxRegUnitWeight, 32) + << " PressureLimitTable[] = {\n"; + for (unsigned i = 0; i < NumSets; ++i ) { + const RegUnitSet &RegUnits = RegBank.getRegSetAt(i); + OS << " " << RegUnits.Weight << ", \t// " << i << ": " + << RegUnits.Name << "\n"; + } + OS << " };\n" + << " return PressureLimitTable[Idx];\n" + << "}\n\n"; - SequenceToOffsetTable> PSetsSeqs(PI.getLanguage()); + SequenceToOffsetTable> PSetsSeqs; // This table may be larger than NumRCs if some register units needed a list // of unit sets that did not correspond to a register class. @@ -106,11 +313,48 @@ EmitRegUnitPressure(const CodeGenRegBank &RegBank, } PSetsSeqs.layout(); - PI.regInfoEmitRCSetsTable(ClassName, NumRCs, PSetsSeqs, PSets); - PI.regInfoEmitGetRegUnitPressureSets(PSetsSeqs, RegBank, - ClassName, PSets); + + OS << "/// Table of pressure sets per register class or unit.\n" + << "static const int RCSetsTable[] = {\n"; + PSetsSeqs.emit(OS, printInt, "-1"); + OS << "};\n\n"; + + OS << "/// Get the dimensions of register pressure impacted by this " + << "register class.\n" + << "/// Returns a -1 terminated array of pressure set IDs\n" + << "const int *" << ClassName << "::\n" + << "getRegClassPressureSets(const TargetRegisterClass *RC) const {\n"; + OS << " static const " << getMinimalTypeForRange(PSetsSeqs.size() - 1, 32) + << " RCSetStartTable[] = {\n "; + for (unsigned i = 0, e = NumRCs; i != e; ++i) { + OS << PSetsSeqs.get(PSets[i]) << ","; + } + OS << "};\n" + << " return &RCSetsTable[RCSetStartTable[RC->getID()]];\n" + << "}\n\n"; + + OS << "/// Get the dimensions of register pressure impacted by this " + << "register unit.\n" + << "/// Returns a -1 terminated array of pressure set IDs\n" + << "const int *" << ClassName << "::\n" + << "getRegUnitPressureSets(unsigned RegUnit) const {\n" + << " assert(RegUnit < " << RegBank.getNumNativeRegUnits() + << " && \"invalid register unit\");\n"; + OS << " static const " << getMinimalTypeForRange(PSetsSeqs.size() - 1, 32) + << " RUSetStartTable[] = {\n "; + for (unsigned UnitIdx = 0, UnitEnd = RegBank.getNumNativeRegUnits(); + UnitIdx < UnitEnd; ++UnitIdx) { + OS << PSetsSeqs.get(PSets[RegBank.getRegUnit(UnitIdx).RegClassUnitSetsIdx]) + << ","; + } + OS << "};\n" + << " return &RCSetsTable[RUSetStartTable[RegUnit]];\n" + << "}\n\n"; } +using DwarfRegNumsMapPair = std::pair>; +using DwarfRegNumsVecTy = std::vector; + static void finalizeDwarfRegNumsKeys(DwarfRegNumsVecTy &DwarfRegNums) { // Sort and unique to get a map-like vector. We want the last assignment to // match previous behaviour. @@ -136,7 +380,7 @@ static void finalizeDwarfRegNumsKeys(DwarfRegNumsVecTy &DwarfRegNums) { } void RegisterInfoEmitter::EmitRegMappingTables( - const std::deque &Regs, bool isCtor) { + raw_ostream &OS, const std::deque &Regs, bool isCtor) { // Collect all information about dwarf register numbers DwarfRegNumsVecTy DwarfRegNums; @@ -159,7 +403,49 @@ void RegisterInfoEmitter::EmitRegMappingTables( DwarfRegNum.second.push_back(-1); StringRef Namespace = Regs.front().TheDef->getValueAsString("Namespace"); - PI.regInfoEmitInfoDwarfRegsRev(Namespace, DwarfRegNums, maxLength, isCtor); + + OS << "// " << Namespace << " Dwarf<->LLVM register mappings.\n"; + + // Emit reverse information about the dwarf register numbers. + for (unsigned j = 0; j < 2; ++j) { + for (unsigned I = 0, E = maxLength; I != E; ++I) { + OS << "extern const MCRegisterInfo::DwarfLLVMRegPair " << Namespace; + OS << (j == 0 ? "DwarfFlavour" : "EHFlavour"); + OS << I << "Dwarf2L[]"; + + if (!isCtor) { + OS << " = {\n"; + + // Store the mapping sorted by the LLVM reg num so lookup can be done + // with a binary search. + std::map Dwarf2LMap; + for (auto &DwarfRegNum : DwarfRegNums) { + int DwarfRegNo = DwarfRegNum.second[I]; + if (DwarfRegNo < 0) + continue; + Dwarf2LMap[DwarfRegNo] = DwarfRegNum.first; + } + + for (auto &I : Dwarf2LMap) + OS << " { " << I.first << "U, " << getQualifiedName(I.second) + << " },\n"; + + OS << "};\n"; + } else { + OS << ";\n"; + } + + // We have to store the size in a const global, it's used in multiple + // places. + OS << "extern const unsigned " << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << I << "Dwarf2LSize"; + if (!isCtor) + OS << " = std::size(" << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << I << "Dwarf2L);\n\n"; + else + OS << ";\n\n"; + } + } for (auto &RE : Regs) { Record *Reg = RE.TheDef; @@ -185,11 +471,43 @@ void RegisterInfoEmitter::EmitRegMappingTables( } // Emit information about the dwarf register numbers. - PI.regInfoEmitInfoDwarfRegs(Namespace, DwarfRegNums, maxLength, isCtor); + for (unsigned j = 0; j < 2; ++j) { + for (unsigned i = 0, e = maxLength; i != e; ++i) { + OS << "extern const MCRegisterInfo::DwarfLLVMRegPair " << Namespace; + OS << (j == 0 ? "DwarfFlavour" : "EHFlavour"); + OS << i << "L2Dwarf[]"; + if (!isCtor) { + OS << " = {\n"; + // Store the mapping sorted by the Dwarf reg num so lookup can be done + // with a binary search. + for (auto &DwarfRegNum : DwarfRegNums) { + int RegNo = DwarfRegNum.second[i]; + if (RegNo == -1) // -1 is the default value, don't emit a mapping. + continue; + + OS << " { " << getQualifiedName(DwarfRegNum.first) << ", " << RegNo + << "U },\n"; + } + OS << "};\n"; + } else { + OS << ";\n"; + } + + // We have to store the size in a const global, it's used in multiple + // places. + OS << "extern const unsigned " << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i << "L2DwarfSize"; + if (!isCtor) + OS << " = std::size(" << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i << "L2Dwarf);\n\n"; + else + OS << ";\n\n"; + } + } } void RegisterInfoEmitter::EmitRegMapping( - const std::deque &Regs, bool isCtor) { + raw_ostream &OS, const std::deque &Regs, bool isCtor) { // Emit the initializer so the tables from EmitRegMappingTables get wired up // to the MCRegisterInfo object. unsigned maxLength = 0; @@ -199,10 +517,125 @@ void RegisterInfoEmitter::EmitRegMapping( Reg->getValueAsListOfInts("DwarfNumbers").size()); } + if (!maxLength) + return; + StringRef Namespace = Regs.front().TheDef->getValueAsString("Namespace"); - PI.regInfoEmitInfoRegMapping(Namespace, maxLength, isCtor); + + // Emit reverse information about the dwarf register numbers. + for (unsigned j = 0; j < 2; ++j) { + OS << " switch ("; + if (j == 0) + OS << "DwarfFlavour"; + else + OS << "EHFlavour"; + OS << ") {\n" + << " default:\n" + << " llvm_unreachable(\"Unknown DWARF flavour\");\n"; + + for (unsigned i = 0, e = maxLength; i != e; ++i) { + OS << " case " << i << ":\n"; + OS << " "; + if (!isCtor) + OS << "RI->"; + std::string Tmp; + raw_string_ostream(Tmp) << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i + << "Dwarf2L"; + OS << "mapDwarfRegsToLLVMRegs(" << Tmp << ", " << Tmp << "Size, "; + if (j == 0) + OS << "false"; + else + OS << "true"; + OS << ");\n"; + OS << " break;\n"; + } + OS << " }\n"; + } + + // Emit information about the dwarf register numbers. + for (unsigned j = 0; j < 2; ++j) { + OS << " switch ("; + if (j == 0) + OS << "DwarfFlavour"; + else + OS << "EHFlavour"; + OS << ") {\n" + << " default:\n" + << " llvm_unreachable(\"Unknown DWARF flavour\");\n"; + + for (unsigned i = 0, e = maxLength; i != e; ++i) { + OS << " case " << i << ":\n"; + OS << " "; + if (!isCtor) + OS << "RI->"; + std::string Tmp; + raw_string_ostream(Tmp) << Namespace + << (j == 0 ? "DwarfFlavour" : "EHFlavour") << i + << "L2Dwarf"; + OS << "mapLLVMRegsToDwarfRegs(" << Tmp << ", " << Tmp << "Size, "; + if (j == 0) + OS << "false"; + else + OS << "true"; + OS << ");\n"; + OS << " break;\n"; + } + OS << " }\n"; + } } +// Print a BitVector as a sequence of hex numbers using a little-endian mapping. +// Width is the number of bits per hex number. +static void printBitVectorAsHex(raw_ostream &OS, + const BitVector &Bits, + unsigned Width) { + assert(Width <= 32 && "Width too large"); + unsigned Digits = (Width + 3) / 4; + for (unsigned i = 0, e = Bits.size(); i < e; i += Width) { + unsigned Value = 0; + for (unsigned j = 0; j != Width && i + j != e; ++j) + Value |= Bits.test(i + j) << j; + OS << format("0x%0*x, ", Digits, Value); + } +} + +// Helper to emit a set of bits into a constant byte array. +class BitVectorEmitter { + BitVector Values; +public: + void add(unsigned v) { + if (v >= Values.size()) + Values.resize(((v/8)+1)*8); // Round up to the next byte. + Values[v] = true; + } + + void print(raw_ostream &OS) { + printBitVectorAsHex(OS, Values, 8); + } +}; + +static void printSimpleValueType(raw_ostream &OS, MVT::SimpleValueType VT) { + OS << getEnumName(VT); +} + +static void printSubRegIndex(raw_ostream &OS, const CodeGenSubRegIndex *Idx) { + OS << Idx->EnumValue; +} + +// Differentially encoded register and regunit lists allow for better +// compression on regular register banks. The sequence is computed from the +// differential list as: +// +// out[0] = InitVal; +// out[n+1] = out[n] + diff[n]; // n = 0, 1, ... +// +// The initial value depends on the specific list. The list is terminated by a +// 0 differential which means we can't encode repeated elements. + +typedef SmallVector DiffVec; +typedef SmallVector MaskVec; + // Differentially encode a sequence of numbers into V. The starting value and // terminating 0 are not added to V, so it will have the same size as List. static @@ -230,6 +663,14 @@ DiffVec &diffEncode(DiffVec &V, unsigned InitVal, Iter Begin, Iter End) { return V; } +static void printDiff16(raw_ostream &OS, uint16_t Val) { + OS << Val; +} + +static void printMask(raw_ostream &OS, LaneBitmask Val) { + OS << "LaneBitmask(0x" << PrintLaneMask(Val) << ')'; +} + // Try to combine Idx's compose map into Vec if it is compatible. // Return false if it's not possible. static bool combine(const CodeGenSubRegIndex *Idx, @@ -252,10 +693,13 @@ static bool combine(const CodeGenSubRegIndex *Idx, } void -RegisterInfoEmitter::emitComposeSubRegIndices(CodeGenRegBank &RegBank, +RegisterInfoEmitter::emitComposeSubRegIndices(raw_ostream &OS, + CodeGenRegBank &RegBank, const std::string &ClName) { const auto &SubRegIndices = RegBank.getSubRegIndices(); - PI.regInfoEmitComposeSubRegIndicesImplHead(ClName); + OS << "unsigned " << ClName + << "::composeSubRegIndicesImpl(unsigned IdxA, unsigned IdxB) const {\n"; + // Many sub-register indexes are composition-compatible, meaning that // // compose(IdxA, IdxB) == compose(IdxA', IdxB) @@ -287,11 +731,40 @@ RegisterInfoEmitter::emitComposeSubRegIndices(CodeGenRegBank &RegBank, } // Output the row map if there is multiple rows. - PI.regInfoEmitComposeSubRegIndicesImplBody(Rows, SubRegIndicesSize, RowMap); + if (Rows.size() > 1) { + OS << " static const " << getMinimalTypeForRange(Rows.size(), 32) + << " RowMap[" << SubRegIndicesSize << "] = {\n "; + for (unsigned i = 0, e = SubRegIndicesSize; i != e; ++i) + OS << RowMap[i] << ", "; + OS << "\n };\n"; + } + + // Output the rows. + OS << " static const " << getMinimalTypeForRange(SubRegIndicesSize + 1, 32) + << " Rows[" << Rows.size() << "][" << SubRegIndicesSize << "] = {\n"; + for (unsigned r = 0, re = Rows.size(); r != re; ++r) { + OS << " { "; + for (unsigned i = 0, e = SubRegIndicesSize; i != e; ++i) + if (Rows[r][i]) + OS << Rows[r][i]->getQualifiedName() << ", "; + else + OS << "0, "; + OS << "},\n"; + } + OS << " };\n\n"; + + OS << " --IdxA; assert(IdxA < " << SubRegIndicesSize << "); (void) IdxA;\n" + << " --IdxB; assert(IdxB < " << SubRegIndicesSize << ");\n"; + if (Rows.size() > 1) + OS << " return Rows[RowMap[IdxA]][IdxB];\n"; + else + OS << " return Rows[0][IdxB];\n"; + OS << "}\n\n"; } void -RegisterInfoEmitter::emitComposeSubRegIndexLaneMask(CodeGenRegBank &RegBank, +RegisterInfoEmitter::emitComposeSubRegIndexLaneMask(raw_ostream &OS, + CodeGenRegBank &RegBank, const std::string &ClName) { // See the comments in computeSubRegLaneMasks() for our goal here. const auto &SubRegIndices = RegBank.getSubRegIndices(); @@ -321,39 +794,114 @@ RegisterInfoEmitter::emitComposeSubRegIndexLaneMask(CodeGenRegBank &RegBank, SubReg2SequenceIndexMap.push_back(Found); } - PI.regInfoEmitLaneMaskComposeSeq(Sequences, SubReg2SequenceIndexMap, SubRegIndices); - PI.regInfoEmitComposeSubRegIdxLaneMask(ClName, SubRegIndices); - PI.regInfoEmitComposeSubRegIdxLaneMaskRev(ClName, SubRegIndices); - + OS << " struct MaskRolOp {\n" + " LaneBitmask Mask;\n" + " uint8_t RotateLeft;\n" + " };\n" + " static const MaskRolOp LaneMaskComposeSequences[] = {\n"; + unsigned Idx = 0; + for (size_t s = 0, se = Sequences.size(); s != se; ++s) { + OS << " "; + const SmallVectorImpl &Sequence = Sequences[s]; + for (size_t p = 0, pe = Sequence.size(); p != pe; ++p) { + const MaskRolPair &P = Sequence[p]; + printMask(OS << "{ ", P.Mask); + OS << format(", %2u }, ", P.RotateLeft); + } + OS << "{ LaneBitmask::getNone(), 0 }"; + if (s+1 != se) + OS << ", "; + OS << " // Sequence " << Idx << "\n"; + Idx += Sequence.size() + 1; + } + auto *IntType = getMinimalTypeForRange(*std::max_element( + SubReg2SequenceIndexMap.begin(), SubReg2SequenceIndexMap.end())); + OS << " };\n" + " static const " + << IntType << " CompositeSequences[] = {\n"; + for (size_t i = 0, e = SubRegIndices.size(); i != e; ++i) { + OS << " "; + OS << SubReg2SequenceIndexMap[i]; + if (i+1 != e) + OS << ","; + OS << " // to " << SubRegIndices[i].getName() << "\n"; + } + OS << " };\n\n"; + + OS << "LaneBitmask " << ClName + << "::composeSubRegIndexLaneMaskImpl(unsigned IdxA, LaneBitmask LaneMask)" + " const {\n" + " --IdxA; assert(IdxA < " << SubRegIndices.size() + << " && \"Subregister index out of bounds\");\n" + " LaneBitmask Result;\n" + " for (const MaskRolOp *Ops =\n" + " &LaneMaskComposeSequences[CompositeSequences[IdxA]];\n" + " Ops->Mask.any(); ++Ops) {\n" + " LaneBitmask::Type M = LaneMask.getAsInteger() & Ops->Mask.getAsInteger();\n" + " if (unsigned S = Ops->RotateLeft)\n" + " Result |= LaneBitmask((M << S) | (M >> (LaneBitmask::BitWidth - S)));\n" + " else\n" + " Result |= LaneBitmask(M);\n" + " }\n" + " return Result;\n" + "}\n\n"; + + OS << "LaneBitmask " << ClName + << "::reverseComposeSubRegIndexLaneMaskImpl(unsigned IdxA, " + " LaneBitmask LaneMask) const {\n" + " LaneMask &= getSubRegIndexLaneMask(IdxA);\n" + " --IdxA; assert(IdxA < " << SubRegIndices.size() + << " && \"Subregister index out of bounds\");\n" + " LaneBitmask Result;\n" + " for (const MaskRolOp *Ops =\n" + " &LaneMaskComposeSequences[CompositeSequences[IdxA]];\n" + " Ops->Mask.any(); ++Ops) {\n" + " LaneBitmask::Type M = LaneMask.getAsInteger();\n" + " if (unsigned S = Ops->RotateLeft)\n" + " Result |= LaneBitmask((M >> S) | (M << (LaneBitmask::BitWidth - S)));\n" + " else\n" + " Result |= LaneBitmask(M);\n" + " }\n" + " return Result;\n" + "}\n\n"; } // // runMCDesc - Print out MC register descriptions. // void -RegisterInfoEmitter::runMCDesc(CodeGenTarget &Target, +RegisterInfoEmitter::runMCDesc(raw_ostream &OS, CodeGenTarget &Target, CodeGenRegBank &RegBank) { - PI.regInfoEmitSourceFileHeader("MC Register Information"); + emitSourceFileHeader("MC Register Information", OS); + + OS << "\n#ifdef GET_REGINFO_MC_DESC\n"; + OS << "#undef GET_REGINFO_MC_DESC\n\n"; const auto &Regs = RegBank.getRegisters(); auto &SubRegIndices = RegBank.getSubRegIndices(); + // The lists of sub-registers and super-registers go in the same array. That + // allows us to share suffixes. + typedef std::vector RegVec; // Differentially encoded lists. - SequenceToOffsetTable DiffSeqs(PI.getLanguage()); + SequenceToOffsetTable DiffSeqs; SmallVector SubRegLists(Regs.size()); SmallVector SuperRegLists(Regs.size()); SmallVector RegUnitLists(Regs.size()); SmallVector RegUnitInitScale(Regs.size()); // List of lane masks accompanying register unit sequences. - SequenceToOffsetTable LaneMaskSeqs(PI.getLanguage()); + SequenceToOffsetTable LaneMaskSeqs; SmallVector RegUnitLaneMasks(Regs.size()); - SequenceToOffsetTable>> SubRegIdxSeqs(PI.getLanguage()); + // Keep track of sub-register names as well. These are not differentially + // encoded. + typedef SmallVector SubRegIdxVec; + SequenceToOffsetTable>> SubRegIdxSeqs; SmallVector SubRegIdxLists(Regs.size()); - SequenceToOffsetTable RegStrings(PI.getLanguage()); + SequenceToOffsetTable RegStrings; // Precompute register lists for the SequenceToOffsetTable. unsigned i = 0; @@ -423,106 +971,271 @@ RegisterInfoEmitter::runMCDesc(CodeGenTarget &Target, LaneMaskSeqs.layout(); SubRegIdxSeqs.layout(); - PI.emitIncludeToggle("GET_REGINFO_MC_DESC", true); - PI.emitNamespace("llvm", true); + OS << "namespace llvm {\n\n"; const std::string &TargetName = std::string(Target.getName()); // Emit the shared table of differential lists. - PI.regInfoEmitRegDiffLists(TargetName, DiffSeqs); + OS << "extern const MCPhysReg " << TargetName << "RegDiffLists[] = {\n"; + DiffSeqs.emit(OS, printDiff16); + OS << "};\n\n"; // Emit the shared table of regunit lane mask sequences. - PI.regInfoEmitLaneMaskLists(TargetName, LaneMaskSeqs); + OS << "extern const LaneBitmask " << TargetName << "LaneMaskLists[] = {\n"; + LaneMaskSeqs.emit(OS, printMask, "LaneBitmask::getAll()"); + OS << "};\n\n"; // Emit the table of sub-register indexes. - PI.regInfoEmitSubRegIdxLists(TargetName, SubRegIdxSeqs); + OS << "extern const uint16_t " << TargetName << "SubRegIdxLists[] = {\n"; + SubRegIdxSeqs.emit(OS, printSubRegIndex); + OS << "};\n\n"; // Emit the table of sub-register index sizes. - PI.regInfoEmitSubRegIdxSizes(TargetName, SubRegIndices); + OS << "extern const MCRegisterInfo::SubRegCoveredBits " + << TargetName << "SubRegIdxRanges[] = {\n"; + OS << " { " << (uint16_t)-1 << ", " << (uint16_t)-1 << " },\n"; + for (const auto &Idx : SubRegIndices) { + OS << " { " << Idx.Offset << ", " << Idx.Size << " },\t// " + << Idx.getName() << "\n"; + } + OS << "};\n\n"; // Emit the string table. RegStrings.layout(); - PI.regInfoEmitSubRegStrTable(TargetName, RegStrings); + RegStrings.emitStringLiteralDef(OS, Twine("extern const char ") + TargetName + + "RegStrings[]"); + + OS << "extern const MCRegisterDesc " << TargetName + << "RegDesc[] = { // Descriptors\n"; + OS << " { " << RegStrings.get("") << ", 0, 0, 0, 0, 0 },\n"; // Emit the register descriptors now. - PI.regInfoEmitRegDesc(LaneMaskSeqs, - Regs, SubRegIdxSeqs, DiffSeqs, - SubRegIdxLists, SubRegLists, - SuperRegLists, RegUnitLists, - RegUnitInitScale, RegUnitLaneMasks, - RegStrings); + i = 0; + for (const auto &Reg : Regs) { + OS << " { " << RegStrings.get(std::string(Reg.getName())) << ", " + << DiffSeqs.get(SubRegLists[i]) << ", " << DiffSeqs.get(SuperRegLists[i]) + << ", " << SubRegIdxSeqs.get(SubRegIdxLists[i]) << ", " + << (DiffSeqs.get(RegUnitLists[i]) * 16 + RegUnitInitScale[i]) << ", " + << LaneMaskSeqs.get(RegUnitLaneMasks[i]) << " },\n"; + ++i; + } + OS << "};\n\n"; // End of register descriptors... // Emit the table of register unit roots. Each regunit has one or two root // registers. - PI.regInfoEmitRegUnitRoots(TargetName, RegBank); + OS << "extern const MCPhysReg " << TargetName << "RegUnitRoots[][2] = {\n"; + for (unsigned i = 0, e = RegBank.getNumNativeRegUnits(); i != e; ++i) { + ArrayRef Roots = RegBank.getRegUnit(i).getRoots(); + assert(!Roots.empty() && "All regunits must have a root register."); + assert(Roots.size() <= 2 && "More than two roots not supported yet."); + OS << " { "; + ListSeparator LS; + for (const CodeGenRegister *R : Roots) + OS << LS << getQualifiedName(R->TheDef); + OS << " },\n"; + } + OS << "};\n\n"; const auto &RegisterClasses = RegBank.getRegClasses(); // Loop over all of the register classes... emitting each one. - PI.emitNamespace("", true, "Register classes..."); + OS << "namespace { // Register classes...\n"; - SequenceToOffsetTable RegClassStrings(PI.getLanguage()); + SequenceToOffsetTable RegClassStrings; // Emit the register enum value arrays for each RegisterClass - PI.regInfoEmitRegClasses(RegisterClasses, RegClassStrings, Target); - PI.emitNamespace("", false); + for (const auto &RC : RegisterClasses) { + ArrayRef Order = RC.getOrder(); + + // Give the register class a legal C name if it's anonymous. + const std::string &Name = RC.getName(); + + RegClassStrings.add(Name); + + // Emit the register list now (unless it would be a zero-length array). + if (!Order.empty()) { + OS << " // " << Name << " Register Class...\n" + << " const MCPhysReg " << Name << "[] = {\n "; + for (Record *Reg : Order) { + OS << getQualifiedName(Reg) << ", "; + } + OS << "\n };\n\n"; + + OS << " // " << Name << " Bit set.\n" + << " const uint8_t " << Name << "Bits[] = {\n "; + BitVectorEmitter BVE; + for (Record *Reg : Order) { + BVE.add(Target.getRegBank().getReg(Reg)->EnumValue); + } + BVE.print(OS); + OS << "\n };\n\n"; + } + } + OS << "} // end anonymous namespace\n\n"; RegClassStrings.layout(); - PI.regInfoEmitStrLiteralRegClasses(TargetName, RegClassStrings); + RegClassStrings.emitStringLiteralDef( + OS, Twine("extern const char ") + TargetName + "RegClassStrings[]"); - PI.regInfoEmitMCRegClassesTable(TargetName, RegisterClasses, RegClassStrings); + OS << "extern const MCRegisterClass " << TargetName + << "MCRegisterClasses[] = {\n"; - EmitRegMappingTables(Regs, false); + for (const auto &RC : RegisterClasses) { + ArrayRef Order = RC.getOrder(); + std::string RCName = Order.empty() ? "nullptr" : RC.getName(); + std::string RCBitsName = Order.empty() ? "nullptr" : RC.getName() + "Bits"; + std::string RCBitsSize = Order.empty() ? "0" : "sizeof(" + RCBitsName + ")"; + assert(isInt<8>(RC.CopyCost) && "Copy cost too large."); + uint32_t RegSize = 0; + if (RC.RSI.isSimple()) + RegSize = RC.RSI.getSimple().RegSize; + OS << " { " << RCName << ", " << RCBitsName << ", " + << RegClassStrings.get(RC.getName()) << ", " << RC.getOrder().size() + << ", " << RCBitsSize << ", " << RC.getQualifiedName() + "RegClassID" + << ", " << RegSize << ", " << RC.CopyCost << ", " + << (RC.Allocatable ? "true" : "false") << " },\n"; + } - // Emit Reg encoding table - PI.regInfoEmitRegEncodingTable(TargetName, Regs); + OS << "};\n\n"; - // MCRegisterInfo initialization routine. - PI.regInfoEmitMCRegInfoInit(TargetName, RegBank, Regs, RegisterClasses, SubRegIndices); + EmitRegMappingTables(OS, Regs, false); - EmitRegMapping(Regs, false); + // Emit Reg encoding table + OS << "extern const uint16_t " << TargetName; + OS << "RegEncodingTable[] = {\n"; + // Add entry for NoRegister + OS << " 0,\n"; + for (const auto &RE : Regs) { + Record *Reg = RE.TheDef; + BitsInit *BI = Reg->getValueAsBitsInit("HWEncoding"); + uint64_t Value = 0; + for (unsigned b = 0, be = BI->getNumBits(); b != be; ++b) { + if (BitInit *B = dyn_cast(BI->getBit(b))) + Value |= (uint64_t)B->getValue() << b; + } + OS << " " << Value << ",\n"; + } + OS << "};\n"; // End of HW encoding table - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_REGINFO_MC_DESC", false); + // MCRegisterInfo initialization routine. + OS << "static inline void Init" << TargetName + << "MCRegisterInfo(MCRegisterInfo *RI, unsigned RA, " + << "unsigned DwarfFlavour = 0, unsigned EHFlavour = 0, unsigned PC = 0) " + "{\n" + << " RI->InitMCRegisterInfo(" << TargetName << "RegDesc, " + << Regs.size() + 1 << ", RA, PC, " << TargetName << "MCRegisterClasses, " + << RegisterClasses.size() << ", " << TargetName << "RegUnitRoots, " + << RegBank.getNumNativeRegUnits() << ", " << TargetName << "RegDiffLists, " + << TargetName << "LaneMaskLists, " << TargetName << "RegStrings, " + << TargetName << "RegClassStrings, " << TargetName << "SubRegIdxLists, " + << (std::distance(SubRegIndices.begin(), SubRegIndices.end()) + 1) << ",\n" + << TargetName << "SubRegIdxRanges, " << TargetName + << "RegEncodingTable);\n\n"; + + EmitRegMapping(OS, Regs, false); + + OS << "}\n\n"; + + OS << "} // end namespace llvm\n\n"; + OS << "#endif // GET_REGINFO_MC_DESC\n\n"; } void -RegisterInfoEmitter::runTargetHeader(CodeGenTarget &Target, +RegisterInfoEmitter::runTargetHeader(raw_ostream &OS, CodeGenTarget &Target, CodeGenRegBank &RegBank) { - PI.regInfoEmitSourceFileHeader("Register Information Header Fragment"); - PI.emitIncludeToggle("GET_REGINFO_HEADER", true); + emitSourceFileHeader("Register Information Header Fragment", OS); + + OS << "\n#ifdef GET_REGINFO_HEADER\n"; + OS << "#undef GET_REGINFO_HEADER\n\n"; const std::string &TargetName = std::string(Target.getName()); std::string ClassName = TargetName + "GenRegisterInfo"; - PI.regInfoEmitHeaderIncludes(); - PI.emitNamespace("llvm", true); + OS << "#include \"llvm/CodeGen/TargetRegisterInfo.h\"\n\n"; + + OS << "namespace llvm {\n\n"; + + OS << "class " << TargetName << "FrameLowering;\n\n"; + + OS << "struct " << ClassName << " : public TargetRegisterInfo {\n" + << " explicit " << ClassName + << "(unsigned RA, unsigned D = 0, unsigned E = 0,\n" + << " unsigned PC = 0, unsigned HwMode = 0);\n"; + if (!RegBank.getSubRegIndices().empty()) { + OS << " unsigned composeSubRegIndicesImpl" + << "(unsigned, unsigned) const override;\n" + << " LaneBitmask composeSubRegIndexLaneMaskImpl" + << "(unsigned, LaneBitmask) const override;\n" + << " LaneBitmask reverseComposeSubRegIndexLaneMaskImpl" + << "(unsigned, LaneBitmask) const override;\n" + << " const TargetRegisterClass *getSubClassWithSubReg" + << "(const TargetRegisterClass *, unsigned) const override;\n" + << " const TargetRegisterClass *getSubRegisterClass" + << "(const TargetRegisterClass *, unsigned) const override;\n"; + } + OS << " const RegClassWeight &getRegClassWeight(" + << "const TargetRegisterClass *RC) const override;\n" + << " unsigned getRegUnitWeight(unsigned RegUnit) const override;\n" + << " unsigned getNumRegPressureSets() const override;\n" + << " const char *getRegPressureSetName(unsigned Idx) const override;\n" + << " unsigned getRegPressureSetLimit(const MachineFunction &MF, unsigned " + "Idx) const override;\n" + << " const int *getRegClassPressureSets(" + << "const TargetRegisterClass *RC) const override;\n" + << " const int *getRegUnitPressureSets(" + << "unsigned RegUnit) const override;\n" + << " ArrayRef getRegMaskNames() const override;\n" + << " ArrayRef getRegMasks() const override;\n" + << " bool isGeneralPurposeRegister(const MachineFunction &, " + << "MCRegister) const override;\n" + << " bool isFixedRegister(const MachineFunction &, " + << "MCRegister) const override;\n" + << " bool isArgumentRegister(const MachineFunction &, " + << "MCRegister) const override;\n" + << " bool isConstantPhysReg(MCRegister PhysReg) const override final;\n" + << " /// Devirtualized TargetFrameLowering.\n" + << " static const " << TargetName << "FrameLowering *getFrameLowering(\n" + << " const MachineFunction &MF);\n"; + const auto &RegisterClasses = RegBank.getRegClasses(); - PI.regInfoEmitHeaderDecl(TargetName, ClassName, !RegBank.getSubRegIndices().empty(), - llvm::any_of(RegisterClasses, [](const auto &RC) { return RC.getBaseClassOrder(); })); + if (llvm::any_of(RegisterClasses, [](const auto &RC) { return RC.getBaseClassOrder(); })) { + OS << " const TargetRegisterClass *getPhysRegBaseClass(MCRegister Reg) const override;\n"; + } + + OS << "};\n\n"; if (!RegisterClasses.empty()) { - PI.emitNamespace(RegisterClasses.front().Namespace.str(), true, "Register classes"); - PI.regInfoEmitHeaderExternRegClasses(RegisterClasses); - PI.emitNamespace(RegisterClasses.front().Namespace.str(), false); + OS << "namespace " << RegisterClasses.front().Namespace + << " { // Register classes\n"; + + for (const auto &RC : RegisterClasses) { + const std::string &Name = RC.getName(); + + // Output the extern for the instance. + OS << " extern const TargetRegisterClass " << Name << "RegClass;\n"; + } + OS << "} // end namespace " << RegisterClasses.front().Namespace << "\n\n"; } - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_REGINFO_HEADER", false); + OS << "} // end namespace llvm\n\n"; + OS << "#endif // GET_REGINFO_HEADER\n\n"; } // // runTargetDesc - Output the target register and register file descriptions. // void -RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, +RegisterInfoEmitter::runTargetDesc(raw_ostream &OS, CodeGenTarget &Target, CodeGenRegBank &RegBank){ - PI.regInfoEmitSourceFileHeader("Target Register and Register Classes Information"); + emitSourceFileHeader("Target Register and Register Classes Information", OS); - PI.emitIncludeToggle("GET_REGINFO_TARGET_DESC", true); - PI.emitNamespace("llvm", true); + OS << "\n#ifdef GET_REGINFO_TARGET_DESC\n"; + OS << "#undef GET_REGINFO_TARGET_DESC\n\n"; + + OS << "namespace llvm {\n\n"; // Get access to MCRegisterClass data. - PI.regInfoEmitExternRegClassesArr(Target.getName().str()); + OS << "extern const MCRegisterClass " << Target.getName() + << "MCRegisterClasses[];\n"; // Start out by emitting each of the register classes. const auto &RegisterClasses = RegBank.getRegClasses(); @@ -543,7 +1256,7 @@ RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, unsigned NumModes = CGH.getNumModeIds(); // Build a shared array of value types. - SequenceToOffsetTable> VTSeqs(PI.getLanguage()); + SequenceToOffsetTable> VTSeqs; for (unsigned M = 0; M < NumModes; ++M) { for (const auto &RC : RegisterClasses) { std::vector S; @@ -553,14 +1266,61 @@ RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, } } VTSeqs.layout(); - PI.regInfoEmitVTSeqs(VTSeqs); + OS << "\nstatic const MVT::SimpleValueType VTLists[] = {\n"; + VTSeqs.emit(OS, printSimpleValueType, "MVT::Other"); + OS << "};\n"; // Emit SubRegIndex names, skipping 0. - PI.regInfoEmitSubRegIdxTable(SubRegIndices); + OS << "\nstatic const char *SubRegIndexNameTable[] = { \""; + + for (const auto &Idx : SubRegIndices) { + OS << Idx.getName(); + OS << "\", \""; + } + OS << "\" };\n\n"; + + // Emit SubRegIndex lane masks, including 0. + OS << "\nstatic const LaneBitmask SubRegIndexLaneMaskTable[] = {\n " + "LaneBitmask::getAll(),\n"; + for (const auto &Idx : SubRegIndices) { + printMask(OS << " ", Idx.LaneMask); + OS << ", // " << Idx.getName() << '\n'; + } + OS << " };\n\n"; + + OS << "\n"; // Now that all of the structs have been emitted, emit the instances. if (!RegisterClasses.empty()) { - PI.regInfoEmitRegClassInfoTable(RegisterClasses, VTSeqs, CGH, NumModes); + OS << "\nstatic const TargetRegisterInfo::RegClassInfo RegClassInfos[]" + << " = {\n"; + for (unsigned M = 0; M < NumModes; ++M) { + unsigned EV = 0; + OS << " // Mode = " << M << " ("; + if (M == 0) + OS << "Default"; + else + OS << CGH.getMode(M).Name; + OS << ")\n"; + for (const auto &RC : RegisterClasses) { + assert(RC.EnumValue == EV && "Unexpected order of register classes"); + ++EV; + (void)EV; + const RegSizeInfo &RI = RC.RSI.get(M); + OS << " { " << RI.RegSize << ", " << RI.SpillSize << ", " + << RI.SpillAlignment; + std::vector VTs; + for (const ValueTypeByHwMode &VVT : RC.VTs) + VTs.push_back(VVT.get(M).SimpleTy); + OS << ", VTLists+" << VTSeqs.get(VTs) << " }, // " + << RC.getName() << '\n'; + } + } + OS << "};\n"; + + + OS << "\nstatic const TargetRegisterClass *const " + << "NullRegClasses[] = { nullptr };\n\n"; // Emit register class bit mask tables. The first bit mask emitted for a // register class, RC, is the set of sub-classes, including RC itself. @@ -580,37 +1340,124 @@ RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, // // Every bit mask present in the list has at least one bit set. + // Compress the sub-reg index lists. + typedef std::vector IdxList; SmallVector SuperRegIdxLists(RegisterClasses.size()); - SequenceToOffsetTable>> SuperRegIdxSeqs(PI.getLanguage()); + SequenceToOffsetTable>> SuperRegIdxSeqs; BitVector MaskBV(RegisterClasses.size()); - PI.regInfoEmitSubClassMaskTable(RegisterClasses, - SuperRegIdxLists, - SuperRegIdxSeqs, - SubRegIndices, - MaskBV); + for (const auto &RC : RegisterClasses) { + OS << "static const uint32_t " << RC.getName() + << "SubClassMask[] = {\n "; + printBitVectorAsHex(OS, RC.getSubClasses(), 32); + + // Emit super-reg class masks for any relevant SubRegIndices that can + // project into RC. + IdxList &SRIList = SuperRegIdxLists[RC.EnumValue]; + for (auto &Idx : SubRegIndices) { + MaskBV.reset(); + RC.getSuperRegClasses(&Idx, MaskBV); + if (MaskBV.none()) + continue; + SRIList.push_back(&Idx); + OS << "\n "; + printBitVectorAsHex(OS, MaskBV, 32); + OS << "// " << Idx.getName(); + } + SuperRegIdxSeqs.add(SRIList); + OS << "\n};\n\n"; + } + OS << "static const uint16_t SuperRegIdxSeqs[] = {\n"; SuperRegIdxSeqs.layout(); - PI.regInfoEmitSuperRegIdxSeqsTable(SuperRegIdxSeqs); + SuperRegIdxSeqs.emit(OS, printSubRegIndex); + OS << "};\n\n"; // Emit NULL terminated super-class lists. - PI.regInfoEmitSuperClassesTable(RegisterClasses); + for (const auto &RC : RegisterClasses) { + ArrayRef Supers = RC.getSuperClasses(); + + // Skip classes without supers. We can reuse NullRegClasses. + if (Supers.empty()) + continue; + + OS << "static const TargetRegisterClass *const " + << RC.getName() << "Superclasses[] = {\n"; + for (const auto *Super : Supers) + OS << " &" << Super->getQualifiedName() << "RegClass,\n"; + OS << " nullptr\n};\n\n"; + } // Emit methods. - PI.regInfoEmitRegClassMethods(RegisterClasses, Target.getName().str()); + for (const auto &RC : RegisterClasses) { + if (!RC.AltOrderSelect.empty()) { + OS << "\nstatic inline unsigned " << RC.getName() + << "AltOrderSelect(const MachineFunction &MF) {" + << RC.AltOrderSelect << "}\n\n" + << "static ArrayRef " << RC.getName() + << "GetRawAllocationOrder(const MachineFunction &MF) {\n"; + for (unsigned oi = 1 , oe = RC.getNumOrders(); oi != oe; ++oi) { + ArrayRef Elems = RC.getOrder(oi); + if (!Elems.empty()) { + OS << " static const MCPhysReg AltOrder" << oi << "[] = {"; + for (unsigned elem = 0; elem != Elems.size(); ++elem) + OS << (elem ? ", " : " ") << getQualifiedName(Elems[elem]); + OS << " };\n"; + } + } + OS << " const MCRegisterClass &MCR = " << Target.getName() + << "MCRegisterClasses[" << RC.getQualifiedName() + "RegClassID];\n" + << " const ArrayRef Order[] = {\n" + << " ArrayRef(MCR.begin(), MCR.getNumRegs()"; + for (unsigned oi = 1, oe = RC.getNumOrders(); oi != oe; ++oi) + if (RC.getOrder(oi).empty()) + OS << "),\n ArrayRef("; + else + OS << "),\n ArrayRef(AltOrder" << oi; + OS << ")\n };\n const unsigned Select = " << RC.getName() + << "AltOrderSelect(MF);\n assert(Select < " << RC.getNumOrders() + << ");\n return Order[Select];\n}\n"; + } + } // Now emit the actual value-initialized register class instances. - PI.emitNamespace(RegisterClasses.front().Namespace.str(), true, "Register class instances"); - PI.regInfomitRegClassInstances(RegisterClasses, - SuperRegIdxSeqs, - SuperRegIdxLists, - Target.getName().str()); - PI.emitNamespace(RegisterClasses.front().Namespace.str(), false); + OS << "\nnamespace " << RegisterClasses.front().Namespace + << " { // Register class instances\n"; + + for (const auto &RC : RegisterClasses) { + OS << " extern const TargetRegisterClass " << RC.getName() + << "RegClass = {\n " << '&' << Target.getName() + << "MCRegisterClasses[" << RC.getName() << "RegClassID],\n " + << RC.getName() << "SubClassMask,\n SuperRegIdxSeqs + " + << SuperRegIdxSeqs.get(SuperRegIdxLists[RC.EnumValue]) << ",\n "; + printMask(OS, RC.LaneMask); + OS << ",\n " << (unsigned)RC.AllocationPriority << ",\n " + << (RC.GlobalPriority ? "true" : "false") << ",\n " + << format("0x%02x", RC.TSFlags) << ", /* TSFlags */\n " + << (RC.HasDisjunctSubRegs ? "true" : "false") + << ", /* HasDisjunctSubRegs */\n " + << (RC.CoveredBySubRegs ? "true" : "false") + << ", /* CoveredBySubRegs */\n "; + if (RC.getSuperClasses().empty()) + OS << "NullRegClasses,\n "; + else + OS << RC.getName() << "Superclasses,\n "; + if (RC.AltOrderSelect.empty()) + OS << "nullptr\n"; + else + OS << RC.getName() << "GetRawAllocationOrder\n"; + OS << " };\n\n"; + } + + OS << "} // end namespace " << RegisterClasses.front().Namespace << "\n"; } - PI.emitNamespace("", true); - PI.regInfoEmitRegClassTable(RegisterClasses); - PI.emitNamespace("", false); + OS << "\nnamespace {\n"; + OS << " const TargetRegisterClass *const RegisterClasses[] = {\n"; + for (const auto &RC : RegisterClasses) + OS << " &" << RC.getQualifiedName() << "RegClass,\n"; + OS << " };\n"; + OS << "} // end anonymous namespace\n"; // Emit extra information about registers. const std::string &TargetName = std::string(Target.getName()); @@ -640,10 +1487,26 @@ RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, // Emit the cost values as a 1D-array after grouping them by their indices, // i.e. the costs for all registers corresponds to index 0, 1, 2, etc. // Size of the emitted array should be NumRegCosts * (Regs.size() + 1). - PI.regInfoEmitCostPerUseTable(AllRegCostPerUse, NumRegCosts); - PI.regInfoEmitInAllocatableClassTable(InAllocClass); - PI.regInfoEmitRegExtraDesc(TargetName, NumRegCosts); - // End of register descriptors... + OS << "\nstatic const uint8_t " + << "CostPerUseTable[] = { \n"; + for (unsigned int I = 0; I < NumRegCosts; ++I) { + for (unsigned J = I, E = AllRegCostPerUse.size(); J < E; J += NumRegCosts) + OS << AllRegCostPerUse[J] << ", "; + } + OS << "};\n\n"; + + OS << "\nstatic const bool " + << "InAllocatableClassTable[] = { \n"; + for (unsigned I = 0, E = InAllocClass.size(); I < E; ++I) { + OS << (InAllocClass[I] ? "true" : "false") << ", "; + } + OS << "};\n\n"; + + OS << "\nstatic const TargetRegisterInfoDesc " << TargetName + << "RegInfoDesc = { // Extra Descriptors\n"; + OS << "CostPerUseTable, " << NumRegCosts << ", " + << "InAllocatableClassTable"; + OS << "};\n\n"; // End of register descriptors... std::string ClassName = Target.getName().str() + "GenRegisterInfo"; @@ -651,19 +1514,90 @@ RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, std::distance(SubRegIndices.begin(), SubRegIndices.end()); if (!SubRegIndices.empty()) { - emitComposeSubRegIndices(RegBank, ClassName); - emitComposeSubRegIndexLaneMask(RegBank, ClassName); + emitComposeSubRegIndices(OS, RegBank, ClassName); + emitComposeSubRegIndexLaneMask(OS, RegBank, ClassName); } if (!SubRegIndices.empty()) { - PI.regInfoEmitSubClassSubRegGetter(ClassName, - SubRegIndicesSize, - SubRegIndices, - RegisterClasses, - RegBank); + // Emit getSubClassWithSubReg. + OS << "const TargetRegisterClass *" << ClassName + << "::getSubClassWithSubReg(const TargetRegisterClass *RC, unsigned Idx)" + << " const {\n"; + // Use the smallest type that can hold a regclass ID with room for a + // sentinel. + if (RegisterClasses.size() <= UINT8_MAX) + OS << " static const uint8_t Table["; + else if (RegisterClasses.size() <= UINT16_MAX) + OS << " static const uint16_t Table["; + else + PrintFatalError("Too many register classes."); + OS << RegisterClasses.size() << "][" << SubRegIndicesSize << "] = {\n"; + for (const auto &RC : RegisterClasses) { + OS << " {\t// " << RC.getName() << "\n"; + for (auto &Idx : SubRegIndices) { + if (CodeGenRegisterClass *SRC = RC.getSubClassWithSubReg(&Idx)) + OS << " " << SRC->EnumValue + 1 << ",\t// " << Idx.getName() + << " -> " << SRC->getName() << "\n"; + else + OS << " 0,\t// " << Idx.getName() << "\n"; + } + OS << " },\n"; + } + OS << " };\n assert(RC && \"Missing regclass\");\n" + << " if (!Idx) return RC;\n --Idx;\n" + << " assert(Idx < " << SubRegIndicesSize << " && \"Bad subreg\");\n" + << " unsigned TV = Table[RC->getID()][Idx];\n" + << " return TV ? getRegClass(TV - 1) : nullptr;\n}\n\n"; + + // Emit getSubRegisterClass + OS << "const TargetRegisterClass *" << ClassName + << "::getSubRegisterClass(const TargetRegisterClass *RC, unsigned Idx)" + << " const {\n"; + + // Use the smallest type that can hold a regclass ID with room for a + // sentinel. + if (RegisterClasses.size() <= UINT8_MAX) + OS << " static const uint8_t Table["; + else if (RegisterClasses.size() <= UINT16_MAX) + OS << " static const uint16_t Table["; + else + PrintFatalError("Too many register classes."); + + OS << RegisterClasses.size() << "][" << SubRegIndicesSize << "] = {\n"; + + for (const auto &RC : RegisterClasses) { + OS << " {\t// " << RC.getName() << '\n'; + for (auto &Idx : SubRegIndices) { + std::optional> + MatchingSubClass = RC.getMatchingSubClassWithSubRegs(RegBank, &Idx); + + unsigned EnumValue = 0; + if (MatchingSubClass) { + CodeGenRegisterClass *SubRegClass = MatchingSubClass->second; + EnumValue = SubRegClass->EnumValue + 1; + } + + OS << " " << EnumValue << ",\t// " + << RC.getName() << ':' << Idx.getName(); + + if (MatchingSubClass) { + CodeGenRegisterClass *SubRegClass = MatchingSubClass->second; + OS << " -> " << SubRegClass->getName(); + } + + OS << '\n'; + } + + OS << " },\n"; + } + OS << " };\n assert(RC && \"Missing regclass\");\n" + << " if (!Idx) return RC;\n --Idx;\n" + << " assert(Idx < " << SubRegIndicesSize << " && \"Bad subreg\");\n" + << " unsigned TV = Table[RC->getID()][Idx];\n" + << " return TV ? getRegClass(TV - 1) : nullptr;\n}\n\n"; } - EmitRegUnitPressure(RegBank, ClassName); + EmitRegUnitPressure(OS, RegBank, ClassName); // Emit register base class mapper if (!RegisterClasses.empty()) { @@ -693,23 +1627,67 @@ RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, Mapping[Reg->EnumValue] = RCIdx + 1; } - PI.regInfoEmitRegBaseClassMapping(ClassName, BaseClasses, Mapping); + OS << "\n// Register to base register class mapping\n\n"; + OS << "\n"; + OS << "const TargetRegisterClass *" << ClassName + << "::getPhysRegBaseClass(MCRegister Reg)" + << " const {\n"; + OS << " static const TargetRegisterClass *BaseClasses[" << (BaseClasses.size() + 1) << "] = {\n"; + OS << " nullptr,\n"; + for (const auto RC : BaseClasses) + OS << " &" << RC->getQualifiedName() << "RegClass,\n"; + OS << " };\n"; + OS << " static const uint8_t Mapping[" << Mapping.size() << "] = {\n "; + for (const uint8_t Value : Mapping) + OS << (unsigned)Value << ","; + OS << " };\n\n"; + OS << " assert(Reg < sizeof(Mapping));\n"; + OS << " return BaseClasses[Mapping[Reg]];\n"; + OS << "}\n"; } } // Emit the constructor of the class... - PI.regInfoEmitExternTableDecl(TargetName); - - EmitRegMappingTables(Regs, true); - - PI.regInfoEmitRegClassInit(TargetName, - ClassName, - RegBank, - RegisterClasses, - Regs, - SubRegIndicesSize); - - EmitRegMapping(Regs, true); + OS << "extern const MCRegisterDesc " << TargetName << "RegDesc[];\n"; + OS << "extern const MCPhysReg " << TargetName << "RegDiffLists[];\n"; + OS << "extern const LaneBitmask " << TargetName << "LaneMaskLists[];\n"; + OS << "extern const char " << TargetName << "RegStrings[];\n"; + OS << "extern const char " << TargetName << "RegClassStrings[];\n"; + OS << "extern const MCPhysReg " << TargetName << "RegUnitRoots[][2];\n"; + OS << "extern const uint16_t " << TargetName << "SubRegIdxLists[];\n"; + OS << "extern const MCRegisterInfo::SubRegCoveredBits " + << TargetName << "SubRegIdxRanges[];\n"; + OS << "extern const uint16_t " << TargetName << "RegEncodingTable[];\n"; + + EmitRegMappingTables(OS, Regs, true); + + OS << ClassName << "::\n" + << ClassName + << "(unsigned RA, unsigned DwarfFlavour, unsigned EHFlavour,\n" + " unsigned PC, unsigned HwMode)\n" + << " : TargetRegisterInfo(&" << TargetName << "RegInfoDesc" + << ", RegisterClasses, RegisterClasses+" << RegisterClasses.size() << ",\n" + << " SubRegIndexNameTable, SubRegIndexLaneMaskTable,\n" + << " "; + printMask(OS, RegBank.CoveringLanes); + OS << ", RegClassInfos, HwMode) {\n" + << " InitMCRegisterInfo(" << TargetName << "RegDesc, " << Regs.size() + 1 + << ", RA, PC,\n " << TargetName + << "MCRegisterClasses, " << RegisterClasses.size() << ",\n" + << " " << TargetName << "RegUnitRoots,\n" + << " " << RegBank.getNumNativeRegUnits() << ",\n" + << " " << TargetName << "RegDiffLists,\n" + << " " << TargetName << "LaneMaskLists,\n" + << " " << TargetName << "RegStrings,\n" + << " " << TargetName << "RegClassStrings,\n" + << " " << TargetName << "SubRegIdxLists,\n" + << " " << SubRegIndicesSize + 1 << ",\n" + << " " << TargetName << "SubRegIdxRanges,\n" + << " " << TargetName << "RegEncodingTable);\n\n"; + + EmitRegMapping(OS, Regs, true); + + OS << "}\n\n"; // Emit CalleeSavedRegs information. std::vector CSRSets = @@ -720,7 +1698,11 @@ RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, assert(Regs && "Cannot expand CalleeSavedRegs instance"); // Emit the *_SaveList list of callee-saved registers. - PI.regInfoEmitSaveListTable(CSRSet, Regs); + OS << "static const MCPhysReg " << CSRSet->getName() + << "_SaveList[] = { "; + for (unsigned r = 0, re = Regs->size(); r != re; ++r) + OS << getQualifiedName((*Regs)[r]) << ", "; + OS << "0 };\n"; // Emit the *_RegMask bit mask of call-preserved registers. BitVector Covered = RegBank.computeCoveredRegisters(*Regs); @@ -744,47 +1726,121 @@ RegisterInfoEmitter::runTargetDesc(CodeGenTarget &Target, Covered |= RegBank.computeCoveredRegisters( ArrayRef(ConstantSet.begin(), ConstantSet.end())); - PI.regInfoEmitRegMaskTable(CSRSet->getName().str(), Covered); + OS << "static const uint32_t " << CSRSet->getName() + << "_RegMask[] = { "; + printBitVectorAsHex(OS, Covered, 32); + OS << "};\n"; } - PI.emitNewline(2); - PI.regInfoEmitGetRegMasks(CSRSets, ClassName); + OS << "\n\n"; + + OS << "ArrayRef " << ClassName + << "::getRegMasks() const {\n"; + if (!CSRSets.empty()) { + OS << " static const uint32_t *const Masks[] = {\n"; + for (Record *CSRSet : CSRSets) + OS << " " << CSRSet->getName() << "_RegMask,\n"; + OS << " };\n"; + OS << " return ArrayRef(Masks);\n"; + } else { + OS << " return std::nullopt;\n"; + } + OS << "}\n\n"; const std::list &RegCategories = RegBank.getRegCategories(); - PI.regInfoEmitGPRCheck(ClassName, RegCategories); - PI.regInfoEmitFixedRegCheck(ClassName, RegCategories); - PI.regInfoEmitArgRegCheck(ClassName, RegCategories); + OS << "bool " << ClassName << "::\n" + << "isGeneralPurposeRegister(const MachineFunction &MF, " + << "MCRegister PhysReg) const {\n" + << " return\n"; + for (const CodeGenRegisterCategory &Category : RegCategories) + if (Category.getName() == "GeneralPurposeRegisters") { + for (const CodeGenRegisterClass *RC : Category.getClasses()) + OS << " " << RC->getQualifiedName() + << "RegClass.contains(PhysReg) ||\n"; + break; + } + OS << " false;\n"; + OS << "}\n\n"; + + OS << "bool " << ClassName << "::\n" + << "isFixedRegister(const MachineFunction &MF, " + << "MCRegister PhysReg) const {\n" + << " return\n"; + for (const CodeGenRegisterCategory &Category : RegCategories) + if (Category.getName() == "FixedRegisters") { + for (const CodeGenRegisterClass *RC : Category.getClasses()) + OS << " " << RC->getQualifiedName() + << "RegClass.contains(PhysReg) ||\n"; + break; + } + OS << " false;\n"; + OS << "}\n\n"; + + OS << "bool " << ClassName << "::\n" + << "isArgumentRegister(const MachineFunction &MF, " + << "MCRegister PhysReg) const {\n" + << " return\n"; + for (const CodeGenRegisterCategory &Category : RegCategories) + if (Category.getName() == "ArgumentRegisters") { + for (const CodeGenRegisterClass *RC : Category.getClasses()) + OS << " " << RC->getQualifiedName() + << "RegClass.contains(PhysReg) ||\n"; + break; + } + OS << " false;\n"; + OS << "}\n\n"; - PI.regInfoEmitIsConstantPhysReg(Regs, ClassName); - PI.regInfoEmitGetRegMaskNames(CSRSets, ClassName); + OS << "bool " << ClassName << "::\n" + << "isConstantPhysReg(MCRegister PhysReg) const {\n" + << " return\n"; + for (const auto &Reg : Regs) + if (Reg.Constant) + OS << " PhysReg == " << getQualifiedName(Reg.TheDef) << " ||\n"; + OS << " false;\n"; + OS << "}\n\n"; + + OS << "ArrayRef " << ClassName + << "::getRegMaskNames() const {\n"; + if (!CSRSets.empty()) { + OS << " static const char *Names[] = {\n"; + for (Record *CSRSet : CSRSets) + OS << " " << '"' << CSRSet->getName() << '"' << ",\n"; + OS << " };\n"; + OS << " return ArrayRef(Names);\n"; + } else { + OS << " return std::nullopt;\n"; + } + OS << "}\n\n"; - PI.regInfoEmitGetFrameLowering(TargetName); + OS << "const " << TargetName << "FrameLowering *\n" << TargetName + << "GenRegisterInfo::getFrameLowering(const MachineFunction &MF) {\n" + << " return static_cast(\n" + << " MF.getSubtarget().getFrameLowering());\n" + << "}\n\n"; - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_REGINFO_TARGET_DESC", false); + OS << "} // end namespace llvm\n\n"; + OS << "#endif // GET_REGINFO_TARGET_DESC\n\n"; } -void RegisterInfoEmitter::run() { +void RegisterInfoEmitter::run(raw_ostream &OS) { CodeGenRegBank &RegBank = Target.getRegBank(); Records.startTimer("Print enums"); - - PI.regInfoEmitSourceFileHeader("Target Register Enum Values"); - PI.regInfoEmitEnums(Target, RegBank); + runEnums(OS, Target, RegBank); Records.startTimer("Print MC registers"); - runMCDesc(Target, RegBank); + runMCDesc(OS, Target, RegBank); Records.startTimer("Print header fragment"); - runTargetHeader(Target, RegBank); + runTargetHeader(OS, Target, RegBank); Records.startTimer("Print target registers"); - runTargetDesc(Target, RegBank); + runTargetDesc(OS, Target, RegBank); if (RegisterInfoDebug) debugDump(errs()); } -void RegisterInfoEmitter::debugDump(raw_ostream &ErrOS) { +void RegisterInfoEmitter::debugDump(raw_ostream &OS) { CodeGenRegBank &RegBank = Target.getRegBank(); const CodeGenHwModes &CGH = Target.getHwModes(); unsigned NumModes = CGH.getNumModeIds(); @@ -795,56 +1851,56 @@ void RegisterInfoEmitter::debugDump(raw_ostream &ErrOS) { }; for (const CodeGenRegisterClass &RC : RegBank.getRegClasses()) { - ErrOS << "RegisterClass " << RC.getName() << ":\n"; - ErrOS << "\tSpillSize: {"; + OS << "RegisterClass " << RC.getName() << ":\n"; + OS << "\tSpillSize: {"; for (unsigned M = 0; M != NumModes; ++M) - ErrOS << ' ' << getModeName(M) << ':' << RC.RSI.get(M).SpillSize; - ErrOS << " }\n\tSpillAlignment: {"; + OS << ' ' << getModeName(M) << ':' << RC.RSI.get(M).SpillSize; + OS << " }\n\tSpillAlignment: {"; for (unsigned M = 0; M != NumModes; ++M) - ErrOS << ' ' << getModeName(M) << ':' << RC.RSI.get(M).SpillAlignment; - ErrOS << " }\n\tNumRegs: " << RC.getMembers().size() << '\n'; - ErrOS << "\tLaneMask: " << PrintLaneMask(RC.LaneMask) << '\n'; - ErrOS << "\tHasDisjunctSubRegs: " << RC.HasDisjunctSubRegs << '\n'; - ErrOS << "\tCoveredBySubRegs: " << RC.CoveredBySubRegs << '\n'; - ErrOS << "\tAllocatable: " << RC.Allocatable << '\n'; - ErrOS << "\tAllocationPriority: " << unsigned(RC.AllocationPriority) << '\n'; - ErrOS << "\tRegs:"; + OS << ' ' << getModeName(M) << ':' << RC.RSI.get(M).SpillAlignment; + OS << " }\n\tNumRegs: " << RC.getMembers().size() << '\n'; + OS << "\tLaneMask: " << PrintLaneMask(RC.LaneMask) << '\n'; + OS << "\tHasDisjunctSubRegs: " << RC.HasDisjunctSubRegs << '\n'; + OS << "\tCoveredBySubRegs: " << RC.CoveredBySubRegs << '\n'; + OS << "\tAllocatable: " << RC.Allocatable << '\n'; + OS << "\tAllocationPriority: " << unsigned(RC.AllocationPriority) << '\n'; + OS << "\tRegs:"; for (const CodeGenRegister *R : RC.getMembers()) { - ErrOS << " " << R->getName(); + OS << " " << R->getName(); } - ErrOS << '\n'; - ErrOS << "\tSubClasses:"; + OS << '\n'; + OS << "\tSubClasses:"; const BitVector &SubClasses = RC.getSubClasses(); for (const CodeGenRegisterClass &SRC : RegBank.getRegClasses()) { if (!SubClasses.test(SRC.EnumValue)) continue; - ErrOS << " " << SRC.getName(); + OS << " " << SRC.getName(); } - ErrOS << '\n'; - ErrOS << "\tSuperClasses:"; + OS << '\n'; + OS << "\tSuperClasses:"; for (const CodeGenRegisterClass *SRC : RC.getSuperClasses()) { - ErrOS << " " << SRC->getName(); + OS << " " << SRC->getName(); } - ErrOS << '\n'; + OS << '\n'; } for (const CodeGenSubRegIndex &SRI : RegBank.getSubRegIndices()) { - ErrOS << "SubRegIndex " << SRI.getName() << ":\n"; - ErrOS << "\tLaneMask: " << PrintLaneMask(SRI.LaneMask) << '\n'; - ErrOS << "\tAllSuperRegsCovered: " << SRI.AllSuperRegsCovered << '\n'; - ErrOS << "\tOffset, Size: " << SRI.Offset << ", " << SRI.Size << '\n'; + OS << "SubRegIndex " << SRI.getName() << ":\n"; + OS << "\tLaneMask: " << PrintLaneMask(SRI.LaneMask) << '\n'; + OS << "\tAllSuperRegsCovered: " << SRI.AllSuperRegsCovered << '\n'; + OS << "\tOffset, Size: " << SRI.Offset << ", " << SRI.Size << '\n'; } for (const CodeGenRegister &R : RegBank.getRegisters()) { - ErrOS << "Register " << R.getName() << ":\n"; - ErrOS << "\tCostPerUse: "; + OS << "Register " << R.getName() << ":\n"; + OS << "\tCostPerUse: "; for (const auto &Cost : R.CostPerUse) - ErrOS << Cost << " "; - ErrOS << '\n'; - ErrOS << "\tCoveredBySubregs: " << R.CoveredBySubRegs << '\n'; - ErrOS << "\tHasDisjunctSubRegs: " << R.HasDisjunctSubRegs << '\n'; + OS << Cost << " "; + OS << '\n'; + OS << "\tCoveredBySubregs: " << R.CoveredBySubRegs << '\n'; + OS << "\tHasDisjunctSubRegs: " << R.HasDisjunctSubRegs << '\n'; for (std::pair P : R.getSubRegs()) { - ErrOS << "\tSubReg " << P.first->getName() + OS << "\tSubReg " << P.first->getName() << " = " << P.second->getName() << '\n'; } } @@ -853,23 +1909,7 @@ void RegisterInfoEmitter::debugDump(raw_ostream &ErrOS) { namespace llvm { void EmitRegisterInfo(RecordKeeper &RK, raw_ostream &OS) { - formatted_raw_ostream FOS(OS); - PrinterLanguage const PLang = PrinterLLVM::getLanguage(); - PrinterLLVM *PI = nullptr; - switch (PLang) { - default: - PrintFatalNote( - "RegisterInfo backend does not support the selected ouput language."); - return; - case PRINTER_LANG_CPP: - PI = new PrinterLLVM(FOS); - break; - case PRINTER_LANG_CAPSTONE_C: - PI = new PrinterCapstone(FOS); - break; - } - RegisterInfoEmitter(RK, *PI).run(); - delete PI; + RegisterInfoEmitter(RK).run(OS); } } // end namespace llvm diff --git a/llvm/utils/TableGen/SearchableTableEmitter.cpp b/llvm/utils/TableGen/SearchableTableEmitter.cpp index 008b40d34b16..c88a2db55502 100644 --- a/llvm/utils/TableGen/SearchableTableEmitter.cpp +++ b/llvm/utils/TableGen/SearchableTableEmitter.cpp @@ -12,26 +12,80 @@ // //===----------------------------------------------------------------------===// -#include "Printer.h" -#include "PrinterTypes.h" -#include "SearchableTablesTypes.h" +#include "CodeGenIntrinsics.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include +#include +#include +#include using namespace llvm; #define DEBUG_TYPE "searchable-table-emitter" +namespace { + int getAsInt(Init *B) { return cast( B->convertInitializerTo(IntRecTy::get(B->getRecordKeeper()))) ->getValue(); } - -namespace { - int getInt(Record *R, StringRef Field) { return getAsInt(R->getValueInit(Field)); } +struct GenericEnum { + using Entry = std::pair; + + std::string Name; + Record *Class = nullptr; + std::string PreprocessorGuard; + std::vector> Entries; + DenseMap EntryMap; +}; + +struct GenericField { + std::string Name; + RecTy *RecType = nullptr; + bool IsCode = false; + bool IsIntrinsic = false; + bool IsInstruction = false; + GenericEnum *Enum = nullptr; + + GenericField(StringRef Name) : Name(std::string(Name)) {} +}; + +struct SearchIndex { + std::string Name; + SMLoc Loc; // Source location of PrimaryKey or Key field definition. + SmallVector Fields; + bool EarlyOut = false; +}; + +struct GenericTable { + std::string Name; + ArrayRef Locs; // Source locations from the Record instance. + std::string PreprocessorGuard; + std::string CppTypeName; + SmallVector Fields; + std::vector Entries; + + std::unique_ptr PrimaryKey; + SmallVector, 2> Indices; + + const GenericField *getFieldByName(StringRef Name) const { + for (const auto &Field : Fields) { + if (Name == Field.Name) + return &Field; + } + return nullptr; + } +}; class SearchableTableEmitter { RecordKeeper &Records; @@ -39,16 +93,47 @@ class SearchableTableEmitter { std::vector> Enums; DenseMap EnumMap; std::set PreprocessorGuards; - PrinterLLVM &PI; public: - SearchableTableEmitter(RecordKeeper &R, PrinterLLVM &PI) : Records(R), PI(PI) {} + SearchableTableEmitter(RecordKeeper &R) : Records(R) {} - void run(); + void run(raw_ostream &OS); private: typedef std::pair SearchTableEntry; + enum TypeContext { + TypeInStaticStruct, + TypeInTempStruct, + TypeInArgument, + }; + + std::string primaryRepresentation(SMLoc Loc, const GenericField &Field, + Init *I) { + if (StringInit *SI = dyn_cast(I)) { + if (Field.IsCode || SI->hasCodeFormat()) + return std::string(SI->getValue()); + else + return SI->getAsString(); + } else if (BitsInit *BI = dyn_cast(I)) + return "0x" + utohexstr(getAsInt(BI)); + else if (BitInit *BI = dyn_cast(I)) + return BI->getValue() ? "true" : "false"; + else if (Field.IsIntrinsic) + return "Intrinsic::" + getIntrinsic(I).EnumName; + else if (Field.IsInstruction) + return I->getAsString(); + else if (Field.Enum) { + auto *Entry = Field.Enum->EntryMap[cast(I)->getDef()]; + if (!Entry) + PrintFatalError(Loc, + Twine("Entry for field '") + Field.Name + "' is null"); + return std::string(Entry->first); + } + PrintFatalError(Loc, Twine("invalid field type for field '") + Field.Name + + "'; expected: bit, bits, string, or code"); + } + bool isIntrinsic(Init *I) { if (DefInit *DI = dyn_cast(I)) return DI->getDef()->isSubClassOf("Intrinsic"); @@ -58,17 +143,50 @@ class SearchableTableEmitter { CodeGenIntrinsic &getIntrinsic(Init *I) { std::unique_ptr &Intr = Intrinsics[I]; if (!Intr) - Intr = std::make_unique(dyn_cast(I)->getDef(), + Intr = std::make_unique(cast(I)->getDef(), std::vector()); return *Intr; } bool compareBy(Record *LHS, Record *RHS, const SearchIndex &Index); - void emitGenericTable(const GenericTable &Table); + std::string searchableFieldType(const GenericTable &Table, + const SearchIndex &Index, + const GenericField &Field, TypeContext Ctx) { + if (isa(Field.RecType)) { + if (Ctx == TypeInStaticStruct) + return "const char *"; + if (Ctx == TypeInTempStruct) + return "std::string"; + return "StringRef"; + } else if (BitsRecTy *BI = dyn_cast(Field.RecType)) { + unsigned NumBits = BI->getNumBits(); + if (NumBits <= 8) + return "uint8_t"; + if (NumBits <= 16) + return "uint16_t"; + if (NumBits <= 32) + return "uint32_t"; + if (NumBits <= 64) + return "uint64_t"; + PrintFatalError(Index.Loc, Twine("In table '") + Table.Name + + "' lookup method '" + Index.Name + + "', key field '" + Field.Name + + "' of type bits is too large"); + } else if (Field.Enum || Field.IsIntrinsic || Field.IsInstruction) + return "unsigned"; + PrintFatalError(Index.Loc, + Twine("In table '") + Table.Name + "' lookup method '" + + Index.Name + "', key field '" + Field.Name + + "' has invalid type: " + Field.RecType->getAsString()); + } + + void emitGenericTable(const GenericTable &Table, raw_ostream &OS); void emitGenericEnum(const GenericEnum &Enum, raw_ostream &OS); + void emitLookupDeclaration(const GenericTable &Table, + const SearchIndex &Index, raw_ostream &OS); void emitLookupFunction(const GenericTable &Table, const SearchIndex &Index, - bool IsPrimary); + bool IsPrimary, raw_ostream &OS); void emitIfdef(StringRef Guard, raw_ostream &OS); bool parseFieldType(GenericField &Field, Init *II); @@ -148,10 +266,8 @@ bool SearchableTableEmitter::compareBy(Record *LHS, Record *RHS, if (LHSv > RHSv) return false; } else { - StringRef LHSIEnum = (Field.IsIntrinsic ? getIntrinsic(LHSI).EnumName : ""); - StringRef RHSIEnum = (Field.IsIntrinsic ? getIntrinsic(LHSI).EnumName : ""); - std::string LHSs = PI.searchableTablesPrimaryRepresentation(Index.Loc, Field, LHSI, LHSIEnum); - std::string RHSs = PI.searchableTablesPrimaryRepresentation(Index.Loc, Field, RHSI, RHSIEnum); + std::string LHSs = primaryRepresentation(Index.Loc, Field, LHSI); + std::string RHSs = primaryRepresentation(Index.Loc, Field, RHSI); if (isa(Field.RecType)) { LHSs = StringRef(LHSs).upper(); @@ -168,10 +284,31 @@ bool SearchableTableEmitter::compareBy(Record *LHS, Record *RHS, return false; } +void SearchableTableEmitter::emitIfdef(StringRef Guard, raw_ostream &OS) { + OS << "#ifdef " << Guard << "\n"; + PreprocessorGuards.insert(std::string(Guard)); +} + +/// Emit a generic enum. +void SearchableTableEmitter::emitGenericEnum(const GenericEnum &Enum, + raw_ostream &OS) { + emitIfdef((Twine("GET_") + Enum.PreprocessorGuard + "_DECL").str(), OS); + + OS << "enum " << Enum.Name << " {\n"; + for (const auto &Entry : Enum.Entries) + OS << " " << Entry->first << " = " << Entry->second << ",\n"; + OS << "};\n"; + + OS << "#endif\n\n"; +} + void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table, const SearchIndex &Index, - bool IsPrimary) { - PI.searchableTablesEmitLookupDeclaration(Table, Index, ST_IMPL_OS); + bool IsPrimary, + raw_ostream &OS) { + OS << "\n"; + emitLookupDeclaration(Table, Index, OS); + OS << " {\n"; std::vector IndexRowsStorage; ArrayRef IndexRows; @@ -183,9 +320,16 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table, IndexName = Table.Name; IndexRows = Table.Entries; } else { - PI.searchableTablesEmitIndexTypeStruct(Table, Index); + OS << " struct IndexType {\n"; + for (const auto &Field : Index.Fields) { + OS << " " + << searchableFieldType(Table, Index, Field, TypeInStaticStruct) << " " + << Field.Name << ";\n"; + } + OS << " unsigned _index;\n"; + OS << " };\n"; - PI.searchableTablesEmitIndexArrayI(); + OS << " static const struct IndexType Index[] = {\n"; std::vector> Entries; Entries.reserve(Table.Entries.size()); @@ -201,20 +345,19 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table, for (const auto &Entry : Entries) { IndexRowsStorage.push_back(Entry.first); - PI.searchableTablesEmitIndexArrayII(); + OS << " { "; ListSeparator LS; for (const auto &Field : Index.Fields) { - StringRef EnumName = (Field.IsIntrinsic ? getIntrinsic(Entry.first->getValueInit(Field.Name)).EnumName : ""); - std::string Repr = PI.searchableTablesPrimaryRepresentation( - Index.Loc, Field, Entry.first->getValueInit(Field.Name), - EnumName); + std::string Repr = primaryRepresentation( + Index.Loc, Field, Entry.first->getValueInit(Field.Name)); if (isa(Field.RecType)) Repr = StringRef(Repr).upper(); - PI.searchableTablesEmitIndexArrayIII(LS, Repr); + OS << LS << Repr; } - PI.searchableTablesEmitIndexArrayIV(Entry); + OS << ", " << Entry.second << " },\n"; } - PI.searchableTablesEmitIndexArrayV(); + + OS << " };\n\n"; IndexTypeName = "IndexType"; IndexName = "Index"; @@ -235,75 +378,149 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table, } if (IsContiguous) { - PI.searchableTablesEmitIsContiguousCase(IndexName, Table, Index, IsPrimary); + OS << " auto Table = ArrayRef(" << IndexName << ");\n"; + OS << " size_t Idx = " << Index.Fields[0].Name << ";\n"; + OS << " return Idx >= Table.size() ? nullptr : "; + if (IsPrimary) + OS << "&Table[Idx]"; + else + OS << "&" << Table.Name << "[Table[Idx]._index]"; + OS << ";\n"; + OS << "}\n"; return; } if (Index.EarlyOut) { const GenericField &Field = Index.Fields[0]; - StringRef EnumNameA = (Field.IsIntrinsic ? - getIntrinsic(IndexRows[0]->getValueInit(Field.Name)).EnumName : - ""); - std::string FirstRepr = PI.searchableTablesPrimaryRepresentation( - Index.Loc, Field, IndexRows[0]->getValueInit(Field.Name), - EnumNameA); - - StringRef EnumNameB = (Field.IsIntrinsic ? - getIntrinsic(IndexRows.back()->getValueInit(Field.Name)).EnumName : - ""); - std::string LastRepr = PI.searchableTablesPrimaryRepresentation( - Index.Loc, Field, IndexRows.back()->getValueInit(Field.Name), - EnumNameB); - PI.searchableTablesEmitIfFieldCase(Field, FirstRepr, LastRepr); + std::string FirstRepr = primaryRepresentation( + Index.Loc, Field, IndexRows[0]->getValueInit(Field.Name)); + std::string LastRepr = primaryRepresentation( + Index.Loc, Field, IndexRows.back()->getValueInit(Field.Name)); + OS << " if ((" << Field.Name << " < " << FirstRepr << ") ||\n"; + OS << " (" << Field.Name << " > " << LastRepr << "))\n"; + OS << " return nullptr;\n\n"; + } + + OS << " struct KeyType {\n"; + for (const auto &Field : Index.Fields) { + OS << " " << searchableFieldType(Table, Index, Field, TypeInTempStruct) + << " " << Field.Name << ";\n"; + } + OS << " };\n"; + OS << " KeyType Key = {"; + ListSeparator LS; + for (const auto &Field : Index.Fields) { + OS << LS << Field.Name; + if (isa(Field.RecType)) { + OS << ".upper()"; + if (IsPrimary) + PrintFatalError(Index.Loc, + Twine("In table '") + Table.Name + + "', use a secondary lookup method for " + "case-insensitive comparison of field '" + + Field.Name + "'"); + } + } + OS << "};\n"; + + OS << " auto Table = ArrayRef(" << IndexName << ");\n"; + OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,\n"; + OS << " [](const " << IndexTypeName << " &LHS, const KeyType &RHS) {\n"; + + for (const auto &Field : Index.Fields) { + if (isa(Field.RecType)) { + OS << " int Cmp" << Field.Name << " = StringRef(LHS." << Field.Name + << ").compare(RHS." << Field.Name << ");\n"; + OS << " if (Cmp" << Field.Name << " < 0) return true;\n"; + OS << " if (Cmp" << Field.Name << " > 0) return false;\n"; + } else if (Field.Enum) { + // Explicitly cast to unsigned, because the signedness of enums is + // compiler-dependent. + OS << " if ((unsigned)LHS." << Field.Name << " < (unsigned)RHS." + << Field.Name << ")\n"; + OS << " return true;\n"; + OS << " if ((unsigned)LHS." << Field.Name << " > (unsigned)RHS." + << Field.Name << ")\n"; + OS << " return false;\n"; + } else { + OS << " if (LHS." << Field.Name << " < RHS." << Field.Name << ")\n"; + OS << " return true;\n"; + OS << " if (LHS." << Field.Name << " > RHS." << Field.Name << ")\n"; + OS << " return false;\n"; + } } - PI.searchableTablesEmitKeyTypeStruct(Table, Index); - PI.searchableTablesEmitKeyArray(Table, Index, IsPrimary); - PI.searchableTablesEmitIndexLamda(Index, IndexName, IndexTypeName); - PI.searchableTablesEmitReturns(Table, Index, IsPrimary); + OS << " return false;\n"; + OS << " });\n\n"; + + OS << " if (Idx == Table.end()"; + + for (const auto &Field : Index.Fields) + OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name; + OS << ")\n return nullptr;\n"; + + if (IsPrimary) + OS << " return &*Idx;\n"; + else + OS << " return &" << Table.Name << "[Idx->_index];\n"; + + OS << "}\n"; +} + +void SearchableTableEmitter::emitLookupDeclaration(const GenericTable &Table, + const SearchIndex &Index, + raw_ostream &OS) { + OS << "const " << Table.CppTypeName << " *" << Index.Name << "("; + + ListSeparator LS; + for (const auto &Field : Index.Fields) + OS << LS << searchableFieldType(Table, Index, Field, TypeInArgument) << " " + << Field.Name; + OS << ")"; } -void SearchableTableEmitter::emitGenericTable(const GenericTable &Table) { - PI.searchableTablesEmitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_DECL").str(), - ST_DECL_OS); +void SearchableTableEmitter::emitGenericTable(const GenericTable &Table, + raw_ostream &OS) { + emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_DECL").str(), OS); // Emit the declarations for the functions that will perform lookup. if (Table.PrimaryKey) { - PI.searchableTablesEmitLookupDeclaration(Table, *Table.PrimaryKey, ST_DECL_OS); + emitLookupDeclaration(Table, *Table.PrimaryKey, OS); + OS << ";\n"; } for (const auto &Index : Table.Indices) { - PI.searchableTablesEmitLookupDeclaration(Table, *Index, ST_DECL_OS); + emitLookupDeclaration(Table, *Index, OS); + OS << ";\n"; } - PI.searchableTablesEmitEndif(ST_DECL_OS); - PI.searchableTablesEmitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_IMPL").str(), - ST_IMPL_OS); + OS << "#endif\n\n"; + + emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_IMPL").str(), OS); - PI.searchableTablesEmitMapI(Table); // The primary data table contains all the fields defined for this map. + OS << "constexpr " << Table.CppTypeName << " " << Table.Name << "[] = {\n"; for (unsigned i = 0; i < Table.Entries.size(); ++i) { Record *Entry = Table.Entries[i]; - PI.searchableTablesEmitMapII(); + OS << " { "; ListSeparator LS; - for (const auto &Field : Table.Fields) { - StringRef EnumName = (Field.IsIntrinsic ? getIntrinsic(Entry->getValueInit(Field.Name)).EnumName : ""); - PI.searchableTablesEmitMapIII(Table, LS, Field, - EnumName, Entry); - } + for (const auto &Field : Table.Fields) + OS << LS + << primaryRepresentation(Table.Locs[0], Field, + Entry->getValueInit(Field.Name)); - PI.searchableTablesEmitMapIV(i); + OS << " }, // " << i << "\n"; } - PI.searchableTablesEmitMapV(); + OS << " };\n"; // Indexes are sorted "{ Thing, PrimaryIdx }" arrays, so that a binary // search can be performed by "Thing". if (Table.PrimaryKey) - emitLookupFunction(Table, *Table.PrimaryKey, true); + emitLookupFunction(Table, *Table.PrimaryKey, true, OS); for (const auto &Index : Table.Indices) - emitLookupFunction(Table, *Index, false); + emitLookupFunction(Table, *Index, false, OS); - PI.searchableTablesEmitEndif(ST_IMPL_OS); + OS << "#endif\n\n"; } bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *TypeOf) { @@ -439,7 +656,7 @@ void SearchableTableEmitter::collectTableEntries( }); } -void SearchableTableEmitter::run() { +void SearchableTableEmitter::run(raw_ostream &OS) { // Emit tables in a deterministic order to avoid needless rebuilds. SmallVector, 4> Tables; DenseMap TableMap; @@ -593,52 +810,22 @@ void SearchableTableEmitter::run() { } // Emit everything. - for (const auto &Enum : Enums) { - std::string Guard = (Twine("GET_") + Enum->PreprocessorGuard + "_DECL").str(); - PreprocessorGuards.insert(Guard); - PI.searchableTablesEmitIfdef(Guard, ST_DECL_OS); - PI.searchableTablesEmitGenericEnum(*Enum); - PI.searchableTablesEmitEndif(ST_DECL_OS); - } + for (const auto &Enum : Enums) + emitGenericEnum(*Enum, OS); for (const auto &Table : Tables) - emitGenericTable(*Table); + emitGenericTable(*Table, OS); // Put all #undefs last, to allow multiple sections guarded by the same // define. for (const auto &Guard : PreprocessorGuards) - PI.searchableTablesEmitUndef(Guard); + OS << "#undef " << Guard << "\n"; } namespace llvm { void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS) { - formatted_raw_ostream FOS(OS); - PrinterLanguage const PLang = PrinterLLVM::getLanguage(); - PrinterLLVM *PI = nullptr; - switch (PLang) { - default: - PrintFatalNote( - "RegisterInfo backend does not support the selected ouput language."); - return; - case PRINTER_LANG_CPP: - PI = new PrinterLLVM(FOS); - break; - case PRINTER_LANG_CAPSTONE_C: - Record *IDef = RK.getClass("I"); - if (!IDef) - // If this is reached we need to implement the search for other classes which have Namespace set. - llvm_unreachable("Base instruction class \"I\" does not exist for this target."); - if (!IDef->getValue("Namespace")) - llvm_unreachable("Field \"Namespace\" does not exist."); - std::string TName = IDef->getValueAsString("Namespace").str(); - PI = new PrinterCapstone(FOS, TName); - break; - } - - SearchableTableEmitter(RK, *PI).run(); - PI->searchableTablesWriteFiles(); - delete PI; + SearchableTableEmitter(RK).run(OS); } } // End llvm namespace. diff --git a/llvm/utils/TableGen/SequenceToOffsetTable.h b/llvm/utils/TableGen/SequenceToOffsetTable.h index fcadfc9d2120..77a404d07b7d 100644 --- a/llvm/utils/TableGen/SequenceToOffsetTable.h +++ b/llvm/utils/TableGen/SequenceToOffsetTable.h @@ -15,7 +15,6 @@ #ifndef LLVM_UTILS_TABLEGEN_SEQUENCETOOFFSETTABLE_H #define LLVM_UTILS_TABLEGEN_SEQUENCETOOFFSETTABLE_H -#include "PrinterTypes.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/raw_ostream.h" #include @@ -69,19 +68,13 @@ class SequenceToOffsetTable { // Entries in the final table, or 0 before layout was called. unsigned Entries; - // The output language of the table. - PrinterLanguage PL; - // If set it will wrap the table content into a #ifndef CAPSTONE_DIET guard; - bool CSDietGuard; - // isSuffix - Returns true if A is a suffix of B. static bool isSuffix(const SeqT &A, const SeqT &B) { return A.size() <= B.size() && std::equal(A.rbegin(), A.rend(), B.rbegin()); } public: - SequenceToOffsetTable() : Entries(0), PL(PRINTER_LANG_CPP), CSDietGuard(false) {} - SequenceToOffsetTable(PrinterLanguage PL, bool CSDiet = false) : Entries(0), PL(PL), CSDietGuard(CSDiet) {} + SequenceToOffsetTable() : Entries(0) {} /// add - Add a sequence to the table. /// This must be called before layout(). @@ -129,39 +122,11 @@ class SequenceToOffsetTable { return I->second + (I->first.size() - Seq.size()); } - void emitStringLiteralDef(raw_ostream &OS, const llvm::Twine &Decl) const { - switch (PL) { - default: - llvm_unreachable("Language not specified to print table in."); - case PRINTER_LANG_CPP: - emitStringLiteralDefCPP(OS, Decl); - break; - case PRINTER_LANG_CAPSTONE_C: - emitStringLiteralDefCCS(OS, Decl); - break; - } - } - - void emit(raw_ostream &OS, - void (*Print)(raw_ostream&, ElemT), - const char *Term = "0") const { - switch (PL) { - default: - llvm_unreachable("Language not specified to print table in."); - case PRINTER_LANG_CPP: - emitCPP(OS, Print, Term); - break; - case PRINTER_LANG_CAPSTONE_C: - emitCCS(OS, Print, Term); - break; - } - } - /// `emitStringLiteralDef` - Print out the table as the body of an array /// initializer, where each element is a C string literal terminated by /// `\0`. Falls back to emitting a comma-separated integer list if /// `EmitLongStrLiterals` is false - void emitStringLiteralDefCPP(raw_ostream &OS, const llvm::Twine &Decl) const { + void emitStringLiteralDef(raw_ostream &OS, const llvm::Twine &Decl) const { assert(Entries && "Call layout() before emitStringLiteralDef()"); if (!EmitLongStrLiterals) { OS << Decl << " = {\n"; @@ -186,54 +151,11 @@ class SequenceToOffsetTable { << "#endif\n\n"; } - void emitStringLiteralDefCCS(raw_ostream &OS, const llvm::Twine &Decl) const { - assert(Entries && "Call layout() before emitStringLiteralDef()"); - if (!EmitLongStrLiterals) { - if (CSDietGuard) - OS << "#ifndef CAPSTONE_DIET\n"; - OS << Decl << " = {\n"; - emit(OS, printChar, "0"); - OS << " 0\n};\n"; - if (CSDietGuard) - OS << "#endif // CAPSTONE_DIET\n\n"; - OS << "\n"; - return; - } - - if (CSDietGuard) - OS << "#ifndef CAPSTONE_DIET\n"; - OS << Decl << " = {\n"; - for (auto I : Seqs) { - OS << " /* " << I.second << " */ \""; - OS.write_escaped(I.first); - OS << "\\0\"\n"; - } - OS << "};\n"; - if (CSDietGuard) - OS << "#endif // CAPSTONE_DIET\n\n"; - } - /// emit - Print out the table as the body of an array initializer. /// Use the Print function to print elements. - void emitCPP(raw_ostream &OS, - void (*Print)(raw_ostream&, ElemT), - const char *Term) const { - assert((empty() || Entries) && "Call layout() before emit()"); - for (typename SeqMap::const_iterator I = Seqs.begin(), E = Seqs.end(); - I != E; ++I) { - OS << " /* " << I->second << " */ "; - for (typename SeqT::const_iterator SI = I->first.begin(), - SE = I->first.end(); SI != SE; ++SI) { - Print(OS, *SI); - OS << ", "; - } - OS << Term << ",\n"; - } - } - - void emitCCS(raw_ostream &OS, + void emit(raw_ostream &OS, void (*Print)(raw_ostream&, ElemT), - const char *Term) const { + const char *Term = "0") const { assert((empty() || Entries) && "Call layout() before emit()"); for (typename SeqMap::const_iterator I = Seqs.begin(), E = Seqs.end(); I != E; ++I) { diff --git a/llvm/utils/TableGen/SubtargetEmitter.cpp b/llvm/utils/TableGen/SubtargetEmitter.cpp index f0ebaa5d8ed1..8afe6d37d0e0 100644 --- a/llvm/utils/TableGen/SubtargetEmitter.cpp +++ b/llvm/utils/TableGen/SubtargetEmitter.cpp @@ -10,9 +10,29 @@ // //===----------------------------------------------------------------------===// -#include "Printer.h" -#include "PrinterTypes.h" -#include "SubtargetEmitterTypes.h" +#include "CodeGenSchedule.h" +#include "CodeGenTarget.h" +#include "PredicateExpander.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCInstrItineraries.h" +#include "llvm/MC/MCSchedule.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TableGen/Error.h" +#include "llvm/TableGen/Record.h" +#include "llvm/TableGen/TableGenBackend.h" +#include +#include +#include +#include +#include +#include +#include using namespace llvm; @@ -21,8 +41,25 @@ using namespace llvm; namespace { class SubtargetEmitter { + // Each processor has a SchedClassDesc table with an entry for each SchedClass. + // The SchedClassDesc table indexes into a global write resource table, write + // latency table, and read advance table. + struct SchedClassTables { + std::vector> ProcSchedClasses; + std::vector WriteProcResources; + std::vector WriteLatencies; + std::vector WriterNames; + std::vector ReadAdvanceEntries; - SchedClassTablesT SchedClassTables; + // Reserve an invalid entry at index 0 + SchedClassTables() { + ProcSchedClasses.resize(1); + WriteProcResources.resize(1); + WriteLatencies.resize(1); + WriterNames.push_back("InvalidWrite"); + ReadAdvanceEntries.resize(1); + } + }; struct LessWriteProcResources { bool operator()(const MCWriteProcResEntry &LHS, @@ -35,23 +72,39 @@ class SubtargetEmitter { RecordKeeper &Records; CodeGenSchedModels &SchedModels; std::string Target; - PrinterLLVM &PI; - void Enumeration(DenseMap &FeatureMap); - void EmitSubtargetInfoMacroCalls(); - unsigned FeatureKeyValues( + void Enumeration(raw_ostream &OS, DenseMap &FeatureMap); + void EmitSubtargetInfoMacroCalls(raw_ostream &OS); + unsigned FeatureKeyValues(raw_ostream &OS, const DenseMap &FeatureMap); - unsigned CPUKeyValues( + unsigned CPUKeyValues(raw_ostream &OS, const DenseMap &FeatureMap); - void EmitStageAndOperandCycleData( + void FormItineraryStageString(const std::string &Names, + Record *ItinData, std::string &ItinString, + unsigned &NStages); + void FormItineraryOperandCycleString(Record *ItinData, std::string &ItinString, + unsigned &NOperandCycles); + void FormItineraryBypassString(const std::string &Names, + Record *ItinData, + std::string &ItinString, unsigned NOperandCycles); + void EmitStageAndOperandCycleData(raw_ostream &OS, std::vector> &ProcItinLists); - void EmitItineraries( + void EmitItineraries(raw_ostream &OS, std::vector> &ProcItinLists); - void EmitLoadStoreQueueInfo(const CodeGenProcModel &ProcModel); - void EmitExtraProcessorInfo(const CodeGenProcModel &ProcModel); - void EmitProcessorResources(const CodeGenProcModel &ProcModel); + unsigned EmitRegisterFileTables(const CodeGenProcModel &ProcModel, + raw_ostream &OS); + void EmitLoadStoreQueueInfo(const CodeGenProcModel &ProcModel, + raw_ostream &OS); + void EmitExtraProcessorInfo(const CodeGenProcModel &ProcModel, + raw_ostream &OS); + void EmitProcessorProp(raw_ostream &OS, const Record *R, StringRef Name, + char Separator); + void EmitProcessorResourceSubUnits(const CodeGenProcModel &ProcModel, + raw_ostream &OS); + void EmitProcessorResources(const CodeGenProcModel &ProcModel, + raw_ostream &OS); Record *FindWriteResources(const CodeGenSchedRW &SchedWrite, const CodeGenProcModel &ProcModel); Record *FindReadAdvance(const CodeGenSchedRW &SchedRead, @@ -59,24 +112,25 @@ class SubtargetEmitter { void ExpandProcResources(RecVec &PRVec, std::vector &Cycles, const CodeGenProcModel &ProcModel); void GenSchedClassTables(const CodeGenProcModel &ProcModel, - SchedClassTablesT &SchedTables); - void EmitSchedClassTables(SchedClassTablesT &SchedTables); - void EmitProcessorModels(); - void EmitSchedModelHelpers(const std::string &ClassName); - void emitSchedModelHelpersImpl(bool OnlyExpandMCInstPredicates = false); - void emitGenMCSubtargetInfo(); - void EmitMCInstrAnalysisPredicateFunctions(); - - void EmitSchedModel(); - void EmitHwModeCheck(const std::string &ClassName); - void ParseFeaturesFunction(); + SchedClassTables &SchedTables); + void EmitSchedClassTables(SchedClassTables &SchedTables, raw_ostream &OS); + void EmitProcessorModels(raw_ostream &OS); + void EmitSchedModelHelpers(const std::string &ClassName, raw_ostream &OS); + void emitSchedModelHelpersImpl(raw_ostream &OS, + bool OnlyExpandMCInstPredicates = false); + void emitGenMCSubtargetInfo(raw_ostream &OS); + void EmitMCInstrAnalysisPredicateFunctions(raw_ostream &OS); + + void EmitSchedModel(raw_ostream &OS); + void EmitHwModeCheck(const std::string &ClassName, raw_ostream &OS); + void ParseFeaturesFunction(raw_ostream &OS); public: - SubtargetEmitter(RecordKeeper &R, CodeGenTarget &TGT, PrinterLLVM &PI) + SubtargetEmitter(RecordKeeper &R, CodeGenTarget &TGT) : TGT(TGT), Records(R), SchedModels(TGT.getSchedModels()), - Target(TGT.getName()), PI(PI) {} + Target(TGT.getName()) {} - void run(); + void run(raw_ostream &o); }; } // end anonymous namespace @@ -84,7 +138,7 @@ class SubtargetEmitter { // // Enumeration - Emit the specified class as an enumeration. // -void SubtargetEmitter::Enumeration( +void SubtargetEmitter::Enumeration(raw_ostream &OS, DenseMap &FeatureMap) { // Get all records of class and sort std::vector DefList = @@ -97,25 +151,52 @@ void SubtargetEmitter::Enumeration( if (N + 1 > MAX_SUBTARGET_FEATURES) PrintFatalError("Too many subtarget features! Bump MAX_SUBTARGET_FEATURES."); - PI.emitNamespace(Target, true); - PI.subtargetEmitFeatureEnum(FeatureMap, DefList, N); - PI.emitNamespace(Target, false); + OS << "namespace " << Target << " {\n"; + + // Open enumeration. + OS << "enum {\n"; + + // For each record + for (unsigned i = 0; i < N; ++i) { + // Next record + Record *Def = DefList[i]; + + // Get and emit name + OS << " " << Def->getName() << " = " << i << ",\n"; + + // Save the index for this feature. + FeatureMap[Def] = i; + } + + OS << " " + << "NumSubtargetFeatures = " << N << "\n"; + + // Close enumeration and namespace + OS << "};\n"; + OS << "} // end namespace " << Target << "\n"; } -static void printFeatureMask(PrinterLLVM const &PI, RecVec &FeatureList, +static void printFeatureMask(raw_ostream &OS, RecVec &FeatureList, const DenseMap &FeatureMap) { std::array Mask = {}; for (const Record *Feature : FeatureList) { unsigned Bit = FeatureMap.lookup(Feature); Mask[Bit / 64] |= 1ULL << (Bit % 64); } - PI.subtargetEmitPrintFeatureMask(Mask); + + OS << "{ { { "; + for (unsigned i = 0; i != Mask.size(); ++i) { + OS << "0x"; + OS.write_hex(Mask[i]); + OS << "ULL, "; + } + OS << "} } }"; } /// Emit some information about the SubtargetFeature as calls to a macro so /// that they can be used from C++. -void SubtargetEmitter::EmitSubtargetInfoMacroCalls() { - PI.emitIncludeToggle("GET_SUBTARGETINFO_MACRO", true, true, true); +void SubtargetEmitter::EmitSubtargetInfoMacroCalls(raw_ostream &OS) { + OS << "\n#ifdef GET_SUBTARGETINFO_MACRO\n"; std::vector FeatureList = Records.getAllDerivedDefinitions("SubtargetFeature"); @@ -131,9 +212,21 @@ void SubtargetEmitter::EmitSubtargetInfoMacroCalls() { if (!IsBool) continue; - PI.subtargetEmitGetSTIMacro(Value, Attribute); + // Some features default to true, with values set to false if enabled. + const char *Default = Value == "false" ? "true" : "false"; + + // Define the getter with lowercased first char: xxxYyy() { return XxxYyy; } + const std::string Getter = + Attribute.substr(0, 1).lower() + Attribute.substr(1).str(); + + OS << "GET_SUBTARGETINFO_MACRO(" << Attribute << ", " << Default << ", " + << Getter << ")\n"; } - PI.emitIncludeToggle("GET_SUBTARGETINFO_MACRO", false, true, true); + OS << "#undef GET_SUBTARGETINFO_MACRO\n"; + OS << "#endif // GET_SUBTARGETINFO_MACRO\n\n"; + + OS << "\n#ifdef GET_SUBTARGETINFO_MC_DESC\n"; + OS << "#undef GET_SUBTARGETINFO_MC_DESC\n\n"; } // @@ -141,7 +234,7 @@ void SubtargetEmitter::EmitSubtargetInfoMacroCalls() { // command line. // unsigned SubtargetEmitter::FeatureKeyValues( - const DenseMap &FeatureMap) { + raw_ostream &OS, const DenseMap &FeatureMap) { // Gather and sort all the features std::vector FeatureList = Records.getAllDerivedDefinitions("SubtargetFeature"); @@ -152,7 +245,9 @@ unsigned SubtargetEmitter::FeatureKeyValues( llvm::sort(FeatureList, LessRecordFieldName()); // Begin feature table - PI.subtargetEmitFeatureKVHeader(Target); + OS << "// Sorted (by key) array of values for CPU features.\n" + << "extern const llvm::SubtargetFeatureKV " << Target + << "FeatureKV[] = {\n"; // For each feature unsigned NumFeatures = 0; @@ -164,19 +259,22 @@ unsigned SubtargetEmitter::FeatureKeyValues( if (CommandLineName.empty()) continue; - PI.subtargetEmitFeatureKVPartI(Target, CommandLineName, Name, Desc); + // Emit as { "feature", "description", { featureEnum }, { i1 , i2 , ... , in } } + OS << " { " + << "\"" << CommandLineName << "\", " + << "\"" << Desc << "\", " + << Target << "::" << Name << ", "; RecVec ImpliesList = Feature->getValueAsListOfDefs("Implies"); - printFeatureMask(PI, ImpliesList, FeatureMap); - - PI.subtargetEmitFeatureKVPartII(); + printFeatureMask(OS, ImpliesList, FeatureMap); + OS << " },\n"; ++NumFeatures; } // End feature table - PI.subtargetEmitFeatureKVEnd(); + OS << "};\n"; return NumFeatures; } @@ -186,7 +284,7 @@ unsigned SubtargetEmitter::FeatureKeyValues( // line. // unsigned -SubtargetEmitter::CPUKeyValues( +SubtargetEmitter::CPUKeyValues(raw_ostream &OS, const DenseMap &FeatureMap) { // Gather and sort processor information std::vector ProcessorList = @@ -194,7 +292,9 @@ SubtargetEmitter::CPUKeyValues( llvm::sort(ProcessorList, LessRecordFieldName()); // Begin processor table - PI.subtargetEmitCPUKVHeader(Target); + OS << "// Sorted (by key) array of values for CPU subtype.\n" + << "extern const llvm::SubtargetSubTypeKV " << Target + << "SubTypeKV[] = {\n"; // For each processor for (Record *Processor : ProcessorList) { @@ -202,42 +302,169 @@ SubtargetEmitter::CPUKeyValues( RecVec FeatureList = Processor->getValueAsListOfDefs("Features"); RecVec TuneFeatureList = Processor->getValueAsListOfDefs("TuneFeatures"); - PI.subtargetEmitCPUKVPartI(Name); + // Emit as { "cpu", "description", 0, { f1 , f2 , ... fn } }, + OS << " { " + << "\"" << Name << "\", "; - printFeatureMask(PI, FeatureList, FeatureMap); - PI.subtargetEmitCPUKVPartII(); - printFeatureMask(PI, TuneFeatureList, FeatureMap); + printFeatureMask(OS, FeatureList, FeatureMap); + OS << ", "; + printFeatureMask(OS, TuneFeatureList, FeatureMap); // Emit the scheduler model pointer. const std::string &ProcModelName = SchedModels.getModelForProc(Processor).ModelName; - PI.subtargetEmitCPUKVPartIII(ProcModelName); + OS << ", &" << ProcModelName << " },\n"; } // End processor table - PI.subtargetEmitCPUKVEnd(); + OS << "};\n"; + return ProcessorList.size(); } +// +// FormItineraryStageString - Compose a string containing the stage +// data initialization for the specified itinerary. N is the number +// of stages. +// +void SubtargetEmitter::FormItineraryStageString(const std::string &Name, + Record *ItinData, + std::string &ItinString, + unsigned &NStages) { + // Get states list + RecVec StageList = ItinData->getValueAsListOfDefs("Stages"); + + // For each stage + unsigned N = NStages = StageList.size(); + for (unsigned i = 0; i < N;) { + // Next stage + const Record *Stage = StageList[i]; + + // Form string as ,{ cycles, u1 | u2 | ... | un, timeinc, kind } + int Cycles = Stage->getValueAsInt("Cycles"); + ItinString += " { " + itostr(Cycles) + ", "; + + // Get unit list + RecVec UnitList = Stage->getValueAsListOfDefs("Units"); + + // For each unit + for (unsigned j = 0, M = UnitList.size(); j < M;) { + // Add name and bitwise or + ItinString += Name + "FU::" + UnitList[j]->getName().str(); + if (++j < M) ItinString += " | "; + } + + int TimeInc = Stage->getValueAsInt("TimeInc"); + ItinString += ", " + itostr(TimeInc); + + int Kind = Stage->getValueAsInt("Kind"); + ItinString += ", (llvm::InstrStage::ReservationKinds)" + itostr(Kind); + + // Close off stage + ItinString += " }"; + if (++i < N) ItinString += ", "; + } +} + +// +// FormItineraryOperandCycleString - Compose a string containing the +// operand cycle initialization for the specified itinerary. N is the +// number of operands that has cycles specified. +// +void SubtargetEmitter::FormItineraryOperandCycleString(Record *ItinData, + std::string &ItinString, unsigned &NOperandCycles) { + // Get operand cycle list + std::vector OperandCycleList = + ItinData->getValueAsListOfInts("OperandCycles"); + + // For each operand cycle + NOperandCycles = OperandCycleList.size(); + ListSeparator LS; + for (int OCycle : OperandCycleList) { + // Next operand cycle + ItinString += LS; + ItinString += " " + itostr(OCycle); + } +} + +void SubtargetEmitter::FormItineraryBypassString(const std::string &Name, + Record *ItinData, + std::string &ItinString, + unsigned NOperandCycles) { + RecVec BypassList = ItinData->getValueAsListOfDefs("Bypasses"); + unsigned N = BypassList.size(); + unsigned i = 0; + ListSeparator LS; + for (; i < N; ++i) { + ItinString += LS; + ItinString += Name + "Bypass::" + BypassList[i]->getName().str(); + } + for (; i < NOperandCycles; ++i) { + ItinString += LS; + ItinString += " 0"; + } +} + // // EmitStageAndOperandCycleData - Generate unique itinerary stages and operand // cycle tables. Create a list of InstrItinerary objects (ProcItinLists) indexed // by CodeGenSchedClass::Index. // void SubtargetEmitter:: -EmitStageAndOperandCycleData(std::vector> - &ProcItinLists) { +EmitStageAndOperandCycleData(raw_ostream &OS, + std::vector> + &ProcItinLists) { + // Multiple processor models may share an itinerary record. Emit it once. + SmallPtrSet ItinsDefSet; + + // Emit functional units for all the itineraries. + for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { + + if (!ItinsDefSet.insert(ProcModel.ItinsDef).second) + continue; - PI.subtargetEmitFunctionalItinaryUnits(SchedModels); + RecVec FUs = ProcModel.ItinsDef->getValueAsListOfDefs("FU"); + if (FUs.empty()) + continue; + + StringRef Name = ProcModel.ItinsDef->getName(); + OS << "\n// Functional units for \"" << Name << "\"\n" + << "namespace " << Name << "FU {\n"; + + for (unsigned j = 0, FUN = FUs.size(); j < FUN; ++j) + OS << " const InstrStage::FuncUnits " << FUs[j]->getName() + << " = 1ULL << " << j << ";\n"; + + OS << "} // end namespace " << Name << "FU\n"; + + RecVec BPs = ProcModel.ItinsDef->getValueAsListOfDefs("BP"); + if (!BPs.empty()) { + OS << "\n// Pipeline forwarding paths for itineraries \"" << Name + << "\"\n" << "namespace " << Name << "Bypass {\n"; + + OS << " const unsigned NoBypass = 0;\n"; + for (unsigned j = 0, BPN = BPs.size(); j < BPN; ++j) + OS << " const unsigned " << BPs[j]->getName() + << " = 1 << " << j << ";\n"; + + OS << "} // end namespace " << Name << "Bypass\n"; + } + } // Begin stages table - std::string StageTable = PI.subtargetGetBeginStageTable(Target); + std::string StageTable = "\nextern const llvm::InstrStage " + Target + + "Stages[] = {\n"; + StageTable += " { 0, 0, 0, llvm::InstrStage::Required }, // No itinerary\n"; // Begin operand cycle table - std::string OperandCycleTable = PI.subtargetGetBeginOperandCycleTable(Target); + std::string OperandCycleTable = "extern const unsigned " + Target + + "OperandCycles[] = {\n"; + OperandCycleTable += " 0, // No itinerary\n"; // Begin pipeline bypass table - std::string BypassTable = PI.subtargetGetBeginBypassTable(Target); + std::string BypassTable = "extern const unsigned " + Target + + "ForwardingPaths[] = {\n"; + BypassTable += " 0, // No itinerary\n"; // For each Itinerary across all processors, add a unique entry to the stages, // operand cycles, and pipeline bypass tables. Then add the new Itinerary @@ -269,24 +496,19 @@ EmitStageAndOperandCycleData(std::vector> std::string ItinStageString; unsigned NStages = 0; if (ItinData) - PI.subtargetFormItineraryStageString(std::string(Name), - ItinData, - ItinStageString, - NStages); + FormItineraryStageString(std::string(Name), ItinData, ItinStageString, + NStages); // Get string and operand cycle count std::string ItinOperandCycleString; unsigned NOperandCycles = 0; std::string ItinBypassString; if (ItinData) { - PI.subtargetFormItineraryOperandCycleString(ItinData, - ItinOperandCycleString, - NOperandCycles); - - PI.subtargetFormItineraryBypassString(std::string(Name), - ItinData, - ItinBypassString, - NOperandCycles); + FormItineraryOperandCycleString(ItinData, ItinOperandCycleString, + NOperandCycles); + + FormItineraryBypassString(std::string(Name), ItinData, ItinBypassString, + NOperandCycles); } // Check to see if stage already exists and create if it doesn't @@ -295,10 +517,10 @@ EmitStageAndOperandCycleData(std::vector> FindStage = ItinStageMap[ItinStageString]; if (FindStage == 0) { // Emit as { cycles, u1 | u2 | ... | un, timeinc }, // indices - StageTable += PI.subtargetGetStageEntryPartI(ItinStageString, StageCount); + StageTable += ItinStageString + ", // " + itostr(StageCount); if (NStages > 1) - StageTable += PI.subtargetGetStageEntryPartII(StageCount, NStages); - StageTable += PI.subtargetGetStageEntryPartIII(); + StageTable += "-" + itostr(StageCount + NStages - 1); + StageTable += "\n"; // Record Itin class number. ItinStageMap[ItinStageString] = FindStage = StageCount; StageCount += NStages; @@ -312,20 +534,17 @@ EmitStageAndOperandCycleData(std::vector> FindOperandCycle = ItinOperandMap[ItinOperandString]; if (FindOperandCycle == 0) { // Emit as cycle, // index - OperandCycleTable += PI.subtargetGetOperandCycleEntryPartI( - ItinOperandCycleString); + OperandCycleTable += ItinOperandCycleString + ", // "; std::string OperandIdxComment = itostr(OperandCycleCount); if (NOperandCycles > 1) - OperandIdxComment += PI.subtargetGetOperandCycleEntryPartII( - OperandCycleCount, NOperandCycles); - OperandCycleTable += PI.subtargetGetOperandCycleEntryPartIII( - OperandIdxComment); + OperandIdxComment += "-" + + itostr(OperandCycleCount + NOperandCycles - 1); + OperandCycleTable += OperandIdxComment + "\n"; // Record Itin class number. ItinOperandMap[ItinOperandCycleString] = FindOperandCycle = OperandCycleCount; // Emit as bypass, // index - BypassTable += PI.subtargetGetOperandCycleEntryPartIV( - ItinBypassString, OperandIdxComment); + BypassTable += ItinBypassString + ", // " + OperandIdxComment + "\n"; OperandCycleCount += NOperandCycles; } } @@ -346,15 +565,20 @@ EmitStageAndOperandCycleData(std::vector> } // Closing stage - StageTable += PI.subtargetGetEndStageTable(); + StageTable += " { 0, 0, 0, llvm::InstrStage::Required } // End stages\n"; + StageTable += "};\n"; // Closing operand cycles - OperandCycleTable += PI.subtargetGetEndOperandCycleTable(); + OperandCycleTable += " 0 // End operand cycles\n"; + OperandCycleTable += "};\n"; - BypassTable += PI.subtargetGetEndBypassTable(); + BypassTable += " 0 // End bypass tables\n"; + BypassTable += "};\n"; // Emit tables. - PI.subtargetEmitStageAndSycleTables(StageTable, OperandCycleTable, BypassTable); + OS << StageTable; + OS << OperandCycleTable; + OS << BypassTable; } // @@ -364,7 +588,7 @@ EmitStageAndOperandCycleData(std::vector> // CodeGenSchedClass::Index. // void SubtargetEmitter:: -EmitItineraries( +EmitItineraries(raw_ostream &OS, std::vector> &ProcItinLists) { // Multiple processor models may share an itinerary record. Emit it once. SmallPtrSet ItinsDefSet; @@ -372,10 +596,10 @@ EmitItineraries( // For each processor's machine model std::vector>::iterator ProcItinListsIter = ProcItinLists.begin(); - for (CodeGenSchedModels::ProcIter PIM = SchedModels.procModelBegin(), - PE = SchedModels.procModelEnd(); PIM != PE; ++PIM, ++ProcItinListsIter) { + for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), + PE = SchedModels.procModelEnd(); PI != PE; ++PI, ++ProcItinListsIter) { - Record *ItinsDef = PIM->ItinsDef; + Record *ItinsDef = PI->ItinsDef; if (!ItinsDefSet.insert(ItinsDef).second) continue; @@ -388,14 +612,72 @@ EmitItineraries( if (ItinList.empty()) continue; - PI.subtargetEmitProcessorItineraryTable(ItinsDef->getName().str(), - ItinList, - SchedModels); + OS << "\n"; + OS << "static const llvm::InstrItinerary "; + + // Begin processor itinerary table + OS << ItinsDef->getName() << "[] = {\n"; + + // For each itinerary class in CodeGenSchedClass::Index order. + for (unsigned j = 0, M = ItinList.size(); j < M; ++j) { + InstrItinerary &Intinerary = ItinList[j]; + + // Emit Itinerary in the form of + // { firstStage, lastStage, firstCycle, lastCycle } // index + OS << " { " << + Intinerary.NumMicroOps << ", " << + Intinerary.FirstStage << ", " << + Intinerary.LastStage << ", " << + Intinerary.FirstOperandCycle << ", " << + Intinerary.LastOperandCycle << " }" << + ", // " << j << " " << SchedModels.getSchedClass(j).Name << "\n"; + } + // End processor itinerary table + OS << " { 0, uint16_t(~0U), uint16_t(~0U), uint16_t(~0U), uint16_t(~0U) }" + "// end marker\n"; + OS << "};\n"; } } +// Emit either the value defined in the TableGen Record, or the default +// value defined in the C++ header. The Record is null if the processor does not +// define a model. +void SubtargetEmitter::EmitProcessorProp(raw_ostream &OS, const Record *R, + StringRef Name, char Separator) { + OS << " "; + int V = R ? R->getValueAsInt(Name) : -1; + if (V >= 0) + OS << V << Separator << " // " << Name; + else + OS << "MCSchedModel::Default" << Name << Separator; + OS << '\n'; +} + +void SubtargetEmitter::EmitProcessorResourceSubUnits( + const CodeGenProcModel &ProcModel, raw_ostream &OS) { + OS << "\nstatic const unsigned " << ProcModel.ModelName + << "ProcResourceSubUnits[] = {\n" + << " 0, // Invalid\n"; + + for (unsigned i = 0, e = ProcModel.ProcResourceDefs.size(); i < e; ++i) { + Record *PRDef = ProcModel.ProcResourceDefs[i]; + if (!PRDef->isSubClassOf("ProcResGroup")) + continue; + RecVec ResUnits = PRDef->getValueAsListOfDefs("Resources"); + for (Record *RUDef : ResUnits) { + Record *const RU = + SchedModels.findProcResUnits(RUDef, ProcModel, PRDef->getLoc()); + for (unsigned J = 0; J < RU->getValueAsInt("NumUnits"); ++J) { + OS << " " << ProcModel.getProcResourceIdx(RU) << ", "; + } + } + OS << " // " << PRDef->getName() << "\n"; + } + OS << "};\n"; +} + static void EmitRetireControlUnitInfo(const CodeGenProcModel &ProcModel, - PrinterLLVM &PI) { + raw_ostream &OS) { int64_t ReorderBufferSize = 0, MaxRetirePerCycle = 0; if (Record *RCU = ProcModel.RetireControlUnit) { ReorderBufferSize = @@ -404,18 +686,88 @@ static void EmitRetireControlUnitInfo(const CodeGenProcModel &ProcModel, std::max(MaxRetirePerCycle, RCU->getValueAsInt("MaxRetirePerCycle")); } - PI.subtargetEmitReorderBufferSize(ReorderBufferSize); - PI.subtargetEmitMaxRetirePerCycle(MaxRetirePerCycle); + OS << ReorderBufferSize << ", // ReorderBufferSize\n "; + OS << MaxRetirePerCycle << ", // MaxRetirePerCycle\n "; +} + +static void EmitRegisterFileInfo(const CodeGenProcModel &ProcModel, + unsigned NumRegisterFiles, + unsigned NumCostEntries, raw_ostream &OS) { + if (NumRegisterFiles) + OS << ProcModel.ModelName << "RegisterFiles,\n " << (1 + NumRegisterFiles); + else + OS << "nullptr,\n 0"; + + OS << ", // Number of register files.\n "; + if (NumCostEntries) + OS << ProcModel.ModelName << "RegisterCosts,\n "; + else + OS << "nullptr,\n "; + OS << NumCostEntries << ", // Number of register cost entries.\n"; +} + +unsigned +SubtargetEmitter::EmitRegisterFileTables(const CodeGenProcModel &ProcModel, + raw_ostream &OS) { + if (llvm::all_of(ProcModel.RegisterFiles, [](const CodeGenRegisterFile &RF) { + return RF.hasDefaultCosts(); + })) + return 0; + + // Print the RegisterCost table first. + OS << "\n// {RegisterClassID, Register Cost, AllowMoveElimination }\n"; + OS << "static const llvm::MCRegisterCostEntry " << ProcModel.ModelName + << "RegisterCosts" + << "[] = {\n"; + + for (const CodeGenRegisterFile &RF : ProcModel.RegisterFiles) { + // Skip register files with a default cost table. + if (RF.hasDefaultCosts()) + continue; + // Add entries to the cost table. + for (const CodeGenRegisterCost &RC : RF.Costs) { + OS << " { "; + Record *Rec = RC.RCDef; + if (Rec->getValue("Namespace")) + OS << Rec->getValueAsString("Namespace") << "::"; + OS << Rec->getName() << "RegClassID, " << RC.Cost << ", " + << RC.AllowMoveElimination << "},\n"; + } + } + OS << "};\n"; + + // Now generate a table with register file info. + OS << "\n // {Name, #PhysRegs, #CostEntries, IndexToCostTbl, " + << "MaxMovesEliminatedPerCycle, AllowZeroMoveEliminationOnly }\n"; + OS << "static const llvm::MCRegisterFileDesc " << ProcModel.ModelName + << "RegisterFiles" + << "[] = {\n" + << " { \"InvalidRegisterFile\", 0, 0, 0, 0, 0 },\n"; + unsigned CostTblIndex = 0; + + for (const CodeGenRegisterFile &RD : ProcModel.RegisterFiles) { + OS << " { "; + OS << '"' << RD.Name << '"' << ", " << RD.NumPhysRegs << ", "; + unsigned NumCostEntries = RD.Costs.size(); + OS << NumCostEntries << ", " << CostTblIndex << ", " + << RD.MaxMovesEliminatedPerCycle << ", " + << RD.AllowZeroMoveEliminationOnly << "},\n"; + CostTblIndex += NumCostEntries; + } + OS << "};\n"; + + return CostTblIndex; } -void SubtargetEmitter::EmitLoadStoreQueueInfo(const CodeGenProcModel &ProcModel) { +void SubtargetEmitter::EmitLoadStoreQueueInfo(const CodeGenProcModel &ProcModel, + raw_ostream &OS) { unsigned QueueID = 0; if (ProcModel.LoadQueue) { const Record *Queue = ProcModel.LoadQueue->getValueAsDef("QueueDescriptor"); QueueID = 1 + std::distance(ProcModel.ProcResourceDefs.begin(), find(ProcModel.ProcResourceDefs, Queue)); } - PI.subtargetEmitResourceDescriptorLoadQueue(QueueID); + OS << " " << QueueID << ", // Resource Descriptor for the Load Queue\n"; QueueID = 0; if (ProcModel.StoreQueue) { @@ -424,40 +776,42 @@ void SubtargetEmitter::EmitLoadStoreQueueInfo(const CodeGenProcModel &ProcModel) QueueID = 1 + std::distance(ProcModel.ProcResourceDefs.begin(), find(ProcModel.ProcResourceDefs, Queue)); } - PI.subtargetEmitResourceDescriptorStoreQueue(QueueID); + OS << " " << QueueID << ", // Resource Descriptor for the Store Queue\n"; } -void SubtargetEmitter::EmitExtraProcessorInfo(const CodeGenProcModel &ProcModel) { +void SubtargetEmitter::EmitExtraProcessorInfo(const CodeGenProcModel &ProcModel, + raw_ostream &OS) { // Generate a table of register file descriptors (one entry per each user // defined register file), and a table of register costs. - unsigned NumCostEntries; - if (llvm::all_of(ProcModel.RegisterFiles, [](const CodeGenRegisterFile &RF) { - return RF.hasDefaultCosts(); - })) - NumCostEntries = 0; - else - NumCostEntries = PI.subtargetEmitRegisterFileTables(ProcModel); + unsigned NumCostEntries = EmitRegisterFileTables(ProcModel, OS); // Now generate a table for the extra processor info. - PI.subtargetEmitMCExtraProcInfoTableHeader(ProcModel.ModelName); + OS << "\nstatic const llvm::MCExtraProcessorInfo " << ProcModel.ModelName + << "ExtraInfo = {\n "; // Add information related to the retire control unit. - EmitRetireControlUnitInfo(ProcModel, PI); + EmitRetireControlUnitInfo(ProcModel, OS); // Add information related to the register files (i.e. where to find register // file descriptors and register costs). - PI.subtargetEmitRegisterFileInfo(ProcModel, ProcModel.RegisterFiles.size(), - NumCostEntries); + EmitRegisterFileInfo(ProcModel, ProcModel.RegisterFiles.size(), + NumCostEntries, OS); // Add information about load/store queues. - EmitLoadStoreQueueInfo(ProcModel); - PI.subtargetEmitMCExtraProcInfoTableEnd(); + EmitLoadStoreQueueInfo(ProcModel, OS); + + OS << "};\n"; } -void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel) { - PI.subtargetEmitProcessorResourceSubUnits(ProcModel, SchedModels); +void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel, + raw_ostream &OS) { + EmitProcessorResourceSubUnits(ProcModel, OS); - PI.subtargetEmitMCProcResourceDescHeader(ProcModel.ModelName); + OS << "\n// {Name, NumUnits, SuperIdx, BufferSize, SubUnitsIdxBegin}\n"; + OS << "static const llvm::MCProcResourceDesc " << ProcModel.ModelName + << "ProcResources" + << "[] = {\n" + << " {\"InvalidUnit\", 0, 0, 0, 0},\n"; unsigned SubUnitsOffset = 1; for (unsigned i = 0, e = ProcModel.ProcResourceDefs.size(); i < e; ++i) { @@ -485,16 +839,23 @@ void SubtargetEmitter::EmitProcessorResources(const CodeGenProcModel &ProcModel) } NumUnits = PRDef->getValueAsInt("NumUnits"); } - PI.subtargetEmitMCProcResourceDesc(PRDef, - SuperDef, - ProcModel.ModelName, - SubUnitsOffset, - SuperIdx, - NumUnits, - BufferSize, - i, SubUnitsBeginOffset); + // Emit the ProcResourceDesc + OS << " {\"" << PRDef->getName() << "\", "; + if (PRDef->getName().size() < 15) + OS.indent(15 - PRDef->getName().size()); + OS << NumUnits << ", " << SuperIdx << ", " << BufferSize << ", "; + if (SubUnitsBeginOffset != SubUnitsOffset) { + OS << ProcModel.ModelName << "ProcResourceSubUnits + " + << SubUnitsBeginOffset; + } else { + OS << "nullptr"; + } + OS << "}, // #" << i+1; + if (SuperDef) + OS << ", Super=" << SuperDef->getName(); + OS << "\n"; } - PI.subtargetEmitMCProcResourceDescEnd(); + OS << "};\n"; } // Find the WriteRes Record that defines processor resources for this @@ -652,7 +1013,7 @@ void SubtargetEmitter::ExpandProcResources(RecVec &PRVec, // Generate the SchedClass table for this processor and update global // tables. Must be called for each processor in order. void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, - SchedClassTablesT &SchedTables) { + SchedClassTables &SchedTables) { SchedTables.ProcSchedClasses.resize(SchedTables.ProcSchedClasses.size() + 1); if (!ProcModel.hasInstrSchedModel()) return; @@ -909,56 +1270,206 @@ void SubtargetEmitter::GenSchedClassTables(const CodeGenProcModel &ProcModel, } } -void SubtargetEmitter::EmitProcessorModels() { +// Emit SchedClass tables for all processors and associated global tables. +void SubtargetEmitter::EmitSchedClassTables(SchedClassTables &SchedTables, + raw_ostream &OS) { + // Emit global WriteProcResTable. + OS << "\n// {ProcResourceIdx, Cycles}\n" + << "extern const llvm::MCWriteProcResEntry " + << Target << "WriteProcResTable[] = {\n" + << " { 0, 0}, // Invalid\n"; + for (unsigned WPRIdx = 1, WPREnd = SchedTables.WriteProcResources.size(); + WPRIdx != WPREnd; ++WPRIdx) { + MCWriteProcResEntry &WPREntry = SchedTables.WriteProcResources[WPRIdx]; + OS << " {" << format("%2d", WPREntry.ProcResourceIdx) << ", " + << format("%2d", WPREntry.Cycles) << "}"; + if (WPRIdx + 1 < WPREnd) + OS << ','; + OS << " // #" << WPRIdx << '\n'; + } + OS << "}; // " << Target << "WriteProcResTable\n"; + + // Emit global WriteLatencyTable. + OS << "\n// {Cycles, WriteResourceID}\n" + << "extern const llvm::MCWriteLatencyEntry " + << Target << "WriteLatencyTable[] = {\n" + << " { 0, 0}, // Invalid\n"; + for (unsigned WLIdx = 1, WLEnd = SchedTables.WriteLatencies.size(); + WLIdx != WLEnd; ++WLIdx) { + MCWriteLatencyEntry &WLEntry = SchedTables.WriteLatencies[WLIdx]; + OS << " {" << format("%2d", WLEntry.Cycles) << ", " + << format("%2d", WLEntry.WriteResourceID) << "}"; + if (WLIdx + 1 < WLEnd) + OS << ','; + OS << " // #" << WLIdx << " " << SchedTables.WriterNames[WLIdx] << '\n'; + } + OS << "}; // " << Target << "WriteLatencyTable\n"; + + // Emit global ReadAdvanceTable. + OS << "\n// {UseIdx, WriteResourceID, Cycles}\n" + << "extern const llvm::MCReadAdvanceEntry " + << Target << "ReadAdvanceTable[] = {\n" + << " {0, 0, 0}, // Invalid\n"; + for (unsigned RAIdx = 1, RAEnd = SchedTables.ReadAdvanceEntries.size(); + RAIdx != RAEnd; ++RAIdx) { + MCReadAdvanceEntry &RAEntry = SchedTables.ReadAdvanceEntries[RAIdx]; + OS << " {" << RAEntry.UseIdx << ", " + << format("%2d", RAEntry.WriteResourceID) << ", " + << format("%2d", RAEntry.Cycles) << "}"; + if (RAIdx + 1 < RAEnd) + OS << ','; + OS << " // #" << RAIdx << '\n'; + } + OS << "}; // " << Target << "ReadAdvanceTable\n"; + + // Emit a SchedClass table for each processor. + for (CodeGenSchedModels::ProcIter PI = SchedModels.procModelBegin(), + PE = SchedModels.procModelEnd(); PI != PE; ++PI) { + if (!PI->hasInstrSchedModel()) + continue; + + std::vector &SCTab = + SchedTables.ProcSchedClasses[1 + (PI - SchedModels.procModelBegin())]; + + OS << "\n// {Name, NumMicroOps, BeginGroup, EndGroup, RetireOOO," + << " WriteProcResIdx,#, WriteLatencyIdx,#, ReadAdvanceIdx,#}\n"; + OS << "static const llvm::MCSchedClassDesc " + << PI->ModelName << "SchedClasses[] = {\n"; + + // The first class is always invalid. We no way to distinguish it except by + // name and position. + assert(SchedModels.getSchedClass(0).Name == "NoInstrModel" + && "invalid class not first"); + OS << " {DBGFIELD(\"InvalidSchedClass\") " + << MCSchedClassDesc::InvalidNumMicroOps + << ", false, false, false, 0, 0, 0, 0, 0, 0},\n"; + + for (unsigned SCIdx = 1, SCEnd = SCTab.size(); SCIdx != SCEnd; ++SCIdx) { + MCSchedClassDesc &MCDesc = SCTab[SCIdx]; + const CodeGenSchedClass &SchedClass = SchedModels.getSchedClass(SCIdx); + OS << " {DBGFIELD(\"" << SchedClass.Name << "\") "; + if (SchedClass.Name.size() < 18) + OS.indent(18 - SchedClass.Name.size()); + OS << MCDesc.NumMicroOps + << ", " << ( MCDesc.BeginGroup ? "true" : "false" ) + << ", " << ( MCDesc.EndGroup ? "true" : "false" ) + << ", " << ( MCDesc.RetireOOO ? "true" : "false" ) + << ", " << format("%2d", MCDesc.WriteProcResIdx) + << ", " << MCDesc.NumWriteProcResEntries + << ", " << format("%2d", MCDesc.WriteLatencyIdx) + << ", " << MCDesc.NumWriteLatencyEntries + << ", " << format("%2d", MCDesc.ReadAdvanceIdx) + << ", " << MCDesc.NumReadAdvanceEntries + << "}, // #" << SCIdx << '\n'; + } + OS << "}; // " << PI->ModelName << "SchedClasses\n"; + } +} + +void SubtargetEmitter::EmitProcessorModels(raw_ostream &OS) { // For each processor model. for (const CodeGenProcModel &PM : SchedModels.procModels()) { // Emit extra processor info if available. if (PM.hasExtraProcessorInfo()) - EmitExtraProcessorInfo(PM); + EmitExtraProcessorInfo(PM, OS); // Emit processor resource table. if (PM.hasInstrSchedModel()) - EmitProcessorResources(PM); + EmitProcessorResources(PM, OS); else if(!PM.ProcResourceDefs.empty()) PrintFatalError(PM.ModelDef->getLoc(), "SchedMachineModel defines " "ProcResources without defining WriteRes SchedWriteRes"); // Begin processor itinerary properties - PI.subtargetEmitProcModelHeader(PM.ModelName); - PI.subtargetEmitProcessorProp(PM.ModelDef, "IssueWidth", ','); - PI.subtargetEmitProcessorProp(PM.ModelDef, "MicroOpBufferSize", ','); - PI.subtargetEmitProcessorProp(PM.ModelDef, "LoopMicroOpBufferSize", ','); - PI.subtargetEmitProcessorProp(PM.ModelDef, "LoadLatency", ','); - PI.subtargetEmitProcessorProp(PM.ModelDef, "HighLatency", ','); - PI.subtargetEmitProcessorProp(PM.ModelDef, "MispredictPenalty", ','); - - PI.subtargetEmitProcModel(PM, SchedModels); + OS << "\n"; + OS << "static const llvm::MCSchedModel " << PM.ModelName << " = {\n"; + EmitProcessorProp(OS, PM.ModelDef, "IssueWidth", ','); + EmitProcessorProp(OS, PM.ModelDef, "MicroOpBufferSize", ','); + EmitProcessorProp(OS, PM.ModelDef, "LoopMicroOpBufferSize", ','); + EmitProcessorProp(OS, PM.ModelDef, "LoadLatency", ','); + EmitProcessorProp(OS, PM.ModelDef, "HighLatency", ','); + EmitProcessorProp(OS, PM.ModelDef, "MispredictPenalty", ','); + + bool PostRAScheduler = + (PM.ModelDef ? PM.ModelDef->getValueAsBit("PostRAScheduler") : false); + + OS << " " << (PostRAScheduler ? "true" : "false") << ", // " + << "PostRAScheduler\n"; + + bool CompleteModel = + (PM.ModelDef ? PM.ModelDef->getValueAsBit("CompleteModel") : false); + + OS << " " << (CompleteModel ? "true" : "false") << ", // " + << "CompleteModel\n"; + + OS << " " << PM.Index << ", // Processor ID\n"; + if (PM.hasInstrSchedModel()) + OS << " " << PM.ModelName << "ProcResources" << ",\n" + << " " << PM.ModelName << "SchedClasses" << ",\n" + << " " << PM.ProcResourceDefs.size()+1 << ",\n" + << " " << (SchedModels.schedClassEnd() + - SchedModels.schedClassBegin()) << ",\n"; + else + OS << " nullptr, nullptr, 0, 0," + << " // No instruction-level machine model.\n"; + if (PM.hasItineraries()) + OS << " " << PM.ItinsDef->getName() << ",\n"; + else + OS << " nullptr, // No Itinerary\n"; + if (PM.hasExtraProcessorInfo()) + OS << " &" << PM.ModelName << "ExtraInfo,\n"; + else + OS << " nullptr // No extra processor descriptor\n"; + OS << "};\n"; } } // // EmitSchedModel - Emits all scheduling model tables, folding common patterns. // -void SubtargetEmitter::EmitSchedModel() { - PI.subtargetEmitDBGMacrosBegin(); +void SubtargetEmitter::EmitSchedModel(raw_ostream &OS) { + OS << "#ifdef DBGFIELD\n" + << "#error \"GenSubtargetInfo.inc requires a DBGFIELD macro\"\n" + << "#endif\n" + << "#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)\n" + << "#define DBGFIELD(x) x,\n" + << "#else\n" + << "#define DBGFIELD(x)\n" + << "#endif\n"; if (SchedModels.hasItineraries()) { std::vector> ProcItinLists; // Emit the stage data - EmitStageAndOperandCycleData(ProcItinLists); - EmitItineraries(ProcItinLists); + EmitStageAndOperandCycleData(OS, ProcItinLists); + EmitItineraries(OS, ProcItinLists); } - PI.subtargetEmitPreOperandTableComment(); + OS << "\n// ===============================================================\n" + << "// Data tables for the new per-operand machine model.\n"; - SchedClassTablesT SchedTables; + SchedClassTables SchedTables; for (const CodeGenProcModel &ProcModel : SchedModels.procModels()) { GenSchedClassTables(ProcModel, SchedTables); } - PI.subtargetEmitSchedClassTables(SchedTables, Target, SchedModels); + EmitSchedClassTables(SchedTables, OS); - PI.subtargetEmitDBGMacrosEnd(); + OS << "\n#undef DBGFIELD\n"; // Emit the processor machine model - EmitProcessorModels(); + EmitProcessorModels(OS); +} + +static void emitPredicateProlog(const RecordKeeper &Records, raw_ostream &OS) { + std::string Buffer; + raw_string_ostream Stream(Buffer); + + // Collect all the PredicateProlog records and print them to the output + // stream. + std::vector Prologs = + Records.getAllDerivedDefinitions("PredicateProlog"); + llvm::sort(Prologs, LessRecord()); + for (Record *P : Prologs) + Stream << P->getValueAsString("Code") << '\n'; + + OS << Buffer; } static bool isTruePredicate(const Record *Rec) { @@ -966,6 +1477,72 @@ static bool isTruePredicate(const Record *Rec) { Rec->getValueAsDef("Pred")->isSubClassOf("MCTrue"); } +static void emitPredicates(const CodeGenSchedTransition &T, + const CodeGenSchedClass &SC, PredicateExpander &PE, + raw_ostream &OS) { + std::string Buffer; + raw_string_ostream SS(Buffer); + + // If not all predicates are MCTrue, then we need an if-stmt. + unsigned NumNonTruePreds = + T.PredTerm.size() - count_if(T.PredTerm, isTruePredicate); + + SS.indent(PE.getIndentLevel() * 2); + + if (NumNonTruePreds) { + bool FirstNonTruePredicate = true; + SS << "if ("; + + PE.setIndentLevel(PE.getIndentLevel() + 2); + + for (const Record *Rec : T.PredTerm) { + // Skip predicates that evaluate to "true". + if (isTruePredicate(Rec)) + continue; + + if (FirstNonTruePredicate) { + FirstNonTruePredicate = false; + } else { + SS << "\n"; + SS.indent(PE.getIndentLevel() * 2); + SS << "&& "; + } + + if (Rec->isSubClassOf("MCSchedPredicate")) { + PE.expandPredicate(SS, Rec->getValueAsDef("Pred")); + continue; + } + + // Expand this legacy predicate and wrap it around braces if there is more + // than one predicate to expand. + SS << ((NumNonTruePreds > 1) ? "(" : "") + << Rec->getValueAsString("Predicate") + << ((NumNonTruePreds > 1) ? ")" : ""); + } + + SS << ")\n"; // end of if-stmt + PE.decreaseIndentLevel(); + SS.indent(PE.getIndentLevel() * 2); + PE.decreaseIndentLevel(); + } + + SS << "return " << T.ToClassIdx << "; // " << SC.Name << '\n'; + OS << Buffer; +} + +// Used by method `SubtargetEmitter::emitSchedModelHelpersImpl()` to generate +// epilogue code for the auto-generated helper. +static void emitSchedModelHelperEpilogue(raw_ostream &OS, + bool ShouldReturnZero) { + if (ShouldReturnZero) { + OS << " // Don't know how to resolve this scheduling class.\n" + << " return 0;\n"; + return; + } + + OS << " report_fatal_error(\"Expected a variant SchedClass\");\n"; +} + static bool hasMCSchedPredicates(const CodeGenSchedTransition &T) { return all_of(T.PredTerm, [](const Record *Rec) { return Rec->isSubClassOf("MCSchedPredicate"); @@ -1009,13 +1586,13 @@ static bool isAlwaysTrue(const CodeGenSchedTransition &T) { } void SubtargetEmitter::emitSchedModelHelpersImpl( - bool OnlyExpandMCInstPredicates) { + raw_ostream &OS, bool OnlyExpandMCInstPredicates) { IdxVec VariantClasses; collectVariantClasses(SchedModels, VariantClasses, OnlyExpandMCInstPredicates); if (VariantClasses.empty()) { - PI.subtargetEmitSchedModelHelperEpilogue(OnlyExpandMCInstPredicates); + emitSchedModelHelperEpilogue(OS, OnlyExpandMCInstPredicates); return; } @@ -1026,23 +1603,33 @@ void SubtargetEmitter::emitSchedModelHelpersImpl( // a variant scheduling class to another scheduling class. Rules are // described by instances of CodeGenSchedTransition. Note that transitions may // not be valid for all processors. - PI.subtargetEmitSchedClassSwitch(); + OS << " switch (SchedClass) {\n"; for (unsigned VC : VariantClasses) { IdxVec ProcIndices; const CodeGenSchedClass &SC = SchedModels.getSchedClass(VC); collectProcessorIndices(SC, ProcIndices); - PI.subtargetEmitSchedClassCase(VC, SC.Name); - - PI.subtargetPrepareSchedClassPreds(Target, OnlyExpandMCInstPredicates); - for (unsigned Pi : ProcIndices) { - PI.subtargetEmitSchedClassProcGuard(Pi, OnlyExpandMCInstPredicates, - (SchedModels.procModelBegin() + Pi)->ModelName); + OS << " case " << VC << ": // " << SC.Name << '\n'; + + PredicateExpander PE(Target); + PE.setByRef(false); + PE.setExpandForMC(OnlyExpandMCInstPredicates); + for (unsigned PI : ProcIndices) { + OS << " "; + + // Emit a guard on the processor ID. + if (PI != 0) { + OS << (OnlyExpandMCInstPredicates + ? "if (CPUID == " + : "if (SchedModel->getProcessorID() == "); + OS << PI << ") "; + OS << "{ // " << (SchedModels.procModelBegin() + PI)->ModelName << '\n'; + } // Now emit transitions associated with processor PI. const CodeGenSchedTransition *FinalT = nullptr; for (const CodeGenSchedTransition &T : SC.Transitions) { - if (Pi != 0 && T.ProcIndex != Pi) + if (PI != 0 && T.ProcIndex != PI) continue; // Emit only transitions based on MCSchedPredicate, if it's the case. @@ -1060,193 +1647,347 @@ void SubtargetEmitter::emitSchedModelHelpersImpl( FinalT = &T; continue; } - PI.subtargetEmitPredicates(T, SchedModels.getSchedClass(T.ToClassIdx), - isTruePredicate, 3); + PE.setIndentLevel(3); + emitPredicates(T, SchedModels.getSchedClass(T.ToClassIdx), PE, OS); } if (FinalT) - PI.subtargetEmitPredicates(*FinalT, SchedModels.getSchedClass(FinalT->ToClassIdx), - isTruePredicate); + emitPredicates(*FinalT, SchedModels.getSchedClass(FinalT->ToClassIdx), + PE, OS); - PI.subtargetEmitProcTransitionEnd(); + OS << " }\n"; - if (Pi == 0) + if (PI == 0) break; } - PI.subtargetEmitSchedClassCaseEnd(SC); + if (SC.isInferred()) + OS << " return " << SC.Index << ";\n"; + OS << " break;\n"; } - PI.subtargetEmitSchedClassSwitchEnd(); + OS << " };\n"; - PI.subtargetEmitSchedModelHelperEpilogue(OnlyExpandMCInstPredicates); + emitSchedModelHelperEpilogue(OS, OnlyExpandMCInstPredicates); } -void SubtargetEmitter::EmitSchedModelHelpers(const std::string &ClassName) { - PI.subtargetEmitResolveSchedClassHdr(ClassName); +void SubtargetEmitter::EmitSchedModelHelpers(const std::string &ClassName, + raw_ostream &OS) { + OS << "unsigned " << ClassName + << "\n::resolveSchedClass(unsigned SchedClass, const MachineInstr *MI," + << " const TargetSchedModel *SchedModel) const {\n"; // Emit the predicate prolog code. - PI.subtargetEmitPredicateProlog(Records); + emitPredicateProlog(Records, OS); // Emit target predicates. - emitSchedModelHelpersImpl(); + emitSchedModelHelpersImpl(OS); - PI.subtargetEmitResolveSchedClassEnd(ClassName); + OS << "} // " << ClassName << "::resolveSchedClass\n\n"; - PI.subtargetEmitResolveVariantSchedClass(Target, ClassName); + OS << "unsigned " << ClassName + << "\n::resolveVariantSchedClass(unsigned SchedClass, const MCInst *MI," + << " const MCInstrInfo *MCII, unsigned CPUID) const {\n" + << " return " << Target << "_MC" + << "::resolveVariantSchedClassImpl(SchedClass, MI, MCII, CPUID);\n" + << "} // " << ClassName << "::resolveVariantSchedClass\n\n"; - PI.subtargetEmitExpandedSTIPreds(Target, ClassName, SchedModels); + STIPredicateExpander PE(Target); + PE.setClassPrefix(ClassName); + PE.setExpandDefinition(true); + PE.setByRef(false); + PE.setIndentLevel(0); + + for (const STIPredicateFunction &Fn : SchedModels.getSTIPredicates()) + PE.expandSTIPredicate(OS, Fn); } -void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName) { +void SubtargetEmitter::EmitHwModeCheck(const std::string &ClassName, + raw_ostream &OS) { const CodeGenHwModes &CGH = TGT.getHwModes(); assert(CGH.getNumModeIds() > 0); if (CGH.getNumModeIds() == 1) return; - PI.subtargetEmitHwModes(CGH, ClassName); + OS << "unsigned " << ClassName << "::getHwMode() const {\n"; + for (unsigned M = 1, NumModes = CGH.getNumModeIds(); M != NumModes; ++M) { + const HwMode &HM = CGH.getMode(M); + OS << " if (checkFeatures(\"" << HM.Features + << "\")) return " << M << ";\n"; + } + OS << " return 0;\n}\n"; } // Produces a subtarget specific function for parsing // the subtarget features string. -void SubtargetEmitter::ParseFeaturesFunction() { +void SubtargetEmitter::ParseFeaturesFunction(raw_ostream &OS) { std::vector Features = Records.getAllDerivedDefinitions("SubtargetFeature"); llvm::sort(Features, LessRecord()); - PI.subtargetEmitParseFeaturesFunction(Target, Features); -} -void SubtargetEmitter::emitGenMCSubtargetInfo() { - PI.emitNamespace(Target + "_MC", true); - PI.subtargetEmitResolveVariantSchedClassImplHdr(); - emitSchedModelHelpersImpl(/* OnlyExpandMCPredicates */ true); - PI.subtargetEmitResolveVariantSchedClassImplEnd(); - PI.emitNamespace(Target + "_MC", false); + OS << "// ParseSubtargetFeatures - Parses features string setting specified\n" + << "// subtarget options.\n" + << "void llvm::"; + OS << Target; + OS << "Subtarget::ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, " + << "StringRef FS) {\n" + << " LLVM_DEBUG(dbgs() << \"\\nFeatures:\" << FS);\n" + << " LLVM_DEBUG(dbgs() << \"\\nCPU:\" << CPU);\n" + << " LLVM_DEBUG(dbgs() << \"\\nTuneCPU:\" << TuneCPU << \"\\n\\n\");\n"; + + if (Features.empty()) { + OS << "}\n"; + return; + } - PI.subtargetEmitGenMCSubtargetInfoClass(Target, TGT.getHwModes().getNumModeIds() > 1); - EmitHwModeCheck(Target + "GenMCSubtargetInfo"); + OS << " InitMCProcessorInfo(CPU, TuneCPU, FS);\n" + << " const FeatureBitset &Bits = getFeatureBits();\n"; + + for (Record *R : Features) { + // Next record + StringRef Instance = R->getName(); + StringRef Value = R->getValueAsString("Value"); + StringRef Attribute = R->getValueAsString("Attribute"); + + if (Value=="true" || Value=="false") + OS << " if (Bits[" << Target << "::" + << Instance << "]) " + << Attribute << " = " << Value << ";\n"; + else + OS << " if (Bits[" << Target << "::" + << Instance << "] && " + << Attribute << " < " << Value << ") " + << Attribute << " = " << Value << ";\n"; + } + + OS << "}\n"; } -void SubtargetEmitter::EmitMCInstrAnalysisPredicateFunctions() { - PI.emitIncludeToggle("GET_STIPREDICATE_DECLS_FOR_MC_ANALYSIS", true); +void SubtargetEmitter::emitGenMCSubtargetInfo(raw_ostream &OS) { + OS << "namespace " << Target << "_MC {\n" + << "unsigned resolveVariantSchedClassImpl(unsigned SchedClass,\n" + << " const MCInst *MI, const MCInstrInfo *MCII, unsigned CPUID) {\n"; + emitSchedModelHelpersImpl(OS, /* OnlyExpandMCPredicates */ true); + OS << "}\n"; + OS << "} // end namespace " << Target << "_MC\n\n"; + + OS << "struct " << Target + << "GenMCSubtargetInfo : public MCSubtargetInfo {\n"; + OS << " " << Target << "GenMCSubtargetInfo(const Triple &TT,\n" + << " StringRef CPU, StringRef TuneCPU, StringRef FS,\n" + << " ArrayRef PF,\n" + << " ArrayRef PD,\n" + << " const MCWriteProcResEntry *WPR,\n" + << " const MCWriteLatencyEntry *WL,\n" + << " const MCReadAdvanceEntry *RA, const InstrStage *IS,\n" + << " const unsigned *OC, const unsigned *FP) :\n" + << " MCSubtargetInfo(TT, CPU, TuneCPU, FS, PF, PD,\n" + << " WPR, WL, RA, IS, OC, FP) { }\n\n" + << " unsigned resolveVariantSchedClass(unsigned SchedClass,\n" + << " const MCInst *MI, const MCInstrInfo *MCII,\n" + << " unsigned CPUID) const override {\n" + << " return " << Target << "_MC" + << "::resolveVariantSchedClassImpl(SchedClass, MI, MCII, CPUID);\n"; + OS << " }\n"; + if (TGT.getHwModes().getNumModeIds() > 1) + OS << " unsigned getHwMode() const override;\n"; + OS << "};\n"; + EmitHwModeCheck(Target + "GenMCSubtargetInfo", OS); +} - PI.subtargetEmitExpandedSTIPredsMCAnaDecl(Target, SchedModels); +void SubtargetEmitter::EmitMCInstrAnalysisPredicateFunctions(raw_ostream &OS) { + OS << "\n#ifdef GET_STIPREDICATE_DECLS_FOR_MC_ANALYSIS\n"; + OS << "#undef GET_STIPREDICATE_DECLS_FOR_MC_ANALYSIS\n\n"; - PI.emitIncludeToggle("GET_STIPREDICATE_DECLS_FOR_MC_ANALYSIS", false); + STIPredicateExpander PE(Target); + PE.setExpandForMC(true); + PE.setByRef(true); + for (const STIPredicateFunction &Fn : SchedModels.getSTIPredicates()) + PE.expandSTIPredicate(OS, Fn); - PI.emitIncludeToggle("GET_STIPREDICATE_DEFS_FOR_MC_ANALYSIS", true); + OS << "#endif // GET_STIPREDICATE_DECLS_FOR_MC_ANALYSIS\n\n"; + + OS << "\n#ifdef GET_STIPREDICATE_DEFS_FOR_MC_ANALYSIS\n"; + OS << "#undef GET_STIPREDICATE_DEFS_FOR_MC_ANALYSIS\n\n"; std::string ClassPrefix = Target + "MCInstrAnalysis"; - PI.subtargetEmitExpandedSTIPreds(Target, ClassPrefix, SchedModels); + PE.setExpandDefinition(true); + PE.setClassPrefix(ClassPrefix); + PE.setIndentLevel(0); + for (const STIPredicateFunction &Fn : SchedModels.getSTIPredicates()) + PE.expandSTIPredicate(OS, Fn); - PI.emitIncludeToggle("GET_STIPREDICATE_DEFS_FOR_MC_ANALYSIS", false); + OS << "#endif // GET_STIPREDICATE_DEFS_FOR_MC_ANALYSIS\n\n"; } // // SubtargetEmitter::run - Main subtarget enumeration emitter. // -void SubtargetEmitter::run() { - PI.subtargetEmitSourceFileHeader(); +void SubtargetEmitter::run(raw_ostream &OS) { + emitSourceFileHeader("Subtarget Enumeration Source Fragment", OS); - PI.emitIncludeToggle("GET_SUBTARGETINFO_ENUM", true); + OS << "\n#ifdef GET_SUBTARGETINFO_ENUM\n"; + OS << "#undef GET_SUBTARGETINFO_ENUM\n\n"; DenseMap FeatureMap; - PI.emitNamespace("llvm", true); - Enumeration(FeatureMap); - PI.emitNamespace("llvm", false); - PI.emitIncludeToggle("GET_SUBTARGETINFO_ENUM", false); + OS << "namespace llvm {\n"; + Enumeration(OS, FeatureMap); + OS << "} // end namespace llvm\n\n"; + OS << "#endif // GET_SUBTARGETINFO_ENUM\n\n"; - EmitSubtargetInfoMacroCalls(); - PI.emitIncludeToggle("GET_SUBTARGETINFO_MC_DESC", true); + EmitSubtargetInfoMacroCalls(OS); - PI.emitNamespace("llvm", true); + OS << "namespace llvm {\n"; #if 0 - PI.emitNamespace("", true); + OS << "namespace {\n"; #endif - unsigned NumFeatures = FeatureKeyValues(FeatureMap); - EmitSchedModel(); - PI.emitString("\n"); - unsigned NumProcs = CPUKeyValues(FeatureMap); - PI.emitString("\n"); + unsigned NumFeatures = FeatureKeyValues(OS, FeatureMap); + OS << "\n"; + EmitSchedModel(OS); + OS << "\n"; + unsigned NumProcs = CPUKeyValues(OS, FeatureMap); + OS << "\n"; #if 0 - PI.emitNamespace("", false); + OS << "} // end anonymous namespace\n\n"; #endif // MCInstrInfo initialization routine. - emitGenMCSubtargetInfo(); - - PI.subtargetEmitMCSubtargetInfoImpl(Target, NumFeatures, NumProcs, SchedModels.hasItineraries()); - - PI.emitNamespace("llvm", false); + emitGenMCSubtargetInfo(OS); + + OS << "\nstatic inline MCSubtargetInfo *create" << Target + << "MCSubtargetInfoImpl(" + << "const Triple &TT, StringRef CPU, StringRef TuneCPU, StringRef FS) {\n"; + OS << " return new " << Target + << "GenMCSubtargetInfo(TT, CPU, TuneCPU, FS, "; + if (NumFeatures) + OS << Target << "FeatureKV, "; + else + OS << "std::nullopt, "; + if (NumProcs) + OS << Target << "SubTypeKV, "; + else + OS << "None, "; + OS << '\n'; OS.indent(22); + OS << Target << "WriteProcResTable, " + << Target << "WriteLatencyTable, " + << Target << "ReadAdvanceTable, "; + OS << '\n'; OS.indent(22); + if (SchedModels.hasItineraries()) { + OS << Target << "Stages, " + << Target << "OperandCycles, " + << Target << "ForwardingPaths"; + } else + OS << "nullptr, nullptr, nullptr"; + OS << ");\n}\n\n"; - PI.emitIncludeToggle("GET_SUBTARGETINFO_MC_DESC", false); + OS << "} // end namespace llvm\n\n"; - PI.emitIncludeToggle("GET_SUBTARGETINFO_TARGET_DESC", true); + OS << "#endif // GET_SUBTARGETINFO_MC_DESC\n\n"; - PI.subtargetEmitIncludeSTIDesc(); + OS << "\n#ifdef GET_SUBTARGETINFO_TARGET_DESC\n"; + OS << "#undef GET_SUBTARGETINFO_TARGET_DESC\n\n"; - ParseFeaturesFunction(); + OS << "#include \"llvm/Support/Debug.h\"\n"; + OS << "#include \"llvm/Support/raw_ostream.h\"\n\n"; + ParseFeaturesFunction(OS); - PI.emitIncludeToggle("GET_SUBTARGETINFO_TARGET_DESC", false); + OS << "#endif // GET_SUBTARGETINFO_TARGET_DESC\n\n"; // Create a TargetSubtargetInfo subclass to hide the MC layer initialization. - PI.emitIncludeToggle("GET_SUBTARGETINFO_HEADER", true); + OS << "\n#ifdef GET_SUBTARGETINFO_HEADER\n"; + OS << "#undef GET_SUBTARGETINFO_HEADER\n\n"; std::string ClassName = Target + "GenSubtargetInfo"; - PI.emitNamespace("llvm", true); - PI.subtargetEmitDFAPacketizerClass(Target, ClassName, TGT.getHwModes().getNumModeIds() > 1); - - PI.subtargetEmitExpandedSTIPredsHeader(Target, SchedModels); - PI.subtargetEmitDFASubtargetInfoImpl(Target, ClassName, NumFeatures, NumProcs, - SchedModels.hasItineraries()); - PI.subtargetEmitDFAPacketizerClassEnd(); + OS << "namespace llvm {\n"; + OS << "class DFAPacketizer;\n"; + OS << "namespace " << Target << "_MC {\n" + << "unsigned resolveVariantSchedClassImpl(unsigned SchedClass," + << " const MCInst *MI, const MCInstrInfo *MCII, unsigned CPUID);\n" + << "} // end namespace " << Target << "_MC\n\n"; + OS << "struct " << ClassName << " : public TargetSubtargetInfo {\n" + << " explicit " << ClassName << "(const Triple &TT, StringRef CPU, " + << "StringRef TuneCPU, StringRef FS);\n" + << "public:\n" + << " unsigned resolveSchedClass(unsigned SchedClass, " + << " const MachineInstr *DefMI," + << " const TargetSchedModel *SchedModel) const override;\n" + << " unsigned resolveVariantSchedClass(unsigned SchedClass," + << " const MCInst *MI, const MCInstrInfo *MCII," + << " unsigned CPUID) const override;\n" + << " DFAPacketizer *createDFAPacketizer(const InstrItineraryData *IID)" + << " const;\n"; + if (TGT.getHwModes().getNumModeIds() > 1) + OS << " unsigned getHwMode() const override;\n"; + + STIPredicateExpander PE(Target); + PE.setByRef(false); + for (const STIPredicateFunction &Fn : SchedModels.getSTIPredicates()) + PE.expandSTIPredicate(OS, Fn); + + OS << "};\n" + << "} // end namespace llvm\n\n"; + + OS << "#endif // GET_SUBTARGETINFO_HEADER\n\n"; + + OS << "\n#ifdef GET_SUBTARGETINFO_CTOR\n"; + OS << "#undef GET_SUBTARGETINFO_CTOR\n\n"; + + OS << "#include \"llvm/CodeGen/TargetSchedule.h\"\n\n"; + OS << "namespace llvm {\n"; + OS << "extern const llvm::SubtargetFeatureKV " << Target << "FeatureKV[];\n"; + OS << "extern const llvm::SubtargetSubTypeKV " << Target << "SubTypeKV[];\n"; + OS << "extern const llvm::MCWriteProcResEntry " + << Target << "WriteProcResTable[];\n"; + OS << "extern const llvm::MCWriteLatencyEntry " + << Target << "WriteLatencyTable[];\n"; + OS << "extern const llvm::MCReadAdvanceEntry " + << Target << "ReadAdvanceTable[];\n"; - PI.emitNamespace("llvm", false); - - PI.emitIncludeToggle("GET_SUBTARGETINFO_HEADER", false); - - PI.emitIncludeToggle("GET_SUBTARGETINFO_CTOR", true); - - PI.subtargetEmitSTICtor(); - PI.emitNamespace("llvm", true); - PI.subtargetEmitExternKVArrays(Target, SchedModels.hasItineraries()); + if (SchedModels.hasItineraries()) { + OS << "extern const llvm::InstrStage " << Target << "Stages[];\n"; + OS << "extern const unsigned " << Target << "OperandCycles[];\n"; + OS << "extern const unsigned " << Target << "ForwardingPaths[];\n"; + } - PI.subtargetEmitClassDefs(Target, - ClassName, - NumFeatures, - NumProcs, - SchedModels.hasItineraries()); + OS << ClassName << "::" << ClassName << "(const Triple &TT, StringRef CPU, " + << "StringRef TuneCPU, StringRef FS)\n" + << " : TargetSubtargetInfo(TT, CPU, TuneCPU, FS, "; + if (NumFeatures) + OS << "ArrayRef(" << Target << "FeatureKV, " << NumFeatures << "), "; + else + OS << "std::nullopt, "; + if (NumProcs) + OS << "ArrayRef(" << Target << "SubTypeKV, " << NumProcs << "), "; + else + OS << "None, "; + OS << '\n'; OS.indent(24); + OS << Target << "WriteProcResTable, " + << Target << "WriteLatencyTable, " + << Target << "ReadAdvanceTable, "; + OS << '\n'; OS.indent(24); + if (SchedModels.hasItineraries()) { + OS << Target << "Stages, " + << Target << "OperandCycles, " + << Target << "ForwardingPaths"; + } else + OS << "nullptr, nullptr, nullptr"; + OS << ") {}\n\n"; - EmitSchedModelHelpers(ClassName); - EmitHwModeCheck(ClassName); + EmitSchedModelHelpers(ClassName, OS); + EmitHwModeCheck(ClassName, OS); - PI.emitNamespace("llvm", false); + OS << "} // end namespace llvm\n\n"; - PI.emitIncludeToggle("GET_SUBTARGETINFO_CTOR", false); + OS << "#endif // GET_SUBTARGETINFO_CTOR\n\n"; - EmitMCInstrAnalysisPredicateFunctions(); + EmitMCInstrAnalysisPredicateFunctions(OS); } namespace llvm { void EmitSubtarget(RecordKeeper &RK, raw_ostream &OS) { CodeGenTarget CGTarget(RK); - - PrinterLanguage const PL = PrinterLLVM::getLanguage(); - PrinterLLVM *PI; - formatted_raw_ostream FOS(OS); - switch(PL) { - default: - llvm_unreachable("Subtarget backend does not support the selected printer language."); - case PRINTER_LANG_CPP: - PI = new PrinterLLVM(FOS, CGTarget.getName().str()); - break; - case PRINTER_LANG_CAPSTONE_C: - PI = new PrinterCapstone(FOS, CGTarget.getName().str()); - break; - } - SubtargetEmitter(RK, CGTarget, *PI).run(); - delete PI; + SubtargetEmitter(RK, CGTarget).run(OS); } } // end namespace llvm diff --git a/llvm/utils/gn/secondary/llvm/version.gni b/llvm/utils/gn/secondary/llvm/version.gni index f7b2599d590a..450f04d97f5b 100644 --- a/llvm/utils/gn/secondary/llvm/version.gni +++ b/llvm/utils/gn/secondary/llvm/version.gni @@ -1,4 +1,4 @@ llvm_version_major = 16 llvm_version_minor = 0 -llvm_version_patch = 4 +llvm_version_patch = 6 llvm_version = "$llvm_version_major.$llvm_version_minor.$llvm_version_patch" diff --git a/llvm/utils/lit/lit/__init__.py b/llvm/utils/lit/lit/__init__.py index e9ef8e89d45f..e9d22cdc6209 100644 --- a/llvm/utils/lit/lit/__init__.py +++ b/llvm/utils/lit/lit/__init__.py @@ -2,7 +2,7 @@ __author__ = 'Daniel Dunbar' __email__ = 'daniel@minormatter.com' -__versioninfo__ = (16, 0, 4) +__versioninfo__ = (16, 0, 6) __version__ = '.'.join(str(v) for v in __versioninfo__) + 'dev' __all__ = []