diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 00000000000000..6c4a7142996bbe --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,311 @@ +name: CMake + +on: + push: + branches: [z80] + +jobs: + build-llvm: + needs: [] + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + build-type: [Debug, Release] + if: always() + runs-on: ${{matrix.runs-on}} + steps: + - name: Uninstall Unused Packages + if: runner.os == 'Linux' + run: sudo apt-get remove ^ghc-[0-9.]+$ + + - name: Prepare Build Environment + if: runner.os != 'Windows' + run: | + cmake -E echo ::set-env name=CC::clang + cmake -E echo ::set-env name=CFLAGS::-std=c17 + cmake -E echo ::set-env name=CXX::clang++ + cmake -E echo ::set-env name=CXXFLAGS::-std=c++17 + - name: Prepare Build Environment + if: runner.os == 'Windows' + run: | + git config --global core.autocrlf false + $path = vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + Write-Output "::add-path::$(join-path $path 'VC\Tools\Llvm\bin')" + cmd /s /c """$(Join-Path $path 'Common7\Tools\vsdevcmd.bat')"" && set" | where { $_ -match '(\w+)=(.*)' } | foreach { Write-Output "::set-env name=$($Matches[1])::$($Matches[2])" } + cmake -E echo ::set-env name=CC::clang-cl + cmake -E echo ::set-env name=CXX::clang-cl + cmake -E echo ::set-env name=EXE::.exe + cmake -E echo ::set-env name=PY::.py + + - name: Prepare Build Environment + if: matrix.build-type == 'Debug' + run: cmake -E echo ::set-env name=DYLIB::ON + - name: Prepare Build Environment + if: matrix.build-type != 'Debug' + run: cmake -E echo ::set-env name=DYLIB::OFF + + - name: Install Build Dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get install -y ninja-build lld + cmake -E echo ::set-env name=LD::lld + - name: Install Build Dependencies + if: runner.os == 'macOS' + run: | + brew install ninja + cmake -E echo ::add-path::/usr/local/opt/llvm/bin + - name: Install Build Dependencies + if: runner.os == 'Windows' + run: pip install ninja + + - name: Checkout Project + uses: actions/checkout@v2 + with: + path: src + - name: Sparse Checkout + working-directory: src + run: | + git version + git sparse-checkout init --cone + git sparse-checkout set llvm clang + + - name: Configure LLVM + run: cmake -Ssrc/llvm -Bbuild -GNinja -DCMAKE_BUILD_TYPE:STRING=${{matrix.build-type}} -DCMAKE_INSTALL_PREFIX:PATH="${{github.workspace}}/llvm" -DCMAKE_C_COMPILER:FILEPATH="${{env.CC}}" -DCMAKE_C_FLAGS:STRING="${{env.CFLAGS}}" -DCMAKE_CXX_COMPILER:FILEPATH="${{env.CXX}}" -DCMAKE_CXX_FLAGS:STRING="${{env.CXXFLAGS}}" -DLLVM_PARALLEL_LINK_JOBS:STRING=1 -DLLVM_USE_LINKER="${{env.LD}}" -DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="Z80" -DLLVM_LINK_LLVM_DYLIB:BOOL=${{env.DYLIB}} -DLLVM_INCLUDE_TESTS:BOOL=OFF -DLLVM_TOOL_DSYMUTIL_BUILD:BOOL=OFF -DLLVM_TOOL_GOLD_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_AS_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_CFI_VERIFY_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_CVTRES_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_DWP_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_ELFABI_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_EXEGESIS_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_GSYMUTIL_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_IFS_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_ISEL_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_ITANIUM_DEMANGLE_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_JITLINK_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_JITLISTENER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_LIPO_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_LTO2_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_LTO_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MCA_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MC_ASSEMBLE_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MC_DISASSEMBLE_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MICROSOFT_DEMANGLE_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_MT_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_OPT_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_PDBUTIL_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_PROFDATA_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_RC_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_SPECIAL_CASE_LIST_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_UNDNAME_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_XRAY_BUILD:BOOL=OFF -DLLVM_TOOL_LLVM_YAML_NUMERIC_PARSER_FUZZER_BUILD:BOOL=OFF -DLLVM_TOOL_LTO_BUILD:BOOL=OFF -DLLVM_TOOL_SANCOV_BUILD:BOOL=OFF -DLLVM_TOOL_SANSTATS_BUILD:BOOL=OFF -DLLVM_TOOL_VFABI_DEMANGLE_FUZZER_BUILD:BOOL=OFF + - name: Build LLVM + run: cmake --build build --config ${{matrix.build-type}} + + - name: Install LLVM + run: cmake --build build --config ${{matrix.build-type}} --target install + - name: Upload LLVM + uses: actions/upload-artifact@v1 + with: + name: LLVM${{runner.os}}${{matrix.build-type}} + path: llvm + + - name: Configure Test + run: cmake build -DLLVM_LINK_LLVM_DYLIB:BOOL=${{env.DYLIB}} -DLLVM_ENABLE_PROJECTS:STRING=clang -DLLVM_INCLUDE_TESTS:BOOL=ON -DLLVM_TOOL_DSYMUTIL_BUILD:BOOL=ON -DLLVM_TOOL_GOLD_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_AS_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_CFI_VERIFY_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_CVTRES_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_DWP_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_ELFABI_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_EXEGESIS_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_GSYMUTIL_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_IFS_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_ISEL_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_ITANIUM_DEMANGLE_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_JITLINK_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_JITLISTENER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_LIPO_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_LTO2_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_LTO_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MCA_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MC_ASSEMBLE_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MC_DISASSEMBLE_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MICROSOFT_DEMANGLE_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_MT_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_OPT_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_PDBUTIL_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_PROFDATA_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_RC_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_SPECIAL_CASE_LIST_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_UNDNAME_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_XRAY_BUILD:BOOL=ON -DLLVM_TOOL_LLVM_YAML_NUMERIC_PARSER_FUZZER_BUILD:BOOL=ON -DLLVM_TOOL_LTO_BUILD:BOOL=ON -DLLVM_TOOL_SANCOV_BUILD:BOOL=ON -DLLVM_TOOL_SANSTATS_BUILD:BOOL=ON -DLLVM_TOOL_VFABI_DEMANGLE_FUZZER_BUILD:BOOL=ON + - name: Install Test + run: | + cmake -E make_directory test/bin test/test/Unit test/tools/clang/bin test/tools/clang/test/Unit + cmake -E copy build/bin/not${{env.EXE}} build/bin/FileCheck${{env.EXE}} build/bin/llvm-PerfectShuffle${{env.EXE}} build/bin/lli-child-target${{env.EXE}} build/bin/llvm-lit${{env.PY}} build/bin/llvm-locstats build/bin/count${{env.EXE}} build/bin/yaml-bench${{env.EXE}} test/bin + cmake -E copy build/test/lit.site.cfg.py test/test + cmake -E copy build/test/Unit/lit.site.cfg.py test/test/Unit + cmake -E copy build/tools/clang/bin/gen_ast_dump_json_test.py test/tools/clang/bin + cmake -E copy build/tools/clang/test/lit.site.cfg.py test/tools/clang/test + cmake -E copy build/tools/clang/test/Unit/lit.site.cfg.py test/tools/clang/test/Unit + - name: Upload Test + uses: actions/upload-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: test + + - name: Disk Usage + if: always() && runner.os != 'Windows' + run: df -h + - name: Disk Usage + if: always() && runner.os == 'Windows' + run: wmic logicaldisk get size,freespace,caption + + build-clang: + needs: [build-llvm] + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + build-type: [Debug, Release] + if: always() + runs-on: ${{matrix.runs-on}} + steps: + - name: Uninstall Unused Packages + if: runner.os == 'Linux' + run: sudo apt-get remove ^ghc-[0-9.]+$ + + - name: Prepare Build Environment + if: runner.os != 'Windows' + run: | + cmake -E echo ::set-env name=CC::clang + cmake -E echo ::set-env name=CFLAGS::-std=c17 + cmake -E echo ::set-env name=CXX::clang++ + cmake -E echo ::set-env name=CXXFLAGS::-std=c++17 + - name: Prepare Build Environment + if: runner.os == 'Windows' + run: | + git config --global core.autocrlf false + $path = vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath + Write-Output "::add-path::$(join-path $path 'VC\Tools\Llvm\bin')" + cmd /s /c """$(Join-Path $path 'Common7\Tools\vsdevcmd.bat')"" && set" | where { $_ -match '(\w+)=(.*)' } | foreach { Write-Output "::set-env name=$($Matches[1])::$($Matches[2])" } + cmake -E echo ::set-env name=CC::clang-cl + cmake -E echo ::set-env name=CXX::clang-cl + cmake -E echo ::set-env name=EXE::.exe + + - name: Prepare Build Environment + if: matrix.build-type == 'Debug' + run: cmake -E echo ::set-env name=DYLIB::ON + - name: Prepare Build Environment + if: matrix.build-type != 'Debug' + run: cmake -E echo ::set-env name=DYLIB::OFF + + - name: Install Build Dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get install -y ninja-build lld + cmake -E echo ::set-env name=LD::lld + - name: Install Build Dependencies + if: runner.os == 'macOS' + run: | + brew install ninja + cmake -E echo ::add-path::/usr/local/opt/llvm/bin + - name: Install Build Dependencies + if: runner.os == 'Windows' + run: pip install ninja + + - name: Checkout Project + uses: actions/checkout@v2 + with: + path: src + - name: Sparse Checkout + working-directory: src + run: | + git version + git sparse-checkout init --cone + git sparse-checkout set clang + + - name: Download LLVM + uses: actions/download-artifact@v1 + with: + name: LLVM${{runner.os}}${{matrix.build-type}} + path: llvm + - name: Set Executable + if: runner.os != 'Windows' + run: chmod +x llvm/bin/* + + - name: Configure Clang + run: cmake -Ssrc/clang -Bbuild -GNinja -DCMAKE_BUILD_TYPE:STRING=${{matrix.build-type}} -DCMAKE_INSTALL_PREFIX:PATH="${{github.workspace}}/clang" -DCMAKE_C_COMPILER:FILEPATH="${{env.CC}}" -DCMAKE_C_FLAGS:STRING="${{env.CFLAGS}}" -DCMAKE_CXX_COMPILER:FILEPATH="${{env.CXX}}" -DCMAKE_CXX_FLAGS:STRING="${{env.CXXFLAGS}}" -DLLVM_USE_LINKER="${{env.LD}}" -DLLVM_DIR:PATH="${{github.workspace}}/llvm/lib/cmake/llvm" -DCLANG_LINK_CLANG_DYLIB:BOOL=${{env.DYLIB}} + - name: Build Clang + run: cmake --build build --config ${{matrix.build-type}} + + - name: Install Clang + run: cmake --build build --config ${{matrix.build-type}} --target install + - name: Upload Clang + uses: actions/upload-artifact@v1 + with: + name: Clang${{runner.os}}${{matrix.build-type}} + path: clang + + - name: Install Executable + run: cmake -E copy clang/bin/clang${{env.EXE}} ez80-clang${{env.EXE}} + - name: Upload Executable + uses: actions/upload-artifact@v1 + with: + name: ez80-clang${{env.EXE}}-${{runner.os}}${{matrix.build-type}} + path: ez80-clang${{env.EXE}} + + - name: Download Test + uses: actions/download-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: test + - name: Install Test + run: cmake -E copy build/bin/arcmt-test${{env.EXE}} build/bin/c-arcmt-test${{env.EXE}} build/bin/clang-diff${{env.EXE}} build/bin/clang-tblgen${{env.EXE}} test/bin + - name: Upload Test + uses: actions/upload-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: test + + - name: Disk Usage + if: always() && runner.os != 'Windows' + run: df -h + - name: Disk Usage + if: always() && runner.os == 'Windows' + run: wmic logicaldisk get size,freespace,caption + + test-llvm: + needs: [build-llvm] + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + build-type: [Debug] + if: always() + runs-on: ${{matrix.runs-on}} + steps: + - name: Uninstall Unused Packages + if: runner.os == 'Linux' + run: sudo apt-get remove ^ghc-[0-9.]+$ + + - name: Checkout Project + uses: actions/checkout@v2 + with: + path: src + - name: Sparse Checkout + working-directory: src + run: | + git version + git sparse-checkout init --cone + git sparse-checkout set llvm/test llvm/utils/lit/lit + + - name: Download Test + uses: actions/download-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: build + - name: Download LLVM + uses: actions/download-artifact@v1 + with: + name: LLVM${{runner.os}}${{matrix.build-type}} + path: build + - name: Set Executable + if: runner.os != 'Windows' + run: chmod +x build/bin/* + + - name: Test LLVM + run: build/bin/llvm-lit -v src/llvm/test + + test-clang: + needs: [build-llvm, build-clang] + strategy: + fail-fast: false + matrix: + runs-on: [ubuntu-latest, macos-latest, windows-latest] + build-type: [Debug] + if: always() + runs-on: ${{matrix.runs-on}} + steps: + - name: Uninstall Unused Packages + if: runner.os == 'Linux' + run: sudo apt-get remove ^ghc-[0-9.]+$ + + - name: Checkout Project + uses: actions/checkout@v2 + with: + path: src + - name: Sparse Checkout + working-directory: src + run: | + git version + git sparse-checkout init --cone + git sparse-checkout set clang/test llvm/utils/lit/lit + + - name: Download Test + uses: actions/download-artifact@v1 + with: + name: Test${{runner.os}}${{matrix.build-type}} + path: build + - name: Download LLVM + uses: actions/download-artifact@v1 + with: + name: LLVM${{runner.os}}${{matrix.build-type}} + path: build + - name: Download Clang + uses: actions/download-artifact@v1 + with: + name: Clang${{runner.os}}${{matrix.build-type}} + path: build + - name: Set Executable + if: runner.os != 'Windows' + run: chmod +x build/bin/* + + - name: Test Clang + run: build/bin/llvm-lit -v src/clang/test diff --git a/.gitignore b/.gitignore index 43ff4794b019f3..d844e23aceee19 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ #==============================================================================# # Temp files created by most text editors. *~ +\#*# # Merge files created by git. *.orig # Byte compiled python modules. diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 2b988be60da9f4..0322cdc0631d87 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -312,6 +312,12 @@ class ASTContext : public RefCountedBase { TemplateTemplateParmDecl * getCanonicalTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTP) const; + /// The typedef for the __int48_t type. + mutable TypedefDecl *Int48Decl = nullptr; + + /// The typedef for the __uint48_t type. + mutable TypedefDecl *UInt48Decl = nullptr; + /// The typedef for the __int128_t type. mutable TypedefDecl *Int128Decl = nullptr; @@ -948,9 +954,10 @@ class ASTContext : public RefCountedBase { CanQualType Char8Ty; // [C++20 proposal] CanQualType Char16Ty; // [C++0x 3.9.1p5], integer type in C99. CanQualType Char32Ty; // [C++0x 3.9.1p5], integer type in C99. - CanQualType SignedCharTy, ShortTy, IntTy, LongTy, LongLongTy, Int128Ty; + CanQualType SignedCharTy, ShortTy, IntTy, LongTy, Int48Ty, LongLongTy, + Int128Ty; CanQualType UnsignedCharTy, UnsignedShortTy, UnsignedIntTy, UnsignedLongTy; - CanQualType UnsignedLongLongTy, UnsignedInt128Ty; + CanQualType UnsignedInt48Ty, UnsignedLongLongTy, UnsignedInt128Ty; CanQualType FloatTy, DoubleTy, LongDoubleTy, Float128Ty; CanQualType ShortAccumTy, AccumTy, LongAccumTy; // ISO/IEC JTC1 SC22 WG14 N1169 Extension @@ -1045,6 +1052,12 @@ class ASTContext : public RefCountedBase { /// Create a new implicit TU-level typedef declaration. TypedefDecl *buildImplicitTypedef(QualType T, StringRef Name) const; + /// Retrieve the declaration for the 48-bit signed integer type. + TypedefDecl *getInt48Decl() const; + + /// Retrieve the declaration for the 48-bit unsigned integer type. + TypedefDecl *getUInt48Decl() const; + /// Retrieve the declaration for the 128-bit signed integer type. TypedefDecl *getInt128Decl() const; diff --git a/clang/include/clang/AST/BuiltinTypes.def b/clang/include/clang/AST/BuiltinTypes.def index 039765dfdfea67..dccd8bbc5f1c96 100644 --- a/clang/include/clang/AST/BuiltinTypes.def +++ b/clang/include/clang/AST/BuiltinTypes.def @@ -89,6 +89,9 @@ UNSIGNED_TYPE(UInt, UnsignedIntTy) // 'unsigned long' UNSIGNED_TYPE(ULong, UnsignedLongTy) +// '__uint48_t' +UNSIGNED_TYPE(UInt48, UnsignedInt48Ty) + // 'unsigned long long' UNSIGNED_TYPE(ULongLong, UnsignedLongLongTy) @@ -115,6 +118,9 @@ SIGNED_TYPE(Int, IntTy) // 'long' or 'signed long' SIGNED_TYPE(Long, LongTy) +// '__int48_t' +SIGNED_TYPE(Int48, Int48Ty) + // 'long long' or 'signed long long' SIGNED_TYPE(LongLong, LongLongTy) diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 10b8b41efeeb0a..92bc01f859c7a0 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -3664,6 +3664,8 @@ class FunctionType : public Type { // | CC |noreturn|produces|nocallersavedregs|regparm|nocfcheck|cmsenscall| // |0 .. 4| 5 | 6 | 7 |8 .. 10| 11 | 12 | + // |tiflags| + // | 13 | // // regparm is either 0 (no regparm attribute) or the regparm value+1. enum { CallConvMask = 0x1F }; @@ -3676,6 +3678,7 @@ class FunctionType : public Type { }; enum { NoCfCheckMask = 0x800 }; enum { CmseNSCallMask = 0x1000 }; + enum { TIFlagsMask = 0x2000 }; uint16_t Bits = CC_C; ExtInfo(unsigned Bits) : Bits(static_cast(Bits)) {} @@ -3685,14 +3688,14 @@ class FunctionType : public Type { // have all the elements (when reading an AST file for example). ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc, bool producesResult, bool noCallerSavedRegs, bool NoCfCheck, - bool cmseNSCall) { + bool cmseNSCall, bool tiFlags) { assert((!hasRegParm || regParm < 7) && "Invalid regparm value"); Bits = ((unsigned)cc) | (noReturn ? NoReturnMask : 0) | (producesResult ? ProducesResultMask : 0) | (noCallerSavedRegs ? NoCallerSavedRegsMask : 0) | (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0) | (NoCfCheck ? NoCfCheckMask : 0) | - (cmseNSCall ? CmseNSCallMask : 0); + (cmseNSCall ? CmseNSCallMask : 0) | (tiFlags ? TIFlagsMask : 0); } // Constructor with all defaults. Use when for example creating a @@ -3708,6 +3711,7 @@ class FunctionType : public Type { bool getCmseNSCall() const { return Bits & CmseNSCallMask; } bool getNoCallerSavedRegs() const { return Bits & NoCallerSavedRegsMask; } bool getNoCfCheck() const { return Bits & NoCfCheckMask; } + bool getTIFlags() const { return Bits & TIFlagsMask; } bool getHasRegParm() const { return ((Bits & RegParmMask) >> RegParmOffset) != 0; } unsigned getRegParm() const { @@ -3764,6 +3768,13 @@ class FunctionType : public Type { return ExtInfo(Bits & ~NoCfCheckMask); } + ExtInfo withTIFlags(bool tiFlags) const { + if (tiFlags) + return ExtInfo(Bits | TIFlagsMask); + else + return ExtInfo(Bits & ~TIFlagsMask); + } + ExtInfo withRegParm(unsigned RegParm) const { assert(RegParm < 7 && "Invalid regparm value"); return ExtInfo((Bits & ~RegParmMask) | diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td index 4540ea0e1952aa..fce7ccfe351d23 100644 --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -287,6 +287,9 @@ let Class = FunctionType in { def : Property<"cmseNSCall", Bool> { let Read = [{ node->getExtInfo().getCmseNSCall() }]; } + def : Property<"tiFlags", Bool> { + let Read = [{ node->getExtInfo().getTIFlags() }]; + } } let Class = FunctionNoProtoType in { @@ -294,7 +297,7 @@ let Class = FunctionNoProtoType in { auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm, callingConvention, producesResult, noCallerSavedRegs, noCfCheck, - cmseNSCall); + cmseNSCall, tiFlags); return ctx.getFunctionNoProtoType(returnType, extInfo); }]>; } @@ -328,7 +331,7 @@ let Class = FunctionProtoType in { auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm, callingConvention, producesResult, noCallerSavedRegs, noCfCheck, - cmseNSCall); + cmseNSCall, tiFlags); FunctionProtoType::ExtProtoInfo epi; epi.ExtInfo = extInfo; epi.Variadic = variadic; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index bc4a380545afe5..7d1fd2f2392d7e 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -365,6 +365,7 @@ def TargetMSP430 : TargetArch<["msp430"]>; def TargetRISCV : TargetArch<["riscv32", "riscv64"]>; def TargetX86 : TargetArch<["x86"]>; def TargetAnyX86 : TargetArch<["x86", "x86_64"]>; +def TargetAnyZ80 : TargetArch<["z80", "ez80"]>; def TargetWebAssembly : TargetArch<["wasm32", "wasm64"]>; def TargetWindows : TargetArch<["x86", "x86_64", "arm", "thumb", "aarch64"]> { let OSes = ["Win32"]; @@ -743,8 +744,8 @@ def Annotate : InheritableParamAttr { } def ARMInterrupt : InheritableAttr, TargetSpecificAttr { - // NOTE: If you add any additional spellings, MSP430Interrupt's, - // MipsInterrupt's and AnyX86Interrupt's spellings must match. + // NOTE: If you add any additional spellings, ARMInterrupt's, + // MSP430Interrupt's, MipsInterrupt's and Z80Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Args = [EnumArgument<"Interrupt", "InterruptType", ["IRQ", "FIQ", "SWI", "ABORT", "UNDEF", ""], @@ -1451,8 +1452,8 @@ def MSABI : DeclOrTypeAttr { } def MSP430Interrupt : InheritableAttr, TargetSpecificAttr { - // NOTE: If you add any additional spellings, ARMInterrupt's, MipsInterrupt's - // and AnyX86Interrupt's spellings must match. + // NOTE: If you add any additional spellings, ARMInterrupt's, + // MSP430Interrupt's, MipsInterrupt's and Z80Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Args = [UnsignedArgument<"Number">]; let ParseKind = "Interrupt"; @@ -1468,7 +1469,7 @@ def Mips16 : InheritableAttr, TargetSpecificAttr { def MipsInterrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, ARMInterrupt's, - // MSP430Interrupt's and AnyX86Interrupt's spellings must match. + // MSP430Interrupt's, MipsInterrupt's and Z80Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Subjects = SubjectList<[Function]>; let Args = [EnumArgument<"Interrupt", "InterruptType", @@ -1500,6 +1501,26 @@ def MipsShortCall : InheritableAttr, TargetSpecificAttr { let Documentation = [MipsShortCallStyleDocs]; } +def AnyZ80Interrupt : InheritableAttr, TargetSpecificAttr { + // NOTE: If you add any additional spellings, MSP430Interrupt's, + // MipsInterrupt's and AnyX86Interrupt's spellings must match. + let Spellings = [GNU<"interrupt">]; + let Subjects = SubjectList<[Function]>; + let Args = [EnumArgument<"Interrupt", "InterruptType", + ["", "nested", "nmi"], + ["Generic", "Nested", "NMI"], + 1>]; + let ParseKind = "Interrupt"; + let HasCustomParsing = 1; + let Documentation = [Undocumented]; +} + +def AnyZ80TIFlags : DeclOrTypeAttr, TargetSpecificAttr { + let Spellings = [Clang<"tiflags">]; + let Subjects = SubjectList<[FunctionLike]>; + let Documentation = [AnyZ80TIFlagsDocs]; +} + def Mode : Attr { let Spellings = [GCC<"mode">]; let Subjects = SubjectList<[Var, Enum, TypedefName, Field], ErrorDiag>; @@ -2554,7 +2575,7 @@ def LTOVisibilityPublic : InheritableAttr { def AnyX86Interrupt : InheritableAttr, TargetSpecificAttr { // NOTE: If you add any additional spellings, ARMInterrupt's, - // MSP430Interrupt's and MipsInterrupt's spellings must match. + // MSP430Interrupt's, MipsInterrupt's and Z80Interrupt's spellings must match. let Spellings = [GCC<"interrupt">]; let Subjects = SubjectList<[HasFunctionProto]>; let ParseKind = "Interrupt"; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index 3cba3a3d96f967..773cd53511e01e 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -3703,6 +3703,15 @@ pointer (by adding nocf_check prefix to the indirect-call instruction). }]; } +def AnyZ80TIFlagsDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +TIOS, which runs on (e)Z80 processors, have a lot of system routines that expect +the IY register to contain a specific value. This attribute marks calls whose +calling convention should be changed appropriately. +}]; +} + def SwiftCallDocs : Documentation { let Category = DocCatVariable; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 70b43f5abc4b63..2293410c2fa2b9 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -289,6 +289,9 @@ def err_anyx86_interrupt_attribute : Error< "a pointer as the first parameter|a %2 type as the second parameter}1">; def err_anyx86_interrupt_called : Error< "interrupt service routine cannot be called directly">; +def err_anyz80_interrupt_attribute : Error< + "%select{z80|ez80}0 'interrupt' attribute only applies to functions that " + "have %select{a 'void' return type|no parameters}1">; def warn_arm_interrupt_calling_convention : Warning< "call to function without interrupt attribute could clobber interruptee's VFP registers">, InGroup; diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h index 2834dea20d0020..99ee221c13a0f2 100644 --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -66,6 +66,7 @@ namespace clang { TST_char16, // C++11 char16_t TST_char32, // C++11 char32_t TST_int, + TST_int48, TST_int128, TST_extint, // Extended Int types. TST_half, // OpenCL half, ARM NEON __fp16 diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h index 140f55ff66b10a..2f82e6f0312851 100644 --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -59,6 +59,7 @@ namespace Builtin { struct Info; } struct TransferrableTargetInfo { unsigned char PointerWidth, PointerAlign; unsigned char BoolWidth, BoolAlign; + unsigned char ShortWidth, ShortAlign; unsigned char IntWidth, IntAlign; unsigned char HalfWidth, HalfAlign; unsigned char BFloat16Width, BFloat16Align; @@ -418,13 +419,10 @@ class TargetInfo : public virtual TransferrableTargetInfo, unsigned getCharWidth() const { return 8; } // FIXME unsigned getCharAlign() const { return 8; } // FIXME - /// Return the size of 'signed short' and 'unsigned short' for this - /// target, in bits. - unsigned getShortWidth() const { return 16; } // FIXME - - /// Return the alignment of 'signed short' and 'unsigned short' for - /// this target. - unsigned getShortAlign() const { return 16; } // FIXME + /// getShortWidth/Align - Return the size of 'signed short' and + /// 'unsigned short' for this target, in bits. + unsigned getShortWidth() const { return ShortWidth; } + unsigned getShortAlign() const { return ShortAlign; } /// getIntWidth/Align - Return the size of 'signed int' and 'unsigned int' for /// this target, in bits. @@ -554,6 +552,11 @@ class TargetInfo : public virtual TransferrableTargetInfo, : getLongFractScale() + 1; } + /// Determine whether the __int48 type is supported on this target. + virtual bool hasInt48Type() const { + return false; + } // FIXME + /// Determine whether the __int128 type is supported on this target. virtual bool hasInt128Type() const { return (getPointerWidth(0) >= 64) || getTargetOpts().ForceEnableInt128; diff --git a/clang/include/clang/Basic/TargetOptions.h b/clang/include/clang/Basic/TargetOptions.h index bbe86aebb0741c..0e2456d69abcc3 100644 --- a/clang/include/clang/Basic/TargetOptions.h +++ b/clang/include/clang/Basic/TargetOptions.h @@ -81,6 +81,9 @@ class TargetOptions { /// * CUDA compilation uses it to control parts of CUDA compilation /// in clang that depend on specific version of the CUDA SDK. llvm::VersionTuple SDKVersion; + + /// Very specific hack for testing the ez80 backend. + bool TestEZ80Hack; }; } // end namespace clang diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 2b353269ed52d9..33ba8f4e173410 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -437,6 +437,7 @@ KEYWORD(__builtin_va_arg , KEYALL) KEYWORD(__extension__ , KEYALL) KEYWORD(__float128 , KEYALL) KEYWORD(__imag , KEYALL) +KEYWORD(__int48 , KEYALL) KEYWORD(__int128 , KEYALL) KEYWORD(__label__ , KEYALL) KEYWORD(__real , KEYALL) diff --git a/clang/include/clang/CodeGen/CGFunctionInfo.h b/clang/include/clang/CodeGen/CGFunctionInfo.h index eaf5a3d5aad710..faaf4a1e9248a4 100644 --- a/clang/include/clang/CodeGen/CGFunctionInfo.h +++ b/clang/include/clang/CodeGen/CGFunctionInfo.h @@ -527,6 +527,9 @@ class CGFunctionInfo final /// Whether this function has nocf_check attribute. unsigned NoCfCheck : 1; + /// Whether this function has tiflags attribute. + unsigned TIFlags : 1; + RequiredArgs Required; /// The struct representing all arguments passed in memory. Only used when @@ -615,6 +618,9 @@ class CGFunctionInfo final /// Whether this function has nocf_check attribute. bool isNoCfCheck() const { return NoCfCheck; } + /// Whether this function has tiflags attribute. + bool isTIFlags() const { return TIFlags; } + /// getASTCallingConvention() - Return the AST-specified calling /// convention. CallingConv getASTCallingConvention() const { @@ -641,7 +647,7 @@ class CGFunctionInfo final return FunctionType::ExtInfo(isNoReturn(), getHasRegParm(), getRegParm(), getASTCallingConvention(), isReturnsRetained(), isNoCallerSavedRegs(), isNoCfCheck(), - isCmseNSCall()); + isCmseNSCall(), isTIFlags()); } CanQualType getReturnType() const { return getArgsBuffer()[0].type; } @@ -683,6 +689,7 @@ class CGFunctionInfo final ID.AddInteger(RegParm); ID.AddBoolean(NoCfCheck); ID.AddBoolean(CmseNSCall); + ID.AddBoolean(TIFlags); ID.AddInteger(Required.getOpaqueData()); ID.AddBoolean(HasExtParameterInfos); if (HasExtParameterInfos) { @@ -711,6 +718,7 @@ class CGFunctionInfo final ID.AddInteger(info.getRegParm()); ID.AddBoolean(info.getNoCfCheck()); ID.AddBoolean(info.getCmseNSCall()); + ID.AddBoolean(info.getTIFlags()); ID.AddInteger(required.getOpaqueData()); ID.AddBoolean(!paramInfos.empty()); if (!paramInfos.empty()) { diff --git a/clang/include/clang/Driver/CC1Options.td b/clang/include/clang/Driver/CC1Options.td index 8729512454c3ad..9f3c49e507f8c7 100644 --- a/clang/include/clang/Driver/CC1Options.td +++ b/clang/include/clang/Driver/CC1Options.td @@ -948,3 +948,10 @@ def dwarf_debug_producer : Separate<["-"], "dwarf-debug-producer">, def defsym : Separate<["-"], "defsym">, HelpText<"Define a value for a symbol">; } // let Flags = [CC1AsOption] + +//===----------------------------------------------------------------------===// +// Temp Debug Options +//===----------------------------------------------------------------------===// +// Temp Debug Options +def test_ez80_hack : Flag<["-"], "test-ez80-hack">, + Flags<[CC1Option]>, HelpText<"Enable hack for testing ez80 code generation.">; diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index eca822c6afa34f..30431c0a347798 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -159,6 +159,8 @@ def m_x86_Features_Group : OptionGroup<"">, Group, Flags<[CoreOption]>, DocName<"X86">; def m_riscv_Features_Group : OptionGroup<"">, Group, DocName<"RISCV">; +def m_z80_Features_Group : OptionGroup<"">, + Group, Flags<[CoreOption]>, DocName<"Z80">; def m_libc_Group : OptionGroup<"">, Group, Flags<[HelpHidden]>; diff --git a/clang/include/clang/Sema/DeclSpec.h b/clang/include/clang/Sema/DeclSpec.h index 8db03babfb1e97..9ac8db7b746c5a 100644 --- a/clang/include/clang/Sema/DeclSpec.h +++ b/clang/include/clang/Sema/DeclSpec.h @@ -278,6 +278,7 @@ class DeclSpec { static const TST TST_char16 = clang::TST_char16; static const TST TST_char32 = clang::TST_char32; static const TST TST_int = clang::TST_int; + static const TST TST_int48 = clang::TST_int48; static const TST TST_int128 = clang::TST_int128; static const TST TST_extint = clang::TST_extint; static const TST TST_half = clang::TST_half; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index c6f9f1d1a08f4b..e8a27f542fb71a 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1080,6 +1080,12 @@ class TypeIdx { // \brief SVE types with auto numeration #define SVE_TYPE(Name, Id, SingletonId) PREDEF_TYPE_##Id##_ID, #include "clang/Basic/AArch64SVEACLETypes.def" + + /// The '__uint48_t' type. + PREDEF_TYPE_UINT48_ID, + + /// The '__int48_t' type. + PREDEF_TYPE_INT48_ID, }; /// The number of predefined type IDs that are reserved for @@ -1198,6 +1204,13 @@ class TypeIdx { /// The internal '__type_pack_element' template. PREDEF_DECL_TYPE_PACK_ELEMENT_ID = 17, + + /// The signed 48-bit integer type. + PREDEF_DECL_INT_48_ID = 18, + + /// The unsigned 48-bit integer type. + PREDEF_DECL_UNSIGNED_INT_48_ID = 19, + }; /// The number of declaration IDs that are predefined. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 2ba643f12a82fa..60b3eea54b3f68 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1234,6 +1234,18 @@ TypedefDecl *ASTContext::buildImplicitTypedef(QualType T, return NewDecl; } +TypedefDecl *ASTContext::getInt48Decl() const { + if (!Int48Decl) + Int48Decl = buildImplicitTypedef(Int48Ty, "__int48_t"); + return Int48Decl; +} + +TypedefDecl *ASTContext::getUInt48Decl() const { + if (!UInt48Decl) + UInt48Decl = buildImplicitTypedef(UnsignedInt48Ty, "__uint48_t"); + return UInt48Decl; +} + TypedefDecl *ASTContext::getInt128Decl() const { if (!Int128Decl) Int128Decl = buildImplicitTypedef(Int128Ty, "__int128_t"); @@ -1326,6 +1338,10 @@ void ASTContext::InitBuiltinTypes(const TargetInfo &Target, InitBuiltinType(SatUnsignedFractTy, BuiltinType::SatUFract); InitBuiltinType(SatUnsignedLongFractTy, BuiltinType::SatULongFract); + // eZ80 extension, 48-bit integers. + InitBuiltinType(Int48Ty, BuiltinType::Int48); + InitBuiltinType(UnsignedInt48Ty, BuiltinType::UInt48); + // GNU extension, 128-bit integers. InitBuiltinType(Int128Ty, BuiltinType::Int128); InitBuiltinType(UnsignedInt128Ty, BuiltinType::UInt128); @@ -1997,6 +2013,11 @@ TypeInfo ASTContext::getTypeInfoImpl(const Type *T) const { Width = Target->getLongWidth(); Align = Target->getLongAlign(); break; + case BuiltinType::UInt48: + case BuiltinType::Int48: + Width = 48; + Align = 8; // int48_t is 8-bit aligned on all (e)Z80 targets. + break; case BuiltinType::ULongLong: case BuiltinType::LongLong: Width = Target->getLongLongWidth(); @@ -6068,28 +6089,33 @@ unsigned ASTContext::getIntegerRank(const Type *T) const { switch (cast(T)->getKind()) { default: llvm_unreachable("getIntegerRank(): not a built-in integer"); + // Standard Integer Types case BuiltinType::Bool: - return 1 + (getIntWidth(BoolTy) << 3); + return 1 + (1 << 3) + (getIntWidth(BoolTy) << 4); case BuiltinType::Char_S: case BuiltinType::Char_U: case BuiltinType::SChar: case BuiltinType::UChar: - return 2 + (getIntWidth(CharTy) << 3); + return 2 + (1 << 3) + (getIntWidth(CharTy) << 4); case BuiltinType::Short: case BuiltinType::UShort: - return 3 + (getIntWidth(ShortTy) << 3); + return 3 + (1 << 3) + (getIntWidth(ShortTy) << 4); case BuiltinType::Int: case BuiltinType::UInt: - return 4 + (getIntWidth(IntTy) << 3); + return 4 + (1 << 3) + (getIntWidth(IntTy) << 4); case BuiltinType::Long: case BuiltinType::ULong: - return 5 + (getIntWidth(LongTy) << 3); + return 5 + (1 << 3) + (getIntWidth(LongTy) << 4); case BuiltinType::LongLong: case BuiltinType::ULongLong: - return 6 + (getIntWidth(LongLongTy) << 3); + return 6 + (1 << 3) + (getIntWidth(LongLongTy) << 4); + // Extended Integer Types + case BuiltinType::Int48: + case BuiltinType::UInt48: + return 1 + (getIntWidth(Int48Ty) << 4); case BuiltinType::Int128: case BuiltinType::UInt128: - return 7 + (getIntWidth(Int128Ty) << 3); + return 2 + (getIntWidth(Int128Ty) << 4); } } @@ -7030,6 +7056,8 @@ static char getObjCEncodingForPrimitiveType(const ASTContext *C, case BuiltinType::SatUShortFract: case BuiltinType::SatUFract: case BuiltinType::SatULongFract: + case BuiltinType::UInt48: + case BuiltinType::Int48: // FIXME: potentially need @encodes for these! return ' '; @@ -9111,6 +9139,8 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, return {}; if (lbaseInfo.getNoCfCheck() != rbaseInfo.getNoCfCheck()) return {}; + if (lbaseInfo.getTIFlags() != rbaseInfo.getTIFlags()) + return {}; // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'. bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn(); @@ -9756,6 +9786,8 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const { return UnsignedIntTy; case BuiltinType::Long: return UnsignedLongTy; + case BuiltinType::Int48: + return UnsignedInt48Ty; case BuiltinType::LongLong: return UnsignedLongLongTy; case BuiltinType::Int128: @@ -10666,8 +10698,12 @@ QualType ASTContext::getIntTypeForBitwidth(unsigned DestWidth, unsigned Signed) const { TargetInfo::IntType Ty = getTargetInfo().getIntTypeByWidth(DestWidth, Signed); CanQualType QualTy = getFromTargetType(Ty); - if (!QualTy && DestWidth == 128) - return Signed ? Int128Ty : UnsignedInt128Ty; + if (!QualTy) { + if (DestWidth == 128) + return Signed ? Int128Ty : UnsignedInt128Ty; + if (DestWidth == 48) + return Signed ? Int48Ty : UnsignedInt48Ty; + } return QualTy; } diff --git a/clang/lib/AST/ASTStructuralEquivalence.cpp b/clang/lib/AST/ASTStructuralEquivalence.cpp index 8b5b2444f1e25b..3a5504b111c1be 100644 --- a/clang/lib/AST/ASTStructuralEquivalence.cpp +++ b/clang/lib/AST/ASTStructuralEquivalence.cpp @@ -391,6 +391,8 @@ static bool IsStructurallyEquivalent(StructuralEquivalenceContext &Context, return false; if (EI1.getNoCfCheck() != EI2.getNoCfCheck()) return false; + if (EI1.getTIFlags() != EI2.getTIFlags()) + return false; return true; } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index afae020f3dd272..6a47cd2cc94426 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -10481,6 +10481,7 @@ EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts) { case BuiltinType::UShort: case BuiltinType::UInt: case BuiltinType::ULong: + case BuiltinType::UInt48: case BuiltinType::ULongLong: case BuiltinType::UInt128: return GCCTypeClass::Integer; diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp index ddfbe9f864991f..ccf96324f1f8a7 100644 --- a/clang/lib/AST/ItaniumMangle.cpp +++ b/clang/lib/AST/ItaniumMangle.cpp @@ -2835,6 +2835,12 @@ void CXXNameMangler::mangleType(const BuiltinType *T) { << type_name; \ break; #include "clang/Basic/AArch64SVEACLETypes.def" + case BuiltinType::Int48: + Out << "9z80_int48"; + break; + case BuiltinType::UInt48: + Out << "10z80_uint48"; + break; } } diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp index 529f301e469644..ce6f5689826e8b 100644 --- a/clang/lib/AST/MicrosoftMangle.cpp +++ b/clang/lib/AST/MicrosoftMangle.cpp @@ -2115,7 +2115,9 @@ void MicrosoftCXXNameMangler::mangleType(const BuiltinType *T, Qualifiers, case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: - case BuiltinType::Float128: { + case BuiltinType::Float128: + case BuiltinType::Int48: + case BuiltinType::UInt48: { DiagnosticsEngine &Diags = Context.getDiags(); unsigned DiagID = Diags.getCustomDiagID( DiagnosticsEngine::Error, "cannot mangle this built-in %0 type yet"); diff --git a/clang/lib/AST/NSAPI.cpp b/clang/lib/AST/NSAPI.cpp index ace7f1ceebe74e..58d9e6e80cd555 100644 --- a/clang/lib/AST/NSAPI.cpp +++ b/clang/lib/AST/NSAPI.cpp @@ -487,6 +487,8 @@ NSAPI::getNSNumberFactoryMethodKind(QualType T) const { case BuiltinType::OMPArrayShaping: case BuiltinType::OMPIterator: case BuiltinType::BFloat16: + case BuiltinType::Int48: + case BuiltinType::UInt48: break; } diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index f3ac181214ac62..89928d02994039 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -749,6 +749,8 @@ bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt, case BuiltinType::Char8: // FIXME: Treat like 'char'? case BuiltinType::Char16: case BuiltinType::Char32: + case BuiltinType::UInt48: + case BuiltinType::Int48: case BuiltinType::UInt128: case BuiltinType::Int128: case BuiltinType::Half: diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 05962f34bbf1c3..01aa70ec66677a 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2905,6 +2905,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { return "int"; case Long: return "long"; + case Int48: + return "__int48"; case LongLong: return "long long"; case Int128: @@ -2917,6 +2919,8 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const { return "unsigned int"; case ULong: return "unsigned long"; + case UInt48: + return "unsigned __int48"; case ULongLong: return "unsigned long long"; case UInt128: diff --git a/clang/lib/AST/TypeLoc.cpp b/clang/lib/AST/TypeLoc.cpp index 57c11ca5571db9..6e498b798a4aba 100644 --- a/clang/lib/AST/TypeLoc.cpp +++ b/clang/lib/AST/TypeLoc.cpp @@ -376,6 +376,8 @@ TypeSpecifierType BuiltinTypeLoc::getWrittenTypeSpec() const { case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::UInt48: + case BuiltinType::Int48: llvm_unreachable("Builtin type needs extra local data!"); // Fall through, if the impossible happens. diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 6f6932e652146e..46af1c49cb5f62 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -956,6 +956,8 @@ void TypePrinter::printFunctionAfter(const FunctionType::ExtInfo &Info, OS << " __attribute__((no_caller_saved_registers))"; if (Info.getNoCfCheck()) OS << " __attribute__((nocf_check))"; + if (Info.getTIFlags()) + OS << " __attribute__((tiflags))"; } void TypePrinter::printFunctionNoProtoBefore(const FunctionNoProtoType *T, @@ -1594,6 +1596,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, // FIXME: When Sema learns to form this AttributedType, avoid printing the // attribute again in printFunctionProtoAfter. case attr::AnyX86NoCfCheck: OS << "nocf_check"; break; + case attr::AnyZ80TIFlags: OS << "tiflags"; break; case attr::CDecl: OS << "cdecl"; break; case attr::FastCall: OS << "fastcall"; break; case attr::StdCall: OS << "stdcall"; break; diff --git a/clang/lib/Basic/CMakeLists.txt b/clang/lib/Basic/CMakeLists.txt index 1b55d841737714..8cda98c5a1c93e 100644 --- a/clang/lib/Basic/CMakeLists.txt +++ b/clang/lib/Basic/CMakeLists.txt @@ -88,6 +88,7 @@ add_clang_library(clangBasic Targets/WebAssembly.cpp Targets/X86.cpp Targets/XCore.cpp + Targets/Z80.cpp TokenKinds.cpp TypeTraits.cpp Version.cpp diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 7f360b715da905..c907e8f3019426 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -39,6 +39,7 @@ TargetInfo::TargetInfo(const llvm::Triple &T) : TargetOpts(), Triple(T) { HasBFloat16 = false; PointerWidth = PointerAlign = 32; BoolWidth = BoolAlign = 8; + ShortWidth = ShortAlign = 16; IntWidth = IntAlign = 32; LongWidth = LongAlign = 32; LongLongWidth = LongLongAlign = 64; diff --git a/clang/lib/Basic/Targets.cpp b/clang/lib/Basic/Targets.cpp index 6bbcafa27dfe64..1d8cd5e5d972c7 100644 --- a/clang/lib/Basic/Targets.cpp +++ b/clang/lib/Basic/Targets.cpp @@ -37,6 +37,7 @@ #include "Targets/WebAssembly.h" #include "Targets/X86.h" #include "Targets/XCore.h" +#include "Targets/Z80.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Triple.h" @@ -617,6 +618,11 @@ TargetInfo *AllocateTarget(const llvm::Triple &Triple, case llvm::Triple::ve: return new LinuxTargetInfo(Triple, Opts); + + case llvm::Triple::z80: + return new Z80TargetInfo(Triple, Opts); + case llvm::Triple::ez80: + return new EZ80TargetInfo(Triple, Opts); } } } // namespace targets diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index c33c608e27c843..e19550aaa1a471 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -668,6 +668,17 @@ class LLVM_LIBRARY_VISIBILITY X86_64TargetInfo : public X86TargetInfo { // x86-64 has atomics up to 16 bytes. MaxAtomicPromoteWidth = 128; MaxAtomicInlineWidth = 64; + + if (Opts.TestEZ80Hack) { + // ez80 debug hack + IntWidth = 24; + LongWidth = LongAlign = 32; + SizeType = UnsignedLongLong; + PtrDiffType = SignedLongLong; + IntPtrType = SignedLongLong; + IntMaxType = SignedLongLong; + Int64Type = SignedLongLong; + } } BuiltinVaListKind getBuiltinVaListKind() const override { diff --git a/clang/lib/Basic/Targets/Z80.cpp b/clang/lib/Basic/Targets/Z80.cpp new file mode 100644 index 00000000000000..f33aa75fbc5a3a --- /dev/null +++ b/clang/lib/Basic/Targets/Z80.cpp @@ -0,0 +1,60 @@ +//===--- Z80.cpp - Implement Z80 target feature support -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements Z80 TargetInfo objects. +// +//===----------------------------------------------------------------------===// + +#include "Z80.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; +using namespace clang::targets; + +bool Z80TargetInfo::setCPU(const std::string &Name) { + return llvm::StringSwitch(Name) + .Case("generic", true) + .Case("z80", true) + .Case("z180", true) + .Default(false); +} + +bool Z80TargetInfo:: +initFeatureMap(llvm::StringMap &Features, + DiagnosticsEngine &Diags, StringRef CPU, + const std::vector &FeaturesVec) const { + if (CPU == "z80") + Features["undoc"] = true; + if (CPU == "z180") + Features["z180"] = true; + return TargetInfo::initFeatureMap(Features, Diags, CPU, FeaturesVec); +} + +void Z80TargetInfo::getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + Z80TargetInfoBase::getTargetDefines(Opts, Builder); + defineCPUMacros(Builder, "Z80", /*Tuning=*/false); + if (getTargetOpts().CPU == "undoc") + defineCPUMacros(Builder, "Z80_UNDOC", /*Tuning=*/false); + else if (getTargetOpts().CPU == "z180") + defineCPUMacros(Builder, "Z180", /*Tuning=*/false); +} + +bool EZ80TargetInfo::setCPU(const std::string &Name) { + return llvm::StringSwitch(Name) + .Case("generic", true) + .Case("ez80", true) + .Default(false); +} + +void EZ80TargetInfo::getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + Z80TargetInfoBase::getTargetDefines(Opts, Builder); + defineCPUMacros(Builder, "EZ80", /*Tuning=*/false); +} diff --git a/clang/lib/Basic/Targets/Z80.h b/clang/lib/Basic/Targets/Z80.h new file mode 100644 index 00000000000000..0fff1261334810 --- /dev/null +++ b/clang/lib/Basic/Targets/Z80.h @@ -0,0 +1,102 @@ +//===--- Z80.h - Declare Z80 target feature support -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares Z80 TargetInfo objects. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_BASIC_TARGETS_Z80_H +#define LLVM_CLANG_LIB_BASIC_TARGETS_Z80_H + +#include "Targets.h" +#include "clang/Basic/TargetInfo.h" +#include "llvm/Support/Compiler.h" + +namespace clang { +namespace targets { + +class LLVM_LIBRARY_VISIBILITY Z80TargetInfoBase : public TargetInfo { +public: + Z80TargetInfoBase(const llvm::Triple &Triple, const TargetOptions &) + : TargetInfo(Triple) { + TLSSupported = false; + PointerAlign = BoolAlign = ShortAlign = IntAlign = HalfAlign = FloatAlign = + DoubleAlign = LongDoubleAlign = LongAlign = LongLongAlign = + SuitableAlign = MinGlobalAlign = 8; + DoubleWidth = 32; + DoubleFormat = &llvm::APFloat::IEEEsingle(); + DefaultAlignForAttributeAligned = 32; + SizeType = UnsignedInt; + WCharType = SignedShort; + PtrDiffType = IntPtrType = WIntType = SignedInt; + Char32Type = UnsignedLong; + } + bool hasInt48Type() const override { return true; } + ArrayRef getTargetBuiltins() const final { return None; } + BuiltinVaListKind getBuiltinVaListKind() const override { + return TargetInfo::CharPtrBuiltinVaList; + } + bool validateAsmConstraint(const char *&Name, + TargetInfo::ConstraintInfo &Info) const override { + return false; + } + const char *getClobbers() const override { return ""; } + ArrayRef getGCCRegNames() const override { return None; } + ArrayRef getGCCRegAliases() const override { + return None; + } + + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override { + } + + bool allowsLargerPreferedTypeAlignment() const override { return false; } +}; + +class LLVM_LIBRARY_VISIBILITY Z80TargetInfo : public Z80TargetInfoBase { +public: + explicit Z80TargetInfo(const llvm::Triple &T, const TargetOptions &Opts) + : Z80TargetInfoBase(T, Opts) { + PointerWidth = IntWidth = 16; + resetDataLayout("e-m:o-p:16:8-p1:8:8-i16:8-i24:8-i32:8-i48:8-i64:8-i96:8-f32:8-f64:8-a:8-n8:16-S8"); + } + +private: + bool setCPU(const std::string &Name) override; + bool + initFeatureMap(llvm::StringMap &Features, DiagnosticsEngine &Diags, + StringRef CPU, + const std::vector &FeaturesVec) const override; + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override; +}; + +class LLVM_LIBRARY_VISIBILITY EZ80TargetInfo : public Z80TargetInfoBase { +public: + explicit EZ80TargetInfo(const llvm::Triple &T, const TargetOptions &Opts) + : Z80TargetInfoBase(T, Opts) { + if (T.getEnvironment() == llvm::Triple::CODE16) { + PointerWidth = IntWidth = 16; + resetDataLayout("e-m:o-p:16:8-p1:16:8-p2:24:8-i16:8-i24:8-i32:8-i48:8-i64:8-i96:8-f32:8-f64:8-a:8-n8:16-S8"); + } else { + PointerWidth = IntWidth = 24; + resetDataLayout("e-m:o-p:24:8-p1:16:8-p2:16:8-i16:8-i24:8-i32:8-i48:8-i64:8-i96:8-f32:8-f64:8-a:8-n8:16:24-S8"); + } + } + +private: + bool setCPU(const std::string &Name) override; + void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const override; +}; + +} // namespace targets +} // namespace clang + +#endif // LLVM_CLANG_LIB_BASIC_TARGETS_Z80_H diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 9e6d5e4593d3d6..b1f22bfde681cb 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -257,6 +257,8 @@ static bool asanUseGlobalsGC(const Triple &T, const CodeGenOptions &CGOpts) { return CGOpts.DataSections && !CGOpts.DisableIntegratedAS; case Triple::XCOFF: llvm::report_fatal_error("ASan not implemented for XCOFF."); + case Triple::OMF: + llvm::report_fatal_error("ASan not implemented for OMF."); case Triple::Wasm: case Triple::UnknownObjectFormat: break; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 87242442a57f8a..3e715ff0f48fa7 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -821,6 +821,7 @@ CGFunctionInfo *CGFunctionInfo::create(unsigned llvmCC, FI->ReturnsRetained = info.getProducesResult(); FI->NoCallerSavedRegs = info.getNoCallerSavedRegs(); FI->NoCfCheck = info.getNoCfCheck(); + FI->TIFlags = info.getTIFlags(); FI->Required = required; FI->HasRegParm = info.getHasRegParm(); FI->RegParm = info.getRegParm(); @@ -1975,6 +1976,8 @@ void CodeGenModule::ConstructAttributeList( FuncAttrs.addAttribute("no_caller_saved_registers"); if (TargetDecl->hasAttr()) FuncAttrs.addAttribute(llvm::Attribute::NoCfCheck); + if (TargetDecl->hasAttr()) + CallingConv = llvm::CallingConv::Z80_TIFlags; HasOptnone = TargetDecl->hasAttr(); if (auto *AllocSize = TargetDecl->getAttr()) { diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index 6965c4a1209c22..c907b4d1296fa7 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -751,6 +751,7 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { break; case BuiltinType::UShort: case BuiltinType::UInt: + case BuiltinType::UInt48: case BuiltinType::UInt128: case BuiltinType::ULong: case BuiltinType::WChar_U: @@ -759,6 +760,7 @@ llvm::DIType *CGDebugInfo::CreateType(const BuiltinType *BT) { break; case BuiltinType::Short: case BuiltinType::Int: + case BuiltinType::Int48: case BuiltinType::Int128: case BuiltinType::Long: case BuiltinType::WChar_S: diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 1d0379afb4b59e..352b7288fb5bed 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -5107,6 +5107,7 @@ std::string CGObjCCommonMac::GetSectionName(StringRef Section, return ("." + Section.substr(2) + "$B").str(); case llvm::Triple::Wasm: case llvm::Triple::XCOFF: + case llvm::Triple::OMF: llvm::report_fatal_error( "Objective-C support is unimplemented for object file format."); } diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 71778ac31660ef..e127e81218fdd1 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4906,6 +4906,8 @@ CodeGenModule::GetAddrOfConstantCFString(const StringLiteral *Literal) { llvm_unreachable("unknown file format"); case llvm::Triple::XCOFF: llvm_unreachable("XCOFF is not yet implemented"); + case llvm::Triple::OMF: + llvm_unreachable("OMF is not yet implemented"); case llvm::Triple::COFF: case llvm::Triple::ELF: case llvm::Triple::Wasm: diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp index d431c0263666ea..a85461b8849290 100644 --- a/clang/lib/CodeGen/CodeGenTypes.cpp +++ b/clang/lib/CodeGen/CodeGenTypes.cpp @@ -515,6 +515,11 @@ llvm::Type *CodeGenTypes::ConvertType(QualType T) { ResultType = llvm::Type::getInt8PtrTy(getLLVMContext()); break; + case BuiltinType::UInt48: + case BuiltinType::Int48: + ResultType = llvm::IntegerType::get(getLLVMContext(), 48); + break; + case BuiltinType::UInt128: case BuiltinType::Int128: ResultType = llvm::IntegerType::get(getLLVMContext(), 128); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index 2829877cfe5d91..16637225c5007e 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -3103,6 +3103,8 @@ static bool TypeInfoIsInStandardLibrary(const BuiltinType *Ty) { case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::Int48: + case BuiltinType::UInt48: return false; case BuiltinType::Dependent: diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 24237c4606877e..595d69a147cd42 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -10620,6 +10620,85 @@ class VETargetCodeGenInfo : public TargetCodeGenInfo { }; } // end anonymous namespace +//===----------------------------------------------------------------------===// +// Z80 ABI Implementation +//===----------------------------------------------------------------------===// + +namespace { +class Z80ABIInfo : public DefaultABIInfo { +public: + Z80ABIInfo(CodeGenTypes &CGT) : DefaultABIInfo(CGT) {} + +private: + void removeExtend(ABIArgInfo &AI) const { + if (AI.isExtend()) { + bool InReg = AI.getInReg(); + AI = ABIArgInfo::getDirect(AI.getCoerceToType()); + AI.setInReg(InReg); + } + } + // DefaultABIInfo's classifyReturnType and classifyArgumentType are + // non-virtual, but computeInfo and EmitVAArg are virtual, so we + // overload them. + void computeInfo(CGFunctionInfo &FI) const override { + if (!getCXXABI().classifyReturnType(FI)) + FI.getReturnInfo() = classifyReturnType(FI.getReturnType()); + removeExtend(FI.getReturnInfo()); + for (auto &Arg : FI.arguments()) + removeExtend(Arg.info = classifyArgumentType(Arg.type)); + } + + Address EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, + QualType Ty) const override; +}; + +class Z80TargetCodeGenInfo : public TargetCodeGenInfo { +public: + Z80TargetCodeGenInfo(CodeGen::CodeGenTypes &CGT) + : TargetCodeGenInfo(std::make_unique(CGT)) {} + void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV, + CodeGen::CodeGenModule &CGM) const override; +}; +} // namespace + +Address Z80ABIInfo::EmitVAArg(CodeGenFunction &CGF, + Address VAListAddr, QualType Ty) const { + Address Addr = emitVoidPtrVAArg( + CGF, VAListAddr, Ty, /*Indirect*/ false, + getContext().getTypeInfoInChars(Ty), + CharUnits::fromQuantity(getDataLayout().getPointerSize()), + /*AllowHigherAlign*/ false); + // Remove SlotSize over-alignment, since stack is never aligned. + return Address(Addr.getPointer(), CharUnits::fromQuantity(1)); +} + +void Z80TargetCodeGenInfo::setTargetAttributes( + const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const { + const FunctionDecl *FD = dyn_cast_or_null(D); + if (!FD) return; + llvm::Function *Fn = cast(GV); + + if (Fn->isDeclaration()) { + if (FD->getAttr()) + Fn->setCallingConv(llvm::CallingConv::Z80_TIFlags); + return; + } + + const AnyZ80InterruptAttr *Attr = FD->getAttr(); + if (!Attr) + return; + + const char *Kind; + switch (Attr->getInterrupt()) { + case AnyZ80InterruptAttr::Generic: Kind = "Generic"; break; + case AnyZ80InterruptAttr::Nested: Kind = "Nested"; break; + case AnyZ80InterruptAttr::NMI: Kind = "NMI"; break; + } + + Fn->setCallingConv(llvm::CallingConv::PreserveAll); + Fn->addFnAttr("interrupt", Kind); +} + //===----------------------------------------------------------------------===// // Driver code //===----------------------------------------------------------------------===// @@ -10824,6 +10903,9 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { return SetCGInfo(new SPIRTargetCodeGenInfo(Types)); case llvm::Triple::ve: return SetCGInfo(new VETargetCodeGenInfo(Types)); + case llvm::Triple::z80: + case llvm::Triple::ez80: + return SetCGInfo(new Z80TargetCodeGenInfo(Types)); } } diff --git a/clang/lib/Driver/CMakeLists.txt b/clang/lib/Driver/CMakeLists.txt index b13789f8b2a8b6..96d8ac4272b688 100644 --- a/clang/lib/Driver/CMakeLists.txt +++ b/clang/lib/Driver/CMakeLists.txt @@ -33,6 +33,7 @@ add_clang_library(clangDriver ToolChains/Arch/SystemZ.cpp ToolChains/Arch/VE.cpp ToolChains/Arch/X86.cpp + ToolChains/Arch/Z80.cpp ToolChains/AIX.cpp ToolChains/Ananas.cpp ToolChains/AMDGPU.cpp diff --git a/clang/lib/Driver/ToolChains/Arch/Z80.cpp b/clang/lib/Driver/ToolChains/Arch/Z80.cpp new file mode 100644 index 00000000000000..b2e82fd5ffe14f --- /dev/null +++ b/clang/lib/Driver/ToolChains/Arch/Z80.cpp @@ -0,0 +1,49 @@ +//===--- Z80.cpp - Z80 Helpers for Tools ------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Z80.h" +#include "ToolChains/CommonArgs.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/DriverDiagnostic.h" +#include "clang/Driver/Options.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/Host.h" + +using namespace clang::driver; +using namespace clang::driver::tools; +using namespace clang; +using namespace llvm::opt; + +const char *z80::getZ80TargetCPU(const ArgList &Args, + const llvm::Triple &Triple) { + if (const Arg *A = Args.getLastArg(clang::driver::options::OPT_march_EQ)) + return A->getValue(); + + // Select the default CPU if none was given (or detection failed). + + if (!Triple.isZ80()) + return nullptr; // This routine is only handling z80 targets. + + bool Is24Bit = Triple.getArch() == llvm::Triple::ez80; + + // Everything else goes to ez80 in 24-bit mode. + if (Is24Bit) + return "ez80"; + + // Fallback to generic + return "z80"; +} + +void z80::getZ80TargetFeatures(const Driver &D, const llvm::Triple &Triple, + const ArgList &Args, + std::vector &Features) { + // Now add any that the user explicitly requested on the command line, + // which may override the defaults. + handleTargetFeaturesGroup(Args, Features, options::OPT_m_z80_Features_Group); +} diff --git a/clang/lib/Driver/ToolChains/Arch/Z80.h b/clang/lib/Driver/ToolChains/Arch/Z80.h new file mode 100644 index 00000000000000..498cbfb829ba80 --- /dev/null +++ b/clang/lib/Driver/ToolChains/Arch/Z80.h @@ -0,0 +1,36 @@ +//===--- Z80.h - Z80-specific Tool Helpers ----------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_Z80_H +#define LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_Z80_H + +#include "clang/Driver/Driver.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Option.h" +#include +#include + +namespace clang { +namespace driver { +namespace tools { +namespace z80 { + +const char *getZ80TargetCPU(const llvm::opt::ArgList &Args, + const llvm::Triple &Triple); + +void getZ80TargetFeatures(const Driver &D, const llvm::Triple &Triple, + const llvm::opt::ArgList &Args, + std::vector &Features); + +} // end namespace z80 +} // end namespace target +} // end namespace driver +} // end namespace clang + +#endif // LLVM_CLANG_LIB_DRIVER_TOOLCHAINS_ARCH_Z80_H diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 8903641a26c6f9..925b9be282eb0d 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -533,6 +533,8 @@ static bool useFramePointerForTargetByDefault(const ArgList &Args, case llvm::Triple::riscv64: case llvm::Triple::amdgcn: case llvm::Triple::r600: + case llvm::Triple::z80: + case llvm::Triple::ez80: return !areOptimizationsEnabled(Args); default: break; diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp index 976db3feb9fcd7..b516bead3b2311 100644 --- a/clang/lib/Driver/ToolChains/CommonArgs.cpp +++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -14,6 +14,7 @@ #include "Arch/SystemZ.h" #include "Arch/VE.h" #include "Arch/X86.h" +#include "Arch/Z80.h" #include "HIP.h" #include "Hexagon.h" #include "InputInfo.h" @@ -364,6 +365,10 @@ std::string tools::getCPUName(const ArgList &Args, const llvm::Triple &T, case llvm::Triple::wasm32: case llvm::Triple::wasm64: return std::string(getWebAssemblyTargetCPU(Args)); + + case llvm::Triple::z80: + case llvm::Triple::ez80: + return std::string(z80::getZ80TargetCPU(Args, T)); } } diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp index 7d792974cd577a..457e24de01efc8 100644 --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -41,6 +41,7 @@ bool FormatToken::isSimpleTypeSpecifier() const { case tok::kw_short: case tok::kw_long: case tok::kw___int64: + case tok::kw___int48: case tok::kw___int128: case tok::kw_signed: case tok::kw_unsigned: diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 8bd248c9503063..6b59fb002467b6 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -3660,6 +3660,7 @@ static void ParseTargetArgs(TargetOptions &Opts, ArgList &Args, else Opts.SDKVersion = Version; } + Opts.TestEZ80Hack = Args.hasArg(OPT_test_ez80_hack); } bool CompilerInvocation::parseSimpleArgs(const ArgList &Args, diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 6eef1e2376f6da..fc01868392c3f9 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -770,6 +770,9 @@ static void InitializePredefinedMacros(const TargetInfo &TI, if (LangOpts.FastMath) Builder.defineMacro("__FAST_MATH__"); + if (!LangOpts.CharIsSigned) + Builder.defineMacro("__CHAR_UNSIGNED__"); + // Initialize target-specific preprocessor defines. // __BYTE_ORDER__ was added in GCC 4.6. It's analogous @@ -803,12 +806,22 @@ static void InitializePredefinedMacros(const TargetInfo &TI, // Define type sizing macros based on the target properties. assert(TI.getCharWidth() == 8 && "Only support 8-bit char so far"); Builder.defineMacro("__CHAR_BIT__", Twine(TI.getCharWidth())); + DefineTypeSize("__CHAR_MAX__", + LangOpts.CharIsSigned ? TargetInfo::SignedChar + : TargetInfo::UnsignedChar, TI, Builder); DefineTypeSize("__SCHAR_MAX__", TargetInfo::SignedChar, TI, Builder); DefineTypeSize("__SHRT_MAX__", TargetInfo::SignedShort, TI, Builder); DefineTypeSize("__INT_MAX__", TargetInfo::SignedInt, TI, Builder); DefineTypeSize("__LONG_MAX__", TargetInfo::SignedLong, TI, Builder); DefineTypeSize("__LONG_LONG_MAX__", TargetInfo::SignedLongLong, TI, Builder); + + DefineTypeSize("__UCHAR_MAX__", TargetInfo::UnsignedChar, TI, Builder); + DefineTypeSize("__USHRT_MAX__", TargetInfo::UnsignedShort, TI, Builder); + DefineTypeSize("__UINT_MAX__", TargetInfo::UnsignedInt, TI, Builder); + DefineTypeSize("__ULONG_MAX__", TargetInfo::UnsignedLong, TI, Builder); + DefineTypeSize("__ULONG_LONG_MAX__", TargetInfo::UnsignedLongLong, TI, Builder); + DefineTypeSize("__WCHAR_MAX__", TI.getWCharType(), TI, Builder); DefineTypeSize("__WINT_MAX__", TI.getWIntType(), TI, Builder); DefineTypeSize("__INTMAX_MAX__", TI.getIntMaxType(), TI, Builder); @@ -835,6 +848,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI, TI.getTypeWidth(TI.getWCharType()), TI, Builder); DefineTypeSizeof("__SIZEOF_WINT_T__", TI.getTypeWidth(TI.getWIntType()), TI, Builder); + if (TI.hasInt48Type()) + DefineTypeSizeof("__SIZEOF_INT48__", 48, TI, Builder); if (TI.hasInt128Type()) DefineTypeSizeof("__SIZEOF_INT128__", 128, TI, Builder); @@ -1116,6 +1131,14 @@ static void InitializePredefinedMacros(const TargetInfo &TI, Builder.defineMacro("__IMAGE_SUPPORT__"); } + if (TI.hasInt48Type() && LangOpts.CPlusPlus && LangOpts.GNUMode) { + // For each extended integer type, g++ defines a macro mapping the + // index of the type (0 in this case) in some list of extended types + // to the type. + Builder.defineMacro("__GLIBCXX_TYPE_INT_N_0", "__int48"); + Builder.defineMacro("__GLIBCXX_BITSIZE_INT_N_0", "48"); + } + if (TI.hasInt128Type() && LangOpts.CPlusPlus && LangOpts.GNUMode) { // For each extended integer type, g++ defines a macro mapping the // index of the type (0 in this case) in some list of extended types diff --git a/clang/lib/Index/USRGeneration.cpp b/clang/lib/Index/USRGeneration.cpp index 0d1e8121982346..10fbc4f9793583 100644 --- a/clang/lib/Index/USRGeneration.cpp +++ b/clang/lib/Index/USRGeneration.cpp @@ -754,6 +754,8 @@ void USRGenerator::VisitType(QualType T) { case BuiltinType::SatUFract: case BuiltinType::SatULongFract: case BuiltinType::BFloat16: + case BuiltinType::UInt48: + case BuiltinType::Int48: IgnoreResults = true; return; case BuiltinType::ObjCId: diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index f336af6f1abcd8..a33e1bd8fe075a 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -3686,6 +3686,10 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, ConsumedEnd = PrevTokLocation; break; } + case tok::kw___int48: + isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int48, Loc, PrevSpec, + DiagID, Policy); + break; case tok::kw___int128: isInvalid = DS.SetTypeSpecType(DeclSpec::TST_int128, Loc, PrevSpec, DiagID, Policy); @@ -4783,6 +4787,7 @@ bool Parser::isKnownToBeTypeSpecifier(const Token &Tok) const { // type-specifiers case tok::kw_short: case tok::kw_long: + case tok::kw___int48: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: @@ -4864,6 +4869,7 @@ bool Parser::isTypeSpecifierQualifier() { // type-specifiers case tok::kw_short: case tok::kw_long: + case tok::kw___int48: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: @@ -5031,6 +5037,7 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { // type-specifiers case tok::kw_short: case tok::kw_long: + case tok::kw___int48: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index f4c30c90ad271c..08079829acde34 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -359,6 +359,7 @@ bool Declarator::isDeclarationOfFunction() const { case TST_float: case TST_half: case TST_int: + case TST_int48: case TST_int128: case TST_extint: case TST_struct: @@ -539,6 +540,7 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T, case DeclSpec::TST_char16: return "char16_t"; case DeclSpec::TST_char32: return "char32_t"; case DeclSpec::TST_int: return "int"; + case DeclSpec::TST_int48: return "__int48"; case DeclSpec::TST_int128: return "__int128"; case DeclSpec::TST_extint: return "_ExtInt"; case DeclSpec::TST_half: return "half"; @@ -1223,9 +1225,10 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) { if (TypeSpecSign != TSS_unspecified) { if (TypeSpecType == TST_unspecified) TypeSpecType = TST_int; // unsigned -> unsigned int, signed -> signed int. - else if (TypeSpecType != TST_int && TypeSpecType != TST_int128 && - TypeSpecType != TST_char && TypeSpecType != TST_wchar && - !IsFixedPointType && TypeSpecType != TST_extint) { + else if (TypeSpecType != TST_int && TypeSpecType != TST_int48 && + TypeSpecType != TST_int128 && TypeSpecType != TST_char && + TypeSpecType != TST_wchar && !IsFixedPointType && + TypeSpecType != TST_extint) { S.Diag(TSSLoc, diag::err_invalid_sign_spec) << getSpecifierName((TST)TypeSpecType, Policy); // signed double -> double. diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp index fc3fb524c8f42a..05a294201666a4 100644 --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -235,6 +235,19 @@ void Sema::Initialize() { if (!TUScope) return; + // Initialize predefined 48-bit integer types, if needed. + if (Context.getTargetInfo().hasInt48Type()) { + // If either of the 48-bit integer types are unavailable to name lookup, + // define them now. + DeclarationName Int48 = &Context.Idents.get("__int48_t"); + if (IdResolver.begin(Int48) == IdResolver.end()) + PushOnScopeChains(Context.getInt48Decl(), TUScope); + + DeclarationName UInt48 = &Context.Idents.get("__uint48_t"); + if (IdResolver.begin(UInt48) == IdResolver.end()) + PushOnScopeChains(Context.getUInt48Decl(), TUScope); + } + // Initialize predefined 128-bit integer types, if needed. if (Context.getTargetInfo().hasInt128Type()) { // If either of the 128-bit integer types are unavailable to name lookup, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 852f204ea7b150..faf4e3c0a5b3bb 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -128,6 +128,7 @@ bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const { // token kind is a valid type specifier case tok::kw_short: case tok::kw_long: + case tok::kw___int48: case tok::kw___int64: case tok::kw___int128: case tok::kw_signed: diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 1a0594512a606f..92e25b8efbf160 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -6023,6 +6023,19 @@ static void handleRISCVInterruptAttr(Sema &S, Decl *D, D->addAttr(::new (S.Context) RISCVInterruptAttr(S.Context, AL, Kind)); } +static void handleAnyZ80InterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (!isFunctionOrMethod(D)) { + S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) + << "'interrupt'" << ExpectedFunction; + return; + } + + if (!checkAttributeNumArgs(S, AL, 0)) + return; + + handleSimpleAttribute(S, D, AL); +} + static void handleInterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) { // Dispatch the interrupt attribute based on the current target. switch (S.Context.getTargetInfo().getTriple().getArch()) { @@ -6044,6 +6057,10 @@ static void handleInterruptAttr(Sema &S, Decl *D, const ParsedAttr &AL) { case llvm::Triple::riscv64: handleRISCVInterruptAttr(S, D, AL); break; + case llvm::Triple::z80: + case llvm::Triple::ez80: + handleAnyZ80InterruptAttr(S, D, AL); + break; default: handleARMInterruptAttr(S, D, AL); break; @@ -7058,6 +7075,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_AnyX86NoCfCheck: handleNoCfCheckAttr(S, D, AL); break; + case ParsedAttr::AT_AnyZ80TIFlags: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_NoThrow: if (!AL.isUsedAsTypeAttr()) handleSimpleAttribute(S, D, AL); diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 7b77d1cb482aee..c123165eca9497 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -861,6 +861,7 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) { case TST_char16: case TST_char32: case TST_int: + case TST_int48: case TST_int128: case TST_half: case TST_float: diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 08d12fc25bf7aa..9e38d184c2a708 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -133,6 +133,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr, case ParsedAttr::AT_CmseNSCall: \ case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \ case ParsedAttr::AT_AnyX86NoCfCheck: \ + case ParsedAttr::AT_AnyZ80TIFlags: \ CALLING_CONV_ATTRS_CASELIST // Microsoft-specific type qualifiers. @@ -1500,6 +1501,15 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { break; } + case DeclSpec::TST_int48: + if (!S.Context.getTargetInfo().hasInt48Type()) + S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported) + << "__int48"; + if (DS.getTypeSpecSign() == DeclSpec::TSS_unsigned) + Result = Context.UnsignedInt48Ty; + else + Result = Context.Int48Ty; + break; case DeclSpec::TST_int128: if (!S.Context.getTargetInfo().hasInt128Type() && !(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice)) @@ -7357,6 +7367,20 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr, return true; } + if (attr.getKind() == ParsedAttr::AT_AnyZ80TIFlags) { + if (S.CheckAttrTarget(attr) || S.CheckAttrNoArgs(attr)) + return true; + + // If this is not a function type, warning will be asserted by subject + // check. + if (!unwrapped.isFunctionType()) + return true; + + FunctionType::ExtInfo EI = unwrapped.get()->getExtInfo().withTIFlags(true); + type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI)); + return true; + } + if (attr.getKind() == ParsedAttr::AT_Regparm) { unsigned value; if (S.CheckRegparmAttr(attr, value)) diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index bf583b02f96b8d..e6ebd88807dbaf 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -47,6 +47,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) { case BuiltinType::ULong: ID = PREDEF_TYPE_ULONG_ID; break; + case BuiltinType::UInt48: + ID = PREDEF_TYPE_UINT48_ID; + break; case BuiltinType::ULongLong: ID = PREDEF_TYPE_ULONGLONG_ID; break; @@ -72,6 +75,9 @@ serialization::TypeIdxFromBuiltin(const BuiltinType *BT) { case BuiltinType::Long: ID = PREDEF_TYPE_LONG_ID; break; + case BuiltinType::Int48: + ID = PREDEF_TYPE_INT48_ID; + break; case BuiltinType::LongLong: ID = PREDEF_TYPE_LONGLONG_ID; break; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 4dd2054d4b0798..2d65094e8fbe98 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -6833,6 +6833,9 @@ QualType ASTReader::GetType(TypeID ID) { case PREDEF_TYPE_ULONG_ID: T = Context.UnsignedLongTy; break; + case PREDEF_TYPE_UINT48_ID: + T = Context.UnsignedInt48Ty; + break; case PREDEF_TYPE_ULONGLONG_ID: T = Context.UnsignedLongLongTy; break; @@ -6854,6 +6857,9 @@ QualType ASTReader::GetType(TypeID ID) { case PREDEF_TYPE_LONG_ID: T = Context.LongTy; break; + case PREDEF_TYPE_INT48_ID: + T = Context.Int48Ty; + break; case PREDEF_TYPE_LONGLONG_ID: T = Context.LongLongTy; break; @@ -7342,6 +7348,12 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) { case PREDEF_DECL_OBJC_PROTOCOL_ID: return Context.getObjCProtocolDecl(); + case PREDEF_DECL_INT_48_ID: + return Context.getInt48Decl(); + + case PREDEF_DECL_UNSIGNED_INT_48_ID: + return Context.getUInt48Decl(); + case PREDEF_DECL_INT_128_ID: return Context.getInt128Decl(); diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 2a17360a67bc4c..1e8ac9f5756f76 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -529,6 +529,7 @@ void ASTWriter::WriteTypeAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // NoCallerSavedRegs Abv->Add(BitCodeAbbrevOp(0)); // NoCfCheck Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // CmseNSCall + Abv->Add(BitCodeAbbrevOp(0)); // TIFlags // FunctionProtoType Abv->Add(BitCodeAbbrevOp(0)); // IsVariadic Abv->Add(BitCodeAbbrevOp(0)); // HasTrailingReturn @@ -4437,6 +4438,8 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, RegisterPredefDecl(Context.ObjCClassDecl, PREDEF_DECL_OBJC_CLASS_ID); RegisterPredefDecl(Context.ObjCProtocolClassDecl, PREDEF_DECL_OBJC_PROTOCOL_ID); + RegisterPredefDecl(Context.Int48Decl, PREDEF_DECL_INT_48_ID); + RegisterPredefDecl(Context.UInt48Decl, PREDEF_DECL_UNSIGNED_INT_48_ID); RegisterPredefDecl(Context.Int128Decl, PREDEF_DECL_INT_128_ID); RegisterPredefDecl(Context.UInt128Decl, PREDEF_DECL_UNSIGNED_INT_128_ID); RegisterPredefDecl(Context.ObjCInstanceTypeDecl, diff --git a/clang/tools/driver/cc1_main.cpp b/clang/tools/driver/cc1_main.cpp index 0872015e0ab051..2fb25e223c9766 100644 --- a/clang/tools/driver/cc1_main.cpp +++ b/clang/tools/driver/cc1_main.cpp @@ -27,10 +27,12 @@ #include "clang/FrontendTool/Utils.h" #include "llvm/ADT/Statistic.h" #include "llvm/Config/llvm-config.h" +#include "llvm/InitializePasses.h" #include "llvm/LinkAllPasses.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptTable.h" +#include "llvm/Pass.h" #include "llvm/Support/BuryPointer.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" @@ -234,6 +236,26 @@ int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) { if (!Success) return 1; + if (!Clang->getFrontendOpts().LLVMArgs.empty()) { + // Initialize codegen and IR passes used by llc so that the -print-after, + // -print-before, and -stop-after options work. + llvm::PassRegistry &Registry = *llvm::PassRegistry::getPassRegistry(); + llvm::initializeCore(Registry); + llvm::initializeCodeGen(Registry); + llvm::initializeLoopStrengthReducePass(Registry); + llvm::initializeLowerIntrinsicsPass(Registry); + llvm::initializeEntryExitInstrumenterPass(Registry); + llvm::initializePostInlineEntryExitInstrumenterPass(Registry); + llvm::initializeUnreachableBlockElimLegacyPassPass(Registry); + llvm::initializeConstantHoistingLegacyPassPass(Registry); + llvm::initializeScalarOpts(Registry); + llvm::initializeVectorization(Registry); + llvm::initializeScalarizeMaskedMemIntrinPass(Registry); + llvm::initializeExpandReductionsPass(Registry); + llvm::initializeHardwareLoopsPass(Registry); + llvm::initializeTransformUtils(Registry); + } + // Execute the frontend actions. { llvm::TimeTraceScope TimeScope("ExecuteCompiler"); diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp index c75d5e106cd02d..7c6b8abbc86a49 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2122,6 +2122,7 @@ bool GDBRemoteCommunicationClient::GetCurrentProcessInfo(bool allow_lazy) { break; case llvm::Triple::Wasm: case llvm::Triple::XCOFF: + case llvm::Triple::OMF: LLDB_LOGF(log, "error: not supported target architecture"); return false; case llvm::Triple::UnknownObjectFormat: diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index fe36d49428a500..f9368deb956286 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -4690,6 +4690,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: + case clang::BuiltinType::Int48: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: return lldb::eEncodingSint; @@ -4703,6 +4704,7 @@ lldb::Encoding TypeSystemClang::GetEncoding(lldb::opaque_compiler_type_t type, case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: + case clang::BuiltinType::UInt48: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: return lldb::eEncodingUint; diff --git a/llvm/.gitignore b/llvm/.gitignore index f2b343ade4f321..d51c02523be885 100644 --- a/llvm/.gitignore +++ b/llvm/.gitignore @@ -74,8 +74,10 @@ bindings/go/llvm/workdir #==============================================================================# # Temp files created by most text editors. *~ +#*# # Merge files created by git. *.orig +*.rej # Byte compiled python modules. *.pyc # vim swap files diff --git a/llvm/include/llvm/ADT/Triple.h b/llvm/include/llvm/ADT/Triple.h index 89679619dd55cd..6f89b9e8c31a50 100644 --- a/llvm/include/llvm/ADT/Triple.h +++ b/llvm/include/llvm/ADT/Triple.h @@ -98,7 +98,9 @@ class Triple { renderscript32, // 32-bit RenderScript renderscript64, // 64-bit RenderScript ve, // NEC SX-Aurora Vector Engine - LastArchType = ve + z80, // Z80 + ez80, // eZ80 + LastArchType = ez80 }; enum SubArchType { NoSubArch, @@ -228,6 +230,7 @@ class Triple { COFF, ELF, MachO, + OMF, Wasm, XCOFF, }; @@ -751,6 +754,11 @@ class Triple { return getArch() == Triple::wasm32 || getArch() == Triple::wasm64; } + /// Tests whether the target is (e)z80. + bool isZ80() const { + return getArch() == Triple::z80 || getArch() == Triple::ez80; + } + /// Tests whether the target supports comdat bool supportsCOMDAT() const { return !(isOSBinFormatMachO() || isOSBinFormatXCOFF()); diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h index bdcf10fd1640cd..4e79a8de019c0c 100644 --- a/llvm/include/llvm/BinaryFormat/ELF.h +++ b/llvm/include/llvm/BinaryFormat/ELF.h @@ -307,6 +307,7 @@ enum { EM_COOL = 217, // iCelero CoolEngine EM_NORC = 218, // Nanoradio Optimized RISC EM_CSR_KALIMBA = 219, // CSR Kalimba architecture family + EM_Z80 = 220, // Zilog Z80 EM_AMDGPU = 224, // AMD GPU architecture EM_RISCV = 243, // RISC-V EM_LANAI = 244, // Lanai 32-bit processor @@ -724,6 +725,23 @@ enum : unsigned { EF_AMDGPU_SRAM_ECC = 0x200, }; +// Z80 specific e_flags +enum : unsigned { + EF_Z80_MACH_Z80 = 0x01, + EF_Z80_MACH_Z180 = 0x02, + EF_Z80_MACH_R800 = 0x03, + EF_Z80_MACH_EZ80_Z80 = 0x04, + EF_Z80_MACH_EZ80_ADL = 0x84, + EF_Z80_MACH_GBZ80 = 0x05, + EF_Z80_MACH_Z80N = 0x06, + EF_Z80_MACH_MSK = 0xff +}; + +// ELF Relocation types for Z80 +enum { +#include "ELFRelocs/Z80.def" +}; + // ELF Relocation types for AMDGPU enum { #include "ELFRelocs/AMDGPU.def" diff --git a/llvm/include/llvm/BinaryFormat/ELFRelocs/Z80.def b/llvm/include/llvm/BinaryFormat/ELFRelocs/Z80.def new file mode 100644 index 00000000000000..38f1d6283880ff --- /dev/null +++ b/llvm/include/llvm/BinaryFormat/ELFRelocs/Z80.def @@ -0,0 +1,19 @@ + +#ifndef ELF_RELOC +#error "ELF_RELOC must be defined" +#endif + +ELF_RELOC(R_Z80_NONE, 0) +ELF_RELOC(R_Z80_8, 1) +ELF_RELOC(R_Z80_8_DIS, 2) +ELF_RELOC(R_Z80_8_PCREL, 3) +ELF_RELOC(R_Z80_16, 4) +ELF_RELOC(R_Z80_24, 5) +ELF_RELOC(R_Z80_32, 6) +ELF_RELOC(R_Z80_BYTE0, 7) +ELF_RELOC(R_Z80_BYTE1, 8) +ELF_RELOC(R_Z80_BYTE2, 9) +ELF_RELOC(R_Z80_BYTE3, 10) +ELF_RELOC(R_Z80_WORD0, 11) +ELF_RELOC(R_Z80_WORD1, 12) +ELF_RELOC(R_Z80_16_BE, 13) diff --git a/llvm/include/llvm/BinaryFormat/Magic.h b/llvm/include/llvm/BinaryFormat/Magic.h index 78227ddbe0954d..d3aa2ea486db84 100644 --- a/llvm/include/llvm/BinaryFormat/Magic.h +++ b/llvm/include/llvm/BinaryFormat/Magic.h @@ -50,6 +50,8 @@ struct file_magic { wasm_object, ///< WebAssembly Object file pdb, ///< Windows PDB debug info file tapi_file, ///< Text-based Dynamic Library Stub file + omf_object, ///< IEEE 695 OMF assembler file + omf_archive, ///< IEEE 695 OMF LIBRARY file }; bool is_object() const { return V != unknown; } diff --git a/llvm/include/llvm/CodeGen/CallingConvLower.h b/llvm/include/llvm/CodeGen/CallingConvLower.h index cc3a75c897d3d4..b8393c4850076c 100644 --- a/llvm/include/llvm/CodeGen/CallingConvLower.h +++ b/llvm/include/llvm/CodeGen/CallingConvLower.h @@ -138,6 +138,10 @@ class CCValAssign { isMem = true; } + void setLocInfo(LocInfo NewHTP) { + HTP = NewHTP; + } + unsigned getValNo() const { return ValNo; } MVT getValVT() const { return ValVT; } diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h index 88a1837665aab6..ef0e6b8865b60d 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CallLowering.h @@ -18,6 +18,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/TargetCallingConv.h" +#include "llvm/IR/Attributes.h" #include "llvm/IR/CallingConv.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MachineValueType.h" @@ -50,13 +51,14 @@ class CallLowering { // if the argument was an incoming arg. SmallVector OrigRegs; Type *Ty; + const Value *Val; SmallVector Flags; bool IsFixed; - ArgInfo(ArrayRef Regs, Type *Ty, + ArgInfo(ArrayRef Regs, Type *Ty, const Value *V, ArrayRef Flags = ArrayRef(), bool IsFixed = true) - : Regs(Regs.begin(), Regs.end()), Ty(Ty), + : Regs(Regs.begin(), Regs.end()), Ty(Ty), Val(V), Flags(Flags.begin(), Flags.end()), IsFixed(IsFixed) { if (!Regs.empty() && Flags.empty()) this->Flags.push_back(ISD::ArgFlagsTy()); @@ -65,11 +67,22 @@ class CallLowering { (Regs.empty() || Regs[0] == 0)) && "only void types should have no register"); } + ArgInfo(ArrayRef Regs, Type *Ty, + ArrayRef Flags = ArrayRef(), + bool IsFixed = true) + : ArgInfo(Regs, Ty, nullptr, Flags, IsFixed) {} + ArgInfo(ArrayRef Regs, const Value *V, + ArrayRef Flags = ArrayRef(), + bool IsFixed = true) + : ArgInfo(Regs, V->getType(), V, Flags, IsFixed) {} - ArgInfo() : Ty(nullptr), IsFixed(false) {} + ArgInfo() : Ty(nullptr), Val(nullptr), IsFixed(false) {} }; struct CallLoweringInfo { + /// Attributes attached to the call. + AttributeList CallAttributes; + /// Calling convention to be used for the call. CallingConv::ID CallConv = CallingConv::C; @@ -151,9 +164,8 @@ class CallLowering { } /// Handle custom values, which may be passed into one or more of \p VAs. - /// \return The number of \p VAs that have been assigned after the first - /// one, and which should therefore be skipped from further - /// processing. + /// \return The number of \p VAs that have been assigned, and which should + /// therefore be skipped from further processing. virtual unsigned assignCustomValue(const ArgInfo &Arg, ArrayRef VAs) { // This is not a pure virtual method because not all targets need to worry @@ -172,6 +184,9 @@ class CallLowering { return AssignFn(ValNo, ValVT, LocVT, LocInfo, Flags, State); } + virtual bool prepareArg(CCValAssign &VA) { return true; } + virtual bool finalize(CCState &State) { return true; } + MachineIRBuilder &MIRBuilder; MachineRegisterInfo &MRI; CCAssignFn *AssignFn; @@ -218,7 +233,8 @@ class CallLowering { /// \p Callback to move them to the assigned locations. /// /// \return True if everything has succeeded, false otherwise. - bool handleAssignments(MachineIRBuilder &MIRBuilder, + bool handleAssignments(CallingConv::ID CallConv, bool IsVarArg, + MachineIRBuilder &MIRBuilder, SmallVectorImpl &Args, ValueHandler &Handler) const; bool handleAssignments(CCState &CCState, diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h index 43a8cb2a1d51c8..397237b95299c2 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -24,13 +24,15 @@ namespace llvm { class GISelChangeObserver; +class GISelKnownBits; +class GlobalValue; +class LegalizerInfo; +class MachineBasicBlock; +class MachineDominatorTree; class MachineIRBuilder; -class MachineRegisterInfo; class MachineInstr; class MachineOperand; -class GISelKnownBits; -class MachineDominatorTree; -class LegalizerInfo; +class MachineRegisterInfo; struct PreferredTuple { LLT Ty; // The result type of the extend. @@ -47,7 +49,22 @@ struct IndexedLoadStoreMatchInfo { struct PtrAddChain { int64_t Imm; - Register Base; + Register Reg; +}; + +struct PtrAddGlobal { + int64_t Imm; + const GlobalValue *Global; +}; + +struct PtrAddConst { + int64_t Imm; + LLT Ty; +}; + +struct FunnelShift { + Register ShiftLeftReg, ShiftRightReg; + int64_t ShiftLeftAmt; }; class CombinerHelper { @@ -95,6 +112,19 @@ class CombinerHelper { /// a single basic block. bool dominates(const MachineInstr &DefMI, const MachineInstr &UseMI); + /// Returns true if \p DefMBB dominates \p UseMBB. By definition a block + /// dominates itself. + /// + /// If we haven't been provided with a MachineDominatorTree during + /// construction, this function returns a conservative result that just checks + /// for equality. + bool dominates(MachineBasicBlock &DefMBB, MachineBasicBlock &UseMBB); + + /// Checks if MI can be moved to the beginning of MBB. + /// + /// \returns true if the instruction can be moved. + bool canMove(MachineInstr &MI, MachineBasicBlock &MBB, bool &SawStore); + /// If \p MI is extend that consumes the result of a load, try to combine it. /// Returns true if MI changed. bool tryCombineExtendingLoads(MachineInstr &MI); @@ -249,6 +279,41 @@ class CombinerHelper { bool applySimplifyAddToSub(MachineInstr &MI, std::tuple &MatchInfo); + bool matchPtrAddGlobalImmed(MachineInstr &MI, PtrAddGlobal &MatchInfo); + bool applyPtrAddGlobalImmed(MachineInstr &MI, PtrAddGlobal &MatchInfo); + + bool matchPtrAddConstImmed(MachineInstr &MI, PtrAddConst &MatchInfo); + bool applyPtrAddConstImmed(MachineInstr &MI, PtrAddConst &MatchInfo); + + bool matchCombineShlToAdd(MachineInstr &MI, unsigned &ShiftVal); + bool applyCombineShlToAdd(MachineInstr &MI, unsigned &ShiftVal); + + bool matchCombineSExtToZExt(MachineInstr &MI); + bool applyCombineSExtToZExt(MachineInstr &MI); + + bool matchCombineOrToAdd(MachineInstr &MI); + bool applyCombineOrToAdd(MachineInstr &MI); + + bool matchCombineFunnelShift(MachineInstr &MI, FunnelShift &MatchInfo); + void applyCombineFunnelShift(MachineInstr &MI, const FunnelShift &MatchInfo); + + bool matchCombineIdentity(MachineInstr &MI); + bool applyCombineIdentity(MachineInstr &MI); + + /// Split branches on conditions combined with and/or into multiple branches. + bool matchSplitConditions(MachineInstr &MI); + void applySplitConditions(MachineInstr &MI); + + bool matchFlipCondition(MachineInstr &MI, MachineInstr *&CmpI); + void applyFlipCondition(MachineInstr &MI, MachineInstr &CmpI); + + /// Undo combines involving popcnt. + bool matchLowerIsPowerOfTwo(MachineInstr &MI); + void applyLowerIsPowerOfTwo(MachineInstr &MI); + + bool matchSinkConstant(MachineInstr &MI, MachineInstr *&DomUseMI); + void applySinkConstant(MachineInstr &MI, MachineInstr &DomUseMI); + /// Try to transform \p MI by using all of the above /// combine functions. Returns true if changed. bool tryCombine(MachineInstr &MI); diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h index 016b0bacab8527..757288e0dceebb 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h @@ -536,7 +536,10 @@ class LegalizationArtifactCombiner { const unsigned NumMergeRegs = MergeI->getNumOperands() - 1; if (NumMergeRegs < NumDefs) { - if (NumDefs % NumMergeRegs != 0) + if (NumMergeRegs % NumDefs != 0) + return false; + + if (ConvertOp && !MRI.getType(MergeI->getOperand(0).getReg()).isVector()) return false; Builder.setInstr(MI); @@ -651,17 +654,6 @@ class LegalizationArtifactCombiner { return true; } - static bool isMergeLikeOpcode(unsigned Opc) { - switch (Opc) { - case TargetOpcode::G_MERGE_VALUES: - case TargetOpcode::G_BUILD_VECTOR: - case TargetOpcode::G_CONCAT_VECTORS: - return true; - default: - return false; - } - } - bool tryCombineExtract(MachineInstr &MI, SmallVectorImpl &DeadInsts, SmallVectorImpl &UpdatedDefs) { @@ -680,34 +672,94 @@ class LegalizationArtifactCombiner { // %3 = G_EXTRACT %1, (N - %0.getSizeInBits() Register SrcReg = lookThroughCopyInstrs(MI.getOperand(1).getReg()); - MachineInstr *MergeI = MRI.getVRegDef(SrcReg); - if (!MergeI || !isMergeLikeOpcode(MergeI->getOpcode())) + MachineInstr *SrcI = MRI.getVRegDef(SrcReg); + if (!SrcI) return false; Register DstReg = MI.getOperand(0).getReg(); - LLT DstTy = MRI.getType(DstReg); - LLT SrcTy = MRI.getType(SrcReg); - - // TODO: Do we need to check if the resulting extract is supported? - unsigned ExtractDstSize = DstTy.getSizeInBits(); - unsigned Offset = MI.getOperand(2).getImm(); - unsigned NumMergeSrcs = MergeI->getNumOperands() - 1; - unsigned MergeSrcSize = SrcTy.getSizeInBits() / NumMergeSrcs; - unsigned MergeSrcIdx = Offset / MergeSrcSize; + LLT ExtractTy = MRI.getType(DstReg); + unsigned ExtractSize = ExtractTy.getSizeInBits(); + LLT ExtractSrcTy = MRI.getType(SrcReg); + unsigned ExtractSrcSize = ExtractSrcTy.getSizeInBits(); + unsigned ExtractOffset = MI.getOperand(2).getImm(); - // Compute the offset of the last bit the extract needs. - unsigned EndMergeSrcIdx = (Offset + ExtractDstSize - 1) / MergeSrcSize; + Builder.setInstr(MI); + switch (SrcI->getOpcode()) { + case TargetOpcode::G_IMPLICIT_DEF: + Builder.buildUndef(DstReg); + break; + case TargetOpcode::G_INSERT: { + // Try to look through insert + // + // %2 = G_INSERT %1, %0, N0 + // %3 = G_EXTRACT %2, N1 + // => + // + // if N0 == N1 && %0.size == %3.size + // %3 = COPY %0 + // else if N0 <= N1 && N0 + %0.size >= N1 + %3.size + // %3 = G_EXTRACT %0, (N1 - N0) + // else if N0 >= N1 + %3.size || N0 + %0.size >= N1 + // %3 = G_EXTRACT %1, N1 + + Register InsertSrcReg = SrcI->getOperand(1).getReg(); + Register InsertReg = SrcI->getOperand(2).getReg(); + LLT InsertTy = MRI.getType(InsertReg); + unsigned InsertSize = InsertTy.getSizeInBits(); + unsigned InsertOffset = SrcI->getOperand(3).getImm(); + if (InsertOffset == ExtractOffset && InsertSize == ExtractSize) + // intervals are equal, use inserted value directly + Builder.buildCopy(DstReg, InsertReg); + else if (InsertOffset <= ExtractOffset && + InsertOffset + InsertSize >= ExtractOffset + ExtractSize) + // extract interval subset of insert interval + // extract from inserted value instead + Builder.buildExtract(DstReg, InsertReg, ExtractOffset - InsertOffset); + else if (InsertOffset >= ExtractOffset + ExtractSize || + InsertOffset + InsertSize >= ExtractOffset) + // intervals are disjoint, extract from insert source instead + Builder.buildExtract(DstReg, InsertSrcReg, ExtractOffset); + else + // Can't handle the case where the extract spans insert source and value + return false; + break; + } + case TargetOpcode::G_MERGE_VALUES: + case TargetOpcode::G_BUILD_VECTOR: + case TargetOpcode::G_CONCAT_VECTORS: { + // TODO: Do we need to check if the resulting extract is supported? + unsigned NumMergeSrcs = SrcI->getNumOperands() - 1; + unsigned MergeSrcSize = ExtractSrcSize / NumMergeSrcs; + unsigned MergeSrcIdx = ExtractOffset / MergeSrcSize; + + // Compute the offset of the last bit the extract needs. + unsigned EndMergeSrcIdx = (ExtractOffset + ExtractSize - 1) / MergeSrcSize; + + Register MergeSrcReg; + if (MergeSrcIdx != EndMergeSrcIdx) { + // Create a sub-merge to extract from. + unsigned NumSubSrcs = EndMergeSrcIdx - MergeSrcIdx + 1; + SmallVector SubRegs; + SubRegs.reserve(NumSubSrcs); + for (unsigned Idx = MergeSrcIdx; Idx <= EndMergeSrcIdx; ++Idx) + SubRegs.push_back(SrcI->getOperand(Idx + 1).getReg()); + auto SubMerge = + Builder.buildMerge(LLT::scalar(MergeSrcSize * NumSubSrcs), SubRegs); + MergeSrcReg = SubMerge->getOperand(0).getReg(); + } else + MergeSrcReg = SrcI->getOperand(MergeSrcIdx + 1).getReg(); - // Can't handle the case where the extract spans multiple inputs. - if (MergeSrcIdx != EndMergeSrcIdx) + // TODO: We could modify MI in place in most cases. + Builder.buildExtract(DstReg, SrcI->getOperand(MergeSrcIdx + 1).getReg(), + ExtractOffset - MergeSrcIdx * MergeSrcSize); + break; + } + default: return false; + } - // TODO: We could modify MI in place in most cases. - Builder.setInstr(MI); - Builder.buildExtract(DstReg, MergeI->getOperand(MergeSrcIdx + 1).getReg(), - Offset - MergeSrcIdx * MergeSrcSize); UpdatedDefs.push_back(DstReg); - markInstAndDefDead(MI, *MergeI, DeadInsts); + markInstAndDefDead(MI, *SrcI, DeadInsts); return true; } diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h index 0fe1d60b630db2..e2694edae7df00 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h @@ -94,7 +94,7 @@ class LegalizerHelper { /// Legalize an instruction by splitting it into simpler parts, hopefully /// understood by the target. - LegalizeResult lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty); + LegalizeResult lower(MachineInstr &MI, unsigned TypeIdx); /// Legalize a vector instruction by splitting into multiple components, each /// acting on the same scalar type as the original but with fewer elements. @@ -172,9 +172,8 @@ class LegalizerHelper { /// Version which handles irregular splits. bool extractParts(Register Reg, LLT RegTy, LLT MainTy, - LLT &LeftoverTy, - SmallVectorImpl &VRegs, - SmallVectorImpl &LeftoverVRegs); + SmallVectorImpl &VRegs, LLT &LeftoverTy, + Register &LeftoverReg); /// Helper function to build a wide generic register \p DstReg of type \p /// RegTy from smaller parts. This will produce a G_MERGE_VALUES, @@ -185,9 +184,9 @@ class LegalizerHelper { /// /// If \p ResultTy does not evenly break into \p PartTy sized pieces, the /// remainder must be specified with \p LeftoverRegs of type \p LeftoverTy. - void insertParts(Register DstReg, LLT ResultTy, - LLT PartTy, ArrayRef PartRegs, - LLT LeftoverTy = LLT(), ArrayRef LeftoverRegs = {}); + void insertParts(Register DstReg, LLT ResultTy, LLT PartTy, + ArrayRef PartRegs, LLT LeftoverTy = LLT(), + Register LeftoverReg = Register()); /// Unmerge \p SrcReg into \p Parts with the greatest common divisor type with /// \p DstTy and \p NarrowTy. Returns the GCD type. @@ -279,10 +278,14 @@ class LegalizerHelper { LegalizeResult narrowScalarShift(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarMul(MachineInstr &MI, LLT Ty); - LegalizeResult narrowScalarExtract(MachineInstr &MI, unsigned TypeIdx, LLT Ty); + LegalizeResult narrowScalarExtract(MachineInstr &MI, unsigned TypeIdx, + LLT Ty); LegalizeResult narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, LLT Ty); + LegalizeResult narrowScalarUnary(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarBasic(MachineInstr &MI, unsigned TypeIdx, LLT Ty); + LegalizeResult narrowScalarExtended(MachineInstr &MI, unsigned TypeIdx, + LLT Ty); LegalizeResult narrowScalarExt(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarSelect(MachineInstr &MI, unsigned TypeIdx, LLT Ty); LegalizeResult narrowScalarCTLZ(MachineInstr &MI, unsigned TypeIdx, LLT Ty); @@ -314,6 +317,8 @@ class LegalizerHelper { LegalizeResult lowerExtract(MachineInstr &MI); LegalizeResult lowerInsert(MachineInstr &MI); LegalizeResult lowerSADDO_SSUBO(MachineInstr &MI); + LegalizeResult lowerUADDSAT_USUBSAT(MachineInstr &MI); + LegalizeResult lowerSADDSAT_SSUBSAT(MachineInstr &MI); LegalizeResult lowerBswap(MachineInstr &MI); LegalizeResult lowerBitreverse(MachineInstr &MI); LegalizeResult lowerReadWriteRegister(MachineInstr &MI); diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h index 61e0418757bce0..de7372beb3bfea 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerInfo.h @@ -20,11 +20,11 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/TargetOpcodes.h" -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/LowLevelTypeImpl.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/LowLevelTypeImpl.h" #include #include #include @@ -1212,11 +1212,16 @@ class LegalizerInfo { const MachineRegisterInfo &MRI) const; /// Called for instructions with the Custom LegalizationAction. - virtual bool legalizeCustom(LegalizerHelper &Helper, - MachineInstr &MI) const { + virtual bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI) const { llvm_unreachable("must implement this if custom action is used"); } + virtual LegalizerHelper::LegalizeResult + legalizeCustomMaybeLegal(LegalizerHelper &Helper, MachineInstr &MI) const { + return legalizeCustom(Helper, MI) ? LegalizerHelper::Legalized + : LegalizerHelper::UnableToLegalize; + } + /// \returns true if MI is either legal or has been legalized and false if not /// legal. /// Return true if MI is either legal or has been legalized and false diff --git a/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h b/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h index 043be086ff417d..879c7a2920e446 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/MIPatternMatch.h @@ -39,9 +39,12 @@ inline OneUse_match m_OneUse(const SubPat &SP) { return SP; } +template struct ConstantMatch { - int64_t &CR; - ConstantMatch(int64_t &C) : CR(C) {} + static_assert(std::numeric_limits::is_integer, + "Only integral types are allowed."); + Int &CR; + ConstantMatch(Int &C) : CR(C) {} bool match(const MachineRegisterInfo &MRI, Register Reg) { if (auto MaybeCst = getConstantVRegVal(Reg, MRI)) { CR = *MaybeCst; @@ -51,7 +54,8 @@ struct ConstantMatch { } }; -inline ConstantMatch m_ICst(int64_t &Cst) { return ConstantMatch(Cst); } +template +inline ConstantMatch m_ICst(Int &Cst) { return {Cst}; } // TODO: Rework this for different kinds of MachineOperand. // Currently assumes the Src for a match is a register. @@ -234,11 +238,17 @@ m_GAnd(const LHS &L, const RHS &R) { } template -inline BinaryOp_match m_GOr(const LHS &L, - const RHS &R) { +inline BinaryOp_match +m_GOr(const LHS &L, const RHS &R) { return BinaryOp_match(L, R); } +template +inline BinaryOp_match +m_GXor(const LHS &L, const RHS &R) { + return BinaryOp_match(L, R); +} + template inline BinaryOp_match m_GShl(const LHS &L, const RHS &R) { @@ -251,6 +261,12 @@ m_GLShr(const LHS &L, const RHS &R) { return BinaryOp_match(L, R); } +template +inline BinaryOp_match +m_GAShr(const LHS &L, const RHS &R) { + return BinaryOp_match(L, R); +} + // Helper for unary instructions (G_[ZSA]EXT/G_TRUNC) etc template struct UnaryOp_match { SrcTy L; diff --git a/llvm/include/llvm/CodeGen/LiveRegUnits.h b/llvm/include/llvm/CodeGen/LiveRegUnits.h index 1ed091e3bb5e9d..32a8a55cf6989b 100644 --- a/llvm/include/llvm/CodeGen/LiveRegUnits.h +++ b/llvm/include/llvm/CodeGen/LiveRegUnits.h @@ -122,6 +122,11 @@ class LiveRegUnits { return true; } + /// Updates liveness when stepping forwards over the instruction \p MI. + /// This removes the units killed in \p MI and then adds all live units + /// defined in \p MI. + void stepForward(const MachineInstr &MI); + /// Updates liveness when stepping backwards over the instruction \p MI. /// This removes all register units defined or clobbered in \p MI and then /// adds the units used (as in use operands) in \p MI. diff --git a/llvm/include/llvm/CodeGen/MachineOperand.h b/llvm/include/llvm/CodeGen/MachineOperand.h index 0f252137364cf1..a7e5233c19bf87 100644 --- a/llvm/include/llvm/CodeGen/MachineOperand.h +++ b/llvm/include/llvm/CodeGen/MachineOperand.h @@ -730,6 +730,11 @@ class MachineOperand { /// the setImm method should be used. void ChangeToImmediate(int64_t ImmVal); + /// ChangeToCImmediate - Replace this operand with a new CI immediate operand + /// of the specified value. If an operand is known to be an CI immediate + /// already, the setCImm method should be used. + void ChangeToCImmediate(const ConstantInt *CI); + /// ChangeToFPImmediate - Replace this operand with a new FP immediate operand /// of the specified value. If an operand is known to be an FP immediate /// already, the setFPImm method should be used. diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h index f920d3016a95b5..3af91edb81c40d 100644 --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -1649,7 +1649,7 @@ class TargetLoweringBase { /// example, on X86 targets without SSE2 f64 load / store are done with fldl / /// fstpl which also does type conversion. Note the specified type doesn't /// have to be legal as the hook is used before type legalization. - virtual bool isSafeMemOpType(MVT /*VT*/) const { return true; } + virtual bool isSafeMemOpType(MVT VT) const { return VT.isPow2Size(); } /// Return lower limit for number of blocks in a jump table. virtual unsigned getMinimumJumpTableEntries() const; diff --git a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h index 6e2c0973e3547d..6b9ad583f66df0 100644 --- a/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h +++ b/llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h @@ -267,6 +267,17 @@ class TargetLoweringObjectFileXCOFF : public TargetLoweringObjectFile { const TargetMachine &TM) const override; }; +class TargetLoweringObjectFileOMF : public TargetLoweringObjectFile { +public: + TargetLoweringObjectFileOMF() = default; + ~TargetLoweringObjectFileOMF() override = default; + + MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind, + const TargetMachine &TM) const override; + MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind, + const TargetMachine &TM) const override; +}; + } // end namespace llvm #endif // LLVM_CODEGEN_TARGETLOWERINGOBJECTFILEIMPL_H diff --git a/llvm/include/llvm/CodeGen/ValueTypes.h b/llvm/include/llvm/CodeGen/ValueTypes.h index db8161caf7d2e9..1d2576df33a51f 100644 --- a/llvm/include/llvm/CodeGen/ValueTypes.h +++ b/llvm/include/llvm/CodeGen/ValueTypes.h @@ -343,6 +343,18 @@ namespace llvm { return getStoreSize() * 8; } + /// getNumParts - Return the number of parts with PartBits bits that make up + /// this VT. + unsigned getNumParts(unsigned PartBits) const { + return (getSizeInBits() + PartBits - 1) / PartBits; + } + + /// getNumParts - Return the number of parts of type PartVT that make up + /// this VT. + unsigned getNumParts(EVT PartVT) const { + return getNumParts(PartVT.getSizeInBits()); + } + /// Rounds the bit-width of the given integer EVT up to the nearest power of /// two (and at least to eight), and returns the integer EVT with that /// number of bits. diff --git a/llvm/include/llvm/CodeGen/ValueTypes.td b/llvm/include/llvm/CodeGen/ValueTypes.td index dfa2e81608be8c..8ddd4659c8fa50 100644 --- a/llvm/include/llvm/CodeGen/ValueTypes.td +++ b/llvm/include/llvm/CodeGen/ValueTypes.td @@ -22,167 +22,168 @@ def OtherVT: ValueType<0 , 1>; // "Other" value def i1 : ValueType<1 , 2>; // One bit boolean value def i8 : ValueType<8 , 3>; // 8-bit integer value def i16 : ValueType<16 , 4>; // 16-bit integer value -def i32 : ValueType<32 , 5>; // 32-bit integer value -def i64 : ValueType<64 , 6>; // 64-bit integer value -def i128 : ValueType<128, 7>; // 128-bit integer value - -def bf16 : ValueType<16 , 8>; // 16-bit brain floating point value -def f16 : ValueType<16 , 9>; // 16-bit floating point value -def f32 : ValueType<32 , 10>; // 32-bit floating point value -def f64 : ValueType<64 , 11>; // 64-bit floating point value -def f80 : ValueType<80 , 12>; // 80-bit floating point value -def f128 : ValueType<128, 13>; // 128-bit floating point value -def ppcf128: ValueType<128, 14>; // PPC 128-bit floating point value - -def v1i1 : ValueType<1 , 15>; // 1 x i1 vector value -def v2i1 : ValueType<2 , 16>; // 2 x i1 vector value -def v4i1 : ValueType<4 , 17>; // 4 x i1 vector value -def v8i1 : ValueType<8 , 18>; // 8 x i1 vector value -def v16i1 : ValueType<16, 19>; // 16 x i1 vector value -def v32i1 : ValueType<32 , 20>; // 32 x i1 vector value -def v64i1 : ValueType<64 , 21>; // 64 x i1 vector value -def v128i1 : ValueType<128, 22>; // 128 x i1 vector value -def v256i1 : ValueType<256, 23>; // 256 x i1 vector value -def v512i1 : ValueType<512, 24>; // 512 x i1 vector value -def v1024i1: ValueType<1024,25>; //1024 x i1 vector value - -def v1i8 : ValueType<8, 26>; // 1 x i8 vector value -def v2i8 : ValueType<16 , 27>; // 2 x i8 vector value -def v4i8 : ValueType<32 , 28>; // 4 x i8 vector value -def v8i8 : ValueType<64 , 29>; // 8 x i8 vector value -def v16i8 : ValueType<128, 30>; // 16 x i8 vector value -def v32i8 : ValueType<256, 31>; // 32 x i8 vector value -def v64i8 : ValueType<512, 32>; // 64 x i8 vector value -def v128i8 : ValueType<1024,33>; //128 x i8 vector value -def v256i8 : ValueType<2048,34>; //256 x i8 vector value - -def v1i16 : ValueType<16 , 35>; // 1 x i16 vector value -def v2i16 : ValueType<32 , 36>; // 2 x i16 vector value -def v3i16 : ValueType<48 , 37>; // 3 x i16 vector value -def v4i16 : ValueType<64 , 38>; // 4 x i16 vector value -def v8i16 : ValueType<128, 39>; // 8 x i16 vector value -def v16i16 : ValueType<256, 40>; // 16 x i16 vector value -def v32i16 : ValueType<512, 41>; // 32 x i16 vector value -def v64i16 : ValueType<1024,42>; // 64 x i16 vector value -def v128i16: ValueType<2048,43>; //128 x i16 vector value - -def v1i32 : ValueType<32 , 44>; // 1 x i32 vector value -def v2i32 : ValueType<64 , 45>; // 2 x i32 vector value -def v3i32 : ValueType<96 , 46>; // 3 x i32 vector value -def v4i32 : ValueType<128, 47>; // 4 x i32 vector value -def v5i32 : ValueType<160, 48>; // 5 x i32 vector value -def v8i32 : ValueType<256, 49>; // 8 x i32 vector value -def v16i32 : ValueType<512, 50>; // 16 x i32 vector value -def v32i32 : ValueType<1024,51>; // 32 x i32 vector value -def v64i32 : ValueType<2048,52>; // 64 x i32 vector value -def v128i32 : ValueType<4096,53>; // 128 x i32 vector value -def v256i32 : ValueType<8182,54>; // 256 x i32 vector value -def v512i32 : ValueType<16384,55>; // 512 x i32 vector value -def v1024i32 : ValueType<32768,56>; // 1024 x i32 vector value -def v2048i32 : ValueType<65536,57>; // 2048 x i32 vector value - -def v1i64 : ValueType<64 , 58>; // 1 x i64 vector value -def v2i64 : ValueType<128, 59>; // 2 x i64 vector value -def v4i64 : ValueType<256, 60>; // 4 x i64 vector value -def v8i64 : ValueType<512, 61>; // 8 x i64 vector value -def v16i64 : ValueType<1024,62>; // 16 x i64 vector value -def v32i64 : ValueType<2048,63>; // 32 x i64 vector value - -def v1i128 : ValueType<128, 64>; // 1 x i128 vector value - -def v2f16 : ValueType<32 , 65>; // 2 x f16 vector value -def v3f16 : ValueType<48 , 66>; // 3 x f16 vector value -def v4f16 : ValueType<64 , 67>; // 4 x f16 vector value -def v8f16 : ValueType<128, 68>; // 8 x f16 vector value -def v16f16 : ValueType<256, 69>; // 16 x f16 vector value -def v32f16 : ValueType<512, 70>; // 32 x f16 vector value -def v64f16 : ValueType<1024, 71>; // 64 x f16 vector value -def v128f16 : ValueType<2048, 72>; // 128 x f16 vector value -def v2bf16 : ValueType<32 , 73>; // 2 x bf16 vector value -def v3bf16 : ValueType<48 , 74>; // 3 x bf16 vector value -def v4bf16 : ValueType<64 , 75>; // 4 x bf16 vector value -def v8bf16 : ValueType<128, 76>; // 8 x bf16 vector value -def v16bf16 : ValueType<256, 77>; // 16 x bf16 vector value -def v32bf16 : ValueType<512, 78>; // 32 x bf16 vector value -def v64bf16 : ValueType<1024, 79>; // 64 x bf16 vector value -def v128bf16 : ValueType<2048, 80>; // 128 x bf16 vector value -def v1f32 : ValueType<32 , 81>; // 1 x f32 vector value -def v2f32 : ValueType<64 , 82>; // 2 x f32 vector value -def v3f32 : ValueType<96 , 83>; // 3 x f32 vector value -def v4f32 : ValueType<128, 84>; // 4 x f32 vector value -def v5f32 : ValueType<160, 85>; // 5 x f32 vector value -def v8f32 : ValueType<256, 86>; // 8 x f32 vector value -def v16f32 : ValueType<512, 87>; // 16 x f32 vector value -def v32f32 : ValueType<1024, 88>; // 32 x f32 vector value -def v64f32 : ValueType<2048, 89>; // 64 x f32 vector value -def v128f32 : ValueType<4096, 90>; // 128 x f32 vector value -def v256f32 : ValueType<8182, 91>; // 256 x f32 vector value -def v512f32 : ValueType<16384, 92>; // 512 x f32 vector value -def v1024f32 : ValueType<32768, 93>; // 1024 x f32 vector value -def v2048f32 : ValueType<65536, 94>; // 2048 x f32 vector value -def v1f64 : ValueType<64, 95>; // 1 x f64 vector value -def v2f64 : ValueType<128, 96>; // 2 x f64 vector value -def v4f64 : ValueType<256, 97>; // 4 x f64 vector value -def v8f64 : ValueType<512, 98>; // 8 x f64 vector value -def v16f64 : ValueType<1024, 99>; // 16 x f64 vector value -def v32f64 : ValueType<2048, 100>; // 32 x f64 vector value - -def nxv1i1 : ValueType<1, 101>; // n x 1 x i1 vector value -def nxv2i1 : ValueType<2, 102>; // n x 2 x i1 vector value -def nxv4i1 : ValueType<4, 103>; // n x 4 x i1 vector value -def nxv8i1 : ValueType<8, 104>; // n x 8 x i1 vector value -def nxv16i1 : ValueType<16, 105>; // n x 16 x i1 vector value -def nxv32i1 : ValueType<32, 106>; // n x 32 x i1 vector value - -def nxv1i8 : ValueType<8, 107>; // n x 1 x i8 vector value -def nxv2i8 : ValueType<16, 108>; // n x 2 x i8 vector value -def nxv4i8 : ValueType<32, 109>; // n x 4 x i8 vector value -def nxv8i8 : ValueType<64, 110>; // n x 8 x i8 vector value -def nxv16i8 : ValueType<128, 111>; // n x 16 x i8 vector value -def nxv32i8 : ValueType<256, 112>; // n x 32 x i8 vector value - -def nxv1i16 : ValueType<16, 113>; // n x 1 x i16 vector value -def nxv2i16 : ValueType<32, 114>; // n x 2 x i16 vector value -def nxv4i16 : ValueType<64, 115>; // n x 4 x i16 vector value -def nxv8i16 : ValueType<128, 116>; // n x 8 x i16 vector value -def nxv16i16: ValueType<256, 117>; // n x 16 x i16 vector value -def nxv32i16: ValueType<512, 118>; // n x 32 x i16 vector value - -def nxv1i32 : ValueType<32, 119>; // n x 1 x i32 vector value -def nxv2i32 : ValueType<64, 120>; // n x 2 x i32 vector value -def nxv4i32 : ValueType<128, 121>; // n x 4 x i32 vector value -def nxv8i32 : ValueType<256, 122>; // n x 8 x i32 vector value -def nxv16i32: ValueType<512, 123>; // n x 16 x i32 vector value -def nxv32i32: ValueType<1024,124>; // n x 32 x i32 vector value - -def nxv1i64 : ValueType<64, 125>; // n x 1 x i64 vector value -def nxv2i64 : ValueType<128, 126>; // n x 2 x i64 vector value -def nxv4i64 : ValueType<256, 127>; // n x 4 x i64 vector value -def nxv8i64 : ValueType<512, 128>; // n x 8 x i64 vector value -def nxv16i64: ValueType<1024,129>; // n x 16 x i64 vector value -def nxv32i64: ValueType<2048,130>; // n x 32 x i64 vector value - -def nxv2f16 : ValueType<32 , 131>; // n x 2 x f16 vector value -def nxv4f16 : ValueType<64 , 132>; // n x 4 x f16 vector value -def nxv8f16 : ValueType<128, 133>; // n x 8 x f16 vector value -def nxv2bf16 : ValueType<32 , 134>; // n x 2 x bf16 vector value -def nxv4bf16 : ValueType<64 , 135>; // n x 4 x bf16 vector value -def nxv8bf16 : ValueType<128, 136>; // n x 8 x bf16 vector value -def nxv1f32 : ValueType<32 , 137>; // n x 1 x f32 vector value -def nxv2f32 : ValueType<64 , 138>; // n x 2 x f32 vector value -def nxv4f32 : ValueType<128, 139>; // n x 4 x f32 vector value -def nxv8f32 : ValueType<256, 140>; // n x 8 x f32 vector value -def nxv16f32 : ValueType<512, 141>; // n x 16 x f32 vector value -def nxv1f64 : ValueType<64, 142>; // n x 1 x f64 vector value -def nxv2f64 : ValueType<128, 143>; // n x 2 x f64 vector value -def nxv4f64 : ValueType<256, 144>; // n x 4 x f64 vector value -def nxv8f64 : ValueType<512, 145>; // n x 8 x f64 vector value - -def x86mmx : ValueType<64 , 146>; // X86 MMX value -def FlagVT : ValueType<0 , 147>; // Pre-RA sched glue -def isVoid : ValueType<0 , 148>; // Produces no value -def untyped: ValueType<8 , 149>; // Produces an untyped value -def exnref : ValueType<0 , 150>; // WebAssembly's exnref type +def i24 : ValueType<24 , 5>; // 24-bit integer value +def i32 : ValueType<32 , 6>; // 32-bit integer value +def i64 : ValueType<64 , 7>; // 64-bit integer value +def i128 : ValueType<128, 8>; // 128-bit integer value + +def bf16 : ValueType<16 , 9>; // 16-bit brain floating point value +def f16 : ValueType<16 , 10>; // 16-bit floating point value +def f32 : ValueType<32 , 11>; // 32-bit floating point value +def f64 : ValueType<64 , 12>; // 64-bit floating point value +def f80 : ValueType<80 , 13>; // 80-bit floating point value +def f128 : ValueType<128, 14>; // 128-bit floating point value +def ppcf128: ValueType<128, 15>; // PPC 128-bit floating point value + +def v1i1 : ValueType<1 , 16>; // 1 x i1 vector value +def v2i1 : ValueType<2 , 17>; // 2 x i1 vector value +def v4i1 : ValueType<4 , 18>; // 4 x i1 vector value +def v8i1 : ValueType<8 , 19>; // 8 x i1 vector value +def v16i1 : ValueType<16, 20>; // 16 x i1 vector value +def v32i1 : ValueType<32 , 21>; // 32 x i1 vector value +def v64i1 : ValueType<64 , 22>; // 64 x i1 vector value +def v128i1 : ValueType<128, 23>; // 128 x i1 vector value +def v256i1 : ValueType<256, 24>; // 256 x i1 vector value +def v512i1 : ValueType<512, 25>; // 512 x i1 vector value +def v1024i1: ValueType<1024,26>; //1024 x i1 vector value + +def v1i8 : ValueType<8, 27>; // 1 x i8 vector value +def v2i8 : ValueType<16 , 28>; // 2 x i8 vector value +def v4i8 : ValueType<32 , 29>; // 4 x i8 vector value +def v8i8 : ValueType<64 , 30>; // 8 x i8 vector value +def v16i8 : ValueType<128, 31>; // 16 x i8 vector value +def v32i8 : ValueType<256, 32>; // 32 x i8 vector value +def v64i8 : ValueType<512, 33>; // 64 x i8 vector value +def v128i8 : ValueType<1024,34>; //128 x i8 vector value +def v256i8 : ValueType<2048,35>; //256 x i8 vector value + +def v1i16 : ValueType<16 , 36>; // 1 x i16 vector value +def v2i16 : ValueType<32 , 37>; // 2 x i16 vector value +def v3i16 : ValueType<48 , 38>; // 3 x i16 vector value +def v4i16 : ValueType<64 , 39>; // 4 x i16 vector value +def v8i16 : ValueType<128, 40>; // 8 x i16 vector value +def v16i16 : ValueType<256, 41>; // 16 x i16 vector value +def v32i16 : ValueType<512, 42>; // 32 x i16 vector value +def v64i16 : ValueType<1024,43>; // 64 x i16 vector value +def v128i16: ValueType<2048,44>; //128 x i16 vector value + +def v1i32 : ValueType<32 , 45>; // 1 x i32 vector value +def v2i32 : ValueType<64 , 46>; // 2 x i32 vector value +def v3i32 : ValueType<96 , 47>; // 3 x i32 vector value +def v4i32 : ValueType<128, 48>; // 4 x i32 vector value +def v5i32 : ValueType<160, 49>; // 5 x i32 vector value +def v8i32 : ValueType<256, 50>; // 8 x i32 vector value +def v16i32 : ValueType<512, 51>; // 16 x i32 vector value +def v32i32 : ValueType<1024,52>; // 32 x i32 vector value +def v64i32 : ValueType<2048,53>; // 64 x i32 vector value +def v128i32 : ValueType<4096,54>; // 128 x i32 vector value +def v256i32 : ValueType<8182,55>; // 256 x i32 vector value +def v512i32 : ValueType<16384,56>; // 512 x i32 vector value +def v1024i32 : ValueType<32768,57>; // 1024 x i32 vector value +def v2048i32 : ValueType<65536,58>; // 2048 x i32 vector value + +def v1i64 : ValueType<64 , 59>; // 1 x i64 vector value +def v2i64 : ValueType<128, 60>; // 2 x i64 vector value +def v4i64 : ValueType<256, 61>; // 4 x i64 vector value +def v8i64 : ValueType<512, 62>; // 8 x i64 vector value +def v16i64 : ValueType<1024,63>; // 16 x i64 vector value +def v32i64 : ValueType<2048,64>; // 32 x i64 vector value + +def v1i128 : ValueType<128, 65>; // 1 x i128 vector value + +def v2f16 : ValueType<32 , 66>; // 2 x f16 vector value +def v3f16 : ValueType<48 , 67>; // 3 x f16 vector value +def v4f16 : ValueType<64 , 68>; // 4 x f16 vector value +def v8f16 : ValueType<128, 69>; // 8 x f16 vector value +def v16f16 : ValueType<256, 70>; // 16 x f16 vector value +def v32f16 : ValueType<512, 71>; // 32 x f16 vector value +def v64f16 : ValueType<1024, 72>; // 64 x f16 vector value +def v128f16 : ValueType<2048, 73>; // 128 x f16 vector value +def v2bf16 : ValueType<32 , 74>; // 2 x bf16 vector value +def v3bf16 : ValueType<48 , 75>; // 3 x bf16 vector value +def v4bf16 : ValueType<64 , 76>; // 4 x bf16 vector value +def v8bf16 : ValueType<128, 77>; // 8 x bf16 vector value +def v16bf16 : ValueType<256, 78>; // 16 x bf16 vector value +def v32bf16 : ValueType<512, 79>; // 32 x bf16 vector value +def v64bf16 : ValueType<1024, 80>; // 64 x bf16 vector value +def v128bf16 : ValueType<2048, 81>; // 128 x bf16 vector value +def v1f32 : ValueType<32 , 82>; // 1 x f32 vector value +def v2f32 : ValueType<64 , 83>; // 2 x f32 vector value +def v3f32 : ValueType<96 , 84>; // 3 x f32 vector value +def v4f32 : ValueType<128, 85>; // 4 x f32 vector value +def v5f32 : ValueType<160, 86>; // 5 x f32 vector value +def v8f32 : ValueType<256, 87>; // 8 x f32 vector value +def v16f32 : ValueType<512, 88>; // 16 x f32 vector value +def v32f32 : ValueType<1024, 89>; // 32 x f32 vector value +def v64f32 : ValueType<2048, 90>; // 64 x f32 vector value +def v128f32 : ValueType<4096, 91>; // 128 x f32 vector value +def v256f32 : ValueType<8182, 92>; // 256 x f32 vector value +def v512f32 : ValueType<16384, 93>; // 512 x f32 vector value +def v1024f32 : ValueType<32768, 94>; // 1024 x f32 vector value +def v2048f32 : ValueType<65536, 95>; // 2048 x f32 vector value +def v1f64 : ValueType<64, 96>; // 1 x f64 vector value +def v2f64 : ValueType<128, 97>; // 2 x f64 vector value +def v4f64 : ValueType<256, 98>; // 4 x f64 vector value +def v8f64 : ValueType<512, 99>; // 8 x f64 vector value +def v16f64 : ValueType<1024,100>; // 16 x f64 vector value +def v32f64 : ValueType<2048, 101>; // 32 x f64 vector value + +def nxv1i1 : ValueType<1, 102>; // n x 1 x i1 vector value +def nxv2i1 : ValueType<2, 103>; // n x 2 x i1 vector value +def nxv4i1 : ValueType<4, 104>; // n x 4 x i1 vector value +def nxv8i1 : ValueType<8, 105>; // n x 8 x i1 vector value +def nxv16i1 : ValueType<16, 106>; // n x 16 x i1 vector value +def nxv32i1 : ValueType<32, 107>; // n x 32 x i1 vector value + +def nxv1i8 : ValueType<8, 108>; // n x 1 x i8 vector value +def nxv2i8 : ValueType<16, 109>; // n x 2 x i8 vector value +def nxv4i8 : ValueType<32, 110>; // n x 4 x i8 vector value +def nxv8i8 : ValueType<64, 111>; // n x 8 x i8 vector value +def nxv16i8 : ValueType<128, 112>; // n x 16 x i8 vector value +def nxv32i8 : ValueType<256, 113>; // n x 32 x i8 vector value + +def nxv1i16 : ValueType<16, 114>; // n x 1 x i16 vector value +def nxv2i16 : ValueType<32, 115>; // n x 2 x i16 vector value +def nxv4i16 : ValueType<64, 116>; // n x 4 x i16 vector value +def nxv8i16 : ValueType<128, 117>; // n x 8 x i16 vector value +def nxv16i16: ValueType<256, 118>; // n x 16 x i16 vector value +def nxv32i16: ValueType<512, 119>; // n x 32 x i16 vector value + +def nxv1i32 : ValueType<32, 120>; // n x 1 x i32 vector value +def nxv2i32 : ValueType<64, 121>; // n x 2 x i32 vector value +def nxv4i32 : ValueType<128, 122>; // n x 4 x i32 vector value +def nxv8i32 : ValueType<256, 123>; // n x 8 x i32 vector value +def nxv16i32: ValueType<512, 124>; // n x 16 x i32 vector value +def nxv32i32: ValueType<1024,125>; // n x 32 x i32 vector value + +def nxv1i64 : ValueType<64, 126>; // n x 1 x i64 vector value +def nxv2i64 : ValueType<128, 127>; // n x 2 x i64 vector value +def nxv4i64 : ValueType<256, 128>; // n x 4 x i64 vector value +def nxv8i64 : ValueType<512, 129>; // n x 8 x i64 vector value +def nxv16i64: ValueType<1024,130>; // n x 16 x i64 vector value +def nxv32i64: ValueType<2048,131>; // n x 32 x i64 vector value + +def nxv2f16 : ValueType<32 , 132>; // n x 2 x f16 vector value +def nxv4f16 : ValueType<64 , 133>; // n x 4 x f16 vector value +def nxv8f16 : ValueType<128, 134>; // n x 8 x f16 vector value +def nxv2bf16 : ValueType<32 , 135>; // n x 2 x bf16 vector value +def nxv4bf16 : ValueType<64 , 136>; // n x 4 x bf16 vector value +def nxv8bf16 : ValueType<128, 137>; // n x 8 x bf16 vector value +def nxv1f32 : ValueType<32 , 138>; // n x 1 x f32 vector value +def nxv2f32 : ValueType<64 , 139>; // n x 2 x f32 vector value +def nxv4f32 : ValueType<128, 140>; // n x 4 x f32 vector value +def nxv8f32 : ValueType<256, 141>; // n x 8 x f32 vector value +def nxv16f32 : ValueType<512, 142>; // n x 16 x f32 vector value +def nxv1f64 : ValueType<64, 143>; // n x 1 x f64 vector value +def nxv2f64 : ValueType<128, 144>; // n x 2 x f64 vector value +def nxv4f64 : ValueType<256, 145>; // n x 4 x f64 vector value +def nxv8f64 : ValueType<512, 146>; // n x 8 x f64 vector value + +def x86mmx : ValueType<64 , 147>; // X86 MMX value +def FlagVT : ValueType<0 , 148>; // Pre-RA sched glue +def isVoid : ValueType<0 , 149>; // Produces no value +def untyped: ValueType<8 , 150>; // Produces an untyped value +def exnref : ValueType<0 , 151>; // WebAssembly's exnref type def token : ValueType<0 , 248>; // TokenTy def MetadataVT: ValueType<0, 249>; // Metadata diff --git a/llvm/include/llvm/IR/CallingConv.h b/llvm/include/llvm/IR/CallingConv.h index d0906de3ea4e11..bd2ce496ce00b4 100644 --- a/llvm/include/llvm/IR/CallingConv.h +++ b/llvm/include/llvm/IR/CallingConv.h @@ -241,6 +241,16 @@ namespace CallingConv { /// The remainder matches the regular calling convention. WASM_EmscriptenInvoke = 99, + /// Calling conventions used for special Z80 rtlib functions + /// which pass in registers and save all registers. + Z80_LibCall = 100, + Z80_LibCall_AB = 101, + Z80_LibCall_AC = 102, + Z80_LibCall_BC = 103, + Z80_LibCall_L = 104, + Z80_LibCall_F = 105, + Z80_TIFlags = 106, + /// The highest possible calling convention ID. Must be some 2^k - 1. MaxID = 1023 }; diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.def b/llvm/include/llvm/IR/RuntimeLibcalls.def index 903db6c7049877..afed93fe8fc632 100644 --- a/llvm/include/llvm/IR/RuntimeLibcalls.def +++ b/llvm/include/llvm/IR/RuntimeLibcalls.def @@ -30,20 +30,73 @@ #endif // Integer +HANDLE_LIBCALL(ZEXT_I16_I24, nullptr) +HANDLE_LIBCALL(SEXT_I16_I24, nullptr) +HANDLE_LIBCALL(SEXT_I24_I32, nullptr) +HANDLE_LIBCALL(NOT_I16, nullptr) +HANDLE_LIBCALL(NOT_I24, nullptr) +HANDLE_LIBCALL(NOT_I32, nullptr) +HANDLE_LIBCALL(NOT_I64, nullptr) +HANDLE_LIBCALL(AND_I8, nullptr) +HANDLE_LIBCALL(AND_I16, nullptr) +HANDLE_LIBCALL(AND_I24, nullptr) +HANDLE_LIBCALL(AND_I32, nullptr) +HANDLE_LIBCALL(AND_I64, nullptr) +HANDLE_LIBCALL(AND_I128, nullptr) +HANDLE_LIBCALL(OR_I8, nullptr) +HANDLE_LIBCALL(OR_I16, nullptr) +HANDLE_LIBCALL(OR_I24, nullptr) +HANDLE_LIBCALL(OR_I32, nullptr) +HANDLE_LIBCALL(OR_I64, nullptr) +HANDLE_LIBCALL(OR_I128, nullptr) +HANDLE_LIBCALL(XOR_I8, nullptr) +HANDLE_LIBCALL(XOR_I16, nullptr) +HANDLE_LIBCALL(XOR_I24, nullptr) +HANDLE_LIBCALL(XOR_I32, nullptr) +HANDLE_LIBCALL(XOR_I64, nullptr) +HANDLE_LIBCALL(XOR_I128, nullptr) +HANDLE_LIBCALL(SHL_I8, nullptr) HANDLE_LIBCALL(SHL_I16, "__ashlhi3") +HANDLE_LIBCALL(SHL_I24, nullptr) HANDLE_LIBCALL(SHL_I32, "__ashlsi3") HANDLE_LIBCALL(SHL_I64, "__ashldi3") HANDLE_LIBCALL(SHL_I128, "__ashlti3") +HANDLE_LIBCALL(SHL_I16_I8, nullptr) +HANDLE_LIBCALL(SHL_I24_I8, nullptr) +HANDLE_LIBCALL(SRL_I8, nullptr) HANDLE_LIBCALL(SRL_I16, "__lshrhi3") +HANDLE_LIBCALL(SRL_I16_I8, nullptr) +HANDLE_LIBCALL(SRL_I24, nullptr) +HANDLE_LIBCALL(SRL_I24_I8, nullptr) HANDLE_LIBCALL(SRL_I32, "__lshrsi3") HANDLE_LIBCALL(SRL_I64, "__lshrdi3") HANDLE_LIBCALL(SRL_I128, "__lshrti3") +HANDLE_LIBCALL(SRA_I8, nullptr) HANDLE_LIBCALL(SRA_I16, "__ashrhi3") +HANDLE_LIBCALL(SRA_I16_I8, nullptr) +HANDLE_LIBCALL(SRA_I24, nullptr) +HANDLE_LIBCALL(SRA_I24_I8, nullptr) HANDLE_LIBCALL(SRA_I32, "__ashrsi3") HANDLE_LIBCALL(SRA_I64, "__ashrdi3") HANDLE_LIBCALL(SRA_I128, "__ashrti3") +HANDLE_LIBCALL(CMP_I32, nullptr) +HANDLE_LIBCALL(CMP_I64, nullptr) +HANDLE_LIBCALL(CMP_I16_0, nullptr) +HANDLE_LIBCALL(CMP_I24_0, nullptr) +HANDLE_LIBCALL(CMP_I32_0, nullptr) +HANDLE_LIBCALL(CMP_I64_0, nullptr) +HANDLE_LIBCALL(SCMP, nullptr) +HANDLE_LIBCALL(ADD_I32, nullptr) +HANDLE_LIBCALL(ADD_I32_I8, nullptr) +HANDLE_LIBCALL(ADD_I64, nullptr) +HANDLE_LIBCALL(ADD_I128, nullptr) +HANDLE_LIBCALL(SUB_I32, nullptr) +HANDLE_LIBCALL(SUB_I64, nullptr) +HANDLE_LIBCALL(SUB_I128, nullptr) HANDLE_LIBCALL(MUL_I8, "__mulqi3") HANDLE_LIBCALL(MUL_I16, "__mulhi3") +HANDLE_LIBCALL(MUL_I24, nullptr) +HANDLE_LIBCALL(MUL_I24_I8, nullptr) HANDLE_LIBCALL(MUL_I32, "__mulsi3") HANDLE_LIBCALL(MUL_I64, "__muldi3") HANDLE_LIBCALL(MUL_I128, "__multi3") @@ -52,39 +105,59 @@ HANDLE_LIBCALL(MULO_I64, "__mulodi4") HANDLE_LIBCALL(MULO_I128, "__muloti4") HANDLE_LIBCALL(SDIV_I8, "__divqi3") HANDLE_LIBCALL(SDIV_I16, "__divhi3") +HANDLE_LIBCALL(SDIV_I24, nullptr) HANDLE_LIBCALL(SDIV_I32, "__divsi3") HANDLE_LIBCALL(SDIV_I64, "__divdi3") HANDLE_LIBCALL(SDIV_I128, "__divti3") HANDLE_LIBCALL(UDIV_I8, "__udivqi3") HANDLE_LIBCALL(UDIV_I16, "__udivhi3") +HANDLE_LIBCALL(UDIV_I24, nullptr) HANDLE_LIBCALL(UDIV_I32, "__udivsi3") HANDLE_LIBCALL(UDIV_I64, "__udivdi3") HANDLE_LIBCALL(UDIV_I128, "__udivti3") HANDLE_LIBCALL(SREM_I8, "__modqi3") HANDLE_LIBCALL(SREM_I16, "__modhi3") +HANDLE_LIBCALL(SREM_I24, nullptr) HANDLE_LIBCALL(SREM_I32, "__modsi3") HANDLE_LIBCALL(SREM_I64, "__moddi3") HANDLE_LIBCALL(SREM_I128, "__modti3") HANDLE_LIBCALL(UREM_I8, "__umodqi3") HANDLE_LIBCALL(UREM_I16, "__umodhi3") +HANDLE_LIBCALL(UREM_I24, nullptr) HANDLE_LIBCALL(UREM_I32, "__umodsi3") HANDLE_LIBCALL(UREM_I64, "__umoddi3") HANDLE_LIBCALL(UREM_I128, "__umodti3") HANDLE_LIBCALL(SDIVREM_I8, nullptr) HANDLE_LIBCALL(SDIVREM_I16, nullptr) +HANDLE_LIBCALL(SDIVREM_I24, nullptr) HANDLE_LIBCALL(SDIVREM_I32, nullptr) HANDLE_LIBCALL(SDIVREM_I64, nullptr) HANDLE_LIBCALL(SDIVREM_I128, nullptr) HANDLE_LIBCALL(UDIVREM_I8, nullptr) HANDLE_LIBCALL(UDIVREM_I16, nullptr) +HANDLE_LIBCALL(UDIVREM_I24, nullptr) HANDLE_LIBCALL(UDIVREM_I32, nullptr) HANDLE_LIBCALL(UDIVREM_I64, nullptr) HANDLE_LIBCALL(UDIVREM_I128, nullptr) +HANDLE_LIBCALL(NEG_I16, nullptr) +HANDLE_LIBCALL(NEG_I24, nullptr) HANDLE_LIBCALL(NEG_I32, "__negsi2") HANDLE_LIBCALL(NEG_I64, "__negdi2") HANDLE_LIBCALL(CTLZ_I32, "__clzsi2") HANDLE_LIBCALL(CTLZ_I64, "__clzdi2") HANDLE_LIBCALL(CTLZ_I128, "__clzti2") +HANDLE_LIBCALL(POPCNT_I8, nullptr) +HANDLE_LIBCALL(POPCNT_I16, nullptr) +HANDLE_LIBCALL(POPCNT_I24, nullptr) +HANDLE_LIBCALL(POPCNT_I32, "__popcountsi2") +HANDLE_LIBCALL(POPCNT_I64, "__popcountdi2") +HANDLE_LIBCALL(POPCNT_I128, nullptr) +HANDLE_LIBCALL(BITREV_I8, nullptr) +HANDLE_LIBCALL(BITREV_I16, nullptr) +HANDLE_LIBCALL(BITREV_I24, nullptr) +HANDLE_LIBCALL(BITREV_I32, nullptr) +HANDLE_LIBCALL(BITREV_I64, nullptr) +HANDLE_LIBCALL(BITREV_I128, nullptr) // Floating-point HANDLE_LIBCALL(ADD_F32, "__addsf3") @@ -279,6 +352,16 @@ HANDLE_LIBCALL(LLRINT_F64, "llrint") HANDLE_LIBCALL(LLRINT_F80, "llrintl") HANDLE_LIBCALL(LLRINT_F128, "llrintl") HANDLE_LIBCALL(LLRINT_PPCF128, "llrintl") +HANDLE_LIBCALL(ABS_F32, "fabsf") +HANDLE_LIBCALL(ABS_F64, "fabs") +HANDLE_LIBCALL(ABS_F80, "fabsl") +HANDLE_LIBCALL(ABS_F128, "fabsl") +HANDLE_LIBCALL(ABS_PPCF128, "fabsl") +HANDLE_LIBCALL(NEG_F32, nullptr) +HANDLE_LIBCALL(NEG_F64, nullptr) +HANDLE_LIBCALL(NEG_F128, nullptr) +HANDLE_LIBCALL(CMP_F32, nullptr) +HANDLE_LIBCALL(CMP_F64, nullptr) // Conversion HANDLE_LIBCALL(FPEXT_F32_PPCF128, "__gcc_stoq") diff --git a/llvm/include/llvm/MC/MCAsmInfo.h b/llvm/include/llvm/MC/MCAsmInfo.h index 974155ff3319db..3b78109e5922c5 100644 --- a/llvm/include/llvm/MC/MCAsmInfo.h +++ b/llvm/include/llvm/MC/MCAsmInfo.h @@ -147,9 +147,10 @@ class MCAsmInfo { const char *InlineAsmEnd; /// These are assembly directives that tells the assembler to interpret the - /// following instructions differently. Defaults to ".code16", ".code32", - /// ".code64". + /// following instructions differently. Defaults to ".code16", ".code24", + /// ".code32", ".code64". const char *Code16Directive; + const char *Code24Directive; const char *Code32Directive; const char *Code64Directive; @@ -190,6 +191,10 @@ class MCAsmInfo { /// used to emit these bytes. Defaults to true. bool ZeroDirectiveSupportsNonZeroValue = true; + /// This should be set to the separator used in block directives between + /// the number of bytes and the fill value. Defaults to ", ". + const char *BlockSeparator; + /// This directive allows emission of an ascii string with the standard C /// escape characters embedded into it. If a target doesn't support this, it /// can be set to null. Defaults to "\t.ascii\t" @@ -203,9 +208,10 @@ class MCAsmInfo { /// These directives are used to output some unit of integer data to the /// current section. If a data directive is set to null, smaller data /// directives will be used to emit the large sizes. Defaults to "\t.byte\t", - /// "\t.short\t", "\t.long\t", "\t.quad\t" + /// "\t.short\t", nullptr, "\t.long\t", "\t.quad\t" const char *Data8bitsDirective; const char *Data16bitsDirective; + const char *Data24bitsDirective; const char *Data32bitsDirective; const char *Data64bitsDirective; @@ -227,6 +233,8 @@ class MCAsmInfo { const char *TPRel32Directive = nullptr; const char *TPRel64Directive = nullptr; + bool AlwaysChangeSection = false; + /// This is true if this target uses "Sun Style" syntax for section switching /// ("#alloc,#write" etc) instead of the normal ELF syntax (,"a,w") in /// .section directives. Defaults to false. @@ -257,6 +265,15 @@ class MCAsmInfo { /// ".globl". const char *GlobalDirective; + /// This is the directive used to declare a local entity. Defaults to + /// nothing. + const char *LGloblDirective = nullptr; + + /// This is the directive used to assign a symbol. Defaults to ".set " and + /// ", ". + const char *SetDirective; + const char *SetSeparator; + /// True if the expression /// .long f - g /// uses a relocation but it can be suppressed by writing @@ -438,6 +455,7 @@ class MCAsmInfo { const char *getData8bitsDirective() const { return Data8bitsDirective; } const char *getData16bitsDirective() const { return Data16bitsDirective; } + const char *getData24bitsDirective() const { return Data24bitsDirective; } const char *getData32bitsDirective() const { return Data32bitsDirective; } const char *getData64bitsDirective() const { return Data64bitsDirective; } const char *getGPRel64Directive() const { return GPRel64Directive; } @@ -483,6 +501,7 @@ class MCAsmInfo { /// returns false => .section .text,#alloc,#execinstr /// returns true => .text virtual bool shouldOmitSectionDirective(StringRef SectionName) const; + bool shouldAlwaysChangeSection() const { return AlwaysChangeSection; } bool usesSunStyleELFSectionSwitchSyntax() const { return SunStyleELFSectionSwitchSyntax; @@ -541,6 +560,7 @@ class MCAsmInfo { const char *getInlineAsmStart() const { return InlineAsmStart; } const char *getInlineAsmEnd() const { return InlineAsmEnd; } const char *getCode16Directive() const { return Code16Directive; } + const char *getCode24Directive() const { return Code24Directive; } const char *getCode32Directive() const { return Code32Directive; } const char *getCode64Directive() const { return Code64Directive; } unsigned getAssemblerDialect() const { return AssemblerDialect; } @@ -560,11 +580,17 @@ class MCAsmInfo { bool doesZeroDirectiveSupportNonZeroValue() const { return ZeroDirectiveSupportsNonZeroValue; } + virtual const char *getBlockDirective(int64_t Size) const { return nullptr; } + const char *getBlockSeparator() const { return BlockSeparator; } const char *getAsciiDirective() const { return AsciiDirective; } const char *getAscizDirective() const { return AscizDirective; } bool getAlignmentIsInBytes() const { return AlignmentIsInBytes; } unsigned getTextAlignFillValue() const { return TextAlignFillValue; } + const char *getGlobalDirective() const { return GlobalDirective; } + const char *getLGloblDirective() const { return LGloblDirective; } + const char *getSetDirective() const { return SetDirective; } + const char *getSetSeparator() const { return SetSeparator; } bool doesSetDirectiveSuppressReloc() const { return SetDirectiveSuppressesReloc; diff --git a/llvm/include/llvm/MC/MCAsmInfoOMF.h b/llvm/include/llvm/MC/MCAsmInfoOMF.h new file mode 100644 index 00000000000000..2f1c8c2b2b9f2d --- /dev/null +++ b/llvm/include/llvm/MC/MCAsmInfoOMF.h @@ -0,0 +1,24 @@ +//===-- llvm/MC/MCAsmInfoOMF.h - OMF Asm info -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCASMINFOWASM_H +#define LLVM_MC_MCASMINFOWASM_H + +#include "llvm/MC/MCAsmInfo.h" + +namespace llvm { +class MCAsmInfoOMF : public MCAsmInfo { + virtual void anchor(); + +protected: + MCAsmInfoOMF(); +}; +} + +#endif // LLVM_MC_MCASMINFOWASM_H diff --git a/llvm/include/llvm/MC/MCContext.h b/llvm/include/llvm/MC/MCContext.h index 3f106c697b01c2..02a3b2b10aacda 100644 --- a/llvm/include/llvm/MC/MCContext.h +++ b/llvm/include/llvm/MC/MCContext.h @@ -51,6 +51,7 @@ namespace llvm { class MCSectionCOFF; class MCSectionELF; class MCSectionMachO; + class MCSectionOMF; class MCSectionWasm; class MCSectionXCOFF; class MCStreamer; @@ -94,6 +95,7 @@ namespace llvm { SpecificBumpPtrAllocator COFFAllocator; SpecificBumpPtrAllocator ELFAllocator; SpecificBumpPtrAllocator MachOAllocator; + SpecificBumpPtrAllocator OMFAllocator; SpecificBumpPtrAllocator WasmAllocator; SpecificBumpPtrAllocator XCOFFAllocator; @@ -277,11 +279,23 @@ namespace llvm { } }; + struct OMFSectionKey { + std::string SectionName; + + explicit OMFSectionKey(StringRef SectionName) + : SectionName(SectionName) {} + + bool operator<(const OMFSectionKey &Other) const { + return SectionName < Other.SectionName; + } + }; + StringMap MachOUniquingMap; std::map ELFUniquingMap; std::map COFFUniquingMap; std::map WasmUniquingMap; std::map XCOFFUniquingMap; + std::map OMFUniquingMap; StringMap RelSecNames; SpecificBumpPtrAllocator MCSubtargetAllocator; @@ -559,6 +573,8 @@ namespace llvm { SectionKind K, const char *BeginSymName = nullptr); + MCSectionOMF *getOMFSection(const Twine &Section, SectionKind Kind); + // Create and save a copy of STI and return a reference to the copy. MCSubtargetInfo &getSubtargetCopy(const MCSubtargetInfo &STI); diff --git a/llvm/include/llvm/MC/MCDirectives.h b/llvm/include/llvm/MC/MCDirectives.h index 51e57ad3702155..30c9fc5d7d13b4 100644 --- a/llvm/include/llvm/MC/MCDirectives.h +++ b/llvm/include/llvm/MC/MCDirectives.h @@ -51,6 +51,7 @@ enum MCAssemblerFlag { MCAF_SyntaxUnified, ///< .syntax (ARM/ELF) MCAF_SubsectionsViaSymbols, ///< .subsections_via_symbols (MachO) MCAF_Code16, ///< .code16 (X86) / .code 16 (ARM) + MCAF_Code24, ///< .assume adl=1 (eZ80) MCAF_Code32, ///< .code32 (X86) / .code 32 (ARM) MCAF_Code64 ///< .code64 (X86) }; diff --git a/llvm/include/llvm/MC/MCOMFStreamer.h b/llvm/include/llvm/MC/MCOMFStreamer.h new file mode 100644 index 00000000000000..1868c6b71ffd93 --- /dev/null +++ b/llvm/include/llvm/MC/MCOMFStreamer.h @@ -0,0 +1,46 @@ +//===- MCOMFStreamer.h - MCStreamer OMF Object File Interface ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCOMFSTREAMER_H +#define LLVM_MC_MCOMFSTREAMER_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCObjectWriter.h" + +namespace llvm { + +class MCAsmBackend; +class MCCodeEmitter; +class MCExpr; +class MCInst; + +class MCOMFStreamer : public MCObjectStreamer { +public: + MCOMFStreamer(MCContext &Context, std::unique_ptr TAB, + std::unique_ptr OW, + std::unique_ptr Emitter) + : MCObjectStreamer(Context, std::move(TAB), std::move(OW), + std::move(Emitter)) {} + + ~MCOMFStreamer() override = default; + + bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; + void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, + unsigned ByteAlignment) override; + void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr, + uint64_t Size = 0, unsigned ByteAlignment = 0, + SMLoc Loc = SMLoc()) override; + void emitInstToData(const MCInst &Inst, const MCSubtargetInfo &) override; +}; + +} // end namespace llvm + +#endif // LLVM_MC_MCOMFSTREAMER_H diff --git a/llvm/include/llvm/MC/MCObjectFileInfo.h b/llvm/include/llvm/MC/MCObjectFileInfo.h index ca04d8e8d3b689..beb3af9eab65d6 100644 --- a/llvm/include/llvm/MC/MCObjectFileInfo.h +++ b/llvm/include/llvm/MC/MCObjectFileInfo.h @@ -405,7 +405,7 @@ class MCObjectFileInfo { return EHFrameSection; } - enum Environment { IsMachO, IsELF, IsCOFF, IsWasm, IsXCOFF }; + enum Environment { IsMachO, IsELF, IsCOFF, IsWasm, IsXCOFF, IsOMF }; Environment getObjectFileType() const { return Env; } bool isPositionIndependent() const { return PositionIndependent; } @@ -422,6 +422,7 @@ class MCObjectFileInfo { void initCOFFMCObjectFileInfo(const Triple &T); void initWasmMCObjectFileInfo(const Triple &T); void initXCOFFMCObjectFileInfo(const Triple &T); + void initOMFMCObjectFileInfo(const Triple &T); MCSection *getDwarfComdatSection(const char *Name, uint64_t Hash) const; public: diff --git a/llvm/include/llvm/MC/MCSection.h b/llvm/include/llvm/MC/MCSection.h index a68e06e661beab..1b2a08c4172e20 100644 --- a/llvm/include/llvm/MC/MCSection.h +++ b/llvm/include/llvm/MC/MCSection.h @@ -40,7 +40,8 @@ class MCSection { public: static constexpr unsigned NonUniqueID = ~0U; - enum SectionVariant { SV_COFF = 0, SV_ELF, SV_MachO, SV_Wasm, SV_XCOFF }; + enum SectionVariant { SV_COFF = 0, SV_ELF, SV_MachO, SV_OMF, SV_Wasm, + SV_XCOFF }; /// Express the state of bundle locked groups while emitting code. enum BundleLockStateType { diff --git a/llvm/include/llvm/MC/MCSectionOMF.h b/llvm/include/llvm/MC/MCSectionOMF.h new file mode 100644 index 00000000000000..e65289e55329d9 --- /dev/null +++ b/llvm/include/llvm/MC/MCSectionOMF.h @@ -0,0 +1,40 @@ +//===- MCSectionOMF.h - OMF Machine Code Sections ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the MCSectionWasm class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MC_MCSECTIONOMF_H +#define LLVM_MC_MCSECTIONOMF_H + +#include "llvm/MC/MCSection.h" + +namespace llvm { + +class MCSectionOMF final : public MCSection { +private: + friend class MCContext; + MCSectionOMF(StringRef Name, SectionKind K, MCSymbol *Begin); + +public: + ~MCSectionOMF(); + + void PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, + raw_ostream &OS, + const MCExpr *Subsection) const override; + bool UseCodeAlign() const override { return getKind().isText(); } + bool isVirtualSection() const override { return getKind().isBSS(); } + + static bool classof(const MCSection *S) { return S->getVariant() == SV_OMF; } +}; + +} // end namespace llvm + +#endif diff --git a/llvm/include/llvm/MC/MCSymbol.h b/llvm/include/llvm/MC/MCSymbol.h index 84263bf94035eb..6184c22870f618 100644 --- a/llvm/include/llvm/MC/MCSymbol.h +++ b/llvm/include/llvm/MC/MCSymbol.h @@ -47,6 +47,7 @@ class MCSymbol { SymbolKindCOFF, SymbolKindELF, SymbolKindMachO, + SymbolKindOMF, SymbolKindWasm, SymbolKindXCOFF, }; @@ -281,6 +282,8 @@ class MCSymbol { bool isXCOFF() const { return Kind == SymbolKindXCOFF; } + bool isOMF() const { return Kind == SymbolKindOMF; } + /// @} /// \name Variable Symbols /// @{ diff --git a/llvm/include/llvm/MC/MCSymbolOMF.h b/llvm/include/llvm/MC/MCSymbolOMF.h new file mode 100644 index 00000000000000..8c891d6c0cbc18 --- /dev/null +++ b/llvm/include/llvm/MC/MCSymbolOMF.h @@ -0,0 +1,34 @@ +//===- MCSymbolOMF.h - -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_MC_MCSYMBOLOMF_H +#define LLVM_MC_MCSYMBOLOMF_H + +#include "llvm/MC/MCSymbol.h" + +namespace llvm { +class MCSymbolOMF : public MCSymbol { + /// An expression describing how to calculate the size of a symbol. If a + /// symbol has no size this field will be NULL. + const MCExpr *SymbolSize = nullptr; + +public: + MCSymbolOMF(const StringMapEntry *Name, bool isTemporary) + : MCSymbol(SymbolKindOMF, Name, isTemporary) {} + void setSize(const MCExpr *SS) { SymbolSize = SS; } + + const MCExpr *getSize() const { return SymbolSize; } + + static bool classof(const MCSymbol *S) { return S->isOMF(); } + +private: + void setIsBindingSet() const; +}; +} + +#endif diff --git a/llvm/include/llvm/Object/ELFObjectFile.h b/llvm/include/llvm/Object/ELFObjectFile.h index 62ecd8b5a7e5cf..ee1bc4863ce5de 100644 --- a/llvm/include/llvm/Object/ELFObjectFile.h +++ b/llvm/include/llvm/Object/ELFObjectFile.h @@ -1097,6 +1097,8 @@ StringRef ELFObjectFile::getFileFormatName() const { return (IsLittleEndian ? "elf32-littlearm" : "elf32-bigarm"); case ELF::EM_AVR: return "elf32-avr"; + case ELF::EM_Z80: + return "elf32-z80"; case ELF::EM_HEXAGON: return "elf32-hexagon"; case ELF::EM_LANAI: @@ -1164,6 +1166,8 @@ template Triple::ArchType ELFObjectFile::getArch() const { return Triple::arm; case ELF::EM_AVR: return Triple::avr; + case ELF::EM_Z80: + return Triple::z80; case ELF::EM_HEXAGON: return Triple::hexagon; case ELF::EM_LANAI: diff --git a/llvm/include/llvm/Support/LowLevelTypeImpl.h b/llvm/include/llvm/Support/LowLevelTypeImpl.h index c1d516f2fe587a..9d5df0870d5f83 100644 --- a/llvm/include/llvm/Support/LowLevelTypeImpl.h +++ b/llvm/include/llvm/Support/LowLevelTypeImpl.h @@ -115,7 +115,7 @@ class LLT { /// Returns the total size of the type in bytes, i.e. number of whole bytes /// needed to represent the size in bits. Must only be called on sized types. unsigned getSizeInBytes() const { - return (getSizeInBits() + 7) / 8; + return divideCeil(getSizeInBits(), 8); } LLT getScalarType() const { diff --git a/llvm/include/llvm/Support/MachineValueType.h b/llvm/include/llvm/Support/MachineValueType.h index e878915868758e..c10c3ac47cac0a 100644 --- a/llvm/include/llvm/Support/MachineValueType.h +++ b/llvm/include/llvm/Support/MachineValueType.h @@ -40,119 +40,120 @@ namespace llvm { i1 = 2, // This is a 1 bit integer value i8 = 3, // This is an 8 bit integer value i16 = 4, // This is a 16 bit integer value - i32 = 5, // This is a 32 bit integer value - i64 = 6, // This is a 64 bit integer value - i128 = 7, // This is a 128 bit integer value + i24 = 5, // This is a 24 bit integer value + i32 = 6, // This is a 32 bit integer value + i64 = 7, // This is a 64 bit integer value + i128 = 8, // This is a 128 bit integer value FIRST_INTEGER_VALUETYPE = i1, LAST_INTEGER_VALUETYPE = i128, - bf16 = 8, // This is a 16 bit brain floating point value - f16 = 9, // This is a 16 bit floating point value - f32 = 10, // This is a 32 bit floating point value - f64 = 11, // This is a 64 bit floating point value - f80 = 12, // This is a 80 bit floating point value - f128 = 13, // This is a 128 bit floating point value - ppcf128 = 14, // This is a PPC 128-bit floating point value + bf16 = 9, // This is a 16 bit brain floating point value + f16 = 10, // This is a 16 bit floating point value + f32 = 11, // This is a 32 bit floating point value + f64 = 12, // This is a 64 bit floating point value + f80 = 13, // This is a 80 bit floating point value + f128 = 14, // This is a 128 bit floating point value + ppcf128 = 15, // This is a PPC 128-bit floating point value FIRST_FP_VALUETYPE = bf16, LAST_FP_VALUETYPE = ppcf128, - v1i1 = 15, // 1 x i1 - v2i1 = 16, // 2 x i1 - v4i1 = 17, // 4 x i1 - v8i1 = 18, // 8 x i1 - v16i1 = 19, // 16 x i1 - v32i1 = 20, // 32 x i1 - v64i1 = 21, // 64 x i1 - v128i1 = 22, // 128 x i1 - v256i1 = 23, // 256 x i1 - v512i1 = 24, // 512 x i1 - v1024i1 = 25, // 1024 x i1 - - v1i8 = 26, // 1 x i8 - v2i8 = 27, // 2 x i8 - v4i8 = 28, // 4 x i8 - v8i8 = 29, // 8 x i8 - v16i8 = 30, // 16 x i8 - v32i8 = 31, // 32 x i8 - v64i8 = 32, // 64 x i8 - v128i8 = 33, //128 x i8 - v256i8 = 34, //256 x i8 - - v1i16 = 35, // 1 x i16 - v2i16 = 36, // 2 x i16 - v3i16 = 37, // 3 x i16 - v4i16 = 38, // 4 x i16 - v8i16 = 39, // 8 x i16 - v16i16 = 40, // 16 x i16 - v32i16 = 41, // 32 x i16 - v64i16 = 42, // 64 x i16 - v128i16 = 43, //128 x i16 - - v1i32 = 44, // 1 x i32 - v2i32 = 45, // 2 x i32 - v3i32 = 46, // 3 x i32 - v4i32 = 47, // 4 x i32 - v5i32 = 48, // 5 x i32 - v8i32 = 49, // 8 x i32 - v16i32 = 50, // 16 x i32 - v32i32 = 51, // 32 x i32 - v64i32 = 52, // 64 x i32 - v128i32 = 53, // 128 x i32 - v256i32 = 54, // 256 x i32 - v512i32 = 55, // 512 x i32 - v1024i32 = 56, // 1024 x i32 - v2048i32 = 57, // 2048 x i32 - - v1i64 = 58, // 1 x i64 - v2i64 = 59, // 2 x i64 - v4i64 = 60, // 4 x i64 - v8i64 = 61, // 8 x i64 - v16i64 = 62, // 16 x i64 - v32i64 = 63, // 32 x i64 - - v1i128 = 64, // 1 x i128 + v1i1 = 16, // 1 x i1 + v2i1 = 17, // 2 x i1 + v4i1 = 18, // 4 x i1 + v8i1 = 19, // 8 x i1 + v16i1 = 20, // 16 x i1 + v32i1 = 21, // 32 x i1 + v64i1 = 22, // 64 x i1 + v128i1 = 23, // 128 x i1 + v256i1 = 24, // 256 x i1 + v512i1 = 25, // 512 x i1 + v1024i1 = 26, // 1024 x i1 + + v1i8 = 27, // 1 x i8 + v2i8 = 28, // 2 x i8 + v4i8 = 29, // 4 x i8 + v8i8 = 30, // 8 x i8 + v16i8 = 31, // 16 x i8 + v32i8 = 32, // 32 x i8 + v64i8 = 33, // 64 x i8 + v128i8 = 34, //128 x i8 + v256i8 = 35, //256 x i8 + + v1i16 = 36, // 1 x i16 + v2i16 = 37, // 2 x i16 + v3i16 = 38, // 3 x i16 + v4i16 = 39, // 4 x i16 + v8i16 = 40, // 8 x i16 + v16i16 = 41, // 16 x i16 + v32i16 = 42, // 32 x i16 + v64i16 = 43, // 64 x i16 + v128i16 = 44, //128 x i16 + + v1i32 = 45, // 1 x i32 + v2i32 = 46, // 2 x i32 + v3i32 = 47, // 3 x i32 + v4i32 = 48, // 4 x i32 + v5i32 = 49, // 5 x i32 + v8i32 = 50, // 8 x i32 + v16i32 = 51, // 16 x i32 + v32i32 = 52, // 32 x i32 + v64i32 = 53, // 64 x i32 + v128i32 = 54, // 128 x i32 + v256i32 = 55, // 256 x i32 + v512i32 = 56, // 512 x i32 + v1024i32 = 57, // 1024 x i32 + v2048i32 = 58, // 2048 x i32 + + v1i64 = 59, // 1 x i64 + v2i64 = 60, // 2 x i64 + v4i64 = 61, // 4 x i64 + v8i64 = 62, // 8 x i64 + v16i64 = 63, // 16 x i64 + v32i64 = 64, // 32 x i64 + + v1i128 = 65, // 1 x i128 FIRST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE = v1i1, LAST_INTEGER_FIXEDLEN_VECTOR_VALUETYPE = v1i128, - v2f16 = 65, // 2 x f16 - v3f16 = 66, // 3 x f16 - v4f16 = 67, // 4 x f16 - v8f16 = 68, // 8 x f16 - v16f16 = 69, // 16 x f16 - v32f16 = 70, // 32 x f16 - v64f16 = 71, // 64 x f16 - v128f16 = 72, // 128 x f16 - v2bf16 = 73, // 2 x bf16 - v3bf16 = 74, // 3 x bf16 - v4bf16 = 75, // 4 x bf16 - v8bf16 = 76, // 8 x bf16 - v16bf16 = 77, // 16 x bf16 - v32bf16 = 78, // 32 x bf16 - v64bf16 = 79, // 64 x bf16 - v128bf16 = 80, // 128 x bf16 - v1f32 = 81, // 1 x f32 - v2f32 = 82, // 2 x f32 - v3f32 = 83, // 3 x f32 - v4f32 = 84, // 4 x f32 - v5f32 = 85, // 5 x f32 - v8f32 = 86, // 8 x f32 - v16f32 = 87, // 16 x f32 - v32f32 = 88, // 32 x f32 - v64f32 = 89, // 64 x f32 - v128f32 = 90, // 128 x f32 - v256f32 = 91, // 256 x f32 - v512f32 = 92, // 512 x f32 - v1024f32 = 93, // 1024 x f32 - v2048f32 = 94, // 2048 x f32 - v1f64 = 95, // 1 x f64 - v2f64 = 96, // 2 x f64 - v4f64 = 97, // 4 x f64 - v8f64 = 98, // 8 x f64 - v16f64 = 99, // 16 x f64 - v32f64 = 100, // 32 x f64 + v2f16 = 66, // 2 x f16 + v3f16 = 67, // 3 x f16 + v4f16 = 68, // 4 x f16 + v8f16 = 69, // 8 x f16 + v16f16 = 70, // 16 x f16 + v32f16 = 71, // 32 x f16 + v64f16 = 72, // 64 x f16 + v128f16 = 73, // 128 x f16 + v2bf16 = 74, // 2 x bf16 + v3bf16 = 75, // 3 x bf16 + v4bf16 = 76, // 4 x bf16 + v8bf16 = 77, // 8 x bf16 + v16bf16 = 78, // 16 x bf16 + v32bf16 = 79, // 32 x bf16 + v64bf16 = 80, // 64 x bf16 + v128bf16 = 81, // 128 x bf16 + v1f32 = 82, // 1 x f32 + v2f32 = 83, // 2 x f32 + v3f32 = 84, // 3 x f32 + v4f32 = 85, // 4 x f32 + v5f32 = 86, // 5 x f32 + v8f32 = 87, // 8 x f32 + v16f32 = 88, // 16 x f32 + v32f32 = 89, // 32 x f32 + v64f32 = 90, // 64 x f32 + v128f32 = 91, // 128 x f32 + v256f32 = 92, // 256 x f32 + v512f32 = 93, // 512 x f32 + v1024f32 = 94, // 1024 x f32 + v2048f32 = 95, // 2048 x f32 + v1f64 = 96, // 1 x f64 + v2f64 = 97, // 2 x f64 + v4f64 = 98, // 4 x f64 + v8f64 = 99, // 8 x f64 + v16f64 = 100, // 16 x f64 + v32f64 = 101, // 32 x f64 FIRST_FP_FIXEDLEN_VECTOR_VALUETYPE = v2f16, LAST_FP_FIXEDLEN_VECTOR_VALUETYPE = v32f64, @@ -160,59 +161,59 @@ namespace llvm { FIRST_FIXEDLEN_VECTOR_VALUETYPE = v1i1, LAST_FIXEDLEN_VECTOR_VALUETYPE = v32f64, - nxv1i1 = 101, // n x 1 x i1 - nxv2i1 = 102, // n x 2 x i1 - nxv4i1 = 103, // n x 4 x i1 - nxv8i1 = 104, // n x 8 x i1 - nxv16i1 = 105, // n x 16 x i1 - nxv32i1 = 106, // n x 32 x i1 - - nxv1i8 = 107, // n x 1 x i8 - nxv2i8 = 108, // n x 2 x i8 - nxv4i8 = 109, // n x 4 x i8 - nxv8i8 = 110, // n x 8 x i8 - nxv16i8 = 111, // n x 16 x i8 - nxv32i8 = 112, // n x 32 x i8 - - nxv1i16 = 113, // n x 1 x i16 - nxv2i16 = 114, // n x 2 x i16 - nxv4i16 = 115, // n x 4 x i16 - nxv8i16 = 116, // n x 8 x i16 - nxv16i16 = 117, // n x 16 x i16 - nxv32i16 = 118, // n x 32 x i16 - - nxv1i32 = 119, // n x 1 x i32 - nxv2i32 = 120, // n x 2 x i32 - nxv4i32 = 121, // n x 4 x i32 - nxv8i32 = 122, // n x 8 x i32 - nxv16i32 = 123, // n x 16 x i32 - nxv32i32 = 124, // n x 32 x i32 - - nxv1i64 = 125, // n x 1 x i64 - nxv2i64 = 126, // n x 2 x i64 - nxv4i64 = 127, // n x 4 x i64 - nxv8i64 = 128, // n x 8 x i64 - nxv16i64 = 129, // n x 16 x i64 - nxv32i64 = 130, // n x 32 x i64 + nxv1i1 = 102, // n x 1 x i1 + nxv2i1 = 103, // n x 2 x i1 + nxv4i1 = 104, // n x 4 x i1 + nxv8i1 = 105, // n x 8 x i1 + nxv16i1 = 106, // n x 16 x i1 + nxv32i1 = 107, // n x 32 x i1 + + nxv1i8 = 108, // n x 1 x i8 + nxv2i8 = 109, // n x 2 x i8 + nxv4i8 = 110, // n x 4 x i8 + nxv8i8 = 111, // n x 8 x i8 + nxv16i8 = 112, // n x 16 x i8 + nxv32i8 = 113, // n x 32 x i8 + + nxv1i16 = 114, // n x 1 x i16 + nxv2i16 = 115, // n x 2 x i16 + nxv4i16 = 116, // n x 4 x i16 + nxv8i16 = 117, // n x 8 x i16 + nxv16i16 = 118, // n x 16 x i16 + nxv32i16 = 119, // n x 32 x i16 + + nxv1i32 = 120, // n x 1 x i32 + nxv2i32 = 121, // n x 2 x i32 + nxv4i32 = 122, // n x 4 x i32 + nxv8i32 = 123, // n x 8 x i32 + nxv16i32 = 124, // n x 16 x i32 + nxv32i32 = 125, // n x 32 x i32 + + nxv1i64 = 126, // n x 1 x i64 + nxv2i64 = 127, // n x 2 x i64 + nxv4i64 = 128, // n x 4 x i64 + nxv8i64 = 129, // n x 8 x i64 + nxv16i64 = 130, // n x 16 x i64 + nxv32i64 = 131, // n x 32 x i64 FIRST_INTEGER_SCALABLE_VECTOR_VALUETYPE = nxv1i1, LAST_INTEGER_SCALABLE_VECTOR_VALUETYPE = nxv32i64, - nxv2f16 = 131, // n x 2 x f16 - nxv4f16 = 132, // n x 4 x f16 - nxv8f16 = 133, // n x 8 x f16 - nxv2bf16 = 134, // n x 2 x bf16 - nxv4bf16 = 135, // n x 4 x bf16 - nxv8bf16 = 136, // n x 8 x bf16 - nxv1f32 = 137, // n x 1 x f32 - nxv2f32 = 138, // n x 2 x f32 - nxv4f32 = 139, // n x 4 x f32 - nxv8f32 = 140, // n x 8 x f32 - nxv16f32 = 141, // n x 16 x f32 - nxv1f64 = 142, // n x 1 x f64 - nxv2f64 = 143, // n x 2 x f64 - nxv4f64 = 144, // n x 4 x f64 - nxv8f64 = 145, // n x 8 x f64 + nxv2f16 = 132, // n x 2 x f16 + nxv4f16 = 133, // n x 4 x f16 + nxv8f16 = 134, // n x 8 x f16 + nxv2bf16 = 135, // n x 2 x bf16 + nxv4bf16 = 136, // n x 4 x bf16 + nxv8bf16 = 137, // n x 8 x bf16 + nxv1f32 = 138, // n x 1 x f32 + nxv2f32 = 139, // n x 2 x f32 + nxv4f32 = 140, // n x 4 x f32 + nxv8f32 = 141, // n x 8 x f32 + nxv16f32 = 142, // n x 16 x f32 + nxv1f64 = 143, // n x 1 x f64 + nxv2f64 = 144, // n x 2 x f64 + nxv4f64 = 145, // n x 4 x f64 + nxv8f64 = 146, // n x 8 x f64 FIRST_FP_SCALABLE_VECTOR_VALUETYPE = nxv2f16, LAST_FP_SCALABLE_VECTOR_VALUETYPE = nxv8f64, @@ -223,20 +224,20 @@ namespace llvm { FIRST_VECTOR_VALUETYPE = v1i1, LAST_VECTOR_VALUETYPE = nxv8f64, - x86mmx = 146, // This is an X86 MMX value + x86mmx = 147, // This is an X86 MMX value - Glue = 147, // This glues nodes together during pre-RA sched + Glue = 148, // This glues nodes together during pre-RA sched - isVoid = 148, // This has no value + isVoid = 149, // This has no value - Untyped = 149, // This value takes a register, but has - // unspecified type. The register class - // will be determined by the opcode. + Untyped = 150, // This value takes a register, but has + // unspecified type. The register class + // will be determined by the opcode. - exnref = 150, // WebAssembly's exnref type + exnref = 151, // WebAssembly's exnref type - FIRST_VALUETYPE = 1, // This is always the beginning of the list. - LAST_VALUETYPE = 151, // This always remains at the end of the list. + FIRST_VALUETYPE = 1, // This is always the beginning of the list. + LAST_VALUETYPE = 152, // This always remains at the end of the list. // This is the current maximum for LAST_VALUETYPE. // MVT::MAX_ALLOWED_VALUETYPE is used for asserts and to size bit vectors @@ -423,6 +424,12 @@ namespace llvm { return getVectorVT(EltVT, EltCnt / 2); } + /// Return true if the width is a power of 2. + bool isPow2Size() { + unsigned BitWidth = getSizeInBits(); + return !(BitWidth & (BitWidth - 1)); + } + /// Returns true if the given vector is a power of 2. bool isPow2VectorType() const { unsigned NElts = getVectorNumElements(); @@ -775,6 +782,7 @@ namespace llvm { case nxv16i1: case nxv2i8: case nxv1i16: return TypeSize::Scalable(16); + case i24: return TypeSize::Fixed(24); case f32 : case i32 : case v32i1: @@ -934,6 +942,18 @@ namespace llvm { return getSizeInBits().isByteSized(); } + /// getNumParts - Return the number of parts with PartBits bits that make up + /// this VT. + unsigned getNumParts(unsigned PartBits) const { + return divideCeil(getSizeInBits(), PartBits); + } + + /// getNumParts - Return the number of parts of type PartVT that make up + /// this VT. + unsigned getNumParts(MVT PartVT) const { + return getNumParts(PartVT.getSizeInBits()); + } + /// Return true if this has more bits than VT. bool bitsGT(MVT VT) const { return getSizeInBits() > VT.getSizeInBits(); @@ -981,6 +1001,8 @@ namespace llvm { return MVT::i8; case 16: return MVT::i16; + case 24: + return MVT::i24; case 32: return MVT::i32; case 64: diff --git a/llvm/include/llvm/Support/TargetRegistry.h b/llvm/include/llvm/Support/TargetRegistry.h index d91eabae823579..86a1f3476806a9 100644 --- a/llvm/include/llvm/Support/TargetRegistry.h +++ b/llvm/include/llvm/Support/TargetRegistry.h @@ -95,6 +95,11 @@ MCStreamer *createMachOStreamer(MCContext &Ctx, std::unique_ptr &&CE, bool RelaxAll, bool DWARFMustBeAtTheEnd, bool LabelSections = false); +MCStreamer *createOMFStreamer(MCContext &Ctx, + std::unique_ptr &&TAB, + std::unique_ptr &&OW, + std::unique_ptr &&CE, + bool RelaxAll); MCStreamer *createWasmStreamer(MCContext &Ctx, std::unique_ptr &&TAB, std::unique_ptr &&OW, @@ -514,6 +519,10 @@ class Target { S = createXCOFFStreamer(Ctx, std::move(TAB), std::move(OW), std::move(Emitter), RelaxAll); break; + case Triple::OMF: + S = createOMFStreamer(Ctx, std::move(TAB), std::move(OW), + std::move(Emitter), RelaxAll); + break; } if (ObjectTargetStreamerCtorFn) ObjectTargetStreamerCtorFn(*S, STI); diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td index eeb2761faeb9f1..86e46d2e46fea3 100644 --- a/llvm/include/llvm/Target/GlobalISel/Combine.td +++ b/llvm/include/llvm/Target/GlobalISel/Combine.td @@ -264,6 +264,81 @@ def identity_combines : GICombineGroup<[select_same_val, right_identity_zero, binop_right_to_zero]>; def trivial_combines : GICombineGroup<[copy_prop, mul_to_shl]>; + +def ptr_add_global_matchdata : GIDefMatchData<"PtrAddGlobal">; +def ptr_add_global_immed : GICombineRule< + (defs root:$mi, ptr_add_global_matchdata:$matchinfo), + (match (wip_match_opcode G_PTR_ADD):$mi, + [{ return Helper.matchPtrAddGlobalImmed(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyPtrAddGlobalImmed(*${mi}, ${matchinfo}); }])>; + +def ptr_add_const_matchdata : GIDefMatchData<"PtrAddConst">; +def ptr_add_const_immed : GICombineRule< + (defs root:$mi, ptr_add_const_matchdata:$matchinfo), + (match (wip_match_opcode G_PTR_ADD):$mi, + [{ return Helper.matchPtrAddConstImmed(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyPtrAddConstImmed(*${mi}, ${matchinfo}); }])>; + +def shl_to_add_matchdata : GIDefMatchData<"unsigned">; +def shl_to_add : GICombineRule< + (defs root:$mi, shl_to_add_matchdata:$matchinfo), + (match (wip_match_opcode G_SHL):$mi, + [{ return Helper.matchCombineShlToAdd(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyCombineShlToAdd(*${mi}, ${matchinfo}); }])>; + +def sext_to_zext : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_SEXT):$mi, + [{ return Helper.matchCombineSExtToZExt(*${mi}); }]), + (apply [{ Helper.applyCombineSExtToZExt(*${mi}); }])>; + +def or_to_add : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_OR):$mi, + [{ return Helper.matchCombineOrToAdd(*${mi}); }]), + (apply [{ Helper.applyCombineOrToAdd(*${mi}); }])>; + +def funnel_shift_matchdata : GIDefMatchData<"FunnelShift">; +def funnel_shift : GICombineRule< + (defs root:$mi, funnel_shift_matchdata:$matchinfo), + (match (wip_match_opcode G_ADD):$mi, + [{ return Helper.matchCombineFunnelShift(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyCombineFunnelShift(*${mi}, ${matchinfo}); }])>; + +def combine_identity : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_ADD, G_SUB, G_MUL, G_SDIV, G_UDIV, + G_AND, G_OR, G_XOR, G_SHL, G_LSHR, G_ASHR, + G_PTR_ADD, G_PTRMASK):$mi, + [{ return Helper.matchCombineIdentity(*${mi}); }]), + (apply [{ Helper.applyCombineIdentity(*${mi}); }])>; + +def split_conditions : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_BRCOND):$mi, + [{ return Helper.matchSplitConditions(*${mi}); }]), + (apply [{ Helper.applySplitConditions(*${mi}); }])>; + +def flip_condition_matchdata : GIDefMatchData<"MachineInstr *">; +def flip_condition : GICombineRule< + (defs root:$mi, flip_condition_matchdata:$matchinfo), + (match (wip_match_opcode G_XOR):$mi, + [{ return Helper.matchFlipCondition(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applyFlipCondition(*${mi}, *${matchinfo}); }])>; + +def lower_is_power_of_two : GICombineRule< + (defs root:$mi), + (match (wip_match_opcode G_ICMP):$mi, + [{ return Helper.matchLowerIsPowerOfTwo(*${mi}); }]), + (apply [{ Helper.applyLowerIsPowerOfTwo(*${mi}); }])>; + +def sink_const_matchdata : GIDefMatchData<"MachineInstr *">; +def sink_const : GICombineRule< + (defs root:$mi, sink_const_matchdata:$matchinfo), + (match (wip_match_opcode G_CONSTANT):$mi, + [{ return Helper.matchSinkConstant(*${mi}, ${matchinfo}); }]), + (apply [{ Helper.applySinkConstant(*${mi}, *${matchinfo}); }])>; + def all_combines : GICombineGroup<[trivial_combines, ptr_add_immed_chain, combines_for_extload, combine_indexed_load_store, undef_combines, identity_combines, simplify_add_to_sub]>; diff --git a/llvm/include/llvm/Target/TargetCallingConv.td b/llvm/include/llvm/Target/TargetCallingConv.td index 057f33083e0804..d256ff2797270d 100644 --- a/llvm/include/llvm/Target/TargetCallingConv.td +++ b/llvm/include/llvm/Target/TargetCallingConv.td @@ -82,6 +82,10 @@ class CCIfNest : CCIf<"ArgFlags.isNest()", A> {} /// the specified action. class CCIfSplit : CCIf<"ArgFlags.isSplit()", A> {} +/// CCIfSplitEnd - If this argument is marked with the 'splitend' attribute, apply +/// the specified action. +class CCIfSplitEnd : CCIf<"ArgFlags.isSplitEnd()", A> {} + /// CCIfSRet - If this argument is marked with the 'sret' attribute, apply /// the specified action. class CCIfSRet : CCIf<"ArgFlags.isSRet()", A> {} diff --git a/llvm/lib/BinaryFormat/Magic.cpp b/llvm/lib/BinaryFormat/Magic.cpp index 61b1504e59b0af..b5b0eab02ad7f2 100644 --- a/llvm/lib/BinaryFormat/Magic.cpp +++ b/llvm/lib/BinaryFormat/Magic.cpp @@ -216,6 +216,13 @@ file_magic llvm::identify_magic(StringRef Magic) { return file_magic::tapi_file; break; + case 0xE0: // IEEE 695 OMF format + if (startswith(Magic, "\xE0\11assembler")) + return file_magic::omf_object; + if (startswith(Magic, "\xE0\7LIBRARY")) + return file_magic::omf_archive; + break; + default: break; } diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index a23f39be6b2a35..81a4a13e078bc5 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -4730,7 +4730,8 @@ static const char *getSectionNameForBitcode(const Triple &T) { return ".llvmbc"; case Triple::XCOFF: llvm_unreachable("XCOFF is not yet implemented"); - break; + case Triple::OMF: + llvm_unreachable("OMF is not yet implemented"); } llvm_unreachable("Unimplemented ObjectFormatType"); } @@ -4746,7 +4747,8 @@ static const char *getSectionNameForCommandline(const Triple &T) { return ".llvmcmd"; case Triple::XCOFF: llvm_unreachable("XCOFF is not yet implemented"); - break; + case Triple::OMF: + llvm_unreachable("OMF is not yet implemented"); } llvm_unreachable("Unimplemented ObjectFormatType"); } diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp index 1540a19687c5fb..f15706d0a9b9b9 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -422,6 +422,8 @@ void AsmPrinter::emitLinkage(const GlobalValue *GV, MCSymbol *GVSym) const { return; case GlobalValue::PrivateLinkage: case GlobalValue::InternalLinkage: + if (MAI->getLGloblDirective()) + OutStreamer->emitSymbolAttribute(GVSym, MCSA_LGlobal); return; case GlobalValue::ExternalWeakLinkage: case GlobalValue::AvailableExternallyLinkage: @@ -1960,6 +1962,8 @@ void AsmPrinter::emitJumpTableInfo() { OutStreamer->emitLabel(GetJTISymbol(JTI, true)); MCSymbol* JTISymbol = GetJTISymbol(JTI); + if (MAI->getLGloblDirective()) + OutStreamer->emitSymbolAttribute(JTISymbol, MCSA_LGlobal); OutStreamer->emitLabel(JTISymbol); for (unsigned ii = 0, ee = JTBBs.size(); ii != ee; ++ii) diff --git a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp index 5bcc242d3528eb..ea9e0697352b5f 100644 --- a/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CallLowering.cpp @@ -44,7 +44,7 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, const CallBase &CB, unsigned i = 0; unsigned NumFixedArgs = CB.getFunctionType()->getNumParams(); for (auto &Arg : CB.args()) { - ArgInfo OrigArg{ArgRegs[i], Arg->getType(), ISD::ArgFlagsTy{}, + ArgInfo OrigArg{ArgRegs[i], Arg, ISD::ArgFlagsTy{}, i < NumFixedArgs}; setArgFlags(OrigArg, i + AttributeList::FirstArgIndex, DL, CB); Info.OrigArgs.push_back(OrigArg); @@ -59,11 +59,12 @@ bool CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, const CallBase &CB, else Info.Callee = MachineOperand::CreateReg(GetCalleeReg(), false); - Info.OrigRet = ArgInfo{ResRegs, CB.getType(), ISD::ArgFlagsTy{}}; + Info.OrigRet = ArgInfo{ResRegs, &CB}; if (!Info.OrigRet.Ty->isVoidTy()) setArgFlags(Info.OrigRet, AttributeList::ReturnIndex, DL, CB); MachineFunction &MF = MIRBuilder.getMF(); + Info.CallAttributes = CB.getAttributes(); Info.KnownCallees = CB.getMetadata(LLVMContext::MD_callees); Info.CallConv = CB.getCallingConv(); Info.SwiftErrorVReg = SwiftErrorVReg; @@ -173,13 +174,14 @@ void CallLowering::unpackRegs(ArrayRef DstRegs, Register SrcReg, MIRBuilder.buildExtract(DstRegs[i], SrcReg, Offsets[i]); } -bool CallLowering::handleAssignments(MachineIRBuilder &MIRBuilder, +bool CallLowering::handleAssignments(CallingConv::ID CC, bool isVarArg, + MachineIRBuilder &MIRBuilder, SmallVectorImpl &Args, ValueHandler &Handler) const { MachineFunction &MF = MIRBuilder.getMF(); const Function &F = MF.getFunction(); SmallVector ArgLocs; - CCState CCInfo(F.getCallingConv(), F.isVarArg(), MF, ArgLocs, F.getContext()); + CCState CCInfo(CC, isVarArg, MF, ArgLocs, F.getContext()); return handleAssignments(CCInfo, ArgLocs, MIRBuilder, Args, Handler); } @@ -194,22 +196,26 @@ bool CallLowering::handleAssignments(CCState &CCInfo, unsigned NumArgs = Args.size(); for (unsigned i = 0; i != NumArgs; ++i) { - MVT CurVT = MVT::getVT(Args[i].Ty); - if (Handler.assignArg(i, CurVT, CurVT, CCValAssign::Full, Args[i], + MVT CurMVT = MVT::getVT(Args[i].Ty); + if (Handler.assignArg(i, CurMVT, CurMVT, CCValAssign::Full, Args[i], Args[i].Flags[0], CCInfo)) { - if (!CurVT.isValid()) - return false; + EVT CurVT = EVT::getEVT(Args[i].Ty); MVT NewVT = TLI->getRegisterTypeForCallingConv( - F.getContext(), F.getCallingConv(), EVT(CurVT)); + F.getContext(), CCInfo.getCallingConv(), CurVT); // If we need to split the type over multiple regs, check it's a scenario // we currently support. unsigned NumParts = TLI->getNumRegistersForCallingConv( - F.getContext(), F.getCallingConv(), CurVT); - if (NumParts > 1) { - // For now only handle exact splits. - if (NewVT.getSizeInBits() * NumParts != CurVT.getSizeInBits()) + F.getContext(), CCInfo.getCallingConv(), CurVT); + assert(NumParts && "NumParts should not be 0"); + bool Exact = NewVT.getSizeInBits() * NumParts == CurVT.getSizeInBits(); + + if (NumParts == 1) { + // Try to use the register type if we couldn't assign the VT. + if (Handler.assignArg(i, NewVT, NewVT, CCValAssign::Full, Args[i], + Args[i].Flags[0], CCInfo)) return false; + continue; } // For incoming arguments (physregs to vregs), we could have values in @@ -220,82 +226,68 @@ bool CallLowering::handleAssignments(CCState &CCInfo, // If we have outgoing args, then we have the opposite case. We have a // vreg with an LLT which we want to assign to a physical location, and // we might have to record that the value has to be split later. - if (Handler.isIncomingArgumentHandler()) { - if (NumParts == 1) { - // Try to use the register type if we couldn't assign the VT. - if (Handler.assignArg(i, NewVT, NewVT, CCValAssign::Full, Args[i], - Args[i].Flags[0], CCInfo)) - return false; + // If we're handling an incoming arg which is split over multiple regs. + // E.g. passing an s128 on AArch64. + // Otherwise, this type is passed via multiple registers in the calling + // convention. We need to extract the individual parts. + ISD::ArgFlagsTy OrigFlags = Args[i].Flags[0]; + Args[i].OrigRegs.push_back(Args[i].Regs[0]); + // We're going to replace the regs and flags with the split ones. + Args[i].Regs.clear(); + Args[i].Flags.clear(); + LLT NewLLT = getLLTForMVT(NewVT); + // For each split register, create and assign a vreg that will store + // the incoming component of the larger value. These will later be + // merged to form the final vreg. + for (unsigned Part = 0; Part < NumParts; ++Part) { + MVT PartVT = NewVT; + LLT PartLLT = NewLLT; + ISD::ArgFlagsTy Flags = OrigFlags; + if (Part == 0) { + Flags.setSplit(); } else { - // We're handling an incoming arg which is split over multiple regs. - // E.g. passing an s128 on AArch64. - ISD::ArgFlagsTy OrigFlags = Args[i].Flags[0]; - Args[i].OrigRegs.push_back(Args[i].Regs[0]); - Args[i].Regs.clear(); - Args[i].Flags.clear(); - LLT NewLLT = getLLTForMVT(NewVT); - // For each split register, create and assign a vreg that will store - // the incoming component of the larger value. These will later be - // merged to form the final vreg. - for (unsigned Part = 0; Part < NumParts; ++Part) { - Register Reg = - MIRBuilder.getMRI()->createGenericVirtualRegister(NewLLT); - ISD::ArgFlagsTy Flags = OrigFlags; - if (Part == 0) { - Flags.setSplit(); - } else { - Flags.setOrigAlign(Align(1)); - if (Part == NumParts - 1) - Flags.setSplitEnd(); + Flags.setOrigAlign(Align(1)); + if (Part == NumParts - 1) { + Flags.setSplitEnd(); + if (!Exact) { + unsigned LeftoverSize = + CurVT.getSizeInBits() - NewVT.getSizeInBits() * Part; + EVT LeftoverVT = EVT::getIntegerVT(F.getContext(), LeftoverSize); + PartVT = TLI->getRegisterTypeForCallingConv( + F.getContext(), CCInfo.getCallingConv(), LeftoverVT); + PartLLT = getLLTForMVT(PartVT); } - Args[i].Regs.push_back(Reg); - Args[i].Flags.push_back(Flags); - if (Handler.assignArg(i + Part, NewVT, NewVT, CCValAssign::Full, - Args[i], Args[i].Flags[Part], CCInfo)) { - // Still couldn't assign this smaller part type for some reason. - return false; - } - } - } - } else { - // Handling an outgoing arg that might need to be split. - if (NumParts < 2) - return false; // Don't know how to deal with this type combination. - - // This type is passed via multiple registers in the calling convention. - // We need to extract the individual parts. - Register LargeReg = Args[i].Regs[0]; - LLT SmallTy = LLT::scalar(NewVT.getSizeInBits()); - auto Unmerge = MIRBuilder.buildUnmerge(SmallTy, LargeReg); - assert(Unmerge->getNumOperands() == NumParts + 1); - ISD::ArgFlagsTy OrigFlags = Args[i].Flags[0]; - // We're going to replace the regs and flags with the split ones. - Args[i].Regs.clear(); - Args[i].Flags.clear(); - for (unsigned PartIdx = 0; PartIdx < NumParts; ++PartIdx) { - ISD::ArgFlagsTy Flags = OrigFlags; - if (PartIdx == 0) { - Flags.setSplit(); - } else { - Flags.setOrigAlign(Align(1)); - if (PartIdx == NumParts - 1) - Flags.setSplitEnd(); } - Args[i].Regs.push_back(Unmerge.getReg(PartIdx)); - Args[i].Flags.push_back(Flags); - if (Handler.assignArg(i + PartIdx, NewVT, NewVT, CCValAssign::Full, - Args[i], Args[i].Flags[PartIdx], CCInfo)) - return false; } + Args[i].Regs.push_back( + MIRBuilder.getMRI()->createGenericVirtualRegister(PartLLT)); + Args[i].Flags.push_back(Flags); + if (Handler.assignArg(i + Part, PartVT, PartVT, CCValAssign::Full, + Args[i], Args[i].Flags[Part], CCInfo)) + // Still couldn't assign this smaller part type for some reason. + return false; } + if (Handler.isIncomingArgumentHandler()) + continue; + + if (Exact) + MIRBuilder.buildUnmerge(Args[i].Regs, Args[i].OrigRegs[0]); + else + for (unsigned Part = 0, Offset = 0; Part < NumParts; + ++Part, Offset += NewVT.getSizeInBits()) + MIRBuilder.buildExtract(Args[i].Regs[Part], Args[i].OrigRegs[0], + Offset); } } - for (unsigned i = 0, e = Args.size(), j = 0; i != e; ++i, ++j) { + int64_t IndirectOffset = 0; + for (unsigned i = 0, e = Args.size(), j = 0; i != e; ++i) { assert(j < ArgLocs.size() && "Skipped too many arg locs"); CCValAssign &VA = ArgLocs[j]; assert(VA.getValNo() == i && "Location doesn't correspond to current arg"); + if (!Handler.prepareArg(VA)) + return false; if (VA.needsCustom()) { unsigned NumArgRegs = @@ -307,18 +299,46 @@ bool CallLowering::handleAssignments(CCState &CCInfo, } // FIXME: Pack registers if we have more than one. - Register ArgReg = Args[i].Regs[0]; - - MVT OrigVT = MVT::getVT(Args[i].Ty); + LLT OrigTy = getLLTForType(*Args[i].Ty, DL); MVT VAVT = VA.getValVT(); - if (VA.isRegLoc()) { - if (Handler.isIncomingArgumentHandler() && VAVT != OrigVT) { - if (VAVT.getSizeInBits() < OrigVT.getSizeInBits()) { - // Expected to be multiple regs for a single incoming arg. - unsigned NumArgRegs = Args[i].Regs.size(); - if (NumArgRegs < 2) - return false; + if (VA.getLocInfo() == CCValAssign::Indirect) { + if (Args[i].Regs.size() > 1) { + LLVM_DEBUG(dbgs() << "Load/store a split arg to/from an indirect " + "pointer not implemented yet\n"); + return false; + } + assert(Args[i].OrigRegs.empty() && "Don't handle split yet"); + Args[i].OrigRegs.push_back(Args[i].Regs[0]); + Args[i].Regs.clear(); + LLT p0 = LLT::pointer(0, DL.getPointerSizeInBits(0)); + Register IndirectAddrReg; + IndirectOffset = 0; + if (Handler.isIncomingArgumentHandler()) + IndirectAddrReg = MIRBuilder.getMRI()->createGenericVirtualRegister(p0); + else { + MachineFrameInfo &MFI = MF.getFrameInfo(); + unsigned Size = VAVT.getStoreSize(); + unsigned Align = DL.getPrefTypeAlignment(Args[i].Ty); + int FI = MFI.CreateStackObject(Size, Align, false); + auto StackSlot = MIRBuilder.buildFrameIndex(p0, FI); + LLT sIndex = LLT::scalar(DL.getIndexSizeInBits(0)); + MIRBuilder.materializePtrAdd(IndirectAddrReg, StackSlot.getReg(0), + sIndex, IndirectOffset); + MachinePointerInfo MPO = MachinePointerInfo::getFixedStack(MF, FI); + CCValAssign IndirectVA = CCValAssign::getMem(i, VAVT, IndirectOffset, + VAVT, CCValAssign::Full); + Handler.assignValueToAddress(Args[i].OrigRegs[0], IndirectAddrReg, Size, + MPO, IndirectVA); + IndirectOffset += Size; + } + Args[i].Regs.push_back(IndirectAddrReg); + } + + unsigned NumArgRegs = Args[i].Regs.size(); + if (VA.isRegLoc()) { + if (Handler.isIncomingArgumentHandler()) { + if (VAVT.getSizeInBits() <= OrigTy.getSizeInBits()) { assert((j + (NumArgRegs - 1)) < ArgLocs.size() && "Too many regs for number of args"); for (unsigned Part = 0; Part < NumArgRegs; ++Part) { @@ -326,67 +346,112 @@ bool CallLowering::handleAssignments(CCState &CCInfo, VA = ArgLocs[j + Part]; Handler.assignValueToReg(Args[i].Regs[Part], VA.getLocReg(), VA); } - j += NumArgRegs - 1; - // Merge the split registers into the expected larger result vreg - // of the original call. - MIRBuilder.buildMerge(Args[i].OrigRegs[0], Args[i].Regs); - continue; - } - const LLT VATy(VAVT); - Register NewReg = - MIRBuilder.getMRI()->createGenericVirtualRegister(VATy); - Handler.assignValueToReg(NewReg, VA.getLocReg(), VA); - // If it's a vector type, we either need to truncate the elements - // or do an unmerge to get the lower block of elements. - if (VATy.isVector() && - VATy.getNumElements() > OrigVT.getVectorNumElements()) { - const LLT OrigTy(OrigVT); - // Just handle the case where the VA type is 2 * original type. - if (VATy.getNumElements() != OrigVT.getVectorNumElements() * 2) { - LLVM_DEBUG(dbgs() - << "Incoming promoted vector arg has too many elts"); - return false; - } - auto Unmerge = MIRBuilder.buildUnmerge({OrigTy, OrigTy}, {NewReg}); - MIRBuilder.buildCopy(ArgReg, Unmerge.getReg(0)); } else { - MIRBuilder.buildTrunc(ArgReg, {NewReg}).getReg(0); + const LLT VATy(VAVT); + Register NewReg = + MIRBuilder.getMRI()->createGenericVirtualRegister(VATy); + Handler.assignValueToReg(NewReg, VA.getLocReg(), VA); + // If it's a vector type, we either need to truncate the elements + // or do an unmerge to get the lower block of elements. + if (VATy.isVector() && + VATy.getNumElements() > OrigTy.getNumElements()) { + // Just handle the case where the VA type is 2 * original type. + if (VATy.getNumElements() != OrigTy.getNumElements() * 2) { + LLVM_DEBUG(dbgs() + << "Incoming promoted vector arg has too many elts"); + return false; + } + auto Unmerge = MIRBuilder.buildUnmerge({OrigTy, OrigTy}, {NewReg}); + MIRBuilder.buildCopy(Args[i].Regs[0], Unmerge.getReg(0)); + } else { + MIRBuilder.buildTrunc(Args[i].Regs[0], {NewReg}).getReg(0); + } } - } else if (!Handler.isIncomingArgumentHandler()) { - assert((j + (Args[i].Regs.size() - 1)) < ArgLocs.size() && + } else { + assert((j + (NumArgRegs - 1)) < ArgLocs.size() && "Too many regs for number of args"); // This is an outgoing argument that might have been split. - for (unsigned Part = 0; Part < Args[i].Regs.size(); ++Part) { + for (unsigned Part = 0; Part < NumArgRegs; ++Part) { // There should be Regs.size() ArgLocs per argument. VA = ArgLocs[j + Part]; Handler.assignValueToReg(Args[i].Regs[Part], VA.getLocReg(), VA); } - j += Args[i].Regs.size() - 1; - } else { - Handler.assignValueToReg(ArgReg, VA.getLocReg(), VA); - } - } else if (VA.isMemLoc()) { - // Don't currently support loading/storing a type that needs to be split - // to the stack. Should be easy, just not implemented yet. - if (Args[i].Regs.size() > 1) { - LLVM_DEBUG( - dbgs() - << "Load/store a split arg to/from the stack not implemented yet"); - return false; } - MVT VT = MVT::getVT(Args[i].Ty); - unsigned Size = VT == MVT::iPTR ? DL.getPointerSize() - : alignTo(VT.getSizeInBits(), 8) / 8; - unsigned Offset = VA.getLocMemOffset(); - MachinePointerInfo MPO; - Register StackAddr = Handler.getStackAddress(Size, Offset, MPO); - Handler.assignValueToAddress(Args[i], StackAddr, Size, MPO, VA); } else { - // FIXME: Support byvals and other weirdness - return false; + assert(VA.isMemLoc()); + assert((j + (NumArgRegs - 1)) < ArgLocs.size() && + "Too many regs for number of args"); + for (unsigned Part = 0; Part < NumArgRegs; ++Part) { + VA = ArgLocs[j + Part]; + unsigned Size; + ISD::ArgFlagsTy OrigFlags = Args[i].Flags[Part]; + if (OrigFlags.isByVal()) + Size = OrigFlags.getByValSize(); + else + Size = DL.getTypeStoreSize(Args[i].Ty); + if (!Size) + continue; + unsigned Offset = VA.getLocMemOffset(); + MachinePointerInfo MPO; + Register StackAddr = Handler.getStackAddress(Size, Offset, MPO); + if (!OrigFlags.isByVal()) + Handler.assignValueToAddress(Args[i].Regs[Part], StackAddr, Size, MPO, + VA); + else if (Handler.isIncomingArgumentHandler()) + MIRBuilder.buildCopy(Args[i].Regs[Part], StackAddr); + else { + Register SizeReg = + MIRBuilder + .buildConstant(LLT::scalar(DL.getIndexSizeInBits(0)), Size) + .getReg(0); + MaybeAlign ArgAlign; + const Value *ArgVal = Args[i].Val; + if (ArgVal) + ArgAlign = ArgVal->getPointerAlignment(DL); + MIRBuilder + .buildIntrinsic(Intrinsic::memcpy, ArrayRef(), + /*HasSideEffects=*/true) + .addUse(StackAddr) + .addUse(Args[i].Regs[0]) + .addUse(SizeReg) + .addImm(/*isTailCall=*/0) + .addMemOperand( + MF.getMachineMemOperand(MPO, MachineMemOperand::MOStore, Size, + OrigFlags.getNonZeroByValAlign())) + .addMemOperand(MF.getMachineMemOperand( + MachinePointerInfo(ArgVal), MachineMemOperand::MOLoad, Size, + ArgAlign.valueOrOne())); + } + } + } + if (Handler.isIncomingArgumentHandler() && NumArgRegs > 1) { + // Merge the split registers into the expected larger result vreg + // of the original call. + SmallVector Offsets; + for (unsigned Part = 0; Part < NumArgRegs; ++Part) + Offsets.push_back(Part * VAVT.getSizeInBits()); + MIRBuilder.buildSequence(Args[i].OrigRegs[0], Args[i].Regs, Offsets); } + + if (VA.getLocInfo() == CCValAssign::Indirect && + Handler.isIncomingArgumentHandler()) { + Register AddrReg; + unsigned Size = VAVT.getStoreSize(); + LLT sIndex = LLT::scalar(DL.getIndexSizeInBits(0)); + MIRBuilder.materializePtrAdd(AddrReg, Args[i].Regs[0], sIndex, + IndirectOffset); + MachinePointerInfo MPO(Args[i].Val, IndirectOffset); + CCValAssign IndirectVA = + CCValAssign::getMem(i, VAVT, IndirectOffset, VAVT, CCValAssign::Full); + Handler.assignValueToAddress(Args[i].OrigRegs[0], AddrReg, Size, MPO, + IndirectVA); + IndirectOffset += Size; + } + + j += Args[i].Regs.size(); } - return true; + + return Handler.finalize(CCInfo); } bool CallLowering::analyzeArgInfo(CCState &CCState, @@ -482,23 +547,18 @@ Register CallLowering::ValueHandler::extendRegister(Register ValReg, default: break; case CCValAssign::Full: case CCValAssign::BCvt: + case CCValAssign::Indirect: // FIXME: bitconverting between vector types may or may not be a // nop in big-endian situations. return ValReg; - case CCValAssign::AExt: { - auto MIB = MIRBuilder.buildAnyExt(LocTy, ValReg); - return MIB.getReg(0); - } - case CCValAssign::SExt: { - Register NewReg = MRI.createGenericVirtualRegister(LocTy); - MIRBuilder.buildSExt(NewReg, ValReg); - return NewReg; - } - case CCValAssign::ZExt: { - Register NewReg = MRI.createGenericVirtualRegister(LocTy); - MIRBuilder.buildZExt(NewReg, ValReg); - return NewReg; - } + case CCValAssign::AExt: + return MIRBuilder.buildAnyExt(LocTy, ValReg).getReg(0); + case CCValAssign::SExt: + return MIRBuilder.buildSExt(LocTy, ValReg).getReg(0); + case CCValAssign::ZExt: + return MIRBuilder.buildZExt(LocTy, ValReg).getReg(0); + case CCValAssign::Trunc: + return MIRBuilder.buildTrunc(LocTy, ValReg).getReg(0); } llvm_unreachable("unable to extend register"); } diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp index 32bad28d318ba9..c84424178a1728 100644 --- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -576,6 +576,37 @@ bool CombinerHelper::dominates(const MachineInstr &DefMI, return isPredecessor(DefMI, UseMI); } +bool CombinerHelper::dominates(MachineBasicBlock &DefMBB, + MachineBasicBlock &UseMBB) { + if (MDT) + return MDT->dominates(&DefMBB, &UseMBB); + return &DefMBB == &UseMBB; +} + +bool CombinerHelper::canMove(MachineInstr &MI, MachineBasicBlock &MBB, + bool &SawStore) { + if (MI.isConvergent() || !MI.isSafeToMove(nullptr, SawStore)) + return false; + for (auto &MO : MI.operands()) { + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (!Reg.isVirtual()) + return false; + if (!MO.isDef() || MO.isImplicit()) + continue; + for (auto &UseMO : MRI.use_nodbg_operands(Reg)) { + auto &UseMI = *UseMO.getParent(); + auto *UseMBB = UseMI.getParent(); + if (UseMI.isPHI()) + UseMBB = UseMI.getOperand(UseMI.getOperandNo(&UseMO) + 1).getMBB(); + if (!dominates(MBB, *UseMBB)) + return false; + } + } + return true; +} + bool CombinerHelper::findPostIndexCandidate(MachineInstr &MI, Register &Addr, Register &Base, Register &Offset) { auto &MF = *MI.getParent()->getParent(); @@ -1360,18 +1391,18 @@ bool CombinerHelper::matchPtrAddImmedChain(MachineInstr &MI, // Pass the combined immediate to the apply function. MatchInfo.Imm = MaybeImmVal->Value + MaybeImm2Val->Value; - MatchInfo.Base = Base; + MatchInfo.Reg = Base; return true; } bool CombinerHelper::applyPtrAddImmedChain(MachineInstr &MI, PtrAddChain &MatchInfo) { assert(MI.getOpcode() == TargetOpcode::G_PTR_ADD && "Expected G_PTR_ADD"); - MachineIRBuilder MIB(MI); + Builder.setInstr(MI); LLT OffsetTy = MRI.getType(MI.getOperand(2).getReg()); - auto NewOffset = MIB.buildConstant(OffsetTy, MatchInfo.Imm); + auto NewOffset = Builder.buildConstant(OffsetTy, MatchInfo.Imm); Observer.changingInstr(MI); - MI.getOperand(1).setReg(MatchInfo.Base); + MI.getOperand(1).setReg(MatchInfo.Reg); MI.getOperand(2).setReg(NewOffset.getReg(0)); Observer.changedInstr(MI); return true; @@ -1391,11 +1422,11 @@ bool CombinerHelper::matchCombineMulToShl(MachineInstr &MI, bool CombinerHelper::applyCombineMulToShl(MachineInstr &MI, unsigned &ShiftVal) { assert(MI.getOpcode() == TargetOpcode::G_MUL && "Expected a G_MUL"); - MachineIRBuilder MIB(MI); + Builder.setInstr(MI); LLT ShiftTy = MRI.getType(MI.getOperand(0).getReg()); - auto ShiftCst = MIB.buildConstant(ShiftTy, ShiftVal); + auto ShiftCst = Builder.buildConstant(ShiftTy, ShiftVal); Observer.changingInstr(MI); - MI.setDesc(MIB.getTII().get(TargetOpcode::G_SHL)); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_SHL)); MI.getOperand(2).setReg(ShiftCst.getReg(0)); Observer.changedInstr(MI); return true; @@ -1709,6 +1740,393 @@ bool CombinerHelper::applySimplifyAddToSub( return true; } +bool CombinerHelper::matchPtrAddGlobalImmed(MachineInstr &MI, + PtrAddGlobal &MatchInfo) { + // We're trying to match the following pattern: + // %t1 = G_GLOBAL_VALUE @global+offset + // %root = G_PTR_ADD %t1, G_CONSTANT imm + // --> + // %root = G_GLOBAL_VALUE @global+offset+imm + + if (MI.getOpcode() != TargetOpcode::G_PTR_ADD) + return false; + + Register Global = MI.getOperand(1).getReg(); + Register Imm = MI.getOperand(2).getReg(); + auto MaybeImmVal = getConstantVRegValWithLookThrough(Imm, MRI); + if (!MaybeImmVal) + return false; + + MachineInstr *GlobalDef = MRI.getUniqueVRegDef(Global); + if (!GlobalDef || GlobalDef->getOpcode() != TargetOpcode::G_GLOBAL_VALUE) + return false; + + const GlobalValue *Base = GlobalDef->getOperand(1).getGlobal(); + auto Offset = GlobalDef->getOperand(1).getOffset(); + + // Pass the combined immediate to the apply function. + MatchInfo.Imm = MaybeImmVal->Value + Offset; + MatchInfo.Global = Base; + return true; +} + +bool CombinerHelper::applyPtrAddGlobalImmed(MachineInstr &MI, + PtrAddGlobal &MatchInfo) { + assert(MI.getOpcode() == TargetOpcode::G_PTR_ADD && "Expected G_PTR_ADD"); + Builder.setInstr(MI); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_GLOBAL_VALUE)); + MI.getOperand(1).ChangeToGA(MatchInfo.Global, MatchInfo.Imm); + MI.RemoveOperand(2); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchPtrAddConstImmed(MachineInstr &MI, + PtrAddConst &MatchInfo) { + // We're trying to match the following pattern: + // %t1 = G_INTTOPTR G_CONSTANT imm2 + // %root = G_PTR_ADD %t1, G_CONSTANT imm + // --> + // %root = G_INTTOPTR G_CONSTANT imm+imm2 + + if (MI.getOpcode() != TargetOpcode::G_PTR_ADD) + return false; + + Register IntToPtr = MI.getOperand(1).getReg(); + Register Imm = MI.getOperand(2).getReg(); + auto MaybeImmVal = getConstantVRegValWithLookThrough(Imm, MRI); + if (!MaybeImmVal) + return false; + + MachineInstr *IntToPtrDef = MRI.getUniqueVRegDef(IntToPtr); + if (!IntToPtrDef || IntToPtrDef->getOpcode() != TargetOpcode::G_INTTOPTR) + return false; + + Register Imm2 = IntToPtrDef->getOperand(1).getReg(); + auto MaybeImm2Val = getConstantVRegValWithLookThrough(Imm2, MRI); + if (!MaybeImm2Val) + return false; + + // Pass the combined immediate to the apply function. + MatchInfo.Imm = MaybeImmVal->Value + MaybeImm2Val->Value; + MatchInfo.Ty = MRI.getType(Imm2); + return true; +} + +bool CombinerHelper::applyPtrAddConstImmed(MachineInstr &MI, + PtrAddConst &MatchInfo) { + assert(MI.getOpcode() == TargetOpcode::G_PTR_ADD && "Expected G_PTR_ADD"); + Builder.setInstr(MI); + auto NewConst = Builder.buildConstant(MatchInfo.Ty, MatchInfo.Imm); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_INTTOPTR)); + MI.getOperand(1).setReg(NewConst.getReg(0)); + MI.RemoveOperand(2); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchCombineShlToAdd(MachineInstr &MI, + unsigned &ShiftVal) { + if (MI.getOpcode() != TargetOpcode::G_SHL) + return false; + Register RHSReg = MI.getOperand(2).getReg(); + auto RHSImm = getConstantVRegValWithLookThrough(RHSReg, MRI); + if (!RHSImm || !RHSImm->Value) + return false; + ShiftVal = RHSImm->Value; + return true; +} + +bool CombinerHelper::applyCombineShlToAdd(MachineInstr &MI, + unsigned &ShiftVal) { + assert(MI.getOpcode() == TargetOpcode::G_SHL && "Expected a G_SHL"); + Register Reg = MI.getOperand(1).getReg(); + LLT RegTy = MRI.getType(Reg); + Builder.setInstr(MI); + while (--ShiftVal) + Reg = Builder.buildAdd(RegTy, Reg, Reg).getReg(0); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_ADD)); + MI.getOperand(1).setReg(Reg); + MI.getOperand(2).setReg(Reg); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchCombineSExtToZExt(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::G_SEXT) + return false; + return KB->getKnownBits(MI.getOperand(1).getReg()).isNonNegative(); +} + +bool CombinerHelper::applyCombineSExtToZExt(MachineInstr &MI) { + assert(MI.getOpcode() == TargetOpcode::G_SEXT && "Expected a G_SEXT"); + Builder.setInstr(MI); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_ZEXT)); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchCombineOrToAdd(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::G_OR) + return false; + APInt Zeroes = KB->getKnownZeroes(MI.getOperand(1).getReg()); + Zeroes |= KB->getKnownZeroes(MI.getOperand(2).getReg()); + return Zeroes.isAllOnesValue(); +} + +bool CombinerHelper::applyCombineOrToAdd(MachineInstr &MI) { + assert(MI.getOpcode() == TargetOpcode::G_OR && "Expected a G_OR"); + Builder.setInstr(MI); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::G_ADD)); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchCombineFunnelShift(MachineInstr &MI, + FunnelShift &MatchInfo) { + Register DstReg = MI.getOperand(0).getReg(); + int64_t ShiftRightAmt; + return mi_match(DstReg, MRI, + m_GAdd(m_GShl(m_Reg(MatchInfo.ShiftLeftReg), + m_ICst(MatchInfo.ShiftLeftAmt)), + m_GLShr(m_Reg(MatchInfo.ShiftRightReg), + m_ICst(ShiftRightAmt)))) && + MatchInfo.ShiftLeftAmt + ShiftRightAmt == + MRI.getType(DstReg).getSizeInBits(); +} + +void CombinerHelper::applyCombineFunnelShift(MachineInstr &MI, + const FunnelShift &MatchInfo) { + MachineIRBuilder MIB(MI); + Register DstReg = MI.getOperand(0).getReg(); + auto AmtI = MIB.buildConstant(MRI.getType(DstReg), MatchInfo.ShiftLeftAmt); + MIB.buildInstr( + TargetOpcode::G_FSHL, {DstReg}, + {MatchInfo.ShiftLeftReg, MatchInfo.ShiftRightReg, AmtI.getReg(0)}); + MI.eraseFromParent(); +} + +bool CombinerHelper::matchCombineIdentity(MachineInstr &MI) { + int64_t IdentityElement; + switch (MI.getOpcode()) { + case TargetOpcode::G_ADD: + case TargetOpcode::G_SUB: + case TargetOpcode::G_OR: + case TargetOpcode::G_XOR: + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: + case TargetOpcode::G_PTR_ADD: + IdentityElement = 0; + break; + case TargetOpcode::G_MUL: + case TargetOpcode::G_SDIV: + case TargetOpcode::G_UDIV: + IdentityElement = 1; + break; + case TargetOpcode::G_AND: + case TargetOpcode::G_PTRMASK: + IdentityElement = -1; + break; + default: + return false; + } + auto MaybeImmVal = + getConstantVRegValWithLookThrough(MI.getOperand(2).getReg(), MRI); + return MaybeImmVal && MaybeImmVal->Value == IdentityElement; +} + +bool CombinerHelper::applyCombineIdentity(MachineInstr &MI) { + Builder.setInstr(MI); + Observer.changingInstr(MI); + MI.setDesc(Builder.getTII().get(TargetOpcode::COPY)); + MI.RemoveOperand(2); + Observer.changedInstr(MI); + return true; +} + +bool CombinerHelper::matchSplitConditions(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::G_BRCOND) + return false; + Register CondReg = MI.getOperand(0).getReg(); + MachineInstr *CondMI = MRI.getVRegDef(CondReg); + return MRI.hasOneUse(CondReg) && CondMI && + (CondMI->getOpcode() == TargetOpcode::G_AND || + CondMI->getOpcode() == TargetOpcode::G_OR); +} + +void CombinerHelper::applySplitConditions(MachineInstr &MI) { + MachineInstr &CondMI = *MRI.getVRegDef(MI.getOperand(0).getReg()); + bool IsAnd = CondMI.getOpcode() == TargetOpcode::G_AND; + Register CondLHSReg = CondMI.getOperand(1).getReg(); + Register CondRHSReg = CondMI.getOperand(2).getReg(); + CondMI.eraseFromParent(); + + MachineBasicBlock::iterator II(MI); + MachineBasicBlock &CurMBB = *MI.getParent(); + MachineFunction &MF = *CurMBB.getParent(); + Builder.setMBB(CurMBB); + auto NextMBB = std::next(CurMBB.getIterator()); + + auto &NewMBB = *MF.CreateMachineBasicBlock(CurMBB.getBasicBlock()); + MF.insert(NextMBB, &NewMBB); + NewMBB.splice(NewMBB.begin(), &CurMBB, II, CurMBB.end()); + NewMBB.transferSuccessorsAndUpdatePHIs(&CurMBB); + + MachineBasicBlock *CommonMBB = MI.getOperand(1).getMBB(), *SuccMBB; + MachineInstr &Term = NewMBB.back(); + if (II != Term) { + assert(std::next(II) == Term && Term.getOpcode() == TargetOpcode::G_BR && + "Expected unconditional branch or nothing after conditional one."); + SuccMBB = Term.getOperand(0).getMBB(); + } else + SuccMBB = &*NextMBB; + if (IsAnd) { + std::swap(CommonMBB, SuccMBB); + II = Builder.buildBrCond(CondLHSReg, NewMBB); + Builder.buildBr(*CommonMBB); + } else + II = Builder.buildBrCond(CondLHSReg, *CommonMBB); + + CurMBB.addSuccessor(&NewMBB); + CurMBB.addSuccessor(CommonMBB); + if (MDT) { + if (SuccMBB->pred_size() == 1) { + MDT->addNewBlock(&NewMBB, &CurMBB); + MDT->changeImmediateDominator(SuccMBB, &NewMBB); + } else + MDT->recordSplitCriticalEdge(&CurMBB, SuccMBB, &NewMBB); + } + for (MachineInstr &PhiMI : CommonMBB->phis()) { + for (unsigned I = 1, E = PhiMI.getNumOperands(); I != E; I += 2) { + if (PhiMI.getOperand(I + 1).getMBB() == &NewMBB) { + MachineInstrBuilder(MF, PhiMI).add(PhiMI.getOperand(I)).addMBB(&CurMBB); + break; + } + } + } + MI.getOperand(0).setReg(CondRHSReg); + + bool SawStore = false; + while (II != CurMBB.begin()) { + auto PrevII = II; + --PrevII; + if (canMove(*PrevII, NewMBB, SawStore)) + NewMBB.splice(NewMBB.begin(), &CurMBB, PrevII); + else + II = PrevII; + } +} + +bool CombinerHelper::matchFlipCondition(MachineInstr &MI, MachineInstr *&CmpI) { + Register DstReg = MI.getOperand(0).getReg(); + int64_t Cst; + return mi_match(DstReg, MRI, m_GXor(m_MInstr(CmpI), m_ICst(Cst))) && + (CmpI->getOpcode() == TargetOpcode::G_ICMP || + CmpI->getOpcode() == TargetOpcode::G_FCMP) && Cst; +} + +void CombinerHelper::applyFlipCondition(MachineInstr &MI, MachineInstr &CmpI) { + Builder.setInsertPt(*MI.getParent(), MI); + Builder.buildInstr(CmpI.getOpcode(), {MI.getOperand(0)}, + {CmpInst::getInversePredicate(CmpInst::Predicate( + CmpI.getOperand(1).getPredicate())), + CmpI.getOperand(2), CmpI.getOperand(3)}); + MI.eraseFromParent(); +} + +bool CombinerHelper::matchLowerIsPowerOfTwo(MachineInstr &MI) { + if (MI.getOpcode() != TargetOpcode::G_ICMP) + return false; + auto Pred = CmpInst::Predicate(MI.getOperand(1).getPredicate()); + MachineInstr *CtPop = MRI.getVRegDef(MI.getOperand(2).getReg()); + Register RHSReg = MI.getOperand(3).getReg(); + auto RHSConst = getConstantVRegVal(RHSReg, MRI); + if (!CtPop || CtPop->getOpcode() != TargetOpcode::G_CTPOP || !RHSConst) + return false; + if (((Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE) && + *RHSConst == 1) || + (Pred == CmpInst::ICMP_ULT && *RHSConst == 2) || + (Pred == CmpInst::ICMP_UGT && *RHSConst == 1)) + return true; + return false; +} + +void CombinerHelper::applyLowerIsPowerOfTwo(MachineInstr &MI) { + Builder.setInsertPt(*MI.getParent(), MI); + Register ResReg = MI.getOperand(0).getReg(); + auto Pred = CmpInst::Predicate(MI.getOperand(1).getPredicate()); + MachineInstr *CtPop = MRI.getVRegDef(MI.getOperand(2).getReg()); + Register SrcReg = CtPop->getOperand(1).getReg(); + LLT Ty = MRI.getType(SrcReg); + auto NegOne = Builder.buildConstant(Ty, -1); + auto Add = Builder.buildAdd(Ty, SrcReg, NegOne); + auto And = Builder.buildAnd(Ty, SrcReg, Add); + auto Zero = Builder.buildConstant(Ty, 0); + if (Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE) { + auto IsNotZero = Builder.buildICmp(CmpInst::getInversePredicate(Pred), + LLT::scalar(1), SrcReg, Zero); + auto IsPowerOfTwo = Builder.buildICmp(Pred, LLT::scalar(1), And, Zero); + Builder.buildInstr(Pred == CmpInst::ICMP_EQ ? TargetOpcode::G_AND + : TargetOpcode::G_OR, + {ResReg}, {IsNotZero, IsPowerOfTwo}); + } else + Builder.buildICmp(Pred == CmpInst::ICMP_ULT ? CmpInst::ICMP_EQ + : CmpInst::ICMP_NE, + ResReg, And, Zero); + MI.eraseFromParent(); +} + +bool CombinerHelper::matchSinkConstant(MachineInstr &MI, + MachineInstr *&DomUseMI) { + if (MI.getOpcode() != TargetOpcode::G_CONSTANT) + return false; + Register Reg = MI.getOperand(0).getReg(); + SmallVector DomMIs; + for (MachineInstr &UseMI : MRI.use_instructions(Reg)) { + if (UseMI.isPHI()) + return false; + bool Dominated = false; + for (unsigned I = 0; I != DomMIs.size();) { + MachineInstr &DomMI = *DomMIs[I]; + if (dominates(DomMI, UseMI)) { + Dominated = true; + break; + } + if (dominates(UseMI, DomMI)) { + if (I != DomMIs.size() - 1) + DomMIs[I] = DomMIs.pop_back_val(); + else + DomMIs.pop_back(); + continue; + } + ++I; + } + if (!Dominated) + DomMIs.push_back(&UseMI); + } + if (DomMIs.size() != 1) + return false; + DomUseMI = DomMIs[0]; + if (MI.getParent() != DomUseMI->getParent()) + return true; + for (MachineBasicBlock::iterator I(MI), E(DomUseMI); I != E; ++I) + if (I->getOpcode() != TargetOpcode::G_CONSTANT) + return true; + return false; +} + +void CombinerHelper::applySinkConstant(MachineInstr &MI, + MachineInstr &DomUseMI) { + MI.removeFromParent(); + DomUseMI.getParent()->insert(DomUseMI, &MI); +} + bool CombinerHelper::tryCombine(MachineInstr &MI) { if (tryCombineCopy(MI)) return true; diff --git a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp index 1d7be54de3b045..b23dffe6df3a55 100644 --- a/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp +++ b/llvm/lib/CodeGen/GlobalISel/Legalizer.cpp @@ -93,6 +93,7 @@ static bool isArtifact(const MachineInstr &MI) { switch (MI.getOpcode()) { default: return false; + case TargetOpcode::G_IMPLICIT_DEF: case TargetOpcode::G_TRUNC: case TargetOpcode::G_ZEXT: case TargetOpcode::G_ANYEXT: @@ -102,9 +103,13 @@ static bool isArtifact(const MachineInstr &MI) { case TargetOpcode::G_CONCAT_VECTORS: case TargetOpcode::G_BUILD_VECTOR: case TargetOpcode::G_EXTRACT: + case TargetOpcode::G_INSERT: return true; } } +static bool isPreISelGenericOpcodeOrCopy(unsigned Opcode) { + return isPreISelGenericOpcode(Opcode) || Opcode == TargetOpcode::COPY; +} using InstListTy = GISelWorkList<256>; using ArtifactListTy = GISelWorkList<128>; @@ -112,19 +117,22 @@ namespace { class LegalizerWorkListManager : public GISelChangeObserver { InstListTy &InstList; ArtifactListTy &ArtifactList; + MachineRegisterInfo &MRI; #ifndef NDEBUG SmallVector NewMIs; #endif public: - LegalizerWorkListManager(InstListTy &Insts, ArtifactListTy &Arts) - : InstList(Insts), ArtifactList(Arts) {} + LegalizerWorkListManager(InstListTy &Insts, ArtifactListTy &Arts, + MachineRegisterInfo &MRI) + : InstList(Insts), ArtifactList(Arts), MRI(MRI) {} void createdOrChangedInstr(MachineInstr &MI) { // Only legalize pre-isel generic instructions. // Legalization process could generate Target specific pseudo // instructions with generic types. Don't record them - if (isPreISelGenericOpcode(MI.getOpcode())) { + // Do record copies in case they become dead and use an artifact. + if (isPreISelGenericOpcodeOrCopy(MI.getOpcode())) { if (isArtifact(MI)) ArtifactList.insert(&MI); else @@ -132,6 +140,13 @@ class LegalizerWorkListManager : public GISelChangeObserver { } } + void maybeRemovingUses(MachineInstr &MI) { + for (const MachineOperand &MO : MI.explicit_uses()) + if (MO.isReg() && MO.getReg().isVirtual()) + for (MachineInstr &DefMI : MRI.def_instructions(MO.getReg())) + createdOrChangedInstr(DefMI); + } + void createdInstr(MachineInstr &MI) override { LLVM_DEBUG(NewMIs.push_back(&MI)); createdOrChangedInstr(MI); @@ -147,12 +162,14 @@ class LegalizerWorkListManager : public GISelChangeObserver { void erasingInstr(MachineInstr &MI) override { LLVM_DEBUG(dbgs() << ".. .. Erasing: " << MI); + maybeRemovingUses(MI); InstList.remove(&MI); ArtifactList.remove(&MI); } void changingInstr(MachineInstr &MI) override { LLVM_DEBUG(dbgs() << ".. .. Changing MI: " << MI); + maybeRemovingUses(MI); } void changedInstr(MachineInstr &MI) override { @@ -185,7 +202,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI, for (MachineInstr &MI : *MBB) { // Only legalize pre-isel generic instructions: others don't have types // and are assumed to be legal. - if (!isPreISelGenericOpcode(MI.getOpcode())) + if (!isPreISelGenericOpcodeOrCopy(MI.getOpcode())) continue; if (isArtifact(MI)) ArtifactList.deferred_insert(&MI); @@ -197,7 +214,7 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI, InstList.finalize(); // This observer keeps the worklists updated. - LegalizerWorkListManager WorkListObserver(InstList, ArtifactList); + LegalizerWorkListManager WorkListObserver(InstList, ArtifactList, MRI); // We want both WorkListObserver as well as all the auxiliary observers (e.g. // CSEInfo) to observe all changes. Use the wrapper observer. GISelObserverWrapper WrapperObserver(&WorkListObserver); @@ -220,8 +237,9 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI, unsigned NumArtifacts = ArtifactList.size(); while (!InstList.empty()) { MachineInstr &MI = *InstList.pop_back_val(); - assert(isPreISelGenericOpcode(MI.getOpcode()) && - "Expecting generic opcode"); + assert(MI.getParent() && "Instruction deleted?"); + assert(isPreISelGenericOpcodeOrCopy(MI.getOpcode()) && + "Expecting generic opcode or copy"); if (isTriviallyDead(MI, MRI)) { LLVM_DEBUG(dbgs() << MI << "Is dead; erasing.\n"); MI.eraseFromParentAndMarkDBGValuesForRemoval(); @@ -269,8 +287,8 @@ Legalizer::legalizeMachineFunction(MachineFunction &MF, const LegalizerInfo &LI, LocObserver.checkpoint(); while (!ArtifactList.empty()) { MachineInstr &MI = *ArtifactList.pop_back_val(); - assert(isPreISelGenericOpcode(MI.getOpcode()) && - "Expecting generic opcode"); + assert(MI.getParent() && "Instruction deleted?"); + assert(isArtifact(MI) && "Expecting artifact"); if (isTriviallyDead(MI, MRI)) { LLVM_DEBUG(dbgs() << MI << "Is dead\n"); RemoveDeadInstFromLists(&MI); diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp index 38590656d1f4b4..cb78b0d78ff943 100644 --- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -35,32 +35,31 @@ using namespace LegalizeActions; /// Returns the number of \p NarrowTy elements needed to reconstruct \p OrigTy, /// with any leftover piece as type \p LeftoverTy /// -/// Returns -1 in the first element of the pair if the breakdown is not -/// satisfiable. -static std::pair -getNarrowTypeBreakDown(LLT OrigTy, LLT NarrowTy, LLT &LeftoverTy) { - assert(!LeftoverTy.isValid() && "this is an out argument"); - +/// Returns 0 if the breakdown is not satisfiable. +static unsigned getNarrowTypeBreakDown(LLT OrigTy, LLT NarrowTy, + LLT &LeftoverTy) { unsigned Size = OrigTy.getSizeInBits(); unsigned NarrowSize = NarrowTy.getSizeInBits(); unsigned NumParts = Size / NarrowSize; unsigned LeftoverSize = Size - NumParts * NarrowSize; + assert( + (!LeftoverTy.isValid() || LeftoverTy.getSizeInBits() == LeftoverSize) && + "LeftoverTy already set and doesn't match this breakdown"); assert(Size > NarrowSize); if (LeftoverSize == 0) - return {NumParts, 0}; + return NumParts; if (NarrowTy.isVector()) { unsigned EltSize = OrigTy.getScalarSizeInBits(); if (LeftoverSize % EltSize != 0) - return {-1, -1}; + return 0; LeftoverTy = LLT::scalarOrVector(LeftoverSize / EltSize, EltSize); } else { LeftoverTy = LLT::scalar(LeftoverSize); } - int NumLeftover = LeftoverSize / LeftoverTy.getSizeInBits(); - return std::make_pair(NumParts, NumLeftover); + return NumParts; } static Type *getFloatTypeForLLT(LLVMContext &Ctx, LLT Ty) { @@ -102,6 +101,8 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { MIRBuilder.setInstrAndDebugLoc(MI); + if (MI.getOpcode() == TargetOpcode::COPY) + return AlreadyLegal; if (MI.getOpcode() == TargetOpcode::G_INTRINSIC || MI.getOpcode() == TargetOpcode::G_INTRINSIC_W_SIDE_EFFECTS) return LI.legalizeIntrinsic(*this, MI) ? Legalized : UnableToLegalize; @@ -124,7 +125,7 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { return bitcast(MI, Step.TypeIdx, Step.NewType); case Lower: LLVM_DEBUG(dbgs() << ".. Lower\n"); - return lower(MI, Step.TypeIdx, Step.NewType); + return lower(MI, Step.TypeIdx); case FewerElements: LLVM_DEBUG(dbgs() << ".. Reduce number of elements\n"); return fewerElementsVector(MI, Step.TypeIdx, Step.NewType); @@ -133,7 +134,7 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { return moreElementsVector(MI, Step.TypeIdx, Step.NewType); case Custom: LLVM_DEBUG(dbgs() << ".. Custom legalization\n"); - return LI.legalizeCustom(*this, MI) ? Legalized : UnableToLegalize; + return LI.legalizeCustomMaybeLegal(*this, MI); default: LLVM_DEBUG(dbgs() << ".. Unable to legalize\n"); return UnableToLegalize; @@ -142,63 +143,56 @@ LegalizerHelper::legalizeInstrStep(MachineInstr &MI) { void LegalizerHelper::extractParts(Register Reg, LLT Ty, int NumParts, SmallVectorImpl &VRegs) { + assert(NumParts && "Expected at least one part"); + if (NumParts == 1) { + VRegs.push_back(Reg); + return; + } for (int i = 0; i < NumParts; ++i) VRegs.push_back(MRI.createGenericVirtualRegister(Ty)); MIRBuilder.buildUnmerge(VRegs, Reg); } -bool LegalizerHelper::extractParts(Register Reg, LLT RegTy, - LLT MainTy, LLT &LeftoverTy, +bool LegalizerHelper::extractParts(Register Reg, LLT RegTy, LLT MainTy, SmallVectorImpl &VRegs, - SmallVectorImpl &LeftoverRegs) { - assert(!LeftoverTy.isValid() && "this is an out argument"); + LLT &LeftoverTy, Register &LeftoverReg) { + assert(!LeftoverReg.isValid() && "this is an out argument"); - unsigned RegSize = RegTy.getSizeInBits(); - unsigned MainSize = MainTy.getSizeInBits(); - unsigned NumParts = RegSize / MainSize; - unsigned LeftoverSize = RegSize - NumParts * MainSize; + unsigned NumParts = getNarrowTypeBreakDown(RegTy, MainTy, LeftoverTy); + if (!NumParts) + return false; // Use an unmerge when possible. - if (LeftoverSize == 0) { + if (!LeftoverTy.isValid()) { for (unsigned I = 0; I < NumParts; ++I) VRegs.push_back(MRI.createGenericVirtualRegister(MainTy)); MIRBuilder.buildUnmerge(VRegs, Reg); return true; } - if (MainTy.isVector()) { - unsigned EltSize = MainTy.getScalarSizeInBits(); - if (LeftoverSize % EltSize != 0) - return false; - LeftoverTy = LLT::scalarOrVector(LeftoverSize / EltSize, EltSize); - } else { - LeftoverTy = LLT::scalar(LeftoverSize); - } - // For irregular sizes, extract the individual parts. - for (unsigned I = 0; I != NumParts; ++I) { - Register NewReg = MRI.createGenericVirtualRegister(MainTy); - VRegs.push_back(NewReg); - MIRBuilder.buildExtract(NewReg, Reg, MainSize * I); - } + unsigned MainSize = MainTy.getSizeInBits(); + unsigned Offset = 0; + for (unsigned I = 0; I != NumParts; ++I, Offset += MainSize) + VRegs.push_back(MIRBuilder.buildExtract(MainTy, Reg, Offset).getReg(0)); - for (unsigned Offset = MainSize * NumParts; Offset < RegSize; - Offset += LeftoverSize) { - Register NewReg = MRI.createGenericVirtualRegister(LeftoverTy); - LeftoverRegs.push_back(NewReg); - MIRBuilder.buildExtract(NewReg, Reg, Offset); - } + if (LeftoverTy.isValid()) + LeftoverReg = MIRBuilder.buildExtract(LeftoverTy, Reg, Offset).getReg(0); return true; } -void LegalizerHelper::insertParts(Register DstReg, - LLT ResultTy, LLT PartTy, - ArrayRef PartRegs, - LLT LeftoverTy, - ArrayRef LeftoverRegs) { +void LegalizerHelper::insertParts(Register DstReg, LLT ResultTy, LLT PartTy, + ArrayRef PartRegs, LLT LeftoverTy, + Register LeftoverReg) { if (!LeftoverTy.isValid()) { - assert(LeftoverRegs.empty()); + assert(!LeftoverReg.isValid()); + + if (ResultTy == PartTy) { + assert(PartRegs.size() == 1); + MIRBuilder.buildCopy(DstReg, PartRegs[0]); + return; + } if (!ResultTy.isVector()) { MIRBuilder.buildMerge(DstReg, PartRegs); @@ -213,7 +207,6 @@ void LegalizerHelper::insertParts(Register DstReg, } unsigned PartSize = PartTy.getSizeInBits(); - unsigned LeftoverPartSize = LeftoverTy.getSizeInBits(); Register CurResultReg = MRI.createGenericVirtualRegister(ResultTy); MIRBuilder.buildUndef(CurResultReg); @@ -226,15 +219,8 @@ void LegalizerHelper::insertParts(Register DstReg, Offset += PartSize; } - for (unsigned I = 0, E = LeftoverRegs.size(); I != E; ++I) { - // Use the original output register for the final insert to avoid a copy. - Register NewResultReg = (I + 1 == E) ? - DstReg : MRI.createGenericVirtualRegister(ResultTy); - - MIRBuilder.buildInsert(NewResultReg, CurResultReg, LeftoverRegs[I], Offset); - CurResultReg = NewResultReg; - Offset += LeftoverPartSize; - } + if (LeftoverTy.isValid()) + MIRBuilder.buildInsert(DstReg, CurResultReg, LeftoverReg, Offset); } /// Return the result registers of G_UNMERGE_VALUES \p MI in \p Regs @@ -398,19 +384,63 @@ static RTLIB::Libcall getRTLibDesc(unsigned Opcode, unsigned Size) { } \ } while (0) - assert((Size == 32 || Size == 64 || Size == 128) && "Unsupported size"); +#define RTLIBCASEALL(LibcallPrefix) \ + do { \ + switch (Size) { \ + case 8: \ + return RTLIB::LibcallPrefix##8; \ + case 16: \ + return RTLIB::LibcallPrefix##16; \ + case 24: \ + return RTLIB::LibcallPrefix##24; \ + case 32: \ + return RTLIB::LibcallPrefix##32; \ + case 64: \ + return RTLIB::LibcallPrefix##64; \ + case 128: \ + return RTLIB::LibcallPrefix##128; \ + default: \ + llvm_unreachable("unexpected size"); \ + } \ + } while (0) switch (Opcode) { + case TargetOpcode::G_ADD: + RTLIBCASE(ADD_I); + case TargetOpcode::G_SUB: + RTLIBCASE(SUB_I); + case TargetOpcode::G_AND: + RTLIBCASEALL(AND_I); + case TargetOpcode::G_OR: + RTLIBCASEALL(OR_I); + case TargetOpcode::G_XOR: + RTLIBCASEALL(XOR_I); + case TargetOpcode::G_SHL: + RTLIBCASEALL(SHL_I); + case TargetOpcode::G_LSHR: + RTLIBCASEALL(SRL_I); + case TargetOpcode::G_ASHR: + RTLIBCASEALL(SRA_I); + case TargetOpcode::G_MUL: + RTLIBCASEALL(MUL_I); case TargetOpcode::G_SDIV: - RTLIBCASE(SDIV_I); + RTLIBCASEALL(SDIV_I); case TargetOpcode::G_UDIV: - RTLIBCASE(UDIV_I); + RTLIBCASEALL(UDIV_I); case TargetOpcode::G_SREM: - RTLIBCASE(SREM_I); + RTLIBCASEALL(SREM_I); case TargetOpcode::G_UREM: - RTLIBCASE(UREM_I); + RTLIBCASEALL(UREM_I); + case TargetOpcode::G_CTPOP: + RTLIBCASEALL(POPCNT_I); + case TargetOpcode::G_BITREVERSE: + RTLIBCASEALL(BITREV_I); case TargetOpcode::G_CTLZ_ZERO_UNDEF: RTLIBCASE(CTLZ_I); + case TargetOpcode::G_INTRINSIC_TRUNC: + RTLIBCASE(TRUNC_F); + case TargetOpcode::G_INTRINSIC_ROUND: + RTLIBCASE(ROUND_F); case TargetOpcode::G_FADD: RTLIBCASE(ADD_F); case TargetOpcode::G_FSUB: @@ -453,6 +483,12 @@ static RTLIB::Libcall getRTLibDesc(unsigned Opcode, unsigned Size) { RTLIBCASE(RINT_F); case TargetOpcode::G_FNEARBYINT: RTLIBCASE(NEARBYINT_F); + case TargetOpcode::G_FCOPYSIGN: + RTLIBCASE(COPYSIGN_F); + case TargetOpcode::G_FNEG: + RTLIBCASE(NEG_F); + case TargetOpcode::G_FABS: + RTLIBCASE(ABS_F); } llvm_unreachable("Unknown libcall function"); } @@ -637,17 +673,47 @@ LegalizerHelper::libcall(MachineInstr &MI) { switch (MI.getOpcode()) { default: return UnableToLegalize; + case TargetOpcode::G_ADD: + case TargetOpcode::G_SUB: + case TargetOpcode::G_AND: + case TargetOpcode::G_OR: + case TargetOpcode::G_XOR: + case TargetOpcode::G_MUL: case TargetOpcode::G_SDIV: case TargetOpcode::G_UDIV: case TargetOpcode::G_SREM: case TargetOpcode::G_UREM: - case TargetOpcode::G_CTLZ_ZERO_UNDEF: { + case TargetOpcode::G_CTLZ_ZERO_UNDEF: + case TargetOpcode::G_BITREVERSE: { Type *HLTy = IntegerType::get(Ctx, Size); auto Status = simpleLibcall(MI, MIRBuilder, Size, HLTy); if (Status != Legalized) return Status; break; } + case TargetOpcode::G_CTPOP: { + Type *ResTy = IntegerType::get(Ctx, Size); + unsigned OpSize = MRI.getType(MI.getOperand(1).getReg()).getSizeInBits(); + Type *OpTy = IntegerType::get(Ctx, OpSize); + RTLIB::Libcall Libcall = getRTLibDesc(MI.getOpcode(), OpSize); + createLibcall(MIRBuilder, Libcall, {MI.getOperand(0).getReg(), ResTy}, + {{MI.getOperand(1).getReg(), OpTy}}); + break; + } + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: { + Type *OpTy = IntegerType::get(Ctx, Size); + RTLIB::Libcall Libcall = getRTLibDesc(MI.getOpcode(), Size); + Register AmountReg = MI.getOperand(2).getReg(); + Type *AmountTy = + IntegerType::get(Ctx, MRI.getType(AmountReg).getSizeInBits()); + createLibcall(MIRBuilder, Libcall, {MI.getOperand(0).getReg(), OpTy}, + {{MI.getOperand(1).getReg(), OpTy}, {AmountReg, AmountTy}}); + break; + } + case TargetOpcode::G_INTRINSIC_TRUNC: + case TargetOpcode::G_INTRINSIC_ROUND: case TargetOpcode::G_FADD: case TargetOpcode::G_FSUB: case TargetOpcode::G_FMUL: @@ -668,7 +734,10 @@ LegalizerHelper::libcall(MachineInstr &MI) { case TargetOpcode::G_FMAXNUM: case TargetOpcode::G_FSQRT: case TargetOpcode::G_FRINT: - case TargetOpcode::G_FNEARBYINT: { + case TargetOpcode::G_FNEARBYINT: + case TargetOpcode::G_FCOPYSIGN: + case TargetOpcode::G_FNEG: + case TargetOpcode::G_FABS: { Type *HLTy = getFloatTypeForLLT(Ctx, LLTy); if (!HLTy || (Size != 32 && Size != 64 && Size != 128)) { LLVM_DEBUG(dbgs() << "No libcall available for size " << Size << ".\n"); @@ -729,23 +798,25 @@ LegalizerHelper::libcall(MachineInstr &MI) { LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, unsigned TypeIdx, LLT NarrowTy) { - uint64_t SizeOp0 = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); - uint64_t NarrowSize = NarrowTy.getSizeInBits(); + Register DstReg = MI.getOperand(0).getReg(); + LLT DstTy = MRI.getType(DstReg); + unsigned DstSize = DstTy.getSizeInBits(); + unsigned NarrowSize = NarrowTy.getSizeInBits(); - switch (MI.getOpcode()) { + switch (unsigned Opc = MI.getOpcode()) { default: return UnableToLegalize; case TargetOpcode::G_IMPLICIT_DEF: { Register DstReg = MI.getOperand(0).getReg(); LLT DstTy = MRI.getType(DstReg); - // If SizeOp0 is not an exact multiple of NarrowSize, emit + // If DstSize is not an exact multiple of NarrowSize, emit // G_ANYEXT(G_IMPLICIT_DEF). Cast result to vector if needed. // FIXME: Although this would also be legal for the general case, it causes // a lot of regressions in the emitted code (superfluous COPYs, artifact // combines not being hit). This seems to be a problem related to the // artifact combiner. - if (SizeOp0 % NarrowSize != 0) { + if (DstSize % NarrowSize != 0) { LLT ImplicitTy = NarrowTy; if (DstTy.isVector()) ImplicitTy = LLT::vector(DstTy.getNumElements(), ImplicitTy); @@ -757,7 +828,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, return Legalized; } - int NumParts = SizeOp0 / NarrowSize; + int NumParts = DstSize / NarrowSize; SmallVector DstRegs; for (int i = 0; i < NumParts; ++i) @@ -771,11 +842,8 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, return Legalized; } case TargetOpcode::G_CONSTANT: { - LLT Ty = MRI.getType(MI.getOperand(0).getReg()); const APInt &Val = MI.getOperand(1).getCImm()->getValue(); - unsigned TotalSize = Ty.getSizeInBits(); - unsigned NarrowSize = NarrowTy.getSizeInBits(); - int NumParts = TotalSize / NarrowSize; + int NumParts = DstSize / NarrowSize; SmallVector PartRegs; for (int I = 0; I != NumParts; ++I) { @@ -786,18 +854,15 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, } LLT LeftoverTy; - unsigned LeftoverBits = TotalSize - NumParts * NarrowSize; - SmallVector LeftoverRegs; - if (LeftoverBits != 0) { + Register LeftoverReg; + if (unsigned LeftoverBits = DstSize - NumParts * NarrowSize) { LeftoverTy = LLT::scalar(LeftoverBits); - auto K = MIRBuilder.buildConstant( - LeftoverTy, - Val.lshr(NumParts * NarrowSize).trunc(LeftoverBits)); - LeftoverRegs.push_back(K.getReg(0)); + auto C = MIRBuilder.buildConstant( + LeftoverTy, Val.lshr(NumParts * NarrowSize).trunc(LeftoverBits)); + LeftoverReg = C.getReg(0); } - insertParts(MI.getOperand(0).getReg(), - Ty, NarrowTy, PartRegs, LeftoverTy, LeftoverRegs); + insertParts(DstReg, DstTy, NarrowTy, PartRegs, LeftoverTy, LeftoverReg); MI.eraseFromParent(); return Legalized; @@ -810,88 +875,48 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, if (TypeIdx != 1) return UnableToLegalize; - uint64_t SizeOp1 = MRI.getType(MI.getOperand(1).getReg()).getSizeInBits(); - if (NarrowTy.getSizeInBits() * 2 != SizeOp1) { - LLVM_DEBUG(dbgs() << "Can't narrow trunc to type " << NarrowTy << "\n"); - return UnableToLegalize; - } - - auto Unmerge = MIRBuilder.buildUnmerge(NarrowTy, MI.getOperand(1)); - MIRBuilder.buildCopy(MI.getOperand(0), Unmerge.getReg(0)); - MI.eraseFromParent(); - return Legalized; - } - - case TargetOpcode::G_FREEZE: - return reduceOperationWidth(MI, TypeIdx, NarrowTy); + Register SrcReg = MI.getOperand(1).getReg(); + LLT SrcTy = MRI.getType(SrcReg); + assert(SrcTy.isScalar() && "Expected scalar"); - case TargetOpcode::G_ADD: { - // FIXME: add support for when SizeOp0 isn't an exact multiple of - // NarrowSize. - if (SizeOp0 % NarrowSize != 0) + SmallVector PartRegs; + LLT LeftoverTy; + Register LeftoverReg; + if (!extractParts(SrcReg, SrcTy, NarrowTy, PartRegs, LeftoverTy, + LeftoverReg)) return UnableToLegalize; - // Expand in terms of carry-setting/consuming G_ADDE instructions. - int NumParts = SizeOp0 / NarrowTy.getSizeInBits(); - - SmallVector Src1Regs, Src2Regs, DstRegs; - extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs); - extractParts(MI.getOperand(2).getReg(), NarrowTy, NumParts, Src2Regs); - - Register CarryIn; - for (int i = 0; i < NumParts; ++i) { - Register DstReg = MRI.createGenericVirtualRegister(NarrowTy); - Register CarryOut = MRI.createGenericVirtualRegister(LLT::scalar(1)); - - if (i == 0) - MIRBuilder.buildUAddo(DstReg, CarryOut, Src1Regs[i], Src2Regs[i]); - else { - MIRBuilder.buildUAdde(DstReg, CarryOut, Src1Regs[i], - Src2Regs[i], CarryIn); - } - DstRegs.push_back(DstReg); - CarryIn = CarryOut; + unsigned NumParts = DstSize / NarrowSize; + auto TruncRegs = makeArrayRef(PartRegs).take_front(NumParts); + if (DstSize > NumParts * NarrowSize) { + LeftoverTy = LLT::scalar(DstSize - NumParts * NarrowSize); + if (NumParts < PartRegs.size()) + LeftoverReg = PartRegs[NumParts]; + LeftoverReg = MIRBuilder.buildTrunc(LeftoverTy, LeftoverReg).getReg(0); + } else { + LeftoverTy = LLT(); + LeftoverReg = Register(); } - Register DstReg = MI.getOperand(0).getReg(); - if(MRI.getType(DstReg).isVector()) - MIRBuilder.buildBuildVector(DstReg, DstRegs); - else - MIRBuilder.buildMerge(DstReg, DstRegs); + + insertParts(DstReg, DstTy, NarrowTy, TruncRegs, LeftoverTy, LeftoverReg); MI.eraseFromParent(); return Legalized; } - case TargetOpcode::G_SUB: { - // FIXME: add support for when SizeOp0 isn't an exact multiple of - // NarrowSize. - if (SizeOp0 % NarrowSize != 0) - return UnableToLegalize; - - int NumParts = SizeOp0 / NarrowTy.getSizeInBits(); - - SmallVector Src1Regs, Src2Regs, DstRegs; - extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, Src1Regs); - extractParts(MI.getOperand(2).getReg(), NarrowTy, NumParts, Src2Regs); - - Register DstReg = MRI.createGenericVirtualRegister(NarrowTy); - Register BorrowOut = MRI.createGenericVirtualRegister(LLT::scalar(1)); - MIRBuilder.buildInstr(TargetOpcode::G_USUBO, {DstReg, BorrowOut}, - {Src1Regs[0], Src2Regs[0]}); - DstRegs.push_back(DstReg); - Register BorrowIn = BorrowOut; - for (int i = 1; i < NumParts; ++i) { - DstReg = MRI.createGenericVirtualRegister(NarrowTy); - BorrowOut = MRI.createGenericVirtualRegister(LLT::scalar(1)); - MIRBuilder.buildInstr(TargetOpcode::G_USUBE, {DstReg, BorrowOut}, - {Src1Regs[i], Src2Regs[i], BorrowIn}); + case TargetOpcode::G_FREEZE: + return narrowScalarUnary(MI, TypeIdx, NarrowTy); - DstRegs.push_back(DstReg); - BorrowIn = BorrowOut; - } - MIRBuilder.buildMerge(MI.getOperand(0), DstRegs); - MI.eraseFromParent(); - return Legalized; - } + case TargetOpcode::G_ADD: + case TargetOpcode::G_UADDO: + case TargetOpcode::G_UADDE: + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: + case TargetOpcode::G_SUB: + case TargetOpcode::G_USUBO: + case TargetOpcode::G_USUBE: + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: + return narrowScalarExtended(MI, TypeIdx, NarrowTy); case TargetOpcode::G_MUL: case TargetOpcode::G_UMULH: return narrowScalarMul(MI, NarrowTy); @@ -919,19 +944,17 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, } case TargetOpcode::G_ZEXTLOAD: case TargetOpcode::G_SEXTLOAD: { - bool ZExt = MI.getOpcode() == TargetOpcode::G_ZEXTLOAD; Register DstReg = MI.getOperand(0).getReg(); Register PtrReg = MI.getOperand(1).getReg(); Register TmpReg = MRI.createGenericVirtualRegister(NarrowTy); auto &MMO = **MI.memoperands_begin(); - if (MMO.getSizeInBits() == NarrowSize) { + if (MMO.getSizeInBits() == NarrowSize) MIRBuilder.buildLoad(TmpReg, PtrReg, MMO); - } else { - MIRBuilder.buildLoadInstr(MI.getOpcode(), TmpReg, PtrReg, MMO); - } + else + MIRBuilder.buildLoadInstr(Opc, TmpReg, PtrReg, MMO); - if (ZExt) + if (Opc == TargetOpcode::G_ZEXTLOAD) MIRBuilder.buildZExt(DstReg, TmpReg); else MIRBuilder.buildSExt(DstReg, TmpReg); @@ -947,7 +970,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, if (SrcTy.isVector()) return UnableToLegalize; - int NumParts = SizeOp0 / NarrowSize; + int NumParts = DstSize / NarrowSize; unsigned HandledSize = NumParts * NarrowTy.getSizeInBits(); unsigned LeftoverBits = SrcTy.getSizeInBits() - HandledSize; if (SrcTy.isVector() && LeftoverBits != 0) @@ -1024,28 +1047,35 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, Observer.changedInstr(MI); return Legalized; case TargetOpcode::G_PHI: { - unsigned NumParts = SizeOp0 / NarrowSize; - SmallVector DstRegs(NumParts); - SmallVector, 2> SrcRegs(MI.getNumOperands() / 2); - Observer.changingInstr(MI); + unsigned NumPredecessors = MI.getNumOperands() / 2; + LLT LeftoverTy; + SmallVector, 2> SrcRegs(NumPredecessors); + SmallVector LeftoverSrcRegs(NumPredecessors); + SmallVector DstRegs; + Register LeftoverDstReg; for (unsigned i = 1; i < MI.getNumOperands(); i += 2) { MachineBasicBlock &OpMBB = *MI.getOperand(i + 1).getMBB(); MIRBuilder.setInsertPt(OpMBB, OpMBB.getFirstTerminator()); - extractParts(MI.getOperand(i).getReg(), NarrowTy, NumParts, - SrcRegs[i / 2]); + extractParts(MI.getOperand(i).getReg(), DstTy, NarrowTy, SrcRegs[i / 2], + LeftoverTy, LeftoverSrcRegs[i / 2]); } MachineBasicBlock &MBB = *MI.getParent(); MIRBuilder.setInsertPt(MBB, MI); - for (unsigned i = 0; i < NumParts; ++i) { - DstRegs[i] = MRI.createGenericVirtualRegister(NarrowTy); - MachineInstrBuilder MIB = - MIRBuilder.buildInstr(TargetOpcode::G_PHI).addDef(DstRegs[i]); + DstRegs.resize(SrcRegs[0].size()); + for (unsigned i = 0; i < DstRegs.size(); ++i) { + auto MIB = MIRBuilder.buildInstr(TargetOpcode::G_PHI, {NarrowTy}, {}); + DstRegs[i] = MIB.getReg(0); for (unsigned j = 1; j < MI.getNumOperands(); j += 2) MIB.addUse(SrcRegs[j / 2][i]).add(MI.getOperand(j + 1)); } + if (LeftoverTy.isValid()) { + auto MIB = MIRBuilder.buildInstr(TargetOpcode::G_PHI, {LeftoverTy}, {}); + LeftoverDstReg = MIB.getReg(0); + for (unsigned j = 1; j < MI.getNumOperands(); j += 2) + MIB.addUse(LeftoverSrcRegs[j / 2]).add(MI.getOperand(j + 1)); + } MIRBuilder.setInsertPt(MBB, MBB.getFirstNonPHI()); - MIRBuilder.buildMerge(MI.getOperand(0), DstRegs); - Observer.changedInstr(MI); + insertParts(DstReg, DstTy, NarrowTy, DstRegs, LeftoverTy, LeftoverDstReg); MI.eraseFromParent(); return Legalized; } @@ -1054,7 +1084,7 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, if (TypeIdx != 2) return UnableToLegalize; - int OpIdx = MI.getOpcode() == TargetOpcode::G_EXTRACT_VECTOR_ELT ? 2 : 3; + int OpIdx = Opc == TargetOpcode::G_EXTRACT_VECTOR_ELT ? 2 : 3; Observer.changingInstr(MI); narrowScalarSrc(MI, NarrowTy, OpIdx); Observer.changedInstr(MI); @@ -1125,9 +1155,9 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, // component containing the extension point becomes a narrower SEXT_INREG. // Components above it are ashr'd from the component containing the // extension point. - if (SizeOp0 % NarrowSize != 0) + if (DstSize % NarrowSize != 0) return UnableToLegalize; - int NumParts = SizeOp0 / NarrowSize; + int NumParts = DstSize / NarrowSize; // List the registers where the destination will be scattered. SmallVector DstRegs; @@ -1184,12 +1214,12 @@ LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalar(MachineInstr &MI, } case TargetOpcode::G_BSWAP: case TargetOpcode::G_BITREVERSE: { - if (SizeOp0 % NarrowSize != 0) + if (DstSize % NarrowSize != 0) return UnableToLegalize; Observer.changingInstr(MI); SmallVector SrcRegs, DstRegs; - unsigned NumParts = SizeOp0 / NarrowSize; + unsigned NumParts = DstSize / NarrowSize; extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, SrcRegs); for (unsigned i = 0; i < NumParts; ++i) { @@ -2283,11 +2313,13 @@ LegalizerHelper::bitcast(MachineInstr &MI, unsigned TypeIdx, LLT CastTy) { } } -LegalizerHelper::LegalizeResult -LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty) { +LegalizerHelper::LegalizeResult LegalizerHelper::lower(MachineInstr &MI, + unsigned TypeIdx) { using namespace TargetOpcode; - switch(MI.getOpcode()) { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + + switch (MI.getOpcode()) { default: return UnableToLegalize; case TargetOpcode::G_BITCAST: @@ -2341,6 +2373,32 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT Ty) { } return Legalized; } + case TargetOpcode::G_SMULH: + case TargetOpcode::G_UMULH: { + Register ResReg = MI.getOperand(0).getReg(); + Register LHSReg = MI.getOperand(1).getReg(); + Register RHSReg = MI.getOperand(2).getReg(); + LLT FullTy = LLT::scalar(Ty.getSizeInBits() * 2); + + unsigned ExtOpc = MI.getOpcode() == TargetOpcode::G_SMULH + ? TargetOpcode::G_SEXT + : TargetOpcode::G_ZEXT; + auto ExtLHSI = MIRBuilder.buildInstr(ExtOpc, {FullTy}, {LHSReg}); + auto ExtRHSI = MIRBuilder.buildInstr(ExtOpc, {FullTy}, {RHSReg}); + auto MulI = MIRBuilder.buildMul(FullTy, ExtLHSI, ExtRHSI); + auto ShiftAmtI = MIRBuilder.buildConstant(FullTy, Ty.getSizeInBits()); + auto ShiftI = MIRBuilder.buildLShr(FullTy, MulI, ShiftAmtI); + MIRBuilder.buildTrunc(ResReg, ShiftI); + + MI.eraseFromParent(); + return Legalized; + } + case TargetOpcode::G_UADDSAT: + case TargetOpcode::G_USUBSAT: + return lowerUADDSAT_USUBSAT(MI); + case TargetOpcode::G_SADDSAT: + case TargetOpcode::G_SSUBSAT: + return lowerSADDSAT_SSUBSAT(MI); case TargetOpcode::G_FNEG: { // TODO: Handle vector types once we are able to // represent them. @@ -2708,25 +2766,26 @@ LegalizerHelper::fewerElementsVectorMultiEltType( // All of the operands need to have the same number of elements, so if we can // determine a type breakdown for the result type, we can for all of the // source types. - int NumParts = getNarrowTypeBreakDown(DstTy, NarrowTy0, LeftoverTy0).first; - if (NumParts < 0) + unsigned NumParts = getNarrowTypeBreakDown(DstTy, NarrowTy0, LeftoverTy0); + if (!NumParts) return UnableToLegalize; SmallVector NewInsts; - SmallVector DstRegs, LeftoverDstRegs; - SmallVector PartRegs, LeftoverRegs; + SmallVector DstRegs, PartRegs; + Register LeftoverDstReg; for (unsigned I = 1, E = MI.getNumOperands(); I != E; ++I) { Register SrcReg = MI.getOperand(I).getReg(); LLT SrcTyI = MRI.getType(SrcReg); LLT NarrowTyI = LLT::scalarOrVector(NewNumElts, SrcTyI.getScalarType()); LLT LeftoverTyI; + Register LeftoverReg; // Split this operand into the requested typed registers, and any leftover // required to reproduce the original type. - if (!extractParts(SrcReg, SrcTyI, NarrowTyI, LeftoverTyI, PartRegs, - LeftoverRegs)) + if (!extractParts(SrcReg, SrcTyI, NarrowTyI, PartRegs, LeftoverTyI, + LeftoverReg)) return UnableToLegalize; if (I == 1) { @@ -2740,15 +2799,14 @@ LegalizerHelper::fewerElementsVectorMultiEltType( DstRegs.push_back(PartDstReg); } - for (Register LeftoverReg : LeftoverRegs) { - Register PartDstReg = MRI.createGenericVirtualRegister(LeftoverTy0); + if (LeftoverReg.isValid()) { + LeftoverDstReg = MRI.createGenericVirtualRegister(LeftoverTy0); NewInsts.push_back(MIRBuilder.buildInstrNoInsert(MI.getOpcode()) - .addDef(PartDstReg) + .addDef(LeftoverDstReg) .addUse(LeftoverReg)); - LeftoverDstRegs.push_back(PartDstReg); } } else { - assert(NewInsts.size() == PartRegs.size() + LeftoverRegs.size()); + assert(NewInsts.size() == PartRegs.size() + LeftoverReg.isValid()); // Add the newly created operand splits to the existing instructions. The // odd-sized pieces are ordered after the requested NarrowTyArg sized @@ -2756,19 +2814,18 @@ LegalizerHelper::fewerElementsVectorMultiEltType( unsigned InstCount = 0; for (unsigned J = 0, JE = PartRegs.size(); J != JE; ++J) NewInsts[InstCount++].addUse(PartRegs[J]); - for (unsigned J = 0, JE = LeftoverRegs.size(); J != JE; ++J) - NewInsts[InstCount++].addUse(LeftoverRegs[J]); + if (LeftoverReg.isValid()) + NewInsts[InstCount++].addUse(LeftoverReg); } PartRegs.clear(); - LeftoverRegs.clear(); } // Insert the newly built operations and rebuild the result register. for (auto &MIB : NewInsts) MIRBuilder.insertInstr(MIB); - insertParts(DstReg, DstTy, NarrowTy0, DstRegs, LeftoverTy0, LeftoverDstRegs); + insertParts(DstReg, DstTy, NarrowTy0, DstRegs, LeftoverTy0, LeftoverDstReg); MI.eraseFromParent(); return Legalized; @@ -2971,19 +3028,17 @@ LegalizerHelper::fewerElementsVectorPhi(MachineInstr &MI, unsigned TypeIdx, // All of the operands need to have the same number of elements, so if we can // determine a type breakdown for the result type, we can for all of the // source types. - int NumParts, NumLeftover; - std::tie(NumParts, NumLeftover) - = getNarrowTypeBreakDown(PhiTy, NarrowTy, LeftoverTy); - if (NumParts < 0) + unsigned NumParts = getNarrowTypeBreakDown(PhiTy, NarrowTy, LeftoverTy); + if (!NumParts) return UnableToLegalize; - SmallVector DstRegs, LeftoverDstRegs; + SmallVector DstRegs; + Register LeftoverDstReg; SmallVector NewInsts; - const int TotalNumParts = NumParts + NumLeftover; - // Insert the new phis in the result block first. - for (int I = 0; I != TotalNumParts; ++I) { + unsigned TotalNumParts = NumParts + LeftoverTy.isValid(); + for (unsigned I = 0; I != TotalNumParts; ++I) { LLT Ty = I < NumParts ? NarrowTy : LeftoverTy; Register PartDstReg = MRI.createGenericVirtualRegister(Ty); NewInsts.push_back(MIRBuilder.buildInstr(TargetOpcode::G_PHI) @@ -2991,35 +3046,33 @@ LegalizerHelper::fewerElementsVectorPhi(MachineInstr &MI, unsigned TypeIdx, if (I < NumParts) DstRegs.push_back(PartDstReg); else - LeftoverDstRegs.push_back(PartDstReg); + LeftoverDstReg = PartDstReg; } MachineBasicBlock *MBB = MI.getParent(); MIRBuilder.setInsertPt(*MBB, MBB->getFirstNonPHI()); - insertParts(DstReg, PhiTy, NarrowTy, DstRegs, LeftoverTy, LeftoverDstRegs); + insertParts(DstReg, PhiTy, NarrowTy, DstRegs, LeftoverTy, LeftoverDstReg); - SmallVector PartRegs, LeftoverRegs; + SmallVector PartRegs; + Register LeftoverReg; // Insert code to extract the incoming values in each predecessor block. for (unsigned I = 1, E = MI.getNumOperands(); I != E; I += 2) { PartRegs.clear(); - LeftoverRegs.clear(); Register SrcReg = MI.getOperand(I).getReg(); MachineBasicBlock &OpMBB = *MI.getOperand(I + 1).getMBB(); MIRBuilder.setInsertPt(OpMBB, OpMBB.getFirstTerminator()); - LLT Unused; - if (!extractParts(SrcReg, PhiTy, NarrowTy, Unused, PartRegs, - LeftoverRegs)) + if (!extractParts(SrcReg, PhiTy, NarrowTy, PartRegs, LeftoverTy, LeftoverReg)) return UnableToLegalize; // Add the newly created operand splits to the existing instructions. The // odd-sized pieces are ordered after the requested NarrowTyArg sized // pieces. - for (int J = 0; J != TotalNumParts; ++J) { + for (unsigned J = 0; J != TotalNumParts; ++J) { MachineInstrBuilder MIB = NewInsts[J]; - MIB.addUse(J < NumParts ? PartRegs[J] : LeftoverRegs[J - NumParts]); + MIB.addUse(J < NumParts ? PartRegs[J] : LeftoverReg); MIB.addMBB(&OpMBB); } } @@ -3153,21 +3206,17 @@ LegalizerHelper::reduceLoadStoreWidth(MachineInstr &MI, unsigned TypeIdx, return UnableToLegalize; } - int NumParts = -1; - int NumLeftover = -1; + unsigned NumParts = 0; LLT LeftoverTy; - SmallVector NarrowRegs, NarrowLeftoverRegs; + SmallVector NarrowRegs; + Register NarrowLeftoverReg; if (IsLoad) { - std::tie(NumParts, NumLeftover) = getNarrowTypeBreakDown(ValTy, NarrowTy, LeftoverTy); - } else { - if (extractParts(ValReg, ValTy, NarrowTy, LeftoverTy, NarrowRegs, - NarrowLeftoverRegs)) { - NumParts = NarrowRegs.size(); - NumLeftover = NarrowLeftoverRegs.size(); - } - } - - if (NumParts == -1) + NumParts = getNarrowTypeBreakDown(ValTy, NarrowTy, LeftoverTy); + NarrowRegs.resize(NumParts); + } else if (extractParts(ValReg, ValTy, NarrowTy, NarrowRegs, LeftoverTy, + NarrowLeftoverReg)) + NumParts = NarrowRegs.size(); + if (!NumParts) return UnableToLegalize; const LLT OffsetTy = LLT::scalar(MRI.getType(AddrReg).getScalarSizeInBits()); @@ -3178,7 +3227,7 @@ LegalizerHelper::reduceLoadStoreWidth(MachineInstr &MI, unsigned TypeIdx, // is a load, return the new registers in ValRegs. For a store, each elements // of ValRegs should be PartTy. Returns the next offset that needs to be // handled. - auto splitTypePieces = [=](LLT PartTy, SmallVectorImpl &ValRegs, + auto splitTypePieces = [=](LLT PartTy, MutableArrayRef ValRegs, unsigned Offset) -> unsigned { MachineFunction &MF = MIRBuilder.getMF(); unsigned PartSize = PartTy.getSizeInBits(); @@ -3195,7 +3244,7 @@ LegalizerHelper::reduceLoadStoreWidth(MachineInstr &MI, unsigned TypeIdx, if (IsLoad) { Register Dst = MRI.createGenericVirtualRegister(PartTy); - ValRegs.push_back(Dst); + ValRegs[Idx] = Dst; MIRBuilder.buildLoad(Dst, NewAddrReg, *NewMMO); } else { MIRBuilder.buildStore(ValRegs[Idx], NewAddrReg, *NewMMO); @@ -3209,12 +3258,11 @@ LegalizerHelper::reduceLoadStoreWidth(MachineInstr &MI, unsigned TypeIdx, // Handle the rest of the register if this isn't an even type breakdown. if (LeftoverTy.isValid()) - splitTypePieces(LeftoverTy, NarrowLeftoverRegs, HandledOffset); + splitTypePieces(LeftoverTy, NarrowLeftoverReg, HandledOffset); - if (IsLoad) { - insertParts(ValReg, ValTy, NarrowTy, NarrowRegs, - LeftoverTy, NarrowLeftoverRegs); - } + if (IsLoad) + insertParts(ValReg, ValTy, NarrowTy, NarrowRegs, LeftoverTy, + NarrowLeftoverReg); MI.eraseFromParent(); return Legalized; @@ -3945,23 +3993,23 @@ LegalizerHelper::narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, if (TypeIdx != 0) return UnableToLegalize; - uint64_t SizeOp0 = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); - uint64_t NarrowSize = NarrowTy.getSizeInBits(); + unsigned DstSize = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits(); + unsigned NarrowSize = NarrowTy.getSizeInBits(); - // FIXME: add support for when SizeOp0 isn't an exact multiple of + // FIXME: add support for when DstSize isn't an exact multiple of // NarrowSize. - if (SizeOp0 % NarrowSize != 0) + if (DstSize % NarrowSize != 0) return UnableToLegalize; - int NumParts = SizeOp0 / NarrowSize; + int NumParts = DstSize / NarrowSize; SmallVector SrcRegs, DstRegs; SmallVector Indexes; extractParts(MI.getOperand(1).getReg(), NarrowTy, NumParts, SrcRegs); Register OpReg = MI.getOperand(2).getReg(); - uint64_t OpStart = MI.getOperand(3).getImm(); - uint64_t OpSize = MRI.getType(OpReg).getSizeInBits(); + unsigned OpStart = MI.getOperand(3).getImm(); + unsigned OpSize = MRI.getType(OpReg).getSizeInBits(); for (int i = 0; i < NumParts; ++i) { unsigned DstStart = i * NarrowSize; @@ -3978,8 +4026,8 @@ LegalizerHelper::narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, // OpSegStart is where this destination segment would start in OpReg if it // extended infinitely in both directions. - int64_t ExtractOffset, InsertOffset; - uint64_t SegSize; + unsigned ExtractOffset, InsertOffset; + unsigned SegSize; if (OpStart < DstStart) { InsertOffset = 0; ExtractOffset = DstStart - OpStart; @@ -4013,6 +4061,37 @@ LegalizerHelper::narrowScalarInsert(MachineInstr &MI, unsigned TypeIdx, return Legalized; } +LegalizerHelper::LegalizeResult +LegalizerHelper::narrowScalarUnary(MachineInstr &MI, unsigned TypeIdx, + LLT NarrowTy) { + Register DstReg = MI.getOperand(0).getReg(); + LLT DstTy = MRI.getType(DstReg); + + assert(MI.getNumOperands() == 2 && TypeIdx == 0); + + SmallVector DstRegs, SrcRegs; + LLT LeftoverTy; + Register DstLeftoverReg, SrcLeftoverReg; + if (!extractParts(MI.getOperand(1).getReg(), DstTy, NarrowTy, SrcRegs, + LeftoverTy, SrcLeftoverReg)) + return UnableToLegalize; + + for (unsigned I = 0, E = SrcRegs.size(); I != E; ++I) + DstRegs.push_back( + MIRBuilder.buildInstr(MI.getOpcode(), {NarrowTy}, {SrcRegs[I]}) + .getReg(0)); + + if (LeftoverTy.isValid()) + DstLeftoverReg = + MIRBuilder.buildInstr(MI.getOpcode(), {LeftoverTy}, {SrcLeftoverReg}) + .getReg(0); + + insertParts(DstReg, DstTy, NarrowTy, DstRegs, LeftoverTy, DstLeftoverReg); + + MI.eraseFromParent(); + return Legalized; +} + LegalizerHelper::LegalizeResult LegalizerHelper::narrowScalarBasic(MachineInstr &MI, unsigned TypeIdx, LLT NarrowTy) { @@ -4021,35 +4100,127 @@ LegalizerHelper::narrowScalarBasic(MachineInstr &MI, unsigned TypeIdx, assert(MI.getNumOperands() == 3 && TypeIdx == 0); - SmallVector DstRegs, DstLeftoverRegs; - SmallVector Src0Regs, Src0LeftoverRegs; - SmallVector Src1Regs, Src1LeftoverRegs; + SmallVector DstRegs, Src1Regs, Src2Regs; LLT LeftoverTy; - if (!extractParts(MI.getOperand(1).getReg(), DstTy, NarrowTy, LeftoverTy, - Src0Regs, Src0LeftoverRegs)) + Register DstLeftoverReg, Src1LeftoverReg, Src2LeftoverReg; + if (!extractParts(MI.getOperand(1).getReg(), DstTy, NarrowTy, Src1Regs, + LeftoverTy, Src1LeftoverReg) || + !extractParts(MI.getOperand(2).getReg(), DstTy, NarrowTy, Src2Regs, + LeftoverTy, Src2LeftoverReg)) return UnableToLegalize; - LLT Unused; - if (!extractParts(MI.getOperand(2).getReg(), DstTy, NarrowTy, Unused, - Src1Regs, Src1LeftoverRegs)) - llvm_unreachable("inconsistent extractParts result"); + for (unsigned I = 0, E = Src1Regs.size(); I != E; ++I) + DstRegs.push_back( + MIRBuilder.buildInstr(MI.getOpcode(), {NarrowTy}, + {Src1Regs[I], Src2Regs[I]}).getReg(0)); + + if (LeftoverTy.isValid()) + DstLeftoverReg = + MIRBuilder.buildInstr(MI.getOpcode(), {LeftoverTy}, + {Src1LeftoverReg, Src2LeftoverReg}).getReg(0); - for (unsigned I = 0, E = Src1Regs.size(); I != E; ++I) { - auto Inst = MIRBuilder.buildInstr(MI.getOpcode(), {NarrowTy}, - {Src0Regs[I], Src1Regs[I]}); - DstRegs.push_back(Inst.getReg(0)); + insertParts(DstReg, DstTy, NarrowTy, DstRegs, LeftoverTy, DstLeftoverReg); + + MI.eraseFromParent(); + return Legalized; +} + +LegalizerHelper::LegalizeResult +LegalizerHelper::narrowScalarExtended(MachineInstr &MI, unsigned TypeIdx, + LLT NarrowTy) { + if (TypeIdx != 0) + return UnableToLegalize; + + unsigned Opc = MI.getOpcode(), OverflowOpc, ExtendOpc, FinalExtendOpc; + Register DstReg, ExtendOutReg, Src1Reg, Src2Reg, ExtendInReg; + + switch (Opc) { + case TargetOpcode::G_ADD: + case TargetOpcode::G_UADDO: + case TargetOpcode::G_UADDE: + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: + OverflowOpc = TargetOpcode::G_UADDO; + ExtendOpc = TargetOpcode::G_UADDE; + break; + case TargetOpcode::G_SUB: + case TargetOpcode::G_USUBO: + case TargetOpcode::G_USUBE: + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: + OverflowOpc = TargetOpcode::G_USUBO; + ExtendOpc = TargetOpcode::G_USUBE; + break; + default: + llvm_unreachable("Unexpected opcode"); + } + switch (Opc) { + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: + FinalExtendOpc = TargetOpcode::G_SADDE; + break; + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: + FinalExtendOpc = TargetOpcode::G_SSUBE; + break; + default: + FinalExtendOpc = ExtendOpc; + break; } - for (unsigned I = 0, E = Src1LeftoverRegs.size(); I != E; ++I) { - auto Inst = MIRBuilder.buildInstr( - MI.getOpcode(), - {LeftoverTy}, {Src0LeftoverRegs[I], Src1LeftoverRegs[I]}); - DstLeftoverRegs.push_back(Inst.getReg(0)); + { + unsigned I = 0; + DstReg = MI.getOperand(I++).getReg(); + if (Opc != TargetOpcode::G_ADD && Opc != TargetOpcode::G_SUB) + ExtendOutReg = MI.getOperand(I++).getReg(); + Src1Reg = MI.getOperand(I++).getReg(); + Src2Reg = MI.getOperand(I++).getReg(); + if (Opc == TargetOpcode::G_UADDE || Opc == TargetOpcode::G_USUBE) + ExtendInReg = MI.getOperand(I++).getReg(); } + LLT OpTy = MRI.getType(DstReg); + + LLT LeftoverTy; + SmallVector DstRegs, Src1Regs, Src2Regs; + Register DstLeftoverReg, Src1LeftoverReg, Src2LeftoverReg; - insertParts(DstReg, DstTy, NarrowTy, DstRegs, - LeftoverTy, DstLeftoverRegs); + if (!extractParts(Src1Reg, OpTy, NarrowTy, Src1Regs, LeftoverTy, + Src1LeftoverReg) || + !extractParts(Src2Reg, OpTy, NarrowTy, Src2Regs, LeftoverTy, + Src2LeftoverReg)) + return UnableToLegalize; + unsigned NumParts = Src1Regs.size(); + unsigned TotalParts = NumParts + LeftoverTy.isValid(); + assert(TotalParts > 1 && "Should be more that one part"); + for (unsigned I = 0; I != TotalParts; ++I) { + bool Leftover = I == NumParts; + LLT PartTy = Leftover ? LeftoverTy : NarrowTy; + Register PartDstReg = MRI.createGenericVirtualRegister(PartTy); + Register PartExtendOutReg; + if (I == TotalParts - 1) { + PartExtendOutReg = ExtendOutReg; + // Restore original signedness for most significant opcode. + ExtendOpc = FinalExtendOpc; + } + if (!PartExtendOutReg.isValid()) + PartExtendOutReg = MRI.createGenericVirtualRegister(LLT::scalar(1)); + Register PartSrc1Reg = Leftover ? Src1LeftoverReg : Src1Regs[I]; + Register PartSrc2Reg = Leftover ? Src2LeftoverReg : Src2Regs[I]; + if (ExtendInReg.isValid()) + MIRBuilder.buildInstr(ExtendOpc, {PartDstReg, PartExtendOutReg}, + {PartSrc1Reg, PartSrc2Reg, ExtendInReg}); + else + MIRBuilder.buildInstr(OverflowOpc, {PartDstReg, PartExtendOutReg}, + {PartSrc1Reg, PartSrc2Reg}); + if (Leftover) + DstLeftoverReg = PartDstReg; + else + DstRegs.push_back(PartDstReg); + ExtendInReg = PartExtendOutReg; + } + + insertParts(DstReg, OpTy, NarrowTy, DstRegs, LeftoverTy, DstLeftoverReg); MI.eraseFromParent(); return Legalized; } @@ -4060,18 +4231,61 @@ LegalizerHelper::narrowScalarExt(MachineInstr &MI, unsigned TypeIdx, if (TypeIdx != 0) return UnableToLegalize; + unsigned Opc = MI.getOpcode(); Register DstReg = MI.getOperand(0).getReg(); Register SrcReg = MI.getOperand(1).getReg(); LLT DstTy = MRI.getType(DstReg); - if (DstTy.isVector()) + LLT SrcTy = MRI.getType(SrcReg); + if (DstTy.isVector() || SrcTy.isVector()) return UnableToLegalize; - SmallVector Parts; - LLT GCDTy = extractGCDType(Parts, DstTy, NarrowTy, SrcReg); - LLT LCMTy = buildLCMMergePieces(DstTy, NarrowTy, GCDTy, Parts, MI.getOpcode()); - buildWidenedRemergeToDst(DstReg, LCMTy, Parts); + unsigned DstSize = DstTy.getSizeInBits(); + unsigned SrcSize = SrcTy.getSizeInBits(); + unsigned NarrowSize = NarrowTy.getSizeInBits(); + SmallVector PartRegs; + LLT LeftoverTy; + Register LeftoverReg; + if (SrcSize <= NarrowSize) + PartRegs.push_back( + MIRBuilder.buildExtOrTrunc(Opc, NarrowTy, SrcReg).getReg(0)); + else if (!extractParts(SrcReg, SrcTy, NarrowTy, PartRegs, LeftoverTy, + LeftoverReg)) + return UnableToLegalize; + + unsigned NumParts = DstSize / NarrowSize; + assert(NumParts >= PartRegs.size() && "Expected sext to increase size"); + if (LeftoverTy.isValid() && NumParts == PartRegs.size()) { + LeftoverTy = LLT::scalar(DstSize - NarrowSize * NumParts); + LeftoverReg = + MIRBuilder.buildInstr(Opc, {LeftoverTy}, {LeftoverReg}).getReg(0); + } else { + if (LeftoverTy.isValid()) + PartRegs.push_back( + MIRBuilder.buildInstr(Opc, {NarrowTy}, {LeftoverReg}).getReg(0)); + + Register ExtReg; + if (Opc == TargetOpcode::G_SEXT) { + auto ShiftAmt = MIRBuilder.buildConstant(NarrowTy, NarrowSize - 1); + ExtReg = + MIRBuilder.buildAShr(NarrowTy, PartRegs.back(), ShiftAmt).getReg(0); + } else if (Opc == TargetOpcode::G_ZEXT) + ExtReg = MIRBuilder.buildConstant(NarrowTy, 0).getReg(0); + else + ExtReg = MIRBuilder.buildUndef(NarrowTy).getReg(0); + while (NumParts > PartRegs.size()) + PartRegs.push_back(ExtReg); + if (DstSize > NumParts * NarrowSize) { + LeftoverTy = LLT::scalar(DstSize - NarrowSize * NumParts); + LeftoverReg = MIRBuilder.buildTrunc(LeftoverTy, ExtReg).getReg(0); + } else { + LeftoverTy = LLT(); + LeftoverReg = Register(); + } + } + + insertParts(DstReg, DstTy, NarrowTy, PartRegs, LeftoverTy, LeftoverReg); MI.eraseFromParent(); return Legalized; } @@ -4090,33 +4304,27 @@ LegalizerHelper::narrowScalarSelect(MachineInstr &MI, unsigned TypeIdx, Register DstReg = MI.getOperand(0).getReg(); LLT DstTy = MRI.getType(DstReg); - SmallVector DstRegs, DstLeftoverRegs; - SmallVector Src1Regs, Src1LeftoverRegs; - SmallVector Src2Regs, Src2LeftoverRegs; + SmallVector DstRegs, Src1Regs, Src2Regs; + Register DstLeftoverReg, Src1LeftoverReg, Src2LeftoverReg; LLT LeftoverTy; - if (!extractParts(MI.getOperand(2).getReg(), DstTy, NarrowTy, LeftoverTy, - Src1Regs, Src1LeftoverRegs)) + if (!extractParts(MI.getOperand(2).getReg(), DstTy, NarrowTy, Src1Regs, + LeftoverTy, Src1LeftoverReg) || + !extractParts(MI.getOperand(3).getReg(), DstTy, NarrowTy, Src2Regs, + LeftoverTy, Src2LeftoverReg)) return UnableToLegalize; - LLT Unused; - if (!extractParts(MI.getOperand(3).getReg(), DstTy, NarrowTy, Unused, - Src2Regs, Src2LeftoverRegs)) - llvm_unreachable("inconsistent extractParts result"); + for (unsigned I = 0, E = Src1Regs.size(); I != E; ++I) + DstRegs.push_back( + MIRBuilder.buildSelect(NarrowTy, CondReg, Src1Regs[I], Src2Regs[I]) + .getReg(0)); - for (unsigned I = 0, E = Src1Regs.size(); I != E; ++I) { - auto Select = MIRBuilder.buildSelect(NarrowTy, - CondReg, Src1Regs[I], Src2Regs[I]); - DstRegs.push_back(Select.getReg(0)); - } - - for (unsigned I = 0, E = Src1LeftoverRegs.size(); I != E; ++I) { - auto Select = MIRBuilder.buildSelect( - LeftoverTy, CondReg, Src1LeftoverRegs[I], Src2LeftoverRegs[I]); - DstLeftoverRegs.push_back(Select.getReg(0)); - } + if (LeftoverTy.isValid()) + DstLeftoverReg = + MIRBuilder + .buildSelect(LeftoverTy, CondReg, Src1LeftoverReg, Src2LeftoverReg) + .getReg(0); - insertParts(DstReg, DstTy, NarrowTy, DstRegs, - LeftoverTy, DstLeftoverRegs); + insertParts(DstReg, DstTy, NarrowTy, DstRegs, LeftoverTy, DstLeftoverReg); MI.eraseFromParent(); return Legalized; @@ -4322,8 +4530,10 @@ LegalizerHelper::lowerBitCount(MachineInstr &MI, unsigned TypeIdx, LLT Ty) { MI.eraseFromParent(); return Legalized; } + Observer.changingInstr(MI); MI.setDesc(TII.get(TargetOpcode::G_CTPOP)); MI.getOperand(1).setReg(MIBTmp.getReg(0)); + Observer.changedInstr(MI); return Legalized; } case TargetOpcode::G_CTPOP: { @@ -5217,6 +5427,47 @@ LegalizerHelper::lowerSADDO_SSUBO(MachineInstr &MI) { return Legalized; } +LegalizerHelper::LegalizeResult +LegalizerHelper::lowerUADDSAT_USUBSAT(MachineInstr &MI) { + Register Dst = MI.getOperand(0).getReg(); + Register LHS = MI.getOperand(1).getReg(); + Register RHS = MI.getOperand(2).getReg(); + const bool IsAdd = MI.getOpcode() == TargetOpcode::G_UADDSAT; + + LLT Ty = MRI.getType(Dst); + unsigned TySize = Ty.getScalarSizeInBits(); + auto Sat = MIRBuilder.buildConstant(Ty, IsAdd ? APInt::getMaxValue(TySize) + : APInt::getMinValue(TySize)); + auto Res = MIRBuilder.buildInstr(IsAdd ? TargetOpcode::G_UADDO + : TargetOpcode::G_USUBO, + {Ty, LLT::scalar(1)}, {LHS, RHS}); + MIRBuilder.buildSelect(Dst, Res.getReg(1), Sat, Res); + MI.eraseFromParent(); + return Legalized; +} + +LegalizerHelper::LegalizeResult +LegalizerHelper::lowerSADDSAT_SSUBSAT(MachineInstr &MI) { + Register Dst = MI.getOperand(0).getReg(); + Register LHS = MI.getOperand(1).getReg(); + Register RHS = MI.getOperand(2).getReg(); + const bool IsAdd = MI.getOpcode() == TargetOpcode::G_SADDSAT; + + LLT Ty = MRI.getType(Dst); + unsigned TySize = Ty.getScalarSizeInBits(); + auto Zero = MIRBuilder.buildConstant(Ty, APInt::getMinValue(TySize)); + auto Min = MIRBuilder.buildConstant(Ty, APInt::getSignedMinValue(TySize)); + auto Max = MIRBuilder.buildConstant(Ty, APInt::getSignedMaxValue(TySize)); + auto Res = MIRBuilder.buildInstr(IsAdd ? TargetOpcode::G_SADDO + : TargetOpcode::G_SSUBO, + {Ty, LLT::scalar(1)}, {LHS, RHS}); + auto Neg = MIRBuilder.buildICmp(CmpInst::ICMP_SLT, LLT::scalar(1), Res, Zero); + auto Sat = MIRBuilder.buildSelect(Ty, Neg, Max, Min); + MIRBuilder.buildSelect(Dst, Res.getReg(1), Sat, Res); + MI.eraseFromParent(); + return Legalized; +} + LegalizerHelper::LegalizeResult LegalizerHelper::lowerBswap(MachineInstr &MI) { Register Dst = MI.getOperand(0).getReg(); diff --git a/llvm/lib/CodeGen/LiveRegUnits.cpp b/llvm/lib/CodeGen/LiveRegUnits.cpp index b2731aa0e7dbca..2406743b27b35a 100644 --- a/llvm/lib/CodeGen/LiveRegUnits.cpp +++ b/llvm/lib/CodeGen/LiveRegUnits.cpp @@ -41,6 +41,24 @@ void LiveRegUnits::addRegsInMask(const uint32_t *RegMask) { } } +void LiveRegUnits::stepForward(const MachineInstr &MI) { + // Remove killed registers and regmask kills from the set. + for (const MachineOperand &MOP : phys_regs_and_masks(MI)) { + if (MOP.isRegMask()) { + removeRegsNotPreserved(MOP.getRegMask()); + continue; + } + + if (MOP.isKill()) + removeReg(MOP.getReg()); + } + + // Add live defs to the set. + for (const MachineOperand &MOP : phys_regs_and_masks(MI)) + if (MOP.isReg() && MOP.isDef() && !MOP.isDead()) + addReg(MOP.getReg()); +} + void LiveRegUnits::stepBackward(const MachineInstr &MI) { // Remove defined registers and regmask kills from the set. for (const MachineOperand &MOP : phys_regs_and_masks(MI)) { diff --git a/llvm/lib/CodeGen/MachineOperand.cpp b/llvm/lib/CodeGen/MachineOperand.cpp index 2b4fd654e46c08..ea222d411a1eb9 100644 --- a/llvm/lib/CodeGen/MachineOperand.cpp +++ b/llvm/lib/CodeGen/MachineOperand.cpp @@ -162,6 +162,15 @@ void MachineOperand::ChangeToImmediate(int64_t ImmVal) { Contents.ImmVal = ImmVal; } +void MachineOperand::ChangeToCImmediate(const ConstantInt *CI) { + assert((!isReg() || !isTied()) && "Cannot change a tied operand into an imm"); + + removeRegFromUses(); + + OpKind = MO_CImmediate; + Contents.CI = CI; +} + void MachineOperand::ChangeToFPImmediate(const ConstantFP *FPImm) { assert((!isReg() || !isTied()) && "Cannot change a tied operand into an imm"); diff --git a/llvm/lib/CodeGen/TargetLoweringBase.cpp b/llvm/lib/CodeGen/TargetLoweringBase.cpp index c8220928af935b..6a41476da09c5a 100644 --- a/llvm/lib/CodeGen/TargetLoweringBase.cpp +++ b/llvm/lib/CodeGen/TargetLoweringBase.cpp @@ -728,6 +728,20 @@ void TargetLoweringBase::initActions() { setOperationAction(ISD::VECREDUCE_UMIN, VT, Expand); setOperationAction(ISD::VECREDUCE_FMAX, VT, Expand); setOperationAction(ISD::VECREDUCE_FMIN, VT, Expand); + + // For most targets, non power of 2 types default to expand. + if (VT.isScalarInteger() && !VT.isPow2Size()) { + std::fill(std::begin(OpActions[(unsigned)VT.SimpleTy]), + std::end(OpActions[(unsigned)VT.SimpleTy]), Expand); + for (MVT OtherVT : MVT::all_valuetypes()) { + for (auto VTs : {std::make_pair(VT, OtherVT), + std::make_pair(OtherVT, VT)}) { + for (unsigned ExtType : {ISD::EXTLOAD, ISD::ZEXTLOAD, ISD::SEXTLOAD}) + setLoadExtAction(ExtType, VTs.first, VTs.second, Expand); + setTruncStoreAction(VTs.first, VTs.second, Expand); + } + } + } } // Most targets ignore the @llvm.prefetch intrinsic. @@ -816,10 +830,8 @@ TargetLoweringBase::getTypeConversion(LLVMContext &Context, EVT VT) const { MVT NVT = TransformToType[SVT.SimpleTy]; LegalizeTypeAction LA = ValueTypeActions.getTypeAction(SVT); - assert((LA == TypeLegal || LA == TypeSoftenFloat || - LA == TypeSoftPromoteHalf || - (NVT.isVector() || - ValueTypeActions.getTypeAction(NVT) != TypePromoteInteger)) && + assert(((LA != TypePromoteInteger && LA != TypeExpandInteger) || + ValueTypeActions.getTypeAction(NVT) != TypePromoteInteger) && "Promote may not follow Expand or Promote"); if (LA == TypeSplitVector) @@ -1171,23 +1183,33 @@ void TargetLoweringBase::computeRegisterProperties( unsigned LargestIntReg = MVT::LAST_INTEGER_VALUETYPE; for (; RegClassForVT[LargestIntReg] == nullptr; --LargestIntReg) assert(LargestIntReg != MVT::i1 && "No integer registers defined!"); + MVT LargestIntVT = (MVT::SimpleValueType)LargestIntReg; // Every integer value type larger than this largest register takes twice as // many registers to represent as the previous ValueType. - for (unsigned ExpandedReg = LargestIntReg + 1; + for (unsigned HalfReg = LargestIntReg, ExpandedReg = LargestIntReg + 1; ExpandedReg <= MVT::LAST_INTEGER_VALUETYPE; ++ExpandedReg) { - NumRegistersForVT[ExpandedReg] = 2*NumRegistersForVT[ExpandedReg-1]; - RegisterTypeForVT[ExpandedReg] = (MVT::SimpleValueType)LargestIntReg; - TransformToType[ExpandedReg] = (MVT::SimpleValueType)(ExpandedReg - 1); - ValueTypeActions.setTypeAction((MVT::SimpleValueType)ExpandedReg, - TypeExpandInteger); + MVT ExpandedVT = (MVT::SimpleValueType)ExpandedReg; + NumRegistersForVT[ExpandedReg] = ExpandedVT.getNumParts(LargestIntVT); + RegisterTypeForVT[ExpandedReg] = LargestIntVT; + if (ExpandedVT.isPow2Size()) { + TransformToType[ExpandedReg] = (MVT::SimpleValueType)HalfReg; + ValueTypeActions.setTypeAction(ExpandedVT, TypeExpandInteger); + HalfReg = ExpandedReg; + } else { + assert(ExpandedReg < MVT::LAST_INTEGER_VALUETYPE && + "Expected a pow 2 type larger than any non pow 2 type"); + TransformToType[ExpandedReg] = (MVT::SimpleValueType)(ExpandedReg + 1); + ValueTypeActions.setTypeAction(ExpandedVT, TypePromoteInteger); + } } + // Inspect all of the ValueType's smaller than the largest integer // register to see which ones need promotion. unsigned LegalIntReg = LargestIntReg; - for (unsigned IntReg = LargestIntReg - 1; - IntReg >= (unsigned)MVT::i1; --IntReg) { + for (unsigned IntReg = LargestIntReg - 1; IntReg >= (unsigned)MVT::i1; + --IntReg) { MVT IVT = (MVT::SimpleValueType)IntReg; if (isTypeLegal(IVT)) { LegalIntReg = IntReg; @@ -1344,6 +1366,7 @@ void TargetLoweringBase::computeRegisterProperties( if (NVT == VT) { // Type is already a power of 2. The default action is to split. TransformToType[i] = MVT::Other; + ValueTypeActions.setTypeAction(VT, PreferredAction); if (PreferredAction == TypeScalarizeVector) ValueTypeActions.setTypeAction(VT, TypeScalarizeVector); else if (PreferredAction == TypeSplitVector) diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp index 03254ed77d71c9..a3e47f69d3513f 100644 --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -2171,3 +2171,31 @@ MCSection *TargetLoweringObjectFileXCOFF::getSectionForTOCEntry( cast(Sym)->getUnqualifiedName(), XCOFF::XMC_TC, XCOFF::XTY_SD, XCOFF::C_HIDEXT, SectionKind::getData()); } + +//===----------------------------------------------------------------------===// +// OMF +//===----------------------------------------------------------------------===// + +MCSection *TargetLoweringObjectFileOMF::getExplicitSectionGlobal( + const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + llvm_unreachable("getExplicitSectionGlobal not yet implemented"); + return nullptr; +} + +MCSection *TargetLoweringObjectFileOMF::SelectSectionForGlobal( + const GlobalObject *GO, SectionKind Kind, const TargetMachine &TM) const { + const MCObjectFileInfo *MOFI = getContext().getObjectFileInfo(); + if (Kind.isCommon()) + // ZDS doesn't support common. It just throws them in BSS and doesn't allow + // them to be redefined. RIP. + return MOFI->getBSSSection(); + if (Kind.isText()) + return MOFI->getTextSection(); + if (Kind.isData()) + return MOFI->getDataSection(); + if (Kind.isBSS()) + return MOFI->getBSSSection(); + if (Kind.isReadOnly()) + return MOFI->getReadOnlySection(); + return nullptr; +} diff --git a/llvm/lib/CodeGen/ValueTypes.cpp b/llvm/lib/CodeGen/ValueTypes.cpp index 68e8acd218ba3f..b9c0d609e7edc1 100644 --- a/llvm/lib/CodeGen/ValueTypes.cpp +++ b/llvm/lib/CodeGen/ValueTypes.cpp @@ -176,6 +176,7 @@ Type *EVT::getTypeForEVT(LLVMContext &Context) const { case MVT::i1: return Type::getInt1Ty(Context); case MVT::i8: return Type::getInt8Ty(Context); case MVT::i16: return Type::getInt16Ty(Context); + case MVT::i24: return IntegerType::get(Context, 24); case MVT::i32: return Type::getInt32Ty(Context); case MVT::i64: return Type::getInt64Ty(Context); case MVT::i128: return IntegerType::get(Context, 128); diff --git a/llvm/lib/MC/CMakeLists.txt b/llvm/lib/MC/CMakeLists.txt index 9dca8d793395c7..1d7902180214b3 100644 --- a/llvm/lib/MC/CMakeLists.txt +++ b/llvm/lib/MC/CMakeLists.txt @@ -6,6 +6,7 @@ add_llvm_component_library(LLVMMC MCAsmInfoCOFF.cpp MCAsmInfoDarwin.cpp MCAsmInfoELF.cpp + MCAsmInfoOMF.cpp MCAsmInfoWasm.cpp MCAsmInfoXCOFF.cpp MCAsmMacro.cpp @@ -32,12 +33,14 @@ add_llvm_component_library(LLVMMC MCObjectFileInfo.cpp MCObjectStreamer.cpp MCObjectWriter.cpp + MCOMFStreamer.cpp MCRegisterInfo.cpp MCSchedule.cpp MCSection.cpp MCSectionCOFF.cpp MCSectionELF.cpp MCSectionMachO.cpp + MCSectionOMF.cpp MCSectionWasm.cpp MCSectionXCOFF.cpp MCStreamer.cpp diff --git a/llvm/lib/MC/MCAsmInfo.cpp b/llvm/lib/MC/MCAsmInfo.cpp index 9767ee6c1133ae..fa678b08a43d2b 100644 --- a/llvm/lib/MC/MCAsmInfo.cpp +++ b/llvm/lib/MC/MCAsmInfo.cpp @@ -38,16 +38,21 @@ MCAsmInfo::MCAsmInfo() { InlineAsmStart = "APP"; InlineAsmEnd = "NO_APP"; Code16Directive = ".code16"; + Code24Directive = ".code24"; Code32Directive = ".code32"; Code64Directive = ".code64"; ZeroDirective = "\t.zero\t"; + BlockSeparator = ", "; AsciiDirective = "\t.ascii\t"; AscizDirective = "\t.asciz\t"; Data8bitsDirective = "\t.byte\t"; + Data24bitsDirective = nullptr; Data16bitsDirective = "\t.short\t"; Data32bitsDirective = "\t.long\t"; Data64bitsDirective = "\t.quad\t"; GlobalDirective = "\t.globl\t"; + SetDirective = ".set "; + SetSeparator = ", "; WeakDirective = "\t.weak\t"; if (DwarfExtendedLoc != Default) SupportsExtendedDwarfLocDirective = DwarfExtendedLoc == Enable; diff --git a/llvm/lib/MC/MCAsmInfoOMF.cpp b/llvm/lib/MC/MCAsmInfoOMF.cpp new file mode 100644 index 00000000000000..39f173d67a9758 --- /dev/null +++ b/llvm/lib/MC/MCAsmInfoOMF.cpp @@ -0,0 +1,23 @@ +//===- MCAsmInfoOMF.cpp - OMF asm properties ------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines target asm properties related what form asm statements +// should take in general on OMF-based targets +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCAsmInfoOMF.h" + +using namespace llvm; + +void MCAsmInfoOMF::anchor() {} + +MCAsmInfoOMF::MCAsmInfoOMF() { + PrivateLabelPrefix = "?"; +} diff --git a/llvm/lib/MC/MCAsmInfoXCOFF.cpp b/llvm/lib/MC/MCAsmInfoXCOFF.cpp index 1531f61da95e74..b278184a707f1b 100644 --- a/llvm/lib/MC/MCAsmInfoXCOFF.cpp +++ b/llvm/lib/MC/MCAsmInfoXCOFF.cpp @@ -32,6 +32,7 @@ MCAsmInfoXCOFF::MCAsmInfoXCOFF() { COMMDirectiveAlignmentIsInBytes = false; LCOMMDirectiveAlignmentType = LCOMM::Log2Alignment; HasDotTypeDotSizeDirective = false; + LGloblDirective = "\t.lglobl\t"; SymbolsHaveSMC = true; UseIntegratedAssembler = false; NeedsFunctionDescriptors = true; diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index 9a86895a2fe13f..7b62590fd370c9 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -504,6 +504,7 @@ void MCAsmStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { case MCAF_SyntaxUnified: OS << "\t.syntax unified"; break; case MCAF_SubsectionsViaSymbols: OS << ".subsections_via_symbols"; break; case MCAF_Code16: OS << '\t'<< MAI->getCode16Directive();break; + case MCAF_Code24: OS << '\t'<< MAI->getCode24Directive();break; case MCAF_Code32: OS << '\t'<< MAI->getCode32Directive();break; case MCAF_Code64: OS << '\t'<< MAI->getCode64Directive();break; } @@ -611,9 +612,9 @@ void MCAsmStreamer::emitAssignment(MCSymbol *Symbol, const MCExpr *Value) { if (E->inlineAssignedExpr()) EmitSet = false; if (EmitSet) { - OS << ".set "; + OS << MAI->getSetDirective(); Symbol->print(OS, MAI); - OS << ", "; + OS << MAI->getSetSeparator(); Value->print(OS, MAI); EmitEOL(); @@ -661,7 +662,9 @@ bool MCAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol, case MCSA_Global: // .globl/.global OS << MAI->getGlobalDirective(); break; - case MCSA_LGlobal: OS << "\t.lglobl\t"; break; + case MCSA_LGlobal: // .lglobl + OS << MAI->getLGloblDirective(); + break; case MCSA_Hidden: OS << "\t.hidden\t"; break; case MCSA_IndirectSymbol: OS << "\t.indirect_symbol\t"; break; case MCSA_Internal: OS << "\t.internal\t"; break; @@ -1033,6 +1036,7 @@ void MCAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, default: break; case 1: Directive = MAI->getData8bitsDirective(); break; case 2: Directive = MAI->getData16bitsDirective(); break; + case 3: Directive = MAI->getData24bitsDirective(); break; case 4: Directive = MAI->getData32bitsDirective(); break; case 8: Directive = MAI->getData64bitsDirective(); break; } @@ -1172,6 +1176,14 @@ void MCAsmStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, return; } + if (const char *BlockDirective = MAI->getBlockDirective(1)) { + OS << BlockDirective; + NumBytes.print(OS, MAI); + OS << MAI->getBlockSeparator() << FillValue; + EmitEOL(); + return; + } + MCStreamer::emitFill(NumBytes, FillValue); } diff --git a/llvm/lib/MC/MCContext.cpp b/llvm/lib/MC/MCContext.cpp index 92d99ec577b5a2..f1045bb1734780 100644 --- a/llvm/lib/MC/MCContext.cpp +++ b/llvm/lib/MC/MCContext.cpp @@ -26,6 +26,7 @@ #include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCSectionOMF.h" #include "llvm/MC/MCSectionWasm.h" #include "llvm/MC/MCSectionXCOFF.h" #include "llvm/MC/MCStreamer.h" @@ -33,6 +34,7 @@ #include "llvm/MC/MCSymbolCOFF.h" #include "llvm/MC/MCSymbolELF.h" #include "llvm/MC/MCSymbolMachO.h" +#include "llvm/MC/MCSymbolOMF.h" #include "llvm/MC/MCSymbolWasm.h" #include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/MC/SectionKind.h" @@ -179,6 +181,8 @@ MCSymbol *MCContext::createSymbolImpl(const StringMapEntry *Name, return new (Name, *this) MCSymbolELF(Name, IsTemporary); case MCObjectFileInfo::IsMachO: return new (Name, *this) MCSymbolMachO(Name, IsTemporary); + case MCObjectFileInfo::IsOMF: + return new (Name, *this) MCSymbolOMF(Name, IsTemporary); case MCObjectFileInfo::IsWasm: return new (Name, *this) MCSymbolWasm(Name, IsTemporary); case MCObjectFileInfo::IsXCOFF: @@ -631,6 +635,29 @@ MCSectionXCOFF *MCContext::getXCOFFSection(StringRef Section, return Result; } +MCSectionOMF *MCContext::getOMFSection(const Twine &Section, SectionKind Kind) { + auto IterBool = OMFUniquingMap.insert( + std::make_pair(OMFSectionKey{Section.str()}, nullptr)); + auto &Entry = *IterBool.first; + if (!IterBool.second) + return Entry.second; + + StringRef CachedName = Entry.first.SectionName; + + MCSymbol *Begin = createSymbol(CachedName, false, false); + + MCSectionOMF *Result = new (OMFAllocator.Allocate()) + MCSectionOMF(CachedName, Kind, Begin); + Entry.second = Result; + + auto *F = new MCDataFragment(); + Result->getFragmentList().insert(Result->begin(), F); + F->setParent(Result); + Begin->setFragment(F); + + return Result; +} + MCSubtargetInfo &MCContext::getSubtargetCopy(const MCSubtargetInfo &STI) { return *new (MCSubtargetAllocator.Allocate()) MCSubtargetInfo(STI); } diff --git a/llvm/lib/MC/MCELFStreamer.cpp b/llvm/lib/MC/MCELFStreamer.cpp index 49d863f258bf98..573a7797a2cd4a 100644 --- a/llvm/lib/MC/MCELFStreamer.cpp +++ b/llvm/lib/MC/MCELFStreamer.cpp @@ -124,6 +124,7 @@ void MCELFStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { switch (Flag) { case MCAF_SyntaxUnified: return; // no-op here. case MCAF_Code16: return; // Change parsing mode; no-op here. + case MCAF_Code24: return; // Change parsing mode; no-op here. case MCAF_Code32: return; // Change parsing mode; no-op here. case MCAF_Code64: return; // Change parsing mode; no-op here. case MCAF_SubsectionsViaSymbols: diff --git a/llvm/lib/MC/MCMachOStreamer.cpp b/llvm/lib/MC/MCMachOStreamer.cpp index 2b1d1b28ea184d..3a35947d625951 100644 --- a/llvm/lib/MC/MCMachOStreamer.cpp +++ b/llvm/lib/MC/MCMachOStreamer.cpp @@ -238,6 +238,7 @@ void MCMachOStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { switch (Flag) { case MCAF_SyntaxUnified: return; // no-op here. case MCAF_Code16: return; // Change parsing mode; no-op here. + case MCAF_Code24: return; // Change parsing mode; no-op here. case MCAF_Code32: return; // Change parsing mode; no-op here. case MCAF_Code64: return; // Change parsing mode; no-op here. case MCAF_SubsectionsViaSymbols: diff --git a/llvm/lib/MC/MCOMFStreamer.cpp b/llvm/lib/MC/MCOMFStreamer.cpp new file mode 100644 index 00000000000000..b0a997f1307338 --- /dev/null +++ b/llvm/lib/MC/MCOMFStreamer.cpp @@ -0,0 +1,48 @@ +//===- lib/MC/MCOMFStreamer.cpp - OMF Object Output -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file assembles .s files and emits OMF .o object files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCOMFStreamer.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/Support/TargetRegistry.h" + +using namespace llvm; + +bool MCOMFStreamer::emitSymbolAttribute(MCSymbol *Symbol, + MCSymbolAttr Attribute) { + llvm_unreachable("Unimplemented!"); +} + +void MCOMFStreamer::emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, + unsigned ByteAlignment) { + llvm_unreachable("Unimplemented!"); +} + +void MCOMFStreamer::emitZerofill(MCSection *Section, MCSymbol *Symbol, + uint64_t Size, unsigned ByteAlignment, + SMLoc Loc) { + llvm_unreachable("Unimplemented!"); +} + +void MCOMFStreamer::emitInstToData(const MCInst &Inst, + const MCSubtargetInfo &) { + llvm_unreachable("Unimplemented!"); +} + +MCStreamer *llvm::createOMFStreamer(MCContext &Context, + std::unique_ptr &&MAB, + std::unique_ptr &&OW, + std::unique_ptr &&CE, + bool RelaxAll) { + return new MCOMFStreamer(Context, std::move(MAB), std::move(OW), std::move(CE)); +} diff --git a/llvm/lib/MC/MCObjectFileInfo.cpp b/llvm/lib/MC/MCObjectFileInfo.cpp index b77a9635f64c40..c90d191f95ab94 100644 --- a/llvm/lib/MC/MCObjectFileInfo.cpp +++ b/llvm/lib/MC/MCObjectFileInfo.cpp @@ -17,6 +17,7 @@ #include "llvm/MC/MCSectionCOFF.h" #include "llvm/MC/MCSectionELF.h" #include "llvm/MC/MCSectionMachO.h" +#include "llvm/MC/MCSectionOMF.h" #include "llvm/MC/MCSectionWasm.h" #include "llvm/MC/MCSectionXCOFF.h" @@ -858,6 +859,13 @@ void MCObjectFileInfo::initXCOFFMCObjectFileInfo(const Triple &T) { DwarfMacinfoSection = nullptr; // SSUBTYP_DWMAC } +void MCObjectFileInfo::initOMFMCObjectFileInfo(const Triple &T) { + TextSection = Ctx->getOMFSection(".text", SectionKind::getText()); + DataSection = Ctx->getOMFSection(".data", SectionKind::getData()); + BSSSection = Ctx->getOMFSection(".bss", SectionKind::getBSS()); + ReadOnlySection = Ctx->getOMFSection(".rodata", SectionKind::getReadOnly()); +} + void MCObjectFileInfo::InitMCObjectFileInfo(const Triple &TheTriple, bool PIC, MCContext &ctx, bool LargeCodeModel) { @@ -908,6 +916,10 @@ void MCObjectFileInfo::InitMCObjectFileInfo(const Triple &TheTriple, bool PIC, Env = IsXCOFF; initXCOFFMCObjectFileInfo(TT); break; + case Triple::OMF: + Env = IsOMF; + initOMFMCObjectFileInfo(TT); + break; case Triple::UnknownObjectFormat: report_fatal_error("Cannot initialize MC for unknown object file format."); break; @@ -924,6 +936,7 @@ MCSection *MCObjectFileInfo::getDwarfComdatSection(const char *Name, case Triple::COFF: case Triple::Wasm: case Triple::XCOFF: + case Triple::OMF: case Triple::UnknownObjectFormat: report_fatal_error("Cannot get DWARF comdat section for this object file " "format: not implemented."); diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 9e92ce09986f68..7cbcef1bfb2192 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -728,6 +728,10 @@ AsmParser::AsmParser(SourceMgr &SM, MCContext &Ctx, MCStreamer &Out, report_fatal_error( "Need to implement createXCOFFAsmParser for XCOFF format."); break; + case MCObjectFileInfo::IsOMF: + report_fatal_error( + "Need to implement createOMFAsmParser for OMF format."); + break; } PlatformParser->Initialize(*this); diff --git a/llvm/lib/MC/MCSectionOMF.cpp b/llvm/lib/MC/MCSectionOMF.cpp new file mode 100644 index 00000000000000..617abf40d0b76f --- /dev/null +++ b/llvm/lib/MC/MCSectionOMF.cpp @@ -0,0 +1,27 @@ +//===- lib/MC/MCSectionOMF.cpp - OMF Code Section Representation ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MC/MCSectionOMF.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +MCSectionOMF::MCSectionOMF(StringRef Name, SectionKind K, MCSymbol *Begin) + : MCSection(SV_OMF, Name, K, Begin) {} + +MCSectionOMF::~MCSectionOMF() {} // anchor. + +void MCSectionOMF::PrintSwitchToSection(const MCAsmInfo &MAI, const Triple &T, + raw_ostream &OS, + const MCExpr *Subsection) const { + assert(!Subsection && "Unimplemented!"); + OS << "\tsection\t" << getName() << '\n'; +} diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 0b650ef936bffb..531135cef5645d 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -1113,7 +1113,8 @@ void MCStreamer::SwitchSection(MCSection *Section, const MCExpr *Subsection) { MCSymbol *Sym = Section->getBeginSymbol(); if (Sym && !Sym->isInSection()) emitLabel(Sym); - } + } else if (Context.getAsmInfo()->shouldAlwaysChangeSection()) + changeSection(Section, Subsection); } MCSymbol *MCStreamer::endSection(MCSection *Section) { diff --git a/llvm/lib/MC/MCWinCOFFStreamer.cpp b/llvm/lib/MC/MCWinCOFFStreamer.cpp index 7f0f7fccd54268..facfd9bbb55d31 100644 --- a/llvm/lib/MC/MCWinCOFFStreamer.cpp +++ b/llvm/lib/MC/MCWinCOFFStreamer.cpp @@ -95,6 +95,7 @@ void MCWinCOFFStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { // None of these require COFF specific handling. case MCAF_SyntaxUnified: case MCAF_Code16: + case MCAF_Code24: case MCAF_Code32: case MCAF_Code64: break; diff --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp index 944d2bc1bca77e..28662bf07aaf4a 100644 --- a/llvm/lib/Object/Binary.cpp +++ b/llvm/lib/Object/Binary.cpp @@ -89,6 +89,9 @@ Expected> object::createBinary(MemoryBufferRef Buffer, return MinidumpFile::create(Buffer); case file_magic::tapi_file: return TapiUniversal::create(Buffer); + case file_magic::omf_object: + case file_magic::omf_archive: + return errorCodeToError(object_error::invalid_file_type); } llvm_unreachable("Unexpected Binary File Type"); } diff --git a/llvm/lib/Object/ObjectFile.cpp b/llvm/lib/Object/ObjectFile.cpp index 61b36ea0f448ae..82d45d7b605f5b 100644 --- a/llvm/lib/Object/ObjectFile.cpp +++ b/llvm/lib/Object/ObjectFile.cpp @@ -146,8 +146,9 @@ ObjectFile::createObjectFile(MemoryBufferRef Object, file_magic Type) { case file_magic::windows_resource: case file_magic::pdb: case file_magic::minidump: - return errorCodeToError(object_error::invalid_file_type); case file_magic::tapi_file: + case file_magic::omf_object: + case file_magic::omf_archive: return errorCodeToError(object_error::invalid_file_type); case file_magic::elf: case file_magic::elf_relocatable: diff --git a/llvm/lib/Object/SymbolicFile.cpp b/llvm/lib/Object/SymbolicFile.cpp index 3db4ad9ed14bd6..0293aa87f45c9b 100644 --- a/llvm/lib/Object/SymbolicFile.cpp +++ b/llvm/lib/Object/SymbolicFile.cpp @@ -54,6 +54,8 @@ SymbolicFile::createSymbolicFile(MemoryBufferRef Object, file_magic Type, case file_magic::pdb: case file_magic::minidump: case file_magic::tapi_file: + case file_magic::omf_object: + case file_magic::omf_archive: return errorCodeToError(object_error::invalid_file_type); case file_magic::elf: case file_magic::elf_executable: diff --git a/llvm/lib/Support/Triple.cpp b/llvm/lib/Support/Triple.cpp index 75ec257e15b51a..f1f3ad2fc80499 100644 --- a/llvm/lib/Support/Triple.cpp +++ b/llvm/lib/Support/Triple.cpp @@ -75,6 +75,8 @@ StringRef Triple::getArchTypeName(ArchType Kind) { case x86: return "i386"; case x86_64: return "x86_64"; case xcore: return "xcore"; + case z80: return "z80"; + case ez80: return "ez80"; } llvm_unreachable("Invalid ArchType!"); @@ -151,6 +153,9 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) { case riscv64: return "riscv"; case ve: return "ve"; + + case z80: + case ez80: return "z80"; } } @@ -321,6 +326,8 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { .Case("renderscript32", renderscript32) .Case("renderscript64", renderscript64) .Case("ve", ve) + .Case("z80", z80) + .Case("ez80", ez80) .Default(UnknownArch); } @@ -450,6 +457,8 @@ static Triple::ArchType parseArch(StringRef ArchName) { .Case("ve", Triple::ve) .Case("wasm32", Triple::wasm32) .Case("wasm64", Triple::wasm64) + .Cases("z80", "z180", Triple::z80) + .Case("ez80", Triple::ez80) .Default(Triple::UnknownArch); // Some architectures require special parsing logic just to compute the @@ -650,6 +659,7 @@ static StringRef getObjectFormatTypeName(Triple::ObjectFormatType Kind) { case Triple::COFF: return "coff"; case Triple::ELF: return "elf"; case Triple::MachO: return "macho"; + case Triple::OMF: return "omf"; case Triple::Wasm: return "wasm"; case Triple::XCOFF: return "xcoff"; } @@ -723,6 +733,10 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { case Triple::wasm32: case Triple::wasm64: return Triple::Wasm; + + case Triple::z80: + case Triple::ez80: + return Triple::OMF; } llvm_unreachable("unknown architecture"); } @@ -1242,8 +1256,12 @@ static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::avr: case llvm::Triple::msp430: + case llvm::Triple::z80: return 16; + case llvm::Triple::ez80: + return 24; + case llvm::Triple::aarch64_32: case llvm::Triple::amdil: case llvm::Triple::arc: @@ -1324,6 +1342,8 @@ Triple Triple::get32BitArchVariant() const { case Triple::ppc64le: case Triple::systemz: case Triple::ve: + case Triple::z80: + case Triple::ez80: T.setArch(UnknownArch); break; @@ -1393,6 +1413,8 @@ Triple Triple::get64BitArchVariant() const { case Triple::tce: case Triple::tcele: case Triple::xcore: + case Triple::z80: + case Triple::ez80: T.setArch(UnknownArch); break; @@ -1476,6 +1498,8 @@ Triple Triple::getBigEndianArchVariant() const { case Triple::x86_64: case Triple::xcore: case Triple::ve: + case Triple::z80: + case Triple::ez80: // ARM is intentionally unsupported here, changing the architecture would // drop any arch suffixes. @@ -1568,6 +1592,8 @@ bool Triple::isLittleEndian() const { case Triple::x86: case Triple::x86_64: case Triple::xcore: + case Triple::z80: + case Triple::ez80: return true; default: return false; diff --git a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp index 7903299fcc33cc..b85e0e8c40873f 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64CallLowering.cpp @@ -372,7 +372,8 @@ bool AArch64CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, } OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFn, AssignFn); - Success = handleAssignments(MIRBuilder, SplitArgs, Handler); + Success = + handleAssignments(CC, F.isVarArg(), MIRBuilder, SplitArgs, Handler); } if (SwiftErrorVReg) { @@ -459,7 +460,8 @@ bool AArch64CallLowering::lowerFormalArguments( TLI.CCAssignFnForCall(F.getCallingConv(), /*IsVarArg=*/false); FormalArgHandler Handler(MIRBuilder, MRI, AssignFn); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) return false; AArch64FunctionInfo *FuncInfo = MF.getInfo(); @@ -883,7 +885,7 @@ bool AArch64CallLowering::lowerTailCall( SmallVector PhysRegs; OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFnFixed, AssignFnVarArg, true, FPDiff); - if (!handleAssignments(MIRBuilder, OutArgs, Handler)) + if (!handleAssignments(CalleeCC, Info.IsVarArg, MIRBuilder, OutArgs, Handler)) return false; if (Info.IsVarArg && Info.IsMustTailCall) { @@ -1001,7 +1003,8 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, SmallVector PhysRegs; OutgoingArgHandler Handler(MIRBuilder, MRI, MIB, AssignFnFixed, AssignFnVarArg, false); - if (!handleAssignments(MIRBuilder, OutArgs, Handler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, OutArgs, + Handler)) return false; // Now we can add the actual call instruction to the correct basic block. @@ -1022,7 +1025,8 @@ bool AArch64CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, if (!Info.OrigRet.Ty->isVoidTy()) { CCAssignFn *RetAssignFn = TLI.CCAssignFnForReturn(Info.CallConv); CallReturnHandler Handler(MIRBuilder, MRI, MIB, RetAssignFn); - if (!handleAssignments(MIRBuilder, InArgs, Handler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, InArgs, + Handler)) return false; } diff --git a/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp index b929a3c60a3770..1a9692bd6c5e78 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUCallLowering.cpp @@ -346,7 +346,7 @@ bool AMDGPUCallLowering::lowerReturnVal(MachineIRBuilder &B, CCAssignFn *AssignFn = TLI.CCAssignFnForReturn(CC, F.isVarArg()); OutgoingValueHandler RetHandler(B, *MRI, Ret, AssignFn); - return handleAssignments(B, SplitRetInfos, RetHandler); + return handleAssignments(CC, F.isVarArg(), B, SplitRetInfos, RetHandler); } bool AMDGPUCallLowering::lowerReturn(MachineIRBuilder &B, diff --git a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp index 16b811185233fa..5cc068bcd64743 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUISelLowering.cpp @@ -134,7 +134,7 @@ AMDGPUTargetLowering::AMDGPUTargetLowering(const TargetMachine &TM, } for (MVT VT : MVT::integer_valuetypes()) { - if (VT == MVT::i64) + if (!VT.isPow2Size() || VT.bitsGE(MVT::i64)) continue; setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); diff --git a/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp b/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp index e0c63bc5e8e182..03aeb744582cdc 100644 --- a/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp +++ b/llvm/lib/Target/AMDGPU/R600ISelLowering.cpp @@ -76,6 +76,9 @@ R600TargetLowering::R600TargetLowering(const TargetMachine &TM, // EXTLOAD should be the same as ZEXTLOAD. It is legal for some address // spaces, so it is custom lowered to handle those where it isn't. for (MVT VT : MVT::integer_valuetypes()) { + if (!VT.isPow2Size()) + continue; + setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i8, Custom); setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i16, Custom); diff --git a/llvm/lib/Target/ARM/ARMCallLowering.cpp b/llvm/lib/Target/ARM/ARMCallLowering.cpp index 4fbb3b6993e4e3..8dea7af5fe5dd0 100644 --- a/llvm/lib/Target/ARM/ARMCallLowering.cpp +++ b/llvm/lib/Target/ARM/ARMCallLowering.cpp @@ -166,7 +166,7 @@ struct OutgoingValueHandler : public CallLowering::ValueHandler { assignValueToReg(NewRegs[0], VA.getLocReg(), VA); assignValueToReg(NewRegs[1], NextVA.getLocReg(), NextVA); - return 1; + return 2; } bool assignArg(unsigned ValNo, MVT ValVT, MVT LocVT, @@ -259,7 +259,8 @@ bool ARMCallLowering::lowerReturnVal(MachineIRBuilder &MIRBuilder, TLI.CCAssignFnForReturn(F.getCallingConv(), F.isVarArg()); OutgoingValueHandler RetHandler(MIRBuilder, MF.getRegInfo(), Ret, AssignFn); - return handleAssignments(MIRBuilder, SplitRetInfos, RetHandler); + return handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitRetInfos, RetHandler); } bool ARMCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, @@ -390,7 +391,7 @@ struct IncomingValueHandler : public CallLowering::ValueHandler { MIRBuilder.buildMerge(Arg.Regs[0], NewRegs); - return 1; + return 2; } /// Marking a physical register as used is different between formal @@ -459,7 +460,8 @@ bool ARMCallLowering::lowerFormalArguments( if (!MBB.empty()) MIRBuilder.setInstr(*MBB.begin()); - if (!handleAssignments(MIRBuilder, SplitArgInfos, ArgHandler)) + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgInfos, ArgHandler)) return false; // Move back to the end of the basic block. @@ -555,7 +557,8 @@ bool ARMCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo & auto ArgAssignFn = TLI.CCAssignFnForCall(Info.CallConv, IsVarArg); OutgoingValueHandler ArgHandler(MIRBuilder, MRI, MIB, ArgAssignFn); - if (!handleAssignments(MIRBuilder, ArgInfos, ArgHandler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, ArgInfos, + ArgHandler)) return false; // Now we can add the actual call instruction to the correct basic block. @@ -569,7 +572,8 @@ bool ARMCallLowering::lowerCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo & splitToValueTypes(Info.OrigRet, ArgInfos, MF); auto RetAssignFn = TLI.CCAssignFnForReturn(Info.CallConv, IsVarArg); CallReturnHandler RetHandler(MIRBuilder, MRI, MIB, RetAssignFn); - if (!handleAssignments(MIRBuilder, ArgInfos, RetHandler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, ArgInfos, + RetHandler)) return false; } diff --git a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 05f870b90ecdb0..b71e8a1c99db7e 100644 --- a/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/llvm/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -6264,6 +6264,7 @@ bool ARMAsmParser::parsePrefix(ARMMCExpr::VariantKind &RefKind) { CurrentFormat = WASM; break; case MCObjectFileInfo::IsXCOFF: + case MCObjectFileInfo::IsOMF: llvm_unreachable("unexpected object format"); break; } diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp index 876741d6c34386..eec6052333c4b4 100644 --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMELFStreamer.cpp @@ -572,6 +572,7 @@ class ARMELFStreamer : public MCELFStreamer { case MCAF_Code32: IsThumb = false; return; // Change to ARM mode + case MCAF_Code24: case MCAF_Code64: return; case MCAF_SubsectionsViaSymbols: diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index bf9b32e1278e38..f65e7273b9d405 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -66,6 +66,9 @@ AVRTargetLowering::AVRTargetLowering(const AVRTargetMachine &TM, setTruncStoreAction(MVT::i16, MVT::i8, Expand); for (MVT VT : MVT::integer_valuetypes()) { + if (!VT.isPow2Size()) + continue; + setOperationAction(ISD::ADDC, VT, Legal); setOperationAction(ISD::SUBC, VT, Legal); setOperationAction(ISD::ADDE, VT, Legal); diff --git a/llvm/lib/Target/LLVMBuild.txt b/llvm/lib/Target/LLVMBuild.txt index e5a9d787e7fa5c..b1119d26977da8 100644 --- a/llvm/lib/Target/LLVMBuild.txt +++ b/llvm/lib/Target/LLVMBuild.txt @@ -37,6 +37,7 @@ subdirectories = WebAssembly X86 XCore + Z80 ; This is a special group whose required libraries are extended (by llvm-build) ; with the best execution engine (the native JIT, if available, or the diff --git a/llvm/lib/Target/X86/X86CallLowering.cpp b/llvm/lib/Target/X86/X86CallLowering.cpp index 319dc947060480..00e0f5346090d3 100644 --- a/llvm/lib/Target/X86/X86CallLowering.cpp +++ b/llvm/lib/Target/X86/X86CallLowering.cpp @@ -72,7 +72,7 @@ bool X86CallLowering::splitToValueTypes(const ArgInfo &OrigArg, if (NumParts == 1) { // replace the original type ( pointer -> GPR ). SplitArgs.emplace_back(OrigArg.Regs[0], VT.getTypeForEVT(Context), - OrigArg.Flags, OrigArg.IsFixed); + OrigArg.Val, OrigArg.Flags, OrigArg.IsFixed); return true; } @@ -216,7 +216,8 @@ bool X86CallLowering::lowerReturn( } OutgoingValueHandler Handler(MIRBuilder, MRI, MIB, RetCC_X86); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) return false; } @@ -366,7 +367,8 @@ bool X86CallLowering::lowerFormalArguments( MIRBuilder.setInstr(*MBB.begin()); FormalArgHandler Handler(MIRBuilder, MRI, CC_X86); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) return false; // Move back to the end of the basic block. @@ -422,7 +424,8 @@ bool X86CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, } // Do the actual argument marshalling. OutgoingValueHandler Handler(MIRBuilder, MRI, MIB, CC_X86); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, SplitArgs, + Handler)) return false; bool IsFixed = Info.OrigArgs.empty() ? true : Info.OrigArgs.back().IsFixed; @@ -471,7 +474,8 @@ bool X86CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, return false; CallReturnHandler Handler(MIRBuilder, MRI, RetCC_X86, MIB); - if (!handleAssignments(MIRBuilder, SplitArgs, Handler)) + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, SplitArgs, + Handler)) return false; if (!NewRegs.empty()) diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp index 23512f3f85dd81..2bb08f73477e96 100644 --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -2915,9 +2915,11 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { unsigned ResultReg = 0; // Check if we have an immediate version. if (const auto *CI = dyn_cast(RHS)) { - static const uint16_t Opc[2][4] = { - { X86::INC8r, X86::INC16r, X86::INC32r, X86::INC64r }, - { X86::DEC8r, X86::DEC16r, X86::DEC32r, X86::DEC64r } + static const uint16_t Opc[2][5] = { + { X86::INC8r, X86::INC16r, X86::INSTRUCTION_LIST_END, + X86::INC32r, X86::INC64r }, + { X86::DEC8r, X86::DEC16r, X86::INSTRUCTION_LIST_END, + X86::DEC32r, X86::DEC64r } }; if (CI->isOne() && (BaseOpc == ISD::ADD || BaseOpc == ISD::SUB) && @@ -2948,8 +2950,10 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { // it manually. if (BaseOpc == X86ISD::UMUL && !ResultReg) { static const uint16_t MULOpc[] = - { X86::MUL8r, X86::MUL16r, X86::MUL32r, X86::MUL64r }; - static const MCPhysReg Reg[] = { X86::AL, X86::AX, X86::EAX, X86::RAX }; + { X86::MUL8r, X86::MUL16r, X86::INSTRUCTION_LIST_END, + X86::MUL32r, X86::MUL64r }; + static const MCPhysReg Reg[] = { X86::AL, X86::AX, X86::NoRegister, + X86::EAX, X86::RAX }; // First copy the first operand into RAX, which is an implicit input to // the X86::MUL*r instruction. BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, @@ -2959,7 +2963,8 @@ bool X86FastISel::fastLowerIntrinsicCall(const IntrinsicInst *II) { TLI.getRegClassFor(VT), RHSReg, RHSIsKill); } else if (BaseOpc == X86ISD::SMUL && !ResultReg) { static const uint16_t MULOpc[] = - { X86::IMUL8r, X86::IMUL16rr, X86::IMUL32rr, X86::IMUL64rr }; + { X86::IMUL8r, X86::IMUL16rr, X86::INSTRUCTION_LIST_END, + X86::IMUL32rr, X86::IMUL64rr }; if (VT == MVT::i8) { // Copy the first operand into AL, which is an implicit input to the // X86::IMUL8r instruction. diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index c3e89854bc489b..013a0a064066a2 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -2285,7 +2285,7 @@ bool X86TargetLowering::isSafeMemOpType(MVT VT) const { return X86ScalarSSEf32; else if (VT == MVT::f64) return X86ScalarSSEf64; - return true; + return TargetLowering::isSafeMemOpType(VT); } bool X86TargetLowering::allowsMisalignedMemoryAccesses( diff --git a/llvm/lib/Target/Z80/CMakeLists.txt b/llvm/lib/Target/Z80/CMakeLists.txt new file mode 100644 index 00000000000000..34b408a039940a --- /dev/null +++ b/llvm/lib/Target/Z80/CMakeLists.txt @@ -0,0 +1,38 @@ +set(LLVM_TARGET_DEFINITIONS Z80.td) + +tablegen(LLVM EZ80GenAsmWriter.inc -gen-asm-writer -asmwriternum=1) +tablegen(LLVM Z80GenAsmWriter.inc -gen-asm-writer) +tablegen(LLVM Z80GenCallingConv.inc -gen-callingconv) +tablegen(LLVM Z80GenGICombiner.inc -gen-global-isel-combiner + -combiners="Z80PreLegalizerCombinerHelper") +tablegen(LLVM Z80GenGlobalISel.inc -gen-global-isel) +tablegen(LLVM Z80GenInstrInfo.inc -gen-instr-info) +tablegen(LLVM Z80GenRegisterBank.inc -gen-register-bank) +tablegen(LLVM Z80GenRegisterInfo.inc -gen-register-info) +tablegen(LLVM Z80GenSubtargetInfo.inc -gen-subtarget) + +add_public_tablegen_target(Z80CommonTableGen) + +set(sources + GISel/Z80CallLowering.cpp + GISel/Z80InstructionSelector.cpp + GISel/Z80LegalizerInfo.cpp + GISel/Z80PreLegalizerCombiner.cpp + GISel/Z80RegisterBankInfo.cpp + Z80AsmPrinter.cpp + Z80CallingConv.cpp + Z80FrameLowering.cpp + Z80ISelLowering.cpp + Z80InstrInfo.cpp + Z80MCInstLower.cpp + Z80MachineLateOptimization.cpp + Z80PostSelectCombiner.cpp + Z80RegisterInfo.cpp + Z80Subtarget.cpp + Z80TargetMachine.cpp + ) + +add_llvm_target(Z80CodeGen ${sources}) + +add_subdirectory(MCTargetDesc) +add_subdirectory(TargetInfo) diff --git a/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp b/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp new file mode 100644 index 00000000000000..017c0d123e7217 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80CallLowering.cpp @@ -0,0 +1,894 @@ +//===- llvm/lib/Target/Z80/Z80CallLowering.cpp - Call lowering ------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file implements the lowering of LLVM calls to machine code calls for +/// GlobalISel. +// +//===----------------------------------------------------------------------===// + +#include "Z80CallLowering.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80CallingConv.h" +#include "Z80ISelLowering.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80Subtarget.h" +#include "llvm/CodeGen/Analysis.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-call-lowering" + +Z80CallLowering::Z80CallLowering(const Z80TargetLowering &TLI) + : CallLowering(&TLI) {} + +namespace { + +struct OutgoingValueHandler : public CallLowering::ValueHandler { + OutgoingValueHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder &MIB, CCAssignFn *AssignFn) + : ValueHandler(MIRBuilder, MRI, AssignFn), MIB(MIB), + DL(MIRBuilder.getMF().getDataLayout()), + STI(MIRBuilder.getMF().getSubtarget()) { + LLT PtrTy = LLT::pointer(0, DL.getPointerSizeInBits(0)); + Register SPReg = STI.getRegisterInfo()->getStackRegister(); + SPRegCopy = MIRBuilder.buildCopy(PtrTy, SPReg).getReg(0); + } + + bool isIncomingArgumentHandler() const override { return false; } + + Register getStackAddress(uint64_t Size, int64_t Off, + MachinePointerInfo &MPO) override { + LLT PtrTy = LLT::pointer(0, DL.getPointerSizeInBits(0)); + LLT OffTy = LLT::scalar(DL.getIndexSizeInBits(0)); + MPO = MachinePointerInfo::getStack(MIRBuilder.getMF(), Off); + auto OffI = MIRBuilder.buildConstant(OffTy, Off); + return MIRBuilder.buildPtrAdd(PtrTy, SPRegCopy, OffI).getReg(0); + } + + void assignValueToReg(Register ValVReg, Register PhysReg, + CCValAssign &VA) override { + MIB.addUse(PhysReg, RegState::Implicit); + MIRBuilder.buildCopy(PhysReg, extendRegister(ValVReg, VA)); + } + + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOStore, VA.getLocVT().getStoreSize(), + Align()); + MIRBuilder.buildStore(extendRegister(ValVReg, VA), Addr, *MMO); + } + + bool finalize(CCState &State) override { + if (State.getCallingConv() == CallingConv::Z80_TIFlags) { + bool Is24Bit = STI.is24Bit(); + MVT VT = Is24Bit ? MVT::i24 : MVT::i16; + Register FlagsReg = + MIRBuilder + .buildConstant(LLT(VT), STI.hasEZ80Ops() ? 0xD00080 : 0x89F0) + .getReg(0); + CCValAssign VA = CCValAssign::getReg(~0, VT, Is24Bit ? Z80::UIY : Z80::IY, + VT, CCValAssign::Full); + assignValueToReg(FlagsReg, VA.getLocReg(), VA); + } + if (MRI.use_empty(SPRegCopy)) + MRI.getVRegDef(SPRegCopy)->eraseFromParent(); + return ValueHandler::finalize(State); + } + +protected: + MachineInstrBuilder &MIB; + const DataLayout &DL; + const Z80Subtarget &STI; + Register SPRegCopy; +}; + +struct TailCallArgHandler : public OutgoingValueHandler { + TailCallArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder &MIB, CCAssignFn *AssignFn, int FPDiff) + : OutgoingValueHandler(MIRBuilder, MRI, MIB, AssignFn), FPDiff(FPDiff) {} + + Register getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + MachineFunction &MF = MIRBuilder.getMF(); + int FI = MF.getFrameInfo().CreateFixedObject(Size, FPDiff + Offset, true); + MPO = MachinePointerInfo::getFixedStack(MF, FI); + return MIRBuilder + .buildFrameIndex(LLT::pointer(0, DL.getPointerSizeInBits(0)), FI) + .getReg(0); + } + +private: + int FPDiff; +}; + +struct CallArgHandler : public OutgoingValueHandler { + CallArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + MachineInstrBuilder &MIB, CCAssignFn *AssignFn) + : OutgoingValueHandler(MIRBuilder, MRI, MIB, AssignFn), + After(MIRBuilder.getInsertPt()) {} + + bool prepareArg(CCValAssign &VA) override { + if (!VA.getValNo()) + Before = std::prev(MIRBuilder.getInsertPt()); + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), std::next(Before)); + return OutgoingValueHandler::prepareArg(VA); + } + + void assignValueToReg(Register ValVReg, Register PhysReg, + CCValAssign &VA) override { + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), After); + OutgoingValueHandler::assignValueToReg(ValVReg, PhysReg, VA); + } + + Register getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), std::next(Before)); + return OutgoingValueHandler::getStackAddress(Size, Offset, MPO); + } + + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + if (MachineInstr *AddrMI = MRI.getVRegDef(Addr)) { + unsigned Size = STI.is24Bit() ? 3 : 2; + if (Size == VA.getLocVT().getStoreSize() && + AddrMI->getOpcode() == TargetOpcode::G_PTR_ADD) { + if (MachineInstr *BaseMI = + getDefIgnoringCopies(AddrMI->getOperand(1).getReg(), MRI)) { + if (auto OffConst = getConstantVRegValWithLookThrough( + AddrMI->getOperand(2).getReg(), MRI)) { + if (BaseMI->getOpcode() == TargetOpcode::COPY && + BaseMI->getOperand(1).getReg() == + STI.getRegisterInfo()->getStackRegister() && + OffConst->Value == SetupFrameAdjustment) { + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), std::next(Before)); + MIRBuilder.buildInstr(Size == 3 ? Z80::PUSH24r : Z80::PUSH16r, {}, + {extendRegister(ValVReg, VA)}); + SetupFrameAdjustment += Size; + return; + } + } + } + } + } + LLT PtrTy = LLT::pointer(0, DL.getPointerSizeInBits(0)); + LLT OffTy = LLT::scalar(DL.getIndexSizeInBits(0)); + auto OffI = MIRBuilder.buildConstant(OffTy, -SetupFrameAdjustment); + Addr = MIRBuilder.buildPtrAdd(PtrTy, Addr, OffI).getReg(0); + return OutgoingValueHandler::assignValueToAddress(ValVReg, Addr, Size, MPO, + VA); + } + + bool finalize(CCState &State) override { + FrameSize = State.getNextStackOffset(); + MIRBuilder.setInsertPt(MIRBuilder.getMBB(), After); + return OutgoingValueHandler::finalize(State); + } + + unsigned getPreFrameAdjustment() const { + return 0; + } + + unsigned getFrameSize() const { + return FrameSize; + } + + unsigned getFrameTotalSize() const { + return getPreFrameAdjustment() + getFrameSize(); + } + + unsigned getSetupFrameAdjustment() const { + return SetupFrameAdjustment; + } + + unsigned getDestroyFrameAdjustment() const { + return 0; + } + +protected: + MachineBasicBlock::iterator After, Before; + unsigned FrameSize, SetupFrameAdjustment = 0; +}; + +struct IncomingValueHandler : public CallLowering::ValueHandler { + IncomingValueHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + CCAssignFn *AssignFn) + : ValueHandler(MIRBuilder, MRI, AssignFn), + DL(MIRBuilder.getMF().getDataLayout()) {} + + bool isIncomingArgumentHandler() const override { return true; } + + Register getStackAddress(uint64_t Size, int64_t Offset, + MachinePointerInfo &MPO) override { + auto &MFI = MIRBuilder.getMF().getFrameInfo(); + int FI = MFI.CreateFixedObject(Size, Offset, true); + MPO = MachinePointerInfo::getFixedStack(MIRBuilder.getMF(), FI); + LLT p0 = LLT::pointer(0, DL.getPointerSizeInBits(0)); + return MIRBuilder.buildFrameIndex(p0, FI).getReg(0); + } + + void assignValueToAddress(Register ValVReg, Register Addr, uint64_t Size, + MachinePointerInfo &MPO, CCValAssign &VA) override { + auto MMO = MIRBuilder.getMF().getMachineMemOperand( + MPO, MachineMemOperand::MOLoad | MachineMemOperand::MOInvariant, Size, + Align()); + MIRBuilder.buildLoad(ValVReg, Addr, *MMO); + } + + void assignValueToReg(Register ValVReg, Register PhysReg, + CCValAssign &VA) override { + markPhysRegUsed(PhysReg); + + switch (VA.getLocInfo()) { + default: { + // If we are copying the value from a physical register with the + // size larger than the size of the value itself - build the copy + // of the phys reg first and then build the truncation of that copy. + // The example of that would be copying from xmm0 to s32, for which + // case ValVT == LocVT == MVT::f32. If LocSize and ValSize are not equal + // we expect this to be handled in SExt/ZExt/AExt case. + unsigned PhysRegSize = + MRI.getTargetRegisterInfo()->getRegSizeInBits(PhysReg, MRI); + unsigned ValSize = VA.getValVT().getSizeInBits(); + unsigned LocSize = VA.getLocVT().getSizeInBits(); + if (PhysRegSize > ValSize && LocSize == ValSize) { + auto Copy = MIRBuilder.buildCopy(LLT::scalar(PhysRegSize), PhysReg); + MIRBuilder.buildTrunc(ValVReg, Copy); + return; + } + + MIRBuilder.buildCopy(ValVReg, PhysReg); + break; + } + case CCValAssign::LocInfo::SExt: + case CCValAssign::LocInfo::ZExt: + case CCValAssign::LocInfo::AExt: { + auto Copy = MIRBuilder.buildCopy(LLT{VA.getLocVT()}, PhysReg); + MIRBuilder.buildTrunc(ValVReg, Copy); + break; + } + } + } + + /// How the physical register gets marked varies between formal + /// parameters (it's a basic-block live-in), and a call instruction + /// (it's an implicit-def of the BL). + virtual void markPhysRegUsed(unsigned PhysReg) = 0; + +protected: + const DataLayout &DL; +}; + +struct FormalArgHandler : public IncomingValueHandler { + FormalArgHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + CCAssignFn *AssignFn) + : IncomingValueHandler(MIRBuilder, MRI, AssignFn) {} + + void markPhysRegUsed(unsigned PhysReg) override { + MIRBuilder.getMRI()->addLiveIn(PhysReg); + MIRBuilder.getMBB().addLiveIn(PhysReg); + } + + bool finalize(CCState &State) override { + MachineFunction &MF = MIRBuilder.getMF(); + auto &FuncInfo = *MF.getInfo(); + FuncInfo.setArgFrameSize(State.getNextStackOffset()); + if (State.isVarArg()) { + int FrameIdx = MF.getFrameInfo().CreateFixedObject( + 1, State.getNextStackOffset(), true); + FuncInfo.setVarArgsFrameIndex(FrameIdx); + } + return true; + } +}; + +struct CallReturnHandler : public IncomingValueHandler { + CallReturnHandler(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, + CCAssignFn *AssignFn, MachineInstrBuilder &MIB) + : IncomingValueHandler(MIRBuilder, MRI, AssignFn), MIB(MIB) {} + + void markPhysRegUsed(unsigned PhysReg) override { + MIB.addDef(PhysReg, RegState::Implicit); + } + +protected: + MachineInstrBuilder &MIB; +}; + +} // end anonymous namespace + +void Z80CallLowering::splitToValueTypes(const ArgInfo &OrigArg, + SmallVectorImpl &SplitArgs, + const DataLayout &DL, + MachineRegisterInfo &MRI) const { + const Z80TargetLowering &TLI = *getTLI(); + LLVMContext &Ctx = OrigArg.Ty->getContext(); + + if (OrigArg.Ty->isVoidTy()) + return; + + SmallVector SplitVTs; + SmallVector Offsets; + ComputeValueVTs(TLI, DL, OrigArg.Ty, SplitVTs, &Offsets, 0); + + for (unsigned I = 0, E = SplitVTs.size(); I != E; ++I) { + Type *SplitTy = SplitVTs[I].getTypeForEVT(Ctx); + SplitArgs.emplace_back(OrigArg.Regs[I], SplitTy, OrigArg.Flags[0], + OrigArg.IsFixed); + } +} + +/// Return true if the calling convention is one that we can guarantee TCO for. +static bool canGuaranteeTCO(CallingConv::ID CC) { + return CC == CallingConv::Fast; +} + +/// Return true if we might ever do TCO for calls with this calling convention. +static bool mayTailCallThisCC(CallingConv::ID CC) { + switch (CC) { + case CallingConv::C: + case CallingConv::PreserveMost: + case CallingConv::Z80_LibCall: + case CallingConv::Z80_LibCall_AB: + case CallingConv::Z80_LibCall_AC: + case CallingConv::Z80_LibCall_BC: + case CallingConv::Z80_LibCall_L: + case CallingConv::Z80_LibCall_F: + case CallingConv::Z80_TIFlags: + return true; + default: + return canGuaranteeTCO(CC); + } +} + +bool Z80CallLowering::doCallerAndCalleePassArgsTheSameWay( + CallLoweringInfo &Info, MachineFunction &MF, + SmallVectorImpl &InArgs) const { + const Function &CallerF = MF.getFunction(); + CallingConv::ID CalleeCC = Info.CallConv; + CallingConv::ID CallerCC = CallerF.getCallingConv(); + + // If the calling conventions match, then everything must be the same. + if (CalleeCC == CallerCC) + return true; + + // Check if the caller and callee will handle arguments in the same way. + if (!resultsCompatible(Info, MF, InArgs, CC_Z80, CC_Z80, CC_Z80, CC_Z80)) + return false; + + // Make sure that the caller and callee preserve all of the same registers. + const auto &TRI = *MF.getSubtarget().getRegisterInfo(); + const uint32_t *CallerPreserved = TRI.getCallPreservedMask(MF, CallerCC); + const uint32_t *CalleePreserved = TRI.getCallPreservedMask(MF, CalleeCC); + + return TRI.regmaskSubsetEqual(CallerPreserved, CalleePreserved); +} + +bool Z80CallLowering::areCalleeOutgoingArgsTailCallable( + CallLoweringInfo &Info, MachineFunction &MF, + SmallVectorImpl &OutArgs) const { + // If there are no outgoing arguments, then we are done. + if (OutArgs.empty()) + return true; + + const Function &CallerF = MF.getFunction(); + CallingConv::ID CalleeCC = Info.CallConv; + CallingConv::ID CallerCC = CallerF.getCallingConv(); + + // We have outgoing arguments. Make sure that we can tail call with them. + SmallVector OutLocs; + CCState OutInfo(CalleeCC, false, MF, OutLocs, CallerF.getContext()); + + if (!analyzeArgInfo(OutInfo, OutArgs, CC_Z80, CC_Z80)) { + LLVM_DEBUG(dbgs() << "... Could not analyze call operands.\n"); + return false; + } + + // Make sure that they can fit on the caller's stack. + const auto &FuncInfo = *MF.getInfo(); + if (OutInfo.getNextStackOffset() > FuncInfo.getArgFrameSize()) { + LLVM_DEBUG(dbgs() << "... Cannot fit call operands on caller's stack.\n"); + return false; + } + + // Verify that the parameters in callee-saved registers match. + // TODO: Port this over to CallLowering as general code once swiftself is + // supported. + const auto &TRI = *MF.getSubtarget().getRegisterInfo(); + const uint32_t *CallerPreservedMask = TRI.getCallPreservedMask(MF, CallerCC); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + for (unsigned i = 0; i < OutLocs.size(); ++i) { + auto &ArgLoc = OutLocs[i]; + // If it's not a register, it's fine. + if (!ArgLoc.isRegLoc()) { + if (Info.IsVarArg) { + // Be conservative and disallow variadic memory operands to match SDAG's + // behaviour. + // FIXME: If the caller's calling convention is C, then we can + // potentially use its argument area. However, for cases like fastcc, + // we can't do anything. + LLVM_DEBUG( + dbgs() + << "... Cannot tail call vararg function with stack arguments\n"); + return false; + } + continue; + } + + Register Reg = ArgLoc.getLocReg(); + + // Only look at callee-saved registers. + if (MachineOperand::clobbersPhysReg(CallerPreservedMask, Reg)) + continue; + + LLVM_DEBUG( + dbgs() + << "... Call has an argument passed in a callee-saved register.\n"); + + // Check if it was copied from. + ArgInfo &OutInfo = OutArgs[i]; + + if (OutInfo.Regs.size() > 1) { + LLVM_DEBUG( + dbgs() << "... Cannot handle arguments in multiple registers.\n"); + return false; + } + + // Check if we copy the register, walking through copies from virtual + // registers. Note that getDefIgnoringCopies does not ignore copies from + // physical registers. + MachineInstr *RegDef = getDefIgnoringCopies(OutInfo.Regs[0], MRI); + if (!RegDef || RegDef->getOpcode() != TargetOpcode::COPY) { + LLVM_DEBUG( + dbgs() + << "... Parameter was not copied into a VReg, cannot tail call.\n"); + return false; + } + + // Got a copy. Verify that it's the same as the register we want. + Register CopyRHS = RegDef->getOperand(1).getReg(); + if (CopyRHS != Reg) { + LLVM_DEBUG(dbgs() << "... Callee-saved register was not copied into " + "VReg, cannot tail call.\n"); + return false; + } + } + + return true; +} + +bool Z80CallLowering::isEligibleForTailCallOptimization( + MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info, + SmallVectorImpl &InArgs, SmallVectorImpl &OutArgs) const { + + // Must pass all target-independent checks in order to tail call optimize. + if (!Info.IsTailCall) + return false; + + CallingConv::ID CalleeCC = Info.CallConv; + MachineFunction &MF = MIRBuilder.getMF(); + const Function &CallerF = MF.getFunction(); + + LLVM_DEBUG(dbgs() << "Attempting to lower call as tail call\n"); + + if (Info.SwiftErrorVReg) { + // TODO: We should handle this. + // Note that this is also handled by the check for no outgoing arguments. + // Proactively disabling this though, because the swifterror handling in + // lowerCall inserts a COPY *after* the location of the call. + LLVM_DEBUG(dbgs() << "... Cannot handle tail calls with swifterror yet.\n"); + return false; + } + + if (!mayTailCallThisCC(CalleeCC)) { + LLVM_DEBUG(dbgs() << "... Calling convention cannot be tail called.\n"); + return false; + } + + // Byval parameters hand the function a pointer directly into the stack area + // we want to reuse during a tail call. Working around this *is* possible (see + // X86). + // + // FIXME: In Z80ISelLowering, this isn't worked around. Can/should we try it? + // + // FIXME: Check whether the callee also has an "inreg" argument. + // + // When the caller has a swifterror argument, we don't want to tail call + // because would have to move into the swifterror register before the + // tail call. + if (any_of(CallerF.args(), [](const Argument &A) { + return A.hasByValAttr() || A.hasInRegAttr() || A.hasSwiftErrorAttr(); + })) { + LLVM_DEBUG(dbgs() << "... Cannot tail call from callers with byval, " + "inreg, or swifterror arguments\n"); + return false; + } + + // If we have -tailcallopt, then we're done. + if (MF.getTarget().Options.GuaranteedTailCallOpt) + return canGuaranteeTCO(CalleeCC) && CalleeCC == CallerF.getCallingConv(); + + // We don't have -tailcallopt, so we're allowed to change the ABI (sibcall). + // Try to find cases where we can do that. + + // I want anyone implementing a new calling convention to think long and hard + // about this assert. + assert((!Info.IsVarArg || CalleeCC == CallingConv::C) && + "Unexpected variadic calling convention"); + + // Verify that the incoming and outgoing arguments from the callee are + // safe to tail call. + if (!doCallerAndCalleePassArgsTheSameWay(Info, MF, InArgs)) { + LLVM_DEBUG( + dbgs() + << "... Caller and callee have incompatible calling conventions.\n"); + return false; + } + + if (!areCalleeOutgoingArgsTailCallable(Info, MF, OutArgs)) + return false; + + LLVM_DEBUG(dbgs() << "... Call is eligible for tail call optimization.\n"); + return true; +} + +bool Z80CallLowering::lowerTailCall(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info, + SmallVectorImpl &OutArgs) const { + MachineFunction &MF = MIRBuilder.getMF(); + const Function &F = MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const auto &STI = MF.getSubtarget(); + const Z80InstrInfo &TII = *STI.getInstrInfo(); + const Z80RegisterInfo &TRI = *STI.getRegisterInfo(); + const auto &FuncInfo = *MF.getInfo(); + + // True when we're tail calling, but without -tailcallopt. + bool IsSibCall = !MF.getTarget().Options.GuaranteedTailCallOpt; + + // TODO: Right now, regbankselect doesn't know how to handle the rtcGPR64 + // register class. Until we can do that, we should fall back here. + if (F.hasFnAttribute("branch-target-enforcement")) { + LLVM_DEBUG( + dbgs() << "Cannot lower indirect tail calls with BTI enabled yet.\n"); + return false; + } + + MachineInstrBuilder CallSeqStart; + if (!IsSibCall) + CallSeqStart = MIRBuilder.buildInstr(TII.getCallFrameSetupOpcode()); + + bool Is24Bit = STI.is24Bit(); + unsigned TCRetOpc = Info.Callee.isReg() + ? Is24Bit ? Z80::TCRETURN24r : Z80::TCRETURN16r + : Is24Bit ? Z80::TCRETURN24 : Z80::TCRETURN16; + auto MIB = MIRBuilder.buildInstrNoInsert(TCRetOpc).add(Info.Callee) + .addRegMask(TRI.getCallPreservedMask(MF, Info.CallConv)); + + // FPDiff is the byte offset of the call's argument area from the callee's. + // Stores to callee stack arguments will be placed in FixedStackSlots offset + // by this amount for a tail call. In a sibling call it must be 0 because the + // caller will deallocate the entire stack and the callee still expects its + // arguments to begin at SP+0. + int FPDiff = 0; + + // This will be 0 for sibcalls, potentially nonzero for tail calls produced + // by -tailcallopt. For sibcalls, the memory operands for the call are + // already available in the caller's incoming argument space. + unsigned NumBytes = 0; + if (!IsSibCall) { + // We aren't sibcalling, so we need to compute FPDiff. We need to do this + // before handling assignments, because FPDiff must be known for memory + // arguments. + unsigned NumReusableBytes = FuncInfo.getArgFrameSize(); + SmallVector OutLocs; + CCState OutInfo(Info.CallConv, false, MF, OutLocs, F.getContext()); + analyzeArgInfo(OutInfo, OutArgs, CC_Z80, CC_Z80); + + // FPDiff will be negative if this tail call requires more space than we + // would automatically have in our incoming argument space. Positive if we + // actually shrink the stack. + FPDiff = NumReusableBytes - NumBytes; + } + + // Do the actual argument marshalling. + SmallVector PhysRegs; + TailCallArgHandler Handler(MIRBuilder, MRI, MIB, CC_Z80, FPDiff); + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, OutArgs, + Handler)) + return false; + + // If we have -tailcallopt, we need to adjust the stack. We'll do the call + // sequence start and end here. + if (!IsSibCall) { + MIB->getOperand(1).setImm(FPDiff); + CallSeqStart.addImm(NumBytes).addImm(0); + // End the call sequence *before* emitting the call. Normally, we would + // tidy the frame up after the call. However, here, we've laid out the + // parameters so that when SP is reset, they will be in the correct + // location. + MIRBuilder.buildInstr(TII.getCallFrameDestroyOpcode()) + .addImm(NumBytes).addImm(0); + } + + // Now we can add the actual call instruction to the correct basic block. + MIRBuilder.insertInstr(MIB); + + // If Callee is a reg, since it is used by a target specific instruction, + // it must have a register class matching the constraint of that instruction. + if (Info.Callee.isReg()) + MIB->getOperand(0).setReg(constrainOperandRegClass( + MF, TRI, MRI, TII, *MF.getSubtarget().getRegBankInfo(), *MIB, + MIB->getDesc(), Info.Callee, 0)); + + MF.getFrameInfo().setHasTailCall(); + Info.LoweredTailCall = true; + return true; +} + +bool Z80CallLowering::lowerCall(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info) const { + MachineFunction &MF = MIRBuilder.getMF(); + const Function &F = MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const DataLayout &DL = F.getParent()->getDataLayout(); + const auto &STI = MF.getSubtarget(); + const Z80InstrInfo &TII = *STI.getInstrInfo(); + const Z80FrameLowering &TFI = *STI.getFrameLowering(); + const Z80RegisterInfo &TRI = *STI.getRegisterInfo(); + + // Look through bitcasts of the callee. + while (Info.Callee.isReg()) { + if (MachineInstr *MI = MRI.getVRegDef(Info.Callee.getReg())) { + switch (MI->getOpcode()) { + case TargetOpcode::COPY: + case TargetOpcode::G_GLOBAL_VALUE: + case TargetOpcode::G_INTTOPTR: + case TargetOpcode::G_CONSTANT: + Info.Callee = MI->getOperand(1); + continue; + } + } + break; + } + + SmallVector OutArgs; + for (const auto &OrigArg : Info.OrigArgs) { + if (OrigArg.Regs.size() > 1) + return false; + splitToValueTypes(OrigArg, OutArgs, DL, MRI); + } + + SmallVector InArgs; + if (!Info.OrigRet.Ty->isVoidTy()) { + if (Info.OrigRet.Regs.size() > 1) + return false; + splitToValueTypes(Info.OrigRet, InArgs, DL, MRI); + } + + bool CanTailCallOpt = + isEligibleForTailCallOptimization(MIRBuilder, Info, InArgs, OutArgs); + + // We must emit a tail call if we have musttail. + if (Info.IsMustTailCall && !CanTailCallOpt) { + // There are types of incoming/outgoing arguments we can't handle yet, so + // it doesn't make sense to actually die here like in ISelLowering. Instead, + // fall back to SelectionDAG and let it try to handle this. + LLVM_DEBUG(dbgs() << "Failed to lower musttail call as tail call\n"); + return false; + } + + if (CanTailCallOpt) + return lowerTailCall(MIRBuilder, Info, OutArgs); + + auto CallSeqStart = MIRBuilder.buildInstr(TII.getCallFrameSetupOpcode()); + + // Look through bitcasts of the callee. + while (Info.Callee.isReg()) { + if (MachineInstr *MI = MRI.getVRegDef(Info.Callee.getReg())) { + switch (MI->getOpcode()) { + case TargetOpcode::COPY: + case TargetOpcode::G_GLOBAL_VALUE: + case TargetOpcode::G_INTTOPTR: + case TargetOpcode::G_CONSTANT: + Info.Callee = MI->getOperand(1); + continue; + } + } + break; + } + + // Create a temporarily-floating call instruction so we can add the implicit + // uses of arg registers. + bool Is24Bit = STI.is24Bit(); + unsigned CallOpc = Info.Callee.isReg() + ? Is24Bit ? Z80::CALL24r : Z80::CALL16r + : Is24Bit ? Z80::CALL24 : Z80::CALL16; + + auto MIB = MIRBuilder.buildInstrNoInsert(CallOpc) + .add(Info.Callee) + .addRegMask(TRI.getCallPreservedMask(MF, Info.CallConv)); + + // Do the actual argument marshalling. + CallArgHandler Handler(MIRBuilder, MRI, MIB, CC_Z80); + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, OutArgs, + Handler)) + return false; + + // Now we can add the actual call instruction to the correct basic block. + MIRBuilder.insertInstr(MIB); + + // If Callee is a reg, since it is used by a target specific + // instruction, it must have a register class matching the + // constraint of that instruction. + if (Info.Callee.isReg()) + MIB->getOperand(0).setReg(constrainOperandRegClass( + MF, TRI, MRI, TII, *MF.getSubtarget().getRegBankInfo(), *MIB, + MIB->getDesc(), Info.Callee, 0)); + + // Finally we can copy the returned value back into its virtual-register. In + // symmetry with the arguments, the physical register must be an + // implicit-define of the call instruction. + + if (!InArgs.empty()) { + SmallVector NewRegs; + + CallReturnHandler Handler(MIRBuilder, MRI, RetCC_Z80, MIB); + if (!handleAssignments(Info.CallConv, Info.IsVarArg, MIRBuilder, InArgs, + Handler)) + return false; + + if (!NewRegs.empty()) { + SmallVector Indices; + uint64_t Index = 0; + for (Register Reg : NewRegs) { + Indices.push_back(Index); + Index += MRI.getType(Reg).getSizeInBits(); + } + MIRBuilder.buildSequence(Info.OrigRet.Regs[0], NewRegs, Indices); + } + } + + CallSeqStart.addImm(Handler.getFrameSize()) + .addImm(Handler.getPreFrameAdjustment()) + .addImm(Handler.getSetupFrameAdjustment()); + + auto CallSeqEnd = MIRBuilder.buildInstr(TII.getCallFrameDestroyOpcode()) + .addImm(Handler.getFrameTotalSize()) + .addImm(Handler.getDestroyFrameAdjustment()); + + // It is too early to know exactly which method will be used, however + // sometimes a better method can be guaranteed and we can adjust the operands + // accordingly. + for (auto CallSeq : {CallSeqStart, CallSeqEnd}) { + const TargetRegisterClass *ScratchRC = nullptr; + switch (TFI.getOptimalStackAdjustmentMethod( + MF, TII.getFrameAdjustment(*CallSeq))) { + case Z80FrameLowering::SAM_None: + case Z80FrameLowering::SAM_Tiny: + case Z80FrameLowering::SAM_All: + // These methods do not need anything. + break; + case Z80FrameLowering::SAM_Small: + // This method clobbers an R register. + ScratchRC = Is24Bit ? &Z80::R24RegClass : &Z80::R16RegClass; + break; + case Z80FrameLowering::SAM_Large: + // This method also clobbers flags. + CallSeq.addDef(Z80::F, RegState::Implicit | RegState::Dead); + LLVM_FALLTHROUGH; + case Z80FrameLowering::SAM_Medium: + // These methods clobber an A register. + ScratchRC = Is24Bit ? &Z80::A24RegClass : &Z80::A16RegClass; + break; + } + if (ScratchRC) + CallSeq.addDef(MRI.createVirtualRegister(ScratchRC), + RegState::Implicit | RegState::Dead); + } + + return true; +} + +bool Z80CallLowering::lowerFormalArguments( + MachineIRBuilder &MIRBuilder, const Function &F, + ArrayRef> VRegs) const { + MachineFunction &MF = MIRBuilder.getMF(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const DataLayout &DL = MF.getDataLayout(); + auto &FuncInfo = *MF.getInfo(); + + SmallVector SplitArgs; + unsigned Idx = 0; + for (auto &Arg : F.args()) { + if (!DL.getTypeStoreSize(Arg.getType())) + continue; + + // TODO: handle not simple cases. + if (Arg.hasAttribute(Attribute::InReg) || + Arg.hasAttribute(Attribute::SwiftSelf) || + Arg.hasAttribute(Attribute::SwiftError) || + Arg.hasAttribute(Attribute::Nest) || VRegs[Idx].size() > 1) + return false; + + if (Arg.hasAttribute(Attribute::StructRet)) + FuncInfo.setSRetReturnReg(VRegs[Idx][0]); + + ArgInfo OrigArg(VRegs[Idx], Arg.getType()); + setArgFlags(OrigArg, Idx + AttributeList::FirstArgIndex, DL, F); + splitToValueTypes(OrigArg, SplitArgs, DL, MRI); + Idx++; + } + + MachineBasicBlock &MBB = MIRBuilder.getMBB(); + if (!MBB.empty()) + MIRBuilder.setInstr(*MBB.begin()); + + FormalArgHandler Handler(MIRBuilder, MRI, CC_Z80); + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) + return false; + + // Move back to the end of the basic block. + MIRBuilder.setMBB(MBB); + + return true; +} + +bool Z80CallLowering::lowerReturn(MachineIRBuilder &MIRBuilder, + const Value *Val, + ArrayRef VRegs) const { + assert(!Val == VRegs.empty() && "Return value without a vreg"); + MachineFunction &MF = MIRBuilder.getMF(); + LLVMContext &Ctx = MF.getFunction().getContext(); + auto &FuncInfo = *MF.getInfo(); + const auto &STI = MF.getSubtarget(); + auto MIB = + MIRBuilder.buildInstrNoInsert(STI.is24Bit() ? Z80::RET24 : Z80::RET16); + + Register SRetReturnReg = FuncInfo.getSRetReturnReg(); + assert((!SRetReturnReg || VRegs.empty()) && + "Struct ret should have void return"); + Type *RetTy = nullptr; + if (SRetReturnReg) { + VRegs = SRetReturnReg; + RetTy = Type::getInt8PtrTy(Ctx); + } else if (!VRegs.empty()) + RetTy = Val->getType(); + + if (!VRegs.empty()) { + const Function &F = MF.getFunction(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const DataLayout &DL = MF.getDataLayout(); + const Z80TargetLowering &TLI = *getTLI(); + + SmallVector SplitEVTs; + ComputeValueVTs(TLI, DL, RetTy, SplitEVTs); + assert(VRegs.size() == SplitEVTs.size() && + "For each split Type there should be exactly one VReg."); + + SmallVector SplitArgs; + for (unsigned I = 0; I < SplitEVTs.size(); ++I) { + ArgInfo CurArgInfo = ArgInfo{VRegs[I], SplitEVTs[I].getTypeForEVT(Ctx)}; + setArgFlags(CurArgInfo, AttributeList::ReturnIndex, DL, F); + splitToValueTypes(CurArgInfo, SplitArgs, DL, MRI); + } + + OutgoingValueHandler Handler(MIRBuilder, MRI, MIB, RetCC_Z80); + if (!handleAssignments(F.getCallingConv(), F.isVarArg(), MIRBuilder, + SplitArgs, Handler)) + return false; + } + + MIRBuilder.insertInstr(MIB); + return true; +} diff --git a/llvm/lib/Target/Z80/GISel/Z80CallLowering.h b/llvm/lib/Target/Z80/GISel/Z80CallLowering.h new file mode 100644 index 00000000000000..adebd20c3741d6 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80CallLowering.h @@ -0,0 +1,63 @@ +//===- llvm/lib/Target/Z80/Z80CallLowering.h - Call lowering ----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// This file describes how to lower LLVM calls to machine code calls. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80CALLLOWERING_H +#define LLVM_LIB_TARGET_Z80_Z80CALLLOWERING_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" + +namespace llvm { + +class Z80TargetLowering; + +class Z80CallLowering : public CallLowering { +public: + Z80CallLowering(const Z80TargetLowering &TLI); + + bool lowerCall(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info) const override; + + bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F, + ArrayRef> VRegs) const override; + + bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val, + ArrayRef VRegs) const override; + +private: + void splitToValueTypes(const ArgInfo &OrigArg, + SmallVectorImpl &SplitArgs, + const DataLayout &DL, MachineRegisterInfo &MRI) const; + + bool + doCallerAndCalleePassArgsTheSameWay(CallLoweringInfo &Info, + MachineFunction &MF, + SmallVectorImpl &InArgs) const; + + bool + areCalleeOutgoingArgsTailCallable(CallLoweringInfo &Info, MachineFunction &MF, + SmallVectorImpl &OutArgs) const; + + bool + isEligibleForTailCallOptimization(MachineIRBuilder &MIRBuilder, + CallLoweringInfo &Info, + SmallVectorImpl &InArgs, + SmallVectorImpl &OutArgs) const; + + bool lowerTailCall(MachineIRBuilder &MIRBuilder, CallLoweringInfo &Info, + SmallVectorImpl &OutArgs) const; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_Z80_Z80CALLLOWERING_H diff --git a/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp b/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp new file mode 100644 index 00000000000000..64a1a6a38fa34a --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80InstructionSelector.cpp @@ -0,0 +1,1483 @@ +//===- Z80InstructionSelector.cpp -----------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the InstructionSelector class for +/// Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80RegisterBankInfo.h" +#include "Z80Subtarget.h" +#include "Z80TargetMachine.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h" +#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +#include "llvm/Support/Debug.h" + +using namespace llvm; +using namespace MIPatternMatch; + +#define DEBUG_TYPE "Z80-isel" + +namespace { + +#define GET_GLOBALISEL_PREDICATE_BITSET +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATE_BITSET + +class Z80InstructionSelector : public InstructionSelector { +public: + Z80InstructionSelector(const Z80TargetMachine &TM, const Z80Subtarget &STI, + const Z80RegisterBankInfo &RBI); + + bool select(MachineInstr &I) const; + bool select(MachineInstr &I) override { + return static_cast(this)->select(I); + } + static const char *getName() { return DEBUG_TYPE; } + +private: + /// tblgen-erated 'select' implementation, used as the initial selector for + /// the patterns that don't require complex C++. + bool selectImpl(MachineInstr &I, CodeGenCoverage &CoverageInfo) const; + + bool selectConstant(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectTrunc(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectSExt(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectZExt(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectAnyExt(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectLoadStore(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + bool selectFrameIndexOrGep(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + + bool selectCopy(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectUnmergeValues(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + bool selectMergeValues(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + + Z80::CondCode foldCompare(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const; + Z80::CondCode foldExtendedAddSub(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const; + Z80::CondCode foldSetCC(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const; + Z80::CondCode foldCond(Register CondReg, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const; + bool selectShift(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectFunnelShift(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectSetCond(MachineInstr &I, MachineRegisterInfo &MRI) const; + + bool selectSelect(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectBrCond(MachineInstr &I, MachineRegisterInfo &MRI) const; + bool selectBrJT(MachineInstr &I, MachineRegisterInfo &MRI, + MachineFunction &MF) const; + bool selectImplicitDefOrPHI(MachineInstr &I, MachineRegisterInfo &MRI) const; + + ComplexRendererFns selectMem(MachineOperand &Root) const; + ComplexRendererFns selectOff(MachineOperand &Root) const; + + const TargetRegisterClass *getRegClass(LLT Ty, const RegisterBank &RB) const; + const TargetRegisterClass *getRegClass(LLT Ty, unsigned Reg, + MachineRegisterInfo &MRI) const; + + const Z80TargetMachine &TM; + const Z80Subtarget &STI; + const Z80InstrInfo &TII; + const Z80RegisterInfo &TRI; + const Z80RegisterBankInfo &RBI; + +#define GET_GLOBALISEL_PREDICATES_DECL +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_DECL + +#define GET_GLOBALISEL_TEMPORARIES_DECL +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_DECL +}; + +} // end anonymous namespace + +#define GET_GLOBALISEL_IMPL +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_IMPL + +Z80InstructionSelector::Z80InstructionSelector(const Z80TargetMachine &TM, + const Z80Subtarget &STI, + const Z80RegisterBankInfo &RBI) + : InstructionSelector(), TM(TM), STI(STI), TII(*STI.getInstrInfo()), + TRI(*STI.getRegisterInfo()), RBI(RBI), +#define GET_GLOBALISEL_PREDICATES_INIT +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_PREDICATES_INIT +#define GET_GLOBALISEL_TEMPORARIES_INIT +#include "Z80GenGlobalISel.inc" +#undef GET_GLOBALISEL_TEMPORARIES_INIT +{ + (void)this->TM; +} + +// FIXME: This should be target-independent, inferred from the types declared +// for each class in the bank. +const TargetRegisterClass * +Z80InstructionSelector::getRegClass(LLT Ty, const RegisterBank &RB) const { + if (RB.getID() == Z80::GPRRegBankID) { + if (Ty.getSizeInBits() <= 8) + return &Z80::R8RegClass; + if (Ty.getSizeInBits() == 16) + return &Z80::R16RegClass; + if (Ty.getSizeInBits() == 24) + return &Z80::R24RegClass; + } + + llvm_unreachable("Unknown RegBank!"); +} + +const TargetRegisterClass * +Z80InstructionSelector::getRegClass(LLT Ty, unsigned Reg, + MachineRegisterInfo &MRI) const { + const RegisterBank &RegBank = *RBI.getRegBank(Reg, MRI, TRI); + return getRegClass(Ty, RegBank); +} + +static int64_t getSubRegIndex(const TargetRegisterClass *RC) { + unsigned SubIdx = Z80::NoSubRegister; + if (RC == &Z80::R16RegClass) + SubIdx = Z80::sub_short; + else if (RC == &Z80::R8RegClass) + SubIdx = Z80::sub_low; + return SubIdx; +} + +static const TargetRegisterClass *getRegClassFromGRPhysReg(Register Reg) { + assert(Reg.isPhysical()); + for (auto *RC : {&Z80::R8RegClass, &Z80::F8RegClass, &Z80::R16RegClass, + &Z80::Z16RegClass, &Z80::R24RegClass, &Z80::Z24RegClass}) + if (RC->contains(Reg)) + return RC; + llvm_unreachable("Unknown RegClass for PhysReg!"); +} + +// Set Z80 Opcode and constrain DstReg. +bool Z80InstructionSelector::selectCopy(MachineInstr &I, + MachineRegisterInfo &MRI) const { + Register DstReg = I.getOperand(0).getReg(); + const unsigned DstSize = RBI.getSizeInBits(DstReg, MRI, TRI); + const RegisterBank &DstRegBank = *RBI.getRegBank(DstReg, MRI, TRI); + + Register SrcReg = I.getOperand(1).getReg(); + const unsigned SrcSize = RBI.getSizeInBits(SrcReg, MRI, TRI); + const RegisterBank &SrcRegBank = *RBI.getRegBank(SrcReg, MRI, TRI); + + if (DstReg.isPhysical()) { + assert(I.isCopy() && "Generic operators do not allow physical registers"); + + if (DstReg == Z80::F && + !RBI.constrainGenericRegister(SrcReg, Z80::F8RegClass, MRI)) + return false; + + if (DstSize > SrcSize && SrcRegBank.getID() == Z80::GPRRegBankID && + DstRegBank.getID() == Z80::GPRRegBankID) { + + const TargetRegisterClass *SrcRC = + getRegClass(MRI.getType(SrcReg), SrcRegBank); + const TargetRegisterClass *DstRC = getRegClassFromGRPhysReg(DstReg); + + if (SrcRC != DstRC) + // This case can be generated by ABI lowering, perform anyext + I.getOperand(1).setReg( + MachineIRBuilder(I) + .buildInstr(TargetOpcode::SUBREG_TO_REG, {DstRC}, + {int64_t(0), SrcReg, getSubRegIndex(SrcRC)}) + .getReg(0)); + } + + return true; + } + + assert((!SrcReg.isPhysical() || I.isCopy()) && + "No phys reg on generic operators"); + assert((DstSize == SrcSize || + // Copies are a means to setup initial types, the number of + // bits may not exactly match. + (SrcReg.isPhysical() && + DstSize <= RBI.getSizeInBits(SrcReg, MRI, TRI))) && + "Copy with different width?!"); + + const TargetRegisterClass *DstRC = MRI.getRegClassOrNull(DstReg); + if (!DstRC) + DstRC = getRegClass(MRI.getType(DstReg), DstRegBank); + + if (SrcRegBank.getID() == Z80::GPRRegBankID && + DstRegBank.getID() == Z80::GPRRegBankID && SrcSize > DstSize && + SrcReg.isPhysical()) { + // Change the physical register to perform truncate. + + const TargetRegisterClass *SrcRC = getRegClassFromGRPhysReg(SrcReg); + + if (DstRC != SrcRC) { + I.getOperand(1).setSubReg(getSubRegIndex(DstRC)); + I.getOperand(1).substPhysReg(SrcReg, TRI); + } + } + + // No need to constrain SrcReg. It will get constrained when + // we hit another of its use or its defs. + // Copies do not have constraints. + const TargetRegisterClass *OldRC = MRI.getRegClassOrNull(DstReg); + if (!OldRC || !DstRC->hasSubClassEq(OldRC)) { + if (!RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) { + LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << " operand\n"); + return false; + } + } + I.setDesc(TII.get(Z80::COPY)); + return true; +} + +bool Z80InstructionSelector::select(MachineInstr &I) const { + assert(I.getParent() && "Instruction should be in a basic block!"); + assert(I.getParent()->getParent() && "Instruction should be in a function!"); + + MachineBasicBlock &MBB = *I.getParent(); + MachineFunction &MF = *MBB.getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + + unsigned Opc = I.getOpcode(); + if (!isPreISelGenericOpcode(Opc)) { + switch (I.getOpcode()) { + case TargetOpcode::COPY: + return selectCopy(I, MRI); + case Z80::SetCC: + return selectSetCond(I, MRI); + } + return true; + } + + assert(I.getNumOperands() == I.getNumExplicitOperands() && + "Generic instruction has unexpected implicit operands"); + + if (selectImpl(I, *CoverageInfo)) + return true; + + LLVM_DEBUG(dbgs() << " C++ instruction selection: "; I.print(dbgs())); + + // TODO: This should be implemented by tblgen. + switch (I.getOpcode()) { + case TargetOpcode::G_CONSTANT: + case TargetOpcode::G_GLOBAL_VALUE: + case TargetOpcode::G_JUMP_TABLE: + return selectConstant(I, MRI); + case TargetOpcode::G_TRUNC: + return selectTrunc(I, MRI); + case TargetOpcode::G_PTRTOINT: + case TargetOpcode::G_INTTOPTR: + case TargetOpcode::G_FREEZE: + return selectCopy(I, MRI); + case TargetOpcode::G_SEXT: + return selectSExt(I, MRI); + case TargetOpcode::G_ZEXT: + return selectZExt(I, MRI); + case TargetOpcode::G_ANYEXT: + return selectAnyExt(I, MRI); + case TargetOpcode::G_LOAD: + case TargetOpcode::G_STORE: + return selectLoadStore(I, MRI, MF); + case TargetOpcode::G_PTR_ADD: + case TargetOpcode::G_FRAME_INDEX: + return selectFrameIndexOrGep(I, MRI, MF); + case TargetOpcode::G_UNMERGE_VALUES: + return selectUnmergeValues(I, MRI, MF); + case TargetOpcode::G_MERGE_VALUES: + return selectMergeValues(I, MRI, MF); + case TargetOpcode::G_SELECT: + return selectSelect(I, MRI); + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: + return selectShift(I, MRI); + case TargetOpcode::G_FSHL: + case TargetOpcode::G_FSHR: + return selectFunnelShift(I, MRI); + case TargetOpcode::G_UADDO: + case TargetOpcode::G_UADDE: + case TargetOpcode::G_USUBO: + case TargetOpcode::G_USUBE: + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: + case TargetOpcode::G_ICMP: + return selectSetCond(I, MRI); + case TargetOpcode::G_BRCOND: + return selectBrCond(I, MRI); + case TargetOpcode::G_BRINDIRECT: + I.setDesc(TII.get(STI.is24Bit() ? Z80::JP24r : Z80::JP16r)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + case TargetOpcode::G_BRJT: + return selectBrJT(I, MRI, MF); + case TargetOpcode::G_IMPLICIT_DEF: + case TargetOpcode::G_PHI: + return selectImplicitDefOrPHI(I, MRI); + default: + return false; + } +} + +bool Z80InstructionSelector::selectConstant(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_CONSTANT || + I.getOpcode() == TargetOpcode::G_GLOBAL_VALUE || + I.getOpcode() == TargetOpcode::G_JUMP_TABLE) && + "unexpected instruction"); + + const Register DefReg = I.getOperand(0).getReg(); + LLT Ty = MRI.getType(DefReg); + + unsigned NewOpc; + switch (Ty.getSizeInBits()) { + case 8: + NewOpc = Z80::LD8ri; + break; + case 16: + NewOpc = Z80::LD16ri; + break; + case 24: + assert(STI.is24Bit() && "Illegal operand size."); + NewOpc = Z80::LD24ri; + break; + default: + llvm_unreachable("Unsupported type."); + } + + I.setDesc(TII.get(NewOpc)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectTrunc(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_TRUNC) && "unexpected instruction"); + + const Register DstReg = I.getOperand(0).getReg(); + const Register SrcReg = I.getOperand(1).getReg(); + + const LLT DstTy = MRI.getType(DstReg); + const LLT SrcTy = MRI.getType(SrcReg); + + const RegisterBank &DstRB = *RBI.getRegBank(DstReg, MRI, TRI); + const RegisterBank &SrcRB = *RBI.getRegBank(SrcReg, MRI, TRI); + + const TargetRegisterClass *DstRC = getRegClass(DstTy, DstRB); + const TargetRegisterClass *SrcRC = getRegClass(SrcTy, SrcRB); + + if (!DstRC || !SrcRC) + return false; + + unsigned SubIdx; + if (DstRC == SrcRC) + // Nothing to be done + SubIdx = Z80::NoSubRegister; + else if (DstRC == &Z80::R8RegClass) + SubIdx = Z80::sub_low; + else if (DstRC == &Z80::R16RegClass) + SubIdx = Z80::sub_short; + else + return false; + + SrcRC = TRI.getSubClassWithSubReg(SrcRC, SubIdx); + + if (!RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI) || + !RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) { + LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << "\n"); + return false; + } + + I.getOperand(1).setSubReg(SubIdx); + + I.setDesc(TII.get(Z80::COPY)); + return true; +} + +bool Z80InstructionSelector::selectSExt(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_SEXT) && "unexpected instruction"); + + const Register DstReg = I.getOperand(0).getReg(); + const Register SrcReg = I.getOperand(1).getReg(); + + const LLT DstTy = MRI.getType(DstReg); + const LLT SrcTy = MRI.getType(SrcReg); + + if (SrcTy != LLT::scalar(1)) + return false; + + MachineIRBuilder MIB(I); + + unsigned FillOpc; + Register FillReg; + const TargetRegisterClass *FillRC; + switch (DstTy.getSizeInBits()) { + case 8: + FillOpc = Z80::SBC8ar; + FillReg = Z80::A; + FillRC = &Z80::R8RegClass; + break; + case 16: + FillOpc = Z80::SBC16aa; + FillReg = Z80::HL; + FillRC = &Z80::R16RegClass; + break; + case 24: + FillOpc = Z80::SBC24aa; + FillReg = Z80::UHL; + FillRC = &Z80::R24RegClass; + break; + default: + return false; + } + + auto Rotate = MIB.buildInstr(Z80::RRC8r, {LLT::scalar(8)}, {SrcReg}); + if (!constrainSelectedInstRegOperands(*Rotate, TII, TRI, RBI)) + return false; + auto Fill = MIB.buildInstr(FillOpc); + Fill->findRegisterUseOperand(FillReg)->setIsUndef(); + if (FillOpc == Z80::SBC8ar) + Fill.addReg(FillReg, RegState::Undef); + if (!constrainSelectedInstRegOperands(*Fill, TII, TRI, RBI)) + return false; + auto CopyFromReg = MIB.buildCopy(DstReg, FillReg); + if (!RBI.constrainGenericRegister(CopyFromReg.getReg(0), *FillRC, MRI)) + return false; + + I.eraseFromParent(); + return true; +} + +bool Z80InstructionSelector::selectZExt(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_ZEXT) && "unexpected instruction"); + + Register DstReg = I.getOperand(0).getReg(); + Register SrcReg = I.getOperand(1).getReg(); + + if (MRI.getType(SrcReg) != LLT::scalar(1)) + return false; + + MachineIRBuilder MIB(I); + + auto CopyToA = MIB.buildCopy(Z80::A, SrcReg); + if (!constrainSelectedInstRegOperands(*CopyToA, TII, TRI, RBI)) + return false; + auto And = MIB.buildInstr(Z80::AND8ai, {}, {int64_t(1)}); + if (!constrainSelectedInstRegOperands(*And, TII, TRI, RBI)) + return false; + + auto CopyFromA = MIB.buildCopy(LLT::scalar(8), Register(Z80::A)); + if (!RBI.constrainGenericRegister(CopyFromA.getReg(0), Z80::R8RegClass, MRI)) + return false; + + auto ZExt = MIB.buildZExtOrTrunc(DstReg, CopyFromA); + + I.eraseFromParent(); + return select(*ZExt); +} + +bool Z80InstructionSelector::selectAnyExt(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_ANYEXT) && "unexpected instruction"); + + const Register DstReg = I.getOperand(0).getReg(); + const Register SrcReg = I.getOperand(1).getReg(); + + const LLT DstTy = MRI.getType(DstReg); + const LLT SrcTy = MRI.getType(SrcReg); + + const RegisterBank &DstRB = *RBI.getRegBank(DstReg, MRI, TRI); + const RegisterBank &SrcRB = *RBI.getRegBank(SrcReg, MRI, TRI); + + assert(DstRB.getID() == SrcRB.getID() && + "G_ANYEXT input/output on different banks\n"); + + assert(DstTy.getSizeInBits() > SrcTy.getSizeInBits() && + "G_ANYEXT incorrect operand size"); + + const TargetRegisterClass *DstRC = getRegClass(DstTy, DstRB); + const TargetRegisterClass *SrcRC = getRegClass(SrcTy, SrcRB); + + if (!RBI.constrainGenericRegister(SrcReg, *SrcRC, MRI) || + !RBI.constrainGenericRegister(DstReg, *DstRC, MRI)) { + LLVM_DEBUG(dbgs() << "Failed to constrain " << TII.getName(I.getOpcode()) + << " operand\n"); + return false; + } + + if (SrcRC == DstRC) { + I.setDesc(TII.get(Z80::COPY)); + return true; + } + + MachineIRBuilder(I).buildInstr(TargetOpcode::SUBREG_TO_REG, {DstReg}, + {int64_t(0), SrcReg, getSubRegIndex(SrcRC)}); + I.eraseFromParent(); + return true; +} + +static bool canFoldLoad(MachineInstr &LoadMI, MachineInstr &TargetMI) { + assert(LoadMI.mayLoad() && "Expected a load"); + MachineBasicBlock *MBB = LoadMI.getParent(); + if (!MBB || TargetMI.getParent() != MBB) + return false; + for (MachineBasicBlock::iterator I(LoadMI), E(MBB->end()); I != E; ++I) { + if (I->isLoadFoldBarrier()) + return false; + if (I == TargetMI) + return true; + } + return false; +} + +bool Z80InstructionSelector::selectLoadStore(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + bool IsLoad = I.getOpcode() == TargetOpcode::G_LOAD; + assert((IsLoad || I.getOpcode() == TargetOpcode::G_STORE) && + "unexpected instruction"); + + Register ValReg = I.getOperand(0).getReg(); + Register PtrReg = I.getOperand(1).getReg(); + MachineInstr *PtrMI = MRI.getVRegDef(PtrReg); + LLT Ty = MRI.getType(ValReg); + + SmallVector RMWOps; + SmallVector MemMOs; + MemMOs.push_back(&I); + MachineInstr *ValMI = MRI.getVRegDef(ValReg); + if (!IsLoad && Ty == LLT::scalar(8) && ValMI) { + unsigned ValLastOpIdx = ValMI->getNumExplicitOperands() - 1; + if (std::next(MachineBasicBlock::iterator(ValMI)) == I && + ValMI->getNumDefs() == 1 && ValLastOpIdx >= 2) { + Register LoadReg = ValMI->getOperand(1).getReg(); + if (MachineInstr *LoadMI = MRI.getVRegDef(LoadReg)) { + if (LoadMI->getOpcode() == TargetOpcode::G_LOAD && + LoadMI->getOperand(1).getReg() == PtrReg && + canFoldLoad(*LoadMI, *ValMI)) { + assert(LoadMI->hasOneMemOperand() && + "Expected load to have one MMO."); + MemMOs.push_back(LoadMI); + Register ImmReg = ValMI->getOperand(ValLastOpIdx).getReg(); + if (auto OffConst = getConstantVRegVal(ImmReg, MRI)) { + switch (ValMI->getOpcode()) { + case TargetOpcode::G_FSHL: + if (ValMI->getOperand(2).getReg() != LoadReg) + break; + if (*OffConst == 1) + RMWOps = {Z80::RLC8p, Z80::RLC8o}; + else if (*OffConst == 7) + RMWOps = {Z80::RRC8p, Z80::RRC8o}; + break; + case TargetOpcode::G_FSHR: + if (ValMI->getOperand(2).getReg() != LoadReg) + break; + if (*OffConst == 1) + RMWOps = {Z80::RLC8p, Z80::RLC8o}; + else if (*OffConst == 7) + RMWOps = {Z80::RRC8p, Z80::RRC8o}; + break; + case TargetOpcode::G_SHL: + if (*OffConst == 1) + RMWOps = {Z80::SLA8p, Z80::SLA8o}; + break; + case TargetOpcode::G_ASHR: + if (*OffConst == 1) + RMWOps = {Z80::SRA8p, Z80::SRA8o}; + break; + case TargetOpcode::G_LSHR: + if (*OffConst == 1) + RMWOps = {Z80::SRL8p, Z80::SRL8o}; + break; + case TargetOpcode::G_AND: + if (isPowerOf2_32(~*OffConst & 0xFF)) + RMWOps = {Z80::RES8bp, Z80::RES8bo, Log2_32(~*OffConst & 0xFF)}; + break; + case TargetOpcode::G_OR: + if (isPowerOf2_32(*OffConst & 0xFF)) + RMWOps = {Z80::SET8bp, Z80::SET8bo, Log2_32(*OffConst & 0xFF)}; + break; + case TargetOpcode::G_ADD: + if (*OffConst == 1) + RMWOps = {Z80::INC8p, Z80::INC8o}; + else if (*OffConst == -1) + RMWOps = {Z80::DEC8p, Z80::DEC8o}; + break; + } + } + } + } + } + } + + I.RemoveOperand(1); + I.RemoveOperand(0); + MachineInstrBuilder MIB(MF, I); + SmallVector MOs; + int32_t Off = 0; + while (PtrMI) { + switch (PtrMI->getOpcode()) { + case TargetOpcode::G_INTTOPTR: + if (MachineInstr *IntMI = MRI.getVRegDef(PtrMI->getOperand(1).getReg())) + if (IntMI->getOpcode() == TargetOpcode::G_CONSTANT) + MOs.push_back(MachineOperand::CreateImm( + IntMI->getOperand(1).getCImm()->getSExtValue() + Off)); + break; + case TargetOpcode::G_GLOBAL_VALUE: + MOs.push_back(PtrMI->getOperand(1)); + MOs.back().setOffset(MOs.back().getOffset() + Off); + break; + case TargetOpcode::G_FRAME_INDEX: + MOs.push_back(PtrMI->getOperand(1)); + MOs.push_back(MachineOperand::CreateImm(Off)); + break; + case TargetOpcode::G_PTR_ADD: + if (auto OffConst = + getConstantVRegVal(PtrMI->getOperand(2).getReg(), MRI)) { + if ((PtrMI = MRI.getVRegDef(PtrMI->getOperand(1).getReg()))) { + Off += *OffConst; + continue; + } + } + break; + } + break; + } + + unsigned Opc; + if (MOs.size() == 1 && (MOs[0].isImm() || MOs[0].isGlobal())) { + if (RMWOps.empty()) { + switch (Ty.getSizeInBits()) { + case 8: { + MachineIRBuilder Builder(I); + MachineInstrBuilder CopyI; + if (IsLoad) { + Builder.setInsertPt(Builder.getMBB(), + std::next(Builder.getInsertPt())); + CopyI = Builder.buildCopy(ValReg, Register(Z80::A)); + Opc = Z80::LD8am; + } else { + CopyI = Builder.buildCopy(Register(Z80::A), ValReg); + Opc = Z80::LD8ma; + } + I.setDesc(TII.get(Opc)); + MIB.add(MOs[0]); + MIB.addReg(Z80::A, getDefRegState(IsLoad) | RegState::Implicit); + return RBI.constrainGenericRegister(ValReg, Z80::R8RegClass, MRI) && + constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + case 16: + if (STI.is24Bit()) + break; + I.setDesc(TII.get(IsLoad ? Z80::LD16rm : Z80::LD16mr)); + if (IsLoad) + MIB.addDef(ValReg); + MIB.add(MOs[0]); + if (!IsLoad) + MIB.addReg(ValReg); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + case 24: + assert(STI.is24Bit() && "Illegal memory access size."); + I.setDesc(TII.get(IsLoad ? Z80::LD24rm : Z80::LD24mr)); + if (IsLoad) + MIB.addDef(ValReg); + MIB.add(MOs[0]); + if (!IsLoad) + MIB.addReg(ValReg); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + } + MOs.clear(); + Off = 0; + } + + if (MOs.empty()) { + if (Off && isInt<8>(Off)) { + MOs.push_back( + MachineOperand::CreateReg(PtrMI->getOperand(0).getReg(), false)); + MOs.push_back(MachineOperand::CreateImm(Off)); + } else + MOs.push_back(MachineOperand::CreateReg(PtrReg, false)); + } + bool IsOff = MOs.size() == 2; + if (RMWOps.empty()) { + Optional ValConst; + switch (Ty.getSizeInBits()) { + case 8: + if (!IsLoad) + ValConst = getConstantVRegVal(ValReg, MRI); + Opc = IsOff ? IsLoad ? Z80::LD8go : ValConst ? Z80::LD8oi : Z80::LD8og + : IsLoad ? Z80::LD8gp : ValConst ? Z80::LD8pi : Z80::LD8pg; + break; + case 16: + Opc = STI.has16BitEZ80Ops() ? IsOff ? IsLoad ? Z80::LD16ro : Z80::LD16or + : IsLoad ? Z80::LD16rp : Z80::LD16pr + : IsOff ? IsLoad ? Z80::LD88ro : Z80::LD88or + : IsLoad ? Z80::LD88rp : Z80::LD88pr; + break; + case 24: + assert(STI.is24Bit() && "Illegal memory access size."); + Opc = IsOff ? IsLoad ? Z80::LD24ro : Z80::LD24or + : IsLoad ? Z80::LD24rp : Z80::LD24pr; + break; + default: + return false; + } + I.setDesc(TII.get(Opc)); + if (IsLoad) + MIB.addDef(ValReg); + for (auto &MO : MOs) + MIB.add(MO); + if (!IsLoad) { + if (ValConst) + MIB.addImm(*ValConst); + else + MIB.addReg(ValReg); + } + } else { + assert(Ty == LLT::scalar(8) && "Expected RMW operation to be 8 bits"); + I.setDesc(TII.get(RMWOps[IsOff])); + if (RMWOps.size() > 2) + MIB.addImm(RMWOps[2]); + for (auto &MO : MOs) + MIB.add(MO); + I.addImplicitDefUseOperands(MF); + MIB.cloneMergedMemRefs(MemMOs); + if (!MRI.use_empty(ValReg)) { + MachineIRBuilder MIB(I); + MIB.setInsertPt(MIB.getMBB(), ++MIB.getInsertPt()); + auto Reload = + MIB.buildInstr(IsOff ? Z80::LD8go : Z80::LD8gp, {ValReg}, {}); + for (auto &MO : MOs) + Reload.add(MO); + Reload.cloneMemRefs(I); + if (!constrainSelectedInstRegOperands(*Reload, TII, TRI, RBI)) + return false; + ValMI->getOperand(0).setReg(MRI.cloneVirtualRegister(ValReg)); + } + } + + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectFrameIndexOrGep(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + bool Is24Bit = STI.is24Bit(); + bool HasLEA = STI.hasEZ80Ops(); + unsigned Opc = I.getOpcode(); + assert( + (Opc == TargetOpcode::G_FRAME_INDEX || Opc == TargetOpcode::G_PTR_ADD) && + "unexpected instruction"); + + if (Opc == TargetOpcode::G_PTR_ADD) { + auto Off = getConstantVRegVal(I.getOperand(2).getReg(), MRI); + if (Off && *Off >= -1 && *Off <= 1) { + I.RemoveOperand(2); + if (!*Off) { + I.setDesc(TII.get(TargetOpcode::COPY)); + return selectCopy(I, MRI); + } + I.setDesc(TII.get(*Off == 1 ? Is24Bit ? Z80::INC24r : Z80::INC16r + : Is24Bit ? Z80::DEC24r : Z80::DEC16r)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + if (!HasLEA || !Off || !isInt<8>(*Off)) { + I.setDesc(TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao)); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); + } + I.getOperand(2).ChangeToImmediate(*Off); + } + + // Use LEA to calculate frame index and GEP + I.setDesc(TII.get(Is24Bit ? Z80::LEA24ro : Z80::LEA16ro)); + + // Make a note if this LEA is illegal. + if (!HasLEA) + MF.getInfo()->setHasIllegalLEA(); + + if (Opc == TargetOpcode::G_FRAME_INDEX) + MachineInstrBuilder(MF, I).addImm(0); + + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectUnmergeValues(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + assert((I.getOpcode() == TargetOpcode::G_UNMERGE_VALUES) && + "unexpected instruction"); + MachineIRBuilder MIB(I); + Register LoReg = I.getOperand(0).getReg(); + Register HiReg = I.getOperand(1).getReg(); + Register SrcReg = I.getOperand(I.getNumOperands() - 1).getReg(); + LLT Ty = MRI.getType(SrcReg); + assert(MRI.getType(LoReg) == LLT::scalar(8) && + MRI.getType(HiReg) == LLT::scalar(8) && Ty.isScalar() && + "Illegal type"); + const TargetRegisterClass *RC; + switch (Ty.getSizeInBits()) { + case 16: + assert(I.getNumOperands() == 3 && "Illegal instruction"); + RC = STI.hasIndexHalfRegs() ? &Z80::R16RegClass : &Z80::G16RegClass; + MIB.buildInstr(TargetOpcode::COPY, {LoReg}, {}) + .addReg(SrcReg, 0, Z80::sub_low); + MIB.buildInstr(TargetOpcode::COPY, {HiReg}, {}) + .addReg(SrcReg, 0, Z80::sub_high); + break; + case 24: { + Register UpReg = I.getOperand(2).getReg(); + assert(STI.is24Bit() && I.getNumOperands() == 4 && + MRI.getType(UpReg) == LLT::scalar(8) && "Illegal instruction"); + RC = &Z80::R24RegClass; + int FI = MF.getFrameInfo().CreateStackObject(3, Align(1), false); + MIB.buildInstr(Z80::LD24or).addFrameIndex(FI).addImm(0).addReg(SrcReg); + MIB.buildInstr(Z80::LD8ro, {UpReg}, {}).addFrameIndex(FI).addImm(2); + MIB.buildInstr(TargetOpcode::COPY, {HiReg}, {}) + .addReg(SrcReg, 0, Z80::sub_high); + MIB.buildInstr(TargetOpcode::COPY, {LoReg}, {}) + .addReg(SrcReg, 0, Z80::sub_low); + if (!RBI.constrainGenericRegister(UpReg, Z80::R8RegClass, MRI)) + return false; + break; + } + default: + llvm_unreachable("Illegal instruction"); + } + I.eraseFromParent(); + return RBI.constrainGenericRegister(LoReg, Z80::R8RegClass, MRI) && + RBI.constrainGenericRegister(HiReg, Z80::R8RegClass, MRI) && + RBI.constrainGenericRegister(SrcReg, *RC, MRI); +} + +bool Z80InstructionSelector::selectMergeValues(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + assert((I.getOpcode() == TargetOpcode::G_MERGE_VALUES) && + "unexpected instruction"); + MachineIRBuilder MIB(I); + Register DstReg = I.getOperand(0).getReg(); + const TargetRegisterClass *RC; + switch (MRI.getType(DstReg).getSizeInBits()) { + case 16: { + assert(I.getNumOperands() == 3 && + MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(2).getReg()) == LLT::scalar(8) && + "Illegal instruction"); + RC = STI.hasIndexHalfRegs() ? &Z80::R16RegClass : &Z80::G16RegClass; + auto SeqI = MIB.buildInstr(TargetOpcode::REG_SEQUENCE, {DstReg}, + {I.getOperand(1), int64_t(Z80::sub_low), + I.getOperand(2), int64_t(Z80::sub_high)}); + if (!constrainSelectedInstRegOperands(*SeqI, TII, TRI, RBI)) + return false; + break; + } + case 24: { + assert(STI.is24Bit() && "Illegal memory access size."); + assert(I.getNumOperands() == 4 && + MRI.getType(I.getOperand(1).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(2).getReg()) == LLT::scalar(8) && + MRI.getType(I.getOperand(3).getReg()) == LLT::scalar(8) && + "Illegal instruction"); + RC = &Z80::R24RegClass; + Register UpReg = I.getOperand(3).getReg(), TmpReg; + unsigned Amt; + if (mi_match(UpReg, MRI, m_GAShr(m_Reg(TmpReg), m_ICst(Amt))) && + Amt == 7) { + auto AddI = MIB.buildInstr(Z80::RLC8r, {LLT::scalar(8)}, {TmpReg}); + auto SbcI = MIB.buildInstr(Z80::SBC24aa); + SbcI->findRegisterUseOperand(Z80::UHL)->setIsUndef(); + TmpReg = MIB.buildCopy(RC, Register(Z80::UHL)).getReg(0); + if (!constrainSelectedInstRegOperands(*AddI, TII, TRI, RBI) || + !constrainSelectedInstRegOperands(*SbcI, TII, TRI, RBI)) + return false; + } else { + UpReg = {}; + int FI = MF.getFrameInfo().CreateStackObject(1, Align(1), false); + auto Store = MIB.buildInstr(Z80::LD8or).addFrameIndex(FI).addImm(0) + .add(I.getOperand(3)); + if (!constrainSelectedInstRegOperands(*Store, TII, TRI, RBI)) + return false; + TmpReg = MIB.buildInstr(Z80::LD24ro, {RC}, {}) + .addFrameIndex(FI).addImm(-2).getReg(0); + } + if (I.getOperand(2).getReg() != UpReg) { + auto InsertI = + MIB.buildInstr(Z80::INSERT_SUBREG, {RC}, + {TmpReg, I.getOperand(2), int64_t(Z80::sub_high)}); + if (!constrainSelectedInstRegOperands(*InsertI, TII, TRI, RBI)) + return false; + TmpReg = InsertI.getReg(0); + } + if (I.getOperand(1).getReg() != UpReg) { + auto InsertI = + MIB.buildInstr(Z80::INSERT_SUBREG, {RC}, + {TmpReg, I.getOperand(1), int64_t(Z80::sub_low)}); + if (!constrainSelectedInstRegOperands(*InsertI, TII, TRI, RBI)) + return false; + TmpReg = InsertI.getReg(0); + } + MIB.buildCopy(DstReg, TmpReg); + break; + } + default: + llvm_unreachable("Illegal instruction"); + } + I.eraseFromParent(); + return RBI.constrainGenericRegister(DstReg, *RC, MRI); +} + +Z80::CondCode +Z80InstructionSelector::foldCompare(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_ICMP && "unexpected instruction"); + const Function &F = MIB.getMF().getFunction(); + bool OptSize = F.hasOptSize(); + + auto Pred = CmpInst::Predicate(I.getOperand(1).getPredicate()); + Register LHSReg = I.getOperand(2).getReg(); + Register RHSReg = I.getOperand(3).getReg(); + LLT OpTy = MRI.getType(LHSReg); + unsigned OpSize = OpTy.getSizeInBits(); + assert(MRI.getType(I.getOperand(0).getReg()) == LLT::scalar(1) && + !OpTy.isVector() && MRI.getType(RHSReg) == OpTy && "Unexpected type"); + + bool IsSigned, IsSwapped, IsConst; + Z80::CondCode CC = + Z80::GetBranchConditionForPredicate(Pred, IsSigned, IsSwapped, IsConst); + auto ConstRHS = getConstantVRegValWithLookThrough(RHSReg, MRI); + if (IsSwapped) { + if (ConstRHS && ConstRHS->Value != int64_t(IsSigned ? maxIntN(OpSize) + : maxUIntN(OpSize))) { + ++ConstRHS->Value; + CC = Z80::GetOppositeBranchCondition(CC); + } else + std::swap(LHSReg, RHSReg); + } + + unsigned Opc, LDIOpc, AddOpc; + Register Reg; + switch (OpSize) { + case 8: + Opc = Z80::CP8ar; + Reg = Z80::A; + break; + case 16: + Opc = Z80::SUB16ao; + LDIOpc = Z80::LD16ri; + AddOpc = Z80::ADD16ao; + Reg = Z80::HL; + break; + case 24: + Opc = Z80::SUB24ao; + LDIOpc = Z80::LD24ri; + AddOpc = Z80::ADD24ao; + Reg = Z80::UHL; + break; + default: + return Z80::COND_INVALID; + } + + if (IsSigned && !OptSize) { + int64_t Sign = minIntN(OpSize); + if (OpSize == 8) { + if (ConstRHS) + ConstRHS->Value += Sign; + else { + auto CopyRHSToA = MIB.buildCopy(Register(Z80::A), RHSReg); + if (!constrainSelectedInstRegOperands(*CopyRHSToA, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto AddRHS = MIB.buildInstr(Z80::ADD8ai, {}, {Sign}); + if (!constrainSelectedInstRegOperands(*AddRHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto CopyRHSFromA = MIB.buildCopy(OpTy, Register(Z80::A)); + if (!constrainSelectedInstRegOperands(*CopyRHSFromA, TII, TRI, RBI)) + return Z80::COND_INVALID; + RHSReg = CopyRHSFromA.getReg(0); + } + auto CopyLHSToA = MIB.buildCopy(Register(Z80::A), LHSReg); + if (!constrainSelectedInstRegOperands(*CopyLHSToA, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto AddLHS = MIB.buildInstr(Z80::ADD8ai, {}, {Sign}); + if (!constrainSelectedInstRegOperands(*AddLHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + auto CopyLHSFromA = MIB.buildCopy(OpTy, Register(Z80::A)); + if (!constrainSelectedInstRegOperands(*CopyLHSFromA, TII, TRI, RBI)) + return Z80::COND_INVALID; + LHSReg = CopyLHSFromA.getReg(0); + if (!RBI.constrainGenericRegister(LHSReg, Z80::R8RegClass, MRI)) + return Z80::COND_INVALID; + } else { + auto LDI = MIB.buildInstr(LDIOpc, {OpTy}, {Sign}); + if (!constrainSelectedInstRegOperands(*LDI, TII, TRI, RBI)) + return Z80::COND_INVALID; + if (ConstRHS) + ConstRHS->Value += Sign; + else { + auto AddRHS = MIB.buildInstr(AddOpc, {OpTy}, {RHSReg, LDI}); + if (!constrainSelectedInstRegOperands(*AddRHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + RHSReg = AddRHS.getReg(0); + } + auto AddLHS = MIB.buildInstr(AddOpc, {OpTy}, {LHSReg, LDI}); + if (!constrainSelectedInstRegOperands(*AddLHS, TII, TRI, RBI)) + return Z80::COND_INVALID; + LHSReg = AddLHS.getReg(0); + } + switch (CC) { + default: + llvm_unreachable("Expected signed condition"); + case Z80::COND_P: + CC = Z80::COND_NC; + break; + case Z80::COND_M: + CC = Z80::COND_C; + break; + } + } + + SmallVector Ops = {RHSReg}; + if (ConstRHS) { + if (!ConstRHS->Value && (CC == Z80::COND_Z || CC == Z80::COND_NZ)) { + if (OpSize == 8) { + Register SrcReg; + uint8_t Mask; + if (mi_match(LHSReg, MRI, + m_OneUse(m_GAnd(m_Reg(SrcReg), m_ICst(Mask)))) && + isPowerOf2_32(Mask)) { + Opc = Z80::BIT8bg; + Reg = {}; + Ops = {uint64_t(findFirstSet(Mask)), SrcReg}; + } else { + Opc = Z80::OR8ar; + Ops = {Reg}; + } + } else { + Opc = OpSize == 24 ? Z80::CP24a0 : Z80::CP16a0; + Ops.clear(); + } + } else if (OpSize == 8) { + Opc = Z80::CP8ai; + Ops = {ConstRHS->Value}; + } else { + auto LDI = MIB.buildInstr(LDIOpc, {OpTy}, {ConstRHS->Value}); + if (!constrainSelectedInstRegOperands(*LDI, TII, TRI, RBI)) + return Z80::COND_INVALID; + Ops = {LDI.getReg(0)}; + } + } + + if (Reg.isValid()) { + auto Copy = MIB.buildCopy(Reg, LHSReg); + if (!constrainSelectedInstRegOperands(*Copy, TII, TRI, RBI)) + return Z80::COND_INVALID; + } + auto Cmp = MIB.buildInstr(Opc, {}, Ops); + if (!constrainSelectedInstRegOperands(*Cmp, TII, TRI, RBI)) + return Z80::COND_INVALID; + if (IsSigned && OptSize) { + LLT s8 = LLT::scalar(8); + Type *Int8Ty = Type::getInt8Ty(F.getContext()); + Register FlagsReg = MIB.buildCopy(s8, Register(Z80::F)).getReg(0); + CallLowering::ArgInfo FlagsArg(FlagsReg, Int8Ty); + Register SignedFlagsReg = MRI.createGenericVirtualRegister(s8); + CallLowering::ArgInfo SignedFlagsArg(SignedFlagsReg, Int8Ty); + createLibcall(MIB, RTLIB::SCMP, SignedFlagsArg, FlagsArg); + MIB.buildCopy(Register(Z80::F), SignedFlagsReg); + if (!RBI.constrainGenericRegister(FlagsReg, Z80::F8RegClass, MRI) || + !RBI.constrainGenericRegister(SignedFlagsReg, Z80::F8RegClass, MRI)) + return Z80::COND_INVALID; + } + return CC; +} + +Z80::CondCode Z80InstructionSelector::foldExtendedAddSub( + MachineInstr &I, MachineIRBuilder &MIB, MachineRegisterInfo &MRI) const { + unsigned Opc = I.getOpcode(); + assert((Opc == TargetOpcode::G_UADDO || Opc == TargetOpcode::G_UADDE || + Opc == TargetOpcode::G_USUBO || Opc == TargetOpcode::G_USUBE || + Opc == TargetOpcode::G_SADDO || Opc == TargetOpcode::G_SADDE || + Opc == TargetOpcode::G_SSUBO || Opc == TargetOpcode::G_SSUBE) && + "unexpected instruction"); + + bool IsAdd = Opc == TargetOpcode::G_UADDO || Opc == TargetOpcode::G_UADDE || + Opc == TargetOpcode::G_SADDO || Opc == TargetOpcode::G_SADDE; + bool IsExtend = Opc == TargetOpcode::G_UADDE || + Opc == TargetOpcode::G_USUBE || + Opc == TargetOpcode::G_SADDE || Opc == TargetOpcode::G_SSUBE; + bool IsSigned = Opc == TargetOpcode::G_SADDO || + Opc == TargetOpcode::G_SADDE || + Opc == TargetOpcode::G_SSUBO || Opc == TargetOpcode::G_SSUBE; + + Register DstReg = I.getOperand(0).getReg(); + Register LHSReg = I.getOperand(2).getReg(); + Register RHSReg = I.getOperand(3).getReg(); + LLT OpTy = MRI.getType(DstReg); + + unsigned AddSubOpc; + Register AddSubReg; + const TargetRegisterClass *AddSubRC; + switch (OpTy.getSizeInBits()) { + case 8: + AddSubOpc = IsAdd ? Z80::ADC8ar : Z80::SBC8ar; + AddSubReg = Z80::A; + AddSubRC = &Z80::R8RegClass; + break; + case 16: + AddSubOpc = IsAdd ? Z80::ADC16ao : Z80::SBC16ao; + AddSubReg = Z80::HL; + AddSubRC = &Z80::R16RegClass; + break; + case 24: + AddSubOpc = IsAdd ? Z80::ADC24ao : Z80::SBC24ao; + AddSubReg = Z80::UHL; + AddSubRC = &Z80::R24RegClass; + break; + default: + return Z80::COND_INVALID; + } + + if (IsExtend) { + Register CarryInReg = I.getOperand(4).getReg(); + Z80::CondCode CC = foldCond(CarryInReg, MIB, MRI); + if (CC == Z80::COND_INVALID) + return CC; + if (CC != Z80::COND_C) { + auto SetCC = MIB.buildInstr(Z80::SetCC, {LLT::scalar(1)}, {int64_t(CC)}); + if (!RBI.constrainGenericRegister(SetCC.getReg(0), Z80::R8RegClass, MRI)) + return Z80::COND_INVALID; + auto BitI = MIB.buildInstr(Z80::RRC8r, {LLT::scalar(8)}, {SetCC}); + if (!constrainSelectedInstRegOperands(*BitI, TII, TRI, RBI)) + return Z80::COND_INVALID; + if (!select(*SetCC)) + return Z80::COND_INVALID; + } + } else + MIB.buildInstr(Z80::RCF); + MIB.buildCopy(AddSubReg, LHSReg); + if (!RBI.constrainGenericRegister(LHSReg, *AddSubRC, MRI)) + return Z80::COND_INVALID; + auto AddSubI = MIB.buildInstr(AddSubOpc, {}, {RHSReg}); + if (!constrainSelectedInstRegOperands(*AddSubI, TII, TRI, RBI)) + return Z80::COND_INVALID; + if (MIB.getInsertPt() == I) { + MIB.buildCopy(DstReg, AddSubReg); + if (!RBI.constrainGenericRegister(DstReg, *AddSubRC, MRI)) + return Z80::COND_INVALID; + } + return IsSigned ? Z80::COND_PE : Z80::COND_C; +} + +Z80::CondCode +Z80InstructionSelector::foldSetCC(MachineInstr &I, MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == Z80::SetCC && "unexpected instruction"); + auto CC = Z80::CondCode(I.getOperand(1).getImm()); + + if (I == MIB.getInsertPt()) + return CC; + + return Z80::COND_INVALID; +} + +Z80::CondCode Z80InstructionSelector::foldCond(Register CondReg, + MachineIRBuilder &MIB, + MachineRegisterInfo &MRI) const { + assert(MRI.getType(CondReg) == LLT::scalar(1) && "Expected s1 condition"); + + MachineInstr *CondDef = nullptr; + while (MachineInstr *LookthroughDef = MRI.getVRegDef(CondReg)) { + if (LookthroughDef != MIB.getInsertPt() && !MRI.hasOneUse(CondReg)) + break; + CondDef = LookthroughDef; + unsigned Opc = CondDef->getOpcode(); + if (Opc != TargetOpcode::COPY && Opc != TargetOpcode::G_TRUNC) + break; + Register SrcReg = CondDef->getOperand(1).getReg(); + if (SrcReg.isPhysical()) + break; + CondReg = SrcReg; + } + + Z80::CondCode CC = Z80::COND_INVALID; + if (CondDef) { + switch (CondDef->getOpcode()) { + case TargetOpcode::G_ICMP: + CC = foldCompare(*CondDef, MIB, MRI); + break; + case TargetOpcode::G_UADDO: + case TargetOpcode::G_UADDE: + case TargetOpcode::G_USUBO: + case TargetOpcode::G_USUBE: + case TargetOpcode::G_SADDO: + case TargetOpcode::G_SADDE: + case TargetOpcode::G_SSUBO: + case TargetOpcode::G_SSUBE: + CC = foldExtendedAddSub(*CondDef, MIB, MRI); + break; + case Z80::SetCC: + CC = foldSetCC(*CondDef, MIB, MRI); + break; + default: + break; + } + } + + if (CC == Z80::COND_INVALID) { + // Fallback to bit test + auto BitI = MIB.buildInstr(Z80::BIT8bg, {}, {int64_t(0), CondReg}); + if (constrainSelectedInstRegOperands(*BitI, TII, TRI, RBI)) + CC = Z80::COND_NZ; + } + + return CC; +} + +bool Z80InstructionSelector::selectShift(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == Z80::G_ASHR && "Unexpected opcode"); + Register DstReg = I.getOperand(0).getReg(); + Register SrcReg = I.getOperand(1).getReg(); + auto Amt = getConstantVRegValWithLookThrough(I.getOperand(2).getReg(), MRI); + assert(Amt && "Expected constant shift amount"); + LLT Ty = MRI.getType(DstReg); + assert(Ty.isScalar() && "Illegal type"); + if (Amt->Value == Ty.getSizeInBits() - 1) { + MachineIRBuilder MIB(I); + Register Reg; + unsigned AddOpc, SbcOpc; + const TargetRegisterClass *RC; + switch (Ty.getSizeInBits()) { + default: + llvm_unreachable("Illegal type"); + case 8: + Reg = Z80::A; + AddOpc = Z80::RLC8r; + SbcOpc = Z80::SBC8ar; + RC = &Z80::R8RegClass; + break; + case 16: + Reg = Z80::HL; + AddOpc = Z80::ADD16aa; + SbcOpc = Z80::SBC16aa; + RC = &Z80::R16RegClass; + break; + case 24: + assert(STI.is24Bit() && "Illegal type"); + Reg = Z80::UHL; + AddOpc = Z80::ADD24aa; + SbcOpc = Z80::SBC24aa; + RC = &Z80::R24RegClass; + break; + } + if (Ty != LLT::scalar(8)) { + MIB.buildCopy(Reg, SrcReg); + if (!RBI.constrainGenericRegister(SrcReg, *RC, MRI)) + return false; + } + auto AddI = MIB.buildInstr(AddOpc, {Ty}, {SrcReg}); + auto SbcI = MIB.buildInstr(SbcOpc); + if (Ty == LLT::scalar(8)) { + SbcI->findRegisterUseOperand(Reg)->setIsUndef(); + SbcI.addReg(Reg, RegState::Undef); + } + MIB.buildCopy(DstReg, Reg); + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*AddI, TII, TRI, RBI) && + constrainSelectedInstRegOperands(*SbcI, TII, TRI, RBI) && + RBI.constrainGenericRegister(DstReg, *RC, MRI); + } + llvm_unreachable("Illegal shift amount"); +} + +bool Z80InstructionSelector::selectFunnelShift(MachineInstr &I, + MachineRegisterInfo &MRI) const { + bool IsLeft = I.getOpcode() == Z80::G_FSHL; + assert((IsLeft || I.getOpcode() == Z80::G_FSHR) && "Unexpected opcode"); + Register DstReg = I.getOperand(0).getReg(); + Register HiReg = I.getOperand(1).getReg(); + Register LoReg = I.getOperand(2).getReg(); + auto Amt = getConstantVRegValWithLookThrough(I.getOperand(3).getReg(), MRI); + LLT Ty = MRI.getType(DstReg); + assert(Ty == LLT::scalar(8) && "Illegal type"); + assert(Amt && (Amt->Value == 1 || Amt->Value == 7) && "Illegal shift amount"); + IsLeft ^= Amt->Value == 7; + MachineIRBuilder MIB(I); + auto ShiftI = MIB.buildInstr(IsLeft ? Z80::RLC8r : Z80::RRC8r, {Ty}, + {IsLeft ? LoReg : HiReg}); + auto RotateI = MIB.buildInstr(IsLeft ? Z80::RL8r : Z80::RR8r, {DstReg}, + {IsLeft ? HiReg : LoReg}); + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*ShiftI, TII, TRI, RBI) && + constrainSelectedInstRegOperands(*RotateI, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectSetCond(MachineInstr &I, + MachineRegisterInfo &MRI) const { + Register CondReg = I.getOperand(I.getNumExplicitDefs() - 1).getReg(); + assert(MRI.getType(CondReg) == LLT::scalar(1) && "Expected s1 condition"); + + MachineIRBuilder MIB(I); + Z80::CondCode CC = foldCond(CondReg, MIB, MRI); + if (CC == Z80::COND_INVALID) + return false; + + if (MRI.reg_empty(CondReg)) + return true; + + auto TrueI = MIB.buildInstr(Z80::LD8ri, {LLT::scalar(8)}, {int64_t(1)}); + auto FalseI = MIB.buildInstr(Z80::LD8ri, {LLT::scalar(8)}, {int64_t(0)}); + auto SelectI = + MIB.buildInstr(Z80::Select8, {CondReg}, {TrueI, FalseI, int64_t(CC)}); + I.eraseFromParent(); + return constrainSelectedInstRegOperands(*FalseI, TII, TRI, RBI) && + constrainSelectedInstRegOperands(*TrueI, TII, TRI, RBI) && + constrainSelectedInstRegOperands(*SelectI, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectSelect(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_SELECT && "unexpected instruction"); + + LLT OpTy = MRI.getType(I.getOperand(2).getReg()); + unsigned Select; + const TargetRegisterClass *RC; + switch (OpTy.getSizeInBits()) { + case 8: + Select = Z80::Select8; + RC = &Z80::R8RegClass; + break; + case 16: + Select = Z80::Select16; + RC = &Z80::R16RegClass; + break; + case 24: + Select = Z80::Select24; + RC = &Z80::R24RegClass; + break; + default: + return false; + } + + MachineIRBuilder MIB(I); + Z80::CondCode CC = foldCond(I.getOperand(1).getReg(), MIB, MRI); + if (CC == Z80::COND_INVALID) + return false; + + I.setDesc(TII.get(Select)); + I.getOperand(1).setReg(I.getOperand(2).getReg()); + I.getOperand(2).setReg(I.getOperand(3).getReg()); + I.getOperand(3).ChangeToImmediate(CC); + return constrainSelectedInstRegOperands(I, TII, TRI, RBI); +} + +bool Z80InstructionSelector::selectBrCond(MachineInstr &I, + MachineRegisterInfo &MRI) const { + assert(I.getOpcode() == TargetOpcode::G_BRCOND && "unexpected instruction"); + + MachineIRBuilder MIB(I); + Z80::CondCode CC = foldCond(I.getOperand(0).getReg(), MIB, MRI); + if (CC == Z80::COND_INVALID) + return false; + + MIB.buildInstr(Z80::JQCC).add(I.getOperand(1)).addImm(CC); + I.eraseFromParent(); + return true; +} + +bool Z80InstructionSelector::selectBrJT(MachineInstr &I, + MachineRegisterInfo &MRI, + MachineFunction &MF) const { + assert((I.getOpcode() == TargetOpcode::G_BRJT) && "unexpected instruction"); + + unsigned EntrySize = MF.getJumpTableInfo()->getEntrySize(MF.getDataLayout()); + assert(EntrySize && EntrySize <= 3 && + "Jump table entry size is expected to be less than pointer size"); + + Register AddrReg = I.getOperand(0).getReg(); + LLT AddrTy = MRI.getType(AddrReg); + Register IndexReg = I.getOperand(2).getReg(); + MachineIRBuilder MIB(I); + + for (unsigned Factor = 0; Factor != EntrySize; ++Factor) { + auto Add = MIB.buildInstr(STI.is24Bit() ? Z80::ADD24ao : Z80::ADD16ao, + {AddrTy}, {AddrReg, IndexReg}); + if (!constrainSelectedInstRegOperands(*Add, TII, TRI, RBI)) + return false; + AddrReg = Add.getReg(0); + } + + auto Load = MIB.buildInstr(STI.is24Bit() + ? Z80::LD24rp + : STI.hasEZ80Ops() ? Z80::LD16rp : Z80::LD88rp, + {AddrTy}, {AddrReg}); + if (!constrainSelectedInstRegOperands(*Load, TII, TRI, RBI)) + return false; + + auto Jump = + MIB.buildInstr(STI.is24Bit() ? Z80::JP24r : Z80::JP16r, {}, {Load}); + if (!constrainSelectedInstRegOperands(*Jump, TII, TRI, RBI)) + return false; + + I.eraseFromParent(); + return true; +} + +bool Z80InstructionSelector::selectImplicitDefOrPHI( + MachineInstr &I, MachineRegisterInfo &MRI) const { + assert((I.getOpcode() == TargetOpcode::G_IMPLICIT_DEF || + I.getOpcode() == TargetOpcode::G_PHI) && + "unexpected instruction"); + + Register DstReg = I.getOperand(0).getReg(); + + if (!MRI.getRegClassOrNull(DstReg)) { + const LLT DstTy = MRI.getType(DstReg); + const TargetRegisterClass *RC = getRegClass(DstTy, DstReg, MRI); + + if (!RBI.constrainGenericRegister(DstReg, *RC, MRI)) + return false; + } + + if (I.getOpcode() == TargetOpcode::G_IMPLICIT_DEF) + I.setDesc(TII.get(Z80::IMPLICIT_DEF)); + else + I.setDesc(TII.get(Z80::PHI)); + + return true; +} + +InstructionSelector::ComplexRendererFns +Z80InstructionSelector::selectMem(MachineOperand &MO) const { + llvm_unreachable("Unimplemented!"); +} + +InstructionSelector::ComplexRendererFns +Z80InstructionSelector::selectOff(MachineOperand &MO) const { + llvm_unreachable("Unimplemented!"); +} + +InstructionSelector * +llvm::createZ80InstructionSelector(const Z80TargetMachine &TM, + Z80Subtarget &Subtarget, + Z80RegisterBankInfo &RBI) { + return new Z80InstructionSelector(TM, Subtarget, RBI); +} diff --git a/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp new file mode 100644 index 00000000000000..eb21a5c8bf1ef0 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.cpp @@ -0,0 +1,512 @@ +//===- Z80LegalizerInfo.cpp --------------------------------------*- C++ -*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the Machinelegalizer class for Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "Z80LegalizerInfo.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80Subtarget.h" +#include "Z80TargetMachine.h" +#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h" +#include +using namespace llvm; +using namespace TargetOpcode; +using namespace LegalizeActions; + +Z80LegalizerInfo::Z80LegalizerInfo(const Z80Subtarget &STI, + const Z80TargetMachine &TM) + : Subtarget(STI), TM(TM) { + bool Is24Bit = Subtarget.is24Bit(); + LLT p0 = LLT::pointer(0, TM.getPointerSizeInBits(0)); + LLT s1 = LLT::scalar(1); + LLT s8 = LLT::scalar(8); + LLT s16 = LLT::scalar(16); + LLT s24 = LLT::scalar(24); + LLT s32 = LLT::scalar(32); + LLT s64 = LLT::scalar(64); + LLT sMax = Is24Bit ? s24 : s16; + auto LegalTypes24 = {p0, s8, s16, s24}, LegalTypes16 = {p0, s8, s16}; + auto LegalTypes = Is24Bit ? LegalTypes24 : LegalTypes16; + auto LegalScalars24 = {s8, s16, s24}, LegalScalars16 = {s8, s16}; + auto LegalScalars = Is24Bit ? LegalScalars24 : LegalScalars16; + auto LegalLibcallScalars24 = {s8, s16, s24, s32, s64}; + auto LegalLibcallScalars16 = {s8, s16, s32, s64}; + auto LegalLibcallScalars = + Is24Bit ? LegalLibcallScalars24 : LegalLibcallScalars16; + auto NotMax24 = {s8, s16}, NotMax16 = {s8}; + auto NotMax = Is24Bit ? NotMax24 : NotMax16; + auto NotMin24 = {s16, s24}, NotMin16 = {s16}; + auto NotMin = Is24Bit ? NotMin24 : NotMin16; + auto NotMaxWithOne24 = {s1, s8, s16}, NotMaxWithOne16 = {s1, s8}; + auto NotMaxWithOne = Is24Bit ? NotMaxWithOne24 : NotMaxWithOne16; + + getActionDefinitionsBuilder(G_MERGE_VALUES) + .legalForCartesianProduct(NotMin, NotMax) + .clampScalar(0, *NotMin.begin(), *std::prev(NotMin.end())) + .clampScalar(1, *NotMax.begin(), *std::prev(NotMax.end())); + + getActionDefinitionsBuilder(G_UNMERGE_VALUES) + .legalForCartesianProduct(NotMax, NotMin) + .clampScalar(1, *NotMin.begin(), *std::prev(NotMin.end())) + .clampScalar(0, *NotMax.begin(), *std::prev(NotMax.end())); + + getActionDefinitionsBuilder({G_ZEXT, G_ANYEXT}) + .legalForCartesianProduct(LegalScalars, NotMaxWithOne) + .clampScalar(0, *LegalScalars.begin(), *std::prev(LegalScalars.end())) + .clampScalar(1, *NotMaxWithOne.begin(), *std::prev(NotMaxWithOne.end())); + + getActionDefinitionsBuilder(G_SEXT) + .legalForCartesianProduct(LegalScalars, {s1}) + .maxScalar(0, sMax) + .maxScalar(0, s8) + .maxScalar(1, s8); + + getActionDefinitionsBuilder(G_TRUNC) + .legalForCartesianProduct(NotMaxWithOne, LegalScalars) + .clampScalar(1, *LegalScalars.begin(), *std::prev(LegalScalars.end())) + .clampScalar(0, *NotMaxWithOne.begin(), *std::prev(NotMaxWithOne.end())); + + getActionDefinitionsBuilder({G_FREEZE, G_IMPLICIT_DEF, G_PHI, G_CONSTANT}) + .legalFor(LegalTypes) + .clampScalar(0, s8, sMax); + + getActionDefinitionsBuilder(G_FCONSTANT) + .customFor({s32, s64}); + + getActionDefinitionsBuilder(G_INTTOPTR) + .legalFor({{p0, sMax}}) + .clampScalar(1, sMax, sMax); + + getActionDefinitionsBuilder(G_PTRTOINT) + .legalFor({{sMax, p0}}) + .clampScalar(0, sMax, sMax); + + getActionDefinitionsBuilder(G_PTR_ADD) + .legalForCartesianProduct({p0}, LegalScalars) + .clampScalar(1, s8, sMax); + + getActionDefinitionsBuilder({G_ADD, G_SUB}) + .legalFor(LegalScalars) + .libcallFor({s32, s64}) + .clampScalar(0, s8, sMax); + + getActionDefinitionsBuilder({G_UADDO, G_UADDE, G_USUBO, G_USUBE, + G_SADDO, G_SADDE, G_SSUBO, G_SSUBE}) + .legalForCartesianProduct(LegalScalars, {s1}) + .clampScalar(0, s8, sMax); + + { + auto &&Mul = getActionDefinitionsBuilder(G_MUL); + if (Subtarget.hasZ180Ops()) + Mul.legalFor({s8}); + Mul.libcallFor(LegalLibcallScalars) + .clampScalar(0, s8, s32); + } + + getActionDefinitionsBuilder({G_SDIV, G_UDIV, G_SREM, G_UREM}) + .libcallFor(LegalLibcallScalars) + .clampScalar(0, s8, s32); + + getActionDefinitionsBuilder({G_AND, G_OR, G_XOR}) + .legalFor({s8}) + .customFor(LegalLibcallScalars) + .clampScalar(0, s8, s32); + + getActionDefinitionsBuilder({G_SHL, G_LSHR, G_ASHR}) + .customForCartesianProduct(LegalLibcallScalars, {s8}) + .clampScalar(1, s8, s8) + .clampScalar(0, s8, s64); + + getActionDefinitionsBuilder({G_FSHL, G_FSHR}) + .custom(); + + getActionDefinitionsBuilder({G_FADD, G_FSUB, G_FMUL, G_FDIV, G_FREM, G_FNEG, + G_FABS, G_INTRINSIC_TRUNC, G_INTRINSIC_ROUND, + G_FCEIL, G_FCOS, G_FSIN, G_FSQRT, G_FFLOOR, + G_FRINT, G_FNEARBYINT}) + .libcallFor({s32, s64}); + + getActionDefinitionsBuilder(G_FCOPYSIGN) + .libcallFor({{s32, s32}, {s64, s64}}); + + //getActionDefinitionsBuilder({G_FNEG, G_FABS) + // .customFor({s32, s64}); + + getActionDefinitionsBuilder(G_FPTRUNC) + .libcallFor({{s32, s64}}); + + getActionDefinitionsBuilder(G_FPEXT) + .libcallFor({{s64, s32}}); + + getActionDefinitionsBuilder({G_FPTOSI, G_FPTOUI}) + .libcallForCartesianProduct({s32, s64}, {s32, s64}) + .clampScalar(0, s32, s64); + + getActionDefinitionsBuilder({G_SITOFP, G_UITOFP}) + .libcallForCartesianProduct({s32, s64}, {s32, s64}) + .clampScalar(1, s32, s64); + + getActionDefinitionsBuilder({G_LOAD, G_STORE}) + .legalForCartesianProduct(LegalTypes, {p0}) + .clampScalar(0, s8, sMax); + for (unsigned MemOp : {G_LOAD, G_STORE}) + setLegalizeScalarToDifferentSizeStrategy(MemOp, 0, + narrowToSmallerAndWidenToSmallest); + + getActionDefinitionsBuilder( + {G_FRAME_INDEX, G_GLOBAL_VALUE, G_BRINDIRECT, G_JUMP_TABLE}) + .legalFor({p0}); + + getActionDefinitionsBuilder(G_VASTART) + .customFor({p0}); + + getActionDefinitionsBuilder(G_ICMP) + .legalForCartesianProduct({s1}, LegalTypes) + .customForCartesianProduct({s1}, {s32, s64}) + .clampScalar(1, s8, s32); + + getActionDefinitionsBuilder(G_FCMP) + .customForCartesianProduct({s1}, {s32, s64}); + + getActionDefinitionsBuilder(G_BRCOND) + .legalFor({s1}); + + getActionDefinitionsBuilder(G_BRJT) + .legalForCartesianProduct({p0}, LegalScalars) + .clampScalar(1, s8, sMax); + + getActionDefinitionsBuilder(G_SELECT) + .legalForCartesianProduct(LegalTypes, {s1}) + .clampScalar(0, s8, sMax); + + getActionDefinitionsBuilder({G_DYN_STACKALLOC, G_CTLZ_ZERO_UNDEF, + G_CTTZ_ZERO_UNDEF, G_CTLZ, G_CTTZ, G_BSWAP, + G_SMULO, G_UMULO, G_SMULH, G_UMULH, + G_UADDSAT, G_SADDSAT, G_USUBSAT, G_SSUBSAT}) + .lower(); + + getActionDefinitionsBuilder(G_CTPOP) + .libcallForCartesianProduct({s8}, LegalLibcallScalars) + .clampScalar(0, s8, s8); + + getActionDefinitionsBuilder(G_BITREVERSE) + .libcallFor(LegalLibcallScalars) + .clampScalar(0, s8, s64); + + computeTables(); + verify(*STI.getInstrInfo()); +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeCustomMaybeLegal(LegalizerHelper &Helper, + MachineInstr &MI) const { + Helper.MIRBuilder.setInstr(MI); + switch (MI.getOpcode()) { + default: + // No idea what to do. + return LegalizerHelper::UnableToLegalize; + case TargetOpcode::G_AND: + case TargetOpcode::G_OR: + case TargetOpcode::G_XOR: + return legalizeBitwise(Helper, MI); + case TargetOpcode::G_FCONSTANT: + return legalizeFConstant(Helper, MI); + case TargetOpcode::G_VASTART: + return legalizeVAStart(Helper, MI); + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: + return legalizeShift(Helper, MI); + case TargetOpcode::G_FSHL: + case TargetOpcode::G_FSHR: + return legalizeFunnelShift(Helper, MI); + case TargetOpcode::G_ICMP: + case TargetOpcode::G_FCMP: + return legalizeCompare(Helper, MI); + } +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeBitwise(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert((MI.getOpcode() == TargetOpcode::G_AND || + MI.getOpcode() == TargetOpcode::G_OR || + MI.getOpcode() == TargetOpcode::G_XOR) && + "Unexpected opcode"); + if (!MI.getParent()->getParent()->getFunction().hasOptSize() && + Helper.MIRBuilder.getMRI()->getType(MI.getOperand(0).getReg()) == + LLT::scalar(16)) + if (Helper.narrowScalar(MI, 0, LLT::scalar(8)) == + LegalizerHelper::Legalized) + return LegalizerHelper::Legalized; + return Helper.libcall(MI); +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeFConstant(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert(MI.getOpcode() == TargetOpcode::G_FCONSTANT && "Unexpected opcode"); + Helper.Observer.changingInstr(MI); + MI.setDesc(Helper.MIRBuilder.getTII().get(TargetOpcode::G_CONSTANT)); + MachineOperand &Imm = MI.getOperand(1); + const ConstantFP *FPImm = Imm.getFPImm(); + Imm.ChangeToCImmediate(ConstantInt::get( + FPImm->getContext(), FPImm->getValueAPF().bitcastToAPInt())); + Helper.Observer.changedInstr(MI); + return LegalizerHelper::Legalized; +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeVAStart(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert(MI.getOpcode() == TargetOpcode::G_VASTART && "Unexpected opcode"); + MachineFunction &MF = Helper.MIRBuilder.getMF(); + Z80MachineFunctionInfo &FuncInfo = *MF.getInfo(); + int FrameIdx = FuncInfo.getVarArgsFrameIndex(); + assert(FrameIdx && "Found va_start but never setVarArgsFrameIndex!"); + LLT p0 = LLT::pointer(0, TM.getPointerSizeInBits(0)); + Helper.MIRBuilder.buildStore(Helper.MIRBuilder.buildFrameIndex(p0, FrameIdx), + MI.getOperand(0).getReg(), + **MI.memoperands_begin()); + MI.eraseFromParent(); + return LegalizerHelper::Legalized; +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeShift(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert((MI.getOpcode() == TargetOpcode::G_SHL || + MI.getOpcode() == TargetOpcode::G_LSHR || + MI.getOpcode() == TargetOpcode::G_ASHR) && + "Unexpected opcode"); + MachineRegisterInfo &MRI = *Helper.MIRBuilder.getMRI(); + Register DstReg = MI.getOperand(0).getReg(); + LLT Ty = MRI.getType(DstReg); + if (auto Amt = + getConstantVRegValWithLookThrough(MI.getOperand(2).getReg(), MRI)) { + if (Ty == LLT::scalar(8) && Amt->Value == 1) + return LegalizerHelper::AlreadyLegal; + if (MI.getOpcode() == TargetOpcode::G_SHL && Amt->Value == 1) { + Helper.Observer.changingInstr(MI); + MI.setDesc(Helper.MIRBuilder.getTII().get(TargetOpcode::G_ADD)); + MI.getOperand(2).setReg(MI.getOperand(1).getReg()); + Helper.Observer.changedInstr(MI); + return LegalizerHelper::Legalized; + } + if (MI.getOpcode() == TargetOpcode::G_ASHR && + Amt->Value == Ty.getSizeInBits() - 1 && + (Ty == LLT::scalar(8) || Ty == LLT::scalar(16) || + (Subtarget.is24Bit() && Ty == LLT::scalar(24)))) + return LegalizerHelper::Legalized; + } + return Helper.libcall(MI); +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeFunnelShift(LegalizerHelper &Helper, + MachineInstr &MI) const { + assert((MI.getOpcode() == TargetOpcode::G_FSHL || + MI.getOpcode() == TargetOpcode::G_FSHR) && + "Unexpected opcode"); + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + MachineRegisterInfo &MRI = *MIRBuilder.getMRI(); + Register DstReg = MI.getOperand(0).getReg(); + Register FwdReg = MI.getOperand(1).getReg(); + Register RevReg = MI.getOperand(2).getReg(); + Register AmtReg = MI.getOperand(3).getReg(); + LLT Ty = MRI.getType(DstReg); + if (auto Amt = getConstantVRegValWithLookThrough(AmtReg, MRI)) + if (Ty == LLT::scalar(8) && (Amt->Value == 1 || Amt->Value == 7)) + return LegalizerHelper::AlreadyLegal; + + unsigned FwdShiftOpc = TargetOpcode::G_SHL; + unsigned RevShiftOpc = TargetOpcode::G_LSHR; + if (MI.getOpcode() == TargetOpcode::G_FSHR) { + std::swap(FwdReg, RevReg); + std::swap(FwdShiftOpc, RevShiftOpc); + } + + auto MaskI = MIRBuilder.buildConstant(Ty, Ty.getSizeInBits() - 1); + auto FwdAmtI = MIRBuilder.buildAnd(Ty, AmtReg, MaskI); + auto FwdI = MIRBuilder.buildInstr(FwdShiftOpc, {Ty}, {FwdReg, FwdAmtI}); + auto RevAmtI = MIRBuilder.buildAnd( + Ty, MIRBuilder.buildSub(Ty, MIRBuilder.buildConstant(Ty, 0), AmtReg), + MaskI); + auto RevI = MIRBuilder.buildInstr(RevShiftOpc, {Ty}, {RevReg, RevAmtI}); + MIRBuilder.buildOr(DstReg, FwdI, RevI); + MI.eraseFromParent(); + return LegalizerHelper::Legalized; +} + +LegalizerHelper::LegalizeResult +Z80LegalizerInfo::legalizeCompare(LegalizerHelper &Helper, + MachineInstr &MI) const { + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + MachineRegisterInfo &MRI = *MIRBuilder.getMRI(); + Register DstReg = MI.getOperand(0).getReg(); + auto Pred = CmpInst::Predicate(MI.getOperand(1).getPredicate()); + Register LHSReg = MI.getOperand(2).getReg(); + Register RHSReg = MI.getOperand(3).getReg(); + LLT OpTy = MRI.getType(LHSReg); + unsigned OpSize = OpTy.getSizeInBits(); + assert(MRI.getType(DstReg) == LLT::scalar(1) && !OpTy.isVector() && + MRI.getType(RHSReg) == OpTy && "Unexpected type"); + + Type *Ty; + RTLIB::Libcall Libcall; + bool IsSigned, IsSwapped, IsConst; + Z80::CondCode CC = + Z80::GetBranchConditionForPredicate(Pred, IsSigned, IsSwapped, IsConst); + if (IsSwapped) + std::swap(LHSReg, RHSReg); + auto &Ctx = MIRBuilder.getMF().getFunction().getContext(); + bool ZeroRHS = false; + if (MI.getOpcode() == TargetOpcode::G_ICMP) { + Ty = IntegerType::get(Ctx, OpSize); + if (auto C = getConstantVRegVal(RHSReg, MRI)) + ZeroRHS = *C == 0; + switch (OpSize) { + case 32: + Libcall = ZeroRHS ? RTLIB::CMP_I32_0 : RTLIB::CMP_I32; + break; + case 64: + Libcall = ZeroRHS ? RTLIB::CMP_I64_0 : RTLIB::CMP_I64; + break; + default: + llvm_unreachable("Unexpected type"); + } + } else { + assert(MI.getOpcode() == TargetOpcode::G_FCMP && "Unexpected opcode"); + assert(OpTy.isScalar() && "Unexpected type"); + switch (OpSize) { + case 32: + Ty = Type::getFloatTy(Ctx); + Libcall = RTLIB::CMP_F32; + break; + case 64: + Ty = Type::getDoubleTy(Ctx); + Libcall = RTLIB::CMP_F64; + break; + default: + llvm_unreachable("Unexpected type"); + } + } + if (!IsConst) { + LLT s8 = LLT::scalar(8); + Type *Int8Ty = Type::getInt8Ty(Ctx); + Register FlagsReg = MRI.createGenericVirtualRegister(s8); + CallLowering::ArgInfo FlagsArg(FlagsReg, Int8Ty); + CallLowering::ArgInfo Args[2] = {{LHSReg, Ty}, {RHSReg, Ty}}; + createLibcall(MIRBuilder, Libcall, FlagsArg, + makeArrayRef(Args, 2 - ZeroRHS)); + if (IsSigned) { + Register SignedFlagsReg = MRI.createGenericVirtualRegister(s8); + CallLowering::ArgInfo SignedFlagsArg(SignedFlagsReg, Int8Ty); + createLibcall(MIRBuilder, RTLIB::SCMP, SignedFlagsArg, FlagsArg); + FlagsReg = SignedFlagsReg; + } + MIRBuilder.buildCopy(Register(Z80::F), FlagsReg); + } else + MIRBuilder.buildInstr(Z80::RCF); + MIRBuilder.buildInstr(Z80::SetCC, {DstReg}, {int64_t(CC)}); + MI.eraseFromParent(); + return LegalizerHelper::Legalized; +} + +bool Z80LegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper, + MachineInstr &MI) const { + MachineIRBuilder &MIRBuilder = Helper.MIRBuilder; + MachineFunction &MF = MIRBuilder.getMF(); + MachineRegisterInfo &MRI = *MIRBuilder.getMRI(); + auto &Ctx = MF.getFunction().getContext(); + auto &CLI = *MF.getSubtarget().getCallLowering(); + bool Is24Bit = Subtarget.is24Bit(); + MIRBuilder.setInstr(MI); + switch (auto IntrinsicID = Intrinsic::ID(MI.getIntrinsicID())) { + case Intrinsic::memcpy: + case Intrinsic::memset: + case Intrinsic::memmove: { + Register DstReg = MI.getOperand(1).getReg(); + LLT DstTy = MRI.getType(DstReg); + Register SrcReg = MI.getOperand(2).getReg(); + Register LenReg = MI.getOperand(3).getReg(); + LLT LenTy = LLT::scalar(Is24Bit ? 24 : 16); + LenReg = MIRBuilder.buildZExtOrTrunc(LenTy, LenReg).getReg(0); + // We need to make sure the number of bytes is non-zero for this lowering to + // be correct. Since we only need to lower constant-length intrinsics for + // now, just support those. + if (auto Len = getConstantVRegVal(LenReg, MRI)) { + // Doing something with zero bytes is a noop anyway. + if (!*Len) + break; + if (MF.getFunction().hasOptSize() && IntrinsicID == Intrinsic::memmove) + // Lowering memmove generates a lot of code... + goto MemLibcall; + if (IntrinsicID == Intrinsic::memset) { + // Store the first byte. + MIRBuilder.buildStore(SrcReg, DstReg, **MI.memoperands_begin()); + // If we are only storing one byte, we are done now. + // TODO: lower small len to a series of stores. + if (*Len == 1) + break; + // Read starting at the stored byte. + SrcReg = DstReg; + // Write starting at the following byte. + auto One = + MIRBuilder.buildConstant(LLT::scalar(DstTy.getSizeInBits()), 1); + DstReg = MIRBuilder.buildPtrAdd(DstTy, DstReg, One).getReg(0); + // Copy one less byte. + auto NegOne = + MIRBuilder.buildConstant(LenTy, -1); + LenReg = MIRBuilder.buildAdd(LenTy, LenReg, NegOne).getReg(0); + // Now it's just an ldir. + } + Register DE = Is24Bit ? Z80::UDE : Z80::DE; + Register HL = Is24Bit ? Z80::UHL : Z80::HL; + Register BC = Is24Bit ? Z80::UBC : Z80::BC; + if (IntrinsicID == Intrinsic::memmove) { + MIRBuilder.buildCopy(HL, SrcReg); + MIRBuilder.buildInstr(Is24Bit ? Z80::CP24ao : Z80::CP16ao, {}, + {DstReg}); + MIRBuilder.buildInstr(Is24Bit ? Z80::LDR24 : Z80::LDR16, {}, + {DstReg, SrcReg, LenReg}).cloneMemRefs(MI); + } else { + // TODO: lower small len to a series of loads and stores. + MIRBuilder.buildCopy(DE, DstReg); + MIRBuilder.buildCopy(HL, SrcReg); + MIRBuilder.buildCopy(BC, LenReg); + MIRBuilder.buildInstr(Is24Bit ? Z80::LDIR24 : Z80::LDIR16) + .cloneMemRefs(MI); + } + break; + } + MemLibcall: + MI.getOperand(3).setReg(LenReg); + if (createMemLibcall(MIRBuilder, MRI, MI) == + LegalizerHelper::UnableToLegalize) + return false; + break; + } + case Intrinsic::trap: { + CallLowering::CallLoweringInfo Info; + Info.CallConv = CallingConv::C; + Info.Callee = MachineOperand::CreateES("abort"); + Info.OrigRet = CallLowering::ArgInfo({0}, Type::getVoidTy(Ctx)); + if (!CLI.lowerCall(MIRBuilder, Info)) + return false; + break; + } + default: + return false; + } + MI.eraseFromParent(); + return true; +} diff --git a/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h new file mode 100644 index 00000000000000..865ba078721880 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80LegalizerInfo.h @@ -0,0 +1,58 @@ +//===- Z80LegalizerInfo.h ----------------------------------------*- C++ -*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the Machinelegalizer class for Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80MACHINELEGALIZER_H +#define LLVM_LIB_TARGET_Z80_Z80MACHINELEGALIZER_H + +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" + +namespace llvm { + +class Z80Subtarget; +class Z80TargetMachine; + +/// This class provides the information for the target register banks. +class Z80LegalizerInfo : public LegalizerInfo { +private: + /// Keep a reference to the Z80Subtarget around so that we can + /// make the right decision when generating code for different targets. + const Z80Subtarget &Subtarget; + const Z80TargetMachine &TM; + +public: + Z80LegalizerInfo(const Z80Subtarget &STI, const Z80TargetMachine &TM); + + LegalizerHelper::LegalizeResult + legalizeCustomMaybeLegal(LegalizerHelper &Helper, + MachineInstr &MI) const override; + + bool legalizeIntrinsic(LegalizerHelper &Helper, + MachineInstr &MI) const override; + +private: + LegalizerHelper::LegalizeResult legalizeBitwise(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeFConstant(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeVAStart(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeShift(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeFunnelShift(LegalizerHelper &Helper, + MachineInstr &MI) const; + LegalizerHelper::LegalizeResult legalizeCompare(LegalizerHelper &Helper, + MachineInstr &MI) const; +}; + +} // End namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/GISel/Z80PreLegalizerCombiner.cpp b/llvm/lib/Target/Z80/GISel/Z80PreLegalizerCombiner.cpp new file mode 100644 index 00000000000000..035283ff67cc32 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80PreLegalizerCombiner.cpp @@ -0,0 +1,139 @@ +//=== lib/CodeGen/GlobalISel/Z80PreLegalizerCombiner.cpp ------------------===// +// +// 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 pass does combining of machine instructions at the generic MI level, +// before the legalizer. +// +//===----------------------------------------------------------------------===// + +#include "Z80.h" +#include "llvm/CodeGen/GlobalISel/Combiner.h" +#include "llvm/CodeGen/GlobalISel/CombinerHelper.h" +#include "llvm/CodeGen/GlobalISel/CombinerInfo.h" +#include "llvm/CodeGen/GlobalISel/GISelKnownBits.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetMachine.h" + +#define DEBUG_TYPE "z80-prelegalizer-combiner" + +using namespace llvm; + +#define Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS +#include "Z80GenGICombiner.inc" +#undef Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_DEPS + +namespace { +#define Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H +#include "Z80GenGICombiner.inc" +#undef Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_H + +class Z80PreLegalizerCombinerInfo : public CombinerInfo { + GISelKnownBits *KB; + MachineDominatorTree *MDT; + Z80GenPreLegalizerCombinerHelperRuleConfig GeneratedRuleCfg; + +public: + Z80PreLegalizerCombinerInfo(bool EnableOpt, bool OptSize, bool MinSize, + GISelKnownBits *KB, MachineDominatorTree *MDT) + : CombinerInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false, + /*LegalizerInfo*/ nullptr, EnableOpt, OptSize, MinSize), + KB(KB), MDT(MDT) { + if (!GeneratedRuleCfg.parseCommandLineOption()) + report_fatal_error("Invalid rule identifier"); + } + + virtual bool combine(GISelChangeObserver &Observer, MachineInstr &MI, + MachineIRBuilder &B) const override; +}; + +bool Z80PreLegalizerCombinerInfo::combine(GISelChangeObserver &Observer, + MachineInstr &MI, + MachineIRBuilder &B) const { + CombinerHelper Helper(Observer, B, KB, MDT); + Z80GenPreLegalizerCombinerHelper Generated(GeneratedRuleCfg, Helper); + return Generated.tryCombineAll(Observer, MI, B, Helper); +} + +#define Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP +#include "Z80GenGICombiner.inc" +#undef Z80PRELEGALIZERCOMBINERHELPER_GENCOMBINERHELPER_CPP + +// Pass boilerplate +// ================ + +class Z80PreLegalizerCombiner : public MachineFunctionPass { +public: + static char ID; + + Z80PreLegalizerCombiner(bool IsOptNone = false); + + StringRef getPassName() const override { + return "Z80 Pre-Legalizer Combiner"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override; +private: + bool IsOptNone; +}; +} // end anonymous namespace + +void Z80PreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const { + AU.addRequired(); + AU.setPreservesCFG(); + getSelectionDAGFallbackAnalysisUsage(AU); + AU.addRequired(); + AU.addPreserved(); + if (!IsOptNone) { + AU.addRequired(); + AU.addPreserved(); + } + MachineFunctionPass::getAnalysisUsage(AU); +} + +Z80PreLegalizerCombiner::Z80PreLegalizerCombiner(bool IsOptNone) + : MachineFunctionPass(ID), IsOptNone(IsOptNone) { + initializeZ80PreLegalizerCombinerPass(*PassRegistry::getPassRegistry()); +} + +bool Z80PreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) { + if (MF.getProperties().hasProperty( + MachineFunctionProperties::Property::FailedISel)) + return false; + auto *TPC = &getAnalysis(); + const Function &F = MF.getFunction(); + bool EnableOpt = + MF.getTarget().getOptLevel() != CodeGenOpt::None && !skipFunction(F); + GISelKnownBits *KB = &getAnalysis().get(MF); + MachineDominatorTree *MDT = + IsOptNone ? nullptr : &getAnalysis(); + Z80PreLegalizerCombinerInfo PCInfo(EnableOpt, F.hasOptSize(), F.hasMinSize(), + KB, MDT); + Combiner C(PCInfo, TPC); + return C.combineMachineInstrs(MF, /*CSEInfo*/ nullptr); +} + +char Z80PreLegalizerCombiner::ID = 0; +INITIALIZE_PASS_BEGIN(Z80PreLegalizerCombiner, DEBUG_TYPE, + "Combine Z80 machine instrs before legalization", + false, false) +INITIALIZE_PASS_DEPENDENCY(TargetPassConfig) +INITIALIZE_PASS_DEPENDENCY(GISelKnownBitsAnalysis) +INITIALIZE_PASS_END(Z80PreLegalizerCombiner, DEBUG_TYPE, + "Combine Z80 machine instrs before legalization", false, + false) + + +namespace llvm { +FunctionPass *createZ80PreLegalizeCombiner(bool IsOptNone) { + return new Z80PreLegalizerCombiner(IsOptNone); +} +} // end namespace llvm diff --git a/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.cpp b/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.cpp new file mode 100644 index 00000000000000..cd20bd7c16fba1 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.cpp @@ -0,0 +1,169 @@ +//===- Z80RegisterBankInfo.cpp -----------------------------------*- C++ -*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the targeting of the RegisterBankInfo class for Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#include "Z80RegisterBankInfo.h" +#include "Z80RegisterInfo.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/GlobalISel/RegisterBank.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_TARGET_REGBANK_IMPL +#include "Z80GenRegisterBank.inc" + +using namespace llvm; +// This file will be TableGen'ed at some point. +#define GET_TARGET_REGBANK_INFO_IMPL +#include "Z80GenRegisterBankInfo.def" + +Z80RegisterBankInfo::Z80RegisterBankInfo(const TargetRegisterInfo &TRI) { + + // validate RegBank initialization. + const RegisterBank &RBGPR = getRegBank(Z80::GPRRegBankID); + (void)RBGPR; + assert(&Z80::GPRRegBank == &RBGPR && "Incorrect RegBanks initialization."); + + // The GPR register bank is fully defined by all the registers in + // R24 + its subclasses. + assert(RBGPR.covers(*TRI.getRegClass(Z80::R24RegClassID)) && + "Subclass not added?"); + assert(RBGPR.getSize() == 24 && "GPRs should hold up to 24-bits"); +} + +const RegisterBank & +Z80RegisterBankInfo::getRegBankFromRegClass(const TargetRegisterClass &RC, + LLT) const { + if (Z80::R8RegClass.hasSubClassEq(&RC) || + Z80::R16RegClass.hasSubClassEq(&RC) || + Z80::R24RegClass.hasSubClassEq(&RC) || + Z80::F8RegClass.hasSubClassEq(&RC) || + Z80::Z16RegClass.hasSubClassEq(&RC) || + Z80::Z24RegClass.hasSubClassEq(&RC)) + return getRegBank(Z80::GPRRegBankID); + + llvm_unreachable("Unsupported register kind."); +} + +Z80GenRegisterBankInfo::PartialMappingIdx +Z80GenRegisterBankInfo::getPartialMappingIdx(const LLT &Ty) { + if (Ty.isVector()) + llvm_unreachable("Vector is unsupported."); + + switch (Ty.getSizeInBits()) { + case 1: + case 8: return PMI_GPR8; + case 16: return PMI_GPR16; + case 24: return PMI_GPR24; + default: + llvm_unreachable("Unsupported register size."); + } +} + +void Z80RegisterBankInfo::getInstrPartialMappingIdxs( + const MachineInstr &MI, const MachineRegisterInfo &MRI, + SmallVectorImpl &OpRegBankIdx) { + + unsigned NumOperands = MI.getNumOperands(); + for (unsigned Idx = 0; Idx < NumOperands; ++Idx) { + auto &MO = MI.getOperand(Idx); + if (!MO.isReg()) + OpRegBankIdx[Idx] = PMI_None; + else + OpRegBankIdx[Idx] = getPartialMappingIdx(MRI.getType(MO.getReg())); + } +} + +bool Z80RegisterBankInfo::getInstrValueMapping( + const MachineInstr &MI, + const SmallVectorImpl &OpRegBankIdx, + SmallVectorImpl &OpdsMapping) { + + unsigned NumOperands = MI.getNumOperands(); + for (unsigned Idx = 0; Idx < NumOperands; ++Idx) { + if (!MI.getOperand(Idx).isReg()) + continue; + + auto Mapping = getValueMapping(OpRegBankIdx[Idx], 1); + if (!Mapping->isValid()) + return false; + + OpdsMapping[Idx] = Mapping; + } + return true; +} + +const RegisterBankInfo::InstructionMapping & +Z80RegisterBankInfo::getSameOperandsMapping(const MachineInstr &MI) const { + const MachineFunction &MF = *MI.getParent()->getParent(); + const MachineRegisterInfo &MRI = MF.getRegInfo(); + + unsigned NumOperands = MI.getNumOperands(); + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + + if (NumOperands != 3 || (Ty != MRI.getType(MI.getOperand(1).getReg())) || + (Ty != MRI.getType(MI.getOperand(2).getReg()))) + llvm_unreachable("Unsupported operand mapping yet."); + + auto Mapping = getValueMapping(getPartialMappingIdx(Ty), 3); + return getInstructionMapping(DefaultMappingID, 1, Mapping, NumOperands); +} + +const RegisterBankInfo::InstructionMapping & +Z80RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const { + const MachineFunction &MF = *MI.getParent()->getParent(); + const MachineRegisterInfo &MRI = MF.getRegInfo(); + unsigned Opc = MI.getOpcode(); + unsigned NumOperands = MI.getNumOperands(); + + // Try the default logic for non-generic instructions that are either copies + // or already have some operands assigned to banks. + if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) { + const InstructionMapping &Mapping = getInstrMappingImpl(MI); + if (Mapping.isValid()) + return Mapping; + } + + switch (Opc) { + case TargetOpcode::G_ADD: + case TargetOpcode::G_SUB: + case TargetOpcode::G_MUL: + return getSameOperandsMapping(MI); + case TargetOpcode::G_SHL: + case TargetOpcode::G_LSHR: + case TargetOpcode::G_ASHR: { + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + + auto Mapping = getValueMapping(getPartialMappingIdx(Ty), 3); + return getInstructionMapping(DefaultMappingID, 1, Mapping, NumOperands); + } + default: + break; + } + + // Track the bank of each register. + SmallVector OpRegBankIdx(NumOperands); + getInstrPartialMappingIdxs(MI, MRI, OpRegBankIdx); + + // Finally construct the computed mapping. + SmallVector OpdsMapping(NumOperands); + if (!getInstrValueMapping(MI, OpRegBankIdx, OpdsMapping)) + return getInvalidInstructionMapping(); + + return getInstructionMapping(DefaultMappingID, /* Cost */ 1, + getOperandsMapping(OpdsMapping), NumOperands); +} + +void Z80RegisterBankInfo::applyMappingImpl( + const OperandsMapper &OpdMapper) const { + return applyDefaultMapping(OpdMapper); +} diff --git a/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.h b/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.h new file mode 100644 index 00000000000000..7b9e2b9b52e1a0 --- /dev/null +++ b/llvm/lib/Target/Z80/GISel/Z80RegisterBankInfo.h @@ -0,0 +1,79 @@ +//===- Z80RegisterBankInfo ---------------------------------------*- C++ -*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// This file declares the targeting of the RegisterBankInfo class for Z80. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80REGISTERBANKINFO_H +#define LLVM_LIB_TARGET_Z80_Z80REGISTERBANKINFO_H + +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" + +#define GET_REGBANK_DECLARATIONS +#include "Z80GenRegisterBank.inc" + +namespace llvm { + +class LLT; + +class Z80GenRegisterBankInfo : public RegisterBankInfo { +protected: +#define GET_TARGET_REGBANK_CLASS +#include "Z80GenRegisterBank.inc" +#define GET_TARGET_REGBANK_INFO_CLASS +#include "Z80GenRegisterBankInfo.def" + + static RegisterBankInfo::PartialMapping PartMappings[]; + static RegisterBankInfo::ValueMapping ValMappings[]; + + static PartialMappingIdx getPartialMappingIdx(const LLT &Ty); + static const RegisterBankInfo::ValueMapping * + getValueMapping(PartialMappingIdx Idx, unsigned NumOperands); +}; + +class TargetRegisterInfo; + +/// This class provides the information for the target register banks. +class Z80RegisterBankInfo final : public Z80GenRegisterBankInfo { +private: + /// Get an instruction mapping. + /// \return An InstructionMappings with a statically allocated + /// OperandsMapping. + const InstructionMapping & + getSameOperandsMapping(const MachineInstr &MI) const; + + /// Track the bank of each instruction operand(register) + static void + getInstrPartialMappingIdxs(const MachineInstr &MI, + const MachineRegisterInfo &MRI, + SmallVectorImpl &OpRegBankIdx); + + /// Construct the instruction ValueMapping from PartialMappingIdxs + /// \return true if mapping succeeded. + static bool + getInstrValueMapping(const MachineInstr &MI, + const SmallVectorImpl &OpRegBankIdx, + SmallVectorImpl &OpdsMapping); + +public: + Z80RegisterBankInfo(const TargetRegisterInfo &TRI); + + const RegisterBank &getRegBankFromRegClass(const TargetRegisterClass &RC, + LLT Ty) const override; + + /// See RegisterBankInfo::applyMapping. + void applyMappingImpl(const OperandsMapper &OpdMapper) const override; + + const InstructionMapping & + getInstrMapping(const MachineInstr &MI) const override; +}; + +} // End namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/LLVMBuild.txt b/llvm/lib/Target/Z80/LLVMBuild.txt new file mode 100644 index 00000000000000..0ab54abb9b6000 --- /dev/null +++ b/llvm/lib/Target/Z80/LLVMBuild.txt @@ -0,0 +1,32 @@ +;===- ./lib/Target/Z80/LLVMBuild.txt ---------------------------*- Conf -*--===; +; +; 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 is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[common] +subdirectories = MCTargetDesc TargetInfo + +[component_0] +type = TargetGroup +name = Z80 +parent = Target +has_asmparser = 1 +has_asmprinter = 1 + +[component_1] +type = Library +name = Z80CodeGen +parent = Z80 +required_libraries = AsmPrinter CodeGen Core GlobalISel MC SelectionDAG Support Target Z80Desc Z80Info +add_to_library_groups = Z80 diff --git a/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt b/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt new file mode 100644 index 00000000000000..35f23d80ecb9ca --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/CMakeLists.txt @@ -0,0 +1,12 @@ +add_llvm_component_library(LLVMZ80Desc + EZ80InstPrinter.cpp + Z80AsmBackend.cpp + Z80ELFObjectWriter.cpp + Z80InstPrinter.cpp + Z80InstPrinterCommon.cpp + Z80MCAsmInfo.cpp + Z80MCCodeEmitter.cpp + Z80ELFStreamer.cpp + Z80MCTargetDesc.cpp + Z80TargetStreamer.cpp + ) diff --git a/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.cpp b/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.cpp new file mode 100644 index 00000000000000..a9576f80e3247b --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.cpp @@ -0,0 +1,23 @@ +//===- EZ80InstPrinter.cpp - Convert EZ80 MCInst to assembly ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an EZ80 MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "EZ80InstPrinter.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +// Include the auto-generated portion of the assembly writer. +#include "EZ80GenAsmWriter.inc" diff --git a/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.h b/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.h new file mode 100644 index 00000000000000..5b8f15e4147831 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/EZ80InstPrinter.h @@ -0,0 +1,36 @@ +//===- EZ80InstPrinter.h - Convert EZ80 MCInst to assembly ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints an EZ80 MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_INSTPRINTER_EZ80INSTPRINTER_H +#define LLVM_LIB_TARGET_Z80_INSTPRINTER_EZ80INSTPRINTER_H + +#include "Z80InstPrinterCommon.h" + +namespace llvm { + class Z80EInstPrinter final : public Z80InstPrinterCommon { + public: + Z80EInstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : Z80InstPrinterCommon(MAI, MII, MRI) {} + + // Autogenerated by tblgen. + void printInstruction(const MCInst *MI, uint64_t Address, + raw_ostream &OS) override; + StringRef getRegName(unsigned RegNo) const override { + return getRegisterName(RegNo); + } + static const char *getRegisterName(unsigned RegNo); + }; +} + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/LLVMBuild.txt b/llvm/lib/Target/Z80/MCTargetDesc/LLVMBuild.txt new file mode 100644 index 00000000000000..d1d1da727aa7d8 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/Target/Z80/MCTargetDesc/LLVMBuild.txt --------------*- Conf -*--===; +; +; 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 is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = Z80Desc +parent = Z80 +required_libraries = MC MCDisassembler Object Support Z80Info +add_to_library_groups = Z80 diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.cpp new file mode 100644 index 00000000000000..5ad4eeb5df7da6 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.cpp @@ -0,0 +1,86 @@ +//===-- Z80AsmBackend.cpp - Z80 Asm Backend ------------------------------===// +// +// 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 Z80AsmBackend class. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80AsmBackend.h" +#include "MCTargetDesc/Z80FixupKinds.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" + +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" + +#include + +namespace llvm { + +std::unique_ptr +Z80AsmBackend::createObjectTargetWriter() const { + return createZ80ELFObjectWriter(ELF::ELFOSABI_STANDALONE); +} + +MCFixupKindInfo const &Z80AsmBackend::getFixupKindInfo(MCFixupKind Kind) const { + const static MCFixupKindInfo Infos[Z80::NumTargetFixupKinds] = { + // This table *must* be in same the order of fixup_* kinds in + // Z80FixupKinds.h. + // + // name offset bits flags + {"fixup_8", 0U, 8U, 0U}, + {"fixup_8_dis", 0U, 8U, 0U}, + {"fixup_8_pcrel", 0U, 8U, MCFixupKindInfo::FKF_IsPCRel}, + {"fixup_16", 0U, 16U, 0U}, + {"fixup_24", 0U, 24U, 0U}, + {"fixup_32", 0U, 32U, 0U}, + {"fixup_byte0", 0U, 32U, 0U}, + {"fixup_byte1", 0U, 32U, 0U}, + {"fixup_byte2", 0U, 32U, 0U}, + {"fixup_byte3", 0U, 32U, 0U}, + {"fixup_word0", 0U, 32U, 0U}, + {"fixup_word1", 0U, 32U, 0U}, + {"fixup_16_be", 0U, 16U, 0U}}; + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; +} + +bool Z80AsmBackend::shouldForceRelocation(const MCAssembler &Asm, + const MCFixup &Fixup, + const MCValue &Target) { + switch ((unsigned)Fixup.getKind()) { + default: + return false; + // Fixups which should always be recorded as relocations. + case Z80::fixup_8_dis: + case Z80::fixup_8_pcrel: + case Z80::fixup_16: + return true; + } +} + +MCAsmBackend *createZ80AsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const llvm::MCTargetOptions &TO) { + return new Z80AsmBackend(); +} + +} // end of namespace llvm diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.h new file mode 100644 index 00000000000000..7c66632438a190 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80AsmBackend.h @@ -0,0 +1,85 @@ +//===-- Z80AsmBackend.h - Z80 Asm Backend --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// \file The Z80 assembly backend implementation. +// +//===----------------------------------------------------------------------===// +// + +#ifndef LLVM_Z80_ASM_BACKEND_H +#define LLVM_Z80_ASM_BACKEND_H + +#include "MCTargetDesc/Z80FixupKinds.h" + +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCFixupKindInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +namespace llvm { + +class MCAsmLayout; +class MCAssembler; +class MCInst; +class MCObjectWriter; +class MCRelaxableFragment; +class MCSubtargetInfo; +class MCValue; +class Target; + +struct MCFixupKindInfo; + +/// Utilities for manipulating generated Z80 machine code. +class Z80AsmBackend : public MCAsmBackend { +public: + Z80AsmBackend() : MCAsmBackend(support::little) {} + + std::unique_ptr + createObjectTargetWriter() const override; + + void applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target, MutableArrayRef Data, + uint64_t Value, bool IsResolved, + const MCSubtargetInfo *STI) const override {} + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override; + + unsigned getNumFixupKinds() const override { + return Z80::NumTargetFixupKinds; + } + + bool mayNeedRelaxation(const MCInst &Inst, + const MCSubtargetInfo &STI) const override { + return false; + } + + bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value, + const MCRelaxableFragment *DF, + const MCAsmLayout &Layout) const override { + llvm_unreachable("RelaxInstruction() unimplemented"); + return false; + } + + bool writeNopData(raw_ostream &OS, uint64_t Count) const override { + OS.write_zeros(Count); + return true; + } + + bool shouldForceRelocation(const MCAssembler &Asm, const MCFixup &Fixup, + const MCValue &Target) override; +}; + +} // end namespace llvm + +#endif // LLVM_Z80_ASM_BACKEND_H diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFObjectWriter.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFObjectWriter.cpp new file mode 100644 index 00000000000000..33448a637d7dcc --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFObjectWriter.cpp @@ -0,0 +1,91 @@ +//===-- Z80ELFObjectWriter.cpp - Z80 ELF Writer ---------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80FixupKinds.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCELFObjectWriter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" + +#include +#include + +namespace llvm { + +/// Writes Z80 machine code into an ELF32 object file. +class Z80ELFObjectWriter : public MCELFObjectTargetWriter { +public: + Z80ELFObjectWriter(uint8_t OSABI); + + virtual ~Z80ELFObjectWriter() {} + + unsigned getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, bool IsPCRel) const override; +}; + +Z80ELFObjectWriter::Z80ELFObjectWriter(uint8_t OSABI) + : MCELFObjectTargetWriter(false, OSABI, ELF::EM_Z80, true) {} + +unsigned Z80ELFObjectWriter::getRelocType(MCContext &Ctx, const MCValue &Target, + const MCFixup &Fixup, + bool IsPCRel) const { + const MCSymbolRefExpr *SymRef = dyn_cast(Fixup.getValue()); + + switch (static_cast(Fixup.getKind())) { + case FK_Data_1: + case Z80::fixup_8: + return ELF::R_Z80_8; + case Z80::fixup_8_dis: + return ELF::R_Z80_8_DIS; + case Z80::fixup_8_pcrel: + assert(IsPCRel); + return ELF::R_Z80_8_PCREL; + case FK_Data_2: + case Z80::fixup_16: + return ELF::R_Z80_16; + case Z80::fixup_24: + return ELF::R_Z80_24; + case FK_Data_4: + case Z80::fixup_32: + return ELF::R_Z80_32; + case Z80::fixup_byte0: + return ELF::R_Z80_BYTE0; + case Z80::fixup_byte1: + return ELF::R_Z80_BYTE1; + case Z80::fixup_byte2: + return ELF::R_Z80_BYTE2; + case Z80::fixup_byte3: + return ELF::R_Z80_BYTE3; + case Z80::fixup_word0: + return ELF::R_Z80_WORD0; + case Z80::fixup_word1: + return ELF::R_Z80_WORD1; + case Z80::fixup_16_be: + return ELF::R_Z80_16_BE; + default: + llvm_unreachable(Twine("Invalid fixup kind! ", + SymRef ? SymRef->getSymbol().getName().str() + : "(not even a symref!)") + .str() + .c_str()); + } +} + +std::unique_ptr createZ80ELFObjectWriter(uint8_t OSABI) { + return std::make_unique(OSABI); +} + +} // end of namespace llvm diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.cpp new file mode 100644 index 00000000000000..22ce454f6baf8b --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.cpp @@ -0,0 +1,27 @@ +//===-- Z80ELFStreamer.cpp - Z80 ELF Streamer -----------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "Z80ELFStreamer.h" + +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/FormattedStream.h" + +namespace llvm { + +Z80ELFStreamer::Z80ELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI) + : Z80TargetStreamer(S) { + MCAssembler &MCA = getStreamer().getAssembler(); + unsigned EFlags = MCA.getELFHeaderEFlags(); + + EFlags |= ELF::EF_Z80_MACH_Z80; + MCA.setELFHeaderEFlags(EFlags); +} + +} // end namespace llvm diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.h new file mode 100644 index 00000000000000..9234797e1e361f --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80ELFStreamer.h @@ -0,0 +1,41 @@ +//===----- Z80ELFStreamer.h - Z80 ELF Streamer -----------------*- C++ -*--===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_Z80_ELF_STREAMER_H +#define LLVM_Z80_ELF_STREAMER_H + +#include "Z80TargetStreamer.h" + +#include "llvm/MC/MCDirectives.h" +#include "llvm/MC/MCELFStreamer.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" + +#include + +namespace llvm { + +/// A target streamer for an Z80 ELF object file. +class Z80ELFStreamer : public Z80TargetStreamer { +public: + Z80ELFStreamer(MCStreamer &S, const MCSubtargetInfo &STI); + + MCELFStreamer &getStreamer() { + return static_cast(Streamer); + } + + void emitAlign(unsigned u) override {} + void emitBlock(uint64_t u) override {} + void emitLocal(MCSymbol *S) override {} + void emitGlobal(MCSymbol *S) override {} + void emitExtern(MCSymbol *S) override {} +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80FixupKinds.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80FixupKinds.h new file mode 100644 index 00000000000000..fcd5e69dd149bf --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80FixupKinds.h @@ -0,0 +1,48 @@ +//===-- Z80FixupKinds.h - Z80 Specific Fixup Entries ------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_Z80_FIXUP_KINDS_H +#define LLVM_Z80_FIXUP_KINDS_H + +#include "llvm/MC/MCFixup.h" + +namespace llvm { +namespace Z80 { + +/// The set of supported fixups. +/// +/// Although most of the current fixup types reflect a unique relocation +/// one can have multiple fixup types for a given relocation and thus need +/// to be uniquely named. +/// +/// \note This table *must* be in the same order of +/// MCFixupKindInfo Infos[Z80::NumTargetFixupKinds] +/// in `Z80AsmBackend.cpp`. +enum Fixups { + fixup_8 = FirstTargetFixupKind, + fixup_8_dis, + fixup_8_pcrel, + fixup_16, + fixup_24, + fixup_32, + fixup_byte0, + fixup_byte1, + fixup_byte2, + fixup_byte3, + fixup_word0, + fixup_word1, + fixup_16_be, + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; + +} // end of namespace Z80 +} // end of namespace llvm + +#endif // LLVM_Z80_FIXUP_KINDS_H diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.cpp new file mode 100644 index 00000000000000..b8a5abea46bed9 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.cpp @@ -0,0 +1,23 @@ +//===- Z80InstPrinter.cpp - Convert Z80 MCInst to assembly ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints a Z80 MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#include "Z80InstPrinter.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +// Include the auto-generated portion of the assembly writer. +#include "Z80GenAsmWriter.inc" diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.h new file mode 100644 index 00000000000000..93e9a2a6c7d71c --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinter.h @@ -0,0 +1,36 @@ +//===- Z80InstPrinter.h - Convert Z80 MCInst to assembly --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class prints a Z80 MCInst to a .s file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_INSTPRINTER_Z80INSTPRINTER_H +#define LLVM_LIB_TARGET_Z80_INSTPRINTER_Z80INSTPRINTER_H + +#include "Z80InstPrinterCommon.h" + +namespace llvm { + class Z80InstPrinter final : public Z80InstPrinterCommon { + public: + Z80InstPrinter(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : Z80InstPrinterCommon(MAI, MII, MRI) {} + + // Autogenerated by tblgen. + void printInstruction(const MCInst *MI, uint64_t Address, + raw_ostream &OS) override; + StringRef getRegName(unsigned RegNo) const override { + return getRegisterName(RegNo); + } + static const char *getRegisterName(unsigned RegNo); + }; +} + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.cpp new file mode 100644 index 00000000000000..df8515e75ba6ec --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.cpp @@ -0,0 +1,114 @@ +//===- Z80InstPrinter.cpp - Convert Z80 MCInst to assembly ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file includes code common for rendering MCInst instances as (e)Z80 +// assembly. +// +//===----------------------------------------------------------------------===// + +#include "Z80InstPrinterCommon.h" +#include "Z80InstPrinter.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FormattedStream.h" +using namespace llvm; + +#define DEBUG_TYPE "asm-printer" + +void Z80InstPrinterCommon::printRegName(raw_ostream &OS, unsigned RegNo) const { + OS << markup(""); +} + +void Z80InstPrinterCommon::printInst(const MCInst *MI, uint64_t Address, + StringRef Annot, + const MCSubtargetInfo &STI, + raw_ostream &OS) { + printInstruction(MI, Address, OS); + printAnnotation(OS, Annot); +} + +void Z80InstPrinterCommon::printOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &OS) { + const MCOperand &Op = MI->getOperand(OpNo); + if (Op.isReg()) { + printRegName(OS, Op.getReg()); + } else if (Op.isImm()) { + OS << Op.getImm(); + } else { + assert(Op.isExpr() && "unknown operand kind in printOperand"); + OS << markup("print(OS, &MAI); + OS << markup(">"); + } +} +void Z80InstPrinterCommon::printCCOperand(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + switch (MI->getOperand(Op).getImm()) { + default: + llvm_unreachable("Invalid CC operand!"); + case 0: + OS << "nz"; + break; + case 1: + OS << "z"; + break; + case 2: + OS << "nc"; + break; + case 3: + OS << "c"; + break; + case 4: + OS << "po"; + break; + case 5: + OS << "pe"; + break; + case 6: + OS << "p"; + break; + case 7: + OS << "m"; + break; + } +} + +void Z80InstPrinterCommon::printMem(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + OS << markup(""); + ; +} +void Z80InstPrinterCommon::printPtr(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + OS << markup(""); +} +void Z80InstPrinterCommon::printOff(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + OS << markup(""); +} +void Z80InstPrinterCommon::printAddr(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + printOperand(MI, Op, OS); + auto Off = MI->getOperand(Op + 1).getImm(); + assert(isInt<8>(Off) && "Offset out of range!"); + OS << " + " << int(int8_t(Off)); +} +void Z80InstPrinterCommon::printBit(const MCInst *MI, unsigned Op, + raw_ostream &OS) { + auto Off = MI->getOperand(Op).getImm(); + assert(isUInt<3>(Off) && "Offset out of range!"); + OS << int(Off & 7); +} diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.h new file mode 100644 index 00000000000000..fbb67a9b21c121 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80InstPrinterCommon.h @@ -0,0 +1,47 @@ +//===- Z80InstPrinterCommon.h - Convert Z80 MCInst to assembly --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file includes code common for rendering MCInst instances as (e)Z80 +// assembly. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_INSTPRINTER_Z80INSTPRINTERCOMMON_H +#define LLVM_LIB_TARGET_Z80_INSTPRINTER_Z80INSTPRINTERCOMMON_H + +#include "llvm/MC/MCInstPrinter.h" + +namespace llvm { + class Z80InstPrinterCommon : public MCInstPrinter { + public: + Z80InstPrinterCommon(const MCAsmInfo &MAI, const MCInstrInfo &MII, + const MCRegisterInfo &MRI) + : MCInstPrinter(MAI, MII, MRI) {} + + void printRegName(raw_ostream &OS, unsigned RegNo) const override; + void printInst(const MCInst *MI, uint64_t Address, StringRef Annot, + const MCSubtargetInfo &STI, raw_ostream &OS) override; + + // Autogenerated by tblgen. + virtual void printInstruction(const MCInst *MI, uint64_t Address, + raw_ostream &OS) = 0; + virtual StringRef getRegName(unsigned RegNo) const = 0; + + void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printCCOperand(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + + void printMem(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printPtr(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printOff(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printAddr(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + void printBit(const MCInst *MI, unsigned OpNo, raw_ostream &OS); + }; +} + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp new file mode 100644 index 00000000000000..78447584482853 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.cpp @@ -0,0 +1,61 @@ +//===-- Z80MCAsmInfo.cpp - Z80 asm properties -----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declarations of the Z80MCAsmInfo properties. +// +//===----------------------------------------------------------------------===// + +#include "Z80MCAsmInfo.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +using namespace llvm; + +void Z80MCAsmInfo::anchor() { } + +Z80MCAsmInfo::Z80MCAsmInfo(const Triple &T) { + bool Is16Bit = T.isArch16Bit() || T.getEnvironment() == Triple::CODE16; + CodePointerSize = CalleeSaveStackSlotSize = Is16Bit ? 2 : 3; + MaxInstLength = 6; + DollarIsPC = true; + SeparatorString = nullptr; + CommentString = ";"; + PrivateGlobalPrefix = PrivateLabelPrefix = ".L"; + Code16Directive = "assume\tadl = 0"; + Code24Directive = "assume\tadl = 1"; + Code32Directive = Code64Directive = nullptr; + AssemblerDialect = !Is16Bit; + SupportsQuotedNames = false; + ZeroDirective = AsciiDirective = AscizDirective = nullptr; + BlockSeparator = " dup "; + Data8bitsDirective = "\tdb\t"; + Data16bitsDirective = "\tdw\t"; + Data24bitsDirective = "\tdl\t"; + Data32bitsDirective = "\tdd\t"; + Data64bitsDirective = nullptr; + AlwaysChangeSection = true; + GlobalDirective = "\tpublic\t"; + SetDirective = "\tlabel\t"; + SetSeparator = " at "; + HasFunctionAlignment = false; + HasDotTypeDotSizeDirective = false; + WeakDirective = "\tweak\t"; + UseIntegratedAssembler = false; + UseLogicalShr = false; + HasSingleParameterDotFile = false; +} + +const char *Z80MCAsmInfo::getBlockDirective(int64_t Size) const { + switch (Size) { + default: return nullptr; + case 1: return "\tdb\t"; + case 2: return "\tdw\t"; + case 3: return "\tdl\t"; + case 4: return "\tdd\t"; + } +} diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.h new file mode 100644 index 00000000000000..1dba0512bed4b4 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCAsmInfo.h @@ -0,0 +1,32 @@ +//===-- Z80MCAsmInfo.h - Z80 asm properties --------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the declaration of the Z80MCAsmInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCASMINFO_H +#define LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCASMINFO_H + +#include "llvm/MC/MCAsmInfoOMF.h" + +namespace llvm { +class Triple; + +class Z80MCAsmInfo : public MCAsmInfoOMF { + void anchor() override; + +public: + explicit Z80MCAsmInfo(const Triple &Triple); + + const char *getBlockDirective(int64_t Size) const override; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.cpp new file mode 100644 index 00000000000000..51617c64e263c2 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.cpp @@ -0,0 +1,5398 @@ +//===-- Z80MCCodeEmitter.cpp - Convert Z80 Code to Machine Code -----------===// +// +// 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 Z80MCCodeEmitter class. +// +//===----------------------------------------------------------------------===// + +#include "Z80MCCodeEmitter.h" + +#include "MCTargetDesc/Z80MCTargetDesc.h" + +#include "Z80FixupKinds.h" +#include "Z80InstrInfo.h" +#include "Z80RegisterInfo.h" +#include "Z80Subtarget.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +#define DEBUG_TYPE "mccodeemitter" + +#define GET_INSTRMAP_INFO +#include "Z80GenInstrInfo.inc" +#undef GET_INSTRMAP_INFO + +namespace llvm { + +static void check_num_operands(StringRef opc, unsigned expct, unsigned actual) { + if (expct != actual) { + std::string msg; + raw_string_ostream Msg(msg); + Msg << "Invalid number of arguments for instruction " << opc << ": "; + Msg << expct << " vs " << actual << '.'; + report_fatal_error(Msg.str()); + } +} + +static LLVM_ATTRIBUTE_NORETURN void report_fatal_instr_problem(StringRef opc, + const char *er) { + std::string msg; + raw_string_ostream Msg(msg); + Msg << opc << ": " << er; + report_fatal_error(Msg.str()); +} + +void Z80MCCodeEmitter::emitByte(uint8_t C, unsigned &CurByte, + raw_ostream &OS) const { + OS << (static_cast(C)); + ++CurByte; +} + +void Z80MCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + // Keep track of the current byte being emitted. + unsigned CurByte = 0U; + + const unsigned opcode = MI.getOpcode(); + const MCInstrDesc &Desc = MCII.get(opcode); + const unsigned numOperands = Desc.getNumOperands(); + const MCInst::const_iterator Operands = MI.begin(); + + if (Z80II::EZ80Mode == ((Desc.TSFlags) & Z80II::ModeMask)) { + std::string msg; + raw_string_ostream Msg(msg); + Msg << "EZ80 machine instructions not supported (yet?)"; + report_fatal_error(Msg.str()); + } + if (Desc.isPseudo()) { + switch (opcode) { + case Z80::JQ: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isExpr())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be an expression."); +#ifdef EMIT_JR_INSTEAD_OF_JP + emitByte(0x18U, CurByte, OS); + Fixups.push_back(MCFixup::create( + CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_8_pcrel), MI.getLoc())); + emitByte(0x00U, CurByte, OS); +#else + emitByte(0xc3U, CurByte, OS); + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); +#endif + break; + case Z80::JQCC: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isExpr())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be an expression."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); +#ifdef EMIT_JRCC_INSTEAD_OF_JPCC + if (4U <= static_cast(Operands[1].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be in range 0..3."); +#else + if (8U <= static_cast(Operands[1].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be in range 0..7."); +#endif +#ifdef EMIT_JRCC_INSTEAD_OF_JPCC + emitByte((static_cast(Operands[1].getImm()) << 3U) | 0x20U, + CurByte, OS); + Fixups.push_back(MCFixup::create( + CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_8_pcrel), MI.getLoc())); + emitByte(0x00U, CurByte, OS); +#else + emitByte((static_cast(Operands[1].getImm()) << 3U) | 0xc2U, + CurByte, OS); + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); +#endif + break; + default: + std::string msg; + raw_string_ostream Msg(msg); + Msg << "Not supported pseudo instr: " << MI; + report_fatal_error(Msg.str()); + } + return; + } + switch (opcode) { + case Z80::ADC8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xceU, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::ADC8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x8eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::ADC8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0x8eU, CurByte, OS); + break; + case Z80::ADC8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x8fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x88U, CurByte, OS); + break; + case Z80::C: + emitByte(0x89U, CurByte, OS); + break; + case Z80::D: + emitByte(0x8aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x8bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x8cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x8dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x8cU, CurByte, OS); // ADC A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x8dU, CurByte, OS); // ADC A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x8cU, CurByte, OS); // ADC A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x8dU, CurByte, OS); // ADC A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::ADD16SP: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + if ((Operands[0].getReg()) != (Operands[1].getReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be the same register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0x39U, CurByte, OS); + break; + case Z80::ADD16aa: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + if ((Operands[0].getReg()) != (Operands[1].getReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be the same register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0x29U, CurByte, OS); + break; + case Z80::ADD16ao: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()) && + (Operands[2].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "All operands should be registers."); + if ((Operands[0].getReg()) != (Operands[1].getReg())) + report_fatal_instr_problem( + MCII.getName(opcode), + "First two of the operands should be the same register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed first two registers are HL, IX, IY."); + } + switch (Operands[2].getReg()) { + case Z80::BC: + emitByte(0x09U, CurByte, OS); + break; + case Z80::DE: + emitByte(0x19U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed last registers are BC, DE."); + } + break; + case Z80::ADD8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xc6U, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::ADD8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x86U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::ADD8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0x86U, CurByte, OS); + break; + case Z80::ADD8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x87U, CurByte, OS); + break; + case Z80::B: + emitByte(0x80U, CurByte, OS); + break; + case Z80::C: + emitByte(0x81U, CurByte, OS); + break; + case Z80::D: + emitByte(0x82U, CurByte, OS); + break; + case Z80::E: + emitByte(0x83U, CurByte, OS); + break; + case Z80::H: + emitByte(0x84U, CurByte, OS); + break; + case Z80::L: + emitByte(0x85U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x84U, CurByte, OS); // ADD A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x85U, CurByte, OS); // ADD A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x84U, CurByte, OS); // ADD A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x85U, CurByte, OS); // ADD A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::AND8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xe6U, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::AND8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xa6U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::AND8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xa6U, CurByte, OS); + break; + case Z80::AND8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xa7U, CurByte, OS); + break; + case Z80::B: + emitByte(0xa0U, CurByte, OS); + break; + case Z80::C: + emitByte(0xa1U, CurByte, OS); + break; + case Z80::D: + emitByte(0xa2U, CurByte, OS); + break; + case Z80::E: + emitByte(0xa3U, CurByte, OS); + break; + case Z80::H: + emitByte(0xa4U, CurByte, OS); + break; + case Z80::L: + emitByte(0xa5U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xa4U, CurByte, OS); // AND A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xa5U, CurByte, OS); // AND A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xa4U, CurByte, OS); // AND A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xa5U, CurByte, OS); // AND A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::BIT8bg: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (8U <= static_cast(Operands[0].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x47U, + CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x40U, + CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x41U, + CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x42U, + CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x43U, + CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x44U, + CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x45U, + CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x44U, + CurByte, OS); // BIT b, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x45U, + CurByte, OS); // BIT b, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x44U, + CurByte, OS); // BIT b, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x45U, + CurByte, OS); // BIT b, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::BIT8bo: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x46, CurByte, + OS); + break; + case Z80::BIT8bp: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (8U <= static_cast(Operands[0].getImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + switch (Operands[1].getReg()) { + case Z80::HL: + emitByte(0xcbU, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xcbU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xcbU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x46U, + CurByte, OS); + break; + case Z80::CALL16: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + emitByte(0xcdU, CurByte, OS); + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Operand should be an expression or immediate."); + } + break; + case Z80::CALL16CC: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + if (8U <= static_cast(Operands[1].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be in range 0..7."); + } + emitByte((static_cast(Operands[1].getImm()) << 3U) | 0xc4U, + CurByte, OS); + if (Operands[0].isExpr()) { + if (!(MCExpr::SymbolRef == (Operands[0].getExpr()->getKind()))) + report_fatal_instr_problem( + MCII.getName(opcode), + "Fitst operand expression should be a call target."); + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "First operand should be an expression or immediate."); + } + break; + case Z80::CCF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x3fU, CurByte, OS); + break; + case Z80::CP8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xfeU, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::CP8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xbeU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::CP8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xbeU, CurByte, OS); + break; + case Z80::CP8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xbfU, CurByte, OS); + break; + case Z80::B: + emitByte(0xb8U, CurByte, OS); + break; + case Z80::C: + emitByte(0xb9U, CurByte, OS); + break; + case Z80::D: + emitByte(0xbaU, CurByte, OS); + break; + case Z80::E: + emitByte(0xbbU, CurByte, OS); + break; + case Z80::H: + emitByte(0xbcU, CurByte, OS); + break; + case Z80::L: + emitByte(0xbdU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xbcU, CurByte, OS); // CP A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xbdU, CurByte, OS); // CP A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xbcU, CurByte, OS); // CP A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xbdU, CurByte, OS); // CP A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::CPD16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa9U, CurByte, OS); + break; + case Z80::CPDR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb9U, CurByte, OS); + break; + case Z80::CPI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa1U, CurByte, OS); + break; + case Z80::CPIR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb1U, CurByte, OS); + break; + case Z80::CPL: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x2fU, CurByte, OS); + break; + case Z80::DEC16SP: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x3bU, CurByte, OS); + break; + case Z80::DEC16r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0x0bU, CurByte, OS); + break; + case Z80::DE: + emitByte(0x1bU, CurByte, OS); + break; + case Z80::HL: + emitByte(0x2bU, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x2bU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x2bU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + break; + case Z80::DEC8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x35U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::DEC8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + emitByte(0x35U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x35U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x35U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + break; + case Z80::DEC8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x3dU, CurByte, OS); + break; + case Z80::B: + emitByte(0x05U, CurByte, OS); + break; + case Z80::C: + emitByte(0x0dU, CurByte, OS); + break; + case Z80::D: + emitByte(0x15U, CurByte, OS); + break; + case Z80::E: + emitByte(0x1dU, CurByte, OS); + break; + case Z80::H: + emitByte(0x25U, CurByte, OS); + break; + case Z80::L: + emitByte(0x2dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x25U, CurByte, OS); // DEC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2dU, CurByte, OS); // DEC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x25U, CurByte, OS); // DEC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2dU, CurByte, OS); // DEC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::DI: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xf3U, CurByte, OS); + break; + case Z80::EI: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xfbU, CurByte, OS); + break; + case Z80::EX16DE: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xebU, CurByte, OS); + break; + case Z80::EX16SP: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + if ((Operands[0].getReg()) != (Operands[1].getReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be the same register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0xe3U, CurByte, OS); + break; + case Z80::EXAF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x08U, CurByte, OS); + break; + case Z80::EXX: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xd9U, CurByte, OS); + break; + case Z80::INC16SP: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x33U, CurByte, OS); + break; + case Z80::INC16r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0x03U, CurByte, OS); + break; + case Z80::DE: + emitByte(0x13U, CurByte, OS); + break; + case Z80::HL: + emitByte(0x23U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x23U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x23U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + break; + case Z80::INC8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x34U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::INC8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + emitByte(0x34U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x34U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x34U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + break; + case Z80::INC8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x3cU, CurByte, OS); + break; + case Z80::B: + emitByte(0x04U, CurByte, OS); + break; + case Z80::C: + emitByte(0x0cU, CurByte, OS); + break; + case Z80::D: + emitByte(0x14U, CurByte, OS); + break; + case Z80::E: + emitByte(0x1cU, CurByte, OS); + break; + case Z80::H: + emitByte(0x24U, CurByte, OS); + break; + case Z80::L: + emitByte(0x2cU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x24U, CurByte, OS); // INC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2cU, CurByte, OS); // INC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x24U, CurByte, OS); // INC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2cU, CurByte, OS); // INC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IND16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xaaU, CurByte, OS); + break; + case Z80::INDR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xbaU, CurByte, OS); + break; + case Z80::INI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa2U, CurByte, OS); + break; + case Z80::INIR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb2U, CurByte, OS); + break; + case Z80::JP16r: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0xe9U, CurByte, OS); + break; + case Z80::LD16SP: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0xf9U, CurByte, OS); + break; + case Z80::LD16am: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0x2aU, CurByte, OS); + if (Operands[1].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[1].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[1].isImm()) { + emitByte(((static_cast(Operands[1].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[1].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Second operand should be an expression or immediate."); + } + break; + case Z80::LD16ma: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::HL: + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(0x22U, CurByte, OS); + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "First operand should be an expression or immediate."); + } + break; + case Z80::LD16mo: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::BC: + emitByte(0xedU, CurByte, OS); + emitByte(0x43U, CurByte, OS); + break; + case Z80::DE: + emitByte(0xedU, CurByte, OS); + emitByte(0x53U, CurByte, OS); + break; + case Z80::HL: + emitByte(0xedU, CurByte, OS); + emitByte(0x63U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x22U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x22U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "First operand should be an expression or immediate."); + } + break; + case Z80::LD16om: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0xedU, CurByte, OS); + emitByte(0x4bU, CurByte, OS); + break; + case Z80::DE: + emitByte(0xedU, CurByte, OS); + emitByte(0x5bU, CurByte, OS); + break; + case Z80::HL: + emitByte(0xedU, CurByte, OS); + emitByte(0x6bU, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x2aU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x2aU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + if (Operands[1].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[1].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[1].isImm()) { + emitByte(((static_cast(Operands[1].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[1].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Second operand should be an expression or immediate."); + } + break; + case Z80::LD16ri: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0x01U, CurByte, OS); + break; + case Z80::DE: + emitByte(0x11U, CurByte, OS); + break; + case Z80::HL: + emitByte(0x21U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x21U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x21U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + if (Operands[1].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[1].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[1].isImm()) { + emitByte(((static_cast(Operands[1].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[1].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Second operand should be an expression or immediate."); + } + break; + case Z80::LD8am: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + emitByte(0x3aU, CurByte, OS); + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Operand should be an expression or immediate."); + } + break; + case Z80::LD8gg: + case Z80::LD8xx: + case Z80::LD8yy: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + switch (Operands[0].getReg()) { /* OUT: r */ + case Z80::A: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x7fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x78U, CurByte, OS); + break; + case Z80::C: + emitByte(0x79U, CurByte, OS); + break; + case Z80::D: + emitByte(0x7aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x7bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x7cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x7dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x7cU, CurByte, OS); // LD A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x7dU, CurByte, OS); // LD A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x7cU, CurByte, OS); // LD A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x7dU, CurByte, OS); // LD A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::B: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x47U, CurByte, OS); + break; + case Z80::B: + emitByte(0x40U, CurByte, OS); + break; + case Z80::C: + emitByte(0x41U, CurByte, OS); + break; + case Z80::D: + emitByte(0x42U, CurByte, OS); + break; + case Z80::E: + emitByte(0x43U, CurByte, OS); + break; + case Z80::H: + emitByte(0x44U, CurByte, OS); + break; + case Z80::L: + emitByte(0x45U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x44U, CurByte, OS); // LD B, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x45U, CurByte, OS); // LD B, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x44U, CurByte, OS); // LD B, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x45U, CurByte, OS); // LD B, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::C: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x4fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x48U, CurByte, OS); + break; + case Z80::C: + emitByte(0x49U, CurByte, OS); + break; + case Z80::D: + emitByte(0x4aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x4bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x4cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x4dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x4cU, CurByte, OS); // LD C, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x4dU, CurByte, OS); // LD C, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x4cU, CurByte, OS); // LD C, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x4dU, CurByte, OS); // LD C, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::D: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x57U, CurByte, OS); + break; + case Z80::B: + emitByte(0x50U, CurByte, OS); + break; + case Z80::C: + emitByte(0x51U, CurByte, OS); + break; + case Z80::D: + emitByte(0x52U, CurByte, OS); + break; + case Z80::E: + emitByte(0x53U, CurByte, OS); + break; + case Z80::H: + emitByte(0x54U, CurByte, OS); + break; + case Z80::L: + emitByte(0x55U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x54U, CurByte, OS); // LD D, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x55U, CurByte, OS); // LD D, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x54U, CurByte, OS); // LD D, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x55U, CurByte, OS); // LD D, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::E: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x5fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x58U, CurByte, OS); + break; + case Z80::C: + emitByte(0x59U, CurByte, OS); + break; + case Z80::D: + emitByte(0x5aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x5bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x5cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x5dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x5cU, CurByte, OS); // LD E, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x5dU, CurByte, OS); // LD E, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x5cU, CurByte, OS); // LD E, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x5dU, CurByte, OS); // LD E, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::H: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x67U, CurByte, OS); + break; + case Z80::B: + emitByte(0x60U, CurByte, OS); + break; + case Z80::C: + emitByte(0x61U, CurByte, OS); + break; + case Z80::D: + emitByte(0x62U, CurByte, OS); + break; + case Z80::E: + emitByte(0x63U, CurByte, OS); + break; + case Z80::H: + emitByte(0x64U, CurByte, OS); + break; + case Z80::L: + emitByte(0x65U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::L: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0x6fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x68U, CurByte, OS); + break; + case Z80::C: + emitByte(0x69U, CurByte, OS); + break; + case Z80::D: + emitByte(0x6aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x6bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x6cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x6dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IXH: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x67U, CurByte, OS); // LD H, A + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::B: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x60U, CurByte, OS); // LD H, B + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::C: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x61U, CurByte, OS); // LD H, C + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::D: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::E: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::H: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x54U, CurByte, OS); // LD D, H + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::L: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x55U, CurByte, OS); // LD D, L + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x64U, CurByte, OS); // LD H, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x65U, CurByte, OS); // LD H, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IXL: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6fU, CurByte, OS); // LD L, A + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::B: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x68U, CurByte, OS); // LD L, B + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::C: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x69U, CurByte, OS); // LD L, C + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::D: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::E: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::H: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5cU, CurByte, OS); // LD E, H + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::L: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5dU, CurByte, OS); // LD E, L + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6cU, CurByte, OS); // LD L, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6dU, CurByte, OS); // LD L, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IYH: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x67U, CurByte, OS); // LD H, A + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::B: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x60U, CurByte, OS); // LD H, B + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::C: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x61U, CurByte, OS); // LD H, C + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::D: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::E: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::H: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x54U, CurByte, OS); // LD D, H + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::L: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x55U, CurByte, OS); // LD D, L + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x62U, CurByte, OS); // LD H, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x63U, CurByte, OS); // LD H, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x64U, CurByte, OS); // LD H, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x65U, CurByte, OS); // LD H, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::IYL: + switch (Operands[1].getReg()) { /* IN: r' */ + case Z80::A: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6fU, CurByte, OS); // LD L, A + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::B: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x68U, CurByte, OS); // LD L, B + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::C: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x69U, CurByte, OS); // LD L, C + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::D: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::E: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::H: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5cU, CurByte, OS); // LD E, H + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::L: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5dU, CurByte, OS); // LD E, L + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6aU, CurByte, OS); // LD L, D + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x6bU, CurByte, OS); // LD L, E + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6cU, CurByte, OS); // LD L, H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x6dU, CurByte, OS); // LD L, L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::LD8go: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "First two operands should be registers."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + emitByte(0x66U, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD H, (IX|Y + Imm) + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + emitByte(0x6eU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD L, (IX|Y + Imm) + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + emitByte(0x66, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD H, (IX|Y + Imm) + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + emitByte(0x6eU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD L, (IX|Y + Imm) + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed second operand registers are IX, IY."); + } + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x7eU, CurByte, OS); + break; + case Z80::B: + emitByte(0x46U, CurByte, OS); + break; + case Z80::C: + emitByte(0x4eU, CurByte, OS); + break; + case Z80::D: + emitByte(0x56U, CurByte, OS); + break; + case Z80::E: + emitByte(0x5eU, CurByte, OS); + break; + case Z80::H: + emitByte(0x66U, CurByte, OS); + break; + case Z80::L: + emitByte(0x6eU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + emitByte(Operands[2].getImm(), CurByte, OS); + } + break; + case Z80::LD8gp: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + switch (Operands[1].getReg()) { + case Z80::HL: + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x7eU, CurByte, OS); + break; + case Z80::B: + emitByte(0x46U, CurByte, OS); + break; + case Z80::C: + emitByte(0x4eU, CurByte, OS); + break; + case Z80::D: + emitByte(0x56U, CurByte, OS); + break; + case Z80::E: + emitByte(0x5eU, CurByte, OS); + break; + case Z80::H: + emitByte(0x66U, CurByte, OS); + break; + case Z80::L: + emitByte(0x6eU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x56U, CurByte, OS); // LD D, (HL) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5eU, CurByte, OS); // LD E, (HL) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x56U, CurByte, OS); // LD D, (HL) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x5eU, CurByte, OS); // LD E, (HL) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::IX: + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xddU, CurByte, OS); + emitByte(0x7eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::B: + emitByte(0xddU, CurByte, OS); + emitByte(0x46U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xddU, CurByte, OS); + emitByte(0x4eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::D: + emitByte(0xddU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::E: + emitByte(0xddU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::H: + emitByte(0xddU, CurByte, OS); + emitByte(0x66U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::L: + emitByte(0xddU, CurByte, OS); + emitByte(0x6eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD D, (IX+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD E, (IX+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD D, (IX+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD E, (IX+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::IY: + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xfdU, CurByte, OS); + emitByte(0x7eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::B: + emitByte(0xfdU, CurByte, OS); + emitByte(0x46U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xfdU, CurByte, OS); + emitByte(0x4eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::D: + emitByte(0xfdU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::E: + emitByte(0xfdU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::H: + emitByte(0xfdU, CurByte, OS); + emitByte(0x66U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::L: + emitByte(0xfdU, CurByte, OS); + emitByte(0x6eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD D, (IY+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD E, (IY+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x56U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD D, (IY+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x5eU, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD E, (IY+0) + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + break; + case Z80::LD8ma: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + emitByte(0x32U, CurByte, OS); + if (Operands[0].isExpr()) { + Fixups.push_back(MCFixup::create(CurByte, Operands[0].getExpr(), + static_cast(Z80::fixup_16), + MI.getLoc())); + emitByte(0x00U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + } else if (Operands[0].isImm()) { + emitByte(((static_cast(Operands[0].getImm())) >> 0U) & 0xffU, + CurByte, OS); + emitByte(((static_cast(Operands[0].getImm())) >> 8U) & 0xffU, + CurByte, OS); + } else { + report_fatal_instr_problem( + MCII.getName(opcode), + "Operand should be an expression or immediate."); + } + break; + case Z80::LD8og: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!((Operands[0].isReg()) && (Operands[2].isReg()))) + report_fatal_instr_problem( + MCII.getName(opcode), "First and third operand should be registers."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[2].getReg()) { + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + emitByte(0x74U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD (IX|Y + Imm), H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + emitByte(0x75U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD (IX|Y + Imm), L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + emitByte(0x74U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD (IX|Y + Imm), H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + emitByte(0x75U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD (IX|Y + Imm), L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are IX, IY."); + } + switch (Operands[2].getReg()) { + case Z80::A: + emitByte(0x77U, CurByte, OS); + break; + case Z80::B: + emitByte(0x70U, CurByte, OS); + break; + case Z80::C: + emitByte(0x71U, CurByte, OS); + break; + case Z80::D: + emitByte(0x72U, CurByte, OS); + break; + case Z80::E: + emitByte(0x73U, CurByte, OS); + break; + case Z80::H: + emitByte(0x74U, CurByte, OS); + break; + case Z80::L: + emitByte(0x75U, CurByte, OS); + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed third operand registers are A, B, C, D, E, H, L."); + } + emitByte(Operands[1].getImm(), CurByte, OS); + } + break; + case Z80::LD8oi: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!((Operands[1].isImm()) && (Operands[2].isImm()))) + report_fatal_instr_problem( + MCII.getName(opcode), + "Second and third operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x36U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); + break; + case Z80::LD8pg: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "Both operands should be registers."); + switch (Operands[0].getReg()) { + case Z80::HL: + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0x77U, CurByte, OS); + break; + case Z80::B: + emitByte(0x70U, CurByte, OS); + break; + case Z80::C: + emitByte(0x71U, CurByte, OS); + break; + case Z80::D: + emitByte(0x72U, CurByte, OS); + break; + case Z80::E: + emitByte(0x73U, CurByte, OS); + break; + case Z80::H: + emitByte(0x74U, CurByte, OS); + break; + case Z80::L: + emitByte(0x75U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x72U, CurByte, OS); // LD (HL), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x73U, CurByte, OS); // LD (HL), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x72U, CurByte, OS); // LD (HL), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0x73U, CurByte, OS); // LD (HL), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::IX: + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xddU, CurByte, OS); + emitByte(0x77U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::B: + emitByte(0xddU, CurByte, OS); + emitByte(0x70U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xddU, CurByte, OS); + emitByte(0x71U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::D: + emitByte(0xddU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::E: + emitByte(0xddU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::H: + emitByte(0xddU, CurByte, OS); + emitByte(0x74U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::L: + emitByte(0xddU, CurByte, OS); + emitByte(0x75U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IX+0), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IX+0), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IX+0), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xddU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IX+0), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::IY: + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xfdU, CurByte, OS); + emitByte(0x77U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::B: + emitByte(0xfdU, CurByte, OS); + emitByte(0x70U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xfdU, CurByte, OS); + emitByte(0x71U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::D: + emitByte(0xfdU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::E: + emitByte(0xfdU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::H: + emitByte(0xfdU, CurByte, OS); + emitByte(0x74U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::L: + emitByte(0xfdU, CurByte, OS); + emitByte(0x75U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IY+0), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IXL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IY+0), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYH: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IY+0), D + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::IYL: + emitByte(0xd5U, CurByte, OS); // PUSH DE + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xd1U, CurByte, OS); // POP DE + emitByte(0xfdU, CurByte, OS); + emitByte(0x73U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD (IY+0), E + emitByte(0xd1U, CurByte, OS); // POP DE + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + break; + case Z80::LD8ri: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x3eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::B: + emitByte(0x06U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::C: + emitByte(0x0eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::D: + emitByte(0x16U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::E: + emitByte(0x1eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::H: + emitByte(0x26U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::L: + emitByte(0x2eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x26U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD H, Imm + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD L, Imm + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x26U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD H, Imm + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x2eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); // LD L, Imm + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed first operand registers are A, B, C, D, E, H, L."); + } + break; + case Z80::LD8pi: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::HL: + emitByte(0x36U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0x36U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0x36U, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are HL, IX, IY."); + } + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::LDD16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa8U, CurByte, OS); + break; + case Z80::LDDR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb8U, CurByte, OS); + break; + case Z80::LDI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa0U, CurByte, OS); + break; + case Z80::LDIR16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb0U, CurByte, OS); + break; + case Z80::LEA16ro: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!((Operands[0].isReg()) && (Operands[1].isReg()))) + report_fatal_instr_problem(MCII.getName(opcode), + "First two operands should be registers."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + emitByte(0xf5U, CurByte, OS); // PUSH AF + if (Z80::BC != Operands[0].getReg()) + emitByte(0xc5U, CurByte, OS); // PUSH BC + emitByte(0x06U, CurByte, OS); + emitByte(0x00U, CurByte, OS); // LD B, 0 + emitByte(0x0eU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); // LD C, Imm + switch (Operands[1].getReg()) { + case Z80::IX: + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + } + emitByte(0xddU, CurByte, OS); + emitByte(0x09U, CurByte, OS); // ADD IX, BC + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + } + break; + case Z80::IY: + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + } + emitByte(0xfdU, CurByte, OS); + emitByte(0x09U, CurByte, OS); // ADD IY, BC + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + } + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed registers in the second operand are IX, IY."); + } + if ((Operands[0].getReg()) != (Operands[1].getReg())) { + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0xc1U, CurByte, OS); // POP BC + break; + case Z80::DE: + emitByte(0xd1U, CurByte, OS); // POP DE + break; + case Z80::HL: + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed registers in the first operand are BC, DE, HL, IX, IY."); + } + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + break; + default: + report_fatal_instr_problem( + MCII.getName(opcode), + "Allowed registers in the second operand are IX, IY."); + } + } + if (Z80::BC != Operands[0].getReg()) + emitByte(0xc1U, CurByte, OS); // POP BC + emitByte(0xf1U, CurByte, OS); // POP AF + break; + case Z80::NEG: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x44U, CurByte, OS); + break; + case Z80::NOP: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x00U, CurByte, OS); + break; + case Z80::OR8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xf6U, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::OR8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xb6U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::OR8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xb6U, CurByte, OS); + break; + case Z80::OR8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xb7U, CurByte, OS); + break; + case Z80::B: + emitByte(0xb0U, CurByte, OS); + break; + case Z80::C: + emitByte(0xb1U, CurByte, OS); + break; + case Z80::D: + emitByte(0xb2U, CurByte, OS); + break; + case Z80::E: + emitByte(0xb3U, CurByte, OS); + break; + case Z80::H: + emitByte(0xb4U, CurByte, OS); + break; + case Z80::L: + emitByte(0xb5U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xb4U, CurByte, OS); // OR A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xb5U, CurByte, OS); // OR A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xb4U, CurByte, OS); // OR A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xb5U, CurByte, OS); // OR A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::OUTD16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xabU, CurByte, OS); + break; + case Z80::OUTDR16: /* OTDR */ + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xbbU, CurByte, OS); + break; + case Z80::OUTI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xa3U, CurByte, OS); + break; + case Z80::OUTIR16: /* OTIR */ + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0xb3U, CurByte, OS); + break; + case Z80::POP16AF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xf1U, CurByte, OS); + break; + case Z80::POP16r: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0xc1U, CurByte, OS); + break; + case Z80::DE: + emitByte(0xd1U, CurByte, OS); + break; + case Z80::HL: + emitByte(0xe1U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + break; + case Z80::PUSH16AF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xf5U, CurByte, OS); + break; + case Z80::PUSH16r: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0xc5U, CurByte, OS); + break; + case Z80::DE: + emitByte(0xd5U, CurByte, OS); + break; + case Z80::HL: + emitByte(0xe5U, CurByte, OS); + break; + case Z80::IX: + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE, HL, IX, IY."); + } + break; + case Z80::RES8bg: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x87U, + CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x80U, + CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x81U, + CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x82U, + CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x83U, + CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x84U, + CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x85U, + CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x84U, + CurByte, OS); // RESn A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x85U, + CurByte, OS); // RESn A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x84U, + CurByte, OS); // RESn A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x85U, + CurByte, OS); // RESn A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::RES8bo: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x86U, + CurByte, OS); + break; + case Z80::RES8bp: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0x86U, + CurByte, OS); + break; + case Z80::RET16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xc9U, CurByte, OS); + break; + case Z80::RET16CC: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be in range 0..7."); + } + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc0U, + CurByte, OS); + break; + case Z80::RETI16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x4dU, CurByte, OS); + break; + case Z80::RETN16: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x45U, CurByte, OS); + break; + case Z80::RL8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x16U, CurByte, OS); + break; + case Z80::RL8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x16U, CurByte, OS); + break; + case Z80::RL8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x17U, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x10U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x11U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x12U, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x13U, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x14U, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x15U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x14U, CurByte, OS); // RL H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x15U, CurByte, OS); // RL L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x14U, CurByte, OS); // RL H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x15U, CurByte, OS); // RL L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::RLC8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x06U, CurByte, OS); + break; + case Z80::RLC8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x06U, CurByte, OS); + break; + case Z80::RLC8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x07U, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x00U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x01U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x02U, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x03U, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x04U, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x05U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x04U, CurByte, OS); // RLC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x05U, CurByte, OS); // RLC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x04U, CurByte, OS); // RLC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x05U, CurByte, OS); // RLC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::RR8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x1eU, CurByte, OS); + break; + case Z80::RR8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x1eU, CurByte, OS); + break; + case Z80::RR8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1fU, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x18U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x19U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1aU, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1bU, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1cU, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x1dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x1cU, CurByte, OS); // RR H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x1dU, CurByte, OS); // RR L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x1cU, CurByte, OS); // RR H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x1dU, CurByte, OS); // RR L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::RRC8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x0eU, CurByte, OS); + break; + case Z80::RRC8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x0eU, CurByte, OS); + break; + case Z80::RRC8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0fU, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x08U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x09U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0aU, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0bU, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0cU, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x0dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x0cU, CurByte, OS); // RRC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x0dU, CurByte, OS); // RRC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x0cU, CurByte, OS); // RRC H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x0dU, CurByte, OS); // RRC L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SBC16SP: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x72U, CurByte, OS); + break; + case Z80::SBC16aa: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0xedU, CurByte, OS); + emitByte(0x62U, CurByte, OS); + break; + case Z80::SBC16ao: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + emitByte(0xedU, CurByte, OS); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::BC: + emitByte(0x42U, CurByte, OS); + break; + case Z80::DE: + emitByte(0x52U, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are BC, DE."); + } + break; + case Z80::SBC8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xdeU, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::SBC8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x9eU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::SBC8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0x9eU, CurByte, OS); + break; + case Z80::SBC8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x9fU, CurByte, OS); + break; + case Z80::B: + emitByte(0x98U, CurByte, OS); + break; + case Z80::C: + emitByte(0x99U, CurByte, OS); + break; + case Z80::D: + emitByte(0x9aU, CurByte, OS); + break; + case Z80::E: + emitByte(0x9bU, CurByte, OS); + break; + case Z80::H: + emitByte(0x9cU, CurByte, OS); + break; + case Z80::L: + emitByte(0x9dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x9cU, CurByte, OS); // SBC A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x9dU, CurByte, OS); // SBC A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x9cU, CurByte, OS); // SBC A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x9dU, CurByte, OS); // SBC A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SCF: + check_num_operands(MCII.getName(opcode), 0U, numOperands); + emitByte(0x37U, CurByte, OS); + break; + case Z80::SET8bg: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc7U, + CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc0U, + CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc1U, + CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc2U, + CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc3U, + CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc4U, + CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc5U, + CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc4U, + CurByte, OS); // SETn A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc5U, + CurByte, OS); // SETn A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc4U, + CurByte, OS); // SETn A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc5U, + CurByte, OS); // SETn A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SET8bo: + check_num_operands(MCII.getName(opcode), 3U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + if (!(Operands[2].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Third operand should be immediate."); + switch (Operands[1].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[2].getImm(), CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc6U, + CurByte, OS); + break; + case Z80::SET8bp: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be immediate."); + if (8U <= static_cast(Operands[0].getImm())) { + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be in range 0..7."); + } + if (!(Operands[1].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be register."); + switch (Operands[1].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte((static_cast(Operands[0].getImm()) << 3U) | 0xc6U, + CurByte, OS); + break; + case Z80::SLA8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x26U, CurByte, OS); + break; + case Z80::SLA8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x26U, CurByte, OS); + break; + case Z80::SLA8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x27U, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x20U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x21U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x22U, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x23U, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x24U, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x25U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x24U, CurByte, OS); // SLA H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x25U, CurByte, OS); // SLA L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x24U, CurByte, OS); // SLA H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x25U, CurByte, OS); // SLA L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SRA8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x2eU, CurByte, OS); + break; + case Z80::SRA8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x2eU, CurByte, OS); + break; + case Z80::SRA8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2fU, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x28U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x29U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2aU, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2bU, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2cU, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x2dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x2cU, CurByte, OS); // SRA H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x2dU, CurByte, OS); // SRA L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x2cU, CurByte, OS); // SRA H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x2dU, CurByte, OS); // SRA L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SRL8o: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + emitByte(0x3eU, CurByte, OS); + break; + case Z80::SRL8p: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xcbU, CurByte, OS); + emitByte(0x3eU, CurByte, OS); + break; + case Z80::SRL8r: + if (!numOperands) + report_fatal_instr_problem(MCII.getName(opcode), "Operand missing."); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "An operand should be an register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3fU, CurByte, OS); + break; + case Z80::B: + emitByte(0xcbU, CurByte, OS); + emitByte(0x38U, CurByte, OS); + break; + case Z80::C: + emitByte(0xcbU, CurByte, OS); + emitByte(0x39U, CurByte, OS); + break; + case Z80::D: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3aU, CurByte, OS); + break; + case Z80::E: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3bU, CurByte, OS); + break; + case Z80::H: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3cU, CurByte, OS); + break; + case Z80::L: + emitByte(0xcbU, CurByte, OS); + emitByte(0x3dU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x3cU, CurByte, OS); // SRL H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x3dU, CurByte, OS); // SRL L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IX + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x3cU, CurByte, OS); // SRL H + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xcbU, CurByte, OS); + emitByte(0x3dU, CurByte, OS); // SRL L + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe1U, CurByte, OS); // POP IY + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::SUB8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xd6U, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::SUB8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0x96U, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::SUB8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0x96U, CurByte, OS); + break; + case Z80::SUB8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0x97U, CurByte, OS); + break; + case Z80::B: + emitByte(0x90U, CurByte, OS); + break; + case Z80::C: + emitByte(0x91U, CurByte, OS); + break; + case Z80::D: + emitByte(0x92U, CurByte, OS); + break; + case Z80::E: + emitByte(0x93U, CurByte, OS); + break; + case Z80::H: + emitByte(0x94U, CurByte, OS); + break; + case Z80::L: + emitByte(0x95U, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x94U, CurByte, OS); // SUB A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x95U, CurByte, OS); // SUB A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x94U, CurByte, OS); // SUB A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0x95U, CurByte, OS); // SUB A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::XOR8ai: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be immediate."); + emitByte(0xeeU, CurByte, OS); + emitByte(Operands[0].getImm(), CurByte, OS); + break; + case Z80::XOR8ao: + check_num_operands(MCII.getName(opcode), 2U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "First operand should be register."); + if (!(Operands[1].isImm())) + report_fatal_instr_problem(MCII.getName(opcode), + "Second operand should be immediate."); + switch (Operands[0].getReg()) { + case Z80::IX: + emitByte(0xddU, CurByte, OS); + break; + case Z80::IY: + emitByte(0xfdU, CurByte, OS); + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed registers are IX, IY."); + } + emitByte(0xaeU, CurByte, OS); + emitByte(Operands[1].getImm(), CurByte, OS); + break; + case Z80::XOR8ap: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::HL: + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "The only allowed register is HL."); + } + emitByte(0xaeU, CurByte, OS); + break; + case Z80::XOR8ar: + check_num_operands(MCII.getName(opcode), 1U, numOperands); + if (!(Operands[0].isReg())) + report_fatal_instr_problem(MCII.getName(opcode), + "Operand should be register."); + switch (Operands[0].getReg()) { + case Z80::A: + emitByte(0xafU, CurByte, OS); + break; + case Z80::B: + emitByte(0xa8U, CurByte, OS); + break; + case Z80::C: + emitByte(0xa9U, CurByte, OS); + break; + case Z80::D: + emitByte(0xaaU, CurByte, OS); + break; + case Z80::E: + emitByte(0xabU, CurByte, OS); + break; + case Z80::H: + emitByte(0xacU, CurByte, OS); + break; + case Z80::L: + emitByte(0xadU, CurByte, OS); + break; + case Z80::IXH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xacU, CurByte, OS); // XOR A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IXL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xddU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IX + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xadU, CurByte, OS); // XOR A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYH: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xacU, CurByte, OS); // XOR A, H + emitByte(0xe1U, CurByte, OS); // POP HL + break; + case Z80::IYL: + emitByte(0xe5U, CurByte, OS); // PUSH HL + emitByte(0xfdU, CurByte, OS); + emitByte(0xe5U, CurByte, OS); // PUSH IY + emitByte(0xe1U, CurByte, OS); // POP HL + emitByte(0xadU, CurByte, OS); // XOR A, L + emitByte(0xe1U, CurByte, OS); // POP HL + break; + default: + report_fatal_instr_problem(MCII.getName(opcode), + "Allowed register are A, B, C, D, E, H, L."); + } + break; + case Z80::ADC16SP: + case Z80::ADC16aa: + case Z80::ADC16ao: + case Z80::JP16: + case Z80::JP16CC: + case Z80::JR: + case Z80::JRCC: + case Z80::LD16or: + case Z80::LD16pr: + case Z80::LD16ro: + case Z80::LD16rp: + report_fatal_instr_problem(MCII.getName(opcode), "Not implemented."); + break; + default: + std::string msg; + raw_string_ostream Msg(msg); + Msg << "Not supported instr: " << MCII.getName(opcode) << ' ' << MI; + report_fatal_error(Msg.str()); + } +} + +} // end of namespace llvm diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.h new file mode 100644 index 00000000000000..c2155d465b0442 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCCodeEmitter.h @@ -0,0 +1,57 @@ +//===-- Z80MCCodeEmitter.h - Convert Z80 Code to Machine Code -------------===// +// +// 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 defines the Z80MCCodeEmitter class. +// +//===----------------------------------------------------------------------===// +// + +#ifndef LLVM_Z80_CODE_EMITTER_H +#define LLVM_Z80_CODE_EMITTER_H + +#include "Z80FixupKinds.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/Support/DataTypes.h" + +#define GET_INSTRINFO_OPERAND_TYPES_ENUM +#include "Z80GenInstrInfo.inc" + +#include + +namespace llvm { + +class MCContext; +class MCFixup; +class MCInst; +class MCInstrInfo; +class MCSubtargetInfo; +class raw_ostream; + +/// Writes Z80 machine code to a stream. +class Z80MCCodeEmitter : public MCCodeEmitter { +public: + Z80MCCodeEmitter(const MCInstrInfo &MCII, MCContext &Ctx) : MCII(MCII) {} + +private: + void emitByte(uint8_t C, unsigned &CurByte, raw_ostream &OS) const; + + void encodeInstruction(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const override; + + Z80MCCodeEmitter(const Z80MCCodeEmitter &) = delete; + void operator=(const Z80MCCodeEmitter &) = delete; + + const MCInstrInfo &MCII; +}; + +} // namespace llvm + +#endif // LLVM_Z80_CODE_EMITTER_H diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp new file mode 100644 index 00000000000000..a39ac387db1715 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.cpp @@ -0,0 +1,166 @@ +//===-- Z80MCTargetDesc.cpp - Z80 Target Descriptions ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Z80 specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#include "Z80MCTargetDesc.h" +#include "EZ80InstPrinter.h" +#include "Z80AsmBackend.h" +#include "Z80ELFStreamer.h" +#include "Z80InstPrinter.h" +#include "Z80MCAsmInfo.h" +#include "Z80MCCodeEmitter.h" +#include "Z80TargetStreamer.h" +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Support/TargetRegistry.h" + +#include +#include + +using namespace llvm; + +#define GET_REGINFO_MC_DESC +#include "Z80GenRegisterInfo.inc" + +#define GET_INSTRINFO_MC_DESC +#include "Z80GenInstrInfo.inc" + +#define GET_SUBTARGETINFO_MC_DESC +#include "Z80GenSubtargetInfo.inc" + +std::string Z80_MC::ParseZ80Triple(const Triple &TT) { + std::string FS; + if (TT.getArch() == Triple::ez80) + FS = "+24bit-mode,-16bit-mode"; + else + FS = "-24bit-mode,+16bit-mode"; + + return FS; +} + +MCSubtargetInfo *Z80_MC::createZ80MCSubtargetInfo(const Triple &TT, + StringRef CPU, StringRef FS) { + std::string ArchFS = Z80_MC::ParseZ80Triple(TT); + if (!FS.empty()) { + if (!ArchFS.empty()) + ArchFS = (Twine(ArchFS) + "," + FS).str(); + else + ArchFS = FS.str(); + } + + return createZ80MCSubtargetInfoImpl(TT, CPU, ArchFS); +} + +static MCAsmInfo *createZ80MCAsmInfo(const MCRegisterInfo &MRI, + const Triple &TheTriple, + const MCTargetOptions &Options) { + return new Z80MCAsmInfo(TheTriple); +} + +MCInstrInfo *createZ80MCInstrInfo() { + MCInstrInfo *X = new MCInstrInfo(); + InitZ80MCInstrInfo(X); + + return X; +} + +static MCRegisterInfo *createZ80MCRegisterInfo(const Triple &TT) { + MCRegisterInfo *X = new MCRegisterInfo(); + InitZ80MCRegisterInfo(X, 0); + + return X; +} + +static MCInstPrinter *createZ80MCInstPrinter(const Triple &T, + unsigned SyntaxVariant, + const MCAsmInfo &MAI, + const MCInstrInfo &MII, + const MCRegisterInfo &MRI) { + if (SyntaxVariant == 0) + return new Z80InstPrinter(MAI, MII, MRI); + if (SyntaxVariant == 1) + return new Z80EInstPrinter(MAI, MII, MRI); + return nullptr; +} + +static MCTargetStreamer * +createZ80AsmTargetStreamer(MCStreamer &S, formatted_raw_ostream &OS, + MCInstPrinter * /*InstPrint*/, + bool /*isVerboseAsm*/) { + return new Z80TargetAsmStreamer(S, OS); +} + +static MCCodeEmitter *createZ80MCCodeEmitter(const MCInstrInfo &MCII, + const MCRegisterInfo &MRI, + MCContext &Ctx) { + return new Z80MCCodeEmitter(MCII, Ctx); +} + +static MCStreamer *createMCStreamer(const Triple &T, MCContext &Context, + std::unique_ptr &&MAB, + std::unique_ptr &&OW, + std::unique_ptr &&Emitter, + bool RelaxAll) { + return createELFStreamer(Context, std::move(MAB), std::move(OW), + std::move(Emitter), RelaxAll); +} + +static MCTargetStreamer * +createZ80ObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) { + return new Z80ELFStreamer(S, STI); +} + +// Force static initialization. +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80TargetMC() { + for (Target *T : {&getTheZ80Target(), &getTheEZ80Target()}) { + // Register the MC asm info. + RegisterMCAsmInfoFn X(*T, createZ80MCAsmInfo); + + // Register the MC instruction info. + TargetRegistry::RegisterMCInstrInfo(*T, createZ80MCInstrInfo); + + // Register the MC register info. + TargetRegistry::RegisterMCRegInfo(*T, createZ80MCRegisterInfo); + + // Register the MC subtarget info. + TargetRegistry::RegisterMCSubtargetInfo(*T, + Z80_MC::createZ80MCSubtargetInfo); + + // Register the MCInstPrinter. + TargetRegistry::RegisterMCInstPrinter(*T, createZ80MCInstPrinter); + + // Register the asm target streamer. + TargetRegistry::RegisterAsmTargetStreamer(*T, createZ80AsmTargetStreamer); + } + // Register the MC Code Emitter. + TargetRegistry::RegisterMCCodeEmitter(getTheZ80Target(), + createZ80MCCodeEmitter); + + // Register the obj streamer. + TargetRegistry::RegisterELFStreamer(getTheZ80Target(), createMCStreamer); + + // Register the obj target streamer. + TargetRegistry::RegisterObjectTargetStreamer(getTheZ80Target(), + createZ80ObjectTargetStreamer); + + // Register the asm backend (as little endian). + TargetRegistry::RegisterMCAsmBackend(getTheZ80Target(), createZ80AsmBackend); +} diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h new file mode 100644 index 00000000000000..2a425ace7d8bed --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80MCTargetDesc.h @@ -0,0 +1,79 @@ +//===-- Z80MCTargetDesc.h - Z80 Target Descriptions -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides Z80 specific target descriptions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCTARGETDESC_H +#define LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80MCTARGETDESC_H + +#include "llvm/Support/DataTypes.h" +#include +#include +#include + +namespace llvm { +class MCAsmBackend; +class MCCodeEmitter; +class MCContext; +class MCInstrInfo; +class MCObjectTargetWriter; +class MCObjectWriter; +class MCRegisterInfo; +class MCSubtargetInfo; +class MCTargetOptions; +class Target; +class Triple; +class StringRef; +class raw_pwrite_stream; + +Target &getTheZ80Target(); +Target &getTheEZ80Target(); + +namespace Z80_MC { +std::string ParseZ80Triple(const Triple &TT); + +/// Create a Z80 MCSubtargetInfo instance. This is exposed so Asm parser, etc. +/// do not need to go through TargetRegistry. +MCSubtargetInfo *createZ80MCSubtargetInfo(const Triple &TT, StringRef CPU, + StringRef FS); +} // namespace Z80_MC + +MCAsmBackend *createZ80AsmBackend(const Target &T, const MCSubtargetInfo &STI, + const MCRegisterInfo &MRI, + const llvm::MCTargetOptions &TO); + +MCAsmBackend *createEZ80AsmBackend(const Target &T, const MCRegisterInfo &MRI, + const Triple &TT, StringRef CPU, + const MCTargetOptions &Options); + +/// Construct a Z80 OMF object writer. +std::unique_ptr createZ80OMFObjectWriter(raw_pwrite_stream &OS); + +/// Construct a Z80 ELF object writer. +std::unique_ptr createZ80ELFObjectWriter(uint8_t OSABI); + +} // namespace llvm + +// Defines symbolic names for Z80 registers. This defines a mapping from +// register name to register number. +// +#define GET_REGINFO_ENUM +#include "Z80GenRegisterInfo.inc" + +// Defines symbolic names for the Z80 instructions. +// +#define GET_INSTRINFO_ENUM +#include "Z80GenInstrInfo.inc" + +#define GET_SUBTARGETINFO_ENUM +#include "Z80GenSubtargetInfo.inc" + +#endif diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp new file mode 100644 index 00000000000000..d86d2049952d59 --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.cpp @@ -0,0 +1,54 @@ +//===- Z80TargetStreamer.cpp - Z80TargetStreamer class --*- C++ -*---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Z80TargetStreamer class. +// +//===----------------------------------------------------------------------===// + +#include "Z80TargetStreamer.h" +#include "llvm/MC/MCContext.h" +#include "llvm/Support/FormattedStream.h" + +using namespace llvm; + +Z80TargetStreamer::Z80TargetStreamer(MCStreamer &S) + : MCTargetStreamer(S) {} + +Z80TargetAsmStreamer::Z80TargetAsmStreamer(MCStreamer &S, + formatted_raw_ostream &OS) + : Z80TargetStreamer(S), MAI(S.getContext().getAsmInfo()), OS(OS) {} + +void Z80TargetAsmStreamer::emitAlign(unsigned ByteAlignment) { + if (ByteAlignment > 1) + OS << "\trb\t" << (ByteAlignment - 1) << " - ($ - $$ + " + << (ByteAlignment - 1) << ") mod " << ByteAlignment << "\n"; +} + +void Z80TargetAsmStreamer::emitBlock(uint64_t NumBytes) { + if (NumBytes) + OS << "\trb\t" << NumBytes << '\n'; +} + +void Z80TargetAsmStreamer::emitLocal(MCSymbol *Symbol) { + OS << "\tprivate\t"; + Symbol->print(OS, MAI); + OS << '\n'; +} + +void Z80TargetAsmStreamer::emitGlobal(MCSymbol *Symbol) { + OS << "\tpublic\t"; + Symbol->print(OS, MAI); + OS << '\n'; +} + +void Z80TargetAsmStreamer::emitExtern(MCSymbol *Symbol) { + OS << "\textern\t"; + Symbol->print(OS, MAI); + OS << '\n'; +} diff --git a/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h new file mode 100644 index 00000000000000..f3b757593fef0d --- /dev/null +++ b/llvm/lib/Target/Z80/MCTargetDesc/Z80TargetStreamer.h @@ -0,0 +1,59 @@ +//==-- Z80TargetStreamer.h - Z80 Target Streamer -----------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file declares Z80-specific target streamer classes. +/// These are for implementing support for target-specific assembly directives. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80TARGETSTREAMER_H +#define LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80TARGETSTREAMER_H + +#include "llvm/MC/MCStreamer.h" + +namespace llvm { + +class Z80TargetStreamer : public MCTargetStreamer { +public: + explicit Z80TargetStreamer(MCStreamer &S); + + // .align + virtual void emitAlign(unsigned ByteAlignment) = 0; + + // .block + virtual void emitBlock(uint64_t NumBytes) = 0; + + // .private + virtual void emitLocal(MCSymbol *Symbol) = 0; + + // .global + virtual void emitGlobal(MCSymbol *Symbol) = 0; + + // .extern + virtual void emitExtern(MCSymbol *Symbol) = 0; +}; + +class Z80TargetAsmStreamer final : public Z80TargetStreamer { + const MCAsmInfo *MAI; + formatted_raw_ostream &OS; + +public: + Z80TargetAsmStreamer(MCStreamer &S, formatted_raw_ostream &OS); + + void emitAlign(unsigned ByteAlignment) override; + void emitBlock(uint64_t NumBytes) override; + void emitLocal(MCSymbol *Symbol) override; + void emitGlobal(MCSymbol *Symbol) override; + void emitExtern(MCSymbol *Symbol) override; +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_Z80_MCTARGETDESC_Z80TARGETSTREAMER_H diff --git a/llvm/lib/Target/Z80/TargetInfo/CMakeLists.txt b/llvm/lib/Target/Z80/TargetInfo/CMakeLists.txt new file mode 100644 index 00000000000000..f8c220752fb025 --- /dev/null +++ b/llvm/lib/Target/Z80/TargetInfo/CMakeLists.txt @@ -0,0 +1,3 @@ +add_llvm_component_library(LLVMZ80Info + Z80TargetInfo.cpp + ) diff --git a/llvm/lib/Target/Z80/TargetInfo/LLVMBuild.txt b/llvm/lib/Target/Z80/TargetInfo/LLVMBuild.txt new file mode 100644 index 00000000000000..3abdee8efa64fb --- /dev/null +++ b/llvm/lib/Target/Z80/TargetInfo/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./lib/Target/Z80/TargetInfo/LLVMBuild.txt ----------------*- Conf -*--===; +; +; 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 is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = Z80Info +parent = Z80 +required_libraries = Support +add_to_library_groups = Z80 diff --git a/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.cpp b/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.cpp new file mode 100644 index 00000000000000..38e4bf7d201b7a --- /dev/null +++ b/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.cpp @@ -0,0 +1,28 @@ +//===-- Z80TargetInfo.cpp - Z80 Target Implementation ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "TargetInfo/Z80TargetInfo.h" +#include "llvm/Support/TargetRegistry.h" +using namespace llvm; + +Target &llvm::getTheZ80Target() { + static Target TheZ80Target; + return TheZ80Target; +} +Target &llvm::getTheEZ80Target() { + static Target TheEZ80Target; + return TheEZ80Target; +} + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80TargetInfo() { + RegisterTarget X( + getTheZ80Target(), "z80", "Z80 [experimental]", "Z80"); + + RegisterTarget Y( + getTheEZ80Target(), "ez80", "eZ80 [experimental]", "EZ80"); +} diff --git a/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.h b/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.h new file mode 100644 index 00000000000000..6d3db110f460f1 --- /dev/null +++ b/llvm/lib/Target/Z80/TargetInfo/Z80TargetInfo.h @@ -0,0 +1,21 @@ +//===-- Z80TargetInfo.h - Z80 Target Implementation -------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_TARGETINFO_Z80TARGETINFO_H +#define LLVM_LIB_TARGET_Z80_TARGETINFO_Z80TARGETINFO_H + +namespace llvm { + +class Target; + +Target &getTheZ80Target(); +Target &getTheEZ80Target(); + +} + +#endif // LLVM_LIB_TARGET_Z80_TARGETINFO_Z80TARGETINFO_H diff --git a/llvm/lib/Target/Z80/Z80.h b/llvm/lib/Target/Z80/Z80.h new file mode 100644 index 00000000000000..de34b070071981 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80.h @@ -0,0 +1,40 @@ +//===-- Z80.h - Top-level interface for Z80 representation ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the entry points for global functions defined in the LLVM +// Z80 back-end. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80_H +#define LLVM_LIB_TARGET_Z80_Z80_H + +namespace llvm { + +class FunctionPass; +class InstructionSelector; +class PassRegistry; +class Z80RegisterBankInfo; +class Z80Subtarget; +class Z80TargetMachine; + +FunctionPass *createZ80PreLegalizeCombiner(bool IsOptNone); +InstructionSelector *createZ80InstructionSelector(const Z80TargetMachine &TM, + Z80Subtarget &, + Z80RegisterBankInfo &); +FunctionPass *createZ80PostSelectCombiner(); +FunctionPass *createZ80MachineLateOptimizationPass(); + +void initializeZ80PreLegalizerCombinerPass(PassRegistry &); +void initializeZ80PostSelectCombinerPass(PassRegistry &); +void initializeZ80MachineLateOptimizationPass(PassRegistry &); + +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/Z80.td b/llvm/lib/Target/Z80/Z80.td new file mode 100644 index 00000000000000..8e603e997c8169 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80.td @@ -0,0 +1,94 @@ +//===-- Z80.td - Target definition file for the Zilog Z80 --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This is a target description file for the Zilog z80 architecture, referred +// to here as the "Z80" architecture. +// +//===----------------------------------------------------------------------===// + +// Get the target-independent interfaces which we are implementing... +// +include "llvm/Target/Target.td" + +//===----------------------------------------------------------------------===// +// Z80 Subtarget features +//===----------------------------------------------------------------------===// + +def FeatureUndoc : SubtargetFeature<"undoc", "HasUndocOps", "true", + "Enable undocumented z80 instructions">; +def FeatureZ180 : SubtargetFeature<"z180", "HasZ180Ops", "true", + "Support z180 instructions">; +def FeatureEZ80 : SubtargetFeature<"ez80", "HasEZ80Ops", "true", + "Support ez80 instructions">; +def FeatureIdxHalf : SubtargetFeature<"idxhalf", "HasIdxHalfRegs", "true", + "Support index half registers">; + +//===----------------------------------------------------------------------===// +// Z80 Subtarget state +//===----------------------------------------------------------------------===// + +def Mode24Bit : SubtargetFeature<"24bit-mode", "In24BitMode", "true", + "24-bit mode (ez80)", [FeatureEZ80]>; +def Mode16Bit : SubtargetFeature<"16bit-mode", "In16BitMode", "true", + "16-bit mode (z80)">; + +//===----------------------------------------------------------------------===// +// Z80 processors supported +//===----------------------------------------------------------------------===// + +let CompleteModel = 0 in def GenericModel : SchedMachineModel; +class Proc Features> + : ProcessorModel; +def : Proc<"generic", []>; +def : Proc<"z80", [FeatureUndoc, FeatureIdxHalf]>; +def : Proc<"z180", [FeatureZ180]>; +def : Proc<"ez80", [FeatureZ180, FeatureEZ80, FeatureIdxHalf]>; + +//===----------------------------------------------------------------------===// +// Register File Description +//===----------------------------------------------------------------------===// + +include "Z80RegisterInfo.td" +include "Z80RegisterBanks.td" + +//===----------------------------------------------------------------------===// +// Instruction Descriptions +//===----------------------------------------------------------------------===// + +include "Z80InstrInfo.td" +include "Z80Combine.td" + +def Z80InstrInfo : InstrInfo; + +//===----------------------------------------------------------------------===// +// Calling Conventions +//===----------------------------------------------------------------------===// + +include "Z80CallingConv.td" + +//===----------------------------------------------------------------------===// +// Assembly writer +//===----------------------------------------------------------------------===// + +let ShouldEmitMatchRegisterName = 0 in { + def Z80AsmParser : AsmParser; + def EZ80AsmParser : AsmParser; +} +def Z80AsmWriter : AsmWriter; +def EZ80AsmWriter : AsmWriter { + string AsmWriterClassName = "EInstPrinter"; + let Variant = 1; +} + +def Z80 : Target { + // Information about the instructions... + let InstructionSet = Z80InstrInfo; + let AssemblyParsers = [Z80AsmParser, EZ80AsmParser]; + let AssemblyWriters = [Z80AsmWriter, EZ80AsmWriter]; +} diff --git a/llvm/lib/Target/Z80/Z80AsmPrinter.cpp b/llvm/lib/Target/Z80/Z80AsmPrinter.cpp new file mode 100644 index 00000000000000..d8c3c19239a7bd --- /dev/null +++ b/llvm/lib/Target/Z80/Z80AsmPrinter.cpp @@ -0,0 +1,106 @@ +//===-- Z80AsmPrinter.cpp - Convert Z80 LLVM code to AT&T assembly --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a printer that converts from our internal representation +// of machine-dependent LLVM code to Z80 machine code. +// +//===----------------------------------------------------------------------===// + +#include "Z80AsmPrinter.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "MCTargetDesc/Z80TargetStreamer.h" +#include "Z80.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetLoweringObjectFile.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +//===----------------------------------------------------------------------===// +// Target Registry Stuff +//===----------------------------------------------------------------------===// + +static bool isCode16(const Triple &TT) { + return TT.getEnvironment() == Triple::CODE16; +} + +void Z80AsmPrinter::emitStartOfAsmFile(Module &M) { + const Triple &TT = TM.getTargetTriple(); + if (TT.getArch() == Triple::ez80) + OutStreamer->emitAssemblerFlag(isCode16(TT) ? MCAF_Code16 : MCAF_Code24); +} + +void Z80AsmPrinter::emitInlineAsmEnd(const MCSubtargetInfo &StartInfo, + const MCSubtargetInfo *EndInfo) const { + bool Was16 = isCode16(StartInfo.getTargetTriple()); + if (!EndInfo || Was16 != isCode16(EndInfo->getTargetTriple())) + OutStreamer->emitAssemblerFlag(Was16 ? MCAF_Code16 : MCAF_Code24); +} + +void Z80AsmPrinter::emitEndOfAsmFile(Module &M) { + Z80TargetStreamer *TS = + static_cast(OutStreamer->getTargetStreamer()); + for (const auto &Symbol : OutContext.getSymbols()) + if (!Symbol.second->isDefined()) + TS->emitExtern(Symbol.second); +} + +void Z80AsmPrinter::emitGlobalVariable(const GlobalVariable *GV) { + Z80TargetStreamer *TS = + static_cast(OutStreamer->getTargetStreamer()); + + if (GV->hasInitializer()) { + // Check to see if this is a special global used by LLVM, if so, emit it. + if (emitSpecialLLVMGlobal(GV)) + return; + } + + MCSymbol *GVSym = getSymbol(GV); + + if (!GV->hasInitializer()) // External globals require no extra code. + return; + + GVSym->redefineIfPossible(); + if (GVSym->isDefined() || GVSym->isVariable()) + report_fatal_error("symbol '" + Twine(GVSym->getName()) + + "' is already defined"); + + SectionKind GVKind = TargetLoweringObjectFile::getKindForGlobal(GV, TM); + + const DataLayout &DL = GV->getParent()->getDataLayout(); + uint64_t Size = DL.getTypeAllocSize(GV->getType()->getElementType()); + + // If the alignment is specified, we *must* obey it. Overaligning a global + // with a specified alignment is a prompt way to break globals emitted to + // sections and expected to be contiguous (e.g. ObjC metadata). + unsigned Align = DL.getPreferredAlignment(GV); + + // Determine to which section this global should be emitted. + MCSection *TheSection = getObjFileLowering().SectionForGlobal(GV, GVKind, TM); + + OutStreamer->SwitchSection(TheSection); + TS->emitAlign(Align); + if (GV->hasLocalLinkage()) + TS->emitLocal(GVSym); + else + TS->emitGlobal(GVSym); + OutStreamer->emitLabel(GVSym); + if (GVKind.isBSS()) + TS->emitBlock(Size); + else + emitGlobalConstant(DL, GV->getInitializer()); + OutStreamer->AddBlankLine(); +} + +// Force static initialization. +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80AsmPrinter() { + RegisterAsmPrinter X(getTheZ80Target()); + RegisterAsmPrinter Y(getTheEZ80Target()); +} diff --git a/llvm/lib/Target/Z80/Z80AsmPrinter.h b/llvm/lib/Target/Z80/Z80AsmPrinter.h new file mode 100644 index 00000000000000..249f699aaabd73 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80AsmPrinter.h @@ -0,0 +1,42 @@ +//===-- Z80AsmPrinter.h - Z80 implementation of AsmPrinter ------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80ASMPRINTER_H +#define LLVM_LIB_TARGET_Z80_Z80ASMPRINTER_H + +#include "Z80Subtarget.h" +#include "llvm/CodeGen/AsmPrinter.h" + +namespace llvm { + +class LLVM_LIBRARY_VISIBILITY Z80AsmPrinter : public AsmPrinter { + const Z80Subtarget *Subtarget; + +public: + explicit Z80AsmPrinter(TargetMachine &TM, + std::unique_ptr Streamer) + : AsmPrinter(TM, std::move(Streamer)) {} + + StringRef getPassName() const override { + return "Z80 Assembly / Object Emitter"; + } + + const Z80Subtarget &getSubtarget() const { return *Subtarget; } + + + void emitStartOfAsmFile(Module &M) override; + void emitInlineAsmEnd(const MCSubtargetInfo &StartInfo, + const MCSubtargetInfo *EndInfo) const override; + void emitEndOfAsmFile(Module &M) override; + void emitGlobalVariable(const GlobalVariable *GV) override; + void emitInstruction(const MachineInstr *MI) override; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80CallingConv.cpp b/llvm/lib/Target/Z80/Z80CallingConv.cpp new file mode 100644 index 00000000000000..94fb3f19f2fb5c --- /dev/null +++ b/llvm/lib/Target/Z80/Z80CallingConv.cpp @@ -0,0 +1,19 @@ +//=== Z80CallingConv.cpp - Z80 Custom Calling Convention Impl -*- C++ -*-===// +// +// 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 contains the implementation of custom routines for the Z80 +// Calling Convention that aren't done by tablegen. +// +//===----------------------------------------------------------------------===// + +#include "Z80CallingConv.h" +#include "Z80Subtarget.h" +using namespace llvm; + +// Provides entry points of CC_Z80 and RetCC_Z80. +#include "Z80GenCallingConv.inc" diff --git a/llvm/lib/Target/Z80/Z80CallingConv.h b/llvm/lib/Target/Z80/Z80CallingConv.h new file mode 100644 index 00000000000000..9c3e7670045ff8 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80CallingConv.h @@ -0,0 +1,32 @@ +//=== Z80CallingConv.h - Z80 Custom Calling Convention Routines -*- C++ -*-===// +// +// 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 contains the custom routines for the Z80 Calling Convention that +// aren't done by tablegen. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80CALLINGCONV_H +#define LLVM_LIB_TARGET_Z80_Z80CALLINGCONV_H + +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/CallingConvLower.h" +#include "llvm/IR/CallingConv.h" + +namespace llvm { + +bool RetCC_Z80(unsigned ValNo, MVT ValVT, MVT LocVT, + CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, + CCState &State); + +bool CC_Z80(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, + ISD::ArgFlagsTy ArgFlags, CCState &State); + +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80CallingConv.td b/llvm/lib/Target/Z80/Z80CallingConv.td new file mode 100644 index 00000000000000..924ba8e8233fde --- /dev/null +++ b/llvm/lib/Target/Z80/Z80CallingConv.td @@ -0,0 +1,128 @@ +//===-- Z80CallingConv.td - Calling Conventions Z80/EZ80 ---*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This describes the calling conventions for the Z80 and EZ80 architectures. +// +//===----------------------------------------------------------------------===// + +/// CCIfSubtarget - Match if the current subtarget has a feature F. +class CCIfSubtarget + : CCIf<"State.getMachineFunction().getSubtarget()." # F, A>; + +//===----------------------------------------------------------------------===// +// Z80 Register Calling Conventions +//===----------------------------------------------------------------------===// + +def RetCC_Z80_Reg : CallingConv<[ +//CCCustom<"CC_Z80_HandleSplit">, + CCIfType<[ i24 ], CCIfSplit>>, +//CCIfType<[ i24 ], CCIfSplitEnd>>, + CCIfType<[ i24 ], CCAssignToRegWithShadow<[ UHL, UDE, UBC, UIY ], + [ UDE, UHL, UIY, UBC ]>>, + CCIfType<[ i16 ], CCIfSplit>>, +//CCIfType<[ i16 ], CCIfSplitEnd>>, + CCIfType<[ i16 ], CCAssignToRegWithShadow<[ HL, DE, BC, IY ], + [ DE, HL, IY, BC ]>>, + CCIfType<[ i8 ], CCIfSplitEnd>> +]>; +def CC_Z80_Reg : CallingConv<[ + CCIfType<[ i64, f64 ], CCIfSubtarget<"is16Bit()", CCPassIndirect>>, + CCIfType<[ i64, f64 ], CCIfSubtarget<"is24Bit()", CCPassIndirect>>, + CCDelegateTo +]>; + +//===----------------------------------------------------------------------===// +// Z80 Argument Calling Conventions +//===----------------------------------------------------------------------===// + +def CC_Z80_C : CallingConv<[ + CCIfByVal>, + CCIfType<[ i1, i8 ], CCPromoteToType>, + CCIfType<[ i16 ], CCAssignToStack<2, 1>>, +]>; +def CC_EZ80_C : CallingConv<[ + CCIfByVal>, + CCIfType<[ i1, i8, i16 ], CCPromoteToType>, + CCIfType<[ i24 ], CCAssignToStack<3, 1>>, +]>; + +def CC_Z80_LC : CallingConv<[ + CCDelegateTo, + CCIfType<[ i8 ], CCAssignToReg<[ L, C ]>> +]>; +def CC_Z80_LC_AB : CallingConv<[ + CCIfType<[ i8 ], CCAssignToReg<[ A, B ]>> +]>; +def CC_Z80_LC_AC : CallingConv<[ + CCIfType<[ i8 ], CCAssignToReg<[ A, C ]>> +]>; +def CC_Z80_LC_BC : CallingConv<[ + CCIfType<[ i8 ], CCAssignToReg<[ B, C ]>> +]>; +def CC_Z80_LC_L : CallingConv<[ + CCIfType<[ i64, f64 ], CCIfSubtarget<"is16Bit()", CCPassIndirect>>, + CCIfType<[ i64, f64 ], CCIfSubtarget<"is24Bit()", CCPassIndirect>>, + CCIfType<[ i24 ], CCIfSplit>>, + CCIfType<[ i24 ], CCAssignToRegWithShadow<[ UBC, UIY, UHL, UDE ], + [ UIY, UBC, UDE, UHL ]>>, + CCIfType<[ i16 ], CCIfSplit>>, + CCIfType<[ i16 ], CCAssignToRegWithShadow<[ BC, IY, HL, DE ], + [ IY, BC, DE, HL ]>>, + CCIfType<[ i8 ], CCAssignToReg<[ C, A, L, E ]>> +]>; +def CC_Z80_LC_F : CallingConv<[ + CCDelegateTo, + CCIfType<[ i8 ], CCAssignToReg<[ F ]>> +]>; + +let Entry = 1 in +def CC_Z80 : CallingConv<[ + CCIfCC<"CallingConv::Z80_LibCall", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_AB", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_AC", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_BC", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_L", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_F", CCDelegateTo>, + CCIfSubtarget<"is16Bit()", CCDelegateTo>, + CCIfSubtarget<"is24Bit()", CCDelegateTo> +]>; + +//===----------------------------------------------------------------------===// +// Z80 Return Calling Conventions +//===----------------------------------------------------------------------===// +def RetCC_Z80_C : CallingConv<[ + CCIfType<[ i16 ], CCAssignToReg<[ HL, DE, BC, IY ]>>, + CCIfType<[ i8 ], CCAssignToReg<[ A, L, H, E, D, C, B, IYL, IYH ]>> +]>; +def RetCC_EZ80_C : CallingConv<[ + CCDelegateTo, + CCIfType<[ i8 ], CCAssignToReg<[ A ]>> +]>; + +// This is the return-value convention used for the entire Z80 backend. +let Entry = 1 in +def RetCC_Z80 : CallingConv<[ + CCIfType<[ i1 ], CCPromoteToType>, + CCIfCC<"CallingConv::Z80_LibCall_F", CCIfType<[ i8 ], CCAssignToReg<[ F ]>>>, + CCIfSubtarget<"is16Bit()", CCDelegateTo>, + CCIfCC<"CallingConv::Z80_LibCall_L", CCDelegateTo>, + CCIfSubtarget<"is24Bit()", CCDelegateTo> +]>; + +//===----------------------------------------------------------------------===// +// Callee-saved Registers. +//===----------------------------------------------------------------------===// + +def CSR_NoRegs : CalleeSavedRegs<(add)>; +def CSR_Z80_C : CalleeSavedRegs<(add IX)>; +def CSR_EZ80_C : CalleeSavedRegs<(add UIX)>; +def CSR_Z80_AllRegs : CalleeSavedRegs<(add R16, A)>; +def CSR_EZ80_AllRegs : CalleeSavedRegs<(add R24, A)>; +def CSR_Z80_TIFlags : CalleeSavedRegs<(add I16)>; +def CSR_EZ80_TIFlags : CalleeSavedRegs<(add I24)>; diff --git a/llvm/lib/Target/Z80/Z80Combine.td b/llvm/lib/Target/Z80/Z80Combine.td new file mode 100644 index 00000000000000..d6a3e426cb6211 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80Combine.td @@ -0,0 +1,22 @@ +//=- Z80.td - Define Z80 Combine Rules -----------------------*- tablegen -*-=// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +include "llvm/Target/GlobalISel/Combine.td" + +def Z80PreLegalizerCombinerHelper + : GICombinerHelper<"Z80GenPreLegalizerCombinerHelper", [ + trivial_combines, elide_br_by_inverting_cond, ptr_add_immed_chain, + ptr_add_global_immed, ptr_add_const_immed, sext_to_zext, + or_to_add, funnel_shift, combine_identity, split_conditions, + flip_condition, lower_is_power_of_two, sink_const, undef_combines + ]> { + let DisableRuleOption = "z80prelegalizercombiner-disable-rule"; +} diff --git a/llvm/lib/Target/Z80/Z80FrameLowering.cpp b/llvm/lib/Target/Z80/Z80FrameLowering.cpp new file mode 100644 index 00000000000000..cf608b7ea22393 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80FrameLowering.cpp @@ -0,0 +1,452 @@ +//===-- Z80FrameLowering.cpp - Z80 Frame Information ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the z80 implementation of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#include "Z80FrameLowering.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80InstrInfo.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80Subtarget.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/Target/TargetMachine.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-frame-lowering" + +Z80FrameLowering::Z80FrameLowering(const Z80Subtarget &STI) + : TargetFrameLowering(StackGrowsDown, Align(), STI.is24Bit() ? -3 : -2), + STI(STI), TII(*STI.getInstrInfo()), TRI(STI.getRegisterInfo()), + Is24Bit(STI.is24Bit()), SlotSize(Is24Bit ? 3 : 2) {} + +/// hasFP - Return true if the specified function should have a dedicated frame +/// pointer register. This is true if the function has variable sized allocas +/// or if frame pointer elimination is disabled. +bool Z80FrameLowering::hasFP(const MachineFunction &MF) const { + return MF.getTarget().Options.DisableFramePointerElim(MF) || + MF.getFrameInfo().hasStackObjects(); +} +bool Z80FrameLowering::isFPSaved(const MachineFunction &MF) const { + return hasFP(MF) && MF.getInfo()->getUsesAltFP() == + Z80MachineFunctionInfo::AFPM_None; +} +bool Z80FrameLowering::hasReservedCallFrame(const MachineFunction &MF) const { + return false; // call frames are not implemented yet +} +bool Z80FrameLowering::needsFrameIndexResolution( + const MachineFunction &MF) const { + return TargetFrameLowering::needsFrameIndexResolution(MF) || + !hasReservedCallFrame(MF); +} + +Z80FrameLowering::StackAdjustmentMethod +Z80FrameLowering::getOptimalStackAdjustmentMethod(MachineFunction &MF, + int Offset, int FPOffset, + bool ScratchIsIndex, + bool UnknownOffset) const { + if (!Offset) + return SAM_None; + + if (MF.getFunction().hasOptNone()) + return SAM_Large; + + bool OptSize = MF.getFunction().hasOptSize(); + bool HasEZ80Ops = STI.hasEZ80Ops(); + + // Optimal for small offsets + // POP/PUSH for every SlotSize bytes + unsigned PopPushCount = std::abs(Offset) / SlotSize; + unsigned PopPushCost = OptSize ? 1 : HasEZ80Ops ? 4 : Offset > 0 ? 10 : 11; + if (ScratchIsIndex) + PopPushCost += OptSize || HasEZ80Ops ? 1 : 4; + // INC/DEC SP for remaining bytes + unsigned IncDecCount = std::abs(Offset) % SlotSize; + unsigned IncDecCost = OptSize || HasEZ80Ops ? 1 : 6; + + StackAdjustmentMethod BestMethod = PopPushCount ? SAM_Small : SAM_Tiny; + unsigned BestCost = PopPushCount * PopPushCost + IncDecCount * IncDecCost; + + if (isFPSaved(MF)) { + if (UnknownOffset || (FPOffset >= 0 && FPOffset == Offset)) { + // Optimal if we are trying to set SP = FP, except for tiny Offset + unsigned AllCost = 0; + // LD SP, FP + AllCost += OptSize || HasEZ80Ops ? 2 : 10; + + return AllCost <= BestCost ? SAM_All : BestMethod; + } + + if (HasEZ80Ops && FPOffset >= 0 && isInt<8>(Offset - FPOffset)) { + // Optimal for medium offsets + unsigned MediumCost = 0; + // LEA , FP - Offset - FPOffset + MediumCost += 3; + // LD SP, + MediumCost += ScratchIsIndex ? 2 : 1; + + if (MediumCost < BestCost) { + BestMethod = SAM_Medium; + BestCost = MediumCost; + } + } + } + + // Optimal for large offsets + unsigned LargeCost = 0; + // LD , Offset + LargeCost += OptSize || HasEZ80Ops ? 1 + SlotSize : 10; + // ADD , SP + LargeCost += OptSize || HasEZ80Ops ? 1 : 11; + // LD SP, + LargeCost += OptSize || HasEZ80Ops ? 1 : 6; + if (ScratchIsIndex) + LargeCost += OptSize || HasEZ80Ops ? 3 : 12; + + return LargeCost < BestCost ? SAM_Large : BestMethod; +} + +void Z80FrameLowering::BuildStackAdjustment(MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + DebugLoc DL, Register ScratchReg, + int Offset, int FPOffset, + bool UnknownOffset) const { + Register FrameReg = TRI->getFrameRegister(MF), ResultReg; + bool ScratchIsIndex = Z80::I24RegClass.contains(ScratchReg) || + Z80::I16RegClass.contains(ScratchReg); + switch (getOptimalStackAdjustmentMethod(MF, Offset, FPOffset, ScratchIsIndex, + UnknownOffset)) { + case SAM_None: + // Nothing to do + return; + case SAM_Small: + for (unsigned PopPushCount = std::abs(Offset) / SlotSize; PopPushCount; + --PopPushCount) + BuildMI(MBB, MI, DL, + TII.get(Offset >= 0 ? (Is24Bit ? Z80::POP24r : Z80::POP16r) + : (Is24Bit ? Z80::PUSH24r : Z80::PUSH16r))) + .addReg(ScratchReg, getDefRegState(Offset >= 0) | + getDeadRegState(Offset >= 0) | + getUndefRegState(Offset < 0)); + LLVM_FALLTHROUGH; + case SAM_Tiny: + for (unsigned IncDecCount = std::abs(Offset) % SlotSize; IncDecCount; + --IncDecCount) + BuildMI(MBB, MI, DL, + TII.get(Offset >= 0 ? (Is24Bit ? Z80::INC24SP : Z80::INC16SP) + : (Is24Bit ? Z80::DEC24SP : Z80::DEC16SP))); + return; + case SAM_All: + // Just store FP to SP + ResultReg = FrameReg; + break; + case SAM_Medium: + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LEA24ro : Z80::LEA16ro), + ScratchReg).addUse(FrameReg).addImm(Offset - FPOffset); + ResultReg = ScratchReg; + break; + case SAM_Large: + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), + ScratchReg).addImm(Offset); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::ADD24SP : Z80::ADD16SP), + ScratchReg).addUse(ScratchReg)->addRegisterDead(Z80::F, TRI); + ResultReg = ScratchReg; + break; + } + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24SP : Z80::LD16SP)) + .addUse(ResultReg, RegState::Kill); +} + +/// emitPrologue - Push callee-saved registers onto the stack, which +/// automatically adjust the stack pointer. Adjust the stack pointer to allocate +/// space for local variables. +void Z80FrameLowering::emitPrologue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MI = MBB.begin(); + + // Debug location must be unknown since the first debug location is used + // to determine the end of the prologue. + DebugLoc DL; + + MachineFrameInfo &MFI = MF.getFrameInfo(); + int StackSize = -int(MFI.getStackSize()); + MCRegister ScratchReg = Is24Bit ? Z80::UHL : Z80::HL; + + // skip callee-saved saves + while (MI != MBB.end() && MI->getFlag(MachineInstr::FrameSetup)) + ++MI; + + int FPOffset = -1; + if (hasFP(MF)) { + if (MF.getFunction().hasOptSize()) { + if (StackSize) { + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), + ScratchReg).addImm(StackSize); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::CALL24 : Z80::CALL16)) + .addExternalSymbol("_frameset") + .addUse(ScratchReg, RegState::ImplicitKill); + return; + } + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::CALL24 : Z80::CALL16)) + .addExternalSymbol("_frameset0"); + return; + } + Register FrameReg = TRI->getFrameRegister(MF); + if (isFPSaved(MF)) + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addUse(FrameReg); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), FrameReg) + .addImm(0); + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::ADD24SP : Z80::ADD16SP), + FrameReg).addUse(FrameReg)->addRegisterDead(Z80::F, TRI); + FPOffset = 0; + } + BuildStackAdjustment(MF, MBB, MI, DL, ScratchReg, StackSize, FPOffset); +} + +void Z80FrameLowering::emitEpilogue(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MachineBasicBlock::iterator MI = MBB.getFirstTerminator(); + DebugLoc DL = MBB.findDebugLoc(MI); + + MachineFrameInfo &MFI = MF.getFrameInfo(); + int StackSize = int(MFI.getStackSize()); + + const TargetRegisterClass *ScratchRC = Is24Bit ? &Z80::A24RegClass + : &Z80::A16RegClass; + TargetRegisterClass::iterator ScratchReg = ScratchRC->begin(); + for (; MI->readsRegister(TRI->getSubReg(*ScratchReg, Z80::sub_low), TRI); + ++ScratchReg) + assert(ScratchReg != ScratchRC->end() && + "Could not allocate a scratch register!"); + bool HasFP = hasFP(MF); + assert((HasFP || *ScratchReg != TRI->getFrameRegister(MF)) && + "Cannot allocate csr as scratch register!"); + + // skip callee-saved restores + while (MI != MBB.begin()) + if (!(--MI)->getFlag(MachineInstr::FrameDestroy)) { + ++MI; + break; + } + + // consume stack adjustment + while (MI != MBB.begin()) { + MachineBasicBlock::iterator PI = std::prev(MI); + unsigned Opc = PI->getOpcode(); + if ((Opc == Z80::POP24r || Opc == Z80::POP16r) && + PI->getOperand(0).isDead()) { + StackSize += SlotSize; + } else if (Opc == Z80::LD24SP || Opc == Z80::LD16SP) { + bool Is24Bit = Opc == Z80::LD24SP; + unsigned Reg = PI->getOperand(0).getReg(); + if (PI == MBB.begin()) + break; + MachineBasicBlock::iterator AI = std::prev(PI); + Opc = AI->getOpcode(); + if (AI == MBB.begin() || Opc != (Is24Bit ? Z80::ADD24SP : Z80::ADD16SP) || + AI->getOperand(0).getReg() != Reg || + AI->getOperand(1).getReg() != Reg) + break; + MachineBasicBlock::iterator LI = std::prev(AI); + Opc = LI->getOpcode(); + if (Opc != (Is24Bit ? Z80::LD24ri : Z80::LD16ri) || + LI->getOperand(0).getReg() != Reg) + break; + StackSize += LI->getOperand(1).getImm(); + LI->removeFromParent(); + AI->removeFromParent(); + } else + break; + PI->removeFromParent(); + } + + BuildStackAdjustment(MF, MBB, MI, DL, *ScratchReg, StackSize, + HasFP ? StackSize : -1, MFI.hasVarSizedObjects()); + if (isFPSaved(MF)) + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), + TRI->getFrameRegister(MF)); +} + +// Only non-nested non-nmi interrupts can use shadow registers. +static bool shouldUseShadow(const MachineFunction &MF) { + const Function &F = MF.getFunction(); + return F.getFnAttribute("interrupt").getValueAsString() == "Generic"; +} + +void Z80FrameLowering::shadowCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, DebugLoc DL, + MachineInstr::MIFlag Flag, const std::vector &CSI) const { + assert(shouldUseShadow(*MBB.getParent()) && + "Can't use shadow registers in this function."); + bool SaveAF = false, SaveG = false; + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + unsigned Reg = CSI[i].getReg(); + if (Reg == Z80::AF) + SaveAF = true; + else if (Z80::G24RegClass.contains(Reg) || + Z80::G16RegClass.contains(Reg)) + SaveG = true; + } + if (SaveAF) + BuildMI(MBB, MI, DL, TII.get(Z80::EXAF)) + .setMIFlag(Flag); + if (SaveG) + BuildMI(MBB, MI, DL, TII.get(Z80::EXX)) + .setMIFlag(Flag); +} + +static Z80MachineFunctionInfo::AltFPMode +shouldUseAltFP(MachineFunction &MF, MCRegister AltFPReg, + const TargetRegisterInfo *TRI) { + if (MF.getFunction().hasOptSize() || + MF.getFrameInfo().hasVarSizedObjects() || + MF.getTarget().Options.DisableFramePointerElim(MF)) + return Z80MachineFunctionInfo::AFPM_None; + if (!MF.getRegInfo().isPhysRegUsed(Z80::UIY)) + return Z80MachineFunctionInfo::AFPM_Full; + MachineBasicBlock::iterator LastFrameIdx; + bool AltFPModified = false; + for (const MachineBasicBlock &MBB : MF) { + for (const MachineInstr &MI : MBB) { + if (AltFPModified) { + for (const MachineOperand &MO : MI.operands()) + if (MO.isFI() || (MO.isRegMask() && MO.clobbersPhysReg(AltFPReg))) + return Z80MachineFunctionInfo::AFPM_None; + } else if (MI.modifiesRegister(AltFPReg, TRI)) + AltFPModified = true; + } + AltFPModified = true; + } + return Z80MachineFunctionInfo::AFPM_Partial; +} + +bool Z80FrameLowering::assignCalleeSavedSpillSlots( + MachineFunction &MF, const TargetRegisterInfo *TRI, + std::vector &CSI) const { + auto &FuncInfo = *MF.getInfo(); + FuncInfo.setUsesAltFP(shouldUseAltFP(MF, Is24Bit ? Z80::UIY : Z80::IY, TRI)); + MF.getRegInfo().freezeReservedRegs(MF); + FuncInfo.setCalleeSavedFrameSize((CSI.size() + isFPSaved(MF)) * SlotSize); + return true; +} + +bool Z80FrameLowering::spillCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + ArrayRef CSI, const TargetRegisterInfo *TRI) const { + const MachineFunction &MF = *MBB.getParent(); + const MachineRegisterInfo &MRI = MF.getRegInfo(); + bool UseShadow = shouldUseShadow(MF); + DebugLoc DL = MBB.findDebugLoc(MI); + if (UseShadow) + shadowCalleeSavedRegisters(MBB, MI, DL, MachineInstr::FrameSetup, CSI); + for (unsigned i = CSI.size(); i != 0; --i) { + unsigned Reg = CSI[i - 1].getReg(); + + // Non-index registers can be spilled to shadow registers. + if (UseShadow && !Z80::I24RegClass.contains(Reg) && + !Z80::I16RegClass.contains(Reg)) + continue; + + bool isLiveIn = MRI.isLiveIn(Reg); + if (!isLiveIn) + MBB.addLiveIn(Reg); + + // Decide whether we can add a kill flag to the use. + bool CanKill = !isLiveIn; + // Check if any subregister is live-in + if (CanKill) { + for (MCRegAliasIterator AReg(Reg, TRI, false); AReg.isValid(); ++AReg) { + if (MRI.isLiveIn(*AReg)) { + CanKill = false; + break; + } + } + } + + // Do not set a kill flag on values that are also marked as live-in. This + // happens with the @llvm-returnaddress intrinsic and with arguments + // passed in callee saved registers. + // Omitting the kill flags is conservatively correct even if the live-in + // is not used after all. + MachineInstrBuilder MIB; + if (Reg == Z80::AF) + MIB = BuildMI(MBB, MI, DL, + TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + else + MIB = BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addUse(Reg, getKillRegState(CanKill)); + MIB.setMIFlag(MachineInstr::FrameSetup); + } + return true; +} +bool Z80FrameLowering::restoreCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + MutableArrayRef CSI, const TargetRegisterInfo *TRI) const { + const MachineFunction &MF = *MBB.getParent(); + bool UseShadow = shouldUseShadow(MF); + DebugLoc DL = MBB.findDebugLoc(MI); + for (unsigned i = 0, e = CSI.size(); i != e; ++i) { + unsigned Reg = CSI[i].getReg(); + + // Non-index registers can be spilled to shadow registers. + if (UseShadow && !Z80::I24RegClass.contains(Reg) && + !Z80::I16RegClass.contains(Reg)) + continue; + + MachineInstrBuilder MIB; + if (Reg == Z80::AF) + MIB = BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::POP24AF + : Z80::POP16AF)); + else + MIB = BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), + Reg); + MIB.setMIFlag(MachineInstr::FrameDestroy); + } + if (UseShadow) + shadowCalleeSavedRegisters(MBB, MI, DL, MachineInstr::FrameDestroy, CSI); + return true; +} + +void Z80FrameLowering::processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS) const { + MachineFrameInfo &MFI = MF.getFrameInfo(); + MFI.setMaxCallFrameSize(0); // call frames are not implemented yet + if (MF.getInfo()->getHasIllegalLEA() || + MFI.estimateStackSize(MF) > 0x80 - 2) { + int64_t MinFixedObjOffset = -int64_t(SlotSize); + for (int I = MFI.getObjectIndexBegin(); I < 0; ++I) + MinFixedObjOffset = std::min(MinFixedObjOffset, MFI.getObjectOffset(I)); + int FI = MFI.CreateFixedSpillStackObject( + SlotSize, MinFixedObjOffset - SlotSize * (1 + isFPSaved(MF))); + RS->addScavengingFrameIndex(FI); + } +} + +MachineBasicBlock::iterator Z80FrameLowering::eliminateCallFramePseudoInstr( + MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + if (!hasReservedCallFrame(MF)) { + if (int Amount = TII.getFrameAdjustment(*I)) { + Register ScratchReg = I->getOperand(I->getNumOperands() - 1).getReg(); + assert(ScratchReg.isPhysical() && + "Reg alloc should have already happened."); + BuildStackAdjustment(MF, MBB, I, I->getDebugLoc(), ScratchReg, Amount); + } + } + + return MBB.erase(I); +} diff --git a/llvm/lib/Target/Z80/Z80FrameLowering.h b/llvm/lib/Target/Z80/Z80FrameLowering.h new file mode 100644 index 00000000000000..da3d98d623f053 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80FrameLowering.h @@ -0,0 +1,93 @@ +//===-- Z80TargetFrameLowering.h - Define frame lowering for Z80 -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This class implements z80-specific bits of TargetFrameLowering class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80FRAMELOWERING_H +#define LLVM_LIB_TARGET_Z80_Z80FRAMELOWERING_H + +#include "llvm/CodeGen/TargetFrameLowering.h" + +namespace llvm { +class Z80Subtarget; +class Z80InstrInfo; +class Z80RegisterInfo; + +class Z80FrameLowering : public TargetFrameLowering { + const Z80Subtarget &STI; + const Z80InstrInfo &TII; + const Z80RegisterInfo *TRI; + + bool Is24Bit; + unsigned SlotSize; + +public: + explicit Z80FrameLowering(const Z80Subtarget &STI); + + /// Z80 has no alignment requirements. + bool targetHandlesStackFrameRounding() const override { return true; } + + /// emitProlog/emitEpilog - These methods insert prolog and epilog code into + /// the function. + void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; + + bool + assignCalleeSavedSpillSlots(MachineFunction &MF, + const TargetRegisterInfo *TRI, + std::vector &CSI) const override; + bool spillCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + ArrayRef CSI, + const TargetRegisterInfo *TRI) const override; + bool + restoreCalleeSavedRegisters(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + MutableArrayRef CSI, + const TargetRegisterInfo *TRI) const override; + + void processFunctionBeforeFrameFinalized( + MachineFunction &MF, RegScavenger *RS = nullptr) const override; + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const override; + + bool hasFP(const MachineFunction &MF) const override; + bool isFPSaved(const MachineFunction &MF) const; + bool hasReservedCallFrame(const MachineFunction &MF) const override; + bool needsFrameIndexResolution(const MachineFunction &MF) const override; + + enum StackAdjustmentMethod { + SAM_None, + SAM_Tiny, + SAM_All, + SAM_Small, + SAM_Medium, + SAM_Large + }; + StackAdjustmentMethod getOptimalStackAdjustmentMethod( + MachineFunction &MF, int Offset, int FPOffset = -1, + bool ScratchIsIndex = false, bool UnknownOffset = false) const; + +private: + void BuildStackAdjustment(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, DebugLoc DL, + Register ScratchReg, int Offset, int FPOffset = -1, + bool UnknownOffset = false) const; + + void shadowCalleeSavedRegisters( + MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, DebugLoc DL, + MachineInstr::MIFlag Flag, const std::vector &CSI) const; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80GenRegisterBankInfo.def b/llvm/lib/Target/Z80/Z80GenRegisterBankInfo.def new file mode 100644 index 00000000000000..bfb17dec8eb7b6 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80GenRegisterBankInfo.def @@ -0,0 +1,72 @@ +//===- Z80GenRegisterBankInfo.def --------------------------------*- C++ -*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines all the static objects used by Z80RegisterBankInfo. +/// \todo This should be generated by TableGen. +//===----------------------------------------------------------------------===// + +#ifdef GET_TARGET_REGBANK_INFO_IMPL +RegisterBankInfo::PartialMapping Z80GenRegisterBankInfo::PartMappings[]{ + /* StartIdx, Length, RegBank */ + // GPR value + {0, 8, Z80::GPRRegBank}, // :0 + {0, 16, Z80::GPRRegBank}, // :1 + {0, 24, Z80::GPRRegBank}, // :2 +}; +#endif // GET_TARGET_REGBANK_INFO_IMPL + +#ifdef GET_TARGET_REGBANK_INFO_CLASS +enum PartialMappingIdx { + PMI_None = -1, + PMI_GPR8, + PMI_GPR16, + PMI_GPR24 +}; +#endif // GET_TARGET_REGBANK_INFO_CLASS + +#ifdef GET_TARGET_REGBANK_INFO_IMPL +#define INSTR_3OP(INFO) INFO, INFO, INFO, +#define BREAKDOWN(INDEX, NUM) \ + { &Z80GenRegisterBankInfo::PartMappings[INDEX], NUM } +// ValueMappings. +RegisterBankInfo::ValueMapping Z80GenRegisterBankInfo::ValMappings[]{ + /* BreakDown, NumBreakDowns */ + // 3-operands instructions (all binary operations should end up with one of + // those mapping). + INSTR_3OP(BREAKDOWN(PMI_GPR8, 1)) // 0: GPR_8 + INSTR_3OP(BREAKDOWN(PMI_GPR16, 1)) // 3: GPR_16 + INSTR_3OP(BREAKDOWN(PMI_GPR24, 1)) // 6: GPR_32 +}; +#undef INSTR_3OP +#undef BREAKDOWN +#endif // GET_TARGET_REGBANK_INFO_IMPL + +#ifdef GET_TARGET_REGBANK_INFO_CLASS +enum ValueMappingIdx { + VMI_None = -1, + VMI_3OpsGpr8Idx = PMI_GPR8 * 3, + VMI_3OpsGpr16Idx = PMI_GPR16 * 3, + VMI_3OpsGpr24Idx = PMI_GPR24 * 3, +}; +#undef GET_TARGET_REGBANK_INFO_CLASS +#endif // GET_TARGET_REGBANK_INFO_CLASS + +#ifdef GET_TARGET_REGBANK_INFO_IMPL +#undef GET_TARGET_REGBANK_INFO_IMPL +const RegisterBankInfo::ValueMapping * +Z80GenRegisterBankInfo::getValueMapping(PartialMappingIdx Idx, + unsigned NumOperands) { + // We can use VMI_3Ops Mapping for all the cases. + if (NumOperands <= 3 && (Idx >= PMI_GPR8 && Idx <= PMI_GPR24)) + return &ValMappings[(unsigned)Idx * 3]; + + llvm_unreachable("Unsupported PartialMappingIdx."); +} + +#endif // GET_TARGET_REGBANK_INFO_IMPL + diff --git a/llvm/lib/Target/Z80/Z80ISelLowering.cpp b/llvm/lib/Target/Z80/Z80ISelLowering.cpp new file mode 100644 index 00000000000000..05263a60e08609 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80ISelLowering.cpp @@ -0,0 +1,535 @@ +//===-- Z80ISelLowering.cpp - Z80 DAG Lowering Implementation -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that Z80 uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#include "Z80ISelLowering.h" +#include "Z80TargetMachine.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/MachineJumpTableInfo.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-isel" + +void Z80TargetLowering::setLibcall(RTLIB::Libcall Call, const char *Name, + CallingConv::ID CC) { + setLibcallName(Call, Name); + setLibcallCallingConv(Call, CC); +} + +Z80TargetLowering::Z80TargetLowering(const Z80TargetMachine &TM, + const Z80Subtarget &STI) + : TargetLowering(TM), Subtarget(STI) { + bool Is24Bit = Subtarget.is24Bit(); + + setSchedulingPreference(Sched::RegPressure); + + // Set up the register classes. + addRegisterClass(MVT::i8, &Z80::R8RegClass); + addRegisterClass(MVT::i16, &Z80::R16RegClass); + if (Is24Bit) + addRegisterClass(MVT::i24, &Z80::R24RegClass); + + setStackPointerRegisterToSaveRestore(Is24Bit ? Z80::SPL : Z80::SPS); + + // Compute derived properties from the register classes + computeRegisterProperties(STI.getRegisterInfo()); + + setLibcall(RTLIB::ZEXT_I16_I24, "_stoiu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SEXT_I16_I24, "_stoi", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SEXT_I24_I32, "_itol", CallingConv::Z80_LibCall ); + + setLibcall(RTLIB::NOT_I16, "_snot", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NOT_I24, "_inot", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NOT_I32, "_lnot", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NOT_I64, "_llnot", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::AND_I16, "_sand", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::AND_I24, "_iand", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::AND_I32, "_land", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::AND_I64, "_lland", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::OR_I16, "_sor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::OR_I24, "_ior", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::OR_I32, "_lor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::OR_I64, "_llor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::XOR_I16, "_sxor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::XOR_I24, "_ixor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::XOR_I32, "_lxor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::XOR_I64, "_llxor", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SHL_I8, "_bshl", CallingConv::Z80_LibCall_AB); + setLibcall(RTLIB::SHL_I16, "_sshl", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SHL_I16_I8, "_sshl_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SHL_I24, "_ishl", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SHL_I24_I8, "_ishl_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SHL_I32, "_lshl", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SHL_I64, "_llshl", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRA_I8, "_bshrs", CallingConv::Z80_LibCall_AB); + setLibcall(RTLIB::SRA_I16, "_sshrs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRA_I16_I8, "_sshrs_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SRA_I24, "_ishrs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRA_I24_I8, "_ishrs_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SRA_I32, "_lshrs", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SRA_I64, "_llshrs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRL_I8, "_bshru", CallingConv::Z80_LibCall_AB); + setLibcall(RTLIB::SRL_I16, "_sshru", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRL_I16_I8, "_sshru_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SRL_I24, "_ishru", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SRL_I24_I8, "_ishru_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SRL_I32, "_lshru", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SRL_I64, "_llshru", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::CMP_I32, "_lcmpu", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I64, "_llcmpu", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I16_0, "_scmpzero", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I24_0, "_icmpzero", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I32_0, "_lcmpzero", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::CMP_I64_0, "_llcmpzero",CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::SCMP, "_setflag", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::NEG_I16, "_sneg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NEG_I24, "_ineg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NEG_I32, "_lneg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NEG_I64, "_llneg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::ADD_I32, "_ladd", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::ADD_I32_I8, "_ladd_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::ADD_I64, "_lladd", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SUB_I32, "_lsub", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SUB_I64, "_llsub", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_I8, "_bmulu", CallingConv::Z80_LibCall_BC); + setLibcall(RTLIB::MUL_I16, "_smulu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_I24, "_imulu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_I24_I8, "_imul_b", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::MUL_I32, "_lmulu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_I64, "_llmulu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SDIV_I8, "_bdivs", CallingConv::Z80_LibCall_BC); + setLibcall(RTLIB::SDIV_I16, "_sdivs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SDIV_I24, "_idivs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SDIV_I32, "_ldivs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SDIV_I64, "_lldivs", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIV_I8, "_bdivu", CallingConv::Z80_LibCall_BC); + setLibcall(RTLIB::UDIV_I16, "_sdivu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIV_I24, "_idivu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIV_I32, "_ldivu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIV_I64, "_lldivu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SREM_I8, "_brems", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::SREM_I16, "_srems", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SREM_I24, "_irems", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SREM_I32, "_lrems", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SREM_I64, "_llrems", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UREM_I8, "_bremu", CallingConv::Z80_LibCall_AC); + setLibcall(RTLIB::UREM_I16, "_sremu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UREM_I24, "_iremu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UREM_I32, "_lremu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UREM_I64, "_llremu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIVREM_I24, "_idvrmu", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UDIVREM_I32, "_ldvrmu", CallingConv::Z80_LibCall ); + + setLibcall(RTLIB::POPCNT_I8, "_bpopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::POPCNT_I16, "_spopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::POPCNT_I24, "_ipopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::POPCNT_I32, "_lpopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::POPCNT_I64, "_llpopcnt", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I8, "_bbitrev", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I16, "_sbitrev", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I24, "_ibitrev", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I32, "_lbitrev", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::BITREV_I64, "_llbitrev", CallingConv::Z80_LibCall ); + + setLibcall(RTLIB::ADD_F32, "_fadd", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SUB_F32, "_fsub", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::MUL_F32, "_fmul", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::DIV_F32, "_fdiv", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::REM_F32, "_frem", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::NEG_F32, "_fneg", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::CMP_F32, "_fcmp", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::FPTOSINT_F32_I32, "_ftol", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::FPTOUINT_F32_I32, "_ftol", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::SINTTOFP_I32_F32, "_ltof", CallingConv::Z80_LibCall_L ); + setLibcall(RTLIB::UINTTOFP_I32_F32, "_ultof", CallingConv::Z80_LibCall_L ); + + setLibcall(RTLIB::ADD_F64, "_dadd", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SUB_F64, "_dsub", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::MUL_F64, "_dmul", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::DIV_F64, "_ddiv", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::REM_F64, "_drem", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::NEG_F64, "_dneg", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::CMP_F64, "_dcmp", CallingConv::Z80_LibCall_F ); + setLibcall(RTLIB::FPEXT_F32_F64, "_ftod", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPROUND_F64_F32, "_dtof", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPTOSINT_F64_I32, "_dtol", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPTOUINT_F64_I32, "_dtoul", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SINTTOFP_I32_F64, "_ltod", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UINTTOFP_I32_F64, "_ultod", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPTOSINT_F64_I64, "_dtoll", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::FPTOUINT_F64_I64, "_dtoll", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::SINTTOFP_I64_F64, "_lltod", CallingConv::Z80_LibCall ); + setLibcall(RTLIB::UINTTOFP_I64_F64, "_ulltod", CallingConv::Z80_LibCall ); +} + +unsigned Z80TargetLowering::getJumpTableEncoding() const { + return MachineJumpTableInfo::EK_BlockAddress; +} + +/// Return true if the target has native support for the specified value type +/// and it is 'desirable' to use the type for the given node type. e.g. On ez80 +/// i16 is legal, but undesirable since i16 instruction encodings are longer and +/// slower. +bool Z80TargetLowering::isTypeDesirableForOp(unsigned Opc, EVT VT) const { + if (!isTypeLegal(VT)) + return false; + if (Subtarget.is16Bit()) + return true; + + switch (Opc) { + default: + case ISD::SIGN_EXTEND: + case ISD::ZERO_EXTEND: + case ISD::ANY_EXTEND: + return true; + case ISD::LOAD: + case ISD::STORE: + case ISD::ADD: + case ISD::SUB: + return VT != MVT::i16; + case ISD::MUL: + case ISD::AND: + case ISD::OR: + case ISD::XOR: + case ISD::SHL: + case ISD::SRA: + case ISD::SRL: + return VT != MVT::i24; + } +} + +/// Return true if x op y -> (SrcVT)((DstVT)x op (DstVT)y) is beneficial. +bool Z80TargetLowering::isDesirableToShrinkOp(unsigned Opc, EVT SrcVT, + EVT DstVT) const { + if (!isTypeLegal(DstVT)) + return false; + switch (Opc) { + default: + return false; + case ISD::ADD: + case ISD::SUB: + case ISD::ADDC: + case ISD::SUBC: + case ISD::ADDE: + case ISD::SUBE: + // These require a .sis suffix for i24 -> i16 + return DstVT != MVT::i16 || Subtarget.is16Bit(); + case ISD::MUL: + case ISD::AND: + case ISD::OR: + case ISD::XOR: + case ISD::SHL: + case ISD::SRA: + case ISD::SRL: + case ISD::ROTL: + case ISD::ROTR: + // These are more expensive on larger types, so always shrink. + return true; + } +} + +/// This method query the target whether it is beneficial for dag combiner to +/// promote the specified node. If true, it should return the desired promotion +/// type by reference. +bool Z80TargetLowering::IsDesirableToPromoteOp(SDValue Op, EVT &PVT) const { + if (isDesirableToShrinkOp(Op.getOpcode(), MVT::i24, Op.getValueType())) + return false; + PVT = MVT::i24; + return true; +} + +MachineBasicBlock * +Z80TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const { + switch (MI.getOpcode()) { + default: llvm_unreachable("Unexpected instr type to insert"); + /*case Z80::Sub016: + case Z80::Sub024: + return EmitLoweredSub0(MI, BB); + case Z80::Sub16: + case Z80::Sub24: + return EmitLoweredSub(MI, BB); + case Z80::Cp16a0: + case Z80::Cp24a0: + return EmitLoweredCmp0(MI, BB); + case Z80::Cp16ao: + case Z80::Cp24ao: + return EmitLoweredCmp(MI, BB);*/ + case Z80::SetCC: + case Z80::Select8: + case Z80::Select16: + case Z80::Select24: + return EmitLoweredSelect(MI, BB); + case Z80::SExt8: + case Z80::SExt16: + case Z80::SExt24: + return EmitLoweredSExt(MI, BB); + case Z80::LDR16: + case Z80::LDR24: + return EmitLoweredMemMove(MI, BB); + } +} + +void Z80TargetLowering::AdjustInstrPostInstrSelection(MachineInstr &MI, + SDNode *Node) const { + switch (MI.getOpcode()) { + default: llvm_unreachable("Unexpected instr type to insert"); + case Z80::ADJCALLSTACKUP16: + case Z80::ADJCALLSTACKUP24: + case Z80::ADJCALLSTACKDOWN16: + case Z80::ADJCALLSTACKDOWN24: + return AdjustAdjCallStack(MI); + } +} + +void Z80TargetLowering::AdjustAdjCallStack(MachineInstr &MI) const { + bool Is24Bit = MI.getOpcode() == Z80::ADJCALLSTACKUP24 || + MI.getOpcode() == Z80::ADJCALLSTACKDOWN24; + assert((Is24Bit || MI.getOpcode() == Z80::ADJCALLSTACKUP16 || + MI.getOpcode() == Z80::ADJCALLSTACKDOWN16) && + "Unexpected opcode"); + MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo(); + unsigned Reg = MRI.createVirtualRegister(Is24Bit ? &Z80::A24RegClass + : &Z80::A16RegClass); + MachineInstrBuilder(*MI.getParent()->getParent(), MI) + .addReg(Reg, RegState::ImplicitDefine | RegState::Dead); + LLVM_DEBUG(MI.dump()); +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredSub(MachineInstr &MI, + MachineBasicBlock *BB) const { + bool Is24Bit = MI.getOpcode() == Z80::SUB24ao; + assert((Is24Bit || MI.getOpcode() == Z80::SUB16ao) && "Unexpected opcode"); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + LLVM_DEBUG(BB->dump()); + BuildMI(*BB, MI, DL, TII->get(Z80::RCF)); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::SBC24ao : Z80::SBC16ao)) + .addReg(MI.getOperand(0).getReg()); + MI.eraseFromParent(); + LLVM_DEBUG(BB->dump()); + return BB; +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredCmp(MachineInstr &MI, + MachineBasicBlock *BB) const { + bool Is24Bit = MI.getOpcode() == Z80::CP24ao; + assert((Is24Bit || MI.getOpcode() == Z80::CP16ao) && "Unexpected opcode"); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + BuildMI(*BB, MI, DL, TII->get(Z80::RCF)); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::SBC24ao : Z80::SBC16ao)) + .addReg(MI.getOperand(0).getReg()); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), + Is24Bit ? Z80::UHL : Z80::HL).addReg(Is24Bit ? Z80::UHL : Z80::HL) + .addReg(MI.getOperand(0).getReg()); + MI.eraseFromParent(); + LLVM_DEBUG(BB->dump()); + return BB; +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredCmp0(MachineInstr &MI, + MachineBasicBlock *BB) const { + bool Is24Bit = MI.getOpcode() == Z80::CP24a0; + assert((Is24Bit || MI.getOpcode() == Z80::CP16a0) && "Unexpected opcode"); + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), + Is24Bit ? Z80::UHL : Z80::HL).addReg(Is24Bit ? Z80::UHL : Z80::HL) + .addReg(MI.getOperand(0).getReg()); + BuildMI(*BB, MI, DL, TII->get(Z80::RCF)); + BuildMI(*BB, MI, DL, TII->get(Is24Bit ? Z80::SBC24ao : Z80::SBC16ao)) + .addReg(MI.getOperand(0).getReg()); + MI.eraseFromParent(); + LLVM_DEBUG(BB->dump()); + return BB; +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredSelect(MachineInstr &MI, + MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + // To "insert" a SELECT_CC instruction, we actually have to insert the + // diamond control-flow pattern. The incoming instruction knows the + // destination vreg to set, the condition code register to branch on, the + // true/false values to select between, and a branch opcode to use. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator I = ++BB->getIterator(); + + // thisMBB: + // ... + // %FalseVal = ... + // cmpTY ccX, r1, r2 + // bCC copy1MBB + // fallthrough --> copy0MBB + MachineBasicBlock *thisMBB = BB; + MachineFunction *F = BB->getParent(); + MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *copy1MBB = F->CreateMachineBasicBlock(LLVM_BB); + F->insert(I, copy0MBB); + F->insert(I, copy1MBB); + + // Update machine-CFG edges by transferring all successors of the current + // block to the new block which will contain the Phi node for the select. + copy1MBB->splice(copy1MBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + copy1MBB->transferSuccessorsAndUpdatePHIs(BB); + // Next, add the true and fallthrough blocks as its successors. + BB->addSuccessor(copy0MBB); + BB->addSuccessor(copy1MBB); + + BuildMI(BB, DL, TII->get(Z80::JQCC)).addMBB(copy1MBB) + .addImm(MI.getOperand(3).getImm()); + + // copy0MBB: + // %TrueVal = ... + // # fallthrough to copy1MBB + BB = copy0MBB; + + // Update machine-CFG edges + BB->addSuccessor(copy1MBB); + + // copy1MBB: + // %Result = phi [ %FalseValue, copy0MBB ], [ %TrueValue, thisMBB ] + // ... + BB = copy1MBB; + BuildMI(*BB, BB->begin(), DL, TII->get(Z80::PHI), + MI.getOperand(0).getReg()) + .addReg(MI.getOperand(1).getReg()).addMBB(thisMBB) + .addReg(MI.getOperand(2).getReg()).addMBB(copy0MBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + LLVM_DEBUG(F->dump()); + return BB; +} + +MachineBasicBlock *Z80TargetLowering::EmitLoweredSExt( + MachineInstr &MI, MachineBasicBlock *BB) const { + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + unsigned Opc, Reg; + switch (MI.getOpcode()) { + default: llvm_unreachable("Unexpected opcode"); + case Z80::SExt8: + Opc = Z80::SBC8ar; + Reg = Z80::A; + break; + case Z80::SExt16: + Opc = Z80::SBC16aa; + Reg = Z80::HL; + break; + case Z80::SExt24: + Opc = Z80::SBC24aa; + Reg = Z80::UHL; + break; + } + MachineInstrBuilder MIB = BuildMI(*BB, MI, DL, TII->get(Opc)); + MIB->findRegisterUseOperand(Reg)->setIsUndef(); + if (Reg == Z80::A) + MIB.addReg(Reg, RegState::Undef); + MI.eraseFromParent(); + return BB; +} + +MachineBasicBlock * +Z80TargetLowering::EmitLoweredMemMove(MachineInstr &MI, + MachineBasicBlock *BB) const { + bool Is24Bit = MI.getOpcode() == Z80::LDR24; + Register DE = Is24Bit ? Z80::UDE : Z80::DE; + Register HL = Is24Bit ? Z80::UHL : Z80::HL; + Register BC = Is24Bit ? Z80::UBC : Z80::BC; + Register PhysRegs[] = { DE, HL, BC }; + assert((Is24Bit || MI.getOpcode() == Z80::LDR16) && "Unexpected opcode"); + + const TargetInstrInfo *TII = Subtarget.getInstrInfo(); + DebugLoc DL = MI.getDebugLoc(); + + // A memmove needs to choose between a forward and backwards copy. + const BasicBlock *LLVM_BB = BB->getBasicBlock(); + MachineFunction::iterator I = ++BB->getIterator(); + + MachineFunction *F = BB->getParent(); + MachineBasicBlock *LDIR_BB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *LDDR_BB = F->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *NextBB = F->CreateMachineBasicBlock(LLVM_BB); + for (auto *MBB : {LDIR_BB, LDDR_BB}) { + F->insert(I, MBB); + for (auto LiveIn : {BC, DE, HL}) + MBB->addLiveIn(LiveIn); + } + F->insert(I, NextBB); + + // Update machine-CFG edges by transferring all successors of the current + // block to the new block. + NextBB->splice(NextBB->begin(), BB, + std::next(MachineBasicBlock::iterator(MI)), BB->end()); + NextBB->transferSuccessorsAndUpdatePHIs(BB); + + // BB: + // JP C,LDDR_BB + // fallthrough --> LDIR_BB + BuildMI(BB, DL, TII->get(Z80::JQCC)).addMBB(LDDR_BB) + .addImm(Z80::COND_C); + // Next, add the LDIR and LDDR blocks as its successors. + BB->addSuccessor(LDIR_BB); + BB->addSuccessor(LDDR_BB); + + // LDIR_BB: + // LDIR + // JP NextBB + for (int I = 0; I != 3; ++I) + BuildMI(LDIR_BB, DL, TII->get(Z80::COPY), PhysRegs[I]) + .add(MI.getOperand(I)); + BuildMI(LDIR_BB, DL, TII->get(Is24Bit ? Z80::LDIR24 : Z80::LDIR16)); + BuildMI(LDIR_BB, DL, TII->get(Z80::JQ)).addMBB(NextBB); + // Update machine-CFG edges + LDIR_BB->addSuccessor(NextBB); + + // LDDR_BB: + // ADD HL,BC + // DEC HL + // EX DE,HL + // ADD HL,BC + // DEC HL + // EX DE,HL + // LDDR + // # Fallthrough to Next MBB + for (int I = 0; I != 3; ++I) + BuildMI(LDDR_BB, DL, TII->get(Z80::COPY), PhysRegs[I]) + .add(MI.getOperand(I)); + for (int I = 0; I != 2; ++I) { + BuildMI(LDDR_BB, DL, TII->get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), HL) + .addUse(HL).addUse(BC); + BuildMI(LDDR_BB, DL, TII->get(Is24Bit ? Z80::DEC24r : Z80::DEC16r), HL) + .addUse(HL); + BuildMI(LDDR_BB, DL, TII->get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)); + } + BuildMI(LDDR_BB, DL, TII->get(Is24Bit ? Z80::LDDR24 : Z80::LDDR16)); + LDDR_BB->addSuccessor(NextBB); + + MI.eraseFromParent(); // The pseudo instruction is gone now. + LLVM_DEBUG(F->dump()); + return NextBB; +} + +/// HandleByVal - Target-specific cleanup for ByVal support. +void Z80TargetLowering::HandleByVal(CCState *State, unsigned &Size, + Align Alignment) const { + // Round up to a multiple of the stack slot size. + Size = alignTo(Size, Subtarget.is24Bit() ? 3 : 2); +} diff --git a/llvm/lib/Target/Z80/Z80ISelLowering.h b/llvm/lib/Target/Z80/Z80ISelLowering.h new file mode 100644 index 00000000000000..80a098224f88ab --- /dev/null +++ b/llvm/lib/Target/Z80/Z80ISelLowering.h @@ -0,0 +1,129 @@ +//===-- Z80ISelLowering.h - Z80 DAG Lowering Interface ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interfaces that Z80 uses to lower LLVM code into a +// selection DAG. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80ISELLOWERING_H +#define LLVM_LIB_TARGET_Z80_Z80ISELLOWERING_H + +#include "llvm/CodeGen/TargetLowering.h" + +namespace llvm { +class Z80Subtarget; +class Z80TargetMachine; + +namespace Z80ISD { +// Z80 Specific DAG Nodes +enum NodeType : unsigned { + // Start the numbering where the builtin ops leave off. + FIRST_NUMBER = ISD::BUILTIN_OP_END, + + /// A wrapper node for TargetConstantPool, TargetExternalSymbol, and + /// TargetGlobalAddress. + Wrapper, + + /// Shift/Rotate + RLC, RRC, RL, RR, SLA, SRA, SRL, + + /// Arithmetic operation with flags results. + INC, DEC, ADD, ADC, SUB, SBC, AND, XOR, OR, + + /// Z80 compare and test + CP, TST, + + MLT, + + /// This produces an all zeros/ones value from an input carry (SBC r,r). + SEXT, + + /// This operation represents an abstract Z80 call instruction, which + /// includes a bunch of information. + CALL, + + /// Return with a flag operand. Operand 0 is the chain operand, operand + /// 1 is the number of bytes of stack to pop. + RET_FLAG, + + /// Return from interrupt. + RETN_FLAG, RETI_FLAG, + + /// Tail call return. + TC_RETURN, + + /// BRCOND - Z80 conditional branch. The first operand is the chain, the + /// second is the block to branch to if the condition is true, the third is + /// the condition, and the fourth is the flag operand. + BRCOND, + + /// SELECT - Z80 select - This selects between a true value and a false + /// value (ops #1 and #2) based on the condition in op #0 and flag in op #3. + SELECT, + + /// Stack operations + POP = ISD::FIRST_TARGET_MEMORY_OPCODE, PUSH +}; +} // end Z80ISD namespace + +//===----------------------------------------------------------------------===// +// Z80 Implementation of the TargetLowering interface +class Z80TargetLowering final : public TargetLowering { + /// Keep a reference to the Z80Subtarget around so that we can + /// make the right decision when generating code for different targets. + const Z80Subtarget &Subtarget; + + void setLibcall(RTLIB::Libcall Call, const char *Name, CallingConv::ID CC); + +public: + Z80TargetLowering(const Z80TargetMachine &TM, const Z80Subtarget &STI); + +private: + unsigned getJumpTableEncoding() const override; + + /// Return true if the target has native support for + /// the specified value type and it is 'desirable' to use the type for the + /// given node type. e.g. On ez80 i16 is legal, but undesirable since i16 + /// instruction encodings are longer and slower. + bool isTypeDesirableForOp(unsigned Opc, EVT VT) const override; + + /// Return true if x op y -> (SrcVT)((DstVT)x op (DstVT)y) is beneficial. + bool isDesirableToShrinkOp(unsigned Opc, EVT SrcVT, EVT DstVT) const; + + bool IsDesirableToPromoteOp(SDValue Op, EVT &PVT) const override; + + MachineBasicBlock * + EmitInstrWithCustomInserter(MachineInstr &MI, + MachineBasicBlock *BB) const override; + void AdjustInstrPostInstrSelection(MachineInstr &MI, + SDNode *Node) const override; + + void AdjustAdjCallStack(MachineInstr &MI) const; + MachineBasicBlock *EmitLoweredSub0(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredSub(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredCmp0(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredCmp(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredSelect(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredSExt(MachineInstr &MI, + MachineBasicBlock *BB) const; + MachineBasicBlock *EmitLoweredMemMove(MachineInstr &MI, + MachineBasicBlock *BB) const; + + /// HandleByVal - Target-specific cleanup for ByVal support. + void HandleByVal(CCState *, unsigned &, Align) const override; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80InstrFormats.td b/llvm/lib/Target/Z80/Z80InstrFormats.td new file mode 100644 index 00000000000000..d48426e34af227 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80InstrFormats.td @@ -0,0 +1,174 @@ +//===-- Z80InstrFormats.td - Z80 Instruction Formats -------*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Z80 Instruction Format Definitions. +// + +class Mode val> { + bits<2> Value = val; +} +def AnyMode : Mode<0>; +def CurMode : Mode<1>; +def Z80Mode : Mode<2>; +def EZ80Mode : Mode<3>; + +class Prefix val> { + bits<4> Value = val; +} +def NoPre : Prefix<0>; +def CBPre : Prefix<1>; +def DDPre : Prefix<2>; +def DDCBPre : Prefix<3>; +def EDPre : Prefix<4>; +def FDPre : Prefix<5>; +def FDCBPre : Prefix<6>; +def IdxPre : Prefix<7>; +def IdxCBPre : Prefix<8>; +foreach i = 0-7 in +def Idx#i#Pre : Prefix; + +class ImmType val> { + bits<2> Value = val; +} +def NoImm : ImmType<0>; +def Imm : ImmType<1>; +def Off : ImmType<2>; +def OffImm : ImmType<3>; + +class Z80Inst opcode, ImmType immediate, + dag outputs = (outs), dag inputs = (ins), list pattern = [], + string asm = "", string constraints = ""> + : Instruction { + let Namespace = "Z80"; + + bits<8> Opcode = opcode; + bit hasImm = immediate.Value{0}; + bit hasOff = immediate.Value{1}; + + let OutOperandList = outputs; + let InOperandList = inputs; + let Pattern = pattern; + let AsmString = asm; + let Constraints = constraints; + + let TSFlags{3-0} = prefix.Value; + let TSFlags{5-4} = mode.Value; + let TSFlags{7-6} = immediate.Value; + let TSFlags{15-8} = opcode; + + let isCodeGenOnly = 1; +} + +let isPseudo = 1 in +class Pseudo pattern = []> + : Z80Inst< AnyMode, NoPre, 0, NoImm, outs, ins, pattern, + !strconcat(mnem, args), con>; + +class Inst opcode, ImmType immediate, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Z80Inst< AnyMode, prefix, opcode, immediate, outputs, inputs, pattern, + !strconcat(mnemonic, arguments), constraints>; +class Inst8 opcode, ImmType immediate, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Z80Inst< CurMode, prefix, opcode, immediate, outputs, inputs, pattern, + !strconcat(mnemonic, arguments), constraints>; +class Inst16 opcode, ImmType immediate, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Z80Inst< Z80Mode, prefix, opcode, immediate, outputs, inputs, pattern, + !strconcat(mnemonic, "{|.sis}", arguments), constraints>; +class Inst24 opcode, ImmType immediate, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Z80Inst, + Requires<[HaveEZ80Ops]>; + +class I opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst ; +class Ii opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst ; +class Io opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst ; +class Ioi opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst ; + +class I8i opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst8 ; +class I8oi opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst8 ; + +class I16 opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst16; +class I16i opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst16; +class I16o opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst16; +class I16oi opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst16; + +class I24 opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst24; +class I24i opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst24; +class I24o opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst24; +class I24oi opcode, + string mnemonic, string arguments = "", string constraints = "", + dag outputs = (outs), dag inputs = (ins), list pattern = []> + : Inst24; + +class P pattern = []> + : Z80Inst { + let isPseudo = 1; +} diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.cpp b/llvm/lib/Target/Z80/Z80InstrInfo.cpp new file mode 100644 index 00000000000000..ba8a248117ba8d --- /dev/null +++ b/llvm/lib/Target/Z80/Z80InstrInfo.cpp @@ -0,0 +1,1578 @@ +//===-- Z80InstrInfo.cpp - Z80 Instruction Information --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Z80 implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#include "Z80InstrInfo.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80Subtarget.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/MC/MCExpr.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/Debug.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-instr-info" + +#define GET_INSTRINFO_CTOR_DTOR +#include "Z80GenInstrInfo.inc" + +// Pin the vtable to this file. +void Z80InstrInfo::anchor() {} + +Z80InstrInfo::Z80InstrInfo(Z80Subtarget &STI) + : Z80GenInstrInfo((STI.is24Bit() ? Z80::ADJCALLSTACKDOWN24 + : Z80::ADJCALLSTACKDOWN16), + (STI.is24Bit() ? Z80::ADJCALLSTACKUP24 + : Z80::ADJCALLSTACKUP16)), + Subtarget(STI), RI(STI.getTargetTriple()) { +} + +int Z80InstrInfo::getSPAdjust(const MachineInstr &MI) const { + switch (MI.getOpcode()) { + case Z80::POP16r: + case Z80::POP16AF: + return 2; + case Z80::POP24r: + case Z80::POP24AF: + return 3; + case Z80::PUSH16r: + case Z80::PUSH16AF: + case Z80::PEA16o: + return -2; + case Z80::PUSH24r: + case Z80::PUSH24AF: + case Z80::PEA24o: + return -3; + } + return TargetInstrInfo::getSPAdjust(MI); +} + +static bool isIndex(const MachineOperand &MO, const MCRegisterInfo &RI) { + if (MO.isFI()) + return true; + if (MO.isReg()) + for (MCRegister IndexReg : Z80::I24RegClass) + if (RI.isSubRegisterEq(IndexReg, MO.getReg())) + return true; + return false; +} + +static bool hasIndex(const MachineInstr &MI, const MCRegisterInfo &RI) { + for (const MachineOperand &Op : MI.explicit_operands()) + if (isIndex(Op, RI)) + return true; + return false; +} + +unsigned Z80InstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { + auto TSFlags = MI.getDesc().TSFlags; + // 1 byte for opcode + unsigned Size = 1; + // 1 byte if we need a suffix + switch (TSFlags & Z80II::ModeMask) { + case Z80II::AnyMode: + case Z80II::CurMode: + break; + case Z80II::Z80Mode: + Size += Subtarget.is24Bit(); + break; + case Z80II::EZ80Mode: + Size += Subtarget.is16Bit(); + break; + } + // prefix byte(s) + unsigned Prefix = TSFlags & Z80II::PrefixMask; + if (TSFlags & Z80II::IndexedIndexPrefix) + Size += isIndex(MI.getOperand(Prefix >> Z80II::PrefixShift), + getRegisterInfo()); + else + switch (Prefix) { + case Z80II::NoPrefix: + break; + case Z80II::CBPrefix: + case Z80II::DDPrefix: + case Z80II::EDPrefix: + case Z80II::FDPrefix: + Size += 1; + break; + case Z80II::DDCBPrefix: + case Z80II::FDCBPrefix: + Size += 2; + break; + case Z80II::AnyIndexPrefix: + Size += hasIndex(MI, getRegisterInfo()); + break; + } + // immediate byte(s) + if (TSFlags & Z80II::HasImm) + switch (TSFlags & Z80II::ModeMask) { + case Z80II::AnyMode: + Size += 1; + break; + case Z80II::CurMode: + Size += Subtarget.is24Bit() ? 3 : 2; + break; + case Z80II::Z80Mode: + Size += 2; + break; + case Z80II::EZ80Mode: + Size += 3; + break; + } + // 1 byte if we need an offset + if (TSFlags & Z80II::HasOff) + Size += 1; + return Size; +} + +Z80::CondCode Z80::GetBranchConditionForPredicate(CmpInst::Predicate Pred, + bool &IsSigned, + bool &IsSwapped, + bool &IsConst) { + IsSigned = CmpInst::isSigned(Pred); + IsSwapped = IsConst = false; + switch (Pred) { + case CmpInst::FCMP_FALSE: + case CmpInst::FCMP_UNO: + IsConst = true; + return Z80::COND_C; + case CmpInst::FCMP_TRUE: + case CmpInst::FCMP_ORD: + IsConst = true; + return Z80::COND_NC; + case CmpInst::FCMP_OEQ: + case CmpInst::FCMP_UEQ: + case CmpInst::ICMP_EQ: + return Z80::COND_Z; + case CmpInst::FCMP_ONE: + case CmpInst::FCMP_UNE: + case CmpInst::ICMP_NE: + return Z80::COND_NZ; + case CmpInst::ICMP_UGT: + IsSwapped = true; + LLVM_FALLTHROUGH; + case CmpInst::ICMP_ULT: + return Z80::COND_C; + case CmpInst::ICMP_ULE: + IsSwapped = true; + LLVM_FALLTHROUGH; + case CmpInst::ICMP_UGE: + return Z80::COND_NC; + case CmpInst::FCMP_OGT: + case CmpInst::FCMP_UGT: + case CmpInst::ICMP_SGT: + IsSwapped = true; + LLVM_FALLTHROUGH; + case CmpInst::FCMP_OLT: + case CmpInst::FCMP_ULT: + case CmpInst::ICMP_SLT: + return Z80::COND_M; + case CmpInst::FCMP_OLE: + case CmpInst::FCMP_ULE: + case CmpInst::ICMP_SLE: + IsSwapped = true; + LLVM_FALLTHROUGH; + case CmpInst::FCMP_OGE: + case CmpInst::FCMP_UGE: + case CmpInst::ICMP_SGE: + return Z80::COND_P; + default: + llvm_unreachable("Unknown predicate"); + } +} + +/// Return the inverse of the specified condition, +/// e.g. turning COND_E to COND_NE. +Z80::CondCode Z80::GetOppositeBranchCondition(Z80::CondCode CC) { + return Z80::CondCode(CC ^ 1); +} + +bool Z80InstrInfo::analyzeBranch(MachineBasicBlock &MBB, + MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const { + // Start from the bottom of the block and work up, examining the + // terminator instructions. + MachineBasicBlock::iterator I = MBB.end(), UnCondBrIter = I; + while (I != MBB.begin()) { + --I; + if (I->isDebugValue()) + continue; + + // Working from the bottom, when we see a non-terminator instruction, we're + // done. + if (!isUnpredicatedTerminator(*I)) + break; + + // A terminator that isn't a branch can't easily be handled by this + // analysis. + if (!I->isBranch()) + return true; + + // Cannot handle branches that don't branch to a block. + if (!I->getOperand(0).isMBB()) + return true; + + // Handle unconditional branches. + if (I->getNumOperands() == 1) { + UnCondBrIter = I; + + if (!AllowModify) { + TBB = I->getOperand(0).getMBB(); + continue; + } + + // If the block has any instructions after a JMP, delete them. + while (std::next(I) != MBB.end()) + std::next(I)->eraseFromParent(); + Cond.clear(); + FBB = nullptr; + + // Delete the JMP if it's equivalent to a fall-through. + if (MBB.isLayoutSuccessor(I->getOperand(0).getMBB())) { + TBB = nullptr; + I->eraseFromParent(); + I = MBB.end(); + UnCondBrIter = I; + continue; + } + + // TBB is used to indicate the unconditional destination. + TBB = I->getOperand(0).getMBB(); + continue; + } + + // Handle conditional branches. + assert(I->getNumExplicitOperands() == 2 && "Invalid conditional branch"); + Z80::CondCode BranchCode = Z80::CondCode(I->getOperand(1).getImm()); + + // Working from the bottom, handle the first conditional branch. + if (Cond.empty()) { + MachineBasicBlock *TargetBB = I->getOperand(0).getMBB(); + if (AllowModify && UnCondBrIter != MBB.end() && + MBB.isLayoutSuccessor(TargetBB)) { + // If we can modify the code and it ends in something like: + // + // jCC L1 + // jmp L2 + // L1: + // ... + // L2: + // + // Then we can change this to: + // + // jnCC L2 + // L1: + // ... + // L2: + // + // Which is a bit more efficient. + // We conditionally jump to the fall-through block. + BranchCode = GetOppositeBranchCondition(BranchCode); + MachineBasicBlock::iterator OldInst = I; + + BuildMI(MBB, UnCondBrIter, MBB.findDebugLoc(I), get(Z80::JQCC)) + .addMBB(UnCondBrIter->getOperand(0).getMBB()).addImm(BranchCode); + BuildMI(MBB, UnCondBrIter, MBB.findDebugLoc(I), get(Z80::JQ)) + .addMBB(TargetBB); + + OldInst->eraseFromParent(); + UnCondBrIter->eraseFromParent(); + + // Restart the analysis. + UnCondBrIter = MBB.end(); + I = MBB.end(); + continue; + } + + FBB = TBB; + TBB = I->getOperand(0).getMBB(); + Cond.push_back(MachineOperand::CreateImm(BranchCode)); + continue; + } + + return true; + } + + return false; +} + +unsigned Z80InstrInfo::removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved) const { + assert(!BytesRemoved && "code size not handled"); + MachineBasicBlock::iterator I = MBB.end(); + unsigned Count = 0; + + while (I != MBB.begin()) { + --I; + if (I->isDebugValue()) + continue; + if (I->getOpcode() != Z80::JQ && + I->getOpcode() != Z80::JQCC) + break; + // Remove the branch. + I->eraseFromParent(); + I = MBB.end(); + ++Count; + } + + return Count; +} + +unsigned Z80InstrInfo::insertBranch(MachineBasicBlock &MBB, + MachineBasicBlock *TBB, + MachineBasicBlock *FBB, + ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded) const { + // Shouldn't be a fall through. + assert(TBB && "InsertBranch must not be told to insert a fallthrough"); + assert(Cond.size() <= 1 && "Z80 branch conditions have one component!"); + assert(!BytesAdded && "code size not handled"); + + if (Cond.empty()) { + // Unconditional branch? + assert(!FBB && "Unconditional branch with multiple successors!"); + BuildMI(&MBB, DL, get(Z80::JQ)).addMBB(TBB); + return 1; + } + + // Conditional branch. + unsigned Count = 0; + BuildMI(&MBB, DL, get(Z80::JQCC)).addMBB(TBB).add(Cond[0]); + ++Count; + + // If FBB is null, it is implied to be a fall-through block. + if (FBB) { + // Two-way Conditional branch. Insert the second branch. + BuildMI(&MBB, DL, get(Z80::JQ)).addMBB(FBB); + ++Count; + } + return Count; +} + +bool Z80InstrInfo:: +reverseBranchCondition(SmallVectorImpl &Cond) const { + assert(Cond.size() == 1 && "Invalid Z80 branch condition!"); + Z80::CondCode CC = static_cast(Cond[0].getImm()); + Cond[0].setImm(GetOppositeBranchCondition(CC)); + return false; +} + +bool Z80::splitReg( + unsigned ByteSize, unsigned Opc8, unsigned Opc16, unsigned Opc24, + unsigned &RC, unsigned &LoOpc, unsigned &LoIdx, unsigned &HiOpc, + unsigned &HiIdx, unsigned &HiOff, bool Has16BitEZ80Ops) { + switch (ByteSize) { + default: llvm_unreachable("Unexpected Size!"); + case 1: + RC = Z80::R8RegClassID; + LoOpc = HiOpc = Opc8; + LoIdx = HiIdx = Z80::NoSubRegister; + HiOff = 0; + return false; + case 2: + if (Has16BitEZ80Ops) { + RC = Z80::R16RegClassID; + LoOpc = HiOpc = Opc16; + LoIdx = HiIdx = Z80::NoSubRegister; + HiOff = 0; + return false; + } + RC = Z80::R16RegClassID; + LoOpc = HiOpc = Opc8; + LoIdx = Z80::sub_low; + HiIdx = Z80::sub_high; + HiOff = 1; + return true; + case 3: + // Legalization should have taken care of this if we don't have eZ80 ops + //assert(Is24Bit && HasEZ80Ops && "Need eZ80 24-bit load/store"); + RC = Z80::R24RegClassID; + LoOpc = HiOpc = Opc24; + LoIdx = HiIdx = Z80::NoSubRegister; + HiOff = 0; + return false; + } +} + +bool Z80InstrInfo::canExchange(MCRegister RegA, MCRegister RegB) const { + // The only regs that can be directly exchanged are DE and HL, in any order. + bool DE = false, HL = false; + for (MCRegister Reg : {RegA, RegB}) { + if (RI.isSubRegisterEq(Z80::UDE, Reg)) + DE = true; + else if (RI.isSubRegisterEq(Z80::UHL, Reg)) + HL = true; + } + return DE && HL; +} + +void Z80InstrInfo::copyPhysReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + const DebugLoc &DL, MCRegister DstReg, + MCRegister SrcReg, bool KillSrc) const { + LLVM_DEBUG(dbgs() << RI.getName(DstReg) << " = " << RI.getName(SrcReg) + << '\n'); + /*for (auto Regs : {std::make_pair(DstReg, &SrcReg), + std::make_pair(SrcReg, &DstReg)}) { + if (Z80::R8RegClass.contains(Regs.first) && + (Z80::R16RegClass.contains(*Regs.second) || + Z80::R24RegClass.contains(*Regs.second))) + *Regs.second = RI.getSubReg(*Regs.second, Z80::sub_low); + }*/ + // Identity copy. + if (DstReg == SrcReg) + return; + bool Is24Bit = Subtarget.is24Bit(); + if (Z80::R8RegClass.contains(DstReg, SrcReg)) { + // Byte copy. + if (Z80::G8RegClass.contains(DstReg, SrcReg)) { + // Neither are index registers. + BuildMI(MBB, MI, DL, get(Z80::LD8gg), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + } else if (Z80::I8RegClass.contains(DstReg, SrcReg)) { + assert(Subtarget.hasIndexHalfRegs() && "Need index half registers"); + // Both are index registers. + if (Z80::X8RegClass.contains(DstReg, SrcReg)) { + BuildMI(MBB, MI, DL, get(Z80::LD8xx), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + } else if (Z80::Y8RegClass.contains(DstReg, SrcReg)) { + BuildMI(MBB, MI, DL, get(Z80::LD8yy), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)); + } else { + // We are copying between different index registers, so we need to use + // an intermediate register. + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)) + ->findRegisterUseOperand(Z80::AF)->setIsUndef(); + BuildMI(MBB, MI, DL, get(Z80::X8RegClass.contains(SrcReg) ? Z80::LD8xx + : Z80::LD8yy), + Z80::A).addReg(SrcReg, getKillRegState(KillSrc)); + BuildMI(MBB, MI, DL, get(Z80::X8RegClass.contains(DstReg) ? Z80::LD8xx + : Z80::LD8yy), + DstReg).addReg(Z80::A); + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + } + } else { + assert(Subtarget.hasIndexHalfRegs() && "Need index half registers"); + // Only one is an index register, which isn't directly possible if one of + // them is from HL. If so, surround with EX DE,HL and use DE instead. + bool NeedEX = false; + for (MCRegister *Reg : {&DstReg, &SrcReg}) { + switch (*Reg) { + case Z80::H: *Reg = Z80::D; NeedEX = true; break; + case Z80::L: *Reg = Z80::E; NeedEX = true; break; + } + } + unsigned ExOpc = Is24Bit ? Z80::EX24DE : Z80::EX16DE; + if (NeedEX) { + // If the prev instr was an EX DE,HL, just kill it. + if (MI == MBB.begin() || std::prev(MI)->getOpcode() != ExOpc) { + auto Ex = BuildMI(MBB, MI, DL, get(ExOpc)); + Ex->findRegisterUseOperand(Is24Bit ? Z80::UDE : Z80::DE) + ->setIsUndef(); + Ex->findRegisterUseOperand(Is24Bit ? Z80::UHL : Z80::HL) + ->setIsUndef(); + } else + std::prev(MI)->eraseFromParent(); + } + BuildMI(MBB, MI, DL, + get(Z80::X8RegClass.contains(DstReg, SrcReg) ? Z80::LD8xx + : Z80::LD8yy), + DstReg).addReg(SrcReg, getKillRegState(KillSrc)); + if (NeedEX) + BuildMI(MBB, MI, DL, get(ExOpc)) + ->findRegisterUseOperand(Is24Bit ? Z80::UHL : Z80::HL)->setIsUndef(); + } + return; + } + // Specialized word copy. + if (DstReg == Z80::SPS || DstReg == Z80::SPL) { + // Copies to SP. + assert((Z80::A16RegClass.contains(SrcReg) || SrcReg == Z80::DE || + Z80::A24RegClass.contains(SrcReg) || SrcReg == Z80::UDE) && + "Unimplemented"); + Is24Bit |= DstReg == Z80::SPL; + MCRegister ExReg; + if (SrcReg == Z80::DE || SrcReg == Z80::UDE) { + ExReg = SrcReg; + SrcReg = SrcReg == Z80::DE ? Z80::HL : Z80::UHL; + } + if (ExReg) + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)) + .addReg(ExReg, RegState::ImplicitDefine) + .addReg(SrcReg, RegState::ImplicitKill); + BuildMI(MBB, MI, DL, get(DstReg == Z80::SPL ? Z80::LD24SP : Z80::LD16SP)) + .addReg(SrcReg, getKillRegState(KillSrc)); + if (ExReg) + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)) + .addReg(ExReg, RegState::ImplicitDefine) + .addReg(SrcReg, RegState::ImplicitDefine); + return; + } + if (SrcReg == Z80::SPL || SrcReg == Z80::SPS) { + // Copies from SP. + Is24Bit |= SrcReg == Z80::SPL; + MCRegister PopReg, ExReg; + if (DstReg == Z80::UBC || DstReg == Z80::BC) { + PopReg = DstReg; + DstReg = Is24Bit ? Z80::UHL : Z80::HL; + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(DstReg); + } else if (DstReg == Z80::UDE || DstReg == Z80::DE) { + ExReg = DstReg; + DstReg = Is24Bit ? Z80::UHL : Z80::HL; + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)) + .addReg(ExReg, RegState::ImplicitDefine) + .addReg(DstReg, RegState::ImplicitKill); + } + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::LD24ri : Z80::LD16ri), + DstReg).addImm(PopReg ? 2 + Is24Bit : 0); + BuildMI(MBB, MI, DL, get(SrcReg == Z80::SPL ? Z80::ADD24SP : Z80::ADD16SP), + DstReg).addReg(DstReg)->addRegisterDead(Z80::F, &getRegisterInfo()); + if (PopReg) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24SP : Z80::EX16SP), DstReg) + .addReg(DstReg); + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::POP24r : Z80::POP16r), PopReg); + } else if (ExReg) + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24DE : Z80::EX16DE)) + .addReg(ExReg, RegState::ImplicitDefine) + .addReg(DstReg, RegState::Implicit | getRegState(MI->getOperand(0))); + return; + } + Is24Bit = Z80::R24RegClass.contains(DstReg, SrcReg); + if (Is24Bit == Subtarget.is24Bit()) { + // Special case DE/HL = HL/DE as EX DE,HL. + if (KillSrc && canExchange(DstReg, SrcReg)) { + MachineInstrBuilder MIB = BuildMI(MBB, MI, DL, + get(Is24Bit ? Z80::EX24DE + : Z80::EX16DE)); + MIB->findRegisterUseOperand(SrcReg)->setIsKill(); + MIB->findRegisterDefOperand(SrcReg)->setIsDead(); + MIB->findRegisterUseOperand(DstReg)->setIsUndef(); + return; + } + bool IsSrcIndexReg = Z80::I16RegClass.contains(SrcReg) || + Z80::I24RegClass.contains(SrcReg); + // Special case copies from index registers when we have eZ80 ops. + if (Subtarget.hasEZ80Ops() && IsSrcIndexReg) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::LEA24ro : Z80::LEA16ro), DstReg) + .addReg(SrcReg, getKillRegState(KillSrc)).addImm(0); + return; + } + // If both are 24-bit then the upper byte needs to be preserved. + // Otherwise copies of index registers may need to use this method if: + // - We are optimizing for size and exactly one reg is an index reg because + // PUSH SrcReg \ POP DstReg is (2 + NumIndexRegs) bytes but slower + // LD DstRegLo,SrcRegLo \ LD DstRegHi,SrcRegHi is 4 bytes but faster + // - We don't have undocumented half index copies + bool IsDstIndexReg = Z80::I16RegClass.contains(DstReg) || + Z80::I24RegClass.contains(DstReg); + unsigned NumIndexRegs = IsSrcIndexReg + IsDstIndexReg; + bool OptSize = MBB.getParent()->getFunction().hasOptSize(); + if (Is24Bit || (NumIndexRegs == 1 && OptSize) || + (NumIndexRegs && !Subtarget.hasIndexHalfRegs())) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(SrcReg, getKillRegState(KillSrc)); + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::POP24r : Z80::POP16r), DstReg); + return; + } + } + // Otherwise, implement as two copies. A 16-bit copy should copy high and low + // 8 bits separately. + assert(Z80::R16RegClass.contains(DstReg, SrcReg) && "Unknown copy width"); + unsigned SubLo = Z80::sub_low; + unsigned SubHi = Z80::sub_high; + MCRegister DstLoReg = RI.getSubReg(DstReg, SubLo); + MCRegister SrcLoReg = RI.getSubReg(SrcReg, SubLo); + MCRegister DstHiReg = RI.getSubReg(DstReg, SubHi); + MCRegister SrcHiReg = RI.getSubReg(SrcReg, SubHi); + /*bool DstLoSrcHiOverlap = RI.regsOverlap(DstLoReg, SrcHiReg); + bool SrcLoDstHiOverlap = RI.regsOverlap(SrcLoReg, DstHiReg); + if (DstLoSrcHiOverlap && SrcLoDstHiOverlap) { + assert(KillSrc && + "Both parts of SrcReg and DstReg overlap but not killing source!"); + // e.g. EUHL = LUDE so just swap the operands + MCRegister OtherReg; + if (canExchange(DstLoReg, SrcLoReg)) { + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::EX24DE : Z80::EX16DE)) + .addReg(DstReg, RegState::ImplicitDefine) + .addReg(SrcReg, RegState::ImplicitKill); + } else if ((OtherReg = DstLoReg, RI.isSubRegisterEq(Z80::UHL, SrcLoReg)) || + (OtherReg = SrcLoReg, RI.isSubRegisterEq(Z80::UHL, DstLoReg))) { + BuildMI(MBB, MI, DL, + get(Subtarget.is24Bit() ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(OtherReg, RegState::Kill); + BuildMI(MBB, MI, DL, + get(Subtarget.is24Bit() ? Z80::EX24SP : Z80::EX16SP)); + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::POP24r : Z80::POP16r), + OtherReg); + } else { + BuildMI(MBB, MI, DL, + get(Subtarget.is24Bit() ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(SrcLoReg, RegState::Kill); + BuildMI(MBB, MI, DL, + get(Subtarget.is24Bit() ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(DstLoReg, RegState::Kill); + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::POP24r : Z80::POP16r), + SrcLoReg); + BuildMI(MBB, MI, DL, get(Subtarget.is24Bit() ? Z80::POP24r : Z80::POP16r), + DstLoReg); + } + // Check if top needs to be moved (e.g. EUHL = HUDE). + unsigned DstHiIdx = RI.getSubRegIndex(SrcLoReg, DstHiReg); + unsigned SrcHiIdx = RI.getSubRegIndex(DstLoReg, SrcHiReg); + if (DstHiIdx != SrcHiIdx) + copyPhysReg(MBB, MI, DL, DstHiReg, + RI.getSubReg(DstLoReg, SrcHiIdx), KillSrc); + } else if (DstLoSrcHiOverlap) { + // Copy out SrcHi before SrcLo overwrites it. + copyPhysReg(MBB, MI, DL, DstHiReg, SrcHiReg, KillSrc); + copyPhysReg(MBB, MI, DL, DstLoReg, SrcLoReg, KillSrc); + } else*/ { + // If SrcLoDstHiOverlap then copy out SrcLo before SrcHi overwrites it, + // otherwise the order doesn't matter. + copyPhysReg(MBB, MI, DL, DstLoReg, SrcLoReg, KillSrc); + copyPhysReg(MBB, MI, DL, DstHiReg, SrcHiReg, KillSrc); + } + --MI; + MI->addRegisterDefined(DstReg, &RI); + if (KillSrc) + MI->addRegisterKilled(SrcReg, &RI, true); +} + +static const MachineInstrBuilder &addSubReg(const MachineInstrBuilder &MIB, + Register Reg, unsigned Idx, + const MCRegisterInfo *TRI, + unsigned Flags = 0) { + if (Idx && Reg.isPhysical()) { + Reg = TRI->getSubReg(Reg, Idx); + Idx = 0; + } + return MIB.addReg(Reg, Flags, Idx); +} + +void Z80InstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + Register SrcReg, bool IsKill, int FI, + const TargetRegisterClass *TRC, + const TargetRegisterInfo *TRI) const { + unsigned Opc; + switch (TRI->getSpillSize(*TRC)) { + default: + llvm_unreachable("Unexpected regclass size"); + case 1: + Opc = Z80::LD8or; + break; + case 2: + Opc = Subtarget.has16BitEZ80Ops() ? Z80::LD16or : Z80::LD88or; + break; + case 3: + assert(Subtarget.is24Bit() && "Only 24-bit should have 3 byte stack slots"); + Opc = Z80::LD24or; + break; + } + BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(Opc)).addFrameIndex(FI).addImm(0) + .addReg(SrcReg, getKillRegState(IsKill)); + return; + unsigned RC, LoOpc, LoIdx, HiOpc, HiIdx, HiOff; + bool Split = + Z80::splitReg(TRI->getSpillSize(*TRC), Z80::LD8or, Z80::LD16or, Z80::LD24or, + RC, LoOpc, LoIdx, HiOpc, HiIdx, HiOff, + Subtarget.has16BitEZ80Ops()); + addSubReg(BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(LoOpc)) + .addFrameIndex(FI).addImm(0), SrcReg, LoIdx, TRI, + getKillRegState(IsKill)); + if (Split) { + MachineInstrBuilder HiMIB = addSubReg( + BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(HiOpc)) + .addFrameIndex(FI).addImm(HiOff), SrcReg, HiIdx, TRI, + getKillRegState(IsKill)); + if (IsKill) + HiMIB->addRegisterKilled(SrcReg, TRI, true); + } +} + +void Z80InstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + Register DstReg, int FI, + const TargetRegisterClass *TRC, + const TargetRegisterInfo *TRI) const { + unsigned Opc; + switch (TRI->getSpillSize(*TRC)) { + default: + llvm_unreachable("Unexpected regclass size"); + case 1: + Opc = Z80::LD8ro; + break; + case 2: + Opc = Subtarget.has16BitEZ80Ops() ? Z80::LD16ro : Z80::LD88ro; + break; + case 3: + assert(Subtarget.is24Bit() && "Only 24-bit should have 3 byte stack slots"); + Opc = Z80::LD24ro; + break; + } + BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(Opc), DstReg).addFrameIndex(FI) + .addImm(0); + return; + unsigned RC, LoOpc, LoIdx, HiOpc, HiIdx, HiOff; + bool Split = + Z80::splitReg(TRI->getSpillSize(*TRC), Z80::LD8ro, Z80::LD16ro, Z80::LD24ro, + RC, LoOpc, LoIdx, HiOpc, HiIdx, HiOff, + Subtarget.hasEZ80Ops()); + addSubReg(BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(LoOpc)), DstReg, LoIdx, + TRI, RegState::DefineNoRead).addFrameIndex(FI).addImm(0); + if (Split) { + MachineInstrBuilder HiMIB = addSubReg( + BuildMI(MBB, MI, MBB.findDebugLoc(MI), get(HiOpc)), DstReg, HiIdx, + TRI, RegState::Define).addFrameIndex(FI).addImm(HiOff); + HiMIB->addRegisterDefined(DstReg, TRI); + } +} + +/// Return true and the FrameIndex if the specified +/// operand and follow operands form a reference to the stack frame. +bool Z80InstrInfo::isFrameOperand(const MachineInstr &MI, unsigned int Op, + int &FrameIndex) const { + if (MI.getOperand(Op).isFI() && + MI.getOperand(Op + 1).isImm() && MI.getOperand(Op + 1).getImm() == 0) { + FrameIndex = MI.getOperand(Op).getIndex(); + return true; + } + return false; +} + +static bool isFrameLoadOpcode(int Opcode) { + switch (Opcode) { + default: + return false; + case Z80::LD8ro: + case Z80::LD8go: + case Z80::LD16ro: + case Z80::LD88ro: + case Z80::LD24ro: + return true; + } +} +unsigned Z80InstrInfo::isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + if (isFrameLoadOpcode(MI.getOpcode()) && !MI.getOperand(0).getSubReg() && + isFrameOperand(MI, 1, FrameIndex)) + return MI.getOperand(0).getReg(); + return 0; +} + +static bool isFrameStoreOpcode(int Opcode) { + switch (Opcode) { + default: + return false; + case Z80::LD8or: + case Z80::LD8og: + case Z80::LD16or: + case Z80::LD88or: + case Z80::LD24or: + return true; + } +} +unsigned Z80InstrInfo::isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const { + if (isFrameStoreOpcode(MI.getOpcode()) && !MI.getOperand(2).getSubReg() && + isFrameOperand(MI, 0, FrameIndex)) + return MI.getOperand(2).getReg(); + return 0; +} + +bool Z80InstrInfo::isReallyTriviallyReMaterializable(const MachineInstr &MI, + AAResults *AA) const { + switch (MI.getOpcode()) { + case Z80::LD8r0: + case Z80::LD24r0: + case Z80::LD24r_1: + return true; + } + return false; +} + +void Z80InstrInfo::reMaterialize(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I, Register DstReg, + unsigned SubIdx, const MachineInstr &Orig, + const TargetRegisterInfo &TRI) const { + if (!Orig.modifiesRegister(Z80::F, &TRI) || + MBB.computeRegisterLiveness(&TRI, Z80::F, I) == + MachineBasicBlock::LQR_Dead) + return TargetInstrInfo::reMaterialize(MBB, I, DstReg, SubIdx, Orig, TRI); + // The instruction clobbers F. Re-materialize as LDri to avoid side effects. + unsigned Opc; + int Value; + switch (Orig.getOpcode()) { + default: llvm_unreachable("Unexpected instruction!"); + case Z80::LD8r0: Opc = Z80::LD8ri; Value = 0; break; + case Z80::LD24r0: Opc = Z80::LD24ri; Value = 0; break; + case Z80::LD24r_1: Opc = Z80::LD24ri; Value = -1; break; + } + const MachineOperand &OrigReg = Orig.getOperand(0); + MachineInstr &NewMI = + *BuildMI(MBB, I, Orig.getDebugLoc(), get(Opc)).add(OrigReg).addImm(Value); + NewMI.substituteRegister(OrigReg.getReg(), DstReg, SubIdx, TRI); +} + +void Z80InstrInfo:: +expandLoadStoreWord(const TargetRegisterClass *ARC, unsigned AOpc, + const TargetRegisterClass *ORC, unsigned OOpc, + MachineInstr &MI, unsigned RegIdx) const { + MCRegister Reg = MI.getOperand(RegIdx).getReg(); + assert(ARC->contains(Reg) != ORC->contains(Reg) && + "RegClasses should be covering and disjoint"); + MI.setDesc(get(ARC->contains(Reg) ? AOpc : OOpc)); + (void)ORC; +} + +bool Z80InstrInfo::expandPostRAPseudo(MachineInstr &MI) const { + DebugLoc DL = MI.getDebugLoc(); + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + auto Next = ++MachineBasicBlock::iterator(MI); + MachineInstrBuilder MIB(MF, MI); + const TargetRegisterInfo &TRI = getRegisterInfo(); + bool Is24Bit = Subtarget.is24Bit(); + bool UseLEA = Is24Bit && !MF.getFunction().hasOptSize(); + LLVM_DEBUG(dbgs() << "\nZ80InstrInfo::expandPostRAPseudo:"; MI.dump()); + switch (unsigned Opc = MI.getOpcode()) { + default: + return false; + case Z80::RCF: + MI.setDesc(get(Z80::OR8ar)); + MIB.addReg(Z80::A, RegState::Undef); + break; + case Z80::LD8r0: + if (MI.getOperand(0).getReg() == Z80::A) { + MI.setDesc(get(Z80::XOR8ar)); + MI.getOperand(0).setIsUse(); + MI.getOperand(0).setIsUndef(); + MIB.addReg(Z80::A, RegState::ImplicitDefine); + } else { + MI.setDesc(get(Z80::LD8ri)); + MI.findRegisterDefOperand(Z80::F)->ChangeToImmediate(0); + } + break; + case Z80::LD24r0: + case Z80::LD24r_1: + if (MI.getOperand(0).getReg() == Z80::UHL) { + if (Opc == Z80::LD24r0) + expandPostRAPseudo(*BuildMI(MBB, MI, DL, get(Z80::RCF))); + else + BuildMI(MBB, MI, DL, get(Z80::SCF)); + MI.setDesc(get(Z80::SBC24aa)); + MI.getOperand(0).setImplicit(); + MIB.addReg(Z80::UHL, RegState::Implicit | RegState::Undef); + MIB.addReg(Z80::F, RegState::Implicit); + } else { + MI.setDesc(get(Z80::LD24ri)); + MI.findRegisterDefOperand(Z80::F) + ->ChangeToImmediate(Opc == Z80::LD24r0 ? 0 : -1); + } + break; + case Z80::CP16ao: + case Z80::CP24ao: { + MCRegister Reg = Opc == Z80::CP24ao ? Z80::UHL : Z80::HL; + if (MBB.computeRegisterLiveness(&TRI, Reg, Next) != + MachineBasicBlock::LQR_Dead) { + BuildMI(MBB, Next, DL, get(Opc == Z80::CP24ao ? Z80::ADD24ao + : Z80::ADD16ao), Reg) + .addReg(Reg).add(MI.getOperand(0)); + MI.getOperand(0).setIsKill(false); + } + LLVM_FALLTHROUGH; + } + case Z80::SUB16ao: + case Z80::SUB24ao: + expandPostRAPseudo(*BuildMI(MBB, MI, DL, get(Z80::RCF))); + MI.setDesc(get(Opc == Z80::CP24ao || Opc == Z80::SUB24ao ? Z80::SBC24ao + : Z80::SBC16ao)); + break; + case Z80::CP16a0: + case Z80::CP24a0: { + MCRegister Reg = Opc == Z80::CP24a0 ? Z80::UHL : Z80::HL; + MCRegister UndefReg = Opc == Z80::CP24a0 ? Z80::UBC : Z80::BC; + BuildMI(MBB, MI, DL, get(Opc == Z80::CP24a0 ? Z80::ADD24ao : Z80::ADD16ao), + Reg).addReg(Reg).addReg(UndefReg, RegState::Undef); + expandPostRAPseudo(*BuildMI(MBB, MI, DL, get(Z80::RCF))); + MI.setDesc(get(Opc == Z80::CP24a0 ? Z80::SBC24ao : Z80::SBC16ao)); + MIB.addReg(UndefReg, RegState::Undef); + break; + } + case Z80::LD8ro: + case Z80::LD8rp: { + MachineOperand &DstOp = MI.getOperand(0); + if (Z80::I8RegClass.contains(DstOp.getReg())) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)) + ->findRegisterUseOperand(Z80::AF)->setIsUndef(); + copyPhysReg(MBB, Next, DL, DstOp.getReg(), Z80::A, true); + DstOp.setReg(Z80::A); + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + } + MI.setDesc(get(Opc == Z80::LD8ro ? Z80::LD8go : Z80::LD8gp)); + break; + } + case Z80::LD88rp: + case Z80::LD88ro: { + assert(!Subtarget.has16BitEZ80Ops() && + "LD88rp/LD88ro should not be used on the ez80 in 16-bit mode"); + MachineOperand &DstOp = MI.getOperand(0); + MCRegister OrigReg = DstOp.getReg(); + MCRegister Reg = OrigReg; + const MachineOperand &AddrOp = MI.getOperand(1); + MCRegister AddrReg = AddrOp.getReg(); + unsigned LowOpc = Opc == Z80::LD88rp ? Z80::LD8rp : Z80::LD8ro; + unsigned HighOpc = + RI.isSubRegisterEq(Z80::UHL, AddrReg) ? Z80::LD8rp : Z80::LD8ro; + bool Index = Z80::I16RegClass.contains(Reg); + if (Index) + Reg = Z80::HL; + bool Overlap = RI.isSubRegisterEq(AddrReg, Reg); + if (Overlap) + Reg = Z80::DE; + MCRegister ScratchReg = + Is24Bit + ? TRI.getMatchingSuperReg(Reg, Z80::sub_short, &Z80::R24RegClass) + : Reg; + if (Index || Overlap) + // Save scratch register + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(ScratchReg, RegState::Undef); + MI.setDesc(get(HighOpc)); + DstOp.setReg(TRI.getSubReg(Reg, Z80::sub_high)); + if (LowOpc != HighOpc) { + assert(LowOpc == Z80::LD8rp && HighOpc == Z80::LD8ro && + "Can't offset low only"); + // Fixup if converting high to offset + MIB.addImm(1); + } + MIB = BuildMI(MBB, MI, DL, get(LowOpc), TRI.getSubReg(Reg, Z80::sub_low)) + .addReg(AddrReg); + if (HighOpc == Z80::LD8rp) { + // Compute high address + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::INC24r : Z80::INC16r), AddrReg) + .addReg(AddrReg); + if (!AddrOp.isKill()) + // Restore address register + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::DEC24r : Z80::DEC16r), + AddrReg).addReg(AddrReg); + } else if (LowOpc == Z80::LD8ro) { + // Fixup if both are offset + MachineOperand &OffOp = MI.getOperand(2); + MIB.add(OffOp); + OffOp.setImm(OffOp.getImm() + 1); + assert(isInt<8>(OffOp.getImm()) && "LD88 can't have maximum offset."); + } + if (Index) { + if (Reg == Z80::HL) + // Restore scratch register and prepare to set original register below + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::EX24SP : Z80::EX16SP), + ScratchReg).addReg(ScratchReg); + else + // Set original register directly + copyPhysReg(MBB, Next, DL, OrigReg, Reg); + } else if (Overlap) + copyPhysReg(MBB, Next, DL, OrigReg, Reg, true); + if (Index || Overlap) + // Restore scratch register or set original register + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24r : Z80::POP16r), + Index && Reg == Z80::HL + ? Is24Bit ? TRI.getMatchingSuperReg(OrigReg, Z80::sub_short, + &Z80::R24RegClass) + : OrigReg + : ScratchReg); + expandPostRAPseudo(*MIB); + expandPostRAPseudo(MI); + break; + } + case Z80::LD8or: + case Z80::LD8pr: { + MachineOperand &SrcOp = MI.getOperand(MI.getNumExplicitOperands() - 1); + if (Z80::I8RegClass.contains(SrcOp.getReg())) { + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)) + ->findRegisterUseOperand(Z80::AF)->setIsUndef(); + copyPhysReg(MBB, MI, DL, Z80::A, SrcOp.getReg(), SrcOp.isKill()); + SrcOp.setReg(Z80::A); + SrcOp.setIsKill(); + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + } + MI.setDesc(get(Opc == Z80::LD8or ? Z80::LD8og : Z80::LD8pg)); + break; + } + case Z80::LD88pr: + case Z80::LD88or: { + assert(!Subtarget.has16BitEZ80Ops() && + "LD88pr/LD88or should not be used on the ez80 in 16-bit mode"); + const MachineOperand &AddrOp = MI.getOperand(0); + MCRegister AddrReg = AddrOp.getReg(); + MachineOperand &SrcOp = MI.getOperand(MI.getNumExplicitOperands() - 1); + MCRegister OrigReg = SrcOp.getReg(); + MCRegister Reg = OrigReg; + unsigned LowOpc = Opc == Z80::LD88pr ? Z80::LD8pr : Z80::LD8or; + unsigned HighOpc = + RI.isSubRegisterEq(Z80::UHL, AddrReg) ? Z80::LD8pr : Z80::LD8or; + bool Index = Z80::I16RegClass.contains(Reg); + if (Index) + Reg = Z80::HL; + bool Overlap = RI.isSubRegisterEq(AddrReg, Reg); + if (Overlap && Index) { + // Just use DE instead, so there's no overlap to worry about. + Reg = Z80::DE; + Overlap = false; + assert(!RI.isSubRegisterEq(AddrReg, Reg) && "Shouldn't overlap"); + } + assert((!Overlap || HighOpc == Z80::LD8pr) && + "If we are overlapping, then high shouldn't be offset"); + MCRegister ScratchReg = + Is24Bit + ? TRI.getMatchingSuperReg(Reg, Z80::sub_short, &Z80::R24RegClass) + : Reg; + MCRegister OrigHighReg = TRI.getSubReg(Reg, Z80::sub_high); + MCRegister HighReg = OrigHighReg; + if (Index) { + MCRegister OrigSuperReg = + TRI.getMatchingSuperReg(OrigReg, Z80::sub_short, &Z80::R24RegClass); + // Save scratch register or prepare to set scratch register + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(UseLEA || Reg == Z80::DE ? ScratchReg + : Is24Bit ? OrigSuperReg : OrigReg); + if (UseLEA) + // Set scratch register + BuildMI(MBB, MI, DL, get(Z80::LEA24ro), ScratchReg) + .addReg(OrigSuperReg).addImm(0); + else if (Reg == Z80::HL) + // Save and set scratch register + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::EX24SP : Z80::EX16SP), + ScratchReg).addReg(ScratchReg); + else + // Set scratch register directly + copyPhysReg(MBB, MI, DL, Reg, OrigReg); + } else if (Overlap) { + // Save A + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)) + ->findRegisterUseOperand(Z80::AF)->setIsUndef(); + HighReg = Z80::A; + } + MI.setDesc(get(HighOpc)); + SrcOp.setReg(TRI.getSubReg(Reg, Z80::sub_high)); + if (LowOpc != HighOpc) { + assert(LowOpc == Z80::LD8pr && HighOpc == Z80::LD8or && + "Can't offset low only"); + // Fixup if converting high to offset + SrcOp.ChangeToImmediate(1); + MIB.addReg(HighReg); + } else + SrcOp.setReg(HighReg); + MIB = BuildMI(MBB, MI, DL, get(LowOpc)).addReg(AddrReg); + if (HighOpc == Z80::LD8pr) { + if (Overlap) { + assert(!Index && "Should have changed to DE above"); + // Save high reg in A so it isn't clobbered by the inc below + copyPhysReg(MBB, MI, DL, HighReg, OrigHighReg, false); + } + // Compute high address + BuildMI(MBB, MI, DL, get(Is24Bit ? Z80::INC24r : Z80::INC16r), AddrReg) + .addReg(AddrReg); + if (!AddrOp.isKill()) + // Restore address register + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::DEC24r : Z80::DEC16r), + AddrReg).addReg(AddrReg); + } else if (LowOpc == Z80::LD8or) { + // Fixup if both are offset + MachineOperand &OffOp = MI.getOperand(1); + MIB.add(OffOp); + OffOp.setImm(OffOp.getImm() + 1); + assert(isInt<8>(OffOp.getImm()) && "LD88 can't have maximum offset."); + } + MIB.addReg(TRI.getSubReg(Reg, Z80::sub_low)); + if (Index) + // Restore scratch register + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24r : Z80::POP16r), + ScratchReg); + else if (Overlap) + // Restore A + BuildMI(MBB, Next, DL, get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + expandPostRAPseudo(*MIB); + expandPostRAPseudo(MI); + break; + } + case Z80::LD16rm: + expandLoadStoreWord(&Z80::A16RegClass, Z80::LD16am, + &Z80::O16RegClass, Z80::LD16om, MI, 0); + break; + case Z80::LD24rm: + expandLoadStoreWord(&Z80::A24RegClass, Z80::LD24am, + &Z80::O24RegClass, Z80::LD24om, MI, 0); + break; + case Z80::LD16mr: + expandLoadStoreWord(&Z80::A16RegClass, Z80::LD16ma, + &Z80::O16RegClass, Z80::LD16mo, MI, 1); + break; + case Z80::LD24mr: + expandLoadStoreWord(&Z80::A24RegClass, Z80::LD24ma, + &Z80::O24RegClass, Z80::LD24mo, MI, 1); + break; + case Z80::CALL16r: + case Z80::CALL24r: { + const char *Symbol; + switch (MIB->getOperand(0).getReg()) { + default: llvm_unreachable("Unexpected indcall register"); + case Z80::HL: case Z80::UHL: Symbol = "_indcallhl"; break; + case Z80::IX: case Z80::UIX: Symbol = "_indcallix"; break; + case Z80::IY: case Z80::UIY: Symbol = "_indcall"; break; + } + MI.setDesc(get(Opc == Z80::CALL24r ? Z80::CALL24 : Z80::CALL16)); + MI.getOperand(0).ChangeToES(Symbol); + break; + } + case Z80::EI_RETI: + BuildMI(MBB, MI, DL, get(Z80::EI)); + MI.setDesc(get(Is24Bit ? Z80::RETI24 : Z80::RETI16)); + break; + case Z80::TCRETURN16: + case Z80::TCRETURN24: + MI.setDesc(get(Z80::JQ)); + break; + case Z80::TCRETURN16r: + MI.setDesc(get(Z80::JP16r)); + break; + case Z80::TCRETURN24r: + MI.setDesc(get(Z80::JP24r)); + break; + } + LLVM_DEBUG(MIB->dump()); + return true; +} + +bool Z80InstrInfo::analyzeCompare(const MachineInstr &MI, Register &SrcReg, + Register &SrcReg2, int &CmpMask, + int &CmpValue) const { + switch (MI.getOpcode()) { + default: return false; + case Z80::OR8ar: + SrcReg = Z80::A; + if (MI.getOperand(1).getReg() != SrcReg) + return false; + // Compare against zero. + SrcReg2 = 0; + CmpMask = ~0; + CmpValue = 0; + break; + case Z80::CP8ai: + case Z80::SUB8ai: + SrcReg = Z80::A; + SrcReg2 = 0; + CmpMask = CmpValue = 0; + if (MI.getOperand(0).isImm()) { + CmpMask = ~0; + CmpValue = MI.getOperand(0).getImm(); + } + break; + case Z80::CP8ar: + case Z80::SUB8ar: + SrcReg = Z80::A; + SrcReg2 = MI.getOperand(0).getReg(); + CmpMask = CmpValue = 0; + break; + case Z80::CP8ap: + case Z80::CP8ao: + case Z80::SUB8ap: + case Z80::SUB8ao: + SrcReg = Z80::A; + SrcReg2 = CmpMask = CmpValue = 0; + break; + } + MachineBasicBlock::const_reverse_iterator I = MI, E = MI.getParent()->rend(); + while (++I != E && I->isFullCopy()) + for (Register *Reg : {&SrcReg, &SrcReg2}) + if (Reg->isPhysical() && *Reg == I->getOperand(0).getReg()) + *Reg = I->getOperand(1).getReg(); + return true; +} + +/// Check whether the first instruction, whose only purpose is to update flags, +/// can be made redundant. CP8ar is made redundant by SUB8ar if the operands are +/// the same. +/// SrcReg, SrcReg2: register operands for FlagI. +/// ImmValue: immediate for FlagI if it takes an immediate. +inline static bool isRedundantFlagInstr(MachineInstr &FI, Register SrcReg, + Register SrcReg2, int ImmMask, + int ImmValue, MachineInstr &OI) { + if (ImmMask) + return (FI.getOpcode() == Z80::CP8ai && OI.getOpcode() == Z80::SUB8ai) && + OI.getOperand(1).getImm() == ImmValue; + return (FI.getOpcode() == Z80::CP8ar && OI.getOpcode() == Z80::SUB8ar) && + OI.getOperand(1).getReg() == SrcReg2; +} + +/// Check whether the instruction sets the sign and zero flag based on its +/// result. +inline static bool isSZSettingInstr(MachineInstr &MI) { + switch (MI.getOpcode()) { + default: return false; + case Z80::INC8r: case Z80::INC8p: case Z80::INC8o: + case Z80::DEC8r: case Z80::DEC8p: case Z80::DEC8o: + case Z80::ADD8ar: case Z80::ADD8ai: case Z80::ADD8ap: case Z80::ADD8ao: + case Z80::ADC8ar: case Z80::ADC8ai: case Z80::ADC8ap: case Z80::ADC8ao: + case Z80::SUB8ar: case Z80::SUB8ai: case Z80::SUB8ap: case Z80::SUB8ao: + case Z80::SBC8ar: case Z80::SBC8ai: case Z80::SBC8ap: case Z80::SBC8ao: + case Z80::AND8ar: case Z80::AND8ai: case Z80::AND8ap: case Z80::AND8ao: + case Z80::XOR8ar: case Z80::XOR8ai: case Z80::XOR8ap: case Z80::XOR8ao: + case Z80:: OR8ar: case Z80:: OR8ai: case Z80:: OR8ap: case Z80:: OR8ao: + case Z80::SBC16ao:case Z80::NEG: case Z80::ADC16ao: + case Z80::SUB16ao:case Z80::SUB24ao: + case Z80::RLC8r: case Z80::RLC8p: case Z80::RLC8o: + case Z80::RRC8r: case Z80::RRC8p: case Z80::RRC8o: + case Z80:: RL8r: case Z80:: RL8p: case Z80:: RL8o: + case Z80:: RR8r: case Z80:: RR8p: case Z80:: RR8o: + case Z80::SLA8r: case Z80::SLA8p: case Z80::SLA8o: + case Z80::SRA8r: case Z80::SRA8p: case Z80::SRA8o: + case Z80::SRL8r: case Z80::SRL8p: case Z80::SRL8o: + return true; + } +} + +/// Check if there exists an earlier instruction that operates on the same +/// source operands and sets flags in the same way as Compare; remove Compare if +/// possible. +bool Z80InstrInfo::optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, + Register SrcReg2, int CmpMask, + int CmpValue, + const MachineRegisterInfo *MRI) const { + // If we are comparing against zero, check whether we can use MI to update F. + bool IsCmpZero = CmpMask && !CmpValue; + + // Check whether we can replace SUB with CP. + unsigned CpOpc; + switch (CmpInstr.getOpcode()) { + default: CpOpc = 0; break; + case Z80::SUB8ai: CpOpc = IsCmpZero ? Z80::OR8ar : Z80::CP8ai; break; + case Z80::SUB8ar: CpOpc = Z80::CP8ar; break; + case Z80::SUB8ap: CpOpc = Z80::CP8ap; break; + case Z80::SUB8ao: CpOpc = Z80::CP8ao; break; + } + if (CpOpc) { + int DeadDef = CmpInstr.findRegisterDefOperandIdx(Z80::A, /*isDead*/true); + if (DeadDef == -1) + return false; + // There is no use of the destination register, so we replace SUB with CP. + CmpInstr.setDesc(get(CpOpc)); + if (CpOpc == Z80::OR8ar) + CmpInstr.getOperand(0).ChangeToRegister(Z80::A, false); + else + CmpInstr.RemoveOperand(DeadDef); + } + + // Get the unique definition of SrcReg. + MachineInstr *MI = MRI->getUniqueVRegDef(SrcReg); + if (!MI) return false; + + MachineBasicBlock::iterator I = CmpInstr, Def = MI; + + const TargetRegisterInfo *TRI = &getRegisterInfo(); + for (auto RI = ++Def.getReverse(), RE = MI->getParent()->rend(); + MI->isFullCopy() && RI != RE; ++RI) + if (RI->definesRegister(MI->getOperand(1).getReg(), TRI)) + MI = &*RI; + + // If MI is not in the same BB as CmpInstr, do not optimize. + if (IsCmpZero && (MI->getParent() != CmpInstr.getParent() || + !isSZSettingInstr(*MI))) + return false; + + // We are searching for an earlier instruction, which will be stored in + // SubInstr, that can make CmpInstr redundant. + MachineInstr *SubInstr = nullptr; + + // We iterate backwards, starting from the instruction before CmpInstr, and + // stopping when we reach the definition of a source register or the end of + // the BB. RI points to the instruction before CmpInstr. If the definition is + // in this BB, RE points to it, otherwise RE is the beginning of the BB. + MachineBasicBlock::reverse_iterator RE = CmpInstr.getParent()->rend(); + if (CmpInstr.getParent() == MI->getParent()) + RE = Def.getReverse(); // points to MI + for (auto RI = ++I.getReverse(); RI != RE; ++RI) { + MachineInstr &Instr = *RI; + // Check whether CmpInstr can be made redundant by the current instruction. + if (!IsCmpZero && isRedundantFlagInstr(CmpInstr, SrcReg, SrcReg2, CmpMask, + CmpValue, Instr)) { + SubInstr = &Instr; + break; + } + + // If this instruction modifies F, we can't remove CmpInstr. + if (Instr.modifiesRegister(Z80::F, TRI)) + return false; + } + + // Return false if no candidates exist. + if (!IsCmpZero && !SubInstr) + return false; + + // Scan forward from the instruction after CmpInstr for uses of F. + // It is safe to remove CmpInstr if F is redefined or killed. + // If we are at the end of the BB, we need to check whether F is live-out. + bool IsSafe = false; + MachineBasicBlock::iterator E = CmpInstr.getParent()->end(); + for (++I; I != E; ++I) { + const MachineInstr &Instr = *I; + bool ModifiesFlags = Instr.modifiesRegister(Z80::F, TRI); + bool UsesFlags = Instr.readsRegister(Z80::F, TRI); + if (ModifiesFlags && !UsesFlags) { + IsSafe = true; + break; + } + if (!ModifiesFlags && !UsesFlags) + continue; + if (IsCmpZero) { + Z80::CondCode OldCC; + switch (Instr.getOpcode()) { + default: + OldCC = Z80::COND_INVALID; + break; + case Z80::JQCC: + OldCC = static_cast(Instr.getOperand(1).getImm()); + break; + case Z80::ADC8ar: case Z80::ADC8ai: case Z80::ADC8ap: case Z80::ADC8ao: + case Z80::SBC8ar: case Z80::SBC8ai: case Z80::SBC8ap: case Z80::SBC8ao: + case Z80::SBC16ao:case Z80::ADC16ao: + OldCC = Z80::COND_C; + break; + } + switch (OldCC) { + default: break; + case Z80::COND_NC: case Z80::COND_C: + case Z80::COND_PO: case Z80::COND_PE: + // CF or PV are used, we can't perform this optimization. + return false; + } + } + if (ModifiesFlags || Instr.killsRegister(Z80::F, TRI)) { + // It is safe to remove CmpInstr if F is updated again or killed. + IsSafe = true; + break; + } + } + if (IsCmpZero && !IsSafe) { + MachineBasicBlock *MBB = CmpInstr.getParent(); + for (MachineBasicBlock *Successor : MBB->successors()) + if (Successor->isLiveIn(Z80::F)) + return false; + } + + // The instruction to be updated is either Sub or MI. + if (IsCmpZero) + SubInstr = MI; + + // Make sure Sub instruction defines F and mark the def live. + unsigned i = 0, e = SubInstr->getNumOperands(); + for (; i != e; ++i) { + MachineOperand &MO = SubInstr->getOperand(i); + if (MO.isReg() && MO.isDef() && MO.getReg() == Z80::F) { + MO.setIsDead(false); + break; + } + } + assert(i != e && "Unable to locate a def EFLAGS operand"); + + CmpInstr.eraseFromParent(); + return true; + + // Check whether we can replace SUB with CMP. + switch (CmpInstr.getOpcode()) { + default: return false; + case Z80::SUB8ai: + // cp a,0 -> or a,a (a szhc have same behavior) + // FIXME: This doesn't work if the pv flag is used. + if (!CmpInstr.getOperand(0).getImm()) { + CmpInstr.setDesc(get(Z80::OR8ar)); + CmpInstr.getOperand(0).ChangeToRegister(Z80::A, /*isDef=*/false); + return true; + } + LLVM_FALLTHROUGH; + case Z80::SUB8ar: + case Z80::SUB8ap: + case Z80::SUB8ao: { + if (!CmpInstr.registerDefIsDead(Z80::A)) + return false; + // There is no use of the destination register, we can replace SUB with CMP. + unsigned NewOpcode = 0; + switch (CmpInstr.getOpcode()) { + default: llvm_unreachable("Unreachable!"); + case Z80::SUB8ai: NewOpcode = Z80::CP8ai; break; + case Z80::SUB8ar: NewOpcode = Z80::CP8ar; break; + case Z80::SUB8ap: NewOpcode = Z80::CP8ap; break; + case Z80::SUB8ao: NewOpcode = Z80::CP8ao; break; + } + CmpInstr.setDesc(get(NewOpcode)); + //CmpInstr.findRegisterDefOperand(Z80::A)->setIsDead(false); + //BuildMI(*CmpInstr.getParent(), ++MachineBasicBlock::iterator(CmpInstr), + // CmpInstr.getDebugLoc(), get(TargetOpcode::COPY), SrcReg) + // .addReg(Z80::A, RegState::Kill); + return true; + } + } +} + +MachineInstr *Z80InstrInfo::optimizeLoadInstr(MachineInstr &MI, + const MachineRegisterInfo *MRI, + unsigned &FoldAsLoadDefReg, + MachineInstr *&DefMI) const { + // Check whether we can move DefMI here. + DefMI = MRI->getVRegDef(FoldAsLoadDefReg); + bool SawStore = false; + if (!DefMI || !DefMI->isSafeToMove(nullptr, SawStore)) + return nullptr; + + // Collect information about virtual register operands of MI. + SmallVector SrcOperandIds; + for (unsigned i = 0, e = MI.getNumOperands(); i != e; ++i) { + MachineOperand &MO = MI.getOperand(i); + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (Reg != FoldAsLoadDefReg) + continue; + // Do not fold if we have a subreg use or a def. + if (MO.getSubReg() || MO.isDef()) + return nullptr; + SrcOperandIds.push_back(i); + } + if (SrcOperandIds.empty()) + return nullptr; + + // Check whether we can fold the def into SrcOperandId. + if (MachineInstr *FoldMI = foldMemoryOperand(MI, SrcOperandIds, *DefMI)) { + FoldAsLoadDefReg = 0; + return FoldMI; + } + + return nullptr; +} + +void Z80InstrInfo::updateOperandRegConstraints(MachineFunction &MF, + MachineInstr &NewMI) const { + MachineRegisterInfo &MRI = MF.getRegInfo(); + const TargetRegisterInfo &TRI = *MRI.getTargetRegisterInfo(); + + for (unsigned Idx : llvm::seq(0u, NewMI.getNumOperands())) { + MachineOperand &MO = NewMI.getOperand(Idx); + // We only need to update constraints on virtual register operands. + if (!MO.isReg()) + continue; + Register Reg = MO.getReg(); + if (!Reg.isVirtual()) + continue; + + auto *NewRC = + MRI.constrainRegClass(Reg, getRegClass(NewMI.getDesc(), Idx, &TRI, MF)); + if (!NewRC) { + LLVM_DEBUG( + dbgs() << "WARNING: Unable to update register constraint for operand " + << Idx << " of instruction:\n"; + NewMI.dump(); dbgs() << "\n"); + } + } +} + +MachineInstr *Z80InstrInfo::foldMemoryOperandImpl( + MachineFunction &MF, MachineInstr &MI, unsigned OpNum, + ArrayRef MOs, MachineBasicBlock::iterator InsertPt) const { + bool IsOff; + if (MOs.size() == 1 && MOs[0].isReg()) + IsOff = false; + else if (MOs.size() == 2 && (MOs[0].isReg() || MOs[0].isFI()) && + MOs[1].isImm()) + IsOff = true; + else + return nullptr; + + unsigned Opc; + switch (MI.getOpcode()) { + case Z80::BIT8bg: Opc = IsOff ? Z80::BIT8bo : Z80::BIT8bp; break; + case Z80::ADD8ar: Opc = IsOff ? Z80::ADD8ao : Z80::ADD8ap; break; + case Z80::ADC8ar: Opc = IsOff ? Z80::ADC8ao : Z80::ADC8ap; break; + case Z80::SUB8ar: Opc = IsOff ? Z80::SUB8ao : Z80::SUB8ap; break; + case Z80::SBC8ar: Opc = IsOff ? Z80::SBC8ao : Z80::SBC8ap; break; + case Z80::AND8ar: Opc = IsOff ? Z80::AND8ao : Z80::AND8ap; break; + case Z80::XOR8ar: Opc = IsOff ? Z80::XOR8ao : Z80::XOR8ap; break; + case Z80:: OR8ar: Opc = IsOff ? Z80:: OR8ao : Z80:: OR8ap; break; + case Z80::TST8ar: Opc = IsOff ? Z80::TST8ao : Z80::TST8ap; break; + default: return nullptr; + } + + MachineInstrBuilder MIB( + MF, MF.CreateMachineInstr(get(Opc), MI.getDebugLoc(), true)); + for (unsigned Idx : llvm::seq(0u, MI.getNumOperands())) { + MachineOperand &MO = MI.getOperand(Idx); + if (Idx == OpNum) { + assert(MO.isReg() && "Expected to fold into reg operand!"); + for (auto &AddrMO : MOs) + MIB.add(AddrMO); + } else + MIB.add(MO); + } + updateOperandRegConstraints(MF, *MIB); + InsertPt->getParent()->insert(InsertPt, MIB); + return MIB; +} + +MachineInstr * +Z80InstrInfo::foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, + ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, + int FrameIndex, LiveIntervals *LIS, + VirtRegMap *VRM) const { + for (auto Op : Ops) + if (MI.getOperand(Op).getSubReg()) + return nullptr; + + const MachineFrameInfo &MFI = MF.getFrameInfo(); + unsigned Size = MFI.getObjectSize(FrameIndex); + + if (Ops.size() != 1 || Size != 1) + return nullptr; + + MachineOperand MOs[2] = {MachineOperand::CreateFI(FrameIndex), + MachineOperand::CreateImm(0)}; + return foldMemoryOperandImpl(MF, MI, Ops[0], MOs, InsertPt); +} + +MachineInstr * +Z80InstrInfo::foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, + ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, + MachineInstr &LoadMI, + LiveIntervals *LIS) const { + for (auto Op : Ops) + if (MI.getOperand(Op).getSubReg()) + return nullptr; + + int FrameIndex; + if (isLoadFromStackSlot(LoadMI, FrameIndex)) + return foldMemoryOperandImpl(MF, MI, Ops, InsertPt, FrameIndex, LIS); + + if (Ops.size() != 1) + return nullptr; + + auto MOs = LoadMI.explicit_uses(); + return foldMemoryOperandImpl(MF, MI, Ops[0], {MOs.begin(), MOs.end()}, + InsertPt); +} diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.h b/llvm/lib/Target/Z80/Z80InstrInfo.h new file mode 100644 index 00000000000000..d22bbb33e8fe21 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80InstrInfo.h @@ -0,0 +1,211 @@ +//===-- Z80InstrInfo.h - Z80 Instruction Information ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Z80 implementation of the TargetInstrInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80INSTRINFO_H +#define LLVM_LIB_TARGET_Z80_Z80INSTRINFO_H + +#include "Z80RegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +#define GET_INSTRINFO_HEADER +#include "Z80GenInstrInfo.inc" + +namespace llvm { +class Z80Subtarget; + +namespace Z80 { + // Z80 specific condition code. These correspond to Z80_*_COND in + // Z80InstrInfo.td. They must be kept in synch. +enum CondCode { + COND_NZ = 0, + COND_Z = 1, + COND_NC = 2, + COND_C = 3, + LAST_SIMPLE_COND = COND_C, + + COND_PO = 4, + COND_PE = 5, + COND_P = 6, + COND_M = 7, + LAST_VALID_COND = COND_M, + + COND_INVALID +}; + +CondCode GetBranchConditionForPredicate(CmpInst::Predicate Pred, bool &IsSigned, + bool &IsSwapped, bool &IsConst); + +/// GetOppositeBranchCondition - Return the inverse of the specified cond, +/// e.g. turning COND_Z to COND_NZ. +CondCode GetOppositeBranchCondition(CondCode CC); + +bool splitReg(unsigned ByteSize, unsigned Opc8, unsigned Opc16, unsigned Opc24, + unsigned &RC, unsigned &LoOpc, unsigned &LoIdx, unsigned &HiOpc, + unsigned &HiIdx, unsigned &HiOff, bool Has16BitEZ80Ops); +} // end namespace Z80; + +namespace Z80II { + enum { + PrefixShift = 0, + NoPrefix = 0 << PrefixShift, + CBPrefix = 1 << PrefixShift, + DDPrefix = 2 << PrefixShift, + DDCBPrefix = 3 << PrefixShift, + EDPrefix = 4 << PrefixShift, + FDPrefix = 5 << PrefixShift, + FDCBPrefix = 6 << PrefixShift, + AnyIndexPrefix = 7 << PrefixShift, + PrefixMask = 7 << PrefixShift, + IndexedIndexPrefix = 8 << PrefixShift, + + ModeShift = 4, + AnyMode = 0 << ModeShift, + CurMode = 1 << ModeShift, + Z80Mode = 2 << ModeShift, + EZ80Mode = 3 << ModeShift, + ModeMask = 3 << ModeShift, + + HasImm = 1 << 6, + HasOff = 1 << 7, + + OpcodeShift = 8, + OpcodeMask = 0xFF << OpcodeShift + }; +} // end namespace Z80II; + +class Z80InstrInfo final : public Z80GenInstrInfo { + Z80Subtarget &Subtarget; + const Z80RegisterInfo RI; + + virtual void anchor(); + +public: + explicit Z80InstrInfo(Z80Subtarget &STI); + + /// getRegisterInfo - TargetInstrInfo is a superset of MRegister info. As + /// such, whenever a client has an instance of instruction info, it should + /// always be able to get register info as well (through this method). + /// + const Z80RegisterInfo &getRegisterInfo() const { return RI; } + + int getSPAdjust(const MachineInstr &MI) const override; + + int64_t getFrameAdjustment(const MachineInstr &MI) const { + assert(isFrameInstr(MI) && "Not a frame instruction"); + int64_t Amount = getFrameSize(MI) - + MI.getOperand(MI.getNumExplicitOperands() - 1).getImm(); + return isFrameSetup(MI) ? -Amount : Amount; + } + + unsigned getInstSizeInBytes(const MachineInstr &MI) const override; + + // Branch analysis. + bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB, + MachineBasicBlock *&FBB, + SmallVectorImpl &Cond, + bool AllowModify) const override; + + unsigned removeBranch(MachineBasicBlock &MBB, + int *BytesRemoved = nullptr) const override; + unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, + MachineBasicBlock *FBB, ArrayRef Cond, + const DebugLoc &DL, + int *BytesAdded = nullptr) const override; + bool + reverseBranchCondition(SmallVectorImpl &Cond) const override; + + void copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, + const DebugLoc &DL, MCRegister DstReg, MCRegister SrcReg, + bool KillSrc = false) const override; + void storeRegToStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, Register SrcReg, + bool isKill, int FrameIndex, + const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + unsigned isStoreToStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + void loadRegFromStackSlot(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, Register DstReg, + int FrameIndex, const TargetRegisterClass *RC, + const TargetRegisterInfo *TRI) const override; + unsigned isLoadFromStackSlot(const MachineInstr &MI, + int &FrameIndex) const override; + + bool isReallyTriviallyReMaterializable(const MachineInstr &MI, + AAResults *AA) const override; + void reMaterialize(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, + Register DstReg, unsigned SubIdx, const MachineInstr &Orig, + const TargetRegisterInfo &TRI) const override; + + bool expandPostRAPseudo(MachineInstr &MI) const override; + + /// For a comparison instruction, return the source registers in SrcReg and + /// SrcReg2 if having two register operands, and the value it compares against + /// in CmpValue. Return true if the comparison instruction can be analyzed. + bool analyzeCompare(const MachineInstr &MI, Register &SrcReg, + Register &SrcReg2, int &CmpMask, + int &CmpValue) const override; + /// Check if there exists an earlier instruction that operates on the same + /// source operands and sets flags in the same way as Compare; remove Compare + /// if possible. + bool optimizeCompareInstr(MachineInstr &CmpInstr, Register SrcReg, + Register SrcReg2, int CmpMask, int CmpValue, + const MachineRegisterInfo *MRI) const override; + + MachineInstr *optimizeLoadInstr(MachineInstr &MI, + const MachineRegisterInfo *MRI, + unsigned &FoldAsLoadDefReg, + MachineInstr *&DefMI) const override; + + /// Check whether the target can fold a load that feeds a subreg operand + /// (or a subreg operand that feeds a store). + bool isSubregFoldable() const override { return true; } + + MachineInstr *foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, + ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, + int FrameIndex, + LiveIntervals *LIS = nullptr, + VirtRegMap *VRM = nullptr) const override; + MachineInstr *foldMemoryOperandImpl( + MachineFunction &MF, MachineInstr &MI, ArrayRef Ops, + MachineBasicBlock::iterator InsertPt, MachineInstr &LoadMI, + LiveIntervals *LIS = nullptr) const override; + +private: + void updateOperandRegConstraints(MachineFunction &MF, + MachineInstr &NewMI) const; + MachineInstr * + foldMemoryOperandImpl(MachineFunction &MF, MachineInstr &MI, unsigned OpNum, + ArrayRef MOs, + MachineBasicBlock::iterator InsertPt) const; + + /// canExchange - This returns whether the two instructions can be directly + /// exchanged with one EX instruction. Since the only register exchange + /// instruction is EX DE,HL, simply returns whether the two arguments are + /// super-registers of E and L, in any order. + bool canExchange(MCRegister RegA, MCRegister RegB) const; + + /// isFrameOperand - Return true and the FrameIndex if the specified + /// operand and follow operands form a reference to the stack frame. + bool isFrameOperand(const MachineInstr &MI, unsigned int Op, + int &FrameIndex) const; + + void expandLoadStoreWord(const TargetRegisterClass *ARC, unsigned AOpc, + const TargetRegisterClass *ORC, unsigned OOpc, + MachineInstr &MI, unsigned RegIdx) const; +}; + +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80InstrInfo.td b/llvm/lib/Target/Z80/Z80InstrInfo.td new file mode 100644 index 00000000000000..9d45b8f617ac5a --- /dev/null +++ b/llvm/lib/Target/Z80/Z80InstrInfo.td @@ -0,0 +1,1087 @@ +//===-- Z80InstrInfo.td - Main Z80 Instruction Definition --*- tablegen -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the Z80 instruction set, defining the instructions, and +// properties of the instructions which are needed for code generation, machine +// code emission, and analysis. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Type Constraints. +//===----------------------------------------------------------------------===// +class SDTCisChain : SDTCisVT; +class SDTCisI8 : SDTCisVT; +class SDTCisFlag : SDTCisI8; +class SDTCisI16 : SDTCisVT; +class SDTCisI24 : SDTCisVT; +class SDTCisPtr : SDTCisVT; + +def p0 : LLT; + +//===----------------------------------------------------------------------===// +// Type Profiles. +//===----------------------------------------------------------------------===// +def SDTUnOpRF : SDTypeProfile<2, 1, [SDTCisInt<0>, + SDTCisFlag<1>, + SDTCisSameAs<2, 0>]>; +def SDTUnOpRFF : SDTypeProfile<2, 2, [SDTCisInt<0>, + SDTCisFlag<1>, + SDTCisSameAs<2, 0>, + SDTCisFlag<3>]>; +def SDTBinOpRF : SDTypeProfile<2, 2, [SDTCisInt<0>, + SDTCisFlag<1>, + SDTCisSameAs<2, 0>, + SDTCisSameAs<3, 0>]>; +def SDTBinOpRFF : SDTypeProfile<2, 3, [SDTCisInt<0>, + SDTCisFlag<1>, + SDTCisSameAs<2, 0>, + SDTCisSameAs<3, 0>, + SDTCisFlag<4>]>; +def SDTBinOpF : SDTypeProfile<1, 2, [SDTCisFlag<0>, + SDTCisInt<1>, + SDTCisSameAs<2, 1>]>; +def SDTBitOpF : SDTypeProfile<1, 2, [SDTCisFlag<0>, + SDTCisI8<1>, + SDTCisI8<2>]>; +def SDTBitOpR : SDTypeProfile<1, 2, [SDTCisI8<0>, + SDTCisI8<1>, + SDTCisI8<2>]>; + +def SDTZ80Wrapper : SDTypeProfile<1, 1, [SDTCisPtrTy<0>, + SDTCisSameAs<1, 0>]>; +def SDT_Z80mlt : SDTypeProfile<1, 1, [SDTCisI16<0>, SDTCisI16<1>]>; +def SDT_Z80sext : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisFlag<1>]>; +def SDT_Z80TCRet : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>; +def SDT_Z80Call : SDTypeProfile<0, -1, [SDTCisPtr<0>]>; +def SDT_Z80BrCond : SDTypeProfile<0, 3, [SDTCisChain<0>, + SDTCisI8<1>, + SDTCisFlag<2>]>; +def SDT_Z80Select : SDTypeProfile<1, 4, [SDTCisInt<0>, + SDTCisSameAs<1, 0>, + SDTCisSameAs<2, 0>, + SDTCisI8<3>, + SDTCisI8<4>]>; +def SDT_Z80Pop : SDTypeProfile<1, 0, [SDTCisPtr<0>]>; +def SDT_Z80Push : SDTypeProfile<0, 1, [SDTCisPtr<0>]>; + +//===----------------------------------------------------------------------===// +// Z80 specific DAG Nodes. +// + +def Z80Wrapper : SDNode<"Z80ISD::Wrapper", SDTZ80Wrapper>; +def Z80rlc_flag : SDNode<"Z80ISD::RLC", SDTUnOpRF>; +def Z80rrc_flag : SDNode<"Z80ISD::RRC", SDTUnOpRF>; +def Z80rl_flag : SDNode<"Z80ISD::RL", SDTUnOpRFF>; +def Z80rr_flag : SDNode<"Z80ISD::RR", SDTUnOpRFF>; +def Z80sla_flag : SDNode<"Z80ISD::SLA", SDTUnOpRF>; +def Z80sra_flag : SDNode<"Z80ISD::SRA", SDTUnOpRF>; +def Z80srl_flag : SDNode<"Z80ISD::SRL", SDTUnOpRF>; +def Z80bit_flag : SDNode<"Z80ISD::BIT", SDTBitOpF>; +def Z80res_flag : SDNode<"Z80ISD::RES", SDTBitOpR>; +def Z80set_flag : SDNode<"Z80ISD::SET", SDTBitOpR>; +def Z80inc_flag : SDNode<"Z80ISD::INC", SDTUnOpRF>; +def Z80dec_flag : SDNode<"Z80ISD::DEC", SDTUnOpRF>; +def Z80add_flag : SDNode<"Z80ISD::ADD", SDTBinOpRF, [SDNPCommutative]>; +def Z80adc_flag : SDNode<"Z80ISD::ADC", SDTBinOpRFF>; +def Z80sub_flag : SDNode<"Z80ISD::SUB", SDTBinOpRF>; +def Z80sbc_flag : SDNode<"Z80ISD::SBC", SDTBinOpRFF>; +def Z80and_flag : SDNode<"Z80ISD::AND", SDTBinOpRF, [SDNPCommutative]>; +def Z80xor_flag : SDNode<"Z80ISD::XOR", SDTBinOpRF, [SDNPCommutative]>; +def Z80or_flag : SDNode<"Z80ISD::OR", SDTBinOpRF, [SDNPCommutative]>; +def Z80cp_flag : SDNode<"Z80ISD::CP", SDTBinOpF>; +def Z80tst_flag : SDNode<"Z80ISD::TST", SDTBinOpF, [SDNPCommutative]>; +def Z80mlt : SDNode<"Z80ISD::MLT", SDT_Z80mlt>; +def Z80sext : SDNode<"Z80ISD::SEXT", SDT_Z80sext>; +def Z80retflag : SDNode<"Z80ISD::RET_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Z80retnflag : SDNode<"Z80ISD::RETN_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Z80retiflag : SDNode<"Z80ISD::RETI_FLAG", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Z80tcret : SDNode<"Z80ISD::TC_RETURN", SDT_Z80TCRet, + [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def Z80call : SDNode<"Z80ISD::CALL", SDT_Z80Call, + [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, + SDNPVariadic]>; +def Z80brcond : SDNode<"Z80ISD::BRCOND", SDT_Z80BrCond, [SDNPHasChain]>; +def Z80select : SDNode<"Z80ISD::SELECT", SDT_Z80Select>; +def Z80pop : SDNode<"Z80ISD::POP", SDT_Z80Pop, + [SDNPHasChain, SDNPMayLoad]>; +def Z80push : SDNode<"Z80ISD::PUSH", SDT_Z80Push, + [SDNPHasChain, SDNPMayStore]>; + +//===----------------------------------------------------------------------===// +// Z80 Instruction Predicate Definitions. +// +def In16BitMode : Predicate<"Subtarget->is16Bit()">, + AssemblerPredicate<(all_of Mode16Bit), "16-bit mode">; +def In24BitMode : Predicate<"Subtarget->is24Bit()">, + AssemblerPredicate<(all_of Mode24Bit), "24-bit mode">; +def HaveUndocOps : Predicate<"Subtarget->hasUndocOps()">, + AssemblerPredicate<(all_of FeatureUndoc), + "undocumented ops">; +def HaveZ180Ops : Predicate<"Subtarget->hasZ180Ops()">, + AssemblerPredicate<(all_of FeatureZ180), "Z180 ops">; +def HaveEZ80Ops : Predicate<"Subtarget->hasEZ80Ops()">, + AssemblerPredicate<(all_of FeatureEZ80), "eZ80 ops">; +def HaveIdxHalf : Predicate<"Subtarget->hasIndexHalfRegs()">, + AssemblerPredicate<(all_of FeatureIdxHalf), + "index half regs">; + +//===----------------------------------------------------------------------===// +// Z80 Instruction Format Definitions. +// + +include "Z80InstrFormats.td" + +//===----------------------------------------------------------------------===// +// Pattern fragments. +// + +// Z80 specific condition code. These correspond to CondCode in +// Z80InstrInfo.h. They must be kept in synch. +def Z80_COND_NZ : PatLeaf<(i8 0)>; +def Z80_COND_Z : PatLeaf<(i8 1)>; +def Z80_COND_NC : PatLeaf<(i8 2)>; +def Z80_COND_C : PatLeaf<(i8 3)>; +def Z80_COND_PO : PatLeaf<(i8 4)>; +def Z80_COND_PE : PatLeaf<(i8 5)>; +def Z80_COND_P : PatLeaf<(i8 6)>; +def Z80_COND_M : PatLeaf<(i8 7)>; + +//===----------------------------------------------------------------------===// +// Z80 Operand Definitions. +// + +def aptr_rc : PointerLikeRegClass<1>; +def iptr_rc : PointerLikeRegClass<2>; + +def mem : Operand { + let PrintMethod = "printMem"; + let MIOperandInfo = (ops imm); + let OperandType = "OPERAND_MEMORY"; +} +def ptr : Operand { + let PrintMethod = "printPtr"; + let MIOperandInfo = (ops aptr_rc); + let OperandType = "OPERAND_MEMORY"; +} +def off : Operand { + let PrintMethod = "printOff"; + let MIOperandInfo = (ops iptr_rc, i8imm); + let OperandType = "OPERAND_MEMORY"; +} +def off16 : Operand { + let PrintMethod = "printAddr"; + let MIOperandInfo = (ops I16, i8imm); +} +def off24 : Operand { + let PrintMethod = "printAddr"; + let MIOperandInfo = (ops I24, i8imm); +} +def bitimm : Operand, PatLeaf<(i8 imm), [{ + return N->getZExtValue() < 8; +}]> { + let PrintMethod = "printBit"; +} + +let OperandType = "OPERAND_IMMEDIATE" in def i24imm : Operand; + +def jmptarget : Operand; +def jmptargetoff : Operand; + +def cc : Operand { + let PrintMethod = "printCCOperand"; +} + +//===----------------------------------------------------------------------===// +// Pattern Fragments. +// +def imm_long_XFORM : SDNodeXFormgetTargetConstant(N->getZExtValue() & 0xffffff, + SDLoc(N), MVT::i24); +}]>; +def imm_top_XFORM : SDNodeXFormgetTargetConstant(N->getZExtValue() >> 24, SDLoc(N), MVT::i8); +}]>; + +//===----------------------------------------------------------------------===// +// Z80 Complex Pattern Definitions. +// +def mempat : ComplexPattern; +def offpat : ComplexPattern; + +def gi_mempat : + GIComplexOperandMatcher, + GIComplexPatternEquiv; +def gi_offpat : + GIComplexOperandMatcher, + GIComplexPatternEquiv; + +//===----------------------------------------------------------------------===// +// Instruction list. +// + +let hasPostISelHook = 1 in { + let Defs = [SPS], Uses = [SPS] in { + def ADJCALLSTACKDOWN16 : P<(outs), + (ins i16imm:$amt1, i16imm:$amt2, i16imm:$amt3)>, + Requires<[In16BitMode]>; + def ADJCALLSTACKUP16 : P<(outs), (ins i16imm:$amt1, i16imm:$amt2)>, + Requires<[In16BitMode]>; + } + let Defs = [SPL], Uses = [SPL] in { + def ADJCALLSTACKDOWN24 : P<(outs), + (ins i24imm:$amt1, i24imm:$amt2, i24imm:$amt3)>, + Requires<[In24BitMode]>; + def ADJCALLSTACKUP24 : P<(outs), (ins i24imm:$amt1, i24imm:$amt2)>, + Requires<[In24BitMode]>; + } +} +let usesCustomInserter = 1 in { + let Uses = [F] in { + def SetCC : P<(outs R8:$dst), (ins i8imm:$cc)>; + def Select8 : P<(outs R8:$dst), (ins R8:$true, R8:$false, i8imm:$cc), + [(set R8:$dst, (Z80select R8:$true, R8:$false, imm:$cc, + F))]>; + def Select16 : P<(outs R16:$dst), (ins R16:$true, R16:$false, i8imm:$cc), + [(set R16:$dst, (Z80select R16:$true, R16:$false, imm:$cc, + F))]>; + def Select24 : P<(outs R24:$dst), (ins R24:$true, R24:$false, i8imm:$cc), + [(set R24:$dst, (Z80select R24:$true, R24:$false, imm:$cc, + F))]>, + Requires<[In24BitMode]>; + } + let Uses = [F] in { + let Defs = [A, F] in + def SExt8 : P<(outs), (ins),[(set A, (Z80sext F))]>; + let Defs = [HL, F] in + def SExt16 : P<(outs), (ins), [(set HL, (Z80sext F))]>; + let Defs = [UHL, F] in + def SExt24 : P<(outs), (ins), [(set UHL, (Z80sext F))]>; + } +} + +let hasSideEffects = 0 in +def NOP : I; +def DI : I; +def EI : I; + +//===----------------------------------------------------------------------===// +// Control Flow Instructions. +// + +// All calls clobber the non-callee saved registers. SP is marked as a use to +// prevent stack-pointer assignments that appear immediately before calls from +// potentially appearing dead. Uses for argument registers are added manually. +let isCall = 1 in { + let Uses = [SPS] in { + def CALL16 : I16i, + Requires<[In16BitMode]>; + def CALL16r : P <(outs), (ins A16:$tgt), [(Z80call A16:$tgt)]>, + Requires<[In16BitMode]>; + } + let Uses = [F, SPS] in + def CALL16CC : I16i; + let Uses = [SPL] in { + def CALL24 : I24i; + def CALL24r : P <(outs), (ins A24:$tgt), [(Z80call A24:$tgt)]>; + } + let Uses = [F, SPL] in + def CALL24CC : I24i; +} + +let isTerminator = 1, isReturn = 1, isBarrier = 1 in { + let Defs = [SPS], Uses = [SPS] in { + def RET16 : I16; + def RETN16 : I16; + def RETI16 : I16; + } + let Defs = [SPS], Uses = [F, SPS] in + def RET16CC : I16; + let Defs = [SPL], Uses = [SPL] in { + def RET24 : I24; + def RETN24 : I24; + def RETI24 : I24; + } + let Defs = [SPL], Uses = [F, SPL] in + def RET24CC : I24; + def EI_RETI : P<(outs), (ins), [(Z80retiflag)]>; +} +let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1 in { + let Uses = [SPS] in { + def TCRETURN16 : P<(outs), (ins i16imm:$tgt), [(Z80tcret mempat:$tgt)]>, + Requires<[In16BitMode]>; + def TCRETURN16r : P<(outs), (ins A16:$tgt), [(Z80tcret A16:$tgt)]>, + Requires<[In16BitMode]>; + } + let Uses = [F, SPS] in + def TCRETURN16CC : P<(outs), (ins i16imm:$tgt, cc:$cc)>, + Requires<[In16BitMode]>; + let Uses = [SPL] in { + def TCRETURN24 : P<(outs), (ins i24imm:$tgt), [(Z80tcret mempat:$tgt)]>, + Requires<[In24BitMode]>; + def TCRETURN24r : P<(outs), (ins A24:$tgt), [(Z80tcret A24:$tgt)]>, + Requires<[In24BitMode]>; + } + let Uses = [F, SPL] in + def TCRETURN24CC : P<(outs), (ins i24imm:$tgt, cc:$cc)>, + Requires<[In24BitMode]>; +} + +let isBranch = 1, isTerminator = 1 in { + let isBarrier = 1 in { + def JQ : Pseudo<"jq", "\t$tgt", "", (outs), (ins jmptarget:$tgt), + [(br bb:$tgt)]>; + def JR : Io ; + def JP16 : I16i; + def JP24 : I24i; + let isIndirectBranch = 1 in { + def JP16r : I16; + def JP24r : I24; + } + } + let Uses = [F] in { + def JQCC : Pseudo<"jq", "\t$cc, $tgt", "", + (outs), (ins jmptarget:$tgt, cc:$cc), + [(Z80brcond bb:$tgt, imm:$cc, F)]>; + def JRCC : Io ; + def JP16CC : I16i; + def JP24CC : I24i; + } +} + +//===----------------------------------------------------------------------===// +// Load Instructions. +// + +def LD8gg : I; +def LD8xx : I, Requires<[HaveIdxHalf]>; +def LD8yy : I, Requires<[HaveIdxHalf]>; + +let isMoveImm = 1, isReMaterializable = 1 in { + let Defs = [F] in { + def LD8r0 : Pseudo<"ld", "\t$dst, 0", "", + (outs R8:$dst), (ins), [(set R8:$dst, 0)]>; + def LD24r0 : Pseudo<"ld", "\t$dst, 0", "", + (outs R24:$dst), (ins), [(set R24:$dst, 0)]>; + def LD24r_1 : Pseudo<"ld", "\t$dst, -1", "", + (outs R24:$dst), (ins), [(set R24:$dst, -1)]>; + } + def LD8ri : I8i ; + def LD16ri : I16i; + def LD24ri : I24i; +} + +let mayLoad = 1, canFoldAsLoad = 1, isReMaterializable = 1 in { + let Defs = [A] in + def LD8am : I8i ; + def LD16rm : Pseudo< "ld", "\t$dst, $src", "", + (outs R16:$dst), (ins mem:$src), + [(set R16:$dst, (load mempat:$src))]>; + def LD24rm : Pseudo< "ld", "\t$dst, $src", "", + (outs R24:$dst), (ins mem:$src), + [(set R24:$dst, (load mempat:$src))]>; + def LD16am : I16i ; + def LD24am : I24i ; + def LD16om : I16i ; + def LD24om : I24i ; + + def LD8rp : Pseudo< "ld", "\t$dst, $src", "", + (outs R8:$dst), (ins ptr:$src)>; + def LD8gp : I ; + def LD16rp : I16 , + Requires<[HaveEZ80Ops]>; + def LD88rp : Pseudo< "ld", "\t$dst, $src", "", + (outs R16:$dst), (ins ptr:$src), + [(set R16:$dst, (load iPTR:$src))]>; + def LD24rp : I24 ; + + def LD8ro : Pseudo< "ld", "\t$dst, $src", "", + (outs R8:$dst), (ins off:$src)>; + def LD8go : Io ; + def LD16ro : I16o , + Requires<[HaveEZ80Ops]>; + def LD88ro : Pseudo< "ld", "\t$dst, $src", "", + (outs R16:$dst), (ins off:$src), + [(set R16:$dst, (load offpat:$src))]>; + def LD24ro : I24o ; +} +def : Pat<(i16 (extloadi8 mempat:$src)), (LD16rm mem:$src)>; +def : Pat<(i16 (extloadi8 iPTR:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), (LD8rp ptr:$src), sub_low)>; +def : Pat<(i16 (extloadi8 offpat:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), (LD8ro off:$src), sub_low)>; +def : Pat<(i24 (extloadi8 mempat:$src)), (LD24rm mem:$src)>; +def : Pat<(i24 (extloadi8 iPTR:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), (LD8rp ptr:$src), sub_low)>; +def : Pat<(i24 (extloadi8 offpat:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), (LD8ro off:$src), sub_low)>; +def : Pat<(i24 (extloadi16 mempat:$src)), (LD24rm mem:$src)>; +def : Pat<(i24 (extloadi16 iPTR:$src)), (LD24rp ptr:$src)>; +def : Pat<(i24 (extloadi16 offpat:$src)), (LD24ro off:$src)>; + +let mayStore = 1 in { + let Uses = [A] in + def LD8ma : I8i < NoPre, 0x32, "ld", "\t$dst, a", "", + (outs), (ins mem:$dst), [(store A, mempat:$dst)]>; + def LD16mr : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins mem:$dst, R16:$src), + [(store R16:$src, mempat:$dst)]>; + def LD24mr : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins mem:$dst, R24:$src), + [(store R24:$src, mempat:$dst)]>; + def LD16ma : I16i ; + def LD24ma : I24i ; + def LD16mo : I16i < EDPre, 0x43, "ld", "\t$dst, $src", "", + (outs), (ins mem:$dst, O16:$src)>; + def LD24mo : I24i < EDPre, 0x43, "ld", "\t$dst, $src", "", + (outs), (ins mem:$dst, O24:$src)>; + + def LD8pr : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, R8:$src)>; + def LD8pg : I < NoPre, 0x70, "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, G8:$src), + [(store G8:$src, iPTR:$dst)]>; + def LD16pr : I16 < EDPre, 0x0F, "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, R16:$src), + [(store R16:$src, iPTR:$dst)]>, + Requires<[In16BitMode, HaveEZ80Ops]>; + def LD88pr : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, R16:$src), + [(store R16:$src, iPTR:$dst)]>; + def LD24pr : I24 < EDPre, 0x0F, "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, R24:$src), + [(store R24:$src, iPTR:$dst)]>; + + def LD8or : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins off:$dst, R8:$src)>; + def LD8og : Io ; + def LD16or : I16o , + Requires<[HaveEZ80Ops]>; +} +// LD16/24or encodes R16/24:$src, but there aren't enough index registers for +// regalloc to use it, since one of the index registers is reserved as the frame +// pointer and the other is used in offpat:$dst, so just pattern match on +// G16/24:$src. +def : Pat<(store G16:$src, offpat:$dst), (LD16or off:$dst, R16:$src)>, + Requires<[In16BitMode, HaveEZ80Ops]>; +let mayStore = 1 in { + def LD88or : Pseudo< "ld", "\t$dst, $src", "", + (outs), (ins off:$dst, R16:$src), + [(store R16:$src, offpat:$dst)]>; + def LD24or : I24o ; +} +// Same as above. +def : Pat<(store G24:$src, offpat:$dst), (LD24or off:$dst, R24:$src)>; + +let mayStore = 1 in { + def LD8pi : I8oi < NoPre, 0x36, "ld", "\t$dst, $src", "", + (outs), (ins ptr:$dst, i8imm:$src), + [(store (i8 imm:$src), iPTR:$dst)]>; + def LD8oi : I8oi ; +} + +let Defs = [SPS] in +def LD16SP : I16; +let Defs = [SPL] in +def LD24SP : I24; + +def EXAF : I; +def EXX : I; + +let Defs = [DE, HL], Uses = [DE, HL] in +def EX16DE : I16; +let Defs = [UDE, UHL], Uses = [UDE, UHL] in +def EX24DE : I24; + +let Constraints = "$imp = $dst" in { +let Uses = [SPS] in +def EX16SP : I16; +let Uses = [SPL] in +def EX24SP : I24; +} + +let Defs = [SPS], Uses = [SPS] in { + let mayLoad = 1 in + def POP16r : I16 , + Requires<[In16BitMode]>; + let mayStore = 1 in { + def PUSH16r : I16 , + Requires<[In16BitMode]>; + def PEA16o : I16o, + Requires<[In16BitMode, HaveEZ80Ops]>; + } +} +let Defs = [AF, SPS], Uses = [SPS], mayLoad = 1 in +def POP16AF : I16, + Requires<[In16BitMode]>; +let Defs = [SPS], Uses = [AF, SPS], mayStore = 1 in +def PUSH16AF : I16, + Requires<[In16BitMode]>; +let Defs = [SPL], Uses = [SPL] in { + let mayLoad = 1 in + def POP24r : I24 ; + let mayStore = 1 in { + def PUSH24r : I24 ; + def PEA24o : I24o; + } +} +let Defs = [AF, SPL], Uses = [SPL], mayLoad = 1 in +def POP24AF : I24; +let Defs = [SPL], Uses = [AF, SPL], mayStore = 1 in +def PUSH24AF : I24; + +let isReMaterializable = 1, Defs = [F] in { + def RCF : P; + def SCF : I; + def CCF : I; +} + +//===----------------------------------------------------------------------===// +// Arithmetic Instructions. +// + +let Defs = [A, F], Uses = [A] in { + def CPL : I; + def NEG : I; + def RLCA : I; + def RRCA : I; +} + +let Defs = [A, F], Uses = [A, F] in { + def RLA : I; + def RRA : I; +} + +let Defs = [F] in +multiclass UnOp8RF opcode, string mnemonic, + Z80RC8 rc8 = !if(!eq(prefix.Value, CBPre.Value), G8, R8)> { + def 8r : I (!strconcat("Z80", mnemonic, "_flag")) + rc8:$imp))]>; + let mayLoad = 1, mayStore = 1 in { + def 8p : I (!strconcat("Z80", mnemonic, "_flag")) + (i8 (load iPTR:$adr))), iPTR:$adr), + (implicit F)]>; + def 8o : Io(!strconcat("Z80", mnemonic, "_flag")) + (i8 (load offpat:$adr))), offpat:$adr), + (implicit F)]>; + } +} +let Defs = [F], Uses = [F] in +multiclass UnOp8RFF opcode, string mnemonic, + Z80RC8 rc8 = !if(!eq(prefix.Value, CBPre.Value), G8, R8)> { + def 8r : I (!strconcat("Z80", mnemonic, "_flag")) + rc8:$imp, F))]>; + let mayLoad = 1, mayStore = 1 in { + def 8p : I (!strconcat("Z80", mnemonic, "_flag")) + (i8 (load iPTR:$adr)), F), iPTR:$adr), + (implicit F)]>; + def 8o : Io(!strconcat("Z80", mnemonic, "_flag")) + (i8 (load offpat:$adr)), F), offpat:$adr), + (implicit F)]>; + } +} +multiclass BinOp8RF opcode, string mnemonic, + bit compare = 0> { + let isCompare = compare, Defs = [A, F], Uses = [A] in { + def 8ar : I (!strconcat("Z80", mnemonic, "_flag")) + A, R8:$src))]>; + def 8ai : Ii(!strconcat("Z80", mnemonic, "_flag")) + A, imm:$src))]>; + let mayLoad = 1 in { + def 8ap : I (!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load iPTR:$src))))]>; + def 8ao : Io(!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load offpat:$src))))]>; + } + } + def : Pat<(!cast(mnemonic) A, R8:$src), + (!cast(!strconcat(NAME, "8ar")) R8:$src)>; + def : Pat<(!cast(mnemonic) A, imm:$src), + (!cast(!strconcat(NAME, "8ai")) imm:$src)>; + def : Pat<(!cast(mnemonic) A, (load iPTR:$src)), + (!cast(!strconcat(NAME, "8ap")) ptr:$src)>; + def : Pat<(!cast(mnemonic) A, (load offpat:$src)), + (!cast(!strconcat(NAME, "8ao")) off:$src)>; +} +multiclass BinOp8RFF opcode, string mnemonic, + SDNode node, bit compare = 0> { + let isCompare = compare, Defs = [A, F], Uses = [A, F] in { + def 8ar : I (!strconcat("Z80", mnemonic, "_flag")) + A, R8:$src, F))]>; + def 8ai : Ii(!strconcat("Z80", mnemonic, "_flag")) + A, imm:$src, F))]>; + let mayLoad = 1 in { + def 8ap : I (!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load iPTR:$src)), F))]>; + def 8ao : Io(!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load offpat:$src)), F))]>; + } + } + def : Pat<(node A, R8:$src), + (!cast(!strconcat(NAME, "8ar")) R8:$src)>; + def : Pat<(node A, imm:$src), + (!cast(!strconcat(NAME, "8ai")) imm:$src)>; + def : Pat<(node A, (load iPTR:$src)), + (!cast(!strconcat(NAME, "8ap")) ptr:$src)>; + def : Pat<(node A, (load offpat:$src)), + (!cast(!strconcat(NAME, "8ao")) off:$src)>; +} +multiclass BinOp8F opcode, string mnemonic, + bit compare = 0> { + let isCompare = compare, Defs = [F], Uses = [A] in { + def 8ar : I (!strconcat("Z80", mnemonic, "_flag")) + A, R8:$src))]>; + def 8ai : Ii(!strconcat("Z80", mnemonic, "_flag")) + A, imm:$src))]>; + let mayLoad = 1 in { + def 8ap : I (!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load iPTR:$src))))]>; + def 8ao : Io(!strconcat("Z80", mnemonic, "_flag")) + A, (i8 (load offpat:$src))))]>; + } + } +} +let Defs = [F] in +multiclass BitOp8F opcode, string mnemonic> { + def 8bg : I < CBPre, {opcode, 0b000, 0b000}, mnemonic, "\t$bit, $src", "", + (outs), (ins bitimm:$bit, G8:$src), + [(set F, (!cast(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, G8:$src))]>; + let mayLoad = 1 in { + def 8bp : I < CBPre, {opcode, 0b000, 0b110}, mnemonic, "\t$bit, $adr", "", + (outs), (ins bitimm:$bit, ptr:$adr), + [(set F, (!cast(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, (i8 (load iPTR:$adr))))]>; + def 8bo : Io(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, (i8 (load offpat:$adr))))]>; + } +} +multiclass BitOp8R opcode, string mnemonic> { + def 8bg : I < CBPre, {opcode, 0b000, 0b000}, mnemonic, "\t$bit, $dst", + "$imp = $dst", (outs G8:$dst), (ins bitimm:$bit, G8:$imp), + [(set G8:$dst, + (!cast(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, G8:$imp))]>; + let mayLoad = 1, mayStore = 1 in { + def 8bp : I < CBPre, {opcode, 0b000, 0b110}, mnemonic, "\t$bit, $adr", "", + (outs), (ins bitimm:$bit, ptr:$adr), + [(store (!cast(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, (i8 (load iPTR:$adr))), + iPTR:$adr), + (implicit F)]>; + def 8bo : Io(!strconcat("Z80", mnemonic, "_flag")) + bitimm:$bit, (i8 (load offpat:$adr))), + offpat:$adr), + (implicit F)]>; + } +} + +defm RLC : UnOp8RF ; +defm RRC : UnOp8RF ; +defm RL : UnOp8RFF ; +defm RR : UnOp8RFF ; +defm SLA : UnOp8RF ; +defm SRA : UnOp8RF ; +defm SRL : UnOp8RF ; +defm BIT : BitOp8F < 1, "bit">; +defm RES : BitOp8R < 2, "res">; +defm SET : BitOp8R < 3, "set">; +defm INC : UnOp8RF ; +defm DEC : UnOp8RF ; +defm ADD : BinOp8RF ; +defm ADC : BinOp8RFF; +defm SUB : BinOp8RF ; +defm SBC : BinOp8RFF; +defm AND : BinOp8RF ; +defm XOR : BinOp8RF ; +defm OR : BinOp8RF ; +defm CP : BinOp8F ; +defm TST : BinOp8F , + Requires<[HaveEZ80Ops]>; + +def : Pat<(fshl G8:$reg, G8:$reg, (i8 1)), (RLC8r G8:$reg)>; +def : Pat<(fshl G8:$reg, G8:$reg, (i8 7)), (RRC8r G8:$reg)>; +def : Pat<(shl G8:$reg, (i8 1)), (SLA8r G8:$reg)>; +def : Pat<(sra G8:$reg, (i8 1)), (SRA8r G8:$reg)>; +def : Pat<(srl G8:$reg, (i8 1)), (SRL8r G8:$reg)>; +def : Pat<(add R8:$reg, (i8 1)), (INC8r R8:$reg)>; +def : Pat<(add R8:$reg, (i8 -1)), (DEC8r R8:$reg)>; + +def INC16r : I16; +def DEC16r : I16; +def INC24r : I24; +def DEC24r : I24; +let Defs = [SPS], Uses = [SPS] in { +def INC16SP : I16; +def DEC16SP : I16; +} +let Defs = [SPL], Uses = [SPL] in { +def INC24SP : I24; +def DEC24SP : I24; +} +def : Pat<(add R16:$imp, 1), (INC16r R16:$imp)>; +def : Pat<(add R16:$imp, -1), (DEC16r R16:$imp)>; +def : Pat<(add R24:$imp, 1), (INC24r R24:$imp)>; +def : Pat<(add R24:$imp, -1), (DEC24r R24:$imp)>; + +let Defs = [F] in { + let AddedComplexity = 1 in { + def ADD16aa : I16; + def ADD24aa : I24; + } + def ADD16ao : I16; + def ADD24ao : I24; + let Uses = [SPS] in + def ADD16SP : I16; + let Uses = [SPL] in + def ADD24SP : I24; +} +let AddedComplexity = 1 in { + def : Pat<(add A16:$imp, A16:$imp), (ADD16aa A16:$imp)>; + def : Pat<(add A24:$imp, A24:$imp), (ADD24aa A24:$imp)>; + def : Pat<(shl A16:$reg, (i8 1)), (ADD16aa A16:$reg)>; + def : Pat<(shl A24:$reg, (i8 1)), (ADD24aa A24:$reg)>; + def : Pat<(addc A16:$imp, A16:$imp), (ADD16aa A16:$imp)>; + def : Pat<(addc A24:$imp, A24:$imp), (ADD24aa A24:$imp)>; +} +def : Pat<(add A16:$imp, O16:$src), (ADD16ao A16:$imp, O16:$src)>; +def : Pat<(add A24:$imp, O24:$src), (ADD24ao A24:$imp, O24:$src)>; +def : Pat<(addc A16:$imp, O16:$src), (ADD16ao A16:$imp, O16:$src)>; +def : Pat<(addc A24:$imp, O24:$src), (ADD24ao A24:$imp, O24:$src)>; + +let Defs = [HL, F] in { + let Uses = [HL, F] in { + def SBC16aa : I16; + def ADC16aa : I16; + def SBC16ao : I16; + def ADC16ao : I16; + } + let Uses = [HL, SPS, F] in { + def SBC16SP : I16; + def ADC16SP : I16; + } +} +let Defs = [UHL, F] in { + let Uses = [UHL, F] in { + def SBC24aa : I24; + def ADC24aa : I24; + def SBC24ao : I24; + def ADC24ao : I24; + } + let Uses = [UHL, SPL, F] in { + def SBC24SP : I24; + def ADC24SP : I24; + } +} +def : Pat<(sube HL, O16:$src), (SBC16ao O16:$src)>; +def : Pat<(adde HL, O16:$src), (ADC16ao O16:$src)>; +def : Pat<(sube UHL, O24:$src), (SBC24ao O24:$src)>; +def : Pat<(adde UHL, O24:$src), (ADC24ao O24:$src)>; + +let Defs = [HL, F], Uses = [HL] in { + def SUB16ao : P<(outs), (ins O16:$src), + [(set HL, F, (Z80sub_flag HL, O16:$src))]>; +} +let Defs = [UHL, F], Uses = [UHL] in { + def SUB24ao : P<(outs), (ins O24:$src), + [(set UHL, F, (Z80sub_flag UHL, O24:$src))]>, + Requires<[HaveEZ80Ops]>; +} +let Defs = [F] in { + let Uses = [HL] in { + def CP16a0 : P<(outs), (ins), [(set F, (Z80cp_flag HL, 0))]>; + def CP16ao : P<(outs), (ins O16:$src), + [(set F, (Z80cp_flag HL, O16:$src))]>; + } + let Uses = [UHL] in { + def CP24a0 : P<(outs), (ins), [(set F, (Z80cp_flag UHL, 0))]>, + Requires<[HaveEZ80Ops]>; + def CP24ao : P<(outs), (ins O24:$src), + [(set F, (Z80cp_flag UHL, O24:$src))]>, + Requires<[HaveEZ80Ops]>; + } +} +def : Pat<(sub HL, O16:$src), (SUB16ao O16:$src)>; +def : Pat<(sub UHL, O24:$src), (SUB24ao O24:$src)>; +def : Pat<(subc HL, O16:$src), (SUB16ao O16:$src)>; +def : Pat<(subc UHL, O24:$src), (SUB24ao O24:$src)>; + +def LEA16ro : I16o, Requires<[HaveEZ80Ops]>; +def LEA24ro : I24o; + +def MLT8rr : I, Requires<[HaveZ180Ops]>; +def : Pat<(i8 (mul R8:$op1, R8:$op2)), + (EXTRACT_SUBREG (MLT8rr (REG_SEQUENCE G16, R8:$op1, sub_high, + R8:$op2, sub_low)), + sub_low)>; +def : Pat<(i16 (mul (zext R8:$op1), (zext R8:$op2))), + (MLT8rr (REG_SEQUENCE G16, R8:$op1, sub_high, R8:$op2, sub_low))>; + +//===----------------------------------------------------------------------===// +// Block Instructions. +//===----------------------------------------------------------------------===// +multiclass Block16 opcode, string mnemonic, + string mnemonicr = mnemonic> { + def I16 : I16; + def D16 : I16; + def IR16 : I16; + def DR16 : I16; +} +multiclass Block24 opcode, string mnemonic, + string mnemonicr = mnemonic> { + def I24 : I24; + def D24 : I24; + def IR24 : I24; + def DR24 : I24; +} + +let Defs = [DE, HL, BC, F], Uses = [DE, HL, BC], mayLoad = 1, mayStore = 1 in +defm LD : Block16<0, "ld">; +let Defs = [DE, HL, BC, F], Uses = [DE, HL, BC], mayLoad = 1 in +defm CP : Block16<1, "cp">; +let Defs = [HL, C, B, F], Uses = [HL, C, B] in { + let mayStore = 1 in defm IN : Block16<2, "in">; + let mayLoad = 1 in defm OUT : Block16<3, "out", "ot">; +} + +let Defs = [UDE, UHL, UBC, F], Uses = [UDE, UHL, UBC], + mayLoad = 1, mayStore = 1 in +defm LD : Block24<0, "ld">; +let Defs = [UHL, UBC, F], Uses = [UHL, UBC, A], mayLoad = 1 in +defm CP : Block24<1, "cp">; +let Defs = [UHL, C, B, F], Uses = [UHL, C, B] in { + let mayStore = 1 in defm IN : Block24<2, "in">; + let mayLoad = 1 in defm OUT : Block24<3, "out", "ot">; +} + +let usesCustomInserter = 1, Defs = [F], Uses = [F], + mayLoad = 1, mayStore = 1 in { + def LDR16 : P<(outs), (ins R16: $de, R16: $hl, R16: $bc)>; + def LDR24 : P<(outs), (ins R24:$ude, R24:$uhl, R24:$ubc)>, + Requires<[HaveEZ80Ops]>; +} + +//===----------------------------------------------------------------------===// +// Non-Instruction Patterns. +//===----------------------------------------------------------------------===// + +// addresses +def : Pat<(i16 (Z80Wrapper tglobaladdr :$src)), (LD16ri tglobaladdr :$src)>; +def : Pat<(i24 (Z80Wrapper tglobaladdr :$src)), (LD24ri tglobaladdr :$src)>; +def : Pat<(i16 (Z80Wrapper texternalsym :$src)), (LD16ri texternalsym :$src)>; +def : Pat<(i24 (Z80Wrapper texternalsym :$src)), (LD24ri texternalsym :$src)>; +def : Pat<(i16 (Z80Wrapper tblockaddress:$src)), (LD16ri tblockaddress:$src)>; +def : Pat<(i24 (Z80Wrapper tblockaddress:$src)), (LD24ri tblockaddress:$src)>; + +// calls +def : Pat<(Z80call (tglobaladdr :$dst)), (CALL16 tglobaladdr :$dst)>, + Requires<[In16BitMode]>; +def : Pat<(Z80call (tglobaladdr :$dst)), (CALL24 tglobaladdr :$dst)>, + Requires<[In24BitMode]>; +def : Pat<(Z80call (texternalsym:$dst)), (CALL16 texternalsym:$dst)>, + Requires<[In16BitMode]>; +def : Pat<(Z80call (texternalsym:$dst)), (CALL24 texternalsym:$dst)>, + Requires<[In24BitMode]>; + +def : Pat<(Z80tcret (tglobaladdr :$dst)), (TCRETURN16 tglobaladdr :$dst)>, + Requires<[In16BitMode]>; +def : Pat<(Z80tcret (tglobaladdr :$dst)), (TCRETURN24 tglobaladdr :$dst)>, + Requires<[In24BitMode]>; +def : Pat<(Z80tcret (texternalsym:$dst)), (TCRETURN16 texternalsym:$dst)>, + Requires<[In16BitMode]>; +def : Pat<(Z80tcret (texternalsym:$dst)), (TCRETURN24 texternalsym:$dst)>, + Requires<[In24BitMode]>; + +//===----------------------------------------------------------------------===// +// Subsystems. +//===----------------------------------------------------------------------===// + +// anyext +def : Pat<(i16 (anyext R8 :$src)), + (INSERT_SUBREG (IMPLICIT_DEF), R8 :$src, sub_low)>; +def : Pat<(i24 (anyext R8 :$src)), + (INSERT_SUBREG (IMPLICIT_DEF), R8 :$src, sub_low)>; +def : Pat<(i24 (anyext R16:$src)), + (INSERT_SUBREG (IMPLICIT_DEF), R16:$src, sub_short)>; + +// zext +def : Pat<(i16 (zext R8 :$src)), + (REG_SEQUENCE R16, (LD8r0), sub_high, R8:$src, sub_low)>; +def : Pat<(i24 (zext R8 :$src)), + (INSERT_SUBREG (i24 (LD24r0)), R8 :$src, sub_low)>; +def : Pat<(i24 (zext R16:$src)), + (INSERT_SUBREG (i24 (LD24r0)), R16:$src, sub_short)>; + +// trunc +def : Pat<(i8 (trunc R16:$src)), (EXTRACT_SUBREG R16:$src, sub_low)>; +def : Pat<(i8 (trunc R24:$src)), (EXTRACT_SUBREG R24:$src, sub_low)>; +def : Pat<(i16 (trunc R24:$src)), (EXTRACT_SUBREG R24:$src, sub_short)>; diff --git a/llvm/lib/Target/Z80/Z80MCInstLower.cpp b/llvm/lib/Target/Z80/Z80MCInstLower.cpp new file mode 100644 index 00000000000000..083a6b70118733 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80MCInstLower.cpp @@ -0,0 +1,114 @@ +//===-- Z80MCInstLower.cpp - Convert Z80 MachineInstr to an MCInst --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains code to lower Z80 MachineInstrs to their corresponding +// MCInst records. +// +//===----------------------------------------------------------------------===// + +#include "Z80AsmPrinter.h" +#include "llvm/IR/Mangler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/Support/Debug.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-mclower" + +namespace { +/// Z80MCInstLower - This class is used to lower a MachineInstr into an MCInst. +class Z80MCInstLower { + MCContext &Ctx; + Z80AsmPrinter &AsmPrinter; + +public: + Z80MCInstLower(const MachineFunction &MF, Z80AsmPrinter &AP); + + Optional LowerMachineOperand(const MachineInstr *MI, + const MachineOperand &MO) const; + + MCSymbol *GetGlobalAddressSymbol(const MachineOperand &MO) const; + MCSymbol *GetExternalSymbolSymbol(const MachineOperand &MO) const; + MCOperand LowerSymbolOperand(const MachineOperand &MO, MCSymbol *Sym) const; + + void Lower(const MachineInstr *MI, MCInst &OutMI) const; + }; +} + +Z80MCInstLower::Z80MCInstLower(const MachineFunction &MF, Z80AsmPrinter &AP) + : Ctx(MF.getContext()), AsmPrinter(AP) {} + +/// GetGlobalAddressSymbol - Lower an MO_GlobalAddress operand to an MCSymbol. +MCSymbol * +Z80MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { + assert(!MO.getTargetFlags() && "Unknown target flag on GV operand"); + return AsmPrinter.getSymbol(MO.getGlobal()); +} + +/// GetExternalSymbolSymbol - Lower an MO_ExternalSymbol operand to an MCSymbol. +MCSymbol * +Z80MCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const { + assert(!MO.getTargetFlags() && "Unknown target flag on GV operand"); + return AsmPrinter.GetExternalSymbolSymbol(MO.getSymbolName()); +} + +MCOperand Z80MCInstLower::LowerSymbolOperand(const MachineOperand &MO, + MCSymbol *Sym) const { + assert(!MO.getTargetFlags() && "Unknown target flag on GV operand"); + const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); + if (!MO.isJTI() && MO.getOffset()) + Expr = MCBinaryExpr::createAdd( + Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); + return MCOperand::createExpr(Expr); +} + +Optional +Z80MCInstLower::LowerMachineOperand(const MachineInstr *MI, + const MachineOperand &MO) const { + switch (MO.getType()) { + default: + LLVM_DEBUG(MI->dump()); + llvm_unreachable("unknown operand type"); + case MachineOperand::MO_Register: + return MCOperand::createReg(MO.getReg()); + case MachineOperand::MO_Immediate: + return MCOperand::createImm(MO.getImm()); + case MachineOperand::MO_CImmediate: + return MCOperand::createImm(MO.getCImm()->getSExtValue()); + case MachineOperand::MO_MachineBasicBlock: + return MCOperand::createExpr(MCSymbolRefExpr::create( + MO.getMBB()->getSymbol(), Ctx)); + case MachineOperand::MO_GlobalAddress: + return LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); + case MachineOperand::MO_ExternalSymbol: + return LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); + case MachineOperand::MO_JumpTableIndex: + return LowerSymbolOperand(MO, AsmPrinter.GetJTISymbol(MO.getIndex())); + case MachineOperand::MO_RegisterMask: + return None; // Ignore call clobbers. + } +} + +void Z80MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const { + OutMI.setOpcode(MI->getOpcode()); + + MCOperand MCOp; + for (const MachineOperand &MO : MI->operands()) + if (auto PossibleMCOp = LowerMachineOperand(MI, MO)) + OutMI.addOperand(*PossibleMCOp); +} + +void Z80AsmPrinter::emitInstruction(const MachineInstr *MI) { + Z80MCInstLower MCInstLowering(*MF, *this); + + MCInst TmpInst; + MCInstLowering.Lower(MI, TmpInst); + EmitToStreamer(*OutStreamer, TmpInst); +} diff --git a/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h b/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h new file mode 100644 index 00000000000000..8179e013929188 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80MachineFunctionInfo.h @@ -0,0 +1,78 @@ +//===-- Z80MachineFunctionInfo.h - Z80 machine function info ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares Z80-specific per-machine-function information. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80MACHINEFUNCTIONINFO_H +#define LLVM_LIB_TARGET_Z80_Z80MACHINEFUNCTIONINFO_H + +#include "llvm/CodeGen/MachineFunction.h" + +namespace llvm { + +/// Z80MachineFunctionInfo - This class is derived from MachineFunction and +/// contains private Z80 target-specific information for each MachineFunction. +class Z80MachineFunctionInfo : public MachineFunctionInfo { +public: + enum AltFPMode { AFPM_None = 0, AFPM_Partial, AFPM_Full }; + +private: + /// Number of bytes of arguments this function has on the stack. All usable + /// during a tail call. + unsigned ArgFrameSize = 0; + + /// CalleeSavedFrameSize - Size of the callee-saved register portion of the + /// stack frame in bytes. + unsigned CalleeSavedFrameSize = 0; + + /// VarArgsFrameIndex - FrameIndex for start of varargs area. + int VarArgsFrameIndex = 0; + + /// SRetReturnReg - Some subtargets require that sret lowering includes + /// returning the value of the returned struct in a register. This field + /// holds the virtual register into which the sret argument is passed. + Register SRetReturnReg = 0; + + /// HasIllegalLEA - We use LEA to materialize a frame index address even if it + /// is illegal. Remember when if we do, to ensure a scavenging frame index is + /// created. + bool HasIllegalLEA = false; + + /// UsesAltFP - We use an alternate frame pointer as an optimization. + AltFPMode UsesAltFP = AFPM_None; + +public: + Z80MachineFunctionInfo() = default; + + explicit Z80MachineFunctionInfo(MachineFunction &MF) {} + + unsigned getArgFrameSize() const { return ArgFrameSize; } + void setArgFrameSize(unsigned Bytes) { ArgFrameSize = Bytes; } + + unsigned getCalleeSavedFrameSize() const { return CalleeSavedFrameSize; } + void setCalleeSavedFrameSize(unsigned Bytes) { CalleeSavedFrameSize = Bytes; } + + int getVarArgsFrameIndex() const { return VarArgsFrameIndex; } + void setVarArgsFrameIndex(int Idx) { VarArgsFrameIndex = Idx; } + + Register getSRetReturnReg() const { return SRetReturnReg; } + void setSRetReturnReg(Register Reg) { SRetReturnReg = Reg; } + + bool getHasIllegalLEA() const { return HasIllegalLEA; } + void setHasIllegalLEA(bool V = true) { HasIllegalLEA = V; } + + AltFPMode getUsesAltFP() const { return UsesAltFP; } + void setUsesAltFP(AltFPMode V) { UsesAltFP = V; } +}; + +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80MachineLateOptimization.cpp b/llvm/lib/Target/Z80/Z80MachineLateOptimization.cpp new file mode 100644 index 00000000000000..aa3993aa0eb8b2 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80MachineLateOptimization.cpp @@ -0,0 +1,286 @@ +//=== lib/CodeGen/GlobalISel/Z80PreLegalizerCombiner.cpp ------------------===// +// +// 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 pass does combining of machine instructions at the generic MI level, +// before the legalizer. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "llvm/CodeGen/LiveRegUnits.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/IR/Constants.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "z80-machine-late-opt" + +using namespace llvm; + +namespace { +class ImmVal { + static constexpr unsigned UnknownOff = ~0u; + const GlobalValue *GV = nullptr; + unsigned Off = UnknownOff, Mask = 0; + MachineInstr *KilledBy = nullptr; + +public: + ImmVal() {} + ImmVal(MCRegister Reg, const TargetRegisterInfo &TRI) { + assert(Reg.isPhysical() && "Expected physical register"); + if (auto *RC = TRI.getMinimalPhysRegClass(Reg)) + Mask = maskTrailingOnes(TRI.getRegSizeInBits(*RC)); + } + ImmVal(MachineOperand &MO, MCRegister Reg, const TargetRegisterInfo &TRI) + : ImmVal(Reg, TRI) { + switch (MO.getType()) { + case MachineOperand::MO_Immediate: + Off = MO.getImm(); + break; + case MachineOperand::MO_CImmediate: + Off = MO.getCImm()->getZExtValue(); + break; + case MachineOperand::MO_GlobalAddress: + GV = MO.getGlobal(); + Off = MO.getOffset(); + break; + default: + return; + } + Off &= Mask; + assert(valid() && "Mask should have been less than 32 bits"); + } + ImmVal(unsigned Imm, MCRegister Reg, const TargetRegisterInfo &TRI) + : ImmVal(Reg, TRI) { + Off = Imm & Mask; + assert(valid() && "Mask should have been less than 32 bits"); + } + ImmVal(const ImmVal &SuperVal, unsigned Idx, const TargetRegisterInfo &TRI) { + Mask = maskTrailingOnes(TRI.getSubRegIdxSize(Idx)); + if (!SuperVal.isImm()) + return; + Off = SuperVal.Off >> TRI.getSubRegIdxOffset(Idx) & Mask; + assert(valid() && "Mask should have been less than 32 bits"); + } + + void clobber() { + Off = UnknownOff; + KilledBy = nullptr; + } + + bool valid() const { + return Off != UnknownOff; + } + bool isImm() const { + return valid() && !GV; + } + bool isGlobal() const { + return valid() && GV; + } + + bool match(ImmVal &Val, int Delta = 0) const { + return valid() && Val.valid() && GV == Val.GV && Mask == Val.Mask && + Off == ((Val.Off + Delta) & Mask); + } + + void setKilledBy(MachineInstr *MI) { + KilledBy = MI; + } + + void reuse(Register Reg, const TargetRegisterInfo &TRI) { + if (KilledBy) { + assert(KilledBy->killsRegister(Reg, &TRI) && + "KilledBy should kill register"); + KilledBy->clearRegisterKills(Reg, &TRI); + KilledBy = nullptr; + } + } + + friend raw_ostream &operator<<(raw_ostream &OS, ImmVal &Val) { + if (!Val.valid()) + return OS << "?"; + if (Val.GV) + OS << Val.GV << '+'; + return OS << Val.Off; + } +}; + +class Z80MachineLateOptimization : public MachineFunctionPass { + const TargetRegisterInfo *TRI; + ImmVal Vals[Z80::NUM_TARGET_REGS]; + + void clobberAll() { + for (unsigned Reg = 1; Reg != Z80::NUM_TARGET_REGS; ++Reg) + Vals[Reg] = ImmVal(Reg, *TRI); + } + template + void clobber(Register Reg, bool IncludeSelf) { + for (MCRegIterator I(Reg, TRI, IncludeSelf); I.isValid(); ++I) + Vals[*I] = ImmVal(*I, *TRI); + } + + bool tryReplaceReg(MachineRegisterInfo &MRI, MCRegister FromReg, + MCRegister ToReg); + void debug(const MachineInstr &MI); + +public: + static char ID; + + Z80MachineLateOptimization() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { + return "Z80 Machine Late Optimization"; + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // end anonymous namespace + +bool Z80MachineLateOptimization::runOnMachineFunction(MachineFunction &MF) { + bool Changed = false; + TRI = MF.getSubtarget().getRegisterInfo(); + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + LiveRegUnits LiveUnits(*TRI); + for (MachineBasicBlock &MBB : MF) { + LiveUnits.clear(); + LiveUnits.addLiveIns(MBB); + clobberAll(); + for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E;) { + MachineInstr &MI = *I; + ++I; + LiveUnits.stepForward(MI); + switch (unsigned Opc = MI.getOpcode()) { + case Z80::LD8ri: + case Z80::LD16ri: + case Z80::LD24ri: + case Z80::LD24r0: + case Z80::LD24r_1: { + Register Reg = MI.getOperand(0).getReg(); + ImmVal Val; + switch (Opc) { + default: + Val = ImmVal(MI.getOperand(1), Reg, *TRI); + break; + case Z80::LD24r0: + Val = ImmVal(0, Reg, *TRI); + break; + case Z80::LD24r_1: + Val = ImmVal(-1, Reg, *TRI); + break; + } + if (Val.match(Vals[Reg])) { + LLVM_DEBUG(dbgs() << "Erasing redundant: "; MI.dump()); + MI.eraseFromParent(); + Vals[Reg].reuse(Reg, *TRI); + Changed = true; + continue; + } + if (Val.match(Vals[Reg], 1)) + switch (Opc) { + case Z80::LD8ri: + if (!LiveUnits.available(Z80::F)) + break; + Opc = Z80::INC8r; + break; + case Z80::LD16ri: + Opc = Z80::INC16r; + break; + case Z80::LD24ri: + case Z80::LD24r0: + case Z80::LD24r_1: + Opc = Z80::INC24r; + break; + } + else if (Val.match(Vals[Reg], -1)) + switch (Opc) { + case Z80::LD8ri: + if (!LiveUnits.available(Z80::F)) + break; + Opc = Z80::DEC8r; + break; + case Z80::LD16ri: + Opc = Z80::DEC16r; + break; + case Z80::LD24ri: + case Z80::LD24r0: + case Z80::LD24r_1: + Opc = Z80::DEC24r; + break; + } + if (Opc != MI.getOpcode()) { + LLVM_DEBUG(dbgs() << "Replacing: "; MI.dump();); + MI.setDesc(TII.get(Opc)); + MI.RemoveOperand(1); + MI.addImplicitDefUseOperands(MF); + Vals[Reg].reuse(Reg, *TRI); + LLVM_DEBUG(dbgs() << "With: "; MI.dump();); + } + clobber(Reg, false); + Vals[Reg] = Val; + for (MCSubRegIndexIterator SRII(Reg, TRI); SRII.isValid(); ++SRII) + Vals[SRII.getSubReg()] = ImmVal(Val, SRII.getSubRegIndex(), *TRI); + debug(MI); + continue; + } + case Z80::RLC8r: + case Z80::RRC8r: + case Z80::RL8r: + case Z80::RR8r: { + if (MI.getOperand(0).getReg() != Z80::A || !LiveUnits.available(Z80::F)) + break; + switch (Opc) { + case Z80::RLC8r: Opc = Z80::RLCA; break; + case Z80::RRC8r: Opc = Z80::RRCA; break; + case Z80::RL8r: Opc = Z80::RLA; break; + case Z80::RR8r: Opc = Z80::RRA; break; + } + LLVM_DEBUG(dbgs() << "Replacing: "; MI.dump(); dbgs() << " With: "); + MI.setDesc(TII.get(Opc)); + MI.getOperand(0).setImplicit(); + MI.getOperand(1).setImplicit(); + break; + } + } + for (MachineOperand &MO : MI.operands()) + if (MO.isReg() && MO.isKill() && MO.getReg().isPhysical()) + for (MCRegAliasIterator I(MO.getReg(), TRI, true); I.isValid(); ++I) + Vals[*I].setKilledBy(&MI); + for (MachineOperand &MO : MI.operands()) { + if (MO.isReg() && MO.isDef() && MO.getReg().isPhysical() && + !(MI.isCopy() && MO.isImplicit())) + clobber(MO.getReg(), true); + else if (MO.isRegMask()) + for (unsigned Reg = 1; Reg != Z80::NUM_TARGET_REGS; ++Reg) + if (MO.clobbersPhysReg(Reg)) + Vals[Reg] = ImmVal(Reg, *TRI); + } + debug(MI); + } + } + return Changed; +} + +void Z80MachineLateOptimization::debug(const MachineInstr &MI) { + for (unsigned Reg = 1; Reg != Z80::NUM_TARGET_REGS; ++Reg) + if (Vals[Reg].valid()) + LLVM_DEBUG(dbgs() << TRI->getName(Reg) << " = " << Vals[Reg] << ' '); + LLVM_DEBUG(MI.dump()); +} + +char Z80MachineLateOptimization::ID = 0; +INITIALIZE_PASS(Z80MachineLateOptimization, DEBUG_TYPE, + "Optimize Z80 machine instrs after regselect", false, false) + +namespace llvm { +FunctionPass *createZ80MachineLateOptimizationPass() { + return new Z80MachineLateOptimization(); +} +} // end namespace llvm diff --git a/llvm/lib/Target/Z80/Z80PostSelectCombiner.cpp b/llvm/lib/Target/Z80/Z80PostSelectCombiner.cpp new file mode 100644 index 00000000000000..78641ff0a30bc0 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80PostSelectCombiner.cpp @@ -0,0 +1,435 @@ +//=== lib/CodeGen/GlobalISel/Z80PostSelectCombiner.cpp --------------------===// +// +// 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 pass does combining of machine instructions at the generic MI level, +// before the legalizer. +// +//===----------------------------------------------------------------------===// + +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80InstrInfo.h" +#include "Z80Subtarget.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/Debug.h" +#include "llvm/Target/TargetMachine.h" + +#define DEBUG_TYPE "z80-postselect-combiner" + +using namespace llvm; + +static cl::opt CondCallThreshold("z80-cond-call-threshold", + cl::Hidden, cl::init(10)); + +namespace { +class Z80PostSelectCombiner : public MachineFunctionPass { +public: + static char ID; + + Z80PostSelectCombiner(); + + StringRef getPassName() const override { return "Z80 Post Select Combiner"; } + + bool runOnMachineFunction(MachineFunction &MF) override; + + void getAnalysisUsage(AnalysisUsage &AU) const override; +}; + +class ValLoc { + Register Reg, Base; + int8_t Off; + + static Register getBaseReg(const MachineInstr &MI, unsigned OpNo) { + Register BaseReg; + const MachineOperand &BaseMO = MI.getOperand(OpNo); + if (BaseMO.isReg()) + BaseReg = BaseMO.getReg(); + else if (BaseMO.getIndex() >= 0) + return Register::index2StackSlot(BaseMO.getIndex()); + return BaseReg; + } + +public: + ValLoc() : Reg(), Base(), Off() {} + + ValLoc &setReg(Register Reg) { + this->Reg = Reg; + return *this; + } + ValLoc &setReg(const MachineInstr &MI, unsigned OpNo) { + return setReg(MI.getOperand(OpNo).getReg()); + } + ValLoc &setMem(Register Base, int8_t Off = 0) { + this->Base = Base; + this->Off = Off; + return *this; + } + ValLoc &setPtr(const MachineInstr &MI, unsigned OpNo) { + return setMem(MI.getOperand(OpNo).getReg()); + } + ValLoc &setOff(const MachineInstr &MI, unsigned OpNo) { + return setMem(getBaseReg(MI, OpNo), MI.getOperand(OpNo + 1).getImm()); + } + + bool matchesReg(Register Reg) const { + return Reg.isValid() && this->Reg == Reg; + } + bool matchesReg(const MachineInstr &MI, unsigned OpNo) const { + return matchesReg(MI.getOperand(OpNo).getReg()); + } + bool matchesMem(Register Base, int8_t Off = 0) const { + return Base.isValid() && this->Base == Base && this->Off == Off; + } + bool matchesPtr(const MachineInstr &MI, unsigned OpNo) const { + return matchesMem(MI.getOperand(OpNo).getReg()); + } + bool matchesOff(const MachineInstr &MI, unsigned OpNo) const { + return matchesMem(getBaseReg(MI, OpNo), MI.getOperand(OpNo + 1).getImm()); + } + + void clobberDefs(const MachineInstr &MI, const TargetRegisterInfo &TRI) { + for (const MachineOperand &DefMO : MI.defs()) + for (Register LocReg : {Reg, Base}) + if (LocReg.isValid() && TRI.regsOverlap(DefMO.getReg(), LocReg)) + return clear(); + } + + void clear() { + *this = ValLoc(); + } +}; + +} // end anonymous namespace + +void Z80PostSelectCombiner::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); +} + +Z80PostSelectCombiner::Z80PostSelectCombiner() : MachineFunctionPass(ID) { + initializeZ80PostSelectCombinerPass(*PassRegistry::getPassRegistry()); +} + +bool Z80PostSelectCombiner::runOnMachineFunction(MachineFunction &MF) { + MachineRegisterInfo &MRI = MF.getRegInfo(); + auto &STI = MF.getSubtarget(); + auto &TII = *STI.getInstrInfo(); + auto &TRI = *STI.getRegisterInfo(); + + bool Changed = false; + for (MachineBasicBlock &MBB : MF) { + auto I = MBB.begin(), E = MBB.end(); + ValLoc SZFlagLoc; + auto FlagLocs = {&SZFlagLoc}; + while (I != E) { + MachineInstr &MI = *I; + ++I; + + switch (unsigned Opc = MI.getOpcode()) { + case TargetOpcode::COPY: { + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesReg(MI, 1)) + FlagLoc->setReg(MI, 0); + Register DstReg = MI.getOperand(0).getReg(); + if (DstReg != Z80::SPS && DstReg != Z80::SPL) + break; + Register TmpReg = + MRI.createVirtualRegister(DstReg == Z80::SPL ? &Z80::A24RegClass + : &Z80::A16RegClass); + BuildMI(MBB, MI, MI.getDebugLoc(), TII.get(TargetOpcode::COPY), TmpReg) + .add(MI.getOperand(1)); + MI.setDesc(TII.get(DstReg == Z80::SPL ? Z80::LD24SP : Z80::LD16SP)); + MI.getOperand(0).setReg(TmpReg); + MI.RemoveOperand(1); + Changed = true; + break; + } + case Z80::PUSH16r: + case Z80::PUSH24r: { + if (!STI.hasEZ80Ops()) + break; + bool IsPush24 = Opc == Z80::PUSH24r; + Register SrcReg = MI.getOperand(0).getReg(); + if (!MRI.hasOneUse(SrcReg)) + break; + MachineInstr *SrcMI = MRI.getVRegDef(SrcReg); + if (!SrcMI || + SrcMI->getOpcode() != (IsPush24 ? Z80::LEA24ro : Z80::LEA16ro)) + break; + MachineOperand &BaseMO = SrcMI->getOperand(1); + auto NewOff = SrcMI->getOperand(2).getImm(); + if (!BaseMO.isReg() || NewOff) { + MI.RemoveOperand(0); + MI.setDesc(TII.get(IsPush24 ? Z80::PEA24o : Z80::PEA16o)); + MachineInstrBuilder(MF, MI).add(SrcMI->getOperand(1)).addImm(NewOff); + } else + MI.getOperand(0).setReg(BaseMO.getReg()); + SrcMI->eraseFromParent(); + Changed = true; + break; + } + case Z80::LD8rp: + case Z80::LD8gp: + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesPtr(MI, 1)) + FlagLoc->setReg(MI, 0); + break; + case Z80::LD8ro: + case Z80::LD8go: + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesOff(MI, 1)) + FlagLoc->setReg(MI, 0); + break; + case Z80::LD8pr: + case Z80::LD8pg: + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesReg(MI, 1)) + FlagLoc->setPtr(MI, 0); + break; + case Z80::LD8or: + case Z80::LD8og: + for (ValLoc *FlagLoc : FlagLocs) + if (FlagLoc->matchesReg(MI, 2)) + FlagLoc->setOff(MI, 0); + break; + case Z80::OR8ar: + if (MI.getOperand(0).getReg() == Z80::A && + SZFlagLoc.matchesReg(MI, 0)) { + MI.eraseFromParent(); + break; + } + LLVM_FALLTHROUGH; + case Z80::ADD8ar: + case Z80::ADD8ai: + case Z80::ADC8ar: + case Z80::ADC8ai: + case Z80::SUB8ar: + case Z80::SUB8ai: + case Z80::SBC8ar: + case Z80::SBC8ai: + case Z80::AND8ar: + case Z80::AND8ai: + case Z80::XOR8ar: + case Z80::XOR8ai: + case Z80::OR8ai: + SZFlagLoc.setReg(Z80::A); + break; + case Z80::RLC8r: + case Z80::RRC8r: + case Z80::RL8r: + case Z80::RR8r: + case Z80::SLA8r: + case Z80::SRA8r: + case Z80::SRL8r: + case Z80::INC8r: + case Z80::DEC8r: + SZFlagLoc.setReg(MI, 0); + break; + case Z80::ADD8ap: + case Z80::ADC8ap: + case Z80::SUB8ap: + case Z80::SBC8ap: + case Z80::AND8ap: + case Z80::XOR8ap: + case Z80::OR8ap: + case Z80::RLC8p: + case Z80::RRC8p: + case Z80::RL8p: + case Z80::RR8p: + case Z80::SLA8p: + case Z80::SRA8p: + case Z80::SRL8p: + case Z80::INC8p: + case Z80::DEC8p: + SZFlagLoc.setPtr(MI, 0); + break; + case Z80::ADD8ao: + case Z80::ADC8ao: + case Z80::SUB8ao: + case Z80::SBC8ao: + case Z80::AND8ao: + case Z80::XOR8ao: + case Z80::OR8ao: + case Z80::RLC8o: + case Z80::RRC8o: + case Z80::RL8o: + case Z80::RR8o: + case Z80::SLA8o: + case Z80::SRA8o: + case Z80::SRL8o: + case Z80::INC8o: + case Z80::DEC8o: + SZFlagLoc.setOff(MI, 0); + break; + default: + if (MI.modifiesRegister(Z80::F, &TRI)) + for (ValLoc *FlagLoc : FlagLocs) + FlagLoc->clear(); + break; + } + + for (ValLoc *FlagLoc : FlagLocs) + FlagLoc->clobberDefs(MI, TRI); + } + } + + for (MachineBasicBlock &MBB : MF) { + MachineBasicBlock *TrueMBB = nullptr, *FalseMBB = nullptr; + SmallVector Cond; + if (TII.analyzeBranch(MBB, TrueMBB, FalseMBB, Cond, false) || Cond.empty()) + continue; + if (!FalseMBB) + FalseMBB = &*std::next(MBB.getIterator()); + assert(TrueMBB && FalseMBB && "Expected to be nonnull"); + for (int I = 0; I != 2; ++I) { + if (TrueMBB->succ_empty() && TrueMBB->isReturnBlock()) { + auto II = TrueMBB->begin(); + while (II->isCopy() || II->isMetaInstruction()) + ++II; + if (++II == TrueMBB->end()) { + // Unimplemented until FPE works. + //Changed = true; + } + } + if (TII.reverseBranchCondition(Cond)) + break; + std::swap(TrueMBB, FalseMBB); + } + // Separate loop because we want to prefer the above optimization. + for (int I = 0; I != 2; ++I) { + if (TrueMBB->pred_size() == 1 && TrueMBB->succ_size() == 1 && + TrueMBB->isSuccessor(FalseMBB)) { + MachineBasicBlock::iterator I = TrueMBB->begin(); + MachineBasicBlock::iterator E = TrueMBB->getFirstTerminator(); + if (I != E && TII.isFrameSetup(*I) && TII.isFrameInstr(*--E) && + I != E) { + unsigned Cost = 0; + MachineInstr *CallMI = nullptr; + struct Result { + Register PhysReg, TrueReg, FalseReg, ResReg; + }; + SmallVector Results; + while (++I != E) { + ++Cost; + unsigned Opc = I->getOpcode(); + if (Opc == Z80::CALL16 || Opc == Z80::CALL24) { + if (CallMI) { + CallMI = nullptr; + break; + } + CallMI = &*I; + } + if (TII.isFrameSetup(*I) || + (!CallMI && I->modifiesRegister(Z80::F, &TRI))) { + CallMI = nullptr; + break; + } + if (CallMI && Opc == TargetOpcode::COPY) { + if (Results.size() == 4) { + CallMI = nullptr; + break; + } + Result &Res = Results.emplace_back(); + Res.TrueReg = I->getOperand(0).getReg(); + Res.PhysReg = I->getOperand(1).getReg(); + assert(Res.TrueReg.isVirtual() && Res.PhysReg.isPhysical() && + "Expected phys to virt reg copy inside call sequence"); + } + } + for (I = FalseMBB->begin(), E = FalseMBB->end(); CallMI && I != E && I->isPHI(); + ++I) { + if (I->getNumOperands() != 5) { + CallMI = nullptr; + break; + } + Register FalseReg, TrueReg; + for (unsigned OpNo = 1; CallMI && OpNo != 5; OpNo += 2) { + Register Reg = I->getOperand(OpNo).getReg(); + MachineBasicBlock &PredMBB = *I->getOperand(OpNo + 1).getMBB(); + if (&PredMBB == &MBB) + FalseReg = Reg; + else if (&PredMBB == TrueMBB) + TrueReg = Reg; + else + CallMI = nullptr; + } + bool Found = false; + for (Result &Res : Results) { + if (Res.TrueReg != TrueReg) + continue; + if (Res.FalseReg.isValid()) + break; + Res.FalseReg = FalseReg; + Res.ResReg = I->getOperand(0).getReg(); + Found = true; + break; + } + if (!Found) + CallMI = nullptr; + } + for (Result &Res : Results) { + if (!Res.FalseReg.isValid()) { + CallMI = nullptr; + break; + } + } + if (CallMI && Cost < CondCallThreshold) { + Register TempReg = MRI.createVirtualRegister(&Z80::F8RegClass); + DebugLoc DL = MBB.findBranchDebugLoc(); + MBB.removeSuccessor(FalseMBB); + TII.removeBranch(MBB); + BuildMI(&MBB, DL, TII.get(TargetOpcode::COPY), TempReg) + .addReg(Z80::F); + if (!MBB.isLayoutSuccessor(TrueMBB)) + TII.insertUnconditionalBranch(MBB, TrueMBB, DL); + BuildMI(*TrueMBB, TrueMBB->begin(), DL, TII.get(TargetOpcode::COPY), + Z80::F).addReg(TempReg); + CallMI->setDesc(TII.get(CallMI->getOpcode() == Z80::CALL24 + ? Z80::CALL24CC : Z80::CALL16CC)); + auto RegMask = CallMI->getOperand(1).getRegMask(); + CallMI->RemoveOperand(1); + MachineInstrBuilder(MF, CallMI) + .add(Cond[0]).addRegMask(RegMask) + .addReg(Z80::F, RegState::Implicit); + for (Result &Res : Results) { + BuildMI(*TrueMBB, CallMI, CallMI->getDebugLoc(), + TII.get(TargetOpcode::COPY), Res.PhysReg) + .addReg(Res.FalseReg); + CallMI->addRegisterKilled(Res.PhysReg, &TRI, true); + } + Changed = true; + } + } + } + if (TII.reverseBranchCondition(Cond)) + break; + std::swap(TrueMBB, FalseMBB); + } + } + + return Changed; +} + +char Z80PostSelectCombiner::ID = 0; +INITIALIZE_PASS_BEGIN(Z80PostSelectCombiner, DEBUG_TYPE, + "Combine Z80 machine instrs after inst selection", false, + false) +INITIALIZE_PASS_DEPENDENCY(TargetPassConfig); +INITIALIZE_PASS_DEPENDENCY(InstructionSelect); +INITIALIZE_PASS_END(Z80PostSelectCombiner, DEBUG_TYPE, + "Combine Z80 machine instrs after inst selection", false, + false) + + +namespace llvm { +FunctionPass *createZ80PostSelectCombiner() { + return new Z80PostSelectCombiner; +} +} // end namespace llvm diff --git a/llvm/lib/Target/Z80/Z80RegisterBanks.td b/llvm/lib/Target/Z80/Z80RegisterBanks.td new file mode 100644 index 00000000000000..e94ca167ca8170 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80RegisterBanks.td @@ -0,0 +1,13 @@ +//=- Z80RegisterBank.td - Describe the AArch64 Banks -----*- tablegen -*-=// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +/// General Purpose Registers +def GPRRegBank : RegisterBank<"GPR", [R24, Z24, Z16, F8]>; diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.cpp b/llvm/lib/Target/Z80/Z80RegisterInfo.cpp new file mode 100644 index 00000000000000..83993cfd8bf3ac --- /dev/null +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.cpp @@ -0,0 +1,410 @@ +//===-- Z80RegisterInfo.cpp - Z80 Register Information --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Z80 implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#include "Z80RegisterInfo.h" +#include "Z80FrameLowering.h" +#include "Z80MachineFunctionInfo.h" +#include "Z80Subtarget.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "llvm/CodeGen/MachineFrameInfo.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/RegisterScavenging.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/Support/Debug.h" +using namespace llvm; + +#define DEBUG_TYPE "z80reginfo" + +#define GET_REGINFO_TARGET_DESC +#include "Z80GenRegisterInfo.inc" + +Z80RegisterInfo::Z80RegisterInfo(const Triple &TT) + : Z80GenRegisterInfo(Z80::PC) { + // Cache some information. + Is24Bit = !TT.isArch16Bit() && TT.getEnvironment() != Triple::CODE16; + + // Use a callee-saved register as the base pointer. These registers must + // not conflict with any ABI requirements. + if (Is24Bit) { + SlotSize = 3; + StackPtr = Z80::SPL; + } else { + SlotSize = 2; + StackPtr = Z80::SPS; + } +} + +const TargetRegisterClass * +Z80RegisterInfo::getPointerRegClass(const MachineFunction &MF, + unsigned Kind) const { + switch (Kind) { + default: llvm_unreachable("Unexpected Kind in getPointerRegClass!"); + case 0: return Is24Bit ? &Z80::G24RegClass : &Z80::G16RegClass; + case 1: return Is24Bit ? &Z80::A24RegClass : &Z80::A16RegClass; + case 2: return Is24Bit ? &Z80::I24RegClass : &Z80::I16RegClass; + } +} + +const TargetRegisterClass * +Z80RegisterInfo::getLargestLegalSuperClass(const TargetRegisterClass *RC, + const MachineFunction &) const { + const TargetRegisterClass *Super = RC; + TargetRegisterClass::sc_iterator I = RC->getSuperClasses(); + do { + switch (Super->getID()) { + case Z80::R8RegClassID: + case Z80::R16RegClassID: + case Z80::R24RegClassID: + return Super; + } + Super = *I++; + } while (Super); + return RC; +} + +unsigned Z80RegisterInfo::getRegPressureLimit(const TargetRegisterClass *RC, + MachineFunction &MF) const { + return 3; + + switch (RC->getID()) { + default: + return 0; + case Z80::R16RegClassID: + case Z80::R24RegClassID: + return 2; + } +} + +const MCPhysReg * +Z80RegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { + switch (MF->getFunction().getCallingConv()) { + default: llvm_unreachable("Unsupported calling convention"); + case CallingConv::C: + case CallingConv::Fast: + return Is24Bit ? CSR_EZ80_C_SaveList : CSR_Z80_C_SaveList; + case CallingConv::PreserveAll: + case CallingConv::Z80_LibCall: + case CallingConv::Z80_LibCall_AB: + case CallingConv::Z80_LibCall_AC: + case CallingConv::Z80_LibCall_BC: + case CallingConv::Z80_LibCall_L: + case CallingConv::Z80_LibCall_F: + return Is24Bit ? CSR_EZ80_AllRegs_SaveList : CSR_Z80_AllRegs_SaveList; + case CallingConv::Z80_TIFlags: + return Is24Bit ? CSR_EZ80_TIFlags_SaveList : CSR_Z80_TIFlags_SaveList; + } +} + +const uint32_t * +Z80RegisterInfo::getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID CC) const { + switch (CC) { + default: llvm_unreachable("Unsupported calling convention"); + case CallingConv::C: + case CallingConv::Fast: + return Is24Bit ? CSR_EZ80_C_RegMask : CSR_Z80_C_RegMask; + case CallingConv::PreserveAll: + case CallingConv::Z80_LibCall: + case CallingConv::Z80_LibCall_AB: + case CallingConv::Z80_LibCall_AC: + case CallingConv::Z80_LibCall_BC: + case CallingConv::Z80_LibCall_L: + case CallingConv::Z80_LibCall_F: + return Is24Bit ? CSR_EZ80_AllRegs_RegMask : CSR_Z80_AllRegs_RegMask; + case CallingConv::Z80_TIFlags: + return Is24Bit ? CSR_EZ80_TIFlags_RegMask : CSR_Z80_TIFlags_RegMask; + } +} +const uint32_t *Z80RegisterInfo::getNoPreservedMask() const { + return CSR_NoRegs_RegMask; +} + +BitVector Z80RegisterInfo::getReservedRegs(const MachineFunction &MF) const { + BitVector Reserved(getNumRegs()); + + // Set the stack-pointer registers as reserved. + Reserved.set(Z80::SPS); + Reserved.set(Z80::SPL); + + // Set the program-counter register as reserved. + Reserved.set(Z80::PC); + + // Set the frame-pointer register and its aliases as reserved if needed. + for (Register Reg : + {Register(Is24Bit ? Z80::UIX : Z80::IX), getFrameRegister(MF)}) + for (MCRegAliasIterator I(Reg, this, /*IncludeSelf=*/true); I.isValid(); + ++I) + Reserved.set(*I); + + return Reserved; +} + +bool Z80RegisterInfo::saveScavengerRegister(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + MachineBasicBlock::iterator &UseMI, + const TargetRegisterClass *RC, + Register Reg) const { + return false; + const Z80Subtarget &STI = MBB.getParent()->getSubtarget(); + const TargetInstrInfo &TII = *STI.getInstrInfo(); + DebugLoc DL; + if (Reg == Z80::AF) + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + else + BuildMI(MBB, MI, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(Reg); + for (MachineBasicBlock::iterator II = MI; II != UseMI; ++II) { + if (II->isDebugValue()) + continue; + if (II->modifiesRegister(Reg, this)) + UseMI = II; + } + if (Reg == Z80::AF) + BuildMI(MBB, UseMI, DL, TII.get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + else + BuildMI(MBB, UseMI, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), Reg); + return true; +} + +void Z80RegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS) const { + MachineInstr &MI = *II; + unsigned Opc = MI.getOpcode(); + MachineBasicBlock &MBB = *MI.getParent(); + MachineFunction &MF = *MBB.getParent(); + auto &FuncInfo = *MF.getInfo(); + const Z80Subtarget &STI = MF.getSubtarget(); + const Z80InstrInfo &TII = *STI.getInstrInfo(); + const Z80FrameLowering *TFI = getFrameLowering(MF); + DebugLoc DL = MI.getDebugLoc(); + int FrameIndex = MI.getOperand(FIOperandNum).getIndex(); + Register BasePtr = getFrameRegister(MF); + LLVM_DEBUG(MF.dump(); II->dump(); + dbgs() << MF.getFunction().arg_size() << '\n'); + assert(TFI->hasFP(MF) && "Stack slot use without fp unimplemented"); + int BaseOff = MF.getFrameInfo().getObjectOffset(FrameIndex); + int SlotSize = Is24Bit ? 3 : 2; + // Skip any saved callee saved registers + BaseOff += FuncInfo.getCalleeSavedFrameSize(); + // Skip return address for arguments + if (FrameIndex < 0) + BaseOff += SlotSize; + int Off = BaseOff + getFrameIndexInstrOffset(&MI, FIOperandNum); + if (isFrameOffsetLegal(&MI, BasePtr, BaseOff) && + (Opc != Z80::LEA16ro || STI.hasEZ80Ops())) { + MI.getOperand(FIOperandNum).ChangeToRegister(BasePtr, false); + if (!Off && (Opc == Z80::PEA24o || Opc == Z80::PEA16o)) { + MI.setDesc(TII.get(Opc == Z80::PEA24o ? Z80::PUSH24r : Z80::PUSH16r)); + MI.RemoveOperand(FIOperandNum + 1); + } else + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Off); + return; + } + bool SaveFlags = RS->isRegUsed(Z80::F); + Register OffReg = RS->scavengeRegister( + Is24Bit ? &Z80::O24RegClass : &Z80::O16RegClass, II, SPAdj); + if ((Opc == Z80::LEA24ro && + Z80::A24RegClass.contains(MI.getOperand(0).getReg())) || + (Opc == Z80::LEA16ro && + Z80::A16RegClass.contains(MI.getOperand(0).getReg()))) { + Register Op0Reg = MI.getOperand(0).getReg(); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), OffReg) + .addImm(Off); + TII.copyPhysReg(MBB, II, DL, Op0Reg, BasePtr); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), Op0Reg) + .addReg(Op0Reg).addReg(OffReg, RegState::Kill) + ->addRegisterDead(Z80::F, this); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + MI.eraseFromParent(); + } else if (Register ScratchReg = RS->FindUnusedReg( + Is24Bit ? &Z80::A24RegClass : &Z80::A16RegClass)) { + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), OffReg) + .addImm(Off); + TII.copyPhysReg(MBB, II, DL, ScratchReg, BasePtr); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), + ScratchReg).addReg(ScratchReg).addReg(OffReg, RegState::Kill) + ->addRegisterDead(Z80::F, this); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + MI.getOperand(FIOperandNum).ChangeToRegister(ScratchReg, false); + if ((Is24Bit ? Z80::I24RegClass : Z80::I16RegClass).contains(ScratchReg)) + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(0); + else { + switch (Opc) { + default: llvm_unreachable("Unexpected opcode!"); + case Z80::LD24ro: Opc = Z80::LD24rp; break; + case Z80::LD16ro: Opc = Z80::LD16rp; break; + case Z80::LD88ro: Opc = Z80::LD88rp; break; + case Z80::LD8ro: Opc = Z80::LD8rp; break; + case Z80::LD8go: Opc = Z80::LD8gp; break; + case Z80::LD24or: Opc = Z80::LD24pr; break; + case Z80::LD16or: Opc = Z80::LD16pr; break; + case Z80::LD88or: Opc = Z80::LD88pr; break; + case Z80::LD8or: Opc = Z80::LD8pr; break; + case Z80::LD8og: Opc = Z80::LD8pg; break; + case Z80::LD8oi: Opc = Z80::LD8pi; break; + case Z80::PEA24o: Opc = Z80::PUSH24r; break; + case Z80::PEA16o: Opc = Z80::PUSH16r; break; + case Z80::RLC8o: Opc = Z80::RLC8p; break; + case Z80::RRC8o: Opc = Z80::RRC8p; break; + case Z80::RL8o: Opc = Z80::RL8p; break; + case Z80::RR8o: Opc = Z80::RR8p; break; + case Z80::SLA8o: Opc = Z80::SLA8p; break; + case Z80::SRA8o: Opc = Z80::SRA8p; break; + case Z80::SRL8o: Opc = Z80::SRL8p; break; + case Z80::BIT8bo: Opc = Z80::BIT8bp; break; + case Z80::RES8bo: Opc = Z80::RES8bp; break; + case Z80::SET8bo: Opc = Z80::SET8bp; break; + case Z80::INC8o: Opc = Z80::INC8p; break; + case Z80::DEC8o: Opc = Z80::DEC8p; break; + case Z80::ADD8ao: Opc = Z80::ADD8ap; break; + case Z80::ADC8ao: Opc = Z80::ADC8ap; break; + case Z80::SUB8ao: Opc = Z80::SUB8ap; break; + case Z80::SBC8ao: Opc = Z80::SBC8ap; break; + case Z80::AND8ao: Opc = Z80::AND8ap; break; + case Z80::XOR8ao: Opc = Z80::XOR8ap; break; + case Z80::OR8ao: Opc = Z80::OR8ap; break; + case Z80::CP8ao: Opc = Z80::CP8ap; break; + case Z80::TST8ao: Opc = Z80::TST8ap; break; + case Z80::LEA24ro: + case Z80::LEA16ro: + Opc = TargetOpcode::COPY; + break; + } + if (Opc == TargetOpcode::COPY) { + TII.copyPhysReg(MBB, ++II, DL, MI.getOperand(0).getReg(), + MI.getOperand(1).getReg()); + MI.eraseFromParent(); + } else { + MI.setDesc(TII.get(Opc)); + MI.RemoveOperand(FIOperandNum + 1); + } + } + } else { + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::PUSH24r : Z80::PUSH16r)) + .addReg(BasePtr); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), OffReg) + .addImm(Off); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::PUSH24AF : Z80::PUSH16AF)); + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), + BasePtr).addReg(BasePtr).addReg(OffReg, RegState::Kill) + ->addRegisterDead(Z80::F, this); + if (SaveFlags) + BuildMI(MBB, II, DL, TII.get(Is24Bit ? Z80::POP24AF : Z80::POP16AF)); + if (Opc == Z80::PEA24o || Opc == Z80::PEA16o) { + MI.setDesc(TII.get(Opc == Z80::PEA24o ? Z80::EX24SP : Z80::EX16SP)); + MI.getOperand(0).ChangeToRegister(BasePtr, true); + MI.getOperand(1).ChangeToRegister(BasePtr, false); + MI.tieOperands(0, 1); + } else { + MI.getOperand(FIOperandNum).ChangeToRegister(BasePtr, false); + MI.getOperand(FIOperandNum + 1).ChangeToImmediate(0); + BuildMI(MBB, ++II, DL, TII.get(Is24Bit ? Z80::POP24r : Z80::POP16r), + BasePtr); + } + } +} + +Register Z80RegisterInfo::getFrameRegister(const MachineFunction &MF) const { + return getFrameLowering(MF)->hasFP(MF) + ? MF.getInfo()->getUsesAltFP() + ? Is24Bit ? Z80::UIY : Z80::IY + : Is24Bit ? Z80::UIX : Z80::IX + : Is24Bit ? Z80::SPL : Z80::SPS; +} + +bool Z80RegisterInfo:: +shouldCoalesce(MachineInstr *MI, + const TargetRegisterClass *SrcRC, unsigned SrcSubReg, + const TargetRegisterClass *DstRC, unsigned DstSubReg, + const TargetRegisterClass *NewRC, LiveIntervals &LIS) const { + LLVM_DEBUG( + dbgs() << getRegClassName(SrcRC) << '[' << SrcRC->getNumRegs() + << "]:" << (SrcSubReg ? getSubRegIndexName(SrcSubReg) : "") + << " -> " << getRegClassName(DstRC) << '[' << DstRC->getNumRegs() + << "]:" << (DstSubReg ? getSubRegIndexName(DstSubReg) : "") << ' ' + << getRegClassName(NewRC) << '[' << NewRC->getNumRegs() << "]\n"); + // Don't coalesce if SrcRC and DstRC have a small intersection. + return std::min(SrcRC->getNumRegs(), DstRC->getNumRegs()) <= + NewRC->getNumRegs(); +} + +bool Z80RegisterInfo::requiresVirtualBaseRegisters( + const MachineFunction &MF) const { + return true; +} +int64_t Z80RegisterInfo::getFrameIndexInstrOffset(const MachineInstr *MI, + int Idx) const { + return MI->getOperand(Idx + 1).getImm(); +} +bool Z80RegisterInfo::needsFrameBaseReg(MachineInstr *MI, + int64_t Offset) const { + const MachineFunction &MF = *MI->getParent()->getParent(); + return !isFrameOffsetLegal(MI, getFrameRegister(MF), Offset); +} +void Z80RegisterInfo::materializeFrameBaseRegister(MachineBasicBlock *MBB, + Register BaseReg, + int FrameIdx, + int64_t Offset) const { + MachineFunction &MF = *MBB->getParent(); + MachineRegisterInfo &MRI = MF.getRegInfo(); + const Z80Subtarget &STI = MF.getSubtarget(); + const Z80InstrInfo &TII = *STI.getInstrInfo(); + MachineBasicBlock::iterator II = MBB->begin(); + DebugLoc DL = MBB->findDebugLoc(II); + MRI.setRegClass(BaseReg, Is24Bit ? &Z80::I24RegClass : &Z80::I16RegClass); + BuildMI(*MBB, II, DL, TII.get(Is24Bit ? Z80::LEA24ro : Z80::LEA16ro), BaseReg) + .addFrameIndex(FrameIdx).addImm(Offset); + return; + Register CopyReg = MRI.createVirtualRegister(Is24Bit ? &Z80::I24RegClass + : &Z80::I16RegClass); + Register OffsetReg = MRI.createVirtualRegister(Is24Bit ? &Z80::O24RegClass + : &Z80::O16RegClass); + TII.copyPhysReg(*MBB, II, DL, CopyReg, getFrameRegister(MF)); + BuildMI(*MBB, II, DL, TII.get(Is24Bit ? Z80::LD24ri : Z80::LD16ri), OffsetReg) + .addImm(Offset); + BuildMI(*MBB, II, DL, TII.get(Is24Bit ? Z80::ADD24ao : Z80::ADD16ao), BaseReg) + .addReg(CopyReg).addReg(OffsetReg)->addRegisterDead(Z80::F, this); +} +void Z80RegisterInfo::resolveFrameIndex(MachineInstr &MI, Register BaseReg, + int64_t Offset) const { + unsigned FIOperandNum = 0; + while (!MI.getOperand(FIOperandNum).isFI()) { + FIOperandNum++; + assert(FIOperandNum < MI.getNumOperands() && "Expected a frame index"); + } + MI.getOperand(FIOperandNum).ChangeToRegister(BaseReg, false); + MI.getOperand(FIOperandNum + 1) + .ChangeToImmediate(MI.getOperand(FIOperandNum + 1).getImm() + Offset); +} +bool Z80RegisterInfo::isFrameOffsetLegal(const MachineInstr *MI, + Register BaseReg, + int64_t Offset) const { + unsigned Idx; + for (Idx = 0; !MI->getOperand(Idx).isFI(); ++Idx) + assert(Idx + 2 < MI->getNumOperands() && + "Instr doesn't have FrameIndex operand!"); + Offset += getFrameIndexInstrOffset(MI, Idx); + unsigned Opc = MI->getOpcode(); + return isInt<8>(Offset) && + ((Opc != Z80::LD88ro && Opc != Z80::LD88or) || isInt<8>(Offset + 1)); +} diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.h b/llvm/lib/Target/Z80/Z80RegisterInfo.h new file mode 100644 index 00000000000000..478e988dab154b --- /dev/null +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.h @@ -0,0 +1,123 @@ +//===-- Z80RegisterInfo.h - Z80 Register Information Impl -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the Z80 implementation of the TargetRegisterInfo class. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80REGISTERINFO_H +#define LLVM_LIB_TARGET_Z80_Z80REGISTERINFO_H + +#include "llvm/CodeGen/TargetRegisterInfo.h" + +#define GET_REGINFO_HEADER +#include "Z80GenRegisterInfo.inc" + +namespace llvm { +class Triple; + +class Z80RegisterInfo final : public Z80GenRegisterInfo { + /// Is24bit - Is the target 24-bits. + /// + bool Is24Bit; + + /// SlotSize - Stack slot size in bytes. + /// + unsigned SlotSize; + + /// StackPtr - Z80 physical register used as stack ptr. + /// + unsigned StackPtr; + +public: + Z80RegisterInfo(const Triple &TT); + + /// Code Generation virtual methods... + /// + bool trackLivenessAfterRegAlloc(const MachineFunction &MF) const override { + // Z80MachineLateOptimization requires liveness. + return true; + } + + /// getPointerRegClass - Returns a TargetRegisterClass used for pointer + /// values. + const TargetRegisterClass * + getPointerRegClass(const MachineFunction &MF, + unsigned Kind = 0) const override; + const TargetRegisterClass * + getLargestLegalSuperClass(const TargetRegisterClass *RC, + const MachineFunction &) const override; + + unsigned getRegPressureLimit(const TargetRegisterClass *RC, + MachineFunction &MF) const override; + + /// getCalleeSavedRegs - Return a null-terminated list of all of the + /// callee-save registers on this target. + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; + const uint32_t *getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID CC) const override; + const uint32_t *getNoPreservedMask() const override; + + /// getReservedRegs - Returns a bitset indexed by physical register number + /// indicating if a register is a special register that has particular uses + /// and should be considered unavailable at all times, e.g. SP, RA. This is + /// used by register scaverger to determine what registers are free. + BitVector getReservedRegs(const MachineFunction &MF) const override; + + bool requiresRegisterScavenging(const MachineFunction &MF) const override { + return true; + } + bool requiresFrameIndexScavenging( + const MachineFunction &MF) const override { + return true; + } + bool requiresFrameIndexReplacementScavenging( + const MachineFunction &MF) const override { + return true; + } + + bool saveScavengerRegister(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI, + MachineBasicBlock::iterator &UseMI, + const TargetRegisterClass *RC, + Register Reg) const override; + + void eliminateFrameIndex(MachineBasicBlock::iterator MI, + int SPAdj, unsigned FIOperandNum, + RegScavenger *RS = nullptr) const override; + + // Support for virtual base registers. + bool requiresVirtualBaseRegisters(const MachineFunction &MF) const override; + int64_t getFrameIndexInstrOffset(const MachineInstr *MI, + int Idx) const override; + bool needsFrameBaseReg(MachineInstr *MI, int64_t Offset) const override; + void materializeFrameBaseRegister(MachineBasicBlock *MBB, Register BaseReg, + int FrameIdx, + int64_t Offset) const override; + void resolveFrameIndex(MachineInstr &MI, Register BaseReg, + int64_t Offset) const override; + bool isFrameOffsetLegal(const MachineInstr *MI, Register BaseReg, + int64_t Offset) const override; + + // Debug information queries. + Register getFrameRegister(const MachineFunction &MF) const override; + Register getStackRegister() const { return StackPtr; } + + /// \brief SrcRC and DstRC will be morphed into NewRC if this returns true. + bool shouldCoalesce(MachineInstr *MI, + const TargetRegisterClass *SrcRC, + unsigned SubReg, + const TargetRegisterClass *DstRC, + unsigned DstSubReg, + const TargetRegisterClass *NewRC, + LiveIntervals &LIS) const override; +}; +} // End llvm namespace + +#endif diff --git a/llvm/lib/Target/Z80/Z80RegisterInfo.td b/llvm/lib/Target/Z80/Z80RegisterInfo.td new file mode 100644 index 00000000000000..ec26ccc0848619 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80RegisterInfo.td @@ -0,0 +1,127 @@ +//===- Z80RegisterInfo.td - Describe the Z80 Register File --*- tablegen -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes the Z80 Register file, defining the registers themselves, +// aliases between the registers, and the register classes built out of the +// registers. +// +//===----------------------------------------------------------------------===// + +class Z80Reg Enc = -1> : Register { + let Namespace = "Z80"; + let HWEncoding = Enc; +} +class Z80RegWithSubRegs sub = [], bits<16> enc = -1> + : Z80Reg { + let SubRegs = sub; +} + +// Subregister indices. +let Namespace = "Z80" in { + def sub_low : SubRegIndex<8>; + def sub_high : SubRegIndex<8, 8>; + def sub_short : SubRegIndex<16>; +} + +//===----------------------------------------------------------------------===// +// Register definitions... +// + +// 8-bit registers +def A : Z80Reg<"a", 7>; +def F : Z80Reg<"f">; +def B : Z80Reg<"b", 0>; +def C : Z80Reg<"c", 1>; +def D : Z80Reg<"d", 2>; +def E : Z80Reg<"e", 3>; +def H : Z80Reg<"h", 4>; +def L : Z80Reg<"l", 5>; + +// 8-bit index registers +let CostPerUse = 1 in { +def IXH : Z80Reg<"ixh", 4>; +def IXL : Z80Reg<"ixl", 5>; +def IYH : Z80Reg<"iyh", 4>; +def IYL : Z80Reg<"iyl", 5>; +} + +let SubRegIndices = [sub_high, sub_low], CoveredBySubRegs = 1 in { +// 16-bit registers +def AF : Z80RegWithSubRegs<"af", [A,F], 3>; +def BC : Z80RegWithSubRegs<"bc", [B,C], 0>; +def DE : Z80RegWithSubRegs<"de", [D,E], 1>; +def HL : Z80RegWithSubRegs<"hl", [H,L], 2>; +// 16-bit index registers +let CostPerUse = 1 in { +def IX : Z80RegWithSubRegs<"ix", [IXH,IXL], 2>; +def IY : Z80RegWithSubRegs<"iy", [IYH,IYL], 2>; +} +} +def SPS : Z80Reg<"sp", 3>; + +let SubRegIndices = [sub_short] in { +// 24-bit registers +def UBC : Z80RegWithSubRegs<"bc", [BC], 0>; +def UDE : Z80RegWithSubRegs<"de", [DE], 1>; +def UHL : Z80RegWithSubRegs<"hl", [HL], 2>; +// 24-bit index registers +let CostPerUse = 1 in { +def UIX : Z80RegWithSubRegs<"ix", [IX], 2>; +def UIY : Z80RegWithSubRegs<"iy", [IY], 2>; +} +} +def SPL : Z80Reg<"sp", 3>; +def PC : Z80Reg<"pc">; + +//===----------------------------------------------------------------------===// +// Register Class Definitions... +// + +class Z80RC8 : RegisterClass<"Z80", [i8 ], 8, regList>; +class Z80RC16 : RegisterClass<"Z80", [i16], 8, regList>; +class Z80RC24 : RegisterClass<"Z80", [i24], 8, regList>; + +def G8 : Z80RC8 <(add A, L, E, C, H, D, B)>; +def O8 : Z80RC8 <(add A, E, C, D, B)>; +def Y8 : Z80RC8 <(add O8, IYL, IYH)>; +def X8 : Z80RC8 <(add O8, IXL, IXH)>; +def I8 : Z80RC8 <(add IYL, IYH, IXL, IXH)>; +def R8 : Z80RC8 <(add G8, I8)>; +let CopyCost = -1 in +def F8 : Z80RC8 <(add F)>; + +def O16 : Z80RC16<(add DE, BC)>; +def G16 : Z80RC16<(add HL, O16)>; +def Y16 : Z80RC16<(add IY, O16)>; +def X16 : Z80RC16<(add IX, O16)>; +def I16 : Z80RC16<(add IY, IX)>; +def A16 : Z80RC16<(add HL, I16)>; +def R16 : Z80RC16<(add G16, I16)>; +let CopyCost = -1 in +def Z16 : Z80RC16<(add SPS, AF)>; +//def S16 : Z80RC16<(add R16, AF)>; +//def L16 : Z80RC16<(add G16, I16)>; +//def R16 : Z80RC16<(add L16, SPS)>; +//def S16 : Z80RC16<(add L16, AF)>; +//def C16 : Z80RC16<(add R16, SPS)>; + +def O24 : Z80RC24<(add UDE, UBC)>; +def G24 : Z80RC24<(add UHL, O24)>; +def Y24 : Z80RC24<(add UIY, O24)>; +def X24 : Z80RC24<(add UIX, O24)>; +def I24 : Z80RC24<(add UIY, UIX)>; +def A24 : Z80RC24<(add UHL, I24)>; +def R24 : Z80RC24<(add G24, I24)>; +let CopyCost = -1 in +def Z24 : Z80RC24<(add SPL, PC)>; +//def S24 : Z80RC24<(add R24, AF)>; +//def L24 : Z80RC24<(add G24, I24)>; +//def R24 : Z80RC24<(add L24, SPL)>; +//def S24 : Z80RC24<(add L24, AF)>; +//def C24 : Z80RC24<(add R24, SPL)>; diff --git a/llvm/lib/Target/Z80/Z80Subtarget.cpp b/llvm/lib/Target/Z80/Z80Subtarget.cpp new file mode 100644 index 00000000000000..b49d8c1931209e --- /dev/null +++ b/llvm/lib/Target/Z80/Z80Subtarget.cpp @@ -0,0 +1,52 @@ +//===-- Z80Subtarget.cpp - Z80 Subtarget Information ----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the Z80 specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#include "Z80Subtarget.h" +#include "GISel/Z80CallLowering.h" +#include "GISel/Z80LegalizerInfo.h" +#include "GISel/Z80RegisterBankInfo.h" +#include "MCTargetDesc/Z80MCTargetDesc.h" +#include "Z80.h" +#include "Z80TargetMachine.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" +using namespace llvm; + +#define DEBUG_TYPE "z80-subtarget" + +#define GET_SUBTARGETINFO_TARGET_DESC +#define GET_SUBTARGETINFO_CTOR +#include "Z80GenSubtargetInfo.inc" + +Z80Subtarget &Z80Subtarget::initializeSubtargetDependencies(StringRef CPU, + StringRef FS) { + if (CPU.empty()) + CPU = TargetTriple.getArchName(); + ParseSubtargetFeatures(CPU, FS); + HasIdxHalfRegs = HasUndocOps || HasEZ80Ops; + return *this; +} + +Z80Subtarget::Z80Subtarget(const Triple &TT, StringRef CPU, StringRef FS, + const Z80TargetMachine &TM) + : Z80GenSubtargetInfo(TT, CPU, FS), TargetTriple(TT), + In16BitMode(TT.isArch16Bit() || TT.getEnvironment() == Triple::CODE16), + In24BitMode(!In16BitMode), + InstrInfo(initializeSubtargetDependencies(CPU, FS)), TLInfo(TM, *this), + FrameLowering(*this) { + CallLoweringInfo.reset(new Z80CallLowering(*getTargetLowering())); + Legalizer.reset(new Z80LegalizerInfo(*this, TM)); + + auto *RBI = new Z80RegisterBankInfo(*getRegisterInfo()); + RegBankInfo.reset(RBI); + InstSelector.reset(createZ80InstructionSelector(TM, *this, *RBI)); +} diff --git a/llvm/lib/Target/Z80/Z80Subtarget.h b/llvm/lib/Target/Z80/Z80Subtarget.h new file mode 100644 index 00000000000000..d171fe4d6b37ed --- /dev/null +++ b/llvm/lib/Target/Z80/Z80Subtarget.h @@ -0,0 +1,119 @@ +//===-- Z80Subtarget.h - Define Subtarget for the Z80 ----------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the Z80 specific subclass of TargetSubtargetInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80SUBTARGET_H +#define LLVM_LIB_TARGET_Z80_Z80SUBTARGET_H + +#include "Z80FrameLowering.h" +#include "Z80InstrInfo.h" +#include "Z80ISelLowering.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelector.h" +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" +#include "llvm/CodeGen/GlobalISel/RegisterBankInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" + +#define GET_SUBTARGETINFO_HEADER +#include "Z80GenSubtargetInfo.inc" + +namespace llvm { +class Z80TargetMachine; + +class Z80Subtarget final : public Z80GenSubtargetInfo { + /// What processor and OS we're targeting. + Triple TargetTriple; + + /// True if compiling for 16-bit, false for 24-bit. + bool In16BitMode; + + /// True if compiling for 24-bit, false for 16-bit. + bool In24BitMode; + + /// True if target has undocumented z80 instructions. + bool HasUndocOps = false; + + /// True if target has z180 instructions. + bool HasZ180Ops = false; + + /// True if target has ez80 instructions. + bool HasEZ80Ops = false; + + /// True if target has index half registers (HasUndocOps || HasEZ80Ops). + bool HasIdxHalfRegs = false; + + // Ordering here is important. Z80InstrInfo initializes Z80RegisterInfo which + // Z80TargetLowering needs. + Z80InstrInfo InstrInfo; + Z80TargetLowering TLInfo; + Z80FrameLowering FrameLowering; + + /// GlobalISel related APIs. + std::unique_ptr CallLoweringInfo; + std::unique_ptr Legalizer; + std::unique_ptr RegBankInfo; + std::unique_ptr InstSelector; + +public: + /// This constructor initializes the data members to match that + /// of the specified triple. + Z80Subtarget(const Triple &TT, StringRef CPU, StringRef FS, + const Z80TargetMachine &TM); + + const Z80TargetLowering *getTargetLowering() const override { + return &TLInfo; + } + const Z80InstrInfo *getInstrInfo() const override { return &InstrInfo; } + const Z80FrameLowering *getFrameLowering() const override { + return &FrameLowering; + } + const Z80RegisterInfo *getRegisterInfo() const override { + return &getInstrInfo()->getRegisterInfo(); + } + + /// ParseSubtargetFeatures - Parses features string setting specified + /// subtarget options. Definition of function is auto generated by tblgen. + void ParseSubtargetFeatures(StringRef CPU, StringRef FS); + + /// Methods used by Global ISel + const CallLowering *getCallLowering() const override { + return CallLoweringInfo.get(); + } + InstructionSelector *getInstructionSelector() const override { + return InstSelector.get(); + } + const LegalizerInfo *getLegalizerInfo() const override { + return Legalizer.get(); + } + const RegisterBankInfo *getRegBankInfo() const override { + return RegBankInfo.get(); + } + +private: + Z80Subtarget &initializeSubtargetDependencies(StringRef CPU, StringRef FS); + void initializeEnvironment(); + +public: + const Triple &getTargetTriple() const { return TargetTriple; } + /// Is this ez80 (disregarding specific ABI / programming model) + bool is24Bit() const { return In24BitMode; } + bool is16Bit() const { return In16BitMode; } + bool hasUndocOps() const { return HasUndocOps; } + bool hasZ180Ops() const { return HasZ180Ops; } + bool hasEZ80Ops() const { return HasEZ80Ops; } + bool hasIndexHalfRegs() const { return HasIdxHalfRegs; } + bool has24BitEZ80Ops() const { return is24Bit() && hasEZ80Ops(); } + bool has16BitEZ80Ops() const { return is16Bit() && hasEZ80Ops(); } +}; +} // namespace llvm + +#endif diff --git a/llvm/lib/Target/Z80/Z80TargetMachine.cpp b/llvm/lib/Target/Z80/Z80TargetMachine.cpp new file mode 100644 index 00000000000000..c5b1331efc1d48 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80TargetMachine.cpp @@ -0,0 +1,195 @@ +//===-- Z80TargetMachine.cpp - Define TargetMachine for the Z80 -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the Z80 specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#include "Z80TargetMachine.h" +#include "TargetInfo/Z80TargetInfo.h" +#include "Z80.h" +#include "Z80Subtarget.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/CodeGen/GlobalISel/IRTranslator.h" +#include "llvm/CodeGen/GlobalISel/InstructionSelect.h" +#include "llvm/CodeGen/GlobalISel/Legalizer.h" +#include "llvm/CodeGen/GlobalISel/RegBankSelect.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Function.h" +#include "llvm/InitializePasses.h" +#include "llvm/Support/TargetRegistry.h" +using namespace llvm; + +extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeZ80Target() { + // Register the target. + RegisterTargetMachine X(getTheZ80Target()); + RegisterTargetMachine Y(getTheEZ80Target()); + + PassRegistry &PR = *PassRegistry::getPassRegistry(); + initializeZ80PreLegalizerCombinerPass(PR); + initializeGlobalISel(PR); + initializeZ80PostSelectCombinerPass(PR); + initializeZ80MachineLateOptimizationPass(PR); +} + +static std::string computeDataLayout(const Triple &TT) { + bool IsEZ80 = TT.getArch() == Triple::ez80; + bool Is16Bit = TT.isArch16Bit() || TT.getEnvironment() == Triple::CODE16; + // Z80 is little endian and mangling is closest to MachO. + std::string Ret = "e-m:o"; + // Memory Address Width + Ret += Is16Bit ? "-p:16:8" : "-p:24:8"; + // Port Address Width + Ret += IsEZ80 ? "-p1:16:8" : "-p1:8:8"; + // Other Address Width + if (IsEZ80) + Ret += Is16Bit ? "-p2:24:8" : "-p2:16:8"; + Ret += "-i16:8-i24:8-i32:8-i48:8-i64:8-i96:8-f32:8-f64:8-a:8-n8:16"; + if (!Is16Bit) + Ret += ":24"; + Ret += "-S8"; + return Ret; +} + +static Reloc::Model getEffectiveRelocModel(Optional RM) { + if (RM) + return *RM; + return Reloc::Static; +} + +/// Create a Z80 target. +/// +Z80TargetMachine::Z80TargetMachine(const Target &T, const Triple &TT, + StringRef CPU, StringRef FS, + const TargetOptions &Options, + Optional RM, + Optional CM, + CodeGenOpt::Level OL, bool JIT) + : LLVMTargetMachine(T, computeDataLayout(TT), TT, CPU, FS, Options, + getEffectiveRelocModel(RM), + getEffectiveCodeModel(CM, CodeModel::Small), OL), + TLOF(std::make_unique()) { + initAsmInfo(); + + setGlobalISel(true); + setGlobalISelAbort(GlobalISelAbortMode::Enable); +} + +Z80TargetMachine::~Z80TargetMachine() {} + +const Z80Subtarget * +Z80TargetMachine::getSubtargetImpl(const Function &F) const { + Attribute CPUAttr = F.getFnAttribute("target-cpu"); + Attribute FSAttr = F.getFnAttribute("target-features"); + + StringRef CPU = !CPUAttr.hasAttribute(Attribute::None) + ? CPUAttr.getValueAsString() + : (StringRef)TargetCPU; + StringRef FS = !FSAttr.hasAttribute(Attribute::None) + ? FSAttr.getValueAsString() + : (StringRef)TargetFS; + + SmallString<512> Key; + Key.reserve(CPU.size() + FS.size()); + Key += CPU; + Key += FS; + + auto &I = SubtargetMap[Key]; + if (!I) { + // This needs to be done before we create a new subtarget since any + // creation will depend on the TM and the code generation flags on the + // function that reside in TargetOptions. + resetTargetOptions(F); + I = std::make_unique(TargetTriple, CPU, FS, *this); + } + return I.get(); +} + +//===----------------------------------------------------------------------===// +// Pass Pipeline Configuration +//===----------------------------------------------------------------------===// + +namespace { +/// Z80 Code Generator Pass Configuration Options. +class Z80PassConfig : public TargetPassConfig { +public: + Z80PassConfig(Z80TargetMachine &TM, PassManagerBase &PM) + : TargetPassConfig(TM, PM) {} + + Z80TargetMachine &getZ80TargetMachine() const { + return getTM(); + } + + bool addIRTranslator() override; + void addPreLegalizeMachineIR() override; + bool addLegalizeMachineIR() override; + bool addRegBankSelect() override; + bool addGlobalInstructionSelect() override; + void addMachineSSAOptimization() override; + void addFastRegAlloc() override; + void addMachineLateOptimization() override; + + std::unique_ptr getCSEConfig() const override; +}; +} // end anonymous namespace + +TargetPassConfig *Z80TargetMachine::createPassConfig(PassManagerBase &PM) { + return new Z80PassConfig(*this, PM); +} + +bool Z80PassConfig::addIRTranslator() { + addPass(new IRTranslator); + return false; +} + +void Z80PassConfig::addPreLegalizeMachineIR() { + bool IsOptNone = getOptLevel() == CodeGenOpt::None; + addPass(createZ80PreLegalizeCombiner(IsOptNone)); +} + +bool Z80PassConfig::addLegalizeMachineIR() { + addPass(new Legalizer); + return false; +} + +bool Z80PassConfig::addRegBankSelect() { + addPass(new RegBankSelect); + return false; +} + +bool Z80PassConfig::addGlobalInstructionSelect() { + addPass(new InstructionSelect); + return false; +} + +void Z80PassConfig::addMachineSSAOptimization() { + addPass(createZ80PostSelectCombiner()); + TargetPassConfig::addMachineSSAOptimization(); +} + +void Z80PassConfig::addFastRegAlloc() { + // FastRegAlloc can't handle the register pressure on the Z80 + if (usingDefaultRegAlloc()) + addOptimizedRegAlloc(); + else + TargetPassConfig::addFastRegAlloc(); +} + +void Z80PassConfig::addMachineLateOptimization() { + addPass(createZ80MachineLateOptimizationPass()); +} + +std::unique_ptr Z80PassConfig::getCSEConfig() const { + return getStandardCSEConfigForOpt(TM->getOptLevel()); +} diff --git a/llvm/lib/Target/Z80/Z80TargetMachine.h b/llvm/lib/Target/Z80/Z80TargetMachine.h new file mode 100644 index 00000000000000..3c0a734b384a80 --- /dev/null +++ b/llvm/lib/Target/Z80/Z80TargetMachine.h @@ -0,0 +1,43 @@ +//===-- Z80TargetMachine.h - Define TargetMachine for the Z80 ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file declares the Sparc specific subclass of TargetMachine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_Z80_Z80TARGETMACHINE_H +#define LLVM_LIB_TARGET_Z80_Z80TARGETMACHINE_H + +#include "Z80Subtarget.h" +#include "llvm/Target/TargetMachine.h" + +namespace llvm { + +class Z80TargetMachine : public LLVMTargetMachine { + std::unique_ptr TLOF; + mutable StringMap> SubtargetMap; + +public: + Z80TargetMachine(const Target &T, const Triple &TT, StringRef CPU, + StringRef FS, const TargetOptions &Options, + Optional RM, Optional CM, + CodeGenOpt::Level OL, bool JIT); + ~Z80TargetMachine() override; + const Z80Subtarget *getSubtargetImpl(const Function &F) const override; + + // Set up the pass pipeline. + TargetPassConfig *createPassConfig(PassManagerBase &PM) override; + TargetLoweringObjectFile *getObjFileLowering() const override { + return TLOF.get(); + } +}; + +} // end namespace llvm + +#endif diff --git a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp index b45be99bece3b2..bcb8de215d8872 100644 --- a/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -1951,6 +1951,7 @@ StringRef ModuleAddressSanitizer::getGlobalMetadataSection() const { case Triple::COFF: return ".ASAN$GL"; case Triple::ELF: return "asan_globals"; case Triple::MachO: return "__DATA,__asan_globals,regular"; + case Triple::OMF: case Triple::Wasm: case Triple::XCOFF: report_fatal_error( diff --git a/llvm/test/CodeGen/Z80/compare16.ll b/llvm/test/CodeGen/Z80/compare16.ll new file mode 100644 index 00000000000000..e1f5ada7ae1f3d --- /dev/null +++ b/llvm/test/CodeGen/Z80/compare16.ll @@ -0,0 +1,1858 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=z80 < %s | FileCheck %s --check-prefixes=Z80 +; RUN: llc -mtriple=ez80-code16 < %s | FileCheck %s --check-prefixes=Z80-CODE16 +; RUN: llc -mtriple=ez80 < %s | FileCheck %s --check-prefixes=EZ80 + +declare void @external() + +define void @icmp.eq.i16(i16, i16) { +; Z80-LABEL: icmp.eq.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld e, (ix + 6) +; Z80-NEXT: ld d, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld e, (ix + 9) +; EZ80-NEXT: ld d, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.eq.i16.0(i16) { +; Z80-LABEL: icmp.eq.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: add hl, bc +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, bc +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: add hl, bc +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, bc +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: add.sis hl, bc +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, bc +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.eq.i16.64(i16) { +; Z80-LABEL: icmp.eq.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 64 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 64 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i16(i16, i16) { +; Z80-LABEL: icmp.ne.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld e, (ix + 6) +; Z80-NEXT: ld d, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld e, (ix + 9) +; EZ80-NEXT: ld d, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ne.i16.0(i16) { +; Z80-LABEL: icmp.ne.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: add hl, bc +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, bc +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: add hl, bc +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, bc +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: add.sis hl, bc +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, bc +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i16.64(i16) { +; Z80-LABEL: icmp.ne.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 64 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 64 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i16(i16, i16) { +; Z80-LABEL: icmp.ult.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld e, (ix + 6) +; Z80-NEXT: ld d, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld e, (ix + 9) +; EZ80-NEXT: ld d, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ult.i16.0(i16) { +; Z80-LABEL: icmp.ult.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 0 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i16.64(i16) { +; Z80-LABEL: icmp.ult.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 64 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 64 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i16(i16, i16) { +; Z80-LABEL: icmp.ule.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld e, (ix + 4) +; Z80-NEXT: ld d, (ix + 5) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld de, (ix + 4) +; Z80-CODE16-NEXT: ld hl, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld e, (ix + 6) +; EZ80-NEXT: ld d, (ix + 7) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ule.i16.0(i16) { +; Z80-LABEL: icmp.ule.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 1 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 1 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 1 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i16.64(i16) { +; Z80-LABEL: icmp.ule.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 65 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 65 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 65 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i16(i16, i16) { +; Z80-LABEL: icmp.ugt.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld e, (ix + 4) +; Z80-NEXT: ld d, (ix + 5) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld de, (ix + 4) +; Z80-CODE16-NEXT: ld hl, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld e, (ix + 6) +; EZ80-NEXT: ld d, (ix + 7) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ugt.i16.0(i16) { +; Z80-LABEL: icmp.ugt.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 1 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 1 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 1 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i16.64(i16) { +; Z80-LABEL: icmp.ugt.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 65 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 65 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 65 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i16(i16, i16) { +; Z80-LABEL: icmp.uge.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld e, (ix + 6) +; Z80-NEXT: ld d, (ix + 7) +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, (ix + 6) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld e, (ix + 9) +; EZ80-NEXT: ld d, (ix + 10) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.uge.i16.0(i16) { +; Z80-LABEL: icmp.uge.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 0 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i16.64(i16) { +; Z80-LABEL: icmp.uge.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, 64 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, 64 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i16(i16, i16) { +; Z80-LABEL: icmp.slt.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: push hl +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: ex (sp), hl +; Z80-NEXT: pop iy +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add iy, de +; Z80-NEXT: add hl, de +; Z80-NEXT: ld e, iyl +; Z80-NEXT: ld d, iyh +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld iy, (ix + 6) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add iy, de +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: lea de, iy + 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: push hl +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: ex (sp), hl +; EZ80-NEXT: pop iy +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis iy, de +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld e, iyl +; EZ80-NEXT: ld d, iyh +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.slt.i16.0(i16) { +; Z80-LABEL: icmp.slt.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i16.64(i16) { +; Z80-LABEL: icmp.slt.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: ld de, -32704 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: ld de, -32704 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld.sis de, -32704 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i16(i16, i16) { +; Z80-LABEL: icmp.sle.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: push hl +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ex (sp), hl +; Z80-NEXT: pop iy +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add iy, de +; Z80-NEXT: add hl, de +; Z80-NEXT: ld e, iyl +; Z80-NEXT: ld d, iyh +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld iy, (ix + 4) +; Z80-CODE16-NEXT: ld hl, (ix + 6) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add iy, de +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: lea de, iy + 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: push hl +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ex (sp), hl +; EZ80-NEXT: pop iy +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis iy, de +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld e, iyl +; EZ80-NEXT: ld d, iyh +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sle.i16.0(i16) { +; Z80-LABEL: icmp.sle.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: inc de +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: inc de +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: inc.sis de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i16.64(i16) { +; Z80-LABEL: icmp.sle.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: ld de, -32703 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: ld de, -32703 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld.sis de, -32703 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i16(i16, i16) { +; Z80-LABEL: icmp.sgt.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: push hl +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ex (sp), hl +; Z80-NEXT: pop iy +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add iy, de +; Z80-NEXT: add hl, de +; Z80-NEXT: ld e, iyl +; Z80-NEXT: ld d, iyh +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld iy, (ix + 4) +; Z80-CODE16-NEXT: ld hl, (ix + 6) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add iy, de +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: lea de, iy + 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: push hl +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ex (sp), hl +; EZ80-NEXT: pop iy +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis iy, de +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld e, iyl +; EZ80-NEXT: ld d, iyh +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sgt.i16.0(i16) { +; Z80-LABEL: icmp.sgt.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: inc de +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: inc de +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: inc.sis de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i16.64(i16) { +; Z80-LABEL: icmp.sgt.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: ld de, -32703 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: ld de, -32703 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld.sis de, -32703 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i16(i16, i16) { +; Z80-LABEL: icmp.sge.i16: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: push hl +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: ld h, (ix + 7) +; Z80-NEXT: ex (sp), hl +; Z80-NEXT: pop iy +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add iy, de +; Z80-NEXT: add hl, de +; Z80-NEXT: ld e, iyl +; Z80-NEXT: ld d, iyh +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i16: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld iy, (ix + 6) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add iy, de +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: lea de, iy + 0 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i16: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: push hl +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: ld h, (ix + 10) +; EZ80-NEXT: ex (sp), hl +; EZ80-NEXT: pop iy +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis iy, de +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld e, iyl +; EZ80-NEXT: ld d, iyh +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i16 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sge.i16.0(i16) { +; Z80-LABEL: icmp.sge.i16.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i16.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i16.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i16 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i16.64(i16) { +; Z80-LABEL: icmp.sge.i16.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld h, (ix + 5) +; Z80-NEXT: ld de, -32768 +; Z80-NEXT: add hl, de +; Z80-NEXT: ld de, -32704 +; Z80-NEXT: or a, a +; Z80-NEXT: sbc hl, de +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i16.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld hl, (ix + 4) +; Z80-CODE16-NEXT: ld de, -32768 +; Z80-CODE16-NEXT: add hl, de +; Z80-CODE16-NEXT: ld de, -32704 +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: sbc hl, de +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i16.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld h, (ix + 7) +; EZ80-NEXT: ld.sis de, -32768 +; EZ80-NEXT: add.sis hl, de +; EZ80-NEXT: ld.sis de, -32704 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc.sis hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i16 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} diff --git a/llvm/test/CodeGen/Z80/compare24.ll b/llvm/test/CodeGen/Z80/compare24.ll new file mode 100644 index 00000000000000..2d22a6e61ed2d9 --- /dev/null +++ b/llvm/test/CodeGen/Z80/compare24.ll @@ -0,0 +1,724 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=ez80 < %s | FileCheck %s --check-prefixes=EZ80 + +declare void @external() + +define void @icmp.eq.i24(i24, i24) { +; EZ80-LABEL: icmp.eq.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.eq.i24.0(i24) { +; EZ80-LABEL: icmp.eq.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: add hl, bc +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, bc +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.eq.i24.64(i24) { +; EZ80-LABEL: icmp.eq.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i24(i24, i24) { +; EZ80-LABEL: icmp.ne.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ne.i24.0(i24) { +; EZ80-LABEL: icmp.ne.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: add hl, bc +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, bc +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i24.64(i24) { +; EZ80-LABEL: icmp.ne.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i24(i24, i24) { +; EZ80-LABEL: icmp.ult.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ult.i24.0(i24) { +; EZ80-LABEL: icmp.ult.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i24.64(i24) { +; EZ80-LABEL: icmp.ult.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i24(i24, i24) { +; EZ80-LABEL: icmp.ule.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld de, (ix + 6) +; EZ80-NEXT: ld hl, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ule.i24.0(i24) { +; EZ80-LABEL: icmp.ule.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 1 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i24.64(i24) { +; EZ80-LABEL: icmp.ule.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 65 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i24(i24, i24) { +; EZ80-LABEL: icmp.ugt.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld de, (ix + 6) +; EZ80-NEXT: ld hl, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ugt.i24.0(i24) { +; EZ80-LABEL: icmp.ugt.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 1 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i24.64(i24) { +; EZ80-LABEL: icmp.ugt.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 65 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i24(i24, i24) { +; EZ80-LABEL: icmp.uge.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, (ix + 9) +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.uge.i24.0(i24) { +; EZ80-LABEL: icmp.uge.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i24.64(i24) { +; EZ80-LABEL: icmp.uge.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, 64 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i24(i24, i24) { +; EZ80-LABEL: icmp.slt.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld iy, (ix + 9) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add iy, de +; EZ80-NEXT: add hl, de +; EZ80-NEXT: lea de, iy + 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.slt.i24.0(i24) { +; EZ80-LABEL: icmp.slt.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i24.64(i24) { +; EZ80-LABEL: icmp.slt.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: ld de, -8388544 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i24(i24, i24) { +; EZ80-LABEL: icmp.sle.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld iy, (ix + 6) +; EZ80-NEXT: ld hl, (ix + 9) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add iy, de +; EZ80-NEXT: add hl, de +; EZ80-NEXT: lea de, iy + 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sle.i24.0(i24) { +; EZ80-LABEL: icmp.sle.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: inc de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i24.64(i24) { +; EZ80-LABEL: icmp.sle.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: ld de, -8388543 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i24(i24, i24) { +; EZ80-LABEL: icmp.sgt.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld iy, (ix + 6) +; EZ80-NEXT: ld hl, (ix + 9) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add iy, de +; EZ80-NEXT: add hl, de +; EZ80-NEXT: lea de, iy + 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sgt.i24.0(i24) { +; EZ80-LABEL: icmp.sgt.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: inc de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i24.64(i24) { +; EZ80-LABEL: icmp.sgt.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: ld de, -8388543 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i24(i24, i24) { +; EZ80-LABEL: icmp.sge.i24: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld iy, (ix + 9) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add iy, de +; EZ80-NEXT: add hl, de +; EZ80-NEXT: lea de, iy + 0 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i24 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sge.i24.0(i24) { +; EZ80-LABEL: icmp.sge.i24.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i24 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i24.64(i24) { +; EZ80-LABEL: icmp.sge.i24.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld hl, (ix + 6) +; EZ80-NEXT: ld de, -8388608 +; EZ80-NEXT: add hl, de +; EZ80-NEXT: ld de, -8388544 +; EZ80-NEXT: or a, a +; EZ80-NEXT: sbc hl, de +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i24 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} diff --git a/llvm/test/CodeGen/Z80/compare8.ll b/llvm/test/CodeGen/Z80/compare8.ll new file mode 100644 index 00000000000000..36c66e5074905f --- /dev/null +++ b/llvm/test/CodeGen/Z80/compare8.ll @@ -0,0 +1,1578 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=z80 < %s | FileCheck %s --check-prefixes=Z80 +; RUN: llc -mtriple=ez80-code16 < %s | FileCheck %s --check-prefixes=Z80-CODE16 +; RUN: llc -mtriple=ez80 < %s | FileCheck %s --check-prefixes=EZ80 + +declare void @external() + +define void @icmp.eq.i8(i8, i8) { +; Z80-LABEL: icmp.eq.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.eq.i8.0(i8) { +; Z80-LABEL: icmp.eq.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: or a, a +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: or a, a +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.eq.i8.64(i8) { +; Z80-LABEL: icmp.eq.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call z, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.eq.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call z, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.eq.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call z, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp eq i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i8(i8, i8) { +; Z80-LABEL: icmp.ne.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ne.i8.0(i8) { +; Z80-LABEL: icmp.ne.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: or a, a +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: or a, a +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: or a, a +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ne.i8.64(i8) { +; Z80-LABEL: icmp.ne.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nz, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ne.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nz, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ne.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nz, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ne i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i8(i8, i8) { +; Z80-LABEL: icmp.ult.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ult.i8.0(i8) { +; Z80-LABEL: icmp.ult.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 0 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 0 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 0 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ult.i8.64(i8) { +; Z80-LABEL: icmp.ult.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ult.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ult.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ult i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i8(i8, i8) { +; Z80-LABEL: icmp.ule.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld a, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld l, (ix + 4) +; Z80-CODE16-NEXT: ld a, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld a, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ule.i8.0(i8) { +; Z80-LABEL: icmp.ule.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 1 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 1 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 1 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ule.i8.64(i8) { +; Z80-LABEL: icmp.ule.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 65 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ule.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 65 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ule.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 65 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ule i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i8(i8, i8) { +; Z80-LABEL: icmp.ugt.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld a, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld l, (ix + 4) +; Z80-CODE16-NEXT: ld a, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld a, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.ugt.i8.0(i8) { +; Z80-LABEL: icmp.ugt.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 1 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 1 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 1 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.ugt.i8.64(i8) { +; Z80-LABEL: icmp.ugt.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 65 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.ugt.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 65 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.ugt.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 65 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp ugt i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i8(i8, i8) { +; Z80-LABEL: icmp.uge.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: cp a, l +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: cp a, l +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: cp a, l +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.uge.i8.0(i8) { +; Z80-LABEL: icmp.uge.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 0 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 0 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 0 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.uge.i8.64(i8) { +; Z80-LABEL: icmp.uge.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: cp a, 64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.uge.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: cp a, 64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.uge.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: cp a, 64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp uge i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i8(i8, i8) { +; Z80-LABEL: icmp.slt.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld a, (ix + 6) +; Z80-NEXT: add a, -128 +; Z80-NEXT: ld e, a +; Z80-NEXT: ld a, l +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, e +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld l, (ix + 4) +; Z80-CODE16-NEXT: ld a, (ix + 6) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: ld e, a +; Z80-CODE16-NEXT: ld a, l +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, e +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld a, (ix + 9) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: ld e, a +; EZ80-NEXT: ld a, l +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, e +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.slt.i8.0(i8) { +; Z80-LABEL: icmp.slt.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -128 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -128 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -128 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.slt.i8.64(i8) { +; Z80-LABEL: icmp.slt.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.slt.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.slt.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp slt i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i8(i8, i8) { +; Z80-LABEL: icmp.sle.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: add a, -128 +; Z80-NEXT: ld e, a +; Z80-NEXT: ld a, l +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, e +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: ld e, a +; Z80-CODE16-NEXT: ld a, l +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, e +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: ld e, a +; EZ80-NEXT: ld a, l +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, e +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sle.i8.0(i8) { +; Z80-LABEL: icmp.sle.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -127 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -127 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -127 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sle.i8.64(i8) { +; Z80-LABEL: icmp.sle.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -63 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sle.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -63 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sle.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -63 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sle i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i8(i8, i8) { +; Z80-LABEL: icmp.sgt.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: ld l, (ix + 6) +; Z80-NEXT: add a, -128 +; Z80-NEXT: ld e, a +; Z80-NEXT: ld a, l +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, e +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call c, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: ld l, (ix + 6) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: ld e, a +; Z80-CODE16-NEXT: ld a, l +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, e +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call c, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: ld l, (ix + 9) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: ld e, a +; EZ80-NEXT: ld a, l +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, e +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call c, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sgt.i8.0(i8) { +; Z80-LABEL: icmp.sgt.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -127 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -127 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -127 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sgt.i8.64(i8) { +; Z80-LABEL: icmp.sgt.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -63 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sgt.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -63 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sgt.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -63 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sgt i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i8(i8, i8) { +; Z80-LABEL: icmp.sge.i8: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld l, (ix + 4) +; Z80-NEXT: ld a, (ix + 6) +; Z80-NEXT: add a, -128 +; Z80-NEXT: ld e, a +; Z80-NEXT: ld a, l +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, e +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i8: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld l, (ix + 4) +; Z80-CODE16-NEXT: ld a, (ix + 6) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: ld e, a +; Z80-CODE16-NEXT: ld a, l +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, e +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i8: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld l, (ix + 6) +; EZ80-NEXT: ld a, (ix + 9) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: ld e, a +; EZ80-NEXT: ld a, l +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, e +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i8 %0, %1 + br i1 %3, label %4, label %5 + call void @external() + br label %5 + ret void +} + +define void @icmp.sge.i8.0(i8) { +; Z80-LABEL: icmp.sge.i8.0: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -128 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i8.0: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -128 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i8.0: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -128 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i8 %0, 0 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} + +define void @icmp.sge.i8.64(i8) { +; Z80-LABEL: icmp.sge.i8.64: +; Z80: ; %bb.0: +; Z80-NEXT: push ix +; Z80-NEXT: ld ix, 0 +; Z80-NEXT: add ix, sp +; Z80-NEXT: ld a, (ix + 4) +; Z80-NEXT: add a, -128 +; Z80-NEXT: cp a, -64 +; Z80-NEXT: ; %bb.1: +; Z80-NEXT: call nc, _external +; Z80-NEXT: ; %bb.2: +; Z80-NEXT: pop ix +; Z80-NEXT: ret +; Z80-NEXT: section .text +; +; Z80-CODE16-LABEL: icmp.sge.i8.64: +; Z80-CODE16: ; %bb.0: +; Z80-CODE16-NEXT: push ix +; Z80-CODE16-NEXT: ld ix, 0 +; Z80-CODE16-NEXT: add ix, sp +; Z80-CODE16-NEXT: ld a, (ix + 4) +; Z80-CODE16-NEXT: add a, -128 +; Z80-CODE16-NEXT: cp a, -64 +; Z80-CODE16-NEXT: ; %bb.1: +; Z80-CODE16-NEXT: call nc, _external +; Z80-CODE16-NEXT: ; %bb.2: +; Z80-CODE16-NEXT: pop ix +; Z80-CODE16-NEXT: ret +; Z80-CODE16-NEXT: section .text +; +; EZ80-LABEL: icmp.sge.i8.64: +; EZ80: ; %bb.0: +; EZ80-NEXT: push ix +; EZ80-NEXT: ld ix, 0 +; EZ80-NEXT: add ix, sp +; EZ80-NEXT: ld a, (ix + 6) +; EZ80-NEXT: add a, -128 +; EZ80-NEXT: cp a, -64 +; EZ80-NEXT: ; %bb.1: +; EZ80-NEXT: call nc, _external +; EZ80-NEXT: ; %bb.2: +; EZ80-NEXT: pop ix +; EZ80-NEXT: ret +; EZ80-NEXT: section .text + icmp sge i8 %0, 64 + br i1 %2, label %3, label %4 + call void @external() + br label %4 + ret void +} diff --git a/llvm/test/CodeGen/Z80/intrinsics.ll b/llvm/test/CodeGen/Z80/intrinsics.ll new file mode 100644 index 00000000000000..b675ed70569d51 --- /dev/null +++ b/llvm/test/CodeGen/Z80/intrinsics.ll @@ -0,0 +1,371 @@ +; RUN: llc -mtriple=z80 < %s +; RUN: llc -mtriple=ez80-code16 < %s +; RUN: llc -mtriple=ez80 < %s + +declare i8 @llvm.bitreverse.i8(i8) +define i8 @bitreverse.i8(i8) { + call i8 @llvm.bitreverse.i8(i8 %0) + ret i8 %2 +} +declare i16 @llvm.bitreverse.i16(i16) +define i16 @bitreverse.i16(i16) { + call i16 @llvm.bitreverse.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.bitreverse.i32(i32) +define i32 @bitreverse.i32(i32) { + call i32 @llvm.bitreverse.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.bitreverse.i64(i64) +define i64 @bitreverse.i64(i64) { + call i64 @llvm.bitreverse.i64(i64 %0) + ret i64 %2 +} + +declare i16 @llvm.bswap.i16(i16) +define i16 @bswap.i16(i16) { + call i16 @llvm.bswap.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.bswap.i32(i32) +define i32 @bswap.i32(i32) { + call i32 @llvm.bswap.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.bswap.i64(i64) +define i64 @bswap.i64(i64) { + call i64 @llvm.bswap.i64(i64 %0) + ret i64 %2 +} + +declare i8 @llvm.ctpop.i8(i8) +define i8 @ctpop.i8(i8) { + call i8 @llvm.ctpop.i8(i8 %0) + ret i8 %2 +} +declare i16 @llvm.ctpop.i16(i16) +define i16 @ctpop.i16(i16) { + call i16 @llvm.ctpop.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.ctpop.i32(i32) +define i32 @ctpop.i32(i32) { + call i32 @llvm.ctpop.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.ctpop.i64(i64) +define i64 @ctpop.i64(i64) { + call i64 @llvm.ctpop.i64(i64 %0) + ret i64 %2 +} + +declare i8 @llvm.ctlz.i8(i8) +define i8 @ctlz.i8(i8) { + call i8 @llvm.ctlz.i8(i8 %0) + ret i8 %2 +} +declare i16 @llvm.ctlz.i16(i16) +define i16 @ctlz.i16(i16) { + call i16 @llvm.ctlz.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.ctlz.i32(i32) +define i32 @ctlz.i32(i32) { + call i32 @llvm.ctlz.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.ctlz.i64(i64) +define i64 @ctlz.i64(i64) { + call i64 @llvm.ctlz.i64(i64 %0) + ret i64 %2 +} + +declare i8 @llvm.cttz.i8(i8) +define i8 @cttz.i8(i8) { + call i8 @llvm.cttz.i8(i8 %0) + ret i8 %2 +} +declare i16 @llvm.cttz.i16(i16) +define i16 @cttz.i16(i16) { + call i16 @llvm.cttz.i16(i16 %0) + ret i16 %2 +} +declare i32 @llvm.cttz.i32(i32) +define i32 @cttz.i32(i32) { + call i32 @llvm.cttz.i32(i32 %0) + ret i32 %2 +} +declare i64 @llvm.cttz.i64(i64) +define i64 @cttz.i64(i64) { + call i64 @llvm.cttz.i64(i64 %0) + ret i64 %2 +} + +declare i8 @llvm.fshl.i8(i8, i8, i8) +define i8 @fshl.i8(i8, i8, i8) { + call i8 @llvm.fshl.i8(i8 %0, i8 %1, i8 %2) + ret i8 %4 +} +declare i16 @llvm.fshl.i16(i16, i16, i16) +define i16 @fshl.i16(i16, i16, i16) { + call i16 @llvm.fshl.i16(i16 %0, i16 %1, i16 %2) + ret i16 %4 +} +declare i32 @llvm.fshl.i32(i32, i32, i32) +define i32 @fshl.i32(i32, i32, i32) { + call i32 @llvm.fshl.i32(i32 %0, i32 %1, i32 %2) + ret i32 %4 +} +declare i64 @llvm.fshl.i64(i64, i64, i64) +define i64 @fshl.i64(i64, i64, i64) { + call i64 @llvm.fshl.i64(i64 %0, i64 %1, i64 %2) + ret i64 %4 +} + +declare i8 @llvm.fshr.i8(i8, i8, i8) +define i8 @fshr.i8(i8, i8, i8) { + call i8 @llvm.fshr.i8(i8 %0, i8 %1, i8 %2) + ret i8 %4 +} +declare i16 @llvm.fshr.i16(i16, i16, i16) +define i16 @fshr.i16(i16, i16, i16) { + call i16 @llvm.fshr.i16(i16 %0, i16 %1, i16 %2) + ret i16 %4 +} +declare i32 @llvm.fshr.i32(i32, i32, i32) +define i32 @fshr.i32(i32, i32, i32) { + call i32 @llvm.fshr.i32(i32 %0, i32 %1, i32 %2) + ret i32 %4 +} +declare i64 @llvm.fshr.i64(i64, i64, i64) +define i64 @fshr.i64(i64, i64, i64) { + call i64 @llvm.fshr.i64(i64 %0, i64 %1, i64 %2) + ret i64 %4 +} + +declare {i8, i1} @llvm.sadd.with.overflow.i8(i8, i8) +define i1 @sadd.with.overflow.i8(i8, i8) { + call {i8, i1} @llvm.sadd.with.overflow.i8(i8 %0, i8 %1) + extractvalue {i8, i1} %3, 1 + ret i1 %4 +} +declare {i16, i1} @llvm.sadd.with.overflow.i16(i16, i16) +define i1 @sadd.with.overflow.i16(i16, i16) { + call {i16, i1} @llvm.sadd.with.overflow.i16(i16 %0, i16 %1) + extractvalue {i16, i1} %3, 1 + ret i1 %4 +} +declare {i32, i1} @llvm.sadd.with.overflow.i32(i32, i32) +define i1 @sadd.with.overflow.i32(i32, i32) { + call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %0, i32 %1) + extractvalue {i32, i1} %3, 1 + ret i1 %4 +} +declare {i64, i1} @llvm.sadd.with.overflow.i64(i64, i64) +define i1 @sadd.with.overflow.i64(i64, i64) { + call {i64, i1} @llvm.sadd.with.overflow.i64(i64 %0, i64 %1) + extractvalue {i64, i1} %3, 1 + ret i1 %4 +} + +declare {i8, i1} @llvm.uadd.with.overflow.i8(i8, i8) +define i1 @uadd.with.overflow.i8(i8, i8) { + call {i8, i1} @llvm.uadd.with.overflow.i8(i8 %0, i8 %1) + extractvalue {i8, i1} %3, 1 + ret i1 %4 +} +declare {i16, i1} @llvm.uadd.with.overflow.i16(i16, i16) +define i1 @uadd.with.overflow.i16(i16, i16) { + call {i16, i1} @llvm.uadd.with.overflow.i16(i16 %0, i16 %1) + extractvalue {i16, i1} %3, 1 + ret i1 %4 +} +declare {i32, i1} @llvm.uadd.with.overflow.i32(i32, i32) +define i1 @uadd.with.overflow.i32(i32, i32) { + call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %0, i32 %1) + extractvalue {i32, i1} %3, 1 + ret i1 %4 +} +declare {i64, i1} @llvm.uadd.with.overflow.i64(i64, i64) +define i1 @uadd.with.overflow.i64(i64, i64) { + call {i64, i1} @llvm.uadd.with.overflow.i64(i64 %0, i64 %1) + extractvalue {i64, i1} %3, 1 + ret i1 %4 +} + +declare {i8, i1} @llvm.ssub.with.overflow.i8(i8, i8) +define i1 @ssub.with.overflow.i8(i8, i8) { + call {i8, i1} @llvm.ssub.with.overflow.i8(i8 %0, i8 %1) + extractvalue {i8, i1} %3, 1 + ret i1 %4 +} +declare {i16, i1} @llvm.ssub.with.overflow.i16(i16, i16) +define i1 @ssub.with.overflow.i16(i16, i16) { + call {i16, i1} @llvm.ssub.with.overflow.i16(i16 %0, i16 %1) + extractvalue {i16, i1} %3, 1 + ret i1 %4 +} +declare {i32, i1} @llvm.ssub.with.overflow.i32(i32, i32) +define i1 @ssub.with.overflow.i32(i32, i32) { + call {i32, i1} @llvm.ssub.with.overflow.i32(i32 %0, i32 %1) + extractvalue {i32, i1} %3, 1 + ret i1 %4 +} +declare {i64, i1} @llvm.ssub.with.overflow.i64(i64, i64) +define i1 @ssub.with.overflow.i64(i64, i64) { + call {i64, i1} @llvm.ssub.with.overflow.i64(i64 %0, i64 %1) + extractvalue {i64, i1} %3, 1 + ret i1 %4 +} + +declare {i8, i1} @llvm.usub.with.overflow.i8(i8, i8) +define i1 @usub.with.overflow.i8(i8, i8) { + call {i8, i1} @llvm.usub.with.overflow.i8(i8 %0, i8 %1) + extractvalue {i8, i1} %3, 1 + ret i1 %4 +} +declare {i16, i1} @llvm.usub.with.overflow.i16(i16, i16) +define i1 @usub.with.overflow.i16(i16, i16) { + call {i16, i1} @llvm.usub.with.overflow.i16(i16 %0, i16 %1) + extractvalue {i16, i1} %3, 1 + ret i1 %4 +} +declare {i32, i1} @llvm.usub.with.overflow.i32(i32, i32) +define i1 @usub.with.overflow.i32(i32, i32) { + call {i32, i1} @llvm.usub.with.overflow.i32(i32 %0, i32 %1) + extractvalue {i32, i1} %3, 1 + ret i1 %4 +} +declare {i64, i1} @llvm.usub.with.overflow.i64(i64, i64) +define i1 @usub.with.overflow.i64(i64, i64) { + call {i64, i1} @llvm.usub.with.overflow.i64(i64 %0, i64 %1) + extractvalue {i64, i1} %3, 1 + ret i1 %4 +} + +declare i8 @llvm.sadd.sat.i8(i8, i8) +define i8 @sadd.sat.i8(i8, i8) { + call i8 @llvm.sadd.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.sadd.sat.i16(i16, i16) +define i16 @sadd.sat.i16(i16, i16) { + call i16 @llvm.sadd.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.sadd.sat.i32(i32, i32) +define i32 @sadd.sat.i32(i32, i32) { + call i32 @llvm.sadd.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.sadd.sat.i64(i64, i64) +define i64 @sadd.sat.i64(i64, i64) { + call i64 @llvm.sadd.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.uadd.sat.i8(i8, i8) +define i8 @uadd.sat.i8(i8, i8) { + call i8 @llvm.uadd.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.uadd.sat.i16(i16, i16) +define i16 @uadd.sat.i16(i16, i16) { + call i16 @llvm.uadd.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.uadd.sat.i32(i32, i32) +define i32 @uadd.sat.i32(i32, i32) { + call i32 @llvm.uadd.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.uadd.sat.i64(i64, i64) +define i64 @uadd.sat.i64(i64, i64) { + call i64 @llvm.uadd.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.ssub.sat.i8(i8, i8) +define i8 @ssub.sat.i8(i8, i8) { + call i8 @llvm.ssub.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.ssub.sat.i16(i16, i16) +define i16 @ssub.sat.i16(i16, i16) { + call i16 @llvm.ssub.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.ssub.sat.i32(i32, i32) +define i32 @ssub.sat.i32(i32, i32) { + call i32 @llvm.ssub.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.ssub.sat.i64(i64, i64) +define i64 @ssub.sat.i64(i64, i64) { + call i64 @llvm.ssub.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.usub.sat.i8(i8, i8) +define i8 @usub.sat.i8(i8, i8) { + call i8 @llvm.usub.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.usub.sat.i16(i16, i16) +define i16 @usub.sat.i16(i16, i16) { + call i16 @llvm.usub.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.usub.sat.i32(i32, i32) +define i32 @usub.sat.i32(i32, i32) { + call i32 @llvm.usub.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.usub.sat.i64(i64, i64) +define i64 @usub.sat.i64(i64, i64) { + call i64 @llvm.usub.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.smul.sat.i8(i8, i8) +define i8 @smul.sat.i8(i8, i8) { + call i8 @llvm.smul.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.smul.sat.i16(i16, i16) +define i16 @smul.sat.i16(i16, i16) { + call i16 @llvm.smul.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.smul.sat.i32(i32, i32) +define i32 @smul.sat.i32(i32, i32) { + call i32 @llvm.smul.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.smul.sat.i64(i64, i64) +define i64 @smul.sat.i64(i64, i64) { + call i64 @llvm.smul.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} + +declare i8 @llvm.umul.sat.i8(i8, i8) +define i8 @umul.sat.i8(i8, i8) { + call i8 @llvm.umul.sat.i8(i8 %0, i8 %1) + ret i8 %3 +} +declare i16 @llvm.umul.sat.i16(i16, i16) +define i16 @umul.sat.i16(i16, i16) { + call i16 @llvm.umul.sat.i16(i16 %0, i16 %1) + ret i16 %3 +} +declare i32 @llvm.umul.sat.i32(i32, i32) +define i32 @umul.sat.i32(i32, i32) { + call i32 @llvm.umul.sat.i32(i32 %0, i32 %1) + ret i32 %3 +} +declare i64 @llvm.umul.sat.i64(i64, i64) +define i64 @umul.sat.i64(i64, i64) { + call i64 @llvm.umul.sat.i64(i64 %0, i64 %1) + ret i64 %3 +} diff --git a/llvm/test/CodeGen/Z80/intrinsics24.ll b/llvm/test/CodeGen/Z80/intrinsics24.ll new file mode 100644 index 00000000000000..22b87820c3c883 --- /dev/null +++ b/llvm/test/CodeGen/Z80/intrinsics24.ll @@ -0,0 +1,101 @@ +; RUN: llc -mtriple=ez80 < %s + +declare i24 @llvm.bitreverse.i24(i24) +define i24 @bitreverse.i24(i24) { + call i24 @llvm.bitreverse.i24(i24 %0) + ret i24 %2 +} + +declare i24 @llvm.ctpop.i24(i24) +define i24 @ctpop.i24(i24) { + call i24 @llvm.ctpop.i24(i24 %0) + ret i24 %2 +} + +declare i24 @llvm.ctlz.i24(i24) +define i24 @ctlz.i24(i24) { + call i24 @llvm.ctlz.i24(i24 %0) + ret i24 %2 +} + +declare i24 @llvm.cttz.i24(i24) +define i24 @cttz.i24(i24) { + call i24 @llvm.cttz.i24(i24 %0) + ret i24 %2 +} + +declare i24 @llvm.fshl.i24(i24, i24, i24) +define i24 @fshl.i24(i24, i24, i24) { + call i24 @llvm.fshl.i24(i24 %0, i24 %1, i24 %2) + ret i24 %4 +} + +declare i24 @llvm.fshr.i24(i24, i24, i24) +define i24 @fshr.i24(i24, i24, i24) { + call i24 @llvm.fshr.i24(i24 %0, i24 %1, i24 %2) + ret i24 %4 +} + +declare {i24, i1} @llvm.sadd.with.overflow.i24(i24, i24) +define i1 @sadd.with.overflow.i24(i24, i24) { + call {i24, i1} @llvm.sadd.with.overflow.i24(i24 %0, i24 %1) + extractvalue {i24, i1} %3, 1 + ret i1 %4 +} + +declare {i24, i1} @llvm.uadd.with.overflow.i24(i24, i24) +define i1 @uadd.with.overflow.i24(i24, i24) { + call {i24, i1} @llvm.uadd.with.overflow.i24(i24 %0, i24 %1) + extractvalue {i24, i1} %3, 1 + ret i1 %4 +} + +declare {i24, i1} @llvm.ssub.with.overflow.i24(i24, i24) +define i1 @ssub.with.overflow.i24(i24, i24) { + call {i24, i1} @llvm.ssub.with.overflow.i24(i24 %0, i24 %1) + extractvalue {i24, i1} %3, 1 + ret i1 %4 +} + +declare {i24, i1} @llvm.usub.with.overflow.i24(i24, i24) +define i1 @usub.with.overflow.i24(i24, i24) { + call {i24, i1} @llvm.usub.with.overflow.i24(i24 %0, i24 %1) + extractvalue {i24, i1} %3, 1 + ret i1 %4 +} + +declare i24 @llvm.sadd.sat.i24(i24, i24) +define i24 @sadd.sat.i24(i24, i24) { + call i24 @llvm.sadd.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.uadd.sat.i24(i24, i24) +define i24 @uadd.sat.i24(i24, i24) { + call i24 @llvm.uadd.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.ssub.sat.i24(i24, i24) +define i24 @ssub.sat.i24(i24, i24) { + call i24 @llvm.ssub.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.usub.sat.i24(i24, i24) +define i24 @usub.sat.i24(i24, i24) { + call i24 @llvm.usub.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.smul.sat.i24(i24, i24) +define i24 @smul.sat.i24(i24, i24) { + call i24 @llvm.smul.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} + +declare i24 @llvm.umul.sat.i24(i24, i24) +define i24 @umul.sat.i24(i24, i24) { + call i24 @llvm.umul.sat.i24(i24 %0, i24 %1) + ret i24 %3 +} diff --git a/llvm/test/CodeGen/Z80/ops.ll b/llvm/test/CodeGen/Z80/ops.ll new file mode 100644 index 00000000000000..8ee9c390d57fb2 --- /dev/null +++ b/llvm/test/CodeGen/Z80/ops.ll @@ -0,0 +1,778 @@ +; RUN: llc -mtriple=z80 < %s +; RUN: llc -mtriple=ez80-code16 < %s +; RUN: llc -mtriple=ez80 < %s + +define void @ret.void() { + ret void +} +define i8 @ret.i8(i8) { + ret i8 %0 +} +define i16 @ret.i16(i16) { + ret i16 %0 +} +define i24 @ret.i24(i24) { + ret i24 %0 +} +define i32 @ret.i32(i32) { + ret i32 %0 +} +define i48 @ret.i48(i48) { + ret i48 %0 +} +define i64 @ret.i64(i64) { + ret i64 %0 +} +define float @ret.float(float) { + ret float %0 +} +define double @ret.double(double) { + ret double %0 +} + +define void @br() { + br label %1 + ret void +} +define i8 @br.i1(i1) { + br i1 %0, label %2, label %3 + ret i8 1 + ret i8 0 +} + +define i8 @switch(i8) { + switch i8 %0, label %2 [ i8 0, label %3 + i8 1, label %4 + i8 2, label %5 + i8 3, label %6 ] + ret i8 -1 + ret i8 0 + ret i8 1 + ret i8 2 + ret i8 3 +} + +define i8 @indirectbr(i8*) { + indirectbr i8* %0, [label %2, label %3] + ret i8 1 + ret i8 0 +} + +define i8 @shl.i8(i8, i8) { + shl i8 %0, %1 + ret i8 %3 +} +define i16 @shl.i16(i16, i16) { + shl i16 %0, %1 + ret i16 %3 +} +define i32 @shl.i32(i32, i32) { + shl i32 %0, %1 + ret i32 %3 +} +define i64 @shl.i64(i64, i64) { + shl i64 %0, %1 + ret i64 %3 +} + +define i8 @lshr.i8(i8, i8) { + lshr i8 %0, %1 + ret i8 %3 +} +define i16 @lshr.i16(i16, i16) { + lshr i16 %0, %1 + ret i16 %3 +} +define i32 @lshr.i32(i32, i32) { + lshr i32 %0, %1 + ret i32 %3 +} +define i64 @lshr.i64(i64, i64) { + lshr i64 %0, %1 + ret i64 %3 +} + +define i8 @ashr.i8(i8, i8) { + ashr i8 %0, %1 + ret i8 %3 +} +define i16 @ashr.i16(i16, i16) { + ashr i16 %0, %1 + ret i16 %3 +} +define i32 @ashr.i32(i32, i32) { + ashr i32 %0, %1 + ret i32 %3 +} +define i64 @ashr.i64(i64, i64) { + ashr i64 %0, %1 + ret i64 %3 +} + +define i8 @and.i8(i8, i8) { + and i8 %0, %1 + ret i8 %3 +} +define i16 @and.i16(i16, i16) { + and i16 %0, %1 + ret i16 %3 +} +define i32 @and.i32(i32, i32) { + and i32 %0, %1 + ret i32 %3 +} +define i64 @and.i64(i64, i64) { + and i64 %0, %1 + ret i64 %3 +} + +define i8 @or.i8(i8, i8) { + or i8 %0, %1 + ret i8 %3 +} +define i16 @or.i16(i16, i16) { + or i16 %0, %1 + ret i16 %3 +} +define i32 @or.i32(i32, i32) { + or i32 %0, %1 + ret i32 %3 +} +define i64 @or.i64(i64, i64) { + or i64 %0, %1 + ret i64 %3 +} + +define i8 @xor.i8(i8, i8) { + xor i8 %0, %1 + ret i8 %3 +} +define i16 @xor.i16(i16, i16) { + xor i16 %0, %1 + ret i16 %3 +} +define i32 @xor.i32(i32, i32) { + xor i32 %0, %1 + ret i32 %3 +} +define i64 @xor.i64(i64, i64) { + xor i64 %0, %1 + ret i64 %3 +} + +define i8 @add.i8(i8, i8) { + add i8 %0, %1 + ret i8 %3 +} +define i16 @add.i16(i16, i16) { + add i16 %0, %1 + ret i16 %3 +} +define i24 @add.i24(i24, i24) { + add i24 %0, %1 + ret i24 %3 +} +define i32 @add.i32(i32, i32) { + add i32 %0, %1 + ret i32 %3 +} +define i48 @add.i48(i48, i48) { + add i48 %0, %1 + ret i48 %3 +} +define i64 @add.i64(i64, i64) { + add i64 %0, %1 + ret i64 %3 +} + +define i8 @sub.i8(i8, i8) { + sub i8 %0, %1 + ret i8 %3 +} +define i16 @sub.i16(i16, i16) { + sub i16 %0, %1 + ret i16 %3 +} +define i24 @sub.i24(i24, i24) { + sub i24 %0, %1 + ret i24 %3 +} +define i32 @sub.i32(i32, i32) { + sub i32 %0, %1 + ret i32 %3 +} +define i48 @sub.i48(i48, i48) { + sub i48 %0, %1 + ret i48 %3 +} +define i64 @sub.i64(i64, i64) { + sub i64 %0, %1 + ret i64 %3 +} + +define i8 @mul.i8(i8, i8) { + mul i8 %0, %1 + ret i8 %3 +} +define i16 @mul.i16(i16, i16) { + mul i16 %0, %1 + ret i16 %3 +} +define i32 @mul.i32(i32, i32) { + mul i32 %0, %1 + ret i32 %3 +} +define i64 @mul.i64(i64, i64) { + mul i64 %0, %1 + ret i64 %3 +} + +define i8 @udiv.i8(i8, i8) { + udiv i8 %0, %1 + ret i8 %3 +} +define i16 @udiv.i16(i16, i16) { + udiv i16 %0, %1 + ret i16 %3 +} +define i32 @udiv.i32(i32, i32) { + udiv i32 %0, %1 + ret i32 %3 +} +define i64 @udiv.i64(i64, i64) { + udiv i64 %0, %1 + ret i64 %3 +} + +define i8 @sdiv.i8(i8, i8) { + sdiv i8 %0, %1 + ret i8 %3 +} +define i16 @sdiv.i16(i16, i16) { + sdiv i16 %0, %1 + ret i16 %3 +} +define i32 @sdiv.i32(i32, i32) { + sdiv i32 %0, %1 + ret i32 %3 +} +define i64 @sdiv.i64(i64, i64) { + sdiv i64 %0, %1 + ret i64 %3 +} + +define i8 @urem.i8(i8, i8) { + urem i8 %0, %1 + ret i8 %3 +} +define i16 @urem.i16(i16, i16) { + urem i16 %0, %1 + ret i16 %3 +} +define i32 @urem.i32(i32, i32) { + urem i32 %0, %1 + ret i32 %3 +} +define i64 @urem.i64(i64, i64) { + urem i64 %0, %1 + ret i64 %3 +} + +define i8 @srem.i8(i8, i8) { + srem i8 %0, %1 + ret i8 %3 +} +define i16 @srem.i16(i16, i16) { + srem i16 %0, %1 + ret i16 %3 +} +define i32 @srem.i32(i32, i32) { + srem i32 %0, %1 + ret i32 %3 +} +define i64 @srem.i64(i64, i64) { + srem i64 %0, %1 + ret i64 %3 +} + +define float @fneg.float(float) { + fneg float %0 + ret float %2 +} +define double @fneg.double(double) { + fneg double %0 + ret double %2 +} + +define float @fadd.float(float, float) { + fadd float %0, %1 + ret float %3 +} +define double @fadd.double(double, double) { + fadd double %0, %1 + ret double %3 +} + +define float @fsub.float(float, float) { + fsub float %0, %1 + ret float %3 +} +define double @fsub.double(double, double) { + fsub double %0, %1 + ret double %3 +} + +define float @fmul.float(float, float) { + fmul float %0, %1 + ret float %3 +} +define double @fmul.double(double, double) { + fmul double %0, %1 + ret double %3 +} + +define float @fdiv.float(float, float) { + fdiv float %0, %1 + ret float %3 +} +define double @fdiv.double(double, double) { + fdiv double %0, %1 + ret double %3 +} + +define float @frem.float(float, float) { + frem float %0, %1 + ret float %3 +} +define double @frem.double(double, double) { + frem double %0, %1 + ret double %3 +} + +define i8 @trunc.i16.i8(i16) { + trunc i16 %0 to i8 + ret i8 %2 +} +define i8 @trunc.i24.i8(i24) { + trunc i24 %0 to i8 + ret i8 %2 +} +define i8 @trunc.i32.i8(i32) { + trunc i32 %0 to i8 + ret i8 %2 +} +define i8 @trunc.i48.i8(i48) { + trunc i48 %0 to i8 + ret i8 %2 +} +define i8 @trunc.i64.i8(i64) { + trunc i64 %0 to i8 + ret i8 %2 +} +define i16 @trunc.i24.i16(i24) { + trunc i24 %0 to i16 + ret i16 %2 +} +define i16 @trunc.i32.i16(i32) { + trunc i32 %0 to i16 + ret i16 %2 +} +define i16 @trunc.i48.i16(i48) { + trunc i48 %0 to i16 + ret i16 %2 +} +define i16 @trunc.i64.i16(i64) { + trunc i64 %0 to i16 + ret i16 %2 +} +define i24 @trunc.i32.i24(i32) { + trunc i32 %0 to i24 + ret i24 %2 +} +define i24 @trunc.i48.i24(i48) { + trunc i48 %0 to i24 + ret i24 %2 +} +define i24 @trunc.i64.i24(i64) { + trunc i64 %0 to i24 + ret i24 %2 +} +define i32 @trunc.i48.i32(i48) { + trunc i48 %0 to i32 + ret i32 %2 +} +define i32 @trunc.i64.i32(i64) { + trunc i64 %0 to i32 + ret i32 %2 +} +define i48 @trunc.i64.i48(i64) { + trunc i64 %0 to i48 + ret i48 %2 +} + +define i16 @zext.i8.i16(i8) { + zext i8 %0 to i16 + ret i16 %2 +} +define i24 @zext.i8.i24(i8) { + zext i8 %0 to i24 + ret i24 %2 +} +define i32 @zext.i8.i32(i8) { + zext i8 %0 to i32 + ret i32 %2 +} +define i48 @zext.i8.i48(i8) { + zext i8 %0 to i48 + ret i48 %2 +} +define i64 @zext.i8.i64(i8) { + zext i8 %0 to i64 + ret i64 %2 +} +define i24 @zext.i16.i24(i16) { + zext i16 %0 to i24 + ret i24 %2 +} +define i32 @zext.i16.i32(i16) { + zext i16 %0 to i32 + ret i32 %2 +} +define i48 @zext.i16.i48(i16) { + zext i16 %0 to i48 + ret i48 %2 +} +define i64 @zext.i16.i64(i16) { + zext i16 %0 to i64 + ret i64 %2 +} +define i32 @zext.i24.i32(i24) { + zext i24 %0 to i32 + ret i32 %2 +} +define i48 @zext.i24.i48(i24) { + zext i24 %0 to i48 + ret i48 %2 +} +define i64 @zext.i24.i64(i24) { + zext i24 %0 to i64 + ret i64 %2 +} +define i48 @zext.i32.i48(i32) { + zext i32 %0 to i48 + ret i48 %2 +} +define i64 @zext.i32.i64(i32) { + zext i32 %0 to i64 + ret i64 %2 +} +define i64 @zext.i48.i64(i48) { + zext i48 %0 to i64 + ret i64 %2 +} + +define i16 @sext.i8.i16(i8) { + sext i8 %0 to i16 + ret i16 %2 +} +define i24 @sext.i8.i24(i8) { + sext i8 %0 to i24 + ret i24 %2 +} +define i32 @sext.i8.i32(i8) { + sext i8 %0 to i32 + ret i32 %2 +} +define i48 @sext.i8.i48(i8) { + sext i8 %0 to i48 + ret i48 %2 +} +define i64 @sext.i8.i64(i8) { + sext i8 %0 to i64 + ret i64 %2 +} +define i24 @sext.i16.i24(i16) { + sext i16 %0 to i24 + ret i24 %2 +} +define i32 @sext.i16.i32(i16) { + sext i16 %0 to i32 + ret i32 %2 +} +define i48 @sext.i16.i48(i16) { + sext i16 %0 to i48 + ret i48 %2 +} +define i64 @sext.i16.i64(i16) { + sext i16 %0 to i64 + ret i64 %2 +} +define i32 @sext.i24.i32(i24) { + sext i24 %0 to i32 + ret i32 %2 +} +define i48 @sext.i24.i48(i24) { + sext i24 %0 to i48 + ret i48 %2 +} +define i64 @sext.i24.i64(i24) { + sext i24 %0 to i64 + ret i64 %2 +} +define i48 @sext.i32.i48(i32) { + sext i32 %0 to i48 + ret i48 %2 +} +define i64 @sext.i32.i64(i32) { + sext i32 %0 to i64 + ret i64 %2 +} +define i64 @sext.i48.i64(i48) { + sext i48 %0 to i64 + ret i64 %2 +} + +define float @fptrunc.double.float(double) { + fptrunc double %0 to float + ret float %2 +} + +define double @fpext.float.double(float) { + fpext float %0 to double + ret double %2 +} + +define i8 @fptoui.float.i8(float) { + fptoui float %0 to i8 + ret i8 %2 +} +define i16 @fptoui.float.i16(float) { + fptoui float %0 to i16 + ret i16 %2 +} +define i24 @fptoui.float.i24(float) { + fptoui float %0 to i24 + ret i24 %2 +} +define i32 @fptoui.float.i32(float) { + fptoui float %0 to i32 + ret i32 %2 +} +define i64 @fptoui.float.i64(float) { + fptoui float %0 to i64 + ret i64 %2 +} +define i8 @fptoui.double.i8(double) { + fptoui double %0 to i8 + ret i8 %2 +} +define i16 @fptoui.double.i16(double) { + fptoui double %0 to i16 + ret i16 %2 +} +define i24 @fptoui.double.i24(double) { + fptoui double %0 to i24 + ret i24 %2 +} +define i32 @fptoui.double.i32(double) { + fptoui double %0 to i32 + ret i32 %2 +} +define i64 @fptoui.double.i64(double) { + fptoui double %0 to i64 + ret i64 %2 +} + +define i8 @fptosi.float.i8(float) { + fptosi float %0 to i8 + ret i8 %2 +} +define i16 @fptosi.float.i16(float) { + fptosi float %0 to i16 + ret i16 %2 +} +define i24 @fptosi.float.i24(float) { + fptosi float %0 to i24 + ret i24 %2 +} +define i32 @fptosi.float.i32(float) { + fptosi float %0 to i32 + ret i32 %2 +} +define i64 @fptosi.float.i64(float) { + fptosi float %0 to i64 + ret i64 %2 +} +define i8 @fptosi.double.i8(double) { + fptosi double %0 to i8 + ret i8 %2 +} +define i16 @fptosi.double.i16(double) { + fptosi double %0 to i16 + ret i16 %2 +} +define i24 @fptosi.double.i24(double) { + fptosi double %0 to i24 + ret i24 %2 +} +define i32 @fptosi.double.i32(double) { + fptosi double %0 to i32 + ret i32 %2 +} +define i64 @fptosi.double.i64(double) { + fptosi double %0 to i64 + ret i64 %2 +} + +define float @uitofp.i8.float(i8) { + uitofp i8 %0 to float + ret float %2 +} +define float @uitofp.i16.float(i16) { + uitofp i16 %0 to float + ret float %2 +} +define float @uitofp.i24.float(i24) { + uitofp i24 %0 to float + ret float %2 +} +define float @uitofp.i32.float(i32) { + uitofp i32 %0 to float + ret float %2 +} +define float @uitofp.i64.float(i64) { + uitofp i64 %0 to float + ret float %2 +} +define double @uitofp.i8.double(i8) { + uitofp i8 %0 to double + ret double %2 +} +define double @uitofp.i16.double(i16) { + uitofp i16 %0 to double + ret double %2 +} +define double @uitofp.i24.double(i24) { + uitofp i24 %0 to double + ret double %2 +} +define double @uitofp.i32.double(i32) { + uitofp i32 %0 to double + ret double %2 +} +define double @uitofp.i64.double(i64) { + uitofp i64 %0 to double + ret double %2 +} + +define float @sitofp.i8.float(i8) { + sitofp i8 %0 to float + ret float %2 +} +define float @sitofp.i16.float(i16) { + sitofp i16 %0 to float + ret float %2 +} +define float @sitofp.i24.float(i24) { + sitofp i24 %0 to float + ret float %2 +} +define float @sitofp.i32.float(i32) { + sitofp i32 %0 to float + ret float %2 +} +define float @sitofp.i64.float(i64) { + sitofp i64 %0 to float + ret float %2 +} +define double @sitofp.i8.double(i8) { + sitofp i8 %0 to double + ret double %2 +} +define double @sitofp.i16.double(i16) { + sitofp i16 %0 to double + ret double %2 +} +define double @sitofp.i24.double(i24) { + sitofp i24 %0 to double + ret double %2 +} +define double @sitofp.i32.double(i32) { + sitofp i32 %0 to double + ret double %2 +} +define double @sitofp.i64.double(i64) { + sitofp i64 %0 to double + ret double %2 +} + +define i8 @ptrtoint.i8(i8*) { + ptrtoint i8* %0 to i8 + ret i8 %2 +} +define i16 @ptrtoint.i16(i16*) { + ptrtoint i16* %0 to i16 + ret i16 %2 +} +define i24 @ptrtoint.i24(i24*) { + ptrtoint i24* %0 to i24 + ret i24 %2 +} +define i32 @ptrtoint.i32(i32*) { + ptrtoint i32* %0 to i32 + ret i32 %2 +} +define i48 @ptrtoint.i48(i48*) { + ptrtoint i48* %0 to i48 + ret i48 %2 +} +define i64 @ptrtoint.i64(i64*) { + ptrtoint i64* %0 to i64 + ret i64 %2 +} + +define i8* @inttoptr.i8(i8) { + inttoptr i8 %0 to i8* + ret i8* %2 +} +define i16* @inttoptr.i16(i16) { + inttoptr i16 %0 to i16* + ret i16* %2 +} +define i24* @inttoptr.i24(i24) { + inttoptr i24 %0 to i24* + ret i24* %2 +} +define i32* @inttoptr.i32(i32) { + inttoptr i32 %0 to i32* + ret i32* %2 +} +define i48* @inttoptr.i48(i48) { + inttoptr i48 %0 to i48* + ret i48* %2 +} +define i64* @inttoptr.i64(i64) { + inttoptr i64 %0 to i64* + ret i64* %2 +} + +define i32 @bitcast.float.i32(float) { + bitcast float %0 to i32 + ret i32 %2 +} +define i64 @bitcast.double.i64(double) { + bitcast double %0 to i64 + ret i64 %2 +} +define float @bitcast.i32.float(i32) { + bitcast i32 %0 to float + ret float %2 +} +define double @bitcast.i64.double(i64) { + bitcast i64 %0 to double + ret double %2 +} +define i16* @bitcast.p0i8.p0i16(i8*) { + bitcast i8* %0 to i16* + ret i16* %2 +} diff --git a/llvm/test/CodeGen/Z80/ops24.ll b/llvm/test/CodeGen/Z80/ops24.ll new file mode 100644 index 00000000000000..02238f954c1961 --- /dev/null +++ b/llvm/test/CodeGen/Z80/ops24.ll @@ -0,0 +1,46 @@ +; RUN: llc -mtriple=ez80 < %s + +define i24 @shl.i24(i24, i24) { + shl i24 %0, %1 + ret i24 %3 +} +define i24 @lshr.i24(i24, i24) { + lshr i24 %0, %1 + ret i24 %3 +} +define i24 @ashr.i24(i24, i24) { + ashr i24 %0, %1 + ret i24 %3 +} +define i24 @and.i24(i24, i24) { + and i24 %0, %1 + ret i24 %3 +} +define i24 @or.i24(i24, i24) { + or i24 %0, %1 + ret i24 %3 +} +define i24 @xor.i24(i24, i24) { + xor i24 %0, %1 + ret i24 %3 +} +define i24 @mul.i24(i24, i24) { + mul i24 %0, %1 + ret i24 %3 +} +define i24 @udiv.i24(i24, i24) { + udiv i24 %0, %1 + ret i24 %3 +} +define i24 @sdiv.i24(i24, i24) { + sdiv i24 %0, %1 + ret i24 %3 +} +define i24 @urem.i24(i24, i24) { + urem i24 %0, %1 + ret i24 %3 +} +define i24 @srem.i24(i24, i24) { + srem i24 %0, %1 + ret i24 %3 +} diff --git a/llvm/tools/bugpoint/CrashDebugger.cpp b/llvm/tools/bugpoint/CrashDebugger.cpp index 1a39ff654f0563..d06c0b83e1d2d2 100644 --- a/llvm/tools/bugpoint/CrashDebugger.cpp +++ b/llvm/tools/bugpoint/CrashDebugger.cpp @@ -68,6 +68,10 @@ cl::opt NoStripDebugTypeInfo("disable-strip-debug-types", cl::opt VerboseErrors("verbose-errors", cl::desc("Print the output of crashing program"), cl::init(false)); +cl::opt VerifyModule("verify-module", + cl::desc("Verify modules before running tests and " + "ignore those that fail verification"), + cl::init(true)); } namespace llvm { @@ -1409,7 +1413,9 @@ Error BugDriver::debugOptimizerCrash(const std::string &ID) { } static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) { - if (Error E = BD.compileProgram(*M)) { + if (VerifyModule && verifyModule(*M)) + errs() << ""; + else if (Error E = BD.compileProgram(*M)) { if (VerboseErrors) errs() << toString(std::move(E)) << "\n"; else { diff --git a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp index 2cfab39d456228..caded0b95b3033 100644 --- a/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp +++ b/llvm/unittests/CodeGen/GlobalISel/LegalizerHelperTest.cpp @@ -42,7 +42,7 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ0) { LegalizerHelper Helper(*MF, Info, Observer, B); // Perform Legalization EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*MIBCTTZ, 0, LLT::scalar(64))); + Helper.lower(*MIBCTTZ, 0)); auto CheckStr = R"( CHECK: [[CZU:%[0-9]+]]:_(s32) = G_CTTZ_ZERO_UNDEF %0 @@ -73,8 +73,9 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ1) { DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); // Perform Legalization - EXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) == - LegalizerHelper::LegalizeResult::Legalized); + + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTTZ, 0)); auto CheckStr = R"( CHECK: [[NEG1:%[0-9]+]]:_(s64) = G_CONSTANT i64 -1 @@ -178,8 +179,8 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ2) { LegalizerHelper Helper(*MF, Info, Observer, B); B.setInsertPt(*EntryMBB, MIBCTTZ->getIterator()); - EXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) == - LegalizerHelper::LegalizeResult::Legalized); + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTTZ, 0)); auto CheckStr = R"( CHECK: [[NEG1:%[0-9]+]]:_(s64) = G_CONSTANT i64 -1 @@ -274,8 +275,8 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTTZ3) { AInfo Info(MF->getSubtarget()); DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); - EXPECT_TRUE(Helper.lower(*MIBCTTZ, 0, LLT::scalar(64)) == - LegalizerHelper::LegalizeResult::Legalized); + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTTZ, 0)); auto CheckStr = R"( CHECK: CTTZ @@ -301,8 +302,8 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTLZ0) { AInfo Info(MF->getSubtarget()); DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); - EXPECT_TRUE(Helper.lower(*MIBCTLZ, 0, LLT::scalar(64)) == - LegalizerHelper::LegalizeResult::Legalized); + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTLZ, 0)); auto CheckStr = R"( CHECK: [[CZU:%[0-9]+]]:_(s64) = G_CTLZ_ZERO_UNDEF %0 @@ -333,7 +334,7 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTLZLibcall) { DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*MIBCTLZ, 0, LLT::scalar(32))); + Helper.lower(*MIBCTLZ, 0)); auto CheckStr = R"( CHECK: [[CZU:%[0-9]+]]:_(s32) = G_CTLZ_ZERO_UNDEF %0 @@ -365,8 +366,8 @@ TEST_F(AArch64GISelMITest, LowerBitCountingCTLZ1) { AInfo Info(MF->getSubtarget()); DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); - EXPECT_TRUE(Helper.lower(*MIBCTLZ, 0, s8) == - LegalizerHelper::LegalizeResult::Legalized); + EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, + Helper.lower(*MIBCTLZ, 0)); auto CheckStr = R"( CHECK: [[Trunc:%[0-9]+]]:_(s8) = G_TRUNC @@ -850,9 +851,9 @@ TEST_F(AArch64GISelMITest, LowerFNEG) { LegalizerHelper Helper(*MF, Info, Observer, B); // Perform Legalization EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*FNeg0, 0, LLT::scalar(64))); + Helper.lower(*FNeg0, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*FNeg1, 0, LLT::scalar(64))); + Helper.lower(*FNeg1, 0)); auto CheckStr = R"( CHECK: [[FADD:%[0-9]+]]:_(s64) = nsz G_FADD %0:_, %1:_ @@ -895,22 +896,22 @@ TEST_F(AArch64GISelMITest, LowerMinMax) { DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*SMin, 0, s64)); + Helper.lower(*SMin, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*SMax, 0, s64)); + Helper.lower(*SMax, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*UMin, 0, s64)); + Helper.lower(*UMin, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*UMax, 0, s64)); + Helper.lower(*UMax, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*SMinV, 0, v2s32)); + Helper.lower(*SMinV, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*SMaxV, 0, v2s32)); + Helper.lower(*SMaxV, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*UMinV, 0, v2s32)); + Helper.lower(*UMinV, 0)); EXPECT_EQ(LegalizerHelper::LegalizeResult::Legalized, - Helper.lower(*UMaxV, 0, v2s32)); + Helper.lower(*UMaxV, 0)); auto CheckStr = R"( CHECK: [[CMP0:%[0-9]+]]:_(s1) = G_ICMP intpred(slt), %0:_(s64), %1:_ @@ -1238,8 +1239,7 @@ TEST_F(AArch64GISelMITest, LowerSEXTINREG) { DummyGISelObserver Observer; LegalizerHelper Helper(*MF, Info, Observer, B); // Perform Legalization - ASSERT_TRUE(Helper.lower(*MIB, 0, LLT()) == - LegalizerHelper::LegalizeResult::Legalized); + ASSERT_EQ(LegalizerHelper::LegalizeResult::Legalized, Helper.lower(*MIB, 0)); auto CheckStr = R"( CHECK: [[T1:%[0-9]+]]:_(s32) = G_TRUNC diff --git a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp index 6fdc116721f333..0f6d55115b426d 100644 --- a/llvm/utils/TableGen/CodeGenDAGPatterns.cpp +++ b/llvm/utils/TableGen/CodeGenDAGPatterns.cpp @@ -2487,7 +2487,7 @@ bool TreePatternNode::ApplyTypeConstraints(TreePattern &TP, bool NotRegisters) { // If this is an INSERT_SUBREG, constrain the source and destination VTs to // be the same. if (getOperator()->getName() == "INSERT_SUBREG") { - assert(getChild(0)->getNumTypes() == 1 && "FIXME: Unhandled"); + assert(getChild(0)->getNumTypes() >= 1 && "FIXME: Unhandled"); MadeChange |= UpdateNodeType(0, getChild(0)->getExtType(0), TP); MadeChange |= getChild(0)->UpdateNodeType(0, getExtType(0), TP); } else if (getOperator()->getName() == "REG_SEQUENCE") { @@ -2800,7 +2800,7 @@ TreePatternNodePtr TreePattern::ParseTreePattern(Init *TheInit, ParseTreePattern(Dag->getArg(0), Dag->getArgNameStr(0)); // Apply the type cast. - assert(New->getNumTypes() == 1 && "FIXME: Unhandled"); + assert(New->getNumTypes() >= 1 && "FIXME: Unhandled"); const CodeGenHwModes &CGH = getDAGPatterns().getTargetInfo().getHwModes(); New->UpdateNodeType(0, getValueTypeByHwMode(Operator, CGH), *this); diff --git a/llvm/utils/TableGen/CodeGenInstruction.cpp b/llvm/utils/TableGen/CodeGenInstruction.cpp index 1df5902b081e15..4189c944154e1b 100644 --- a/llvm/utils/TableGen/CodeGenInstruction.cpp +++ b/llvm/utils/TableGen/CodeGenInstruction.cpp @@ -467,6 +467,15 @@ HasOneImplicitDefWithKnownVT(const CodeGenTarget &TargetInfo) const { return MVT::Other; } +/// HasImplicitDef - If the instruction has the passed register as an +/// implicit def. +bool CodeGenInstruction::HasImplicitDef(Record *Reg) const { + assert(Reg->isSubClassOf("Register")); + for (Record *ImplicitDef : ImplicitDefs) + if (ImplicitDef == Reg) + return true; + return false; +} /// FlattenAsmStringVariants - Flatten the specified AsmString to only /// include text from the specified variant, returning the new string. diff --git a/llvm/utils/TableGen/CodeGenInstruction.h b/llvm/utils/TableGen/CodeGenInstruction.h index af851a11676b12..c80f9e0f938a3d 100644 --- a/llvm/utils/TableGen/CodeGenInstruction.h +++ b/llvm/utils/TableGen/CodeGenInstruction.h @@ -301,6 +301,9 @@ template class ArrayRef; MVT::SimpleValueType HasOneImplicitDefWithKnownVT(const CodeGenTarget &TargetInfo) const; + /// HasImplicitDef - If the instruction has the passed register as an + /// implicit def. + bool HasImplicitDef(Record *Reg) const; /// FlattenAsmStringVariants - Flatten the specified AsmString to only /// include text from the specified variant, returning the new string. diff --git a/llvm/utils/TableGen/CodeGenRegisters.cpp b/llvm/utils/TableGen/CodeGenRegisters.cpp index 4584bc7cfae324..790a50c8dbd6ae 100644 --- a/llvm/utils/TableGen/CodeGenRegisters.cpp +++ b/llvm/utils/TableGen/CodeGenRegisters.cpp @@ -1017,7 +1017,8 @@ CodeGenRegisterClass::getMatchingSubClassWithSubRegs( SuperRegRCs.emplace_back(&RC); llvm::stable_sort(SuperRegRCs, SizeOrder); - assert(SuperRegRCs.front() == BiggestSuperRegRC && + assert(SuperRegRCs.front()->getMembers().size() == + BiggestSuperRegRC->getMembers().size() && "Biggest class wasn't first"); // Find all the subreg classes and order them by size too. diff --git a/llvm/utils/TableGen/CodeGenTarget.cpp b/llvm/utils/TableGen/CodeGenTarget.cpp index 31f9f63d168449..e9d7650c7c7910 100644 --- a/llvm/utils/TableGen/CodeGenTarget.cpp +++ b/llvm/utils/TableGen/CodeGenTarget.cpp @@ -61,6 +61,7 @@ StringRef llvm::getEnumName(MVT::SimpleValueType T) { case MVT::i1: return "MVT::i1"; case MVT::i8: return "MVT::i8"; case MVT::i16: return "MVT::i16"; + case MVT::i24: return "MVT::i24"; case MVT::i32: return "MVT::i32"; case MVT::i64: return "MVT::i64"; case MVT::i128: return "MVT::i128"; @@ -393,6 +394,24 @@ std::vector CodeGenTarget::getRegisterVTs(Record *R) return Result; } +MVT::SimpleValueType CodeGenTarget::getRegisterKnownVT(Record *R) const { + const CodeGenRegister *Reg = getRegBank().getReg(R); + MVT::SimpleValueType KnownVT = MVT::Other; + for (const auto &RC : getRegBank().getRegClasses()) { + if (RC.contains(Reg)) { + for (auto VT : RC.getValueTypes()) { + if (!VT.isSimple()) + return MVT::Other; + auto SimpleVT = VT.getSimple().SimpleTy; + if (KnownVT == MVT::Other) + KnownVT = SimpleVT; + else if (KnownVT != SimpleVT) + return MVT::Other; + } + } + } + return KnownVT; +} void CodeGenTarget::ReadLegalValueTypes() const { for (const auto &RC : getRegBank().getRegClasses()) diff --git a/llvm/utils/TableGen/CodeGenTarget.h b/llvm/utils/TableGen/CodeGenTarget.h index 6c89f34c50eccf..8eececb38500d2 100644 --- a/llvm/utils/TableGen/CodeGenTarget.h +++ b/llvm/utils/TableGen/CodeGenTarget.h @@ -126,6 +126,10 @@ class CodeGenTarget { /// specified physical register. std::vector getRegisterVTs(Record *R) const; + /// getRegisterKnownVT - If the specified physical register has exactly one + /// SimpleValueType it is returned, otherwise MVT::Other is returned. + MVT::SimpleValueType getRegisterKnownVT(Record *R) const; + ArrayRef getLegalValueTypes() const { if (LegalValueTypes.empty()) ReadLegalValueTypes(); diff --git a/llvm/utils/TableGen/DAGISelMatcherGen.cpp b/llvm/utils/TableGen/DAGISelMatcherGen.cpp index 123ea3374c7482..198cce35c98628 100644 --- a/llvm/utils/TableGen/DAGISelMatcherGen.cpp +++ b/llvm/utils/TableGen/DAGISelMatcherGen.cpp @@ -1051,20 +1051,19 @@ void MatcherGen::EmitResultCode() { // If the pattern also has (implicit) results, count them as well. if (!Pattern.getDstRegs().empty()) { - // If the root came from an implicit def in the instruction handling stuff, - // don't re-add it. - Record *HandledReg = nullptr; const TreePatternNode *DstPat = Pattern.getDstPattern(); - if (!DstPat->isLeaf() &&DstPat->getOperator()->isSubClassOf("Instruction")){ - const CodeGenTarget &CGT = CGP.getTargetInfo(); - CodeGenInstruction &II = CGT.getInstruction(DstPat->getOperator()); - - if (II.HasOneImplicitDefWithKnownVT(CGT) != MVT::Other) - HandledReg = II.ImplicitDefs[0]; - } + const CodeGenTarget &CGT = CGP.getTargetInfo(); + CodeGenInstruction *II = nullptr; + if (!DstPat->isLeaf() && DstPat->getOperator()->isSubClassOf("Instruction")) + II = &CGT.getInstruction(DstPat->getOperator()); for (Record *Reg : Pattern.getDstRegs()) { - if (!Reg->isSubClassOf("Register") || Reg == HandledReg) continue; + if (!Reg->isSubClassOf("Register")) continue; + // If the root came from an implicit def in the instruction handling stuff, + // don't re-add it. + if (II && II->HasImplicitDef(Reg) && + CGT.getRegisterKnownVT(Reg) != MVT::Other) + continue; ++NumSrcResults; } } diff --git a/llvm/utils/TableGen/GlobalISelEmitter.cpp b/llvm/utils/TableGen/GlobalISelEmitter.cpp index 026f9ad349444e..4e6e7e0abc7d40 100644 --- a/llvm/utils/TableGen/GlobalISelEmitter.cpp +++ b/llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -652,7 +652,19 @@ class RuleMatcher; class Matcher { public: + enum MatcherKind { + MK_Group, + MK_Switch, + MK_Rule, + }; +private: + MatcherKind TheKind; +public: + Matcher(MatcherKind Kind) : TheKind(Kind) {} virtual ~Matcher() = default; + + MatcherKind getKind() const { return TheKind; } + virtual void optimize() {} virtual void emit(MatchTable &Table) = 0; @@ -682,6 +694,9 @@ class GroupMatcher final : public Matcher { std::vector> MatcherStorage; public: + GroupMatcher() : Matcher(MK_Group) {} + static bool classof(const Matcher *M) { return M->getKind() == MK_Group; } + /// Add a matcher to the collection of nested matchers if it meets the /// requirements, and return true. If it doesn't, do nothing and return false. /// @@ -741,7 +756,7 @@ class GroupMatcher final : public Matcher { bool candidateConditionMatches(const PredicateMatcher &Predicate) const; }; -class SwitchMatcher : public Matcher { +class SwitchMatcher final : public Matcher { /// All the nested matchers, representing distinct switch-cases. The first /// conditions (as Matcher::getFirstCondition() reports) of all the nested /// matchers must share the same type and path to a value they check, in other @@ -762,6 +777,9 @@ class SwitchMatcher : public Matcher { std::vector> MatcherStorage; public: + SwitchMatcher() : Matcher(MK_Switch) {} + static bool classof(const Matcher *M) { return M->getKind() == MK_Switch; } + bool addMatcher(Matcher &Candidate); void finalize(); @@ -796,7 +814,7 @@ class SwitchMatcher : public Matcher { }; /// Generates code to check that a match rule matches. -class RuleMatcher : public Matcher { +class RuleMatcher final : public Matcher { public: using ActionList = std::list>; using action_iterator = ActionList::iterator; @@ -830,7 +848,7 @@ class RuleMatcher : public Matcher { /// A map of anonymous physical register operands defined by the matchers that /// may be referenced by the renderers. - DenseMap PhysRegOperands; + DenseMap PhysRegDefs, PhysRegUses; /// ID for the next instruction variable defined with implicitlyDefineInsnVar() unsigned NextInsnVarID; @@ -858,10 +876,12 @@ class RuleMatcher : public Matcher { public: RuleMatcher(ArrayRef SrcLoc) - : Matchers(), Actions(), InsnVariableIDs(), MutatableInsns(), - DefinedOperands(), NextInsnVarID(0), NextOutputInsnID(0), - NextTempRegID(0), SrcLoc(SrcLoc), ComplexSubOperands(), - RuleID(NextRuleID++) {} + : Matcher(MK_Rule), Matchers(), Actions(), InsnVariableIDs(), + MutatableInsns(), DefinedOperands(), NextInsnVarID(0), + NextOutputInsnID(0), NextTempRegID(0), SrcLoc(SrcLoc), + ComplexSubOperands(), RuleID(NextRuleID++) {} + static bool classof(const Matcher *M) { return M->getKind() == MK_Rule; } + RuleMatcher(RuleMatcher &&Other) = default; RuleMatcher &operator=(RuleMatcher &&Other) = default; @@ -912,9 +932,11 @@ class RuleMatcher : public Matcher { return make_range(actions_begin(), actions_end()); } + bool hasOperand(StringRef SymbolicName); + void defineOperand(StringRef SymbolicName, OperandMatcher &OM); - void definePhysRegOperand(Record *Reg, OperandMatcher &OM); + void definePhysRegOperand(Record *Reg, OperandMatcher &OM, bool IsDef = false); Error defineComplexSubOperand(StringRef SymbolicName, Record *ComplexPattern, unsigned RendererID, unsigned SubOperandID) { @@ -939,7 +961,8 @@ class RuleMatcher : public Matcher { InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const; const OperandMatcher &getOperandMatcher(StringRef Name) const; - const OperandMatcher &getPhysRegOperandMatcher(Record *) const; + const OperandMatcher &getPhysRegOperandMatcher(Record *Reg, + bool IsDef = false) const; void optimize() override; void emit(MatchTable &Table) override; @@ -1096,7 +1119,7 @@ class PredicateMatcher { virtual ~PredicateMatcher() = default; /// Emit MatchTable opcodes that check the predicate for the given operand. virtual void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const = 0; + Matcher& Rule) const = 0; PredicateKind getKind() const { return Kind; } @@ -1160,7 +1183,7 @@ class SameOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override; + Matcher& Rule) const override; bool isIdentical(const PredicateMatcher &B) const override { return OperandPredicateMatcher::isIdentical(B) && @@ -1211,7 +1234,7 @@ class LLTOperandMatcher : public OperandPredicateMatcher { LLTCodeGen getTy() const { return Ty; } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckType") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) << MatchTable::Comment("Type") @@ -1246,7 +1269,7 @@ class PointerToAnyOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckPointerToAny") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1277,7 +1300,7 @@ class ComplexPatternOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { unsigned ID = getAllocatedTemporariesBaseID(); Table << MatchTable::Opcode("GIM_CheckComplexPattern") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) @@ -1312,7 +1335,7 @@ class RegisterBankOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckRegBankForClass") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1333,7 +1356,7 @@ class MBBOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckIsMBB") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) << MatchTable::LineBreak; @@ -1350,7 +1373,7 @@ class ImmOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckIsImm") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) << MatchTable::LineBreak; @@ -1377,7 +1400,7 @@ class ConstantIntOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckConstantInt") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1406,7 +1429,7 @@ class LiteralIntOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckLiteralInt") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1434,7 +1457,7 @@ class CmpPredicateOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckCmpPredicate") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1464,7 +1487,7 @@ class IntrinsicIDOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckIntrinsicID") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Op") << MatchTable::IntValue(OpIdx) @@ -1525,7 +1548,7 @@ class OperandMatcher : public PredicateListMatcher { /// Emit MatchTable opcodes that test whether the instruction named in /// InsnVarID matches all the predicates and all the operands. - void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) { + void emitPredicateOpcodes(MatchTable &Table, Matcher& Rule) { if (!Optimized) { std::string Comment; raw_string_ostream CommentOS(Comment); @@ -1675,7 +1698,7 @@ class InstructionOpcodeMatcher : public InstructionPredicateMatcher { bool hasValue() const override { return OpcodeValues.count(I); } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckOpcode") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << getValue() << MatchTable::LineBreak; @@ -1734,7 +1757,7 @@ class InstructionNumOperandsMatcher final : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckNumOperands") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Expected") @@ -1790,7 +1813,7 @@ class InstructionImmPredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode(getMatchOpcodeForPredicate(Predicate)) << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("Predicate") @@ -1831,7 +1854,7 @@ class AtomicOrderingMMOPredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { StringRef Opcode = "GIM_CheckAtomicOrdering"; if (Comparator == AO_OrStronger) @@ -1867,7 +1890,7 @@ class MemorySizePredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckMemorySizeEqualTo") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("MMO") << MatchTable::IntValue(MMOIdx) @@ -1898,7 +1921,7 @@ class MemoryAddressSpacePredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckMemoryAddressSpace") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("MMO") << MatchTable::IntValue(MMOIdx) @@ -1937,7 +1960,7 @@ class MemoryAlignmentPredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckMemoryAlignment") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("MMO") << MatchTable::IntValue(MMOIdx) @@ -1979,7 +2002,7 @@ class MemoryVsLLTSizePredicateMatcher : public InstructionPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode(Relation == EqualTo ? "GIM_CheckMemorySizeEqualToLLT" : Relation == GreaterThan @@ -2013,7 +2036,7 @@ class GenericInstructionPredicateMatcher : public InstructionPredicateMatcher { .Predicate; } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { Table << MatchTable::Opcode("GIM_CheckCxxInsnPredicate") << MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID) << MatchTable::Comment("FnId") @@ -2122,7 +2145,7 @@ class InstructionMatcher final : public PredicateListMatcher { /// Emit MatchTable opcodes that test whether the instruction named in /// InsnVarName matches all the predicates and all the operands. - void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule) { + void emitPredicateOpcodes(MatchTable &Table, Matcher& Rule) { if (NumOperandsCheck) InstructionNumOperandsMatcher(InsnVarID, getNumOperands()) .emitPredicateOpcodes(Table, Rule); @@ -2235,7 +2258,7 @@ class InstructionOperandMatcher : public OperandPredicateMatcher { InstructionMatcher &getInsnMatcher() const { return *InsnMatcher; } - void emitCaptureOpcodes(MatchTable &Table, RuleMatcher &Rule) const { + void emitCaptureOpcodes(MatchTable &Table, Matcher& Rule) const { const unsigned NewInsnVarID = InsnMatcher->getInsnVarID(); Table << MatchTable::Opcode("GIM_RecordInsn") << MatchTable::Comment("DefineMI") @@ -2247,7 +2270,7 @@ class InstructionOperandMatcher : public OperandPredicateMatcher { } void emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const override { + Matcher& Rule) const override { emitCaptureOpcodes(Table, Rule); InsnMatcher->emitPredicateOpcodes(Table, Rule); } @@ -2372,11 +2395,12 @@ class CopyPhysRegRenderer : public OperandRenderer { protected: unsigned NewInsnID; Record *PhysReg; + bool IsDef; public: - CopyPhysRegRenderer(unsigned NewInsnID, Record *Reg) + CopyPhysRegRenderer(unsigned NewInsnID, Record *Reg, bool IsDef = false) : OperandRenderer(OR_CopyPhysReg), NewInsnID(NewInsnID), - PhysReg(Reg) { + PhysReg(Reg), IsDef(IsDef) { assert(PhysReg); } @@ -2387,14 +2411,14 @@ class CopyPhysRegRenderer : public OperandRenderer { Record *getPhysReg() const { return PhysReg; } void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { - const OperandMatcher &Operand = Rule.getPhysRegOperandMatcher(PhysReg); + const OperandMatcher &Operand = + Rule.getPhysRegOperandMatcher(PhysReg, IsDef); unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher()); Table << MatchTable::Opcode("GIR_Copy") << MatchTable::Comment("NewInsnID") << MatchTable::IntValue(NewInsnID) << MatchTable::Comment("OldInsnID") << MatchTable::IntValue(OldInsnVarID) << MatchTable::Comment("OpIdx") << MatchTable::IntValue(Operand.getOpIdx()) - << MatchTable::Comment(PhysReg->getName()) - << MatchTable::LineBreak; + << MatchTable::Comment(PhysReg->getName()) << MatchTable::LineBreak; } }; @@ -2796,6 +2820,20 @@ class DebugCommentAction : public MatchAction { } }; +/// Generates code to erase an instructiono. +class EraseFromParentAction : public MatchAction { + unsigned InsnID; + +public: + EraseFromParentAction(unsigned InsnID) : InsnID(InsnID) {} + + void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule) const override { + Table << MatchTable::Opcode("GIR_EraseFromParent") + << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID) + << MatchTable::LineBreak; + } +}; + /// Generates code to build an instruction or mutate an existing instruction /// into the desired instruction when this is possible. class BuildMIAction : public MatchAction { @@ -2842,6 +2880,10 @@ class BuildMIAction : public MatchAction { return; } } + // FIXME: This is a hack but it's sufficient for ISel. We'll need to do + // better for combines. Particularly when there are multiple match + // roots. + Rule.addAction(InsnID); } template @@ -2918,14 +2960,6 @@ class BuildMIAction : public MatchAction { Table << MatchTable::NamedValue("GIU_MergeMemOperands_EndOfList") << MatchTable::LineBreak; } - - // FIXME: This is a hack but it's sufficient for ISel. We'll need to do - // better for combines. Particularly when there are multiple match - // roots. - if (InsnID == 0) - Table << MatchTable::Opcode("GIR_EraseFromParent") - << MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID) - << MatchTable::LineBreak; } }; @@ -3041,8 +3075,12 @@ unsigned RuleMatcher::getInsnVarID(InstructionMatcher &InsnMatcher) const { llvm_unreachable("Matched Insn was not captured in a local variable"); } +bool RuleMatcher::hasOperand(StringRef SymbolicName) { + return DefinedOperands.find(SymbolicName) != DefinedOperands.end(); +} + void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) { - if (DefinedOperands.find(SymbolicName) == DefinedOperands.end()) { + if (!hasOperand(SymbolicName)) { DefinedOperands[SymbolicName] = &OM; return; } @@ -3052,7 +3090,9 @@ void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) { OM.addPredicate(OM.getSymbolicName()); } -void RuleMatcher::definePhysRegOperand(Record *Reg, OperandMatcher &OM) { +void RuleMatcher::definePhysRegOperand(Record *Reg, OperandMatcher &OM, + bool IsDef) { + auto &PhysRegOperands = IsDef ? PhysRegDefs : PhysRegUses; if (PhysRegOperands.find(Reg) == PhysRegOperands.end()) { PhysRegOperands[Reg] = &OM; return; @@ -3069,7 +3109,8 @@ RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const { } const OperandMatcher & -RuleMatcher::getPhysRegOperandMatcher(Record *Reg) const { +RuleMatcher::getPhysRegOperandMatcher(Record *Reg, bool IsDef) const { + auto &PhysRegOperands = IsDef ? PhysRegDefs : PhysRegUses; const auto &I = PhysRegOperands.find(Reg); if (I == PhysRegOperands.end()) { @@ -3253,9 +3294,10 @@ bool OperandPredicateMatcher::isHigherPriorityThan( } void SameOperandMatcher::emitPredicateOpcodes(MatchTable &Table, - RuleMatcher &Rule) const { - const OperandMatcher &OtherOM = Rule.getOperandMatcher(MatchingName); - unsigned OtherInsnVarID = Rule.getInsnVarID(OtherOM.getInstructionMatcher()); + Matcher& Rule) const { + RuleMatcher& RM = cast(Rule); + const OperandMatcher &OtherOM = RM.getOperandMatcher(MatchingName); + unsigned OtherInsnVarID = RM.getInsnVarID(OtherOM.getInstructionMatcher()); assert(OtherInsnVarID == OtherOM.getInstructionMatcher().getInsnVarID()); Table << MatchTable::Opcode("GIM_CheckIsSameOperand") @@ -3270,22 +3312,6 @@ void SameOperandMatcher::emitPredicateOpcodes(MatchTable &Table, //===- GlobalISelEmitter class --------------------------------------------===// -static Expected getInstResultType(const TreePatternNode *Dst) { - ArrayRef ChildTypes = Dst->getExtTypes(); - if (ChildTypes.size() != 1) - return failedImport("Dst pattern child has multiple results"); - - Optional MaybeOpTy; - if (ChildTypes.front().isMachineValueType()) { - MaybeOpTy = - MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); - } - - if (!MaybeOpTy) - return failedImport("Dst operand has an unsupported type"); - return *MaybeOpTy; -} - class GlobalISelEmitter { public: explicit GlobalISelEmitter(RecordKeeper &RK); @@ -3323,6 +3349,8 @@ class GlobalISelEmitter { // Rule coverage information. Optional RuleCoverage; + Expected getInstResultType(const TreePatternNode *Dst) const; + void gatherOpcodeValues(); void gatherTypeIDValues(); void gatherNodeEquivs(); @@ -3352,7 +3380,8 @@ class GlobalISelEmitter { Expected createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst); - void importExplicitDefRenderers(BuildMIAction &DstMIBuilder); + void importExplicitDefRenderers(BuildMIAction &DstMIBuilder, + const TreePatternNode *Src); Expected importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M, @@ -3365,9 +3394,8 @@ class GlobalISelEmitter { Error importDefaultOperandRenderers(action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, DagInit *DefaultOps) const; - Error - importImplicitDefRenderers(BuildMIAction &DstMIBuilder, - const std::vector &ImplicitDefs) const; + Error importImplicitDefRenderers(RuleMatcher &M, BuildMIAction &DstMIBuilder, + const TreePatternNode *Src) const; void emitCxxPredicateFns(raw_ostream &OS, StringRef CodeFieldName, StringRef TypeIdentifier, StringRef ArgType, @@ -3445,6 +3473,24 @@ class GlobalISelEmitter { std::vector> &MatcherStorage); }; +Expected +GlobalISelEmitter::getInstResultType(const TreePatternNode *Dst) const { + ArrayRef ChildTypes = Dst->getExtTypes(); + CodeGenInstruction *DstI = &Target.getInstruction(Dst->getOperator()); + if (ChildTypes.size() - DstI->ImplicitDefs.size() != 1) + return failedImport("Dst pattern child has multiple results"); + + Optional MaybeOpTy; + if (ChildTypes.front().isMachineValueType()) { + MaybeOpTy = + MVTToLLT(ChildTypes.front().getMachineValueType().SimpleTy); + } + + if (!MaybeOpTy) + return failedImport("Dst operand has an unsupported type"); + return *MaybeOpTy; +} + void GlobalISelEmitter::gatherOpcodeValues() { InstructionOpcodeMatcher::initOpcodeValuesMap(Target); } @@ -3882,6 +3928,7 @@ Error GlobalISelEmitter::importChildMatcher( return Error::success(); } if (SrcChild->getOperator()->getName() == "timm") { + return failedImport("I don't think this works"); OM.addPredicate(); return Error::success(); } @@ -4012,6 +4059,9 @@ Error GlobalISelEmitter::importChildMatcher( Expected GlobalISelEmitter::importExplicitUseRenderer( action_iterator InsertPt, RuleMatcher &Rule, BuildMIAction &DstMIBuilder, TreePatternNode *DstChild) { + if (DstChild->hasName() && !Rule.hasOperand(DstChild->getName())) + return failedImport("Could not find any uses of operand " + + DstChild->getName() + " in the pattern"); const auto &SubOperand = Rule.getComplexSubOperand(DstChild->getName()); if (SubOperand.hasValue()) { @@ -4178,11 +4228,18 @@ Expected GlobalISelEmitter::createAndImportInstructionRenderer( CopyToPhysRegMIBuilder.addRenderer(PhysInput.first); } - importExplicitDefRenderers(DstMIBuilder); + importExplicitDefRenderers(DstMIBuilder, Src); if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst) .takeError()) return std::move(Error); + InsertPt = InsertPtOrError.get(); + + // Render the implicit defs. + // These are only added to the root of the result. + if (auto Error = importImplicitDefRenderers(M, DstMIBuilder, Src)) + return std::move(Error); + InsertPt = InsertPtOrError.get(); return DstMIBuilder; } @@ -4331,9 +4388,10 @@ Expected GlobalISelEmitter::createInstructionRenderer( } void GlobalISelEmitter::importExplicitDefRenderers( - BuildMIAction &DstMIBuilder) { + BuildMIAction &DstMIBuilder, const llvm::TreePatternNode *Src) { const CodeGenInstruction *DstI = DstMIBuilder.getCGI(); - for (unsigned I = 0; I < DstI->Operands.NumDefs; ++I) { + unsigned NumDefs = std::min(DstI->Operands.NumDefs, Src->getNumResults()); + for (unsigned I = 0; I < NumDefs; ++I) { const CGIOperandList::OperandInfo &DstIOperand = DstI->Operands[I]; DstMIBuilder.addRenderer(DstIOperand.Name); } @@ -4553,10 +4611,22 @@ Error GlobalISelEmitter::importDefaultOperandRenderers( } Error GlobalISelEmitter::importImplicitDefRenderers( - BuildMIAction &DstMIBuilder, - const std::vector &ImplicitDefs) const { - if (!ImplicitDefs.empty()) - return failedImport("Pattern defines a physical register"); + RuleMatcher &M, BuildMIAction &DstMIBuilder, + const TreePatternNode *Src) const { + const CodeGenInstruction &DstI = *DstMIBuilder.getCGI(); + int DstINumImplicitDefs = Src->getNumResults() - DstI.Operands.NumDefs; + + for (int I = 0; I < DstINumImplicitDefs; ++I) { + auto PhysOutput = DstI.ImplicitDefs[I]; + assert(PhysOutput->isSubClassOf("Register")); + BuildMIAction &CopyFromPhysRegMIBuilder = M.addAction( + M.allocateOutputInsnID(), &Target.getInstruction(RK.getDef("COPY"))); + CopyFromPhysRegMIBuilder.addRenderer(PhysOutput, true); + CopyFromPhysRegMIBuilder.addRenderer(PhysOutput); + M.addAction( + CopyFromPhysRegMIBuilder.getInsnID(), 0, + *CGRegs.getRegClassForRegister(PhysOutput)); + } return Error::success(); } @@ -4772,75 +4842,75 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { auto &DstI = Target.getInstruction(DstOp); StringRef DstIName = DstI.TheDef->getName(); - if (DstI.Operands.NumDefs != Src->getExtTypes().size()) - return failedImport("Src pattern results and dst MI defs are different (" + - to_string(Src->getExtTypes().size()) + " def(s) vs " + - to_string(DstI.Operands.NumDefs) + " def(s))"); + unsigned SrcNumResults = Src->getNumResults(); + unsigned DstNumDefs = DstI.Operands.NumDefs + DstI.ImplicitDefs.size(); + if (SrcNumResults > DstNumDefs) + return failedImport("More src pattern results than dst MI defs (" + + to_string(SrcNumResults) + " result(s) > " + + to_string(DstNumDefs) + " def(s))"); // The root of the match also has constraints on the register bank so that it // matches the result instruction. - unsigned OpIdx = 0; - for (const TypeSetByHwMode &VTy : Src->getExtTypes()) { - (void)VTy; - - const auto &DstIOperand = DstI.Operands[OpIdx]; - Record *DstIOpRec = DstIOperand.Rec; - if (DstIName == "COPY_TO_REGCLASS") { - DstIOpRec = getInitValueAsRegClass(Dst->getChild(1)->getLeafValue()); - - if (DstIOpRec == nullptr) - return failedImport( - "COPY_TO_REGCLASS operand #1 isn't a register class"); - } else if (DstIName == "REG_SEQUENCE") { - DstIOpRec = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue()); - if (DstIOpRec == nullptr) - return failedImport("REG_SEQUENCE operand #0 isn't a register class"); - } else if (DstIName == "EXTRACT_SUBREG") { - auto InferredClass = inferRegClassFromPattern(Dst->getChild(0)); - if (!InferredClass) - return failedImport("Could not infer class for EXTRACT_SUBREG operand #0"); - - // We can assume that a subregister is in the same bank as it's super - // register. - DstIOpRec = (*InferredClass)->getDef(); - } else if (DstIName == "INSERT_SUBREG") { - auto MaybeSuperClass = inferSuperRegisterClassForNode( - VTy, Dst->getChild(0), Dst->getChild(2)); - if (!MaybeSuperClass) - return failedImport( - "Cannot infer register class for INSERT_SUBREG operand #0"); - // Move to the next pattern here, because the register class we found - // doesn't necessarily have a record associated with it. So, we can't - // set DstIOpRec using this. - OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); - OM.setSymbolicName(DstIOperand.Name); - M.defineOperand(OM.getSymbolicName(), OM); - OM.addPredicate(**MaybeSuperClass); - ++OpIdx; - continue; - } else if (DstIName == "SUBREG_TO_REG") { - auto MaybeRegClass = inferSuperRegisterClass(VTy, Dst->getChild(2)); - if (!MaybeRegClass) - return failedImport( - "Cannot infer register class for SUBREG_TO_REG operand #0"); - OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); + for (unsigned OpIdx = 0; OpIdx != SrcNumResults; ++OpIdx) { + const auto &VTy = Src->getExtType(OpIdx); + OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); + Record *DstIOpRec; + const CodeGenRegisterClass *RC = nullptr; + if (OpIdx < DstI.Operands.NumDefs) { + const auto &DstIOperand = DstI.Operands[OpIdx]; OM.setSymbolicName(DstIOperand.Name); M.defineOperand(OM.getSymbolicName(), OM); - OM.addPredicate(**MaybeRegClass); - ++OpIdx; - continue; - } else if (DstIOpRec->isSubClassOf("RegisterOperand")) - DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); - else if (!DstIOpRec->isSubClassOf("RegisterClass")) - return failedImport("Dst MI def isn't a register class" + - to_string(*Dst)); + DstIOpRec = DstIOperand.Rec; + + if (DstIName == "COPY_TO_REGCLASS") { + DstIOpRec = getInitValueAsRegClass(Dst->getChild(1)->getLeafValue()); + + if (DstIOpRec == nullptr) + return failedImport( + "COPY_TO_REGCLASS operand #1 isn't a register class"); + } else if (DstIName == "REG_SEQUENCE") { + DstIOpRec = getInitValueAsRegClass(Dst->getChild(0)->getLeafValue()); + if (DstIOpRec == nullptr) + return failedImport("REG_SEQUENCE operand #0 isn't a register class"); + } else if (DstIName == "EXTRACT_SUBREG") { + auto InferredClass = inferRegClassFromPattern(Dst->getChild(0)); + if (!InferredClass) + return failedImport("Could not infer class for EXTRACT_SUBREG operand #0"); + + // We can assume that a subregister is in the same bank as it's super + // register. + DstIOpRec = (*InferredClass)->getDef(); + } else if (DstIName == "INSERT_SUBREG") { + auto MaybeSuperClass = inferSuperRegisterClassForNode( + VTy, Dst->getChild(0), Dst->getChild(2)); + if (!MaybeSuperClass) + return failedImport( + "Cannot infer register class for INSERT_SUBREG operand #0"); + RC = *MaybeSuperClass; + } else if (DstIName == "SUBREG_TO_REG") { + auto MaybeRegClass = inferSuperRegisterClass(VTy, Dst->getChild(2)); + if (!MaybeRegClass) + return failedImport( + "Cannot infer register class for SUBREG_TO_REG operand #0"); + RC = *MaybeRegClass; + } else if (DstIOpRec->isSubClassOf("RegisterOperand")) + DstIOpRec = DstIOpRec->getValueAsDef("RegClass"); + else if (!DstIOpRec->isSubClassOf("RegisterClass")) + return failedImport("Dst MI def isn't a register class" + + to_string(*Dst)); + } else { + DstIOpRec = DstI.ImplicitDefs[OpIdx - DstI.Operands.NumDefs]; + if (!DstIOpRec->isSubClassOf("Register")) + return failedImport("Expected implicit def to be a register."); + RC = CGRegs.getRegClassForRegister(DstIOpRec); + if (!RC) + return failedImport("Cannot infer register class for register"); + M.definePhysRegOperand(DstIOpRec, OM, true); + } - OperandMatcher &OM = InsnMatcher.getOperand(OpIdx); - OM.setSymbolicName(DstIOperand.Name); - M.defineOperand(OM.getSymbolicName(), OM); - OM.addPredicate( - Target.getRegisterClass(DstIOpRec)); - ++OpIdx; + if (!RC) + RC = &Target.getRegisterClass(DstIOpRec); + OM.addPredicate(*RC); } auto DstMIBuilderOrError = @@ -4849,11 +4919,6 @@ Expected GlobalISelEmitter::runOnPattern(const PatternToMatch &P) { return std::move(Error); BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get(); - // Render the implicit defs. - // These are only added to the root of the result. - if (auto Error = importImplicitDefRenderers(DstMIBuilder, P.getDstRegs())) - return std::move(Error); - DstMIBuilder.chooseInsnToMutate(M); // Constrain the registers to classes. This is normally derived from the @@ -5127,8 +5192,8 @@ GlobalISelEmitter::buildMatchTable(MutableArrayRef Rules, std::stable_sort(InputRules.begin(), InputRules.end(), [&OpcodeOrder](const Matcher *A, const Matcher *B) { - auto *L = static_cast(A); - auto *R = static_cast(B); + auto *L = cast(A); + auto *R = cast(B); return std::make_tuple(OpcodeOrder[L->getOpcode()], L->getNumOperands()) < std::make_tuple(OpcodeOrder[R->getOpcode()], @@ -5158,14 +5223,14 @@ void GroupMatcher::optimize() { auto E = Matchers.end(); while (T != E) { while (T != E) { - auto *R = static_cast(*T); + auto *R = cast(*T); if (!R->getFirstConditionAsRootType().get().isValid()) break; ++T; } std::stable_sort(F, T, [](Matcher *A, Matcher *B) { - auto *L = static_cast(A); - auto *R = static_cast(B); + auto *L = cast(A); + auto *R = cast(B); return L->getFirstConditionAsRootType() < R->getFirstConditionAsRootType(); }); @@ -5662,8 +5727,7 @@ void GroupMatcher::emit(MatchTable &Table) { << MatchTable::JumpTarget(LabelID) << MatchTable::LineBreak; } for (auto &Condition : Conditions) - Condition->emitPredicateOpcodes( - Table, *static_cast(*Matchers.begin())); + Condition->emitPredicateOpcodes(Table, **Matchers.begin()); for (const auto &M : Matchers) M->emit(Table); diff --git a/llvm/utils/UpdateTestChecks/asm.py b/llvm/utils/UpdateTestChecks/asm.py index 998d8b1f57736a..426114505c2271 100644 --- a/llvm/utils/UpdateTestChecks/asm.py +++ b/llvm/utils/UpdateTestChecks/asm.py @@ -140,6 +140,16 @@ class string: r'^\s*(\.Lfunc_end[0-9]+:\n|end_function)', flags=(re.M | re.S)) +ASM_FUNCTION_Z80_RE = re.compile( + r'^_(?P[^:]+):\s*;+\s*@(?P=func)\n' + r'(?P.*?)\n' + r'^\s*;\s--\sEnd\sfunction', + flags=(re.M | re.S)) + + +SCRUB_LOOP_COMMENT_RE = re.compile( + r'# =>This Inner Loop Header:.*|# in Loop:.*', flags=re.M) + SCRUB_X86_SHUFFLES_RE = ( re.compile( r'^(\s*\w+) [^#\n]+#+ ((?:[xyz]mm\d+|mem)( \{%k\d+\}( \{z\})?)? = .*)$', @@ -307,6 +317,16 @@ def scrub_asm_wasm32(asm, args): asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) return asm +def scrub_asm_z80(asm, args): + # Scrub runs of whitespace out of the assembly, but leave the leading + # whitespace in place. + asm = common.SCRUB_WHITESPACE_RE.sub(r' ', asm) + # Expand the tabs used for indentation. + asm = string.expandtabs(asm, 2) + # Strip trailing whitespace. + asm = common.SCRUB_TRAILING_WHITESPACE_RE.sub(r'', asm) + return asm + def get_triple_from_march(march): triples = { 'amdgcn': 'amdgcn', @@ -350,6 +370,8 @@ def build_function_body_dictionary_for_triple(args, raw_tool_output, triple, pre 'sparc': (scrub_asm_sparc, ASM_FUNCTION_SPARC_RE), 's390x': (scrub_asm_systemz, ASM_FUNCTION_SYSTEMZ_RE), 'wasm32': (scrub_asm_wasm32, ASM_FUNCTION_WASM32_RE), + 'z80': (scrub_asm_z80, ASM_FUNCTION_Z80_RE), + 'ez80': (scrub_asm_z80, ASM_FUNCTION_Z80_RE), } handler = None best_prefix = '' diff --git a/llvm/utils/emacs/emacs.el b/llvm/utils/emacs/emacs.el index 3a2b47cee1db13..e41119923fdcb1 100644 --- a/llvm/utils/emacs/emacs.el +++ b/llvm/utils/emacs/emacs.el @@ -27,6 +27,7 @@ (add-hook 'c-mode-common-hook (function (lambda nil - (if (string-match "llvm" buffer-file-name) + (if (and buffer-file-name + (string-match "llvm" buffer-file-name)) (progn (c-set-style "llvm.org"))))))