[llvm-branch-commits] [llvm] [4/7][PISA] Add PISA GlobalISel lowering, legalization and combiners (PR #214373)
Michal Paszkowski via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Sep 22 05:40:48 PDT 2026
https://github.com/michalpaszkowski updated https://github.com/llvm/llvm-project/pull/214373
>From df719f2fa35d9f875343b1bbff3a44baba3b09d9 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Wed, 5 Aug 2026 17:10:33 -0700
Subject: [PATCH 01/14] [PISA] Add PISA GlobalISel lowering, legalization and
combiners
Add the PISA GlobalISel lowering path: call lowering, the legalizer rules,
register-bank information and the pre/post-legalization combiners, together
with the machine-function info, frame lowering, and the target-machine
pipeline through register-bank selection.
Adds GlobalISel LIT tests for legalization and the combiners. Instruction
selection is added in the following change.
---
llvm/include/llvm/AsmParser/LLToken.h | 1 +
.../llvm/CodeGen/TargetFrameLowering.h | 1 +
llvm/include/llvm/TargetParser/CMakeLists.txt | 3 +
.../llvm/TargetParser/PISATargetParser.def | 18 +
.../llvm/TargetParser/PISATargetParser.h | 116 +
llvm/lib/AsmParser/LLLexer.cpp | 1 +
llvm/lib/AsmParser/LLParser.cpp | 1 +
llvm/lib/IR/AsmWriter.cpp | 1 +
llvm/lib/Target/PISA/CMakeLists.txt | 14 +
llvm/lib/Target/PISA/PISA.h | 63 +-
llvm/lib/Target/PISA/PISACallLowering.cpp | 758 ++++
llvm/lib/Target/PISA/PISACallLowering.h | 44 +
llvm/lib/Target/PISA/PISAFrameLowering.h | 10 +
llvm/lib/Target/PISA/PISAISelLowering.cpp | 712 ++++
llvm/lib/Target/PISA/PISAISelLowering.h | 112 +
llvm/lib/Target/PISA/PISALegalizerInfo.cpp | 3513 +++++++++++++++++
llvm/lib/Target/PISA/PISALegalizerInfo.h | 30 +
.../Target/PISA/PISAMachineFunctionInfo.cpp | 17 +
.../lib/Target/PISA/PISAMachineFunctionInfo.h | 39 +
.../Target/PISA/PISAPostLegalizerCombiner.cpp | 2005 ++++++++++
.../Target/PISA/PISAPreLegalizerCombiner.cpp | 1664 ++++++++
llvm/lib/Target/PISA/PISARegisterBankInfo.cpp | 53 +
llvm/lib/Target/PISA/PISARegisterBankInfo.h | 37 +
llvm/lib/Target/PISA/PISASubtarget.cpp | 24 +-
llvm/lib/Target/PISA/PISASubtarget.h | 46 +-
llvm/lib/Target/PISA/PISATargetMachine.cpp | 134 +-
llvm/lib/Target/PISA/PISATargetMachine.h | 30 +-
llvm/lib/Target/PISA/PISATargetObjectFile.h | 59 +
llvm/lib/TargetParser/CMakeLists.txt | 1 +
llvm/lib/TargetParser/PISATargetParser.cpp | 24 +
...ild-vector-with-constants-trunc-double.mir | 45 +
.../PISA/GlobalISel/combine-abs-iredsmax.mir | 48 +
.../GlobalISel/combine-cmp-and-all-ones.mir | 89 +
.../combine-extract-build-vector.mir | 88 +
.../PISA/GlobalISel/combine-fadd-constant.mir | 18 +
...ine-getdef-ignoring-bitcasts-novectors.mir | 74 +
.../combine-local-id-range-trunc-zext.mir | 92 +
.../combine-p2i-to-i2p-addrspace.mir | 52 +
.../combine-select-trunc-one-zero.mir | 160 +
.../GlobalISel/combine-trunc-bool-trunc.mir | 91 +
.../GlobalISel/combine-truncated-shift.mir | 56 +
.../PISA/GlobalISel/fix-illegal-shift-amt.mir | 63 +
.../legalize-load-store-subbyte-scalar.mir | 76 +
.../PISA/GlobalISel/legalize-threeway-cmp.mir | 90 +
.../PISA/GlobalISel/legalizer-16-32-vec.mir | 182 +
.../legalizer-concat-vectors-clamp-elts.mir | 40 +
.../PISA/GlobalISel/legalizer-constant.mir | 158 +
.../legalizer-extract-subvector.mir | 15 +
...alizer-extract-vector-elt-illegal-size.mir | 52 +
.../PISA/GlobalISel/legalizer-fldexp.mir | 411 ++
.../PISA/GlobalISel/legalizer-freeze-i96.mir | 48 +
.../PISA/GlobalISel/legalizer-frem.mir | 116 +
.../CodeGen/PISA/GlobalISel/legalizer-i2f.mir | 35 +
.../PISA/GlobalISel/legalizer-icmp.mir | 443 +++
.../GlobalISel/legalizer-implicit-def.mir | 367 ++
.../GlobalISel/legalizer-insert-subvector.mir | 16 +
...egalizer-load-non-power-of-2-overfetch.mir | 388 ++
.../legalizer-load-store-i1-vec.mir | 98 +
.../legalizer-load-store-non-standard-vec.mir | 54 +
...legalizer-load-store-vec-of-large-ints.mir | 84 +
.../PISA/GlobalISel/legalizer-mulh-i64.mir | 139 +
.../PISA/GlobalISel/legalizer-mulh.mir | 88 +
.../PISA/GlobalISel/legalizer-phi-s5.mir | 66 +
.../PISA/GlobalISel/legalizer-phi-s96.mir | 64 +
.../GlobalISel/legalizer-phi-scalarize.mir | 33 +
.../PISA/GlobalISel/legalizer-phi-widen.mir | 33 +
.../GlobalISel/legalizer-select-widen.mir | 38 +
.../legalizer-store-long-alignment.mir | 24 +
.../GlobalISel/legalizer-udiv128-sequence.mir | 515 +++
.../GlobalISel/legalizer-unmerge-vectors.mir | 53 +
.../PISA/GlobalISel/legalizer-zext-big.mir | 30 +
.../PISA/GlobalISel/mir-print-bfloat.mir | 21 +
.../postlegalizer-compare-select.mir | 67 +
...ostlegalizer-extract-subvector-partial.mir | 38 +
.../postlegalizer-no-commute-shift-loop.mir | 52 +
.../PISA/GlobalISel/postrapseudo-i1.mir | 29 +
.../prelegalizer-reduce-predicates.mir | 181 +
.../PISA/GlobalISel/prelegalizer-zext-and.mir | 49 +
.../PISA/GlobalISel/retain-extractvec.mir | 37 +
79 files changed, 14629 insertions(+), 8 deletions(-)
create mode 100644 llvm/include/llvm/TargetParser/PISATargetParser.def
create mode 100644 llvm/include/llvm/TargetParser/PISATargetParser.h
create mode 100644 llvm/lib/Target/PISA/PISACallLowering.cpp
create mode 100644 llvm/lib/Target/PISA/PISACallLowering.h
create mode 100644 llvm/lib/Target/PISA/PISAISelLowering.cpp
create mode 100644 llvm/lib/Target/PISA/PISAISelLowering.h
create mode 100644 llvm/lib/Target/PISA/PISALegalizerInfo.cpp
create mode 100644 llvm/lib/Target/PISA/PISALegalizerInfo.h
create mode 100644 llvm/lib/Target/PISA/PISAMachineFunctionInfo.cpp
create mode 100644 llvm/lib/Target/PISA/PISAMachineFunctionInfo.h
create mode 100644 llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
create mode 100644 llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
create mode 100644 llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
create mode 100644 llvm/lib/Target/PISA/PISARegisterBankInfo.h
create mode 100644 llvm/lib/Target/PISA/PISATargetObjectFile.h
create mode 100644 llvm/lib/TargetParser/PISATargetParser.cpp
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-local-id-range-trunc-zext.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalize-threeway-cmp.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-non-power-of-2-overfetch.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-vec-of-large-ints.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-extract-subvector-partial.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir
diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h
index d2766a05ce9baf..3562be546aa577 100644
--- a/llvm/include/llvm/AsmParser/LLToken.h
+++ b/llvm/include/llvm/AsmParser/LLToken.h
@@ -159,6 +159,7 @@ enum Kind {
kw_ptx_device,
kw_spir_kernel,
kw_spir_func,
+ kw_pisa_kernel,
kw_x86_64_sysvcc,
kw_win64cc,
kw_anyregcc,
diff --git a/llvm/include/llvm/CodeGen/TargetFrameLowering.h b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
index 77a1b709d9e17c..151671e42dd02d 100644
--- a/llvm/include/llvm/CodeGen/TargetFrameLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
@@ -34,6 +34,7 @@ enum Value {
ScalableVector = 2,
WasmLocal = 3,
ScalablePredicateVector = 4,
+ PISAShared = 5,
NoAlloc = 255
};
}
diff --git a/llvm/include/llvm/TargetParser/CMakeLists.txt b/llvm/include/llvm/TargetParser/CMakeLists.txt
index af26db48f2132a..ccd7bdfdc209cd 100644
--- a/llvm/include/llvm/TargetParser/CMakeLists.txt
+++ b/llvm/include/llvm/TargetParser/CMakeLists.txt
@@ -16,5 +16,8 @@ tablegen(LLVM R600TargetParserDef.inc -gen-amdgpu-target-def EXTRA_INCLUDES ${PR
set(LLVM_TARGET_DEFINITIONS ${PROJECT_SOURCE_DIR}/lib/Target/AMDGPU/AMDGPU.td)
tablegen(LLVM AMDGPUTargetParserDef.inc -gen-amdgpu-target-def EXTRA_INCLUDES ${PROJECT_SOURCE_DIR}/lib/Target/AMDGPU)
+set(LLVM_TARGET_DEFINITIONS ${PROJECT_SOURCE_DIR}/lib/Target/PISA/PISA.td)
+tablegen(LLVM PISAGenTargetFeatures.inc -gen-target-features EXTRA_INCLUDES ${PROJECT_SOURCE_DIR}/lib/Target/PISA)
+
# This covers all of the tablegen calls above.
add_public_tablegen_target(target_parser_gen)
diff --git a/llvm/include/llvm/TargetParser/PISATargetParser.def b/llvm/include/llvm/TargetParser/PISATargetParser.def
new file mode 100644
index 00000000000000..fafae0ebd3e8a2
--- /dev/null
+++ b/llvm/include/llvm/TargetParser/PISATargetParser.def
@@ -0,0 +1,18 @@
+//===- PISATargetParser.def - PISA target parsing defines -------*- 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 provides defines to build up the PISA target parser's logic.
+//
+//===----------------------------------------------------------------------===//
+
+// NOTE: NO INCLUDE GUARD DESIRED!
+#ifndef PISA_TARGET
+#define PISA_TARGET(NAME, GEN, VARIANT, FWDCOMPAT)
+#endif
+
+PISA_TARGET("100", 100, VariantNone, true)
diff --git a/llvm/include/llvm/TargetParser/PISATargetParser.h b/llvm/include/llvm/TargetParser/PISATargetParser.h
new file mode 100644
index 00000000000000..e9d5cd5bb1d4ad
--- /dev/null
+++ b/llvm/include/llvm/TargetParser/PISATargetParser.h
@@ -0,0 +1,116 @@
+//===-- PISATargetParser.h - PISA target parsing defines ------------------===//
+//
+// 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_TARGETPARSER_PISATARGETPARSER_H
+#define LLVM_TARGETPARSER_PISATARGETPARSER_H
+
+#include "TargetParser.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/TargetParser/Triple.h"
+
+namespace llvm {
+namespace PISA {
+
+// last known PISA version
+constexpr unsigned LatestPISAVersion = 100; // PISA 1.0
+
+enum class PISATargetVariant : unsigned {
+ VariantNone = 0,
+};
+
+struct PISATargetInfo {
+ StringRef Name;
+ unsigned Gen;
+ PISATargetVariant Variant;
+ bool FwdCompat;
+};
+
+inline PISATargetInfo getPISATargetInfo(StringRef Name) {
+ static constexpr PISATargetInfo DefaultInfo = {
+ "", 0, PISATargetVariant::VariantNone, false};
+ static constexpr PISATargetInfo Info[] = {
+#define PISA_TARGET(NAME, GEN, VARIANT, FWDCOMPAT) \
+ {NAME, GEN, PISATargetVariant::VARIANT, FWDCOMPAT},
+#include "PISATargetParser.def"
+#undef PISA_TARGET
+ };
+ auto *It = llvm::find_if(Info, [&Name](const PISATargetInfo &Entry) {
+ return Entry.Name == Name;
+ });
+ return It == std::end(Info) ? DefaultInfo : *It;
+}
+
+// Defined as a macro (rather than only a StringRef) so the bare target names
+// from PISATargetParser.def can be concatenated into full CPU names as
+// compile-time string literals in fillValidCPUList(); PISACPUPrefix is derived
+// from it so the "igca_" literal lives in exactly one place.
+#define PISA_CPU_PREFIX "igca_"
+inline constexpr StringRef PISACPUPrefix = PISA_CPU_PREFIX;
+
+inline StringRef stripCPUPrefix(StringRef Name) {
+ Name.consume_front(PISACPUPrefix);
+ return Name;
+}
+
+// Full CPU name (with the "igca_" prefix) used as the default device when no
+// -mcpu/-march is specified on the command-line. This is the single source of
+// truth for the default PISA target; the backend subtarget uses the bare name
+// via stripCPUPrefix(), while the Clang driver passes the prefixed name to
+// cc1's -target-cpu.
+inline StringRef getDefaultCPUName() { return PISA_CPU_PREFIX "100"; }
+
+inline bool isValidCPU(StringRef Name) {
+ if (!Name.consume_front(PISACPUPrefix))
+ return false;
+ return !getPISATargetInfo(Name).Name.empty();
+}
+
+inline void fillValidCPUList(SmallVectorImpl<StringRef> &Values) {
+ Values.append({
+#define PISA_TARGET(NAME, GEN, VARIANT, FWDCOMPAT) PISA_CPU_PREFIX NAME,
+#include "PISATargetParser.def"
+#undef PISA_TARGET
+ });
+}
+#undef PISA_CPU_PREFIX
+
+// Fills Features with the full, transitively-implied default feature set for
+// CPU, as declared by the Proc<>/SubtargetFeature Implies lists in
+// PISAFeatures.td/PISA.td (see PISATargetParser.cpp) -- this keeps the
+// CPU->feature expansion clang sees in sync with the real subtarget's
+// TableGen-generated expansion, with the .td file as the single source of
+// truth for both.
+LLVM_ABI void fillFeatureMap(StringRef CPU, StringMap<bool> &Features);
+
+// Check for compatible PISATargetInfo
+// - TInfo - PISA target specified on command-line via -mcpu=
+// - IInfo - instruction PISA target encoded in .td files
+inline bool isCompatiblePISATargetInfo(const PISATargetInfo &TInfo,
+ const PISATargetInfo &IInfo) {
+ if (TInfo.Gen == 0)
+ return false; // no -mcpu specified
+ if (IInfo.Gen == 0)
+ return false; // no instruction target
+ if (IInfo.Gen > TInfo.Gen)
+ return false; // future instruction
+ if ((IInfo.Variant != TInfo.Variant) &&
+ (IInfo.Variant != PISATargetVariant::VariantNone))
+ return false; // variant mismatch
+ if (!IInfo.FwdCompat) // exact match required
+ return (IInfo.Gen == TInfo.Gen) && (IInfo.Variant == TInfo.Variant) &&
+ !TInfo.FwdCompat;
+ return true;
+}
+
+} // namespace PISA
+} // namespace llvm
+
+#endif // LLVM_TARGETPARSER_PISATARGETPARSER_H
diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp
index 069a180056488d..d40cd83971fb5e 100644
--- a/llvm/lib/AsmParser/LLLexer.cpp
+++ b/llvm/lib/AsmParser/LLLexer.cpp
@@ -670,6 +670,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(ptx_device);
KEYWORD(spir_kernel);
KEYWORD(spir_func);
+ KEYWORD(pisa_kernel);
KEYWORD(intel_ocl_bicc);
KEYWORD(x86_64_sysvcc);
KEYWORD(win64cc);
diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp
index 93a79a7035e6f7..3aebe6a9461e5b 100644
--- a/llvm/lib/AsmParser/LLParser.cpp
+++ b/llvm/lib/AsmParser/LLParser.cpp
@@ -2345,6 +2345,7 @@ bool LLParser::parseOptionalCallingConv(unsigned &CC) {
case lltok::kw_ptx_device: CC = CallingConv::PTX_Device; break;
case lltok::kw_spir_kernel: CC = CallingConv::SPIR_KERNEL; break;
case lltok::kw_spir_func: CC = CallingConv::SPIR_FUNC; break;
+ case lltok::kw_pisa_kernel: CC = CallingConv::PISA_KERNEL; break;
case lltok::kw_intel_ocl_bicc: CC = CallingConv::Intel_OCL_BI; break;
case lltok::kw_x86_64_sysvcc: CC = CallingConv::X86_64_SysV; break;
case lltok::kw_win64cc: CC = CallingConv::Win64; break;
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp
index c3202eea12c283..c0c8959b07c02a 100644
--- a/llvm/lib/IR/AsmWriter.cpp
+++ b/llvm/lib/IR/AsmWriter.cpp
@@ -391,6 +391,7 @@ static void printCallingConv(unsigned cc, raw_ostream &Out) {
case CallingConv::Win64: Out << "win64cc"; break;
case CallingConv::SPIR_FUNC: Out << "spir_func"; break;
case CallingConv::SPIR_KERNEL: Out << "spir_kernel"; break;
+ case CallingConv::PISA_KERNEL: Out << "pisa_kernel"; break;
case CallingConv::Swift: Out << "swiftcc"; break;
case CallingConv::SwiftTail: Out << "swifttailcc"; break;
case CallingConv::X86_INTR: Out << "x86_intrcc"; break;
diff --git a/llvm/lib/Target/PISA/CMakeLists.txt b/llvm/lib/Target/PISA/CMakeLists.txt
index 17dd046b4645f0..788ffb53ef90aa 100644
--- a/llvm/lib/Target/PISA/CMakeLists.txt
+++ b/llvm/lib/Target/PISA/CMakeLists.txt
@@ -2,19 +2,33 @@ add_llvm_component_group(PISA)
set(LLVM_TARGET_DEFINITIONS PISA.td)
tablegen(LLVM PISAGenAsmWriter.inc -gen-asm-writer)
+tablegen(LLVM PISAGenGlobalISel.inc -gen-global-isel -warn-on-skipped-patterns -gisel-extended-llt)
tablegen(LLVM PISAGenInstrInfo.inc -gen-instr-info)
tablegen(LLVM PISAGenMCCodeEmitter.inc -gen-emitter)
+tablegen(LLVM PISAGenRegisterBank.inc -gen-register-bank)
tablegen(LLVM PISAGenRegisterInfo.inc -gen-register-info)
tablegen(LLVM PISAGenSubtargetInfo.inc -gen-subtarget)
tablegen(LLVM PISAGenSearchableTables.inc -gen-searchable-tables)
+tablegen(LLVM PISAGenPreLegalizeGICombiner.inc -gen-global-isel-combiner
+ -combiners="PISAPreLegalizerCombiner")
+tablegen(LLVM PISAGenPostLegalizeGICombiner.inc -gen-global-isel-combiner
+ -combiners="PISAPostLegalizerCombiner")
+
add_public_tablegen_target(PISACommonTableGen)
add_llvm_target(PISACodeGen
PISACacheCtrlMMRA.cpp
+ PISACallLowering.cpp
+ PISAISelLowering.cpp
PISAInstrInfo.cpp
+ PISALegalizerInfo.cpp
PISAMCInstLower.cpp
+ PISAMachineFunctionInfo.cpp
+ PISAPostLegalizerCombiner.cpp
+ PISAPreLegalizerCombiner.cpp
PISARegManager.cpp
+ PISARegisterBankInfo.cpp
PISARegisterInfo.cpp
PISASubtarget.cpp
PISATargetMachine.cpp
diff --git a/llvm/lib/Target/PISA/PISA.h b/llvm/lib/Target/PISA/PISA.h
index 21e5c367ce48e8..375208bf283501 100644
--- a/llvm/lib/Target/PISA/PISA.h
+++ b/llvm/lib/Target/PISA/PISA.h
@@ -10,10 +10,71 @@
#define LLVM_LIB_TARGET_PISA_PISA_H
#include "MCTargetDesc/PISAMCTargetDesc.h"
+#include "PISADefines.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/Target/TargetMachine.h"
namespace llvm {
+class ImmutablePass;
+class InstructionSelector;
+class RegisterBankInfo;
+class PISASubtarget;
class PISATargetMachine;
-class PassRegistry;
+
+ModulePass *createPISALegalizeCallsPass();
+ModulePass *createPISAVerifierPass();
+ModulePass *createPISAKernelByValArgsLoweringLegacyPass();
+ModulePass *createPISAPropagateNullPointersPass();
+
+FunctionPass *createPISAExpandIntrinsicsPass();
+FunctionPass *createPISAEmitIntrinsicsPass();
+FunctionPass *createPISALegalizeSubregAccess();
+FunctionPass *createPISAOptimizeSubregAccess();
+FunctionPass *createPISAOptimizeRedundantCopies();
+FunctionPass *createPISAInsertLifetimeStart();
+FunctionPass *createPISAMarkConvergentNoMerge();
+FunctionPass *createPISAPreLegalizerCombiner();
+FunctionPass *createPISAPostLegalizerCombiner();
+FunctionPass *createPISAReplaceIntrinsicsPass();
+MachineFunctionPass *createPISALegalizePredicatesPass();
+MachineFunctionPass *createCacheHintSelectorPass();
+MachineFunctionPass *createPISAScopeSelectorPass();
+
+InstructionSelector *
+createPISAInstructionSelector(const PISATargetMachine &TM,
+ const PISASubtarget &Subtarget,
+ const RegisterBankInfo &RBI);
+
+MachineFunctionPass *
+createPISAMachineFunctionPrinterPass(const std::string &Banner,
+ unsigned Counter);
+FunctionPass *createPISALayoutPass();
+MachineFunctionPass *createPISAVerifyTypesPass();
+
+void initializeCacheHintSelectorPass(PassRegistry &);
+void initializePISAEmitIntrinsicsPass(PassRegistry &);
+void initializePISAExpandIntrinsicsPass(PassRegistry &);
+void initializePISAInsertLifetimeStartPass(PassRegistry &);
+void initializePISAKernelByValArgsLoweringLegacyPass(PassRegistry &);
+void initializePISALegalizeCallsPass(PassRegistry &);
+void initializePISALegalizeSubregAccessPass(PassRegistry &);
+void initializePISAMachineFunctionPrinterPass(PassRegistry &);
+void initializePISAMarkConvergentNoMergePass(PassRegistry &);
+void initializePISAOptimizeRedundantCopiesPass(PassRegistry &);
+void initializePISAOptimizeSubregAccessPass(PassRegistry &);
+void initializePISALegalizePredicatesPass(PassRegistry &);
+void initializePISAPostLegalizerCombinerPass(PassRegistry &);
+void initializePISAPreLegalizerCombinerPass(PassRegistry &);
+void initializePISAPropagateNullPointersPass(PassRegistry &);
+void initializePISAReplaceIntrinsicsPass(PassRegistry &);
+void initializePISAScopeSelectorPass(PassRegistry &);
+void initializePISAVerifierPass(PassRegistry &);
+void initializePISALayoutPass(PassRegistry &);
+void initializePISAVerifyTypesPass(PassRegistry &);
+
+namespace PISA {
+LLVM_READONLY int16_t getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx);
+} // namespace PISA
} // namespace llvm
#endif // LLVM_LIB_TARGET_PISA_PISA_H
diff --git a/llvm/lib/Target/PISA/PISACallLowering.cpp b/llvm/lib/Target/PISA/PISACallLowering.cpp
new file mode 100644
index 00000000000000..6928ed63d3aabb
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISACallLowering.cpp
@@ -0,0 +1,758 @@
+//===-- PISACallLowering.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISACallLowering.h"
+#include "MCTargetDesc/PISABaseInfo.h"
+#include "PISA.h"
+#include "PISAISelLowering.h"
+#include "PISAMachineFunctionInfo.h"
+#include "PISARegisterInfo.h"
+#include "PISASubtarget.h"
+#include "PISAUtils.h"
+#include "llvm/CodeGen/FunctionLoweringInfo.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/IR/Metadata.h"
+#include "llvm/Support/ModRef.h"
+
+using namespace llvm;
+
+PISACallLowering::PISACallLowering(const PISATargetLowering &TLI)
+ : CallLowering(&TLI) {}
+
+bool PISACallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
+ const Value *Val, ArrayRef<Register> VRegs,
+ FunctionLoweringInfo &FLI,
+ Register SwiftErrorVReg) const {
+ // FIXME: Currently the return support is only for registers.
+ // Pending:
+ // - return immediates: fold immediates to return operand
+ if (VRegs.size() > 1)
+ return false;
+ if (Val) {
+ auto &DL = MIRBuilder.getDataLayout();
+ const auto &STI = MIRBuilder.getMF().getSubtarget();
+ unsigned Op = 0;
+ auto *Ty = Val->getType();
+ auto VReg = VRegs[0];
+ if (Ty->isVectorTy()) {
+ auto *VTy = cast<FixedVectorType>(Ty);
+ unsigned NumElts = VTy->getNumElements();
+ unsigned EltSize = DL.getTypeSizeInBits(Ty->getScalarType());
+ switch (EltSize) {
+ case 8:
+ switch (NumElts) {
+ case 2:
+ Op = PISA::retValue_v2i8_r;
+ break;
+ case 3:
+ Op = PISA::retValue_v3i8_r;
+ break;
+ case 4:
+ Op = PISA::retValue_v4i8_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return vector size!");
+ break;
+ }
+ break;
+ case 16:
+ switch (NumElts) {
+ case 2:
+ Op = PISA::retValue_v2i16_r;
+ break;
+ case 3:
+ Op = PISA::retValue_v3i16_r;
+ break;
+ case 4:
+ Op = PISA::retValue_v4i16_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return vector size!");
+ break;
+ }
+ break;
+ case 32:
+ switch (NumElts) {
+ case 2:
+ Op = PISA::retValue_v2i32_r;
+ break;
+ case 3:
+ Op = PISA::retValue_v3i32_r;
+ break;
+ case 4:
+ Op = PISA::retValue_v4i32_r;
+ break;
+ case 5:
+ Op = PISA::retValue_v5i32_r;
+ break;
+ case 6:
+ Op = PISA::retValue_v6i32_r;
+ break;
+ case 7:
+ Op = PISA::retValue_v7i32_r;
+ break;
+ case 8:
+ Op = PISA::retValue_v8i32_r;
+ break;
+ case 16:
+ Op = PISA::retValue_v16i32_r;
+ break;
+ case 32:
+ Op = PISA::retValue_v32i32_r;
+ break;
+ case 64:
+ Op = PISA::retValue_v64i32_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return vector size!");
+ break;
+ }
+ break;
+ case 64:
+ switch (NumElts) {
+ case 2:
+ Op = PISA::retValue_v2i64_r;
+ break;
+ case 3:
+ Op = PISA::retValue_v3i64_r;
+ break;
+ case 4:
+ Op = PISA::retValue_v4i64_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return vector size!");
+ break;
+ }
+ break;
+ default:
+ llvm_unreachable("Unknown return size!");
+ break;
+ }
+ } else {
+ unsigned BitSize = DL.getTypeSizeInBits(Ty);
+ switch (BitSize) {
+ case 1: // change i1 to i16 (see lowerCall())
+ {
+ const LLT I16 = LLT::integer(16);
+ auto Dst = MIRBuilder.getMRI()->createGenericVirtualRegister(I16);
+ auto &MF = MIRBuilder.getMF();
+ auto &F = MF.getFunction();
+ const DataLayout &DL = MF.getDataLayout();
+ ArgInfo RetInfo(VReg, *Val, 0);
+ setArgFlags(RetInfo, AttributeList::ReturnIndex, DL, F);
+ auto Sext = llvm::any_of(
+ RetInfo.Flags, [](const auto &Flag) { return Flag.isSExt(); });
+ auto Zext = llvm::any_of(
+ RetInfo.Flags, [](const auto &Flag) { return Flag.isZExt(); });
+ if (Sext) {
+ MIRBuilder.buildSExt(Dst, VReg);
+ } else if (Zext) {
+ MIRBuilder.buildZExt(Dst, VReg);
+ } else {
+ MIRBuilder.buildAnyExt(Dst, VReg);
+ }
+ VReg = Dst;
+ }
+ Op = PISA::retValue_i16_r;
+ break;
+ case 8:
+ Op = PISA::retValue_i8_r;
+ break;
+ case 16:
+ Op = PISA::retValue_i16_r;
+ break;
+ case 32:
+ Op = PISA::retValue_i32_r;
+ break;
+ case 64:
+ Op = PISA::retValue_i64_r;
+ break;
+ default:
+ llvm_unreachable("Unknown return size!");
+ break;
+ }
+ }
+ // Backend-defined opcodes, e.g. retValue* must have a register
+ // class assigned to their 'source' register. During instruction
+ // combine, a preceeding instructions may be combined, with a new
+ // 'dest' register being assigned. Attempt to replace 'source'
+ // with 'dest' will trigger an assertion in canReplaceReg(), since
+ // there is an expectation of no register class being assigned.
+ // Having an extra copy here eliminates the problem; copy itself
+ // will be removed during instruction selection.
+ auto *MRI = MIRBuilder.getMRI();
+ auto Tmp = MRI->createGenericVirtualRegister(MRI->getType(VReg));
+ MIRBuilder.buildCopy(Tmp, VReg);
+ MIRBuilder.buildInstr(Op).addUse(Tmp).constrainAllUses(
+ MIRBuilder.getTII(), *STI.getRegisterInfo(), *STI.getRegBankInfo());
+ return true;
+ }
+ MIRBuilder.buildInstr(PISA::ret);
+ return true;
+}
+
+bool PISACallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
+ const Function &F,
+ ArrayRef<ArrayRef<Register>> VRegs,
+ FunctionLoweringInfo &FLI) const {
+ auto *MRI = MIRBuilder.getMRI();
+ auto &MF = MIRBuilder.getMF();
+ auto &Ctx = F.getContext();
+ auto *MFInfo = MF.getInfo<PISAMachineFunctionInfo>();
+ auto &DL = F.getParent()->getDataLayout();
+ bool IsKernel = (F.getCallingConv() == CallingConv::PISA_KERNEL);
+ for (const auto [i, Arg] : llvm::enumerate(F.args())) {
+ assert(VRegs[i].size() == 1 && "Formal arg has multiple vregs");
+
+ ArgInfo OrigArg{VRegs[i], Arg, static_cast<unsigned>(i)};
+ setArgFlags(OrigArg, i + AttributeList::FirstArgIndex, DL, F);
+ auto *ArgType = OrigArg.OrigValue->getType();
+ const bool IsByRef = ArgType->isPointerTy() && OrigArg.Flags[0].isByRef();
+ const unsigned ArgSize = IsByRef
+ ? OrigArg.Flags[0].getByRefSize()
+ : MRI->getType(VRegs[i][0]).getSizeInBytes();
+ MFInfo->setArgInfo(i, ArgSize, IsByRef);
+
+ if (IsKernel && Arg.use_empty())
+ continue;
+
+ unsigned Op = 0;
+ if (IsByRef) {
+ assert(IsKernel && "'byref' is only used in kernel!");
+ Op = PISA::G_PISA_PARAM_SLOT;
+ loadParamWithOpcode(MIRBuilder, F, VRegs[i][0], ArgType, Op,
+ Arg.getArgNo(), 0);
+ } else if (IsKernel && ArgType->isVectorTy()) {
+ auto *VectorTy = cast<FixedVectorType>(ArgType);
+ auto NumElts = VectorTy->getNumElements();
+ auto *EltTy = VectorTy->getElementType();
+ auto EltSize = DL.getTypeSizeInBits(EltTy);
+ auto Split = (NumElts > 4) || ((NumElts == 3) && (EltSize != 32)) ||
+ ((NumElts == 4) && (EltSize == 64)) || (NumElts == 1);
+ if (Split) {
+ // handle odd-sized and large kernel args, e.g.
+ // <3 x i8> <16 x i16>
+ // loadParam_8b @[arg+0] loadParam_v4_32b @[arg+0]
+ // loadParam_8b @[arg+1] loadParam_v4_32b @[arg+16]
+ // loadParam_8b @[arg+2] buildVector(<8 x i32>)
+ // buildVector(<3 x i8>) bitcast(<16 x i16)
+ auto TargetReg = VRegs[i][0];
+
+ const auto *EltRegClass =
+ (EltSize == 8
+ ? &PISA::Reg8bRegClass
+ : (EltSize == 16 ? &PISA::Reg16bRegClass
+ : (EltSize == 32 ? &PISA::Reg32bRegClass
+ : &PISA::Reg64bRegClass)));
+ auto TotalSize = NumElts * EltSize;
+ auto EltLLT = LLT::integer(EltSize);
+ auto I32 = LLT::integer(32);
+ if (NumElts <= 4) {
+ // do not group
+ } else if (TotalSize % 128 == 0) { // 4 x i32
+ EltTy = FixedVectorType::get(Type::getInt32Ty(Ctx), 4);
+ EltRegClass = &PISA::RegV4_32bRegClass;
+ EltLLT = LLT::vector(ElementCount::getFixed(4), I32);
+ TargetReg = MRI->createGenericVirtualRegister(
+ LLT::vector(ElementCount::getFixed(TotalSize / 32), I32));
+ NumElts = TotalSize / 128;
+ } else if (TotalSize % 64 == 0) { // 2 x i32
+ EltTy = FixedVectorType::get(Type::getInt32Ty(Ctx), 2);
+ EltRegClass = &PISA::RegV2_32bRegClass;
+ EltLLT = LLT::vector(ElementCount::getFixed(2), I32);
+ TargetReg = MRI->createGenericVirtualRegister(
+ LLT::vector(ElementCount::getFixed(TotalSize / 32), I32));
+ NumElts = TotalSize / 64;
+ } else if (TotalSize % 32 == 0) { // 1 x i32
+ EltTy = Type::getInt32Ty(Ctx);
+ EltRegClass = &PISA::Reg32bRegClass;
+ EltLLT = LLT::integer(32);
+ TargetReg = MRI->createGenericVirtualRegister(
+ LLT::vector(ElementCount::getFixed(TotalSize / 32), I32));
+ NumElts = TotalSize / 32;
+ }
+
+ SmallVector<Register, 4> Regs;
+ for (unsigned I = 0; I < NumElts; I++) {
+ auto Reg = MRI->createGenericVirtualRegister(EltLLT);
+ MRI->setRegClass(Reg, EltRegClass);
+ Op = getLoadParamOpcode(MIRBuilder, F, Reg, EltTy);
+ loadParamWithOpcode(MIRBuilder, F, Reg, EltTy, Op, Arg.getArgNo(),
+ I * EltLLT.getSizeInBytes());
+ if (EltTy->isPointerTy()) {
+ // ld.param loads a scalar value, so convert to ptr here
+ auto AS = cast<PointerType>(EltTy)->getAddressSpace();
+ auto PtrLLT = LLT::pointer(AS, EltSize);
+ auto CastReg = MRI->createGenericVirtualRegister(PtrLLT);
+ MRI->setRegClass(CastReg, EltRegClass);
+ MIRBuilder.buildIntToPtr(CastReg, Reg);
+ Regs.push_back(CastReg);
+ } else if (EltTy->isFloatingPointTy()) {
+ // ld.param loads an integer value; bitcast to float so that
+ // G_BUILD_VECTOR element types match the result vector element
+ // type.
+ auto FloatLLT = EltTy->isBFloatTy() ? LLT::bfloat16()
+ : EltSize == 16 ? LLT::float16()
+ : EltSize == 32 ? LLT::float32()
+ : LLT::float64();
+ auto CastReg = MRI->createGenericVirtualRegister(FloatLLT);
+ MRI->setRegClass(CastReg, EltRegClass);
+ MIRBuilder.buildBitcast(CastReg, Reg);
+ Regs.push_back(CastReg);
+ } else {
+ Regs.push_back(Reg);
+ }
+ }
+ if (NumElts == 1)
+ MIRBuilder.buildCopy(TargetReg, Regs[0]);
+ else if (EltLLT.isVector())
+ MIRBuilder.buildConcatVectors(TargetReg, Regs);
+ else
+ MIRBuilder.buildBuildVector(TargetReg, Regs);
+ if (TargetReg != VRegs[i][0]) {
+ if (MRI->getType(TargetReg) != MRI->getType(VRegs[i][0]))
+ MIRBuilder.buildBitcast(VRegs[i][0], TargetReg);
+ else
+ MIRBuilder.buildCopy(VRegs[i][0], TargetReg);
+ }
+ } else {
+ Op = getLoadParamOpcode(MIRBuilder, F, VRegs[i][0], Arg.getType());
+ loadParamWithOpcode(MIRBuilder, F, VRegs[i][0], ArgType, Op,
+ Arg.getArgNo(), 0);
+ }
+ } else {
+ Op = getLoadParamOpcode(MIRBuilder, F, VRegs[i][0], Arg.getType());
+ loadParamWithOpcode(MIRBuilder, F, VRegs[i][0], ArgType, Op,
+ Arg.getArgNo(), 0);
+ }
+ }
+ return true;
+}
+
+void PISACallLowering::loadParamWithOpcode(MachineIRBuilder &MIRBuilder,
+ const Function &F,
+ const Register &VReg, Type *ArgType,
+ unsigned Opcode, unsigned ArgNo,
+ unsigned Offset) const {
+ auto *MRI = MIRBuilder.getMRI();
+ auto &DL = F.getParent()->getDataLayout();
+ bool IsKernel = (F.getCallingConv() == CallingConv::PISA_KERNEL);
+ const auto BitSize = DL.getTypeSizeInBits(ArgType->getScalarType());
+
+ auto VReg16 = VReg;
+ if (BitSize == 1) { // load arg into i16
+ VReg16 = MRI->createGenericVirtualRegister(LLT::integer(16));
+ MRI->setRegClass(VReg16, &PISA::Reg16bRegClass);
+ }
+
+ auto MIB = MIRBuilder.buildInstr(Opcode).addDef(VReg16).addImm(ArgNo);
+ if (IsKernel)
+ MIB.addImm(Offset);
+
+ // Attach the kernel argument name from !kernel_arg_name metadata as an
+ // extra symbol operand on the loadParam instruction. This allows
+ // PISAInstPrinter to print the actual argument name (e.g., [%input])
+ // instead of the generic [%argN].
+ if (IsKernel)
+ if (MDNode *MD = F.getMetadata("kernel_arg_name"))
+ if (ArgNo < MD->getNumOperands())
+ if (auto *S = dyn_cast<MDString>(MD->getOperand(ArgNo)))
+ if (!S->getString().empty())
+ MIB.addExternalSymbol(
+ MIRBuilder.getMF().createExternalSymbolName(S->getString()));
+
+ if (BitSize == 1) { // convert i16 into i1
+ MIRBuilder.buildTrunc(VReg, VReg16);
+ }
+}
+
+unsigned PISACallLowering::getLoadParamOpcode(MachineIRBuilder &MIRBuilder,
+ const Function &F,
+ const Register &VReg,
+ Type *ArgType) const {
+ auto *MRI = MIRBuilder.getMRI();
+ auto &MF = MIRBuilder.getMF();
+ const auto *TRI = static_cast<const PISARegisterInfo *>(
+ MF.getSubtarget().getRegisterInfo());
+
+ bool IsKernel = (F.getCallingConv() == CallingConv::PISA_KERNEL);
+ unsigned Op = 0;
+ auto &DL = F.getParent()->getDataLayout();
+
+ const unsigned ParamScalar[2][4] = {
+ // [isKernel][8/16/32/64]
+ {PISA::functionParameter_i8, PISA::functionParameter_i16,
+ PISA::functionParameter_i32, PISA::functionParameter_i64},
+ {PISA::loadParam_i8, PISA::loadParam_i16, PISA::loadParam_i32,
+ PISA::loadParam_i64}};
+ const unsigned ParamVector[2][4][3] = {
+ // [isKernel][8/16/32/64][v2/v3/v4]
+ {
+ {PISA::functionParameter_v2i8, PISA::functionParameter_v3i8,
+ PISA::functionParameter_v4i8},
+ {PISA::functionParameter_v2i16, PISA::functionParameter_v3i16,
+ PISA::functionParameter_v4i16},
+ {PISA::functionParameter_v2i32, PISA::functionParameter_v3i32,
+ PISA::functionParameter_v4i32},
+ {PISA::functionParameter_v2i64, PISA::functionParameter_v3i64,
+ PISA::functionParameter_v4i64},
+ },
+ {{PISA::loadParam_v2i8, 0, PISA::loadParam_v4i8},
+ {PISA::loadParam_v2i16, 0, PISA::loadParam_v4i16},
+ {PISA::loadParam_v2i32, PISA::loadParam_v3i32, PISA::loadParam_v4i32},
+ {PISA::loadParam_v2i64, PISA::loadParam_v3i64, 0}}};
+
+ const auto BitSize = DL.getTypeSizeInBits(ArgType->getScalarType());
+ // Calculate the argument size in bytes.
+ if (ArgType->isIntegerTy()) {
+ switch (BitSize) {
+ case 1: // i1 args are loaded via i16 register
+ case 16:
+ MRI->setRegClass(VReg, &PISA::Reg16bRegClass);
+ Op = ParamScalar[IsKernel][1];
+ break;
+ case 8:
+ MRI->setRegClass(VReg, &PISA::Reg8bRegClass);
+ Op = ParamScalar[IsKernel][0];
+ break;
+ case 32:
+ MRI->setRegClass(VReg, &PISA::Reg32bRegClass);
+ Op = ParamScalar[IsKernel][2];
+ break;
+ case 64:
+ MRI->setRegClass(VReg, &PISA::Reg64bRegClass);
+ Op = ParamScalar[IsKernel][3];
+ break;
+ default:
+ assert(false && "Bit size for call arg not supported");
+ }
+ } else if (ArgType->isPointerTy()) {
+ if (BitSize == 64) {
+ MRI->setRegClass(VReg, &PISA::Reg64bRegClass);
+ Op = ParamScalar[IsKernel][3]; // 64bit
+ } else if (BitSize == 32) {
+ MRI->setRegClass(VReg, &PISA::Reg32bRegClass);
+ Op = ParamScalar[IsKernel][2]; // 32bit
+ } else {
+ llvm_unreachable("unsupported pointer size");
+ }
+ } else if (ArgType->isHalfTy()) {
+ MRI->setRegClass(VReg, &PISA::Reg16bRegClass);
+ Op = ParamScalar[IsKernel][1];
+ } else if (ArgType->isBFloatTy()) {
+ MRI->setRegClass(VReg, &PISA::Reg16bRegClass);
+ Op = ParamScalar[IsKernel][1];
+ } else if (ArgType->isFloatTy()) {
+ MRI->setRegClass(VReg, &PISA::Reg32bRegClass);
+ Op = ParamScalar[IsKernel][2];
+ } else if (ArgType->isDoubleTy()) {
+ MRI->setRegClass(VReg, &PISA::Reg64bRegClass);
+ Op = ParamScalar[IsKernel][3];
+ } else if (ArgType->isVectorTy()) {
+ auto *VectorTy = cast<FixedVectorType>(ArgType);
+ auto NumElts = VectorTy->getNumElements();
+ assert(((((BitSize == 8) || (BitSize == 16) || (BitSize == 64)) &&
+ ((NumElts >= 2) && (NumElts <= 4))) ||
+ ((BitSize == 32) &&
+ (((NumElts >= 2) && (NumElts <= 8)) || (NumElts == 16) ||
+ (NumElts == 32) || (NumElts == 64)))) &&
+ "unsupported vector size");
+ MRI->setRegClass(VReg, TRI->getVectorRegClass(NumElts, BitSize));
+ switch (BitSize) {
+ case 8:
+ Op = ParamVector[IsKernel][0][NumElts - 2];
+ break;
+ case 16:
+ Op = ParamVector[IsKernel][1][NumElts - 2];
+ break;
+ case 32: {
+ if (NumElts > 4) {
+ assert(!IsKernel && "large vector arg in kernel is not supported");
+ switch (NumElts) {
+ default:
+ llvm_unreachable("unsupported number of elements in large vector");
+ break;
+ case 5:
+ Op = PISA::functionParameter_v5i32;
+ break;
+ case 6:
+ Op = PISA::functionParameter_v6i32;
+ break;
+ case 7:
+ Op = PISA::functionParameter_v7i32;
+ break;
+ case 8:
+ Op = PISA::functionParameter_v8i32;
+ break;
+ case 16:
+ Op = PISA::functionParameter_v16i32;
+ break;
+ case 32:
+ Op = PISA::functionParameter_v32i32;
+ break;
+ case 64:
+ Op = PISA::functionParameter_v64i32;
+ break;
+ }
+ } else {
+ Op = ParamVector[IsKernel][2][NumElts - 2];
+ }
+ } break;
+ case 64:
+ Op = ParamVector[IsKernel][3][NumElts - 2];
+ break;
+ default:
+ assert(false && "Bit size for call arg not supported");
+ }
+ assert(Op && "argument type is not supported");
+ } else {
+ report_fatal_error("Argument type not supported");
+ }
+ return Op;
+}
+
+bool PISACallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
+ CallLoweringInfo &Info) const {
+ // Currently call returns should have single vregs.
+ // TODO: handle the case of multiple registers.
+ if (Info.OrigRet.Regs.size() > 1)
+ return false;
+
+ bool IsIndirectCall = Info.Callee.isReg();
+ if (!IsIndirectCall) {
+ assert(Info.Callee.isGlobal());
+ const Function *CF =
+ dyn_cast_or_null<const Function>(Info.Callee.getGlobal());
+ if (CF == nullptr)
+ return false;
+ }
+
+ MachineInstrBuilder MIB;
+ const auto *TRI = static_cast<const PISARegisterInfo *>(
+ MIRBuilder.getMF().getSubtarget().getRegisterInfo());
+
+ if (IsIndirectCall) {
+ Register CalleeReg = Info.Callee.getReg();
+ auto *MRI = MIRBuilder.getMRI();
+ LLT CalleeTy = MRI->getType(CalleeReg);
+ Register CalleeI64Reg = MRI->createGenericVirtualRegister(LLT::integer(64));
+
+ if (CalleeTy.isPointer())
+ MIRBuilder.buildPtrToInt(CalleeI64Reg, CalleeReg);
+ else
+ llvm_unreachable("Unexpected indirect callee register type");
+
+ MRI->setRegClass(CalleeI64Reg, TRI->getRegClassFromLLT(LLT::integer(64)));
+ Info.Callee = MachineOperand::CreateReg(CalleeI64Reg, false);
+ }
+
+ // promote i1 args to i16
+ SmallVector<Register, 8> ArgRegs;
+ for (const auto &Arg : Info.OrigArgs) {
+ // Currently call args should have single vregs.
+ if (Arg.Regs.size() > 1)
+ return false;
+ if (Arg.Ty->getScalarSizeInBits() == 1) {
+ const LLT I16 = LLT::integer(16);
+ auto Reg = MIRBuilder.getMRI()->createGenericVirtualRegister(I16);
+ MIRBuilder.getMRI()->setRegClass(Reg, TRI->getRegClassFromLLT(I16));
+ auto Sext = llvm::any_of(Arg.Flags,
+ [](const auto &Flag) { return Flag.isSExt(); });
+ auto Zext = llvm::any_of(Arg.Flags,
+ [](const auto &Flag) { return Flag.isZExt(); });
+ if (Sext) {
+ MIRBuilder.buildSExt(Reg, Arg.Regs[0]);
+ } else if (Zext) {
+ MIRBuilder.buildZExt(Reg, Arg.Regs[0]);
+ } else {
+ MIRBuilder.buildAnyExt(Reg, Arg.Regs[0]);
+ }
+ ArgRegs.push_back(Reg);
+ } else {
+ ArgRegs.push_back(Arg.Regs[0]);
+ }
+ }
+
+ auto MutateI1 = false;
+ if (!Info.OrigRet.Ty->isVoidTy()) {
+ // select the call op according to return type and build MI
+ auto RetLLT =
+ llvm::getLLTForType(*Info.OrigRet.Ty, MIRBuilder.getDataLayout());
+ unsigned CallOp = 0;
+ if (RetLLT.isScalar() || RetLLT.isPointer()) {
+ switch (RetLLT.getSizeInBits()) {
+ case 1:
+ MutateI1 = true;
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_i16_r_i64_r
+ : PISA::functionCall_i16_r;
+ break;
+ case 8:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_i8_r_i64_r
+ : PISA::functionCall_i8_r;
+ break;
+ case 16:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_i16_r_i64_r
+ : PISA::functionCall_i16_r;
+ break;
+ case 32:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_i32_r_i64_r
+ : PISA::functionCall_i32_r;
+ break;
+ case 64:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_i64_r_i64_r
+ : PISA::functionCall_i64_r;
+ break;
+ default:
+ llvm_unreachable("Unsupported function return type");
+ break;
+ }
+ } else if (RetLLT.isVector()) {
+ auto TypeBitSize = RetLLT.getElementType().getSizeInBits();
+ auto NumElts = RetLLT.getNumElements();
+ switch (TypeBitSize) {
+ case 8:
+ switch (NumElts) {
+ case 2:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v2i8_r_i64_r
+ : PISA::functionCall_v2i8_r;
+ break;
+ case 3:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v3i8_r_i64_r
+ : PISA::functionCall_v3i8_r;
+ break;
+ case 4:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v4i8_r_i64_r
+ : PISA::functionCall_v4i8_r;
+ break;
+ default:
+ llvm_unreachable("Vector size not supported");
+ }
+ break;
+ case 16:
+ switch (NumElts) {
+ case 2:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v2i16_r_i64_r
+ : PISA::functionCall_v2i16_r;
+ break;
+ case 3:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v3i16_r_i64_r
+ : PISA::functionCall_v3i16_r;
+ break;
+ case 4:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v4i16_r_i64_r
+ : PISA::functionCall_v4i16_r;
+ break;
+ default:
+ llvm_unreachable("Vector size not supported");
+ }
+ break;
+ case 32:
+ switch (NumElts) {
+ case 2:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v2i32_r_i64_r
+ : PISA::functionCall_v2i32_r;
+ break;
+ case 3:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v3i32_r_i64_r
+ : PISA::functionCall_v3i32_r;
+ break;
+ case 4:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v4i32_r_i64_r
+ : PISA::functionCall_v4i32_r;
+ break;
+ case 5:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v5i32_r_i64_r
+ : PISA::functionCall_v5i32_r;
+ break;
+ case 6:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v6i32_r_i64_r
+ : PISA::functionCall_v6i32_r;
+ break;
+ case 7:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v7i32_r_i64_r
+ : PISA::functionCall_v7i32_r;
+ break;
+ case 8:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v8i32_r_i64_r
+ : PISA::functionCall_v8i32_r;
+ break;
+ case 16:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v16i32_r_i64_r
+ : PISA::functionCall_v16i32_r;
+ break;
+ case 32:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v32i32_r_i64_r
+ : PISA::functionCall_v32i32_r;
+ break;
+ case 64:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v64i32_r_i64_r
+ : PISA::functionCall_v64i32_r;
+ break;
+ default:
+ llvm_unreachable("Vector size not supported");
+ }
+ break;
+ case 64:
+ switch (NumElts) {
+ case 2:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v2i64_r_i64_r
+ : PISA::functionCall_v2i64_r;
+ break;
+ case 3:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v3i64_r_i64_r
+ : PISA::functionCall_v3i64_r;
+ break;
+ case 4:
+ CallOp = IsIndirectCall ? PISA::indirectFunctionCall_v4i64_r_i64_r
+ : PISA::functionCall_v4i64_r;
+ break;
+ default:
+ llvm_unreachable("Vector size not supported");
+ }
+ break;
+ default:
+ llvm_unreachable("Unsupported function return type");
+ break;
+ }
+ } else {
+ llvm_unreachable("Unsupported call return type");
+ }
+
+ // set Ret register class
+ assert(!Info.OrigRet.Regs.empty());
+ Register ResVReg = Info.OrigRet.Regs[0];
+ auto OrigRetLLT = RetLLT;
+ if (MutateI1) { // will return i16 (see lowerReturn())
+ RetLLT = LLT::integer(16);
+ ResVReg = MIRBuilder.getMRI()->createGenericVirtualRegister(RetLLT);
+ }
+
+ MIRBuilder.getMRI()->setRegClass(ResVReg, TRI->getRegClassFromLLT(RetLLT));
+ MIB = MIRBuilder.buildInstr(CallOp).addDef(ResVReg).add(Info.Callee);
+
+ if (MutateI1) { // change i16 back to i1
+ Register VReg = Info.OrigRet.Regs[0];
+ MIRBuilder.getMRI()->setRegClass(VReg,
+ TRI->getRegClassFromLLT(OrigRetLLT));
+ MIRBuilder.buildTrunc(VReg, ResVReg);
+ }
+ } else {
+ // void return
+ MIB = MIRBuilder
+ .buildInstr(IsIndirectCall ? PISA::indirectFunctionCall_void_r
+ : PISA::functionCall_void)
+ .add(Info.Callee);
+ }
+
+ // add function args into MI if any
+ for (auto Reg : ArgRegs) {
+ MIB.addUse(Reg);
+ }
+
+ return true;
+}
diff --git a/llvm/lib/Target/PISA/PISACallLowering.h b/llvm/lib/Target/PISA/PISACallLowering.h
new file mode 100644
index 00000000000000..b3cd194bfd242c
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISACallLowering.h
@@ -0,0 +1,44 @@
+//===-- PISACallLowering.h - 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_PISA_PISACALLLOWERING_H
+#define LLVM_LIB_TARGET_PISA_PISACALLLOWERING_H
+
+#include "llvm/CodeGen/GlobalISel/CallLowering.h"
+
+namespace llvm {
+
+class PISATargetLowering;
+
+class PISACallLowering : public CallLowering {
+
+public:
+ PISACallLowering(const PISATargetLowering &TLI);
+
+ bool lowerReturn(MachineIRBuilder &MIRBuilder, const Value *Val,
+ ArrayRef<Register> VRegs, FunctionLoweringInfo &FLI,
+ Register SwiftErrorVReg) const override;
+
+ bool lowerFormalArguments(MachineIRBuilder &MIRBuilder, const Function &F,
+ ArrayRef<ArrayRef<Register>> VRegs,
+ FunctionLoweringInfo &FLI) const override;
+
+ // Build OpCall, or replace with a builtin function.
+ bool lowerCall(MachineIRBuilder &MIRBuilder,
+ CallLoweringInfo &Info) const override;
+
+private:
+ unsigned getLoadParamOpcode(MachineIRBuilder &MIRBuilder, const Function &F,
+ const Register &VReg, Type *ArgType) const;
+ void loadParamWithOpcode(MachineIRBuilder &MIRBuilder, const Function &F,
+ const Register &VReg, Type *ArgType, unsigned Opcode,
+ unsigned ArgNo, unsigned Offset) const;
+};
+} // end namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_PISACALLLOWERING_H
diff --git a/llvm/lib/Target/PISA/PISAFrameLowering.h b/llvm/lib/Target/PISA/PISAFrameLowering.h
index c3a3b880312401..04767e0aaf690a 100644
--- a/llvm/lib/Target/PISA/PISAFrameLowering.h
+++ b/llvm/lib/Target/PISA/PISAFrameLowering.h
@@ -26,6 +26,16 @@ class PISAFrameLowering : public TargetFrameLowering {
MachineBasicBlock &MBB) const override {}
bool hasFPImpl(const MachineFunction &MF) const override { return false; }
+
+ bool isSupportedStackID(TargetStackID::Value ID) const override {
+ switch (ID) {
+ default:
+ return false;
+ case TargetStackID::Default:
+ case TargetStackID::PISAShared:
+ return true;
+ }
+ }
};
} // namespace llvm
#endif // LLVM_LIB_TARGET_PISA_PISAFRAMELOWERING_H
diff --git a/llvm/lib/Target/PISA/PISAISelLowering.cpp b/llvm/lib/Target/PISA/PISAISelLowering.cpp
new file mode 100644
index 00000000000000..377303af97321e
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAISelLowering.cpp
@@ -0,0 +1,712 @@
+//===-- PISAISelLowering.cpp - PISA DAG Lowering Impl ---------------------===//
+//
+// 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 "PISAISelLowering.h"
+#include "PISA.h"
+#include "PISACacheCtrlMMRA.h"
+#include "PISASubtarget.h"
+#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
+#include "llvm/IR/DerivedTypes.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicsPISA.h"
+#include "llvm/Support/AtomicOrdering.h"
+#include "llvm/Support/KnownBits.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/PISAAddrSpace.h"
+
+#include <type_traits>
+
+#define DEBUG_TYPE "pisa-lower"
+
+using namespace llvm;
+
+//===----------------------------------------------------------------------===//
+// Atomic legalization
+//
+// Atomics are legalized by LLVM's AtomicExpandPass, driven entirely through the
+// TargetLowering hooks below. These tables and helpers describe which native
+// atomic operations PISA supports and which operations need IR expansion.
+//===----------------------------------------------------------------------===//
+namespace {
+using namespace llvm::PISAAS;
+
+constexpr unsigned PrivateAS = static_cast<unsigned>(AddressSpace::PRIVATE);
+constexpr unsigned GenericAS = static_cast<unsigned>(AddressSpace::GENERIC);
+constexpr unsigned SharedAS = static_cast<unsigned>(AddressSpace::SHARED);
+constexpr unsigned GlobalAS = static_cast<unsigned>(AddressSpace::GLOBAL);
+
+// clang-format off
+const bool SupportedGlobal[AtomicRMWInst::LAST_BINOP + 1][4] = {
+ // 16b 32b 64b 128b
+ { true, true, true, true}, // Xchg
+ { true, true, true, false}, // Add
+ { true, true, true, false}, // Sub
+ { true, true, true, false}, // And
+ { true, true, true, false}, // Nand
+ { true, true, true, false}, // Or
+ { true, true, true, false}, // Xor
+ { true, true, true, false}, // Max
+ { true, true, true, false}, // Min
+ { true, true, true, false}, // Umax
+ { true, true, true, false}, // Umin
+ { true, true, true, false}, // FAdd
+ { true, true, true, false}, // FSub
+ { true, true, false, false}, // FMax
+ { true, true, false, false}, // FMin
+ {false, false, false, false}, // FMaximum
+ {false, false, false, false}, // FMinimum
+ {false, false, false, false}, // FMaximumNum
+ {false, false, false, false}, // FMinimumNum
+ {false, true, true, false}, // Uinc_wrap
+ {false, true, true, false}, // Udec_wrap
+ {false, false, false, false}, // Usub_cond
+ {false, false, false, false}, // Usub_sat
+};
+// clang-format on
+
+constexpr uint8_t SupportedGlobalCols =
+ std::extent<decltype(SupportedGlobal), 1>::value;
+constexpr uint8_t SupportedGlobalRows =
+ std::extent<decltype(SupportedGlobal), 0>::value;
+
+// clang-format off
+const bool SupportedShared[AtomicRMWInst::LAST_BINOP + 1][4] = {
+ // 16b 32b 64b 128b
+ { true, true, false, true}, // Xchg
+ { true, true, false, false}, // Add
+ { true, true, false, false}, // Sub
+ { true, true, false, false}, // And
+ { true, true, false, false}, // Nand
+ { true, true, false, false}, // Or
+ { true, true, false, false}, // Xor
+ { true, true, false, false}, // Max
+ { true, true, false, false}, // Min
+ { true, true, false, false}, // Umax
+ { true, true, false, false}, // Umin
+ { true, true, false, false}, // FAdd
+ { true, true, false, false}, // FSub
+ { true, true, false, false}, // FMax
+ { true, true, false, false}, // FMin
+ {false, false, false, false}, // FMaximum
+ {false, false, false, false}, // FMinimum
+ {false, false, false, false}, // FMaximumNum
+ {false, false, false, false}, // FMinimumNum
+ {false, true, false, false}, // Uinc_wrap
+ {false, true, false, false}, // Udec_wrap
+ {false, false, false, false}, // Usub_cond
+ {false, false, false, false}, // Usub_sat
+};
+// clang-format on
+
+constexpr uint8_t SupportedSharedCols =
+ std::extent<decltype(SupportedShared), 1>::value;
+constexpr uint8_t SupportedSharedRows =
+ std::extent<decltype(SupportedShared), 0>::value;
+
+const bool SupportedLoadStoreGlobal[4] = {
+ true, // 16b
+ true, // 32b
+ true, // 64b
+ true, // 128b
+};
+
+const bool SupportedLoadStoreShared[4] = {
+ true, // 16b
+ true, // 32b
+ false, // 64b
+ true, // 128b
+};
+
+bool isLegalLoadStore(unsigned BitWidth, unsigned AddrSpace) {
+ // Don't legalize loads/stores of unsupported bitwidths
+ if (BitWidth < 16 || BitWidth > 128)
+ return true;
+
+ unsigned TableCol = Log2_32(BitWidth / 16);
+ switch (AddrSpace) {
+ case GlobalAS:
+ return SupportedLoadStoreGlobal[TableCol];
+ case SharedAS:
+ return SupportedLoadStoreShared[TableCol];
+ case GenericAS:
+ return SupportedLoadStoreGlobal[TableCol] &&
+ SupportedLoadStoreShared[TableCol];
+ }
+
+ // Don't legalize loads/stores in other address spaces
+ return false;
+}
+
+bool isAtomicRMWLegal(const AtomicRMWInst *A) {
+ Type *Ty = A->getType();
+ const DataLayout &DL = A->getDataLayout();
+ unsigned BitWidth = DL.getTypeSizeInBits(Ty);
+ if (BitWidth < 16 || BitWidth > 128)
+ return false;
+ unsigned TableCol = Log2_32(BitWidth / 16);
+ unsigned Op = A->getOperation();
+
+ unsigned AS = A->getPointerAddressSpace();
+ if (((AS != SharedAS) &&
+ (TableCol >= SupportedGlobalCols || Op >= SupportedGlobalRows)) ||
+ ((AS != GlobalAS) &&
+ (TableCol >= SupportedSharedCols || Op >= SupportedSharedRows)))
+ return false;
+ switch (AS) {
+ default:
+ return false;
+ case GlobalAS:
+ return SupportedGlobal[Op][TableCol];
+ case SharedAS:
+ return SupportedShared[Op][TableCol];
+ case GenericAS:
+ return SupportedGlobal[Op][TableCol] && SupportedShared[Op][TableCol];
+ }
+}
+
+TargetLowering::AtomicExpansionKind
+computeRMWExpansion(const AtomicRMWInst *A) {
+ using Kind = TargetLowering::AtomicExpansionKind;
+ switch (A->getPointerAddressSpace()) {
+ case PrivateAS:
+ return Kind::NotAtomic; // load-op-store, single work-item
+ case GenericAS:
+ // Legal in BOTH global and shared -> native generic; else runtime dispatch.
+ return isAtomicRMWLegal(A) ? Kind::None : Kind::CustomExpand;
+ case GlobalAS:
+ case SharedAS:
+ return isAtomicRMWLegal(A) ? Kind::None : Kind::CmpXChg;
+ default:
+ return Kind::None;
+ }
+}
+
+SyncScope::ID hoistedFenceScope(Instruction *Inst) {
+ // An AtomicRMWInst (the CAS-loop op, incl. the xchg from an expanded store)
+ // is what reaches here today, but stay robust to other atomic instructions.
+ SyncScope::ID SSID = getAtomicSyncScopeID(Inst).value_or(SyncScope::System);
+ // Shared memory has no meaningful system scope, so narrow the scope used for
+ // hoisted fences to the widest valid shared-memory scope.
+ if (const auto *RMW = dyn_cast<AtomicRMWInst>(Inst))
+ if (RMW->getPointerAddressSpace() == SharedAS && SSID == SyncScope::System)
+ return Inst->getContext().getOrInsertSyncScopeID("gpu-shared");
+ return SSID;
+}
+} // namespace
+
+PISATargetLowering::PISATargetLowering(const TargetMachine &TM,
+ const PISASubtarget &STI)
+ : TargetLowering(TM, STI) {
+ // Route atomics through AtomicExpandPass. PISA supports up to
+ // 128-bit atomics; without this every atomic wider than the default falls
+ // back to an unsupported __atomic_* libcall.
+ setMaxAtomicSizeInBitsSupported(128);
+
+ // these numbers need to be large enough to cover cases that are not
+ // expanded by PISAExpandIntrinsics into a ld-st loop.
+ MaxStoresPerMemcpy = 64;
+ MaxStoresPerMemmove = 64;
+ MaxStoresPerMemset = 64;
+
+ // map int types to registers classes
+ addRegisterClass(MVT::i8, &PISA::Reg8bRegClass);
+ addRegisterClass(MVT::i16, &PISA::Reg16bRegClass);
+ addRegisterClass(MVT::i32, &PISA::Reg32bRegClass);
+ addRegisterClass(MVT::i64, &PISA::Reg64bRegClass);
+ addRegisterClass(MVT::bf16, &PISA::Reg16bRegClass);
+ addRegisterClass(MVT::f16, &PISA::Reg16bRegClass);
+ addRegisterClass(MVT::f32, &PISA::Reg32bRegClass);
+ addRegisterClass(MVT::f64, &PISA::Reg64bRegClass);
+ addRegisterClass(MVT::v2i8, &PISA::RegV2_8bRegClass);
+ addRegisterClass(MVT::v3i8, &PISA::RegV3_8bRegClass);
+ addRegisterClass(MVT::v4i8, &PISA::RegV4_8bRegClass);
+ addRegisterClass(MVT::v2i16, &PISA::RegV2_16bRegClass);
+ addRegisterClass(MVT::v3i16, &PISA::RegV3_16bRegClass);
+ addRegisterClass(MVT::v4i16, &PISA::RegV4_16bRegClass);
+ addRegisterClass(MVT::v2i32, &PISA::RegV2_32bRegClass);
+ addRegisterClass(MVT::v3i32, &PISA::RegV3_32bRegClass);
+ addRegisterClass(MVT::v4i32, &PISA::RegV4_32bRegClass);
+ addRegisterClass(MVT::v5i32, &PISA::RegV5_32bRegClass);
+ addRegisterClass(MVT::v6i32, &PISA::RegV6_32bRegClass);
+ addRegisterClass(MVT::v7i32, &PISA::RegV7_32bRegClass);
+ addRegisterClass(MVT::v8i32, &PISA::RegV8_32bRegClass);
+ addRegisterClass(MVT::v16i32, &PISA::RegV16_32bRegClass);
+ addRegisterClass(MVT::v32i32, &PISA::RegV32_32bRegClass);
+ addRegisterClass(MVT::v64i32, &PISA::RegV64_32bRegClass);
+ addRegisterClass(MVT::v2i64, &PISA::RegV2_64bRegClass);
+ addRegisterClass(MVT::v3i64, &PISA::RegV3_64bRegClass);
+ addRegisterClass(MVT::v4i64, &PISA::RegV4_64bRegClass);
+ // map floating-point types to registers classes
+ addRegisterClass(MVT::v2f16, &PISA::RegV2_16bRegClass);
+ addRegisterClass(MVT::v3f16, &PISA::RegV3_16bRegClass);
+ addRegisterClass(MVT::v4f16, &PISA::RegV4_16bRegClass);
+ addRegisterClass(MVT::v2bf16, &PISA::RegV2_16bRegClass);
+ addRegisterClass(MVT::v3bf16, &PISA::RegV3_16bRegClass);
+ addRegisterClass(MVT::v4bf16, &PISA::RegV4_16bRegClass);
+ addRegisterClass(MVT::v2f32, &PISA::RegV2_32bRegClass);
+ addRegisterClass(MVT::v3f32, &PISA::RegV3_32bRegClass);
+ addRegisterClass(MVT::v4f32, &PISA::RegV4_32bRegClass);
+ addRegisterClass(MVT::v5f32, &PISA::RegV5_32bRegClass);
+ addRegisterClass(MVT::v6f32, &PISA::RegV6_32bRegClass);
+ addRegisterClass(MVT::v7f32, &PISA::RegV7_32bRegClass);
+ addRegisterClass(MVT::v8f32, &PISA::RegV8_32bRegClass);
+ addRegisterClass(MVT::v2f64, &PISA::RegV2_64bRegClass);
+ addRegisterClass(MVT::v3f64, &PISA::RegV3_64bRegClass);
+ addRegisterClass(MVT::v4f64, &PISA::RegV4_64bRegClass);
+ // must be done after all classes are added
+ computeRegisterProperties(STI.getRegisterInfo());
+
+ // Jump is Expensive. Don't create extra control flow for 'and', 'or'
+ // condition branches.
+ setJumpIsExpensive(true);
+ setMaxDivRemBitWidthSupported(64);
+}
+
+unsigned PISATargetLowering::getNumRegistersForCallingConv(LLVMContext &Context,
+ CallingConv::ID CC,
+ EVT VT) const {
+ // This code avoids CallLowering fail inside getVectorTypeBreakdown
+ // on v3i1 arguments. Maybe we need to return 1 for all types.
+ // TODO: remove it once this case is supported by the default implementation.
+ if (VT.isVector() && VT.getVectorNumElements() == 3 &&
+ (VT.getVectorElementType() == MVT::i1 ||
+ VT.getVectorElementType() == MVT::i8))
+ return 1;
+ return getNumRegisters(Context, VT);
+}
+
+bool PISATargetLowering::isCheapToSpeculateCttz(Type *Ty) const { return true; }
+
+bool PISATargetLowering::isCheapToSpeculateCtlz(Type *Ty) const { return true; }
+
+bool PISATargetLowering::isReassocProfitable(MachineRegisterInfo &MRI,
+ Register N0, Register N1) const {
+ auto GetOneDefInst = [&MRI](Register N) -> MachineInstr * {
+ auto *Def = MRI.getOneDef(N);
+ if (Def)
+ return Def->getParent();
+ return nullptr;
+ };
+
+ auto *I0 = GetOneDefInst(N0);
+ auto *I1 = GetOneDefInst(N1);
+ if (I0 && I1) {
+ // Prevent reassociating the following pattern
+ // (add (mul a, b), (add (mul c, d), e))
+ // into
+ // (add (add (mul a, b), (mul c, d)), e)
+ // so that more 'mad's could be selected.
+ if (I0->getOpcode() != TargetOpcode::G_MUL)
+ std::swap(I0, I1);
+ if (I0->getOpcode() == TargetOpcode::G_MUL &&
+ I1->getOpcode() == TargetOpcode::G_ADD) {
+ auto *NI0 = GetOneDefInst(I1->getOperand(1).getReg());
+ auto *NI1 = GetOneDefInst(I1->getOperand(2).getReg());
+ if (NI0 && NI1 &&
+ (NI0->getOpcode() == TargetOpcode::G_MUL ||
+ NI1->getOpcode() == TargetOpcode::G_MUL)) {
+ // Don't reassociate to break the selection of MAD.
+ return false;
+ }
+ }
+ }
+
+ return TargetLowering::isReassocProfitable(MRI, N0, N1);
+}
+
+MVT PISATargetLowering::getRegisterTypeForCallingConv(LLVMContext &Context,
+ CallingConv::ID CC,
+ EVT VT) const {
+ // This code avoids CallLowering fail inside getVectorTypeBreakdown
+ // on v3i1 arguments. Maybe we need to return i32 for all types.
+ // TODO: remove it once this case is supported by the default implementation.
+ if (VT.isVector() && VT.getVectorNumElements() == 3) {
+ if (VT.getVectorElementType() == MVT::i1)
+ return MVT::v4i1;
+ if (VT.getVectorElementType() == MVT::i8)
+ return MVT::v4i8;
+ }
+ return getRegisterType(Context, VT);
+}
+
+void PISATargetLowering::getTgtMemIntrinsic(
+ SmallVectorImpl<IntrinsicInfo> &Infos, const CallBase &I,
+ MachineFunction &MF, unsigned Intrinsic) const {
+ IntrinsicInfo Info;
+ Info.flags = MachineMemOperand::MONone;
+ switch (Intrinsic) {
+ case Intrinsic::pisa_cas_fatom:
+ Info.memVT = MVT::getVT(I.getType());
+ Info.ptrVal = I.getArgOperand(0);
+ Info.align.reset();
+ Info.flags |= MachineMemOperand::MOLoad | MachineMemOperand::MOStore;
+ Info.flags |= getTargetMMOFlags(I);
+ // syncscope("<target-scope>") support for atomics
+ if (auto *ConstInt = dyn_cast<ConstantInt>(I.getArgOperand(3)))
+ Info.order = static_cast<llvm::AtomicOrdering>(ConstInt->getZExtValue());
+ Infos.push_back(Info);
+ return;
+ default:
+ break;
+ }
+ return;
+}
+
+LLT PISATargetLowering::getOptimalMemOpLLT(
+ const MemOp &Op, const AttributeList &FuncAttributes) const {
+ auto I8 = LLT::integer(8);
+ auto I16 = LLT::integer(16);
+ auto I32 = LLT::integer(32);
+ auto I64 = LLT::integer(64);
+
+ if (Op.size() >= 16 && Op.isAligned(Align(8)))
+ return LLT::fixed_vector(2, I64);
+ if (Op.size() >= 16 && Op.isAligned(Align(4)))
+ return LLT::fixed_vector(4, I32);
+ if (Op.size() >= 12 && Op.isAligned(Align(4)))
+ return LLT::fixed_vector(3, I32);
+ if (Op.size() >= 8 && Op.isAligned(Align(4)))
+ return LLT::fixed_vector(2, I32);
+ if (Op.size() >= 8 && Op.isAligned(Align(2)))
+ return LLT::fixed_vector(4, I16);
+ if (Op.size() >= 4 && Op.isAligned(Align(4)))
+ return I32;
+ if (Op.size() >= 4 && Op.isAligned(Align(2)))
+ return LLT::fixed_vector(2, I16);
+ if (Op.size() >= 4 && Op.isAligned(Align(1)))
+ return LLT::fixed_vector(4, I8);
+ if (Op.size() >= 2 && Op.isAligned(Align(2)))
+ return I16;
+ if (Op.size() >= 2 && Op.isAligned(Align(1)))
+ return LLT::fixed_vector(2, I8);
+ if (Op.size() >= 1 && Op.isAligned(Align(1)))
+ return I8;
+ return LLT();
+}
+
+bool PISATargetLowering::useFTZ(const MachineFunction &MF) const {
+ return MF.getDenormalMode(APFloat::IEEEsingle()).Output ==
+ DenormalMode::PreserveSign;
+}
+
+TargetLowering::ConstraintType
+PISATargetLowering::getConstraintType(StringRef Constraint) const {
+ if (Constraint.size() == 1) {
+ switch (Constraint[0]) {
+ default:
+ break;
+ case 'P': // Predicate register.
+ return C_RegisterClass;
+ }
+ }
+
+ return TargetLowering::getConstraintType(Constraint);
+}
+
+std::pair<unsigned, const TargetRegisterClass *>
+PISATargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+ StringRef Constraint,
+ MVT VT) const {
+ using namespace PISA;
+ static const TargetRegisterClass *Vector8BitClass[] = {
+ &Reg8bRegClass, &RegV2_8bRegClass, &RegV3_8bRegClass, &RegV4_8bRegClass};
+ static const TargetRegisterClass *Vector16BitClass[] = {
+ &Reg16bRegClass, &RegV2_16bRegClass, &RegV3_16bRegClass,
+ &RegV4_16bRegClass};
+ static const TargetRegisterClass *Vector32BitClass[] = {
+ &Reg32bRegClass, &RegV2_32bRegClass, &RegV3_32bRegClass,
+ &RegV4_32bRegClass, &RegV5_32bRegClass, &RegV6_32bRegClass,
+ &RegV7_32bRegClass, &RegV8_32bRegClass,
+ };
+ static const TargetRegisterClass *Vector64BitClass[] = {
+ &Reg64bRegClass, &RegV2_64bRegClass, &RegV3_64bRegClass,
+ &RegV4_64bRegClass};
+
+ if (Constraint.size() == 1) {
+ const TargetRegisterClass *RC = nullptr;
+ switch (Constraint[0]) {
+ default:
+ break;
+ case 'P':
+ RC = &PredRegClass;
+ break;
+ case 'r': {
+ // FIXME: we already have a method to get the register class from LLT
+ auto VectorSize = VT.isVector() ? VT.getVectorNumElements() : 1;
+ auto ElementSize = VT.getScalarSizeInBits();
+ assert(VectorSize >= 1 &&
+ (ElementSize == 32 ? VectorSize <= 8 || VectorSize == 16 ||
+ VectorSize == 32 || VectorSize == 64
+ : VectorSize <= 4));
+
+ switch (ElementSize) {
+ case 8:
+ RC = Vector8BitClass[VectorSize - 1];
+ break;
+ case 16:
+ RC = Vector16BitClass[VectorSize - 1];
+ break;
+ case 32:
+ if (VectorSize == 64)
+ RC = &RegV64_32bRegClass;
+ else if (VectorSize == 32)
+ RC = &RegV32_32bRegClass;
+ else if (VectorSize == 16)
+ RC = &RegV16_32bRegClass;
+ else
+ RC = Vector32BitClass[VectorSize - 1];
+ break;
+ case 64:
+ RC = Vector64BitClass[VectorSize - 1];
+ break;
+ }
+ } break;
+ }
+
+ if (RC)
+ return std::make_pair(0u, RC);
+ }
+
+ return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
+}
+
+MachineMemOperand::Flags
+PISATargetLowering::getTargetMMOFlags(const Instruction &I) const {
+ MachineMemOperand::Flags Flags = MachineMemOperand::MONone;
+ if (auto Hint = PISA::getCacheCtrlFromMMRA(I)) {
+ auto HintValue = *Hint & 0xF;
+ Flags |= static_cast<MachineMemOperand::Flags>(HintValue << 6);
+ }
+ return Flags;
+}
+
+void PISATargetLowering::computeKnownBitsForTargetInstr(
+ GISelValueTracking &Analysis, Register R, KnownBits &Known,
+ const APInt &DemandedElts, const MachineRegisterInfo &MRI,
+ unsigned Depth) const {
+ MachineInstr *MI = MRI.getVRegDef(R);
+
+ // As we go we can add more cases here for now only enable for
+ // G_INTRINSIC for using for folding range attributes
+ if (!MI || MI->getOpcode() != TargetOpcode::G_INTRINSIC)
+ return;
+
+ Intrinsic::ID IID = cast<GIntrinsic>(*MI).getIntrinsicID();
+
+ if (Intrinsic::isOverloaded(IID))
+ return;
+
+ auto *Ctx = &MI->getMF()->getFunction().getContext();
+ FunctionType *FT = Intrinsic::getType(*Ctx, IID);
+ AttributeList Attrs = Intrinsic::getAttributes(*Ctx, IID, FT);
+
+ if (Attrs.hasRetAttr(Attribute::Range)) {
+ const ConstantRange &CR =
+ Attrs.getRetAttr(Attribute::Range).getValueAsConstantRange();
+ Known = CR.toKnownBits();
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Atomic legalization hooks
+//===----------------------------------------------------------------------===//
+
+TargetLowering::AtomicExpansionKind
+PISATargetLowering::shouldExpandAtomicRMWInIR(const AtomicRMWInst *RMW) const {
+ return computeRMWExpansion(RMW);
+}
+
+TargetLowering::AtomicExpansionKind
+PISATargetLowering::shouldExpandAtomicCmpXchgInIR(
+ const AtomicCmpXchgInst *CI) const {
+ // Private memory is single work-item, so cmpxchg can become a plain
+ // load/compare/conditional-store. Other address spaces keep cmpxchg semantics
+ // and are left for native backend selection.
+ return CI->getPointerAddressSpace() == PrivateAS
+ ? AtomicExpansionKind::NotAtomic
+ : AtomicExpansionKind::None;
+}
+
+TargetLowering::AtomicExpansionKind
+PISATargetLowering::shouldExpandAtomicLoadInIR(LoadInst *LI) const {
+ unsigned AS = LI->getPointerAddressSpace();
+ if (AS == PrivateAS)
+ return AtomicExpansionKind::NotAtomic; // plain load
+ unsigned BW = LI->getDataLayout().getTypeSizeInBits(LI->getType());
+ if (BW < 16 || BW > 128)
+ return AtomicExpansionKind::None; // inverted vs rmw: leave native
+ if (AS == GlobalAS || AS == SharedAS || AS == GenericAS) {
+ if (isLegalLoadStore(BW, AS))
+ return AtomicExpansionKind::None;
+ // Illegal-width atomic load: emulate with an integer cmpxchg in
+ // emitExpandAtomicLoad. AtomicExpand's generic CmpXChg path
+ // (expandAtomicLoadToCmpXchg) is unsuitable for PISA:
+ // - Pointers: it casts only FP/vector to integer, so a pointer load
+ // stays a pointer cmpxchg, which asserts (isScalar) in IRTranslator.
+ // emitExpandAtomicLoad instead emits a same-width integer cmpxchg that
+ // preserves the syncscope, then casts the loaded bits back.
+ return AtomicExpansionKind::CustomExpand;
+ }
+ return AtomicExpansionKind::None;
+}
+
+TargetLowering::AtomicExpansionKind
+PISATargetLowering::shouldExpandAtomicStoreInIR(StoreInst *SI) const {
+ unsigned AS = SI->getPointerAddressSpace();
+ if (AS == PrivateAS)
+ return AtomicExpansionKind::NotAtomic; // plain store
+ Type *ValTy = SI->getValueOperand()->getType();
+ unsigned BW = SI->getDataLayout().getTypeSizeInBits(ValTy);
+ if (BW < 16 || BW > 128)
+ return AtomicExpansionKind::None; // leave native
+ if (AS == GlobalAS || AS == SharedAS || AS == GenericAS) {
+ if (isLegalLoadStore(BW, AS))
+ return AtomicExpansionKind::None;
+ // Illegal-width atomic store: emulate with an integer xchg in
+ // emitExpandAtomicStore. AtomicExpand's generic Expand path
+ // (store -> xchg, expandAtomicStore) is unsuitable for PISA:
+ // - Fences: it re-expands via a direct tryExpandAtomicRMW that skips
+ // the driver's fence-hoisting path, so no leading fence is hoisted.
+ // - Pointers: it feeds a pointer value into the CAS loop, where
+ // createCmpXchgInstFun asserts on the pointer operand.
+ // emitExpandAtomicStore casts the value to a same-width integer and
+ // splits the block so the driver re-walks the xchg and applies fence
+ // hoisting + gpu-shared narrowing, with the scope preserved.
+ return AtomicExpansionKind::CustomExpand;
+ }
+ return AtomicExpansionKind::None;
+}
+
+bool PISATargetLowering::shouldInsertFencesForAtomic(
+ const Instruction *I) const {
+ // Use AtomicExpand fence splitting only for RMWs that become CAS loops.
+ // Native atomics keep their ordering.
+ if (const auto *RMW = dyn_cast<AtomicRMWInst>(I))
+ return computeRMWExpansion(RMW) == AtomicExpansionKind::CmpXChg;
+ return false;
+}
+
+Instruction *PISATargetLowering::emitLeadingFence(IRBuilderBase &Builder,
+ Instruction *Inst,
+ AtomicOrdering Ord) const {
+ if (!isReleaseOrStronger(Ord))
+ return nullptr;
+ AtomicOrdering FenceOrd = (Ord == AtomicOrdering::SequentiallyConsistent)
+ ? Ord
+ : AtomicOrdering::Release;
+ return Builder.CreateFence(FenceOrd, hoistedFenceScope(Inst));
+}
+
+Instruction *PISATargetLowering::emitTrailingFence(IRBuilderBase &Builder,
+ Instruction *Inst,
+ AtomicOrdering Ord) const {
+ if (!isAcquireOrStronger(Ord))
+ return nullptr;
+ AtomicOrdering FenceOrd = (Ord == AtomicOrdering::SequentiallyConsistent)
+ ? Ord
+ : AtomicOrdering::Acquire;
+ return Builder.CreateFence(FenceOrd, hoistedFenceScope(Inst));
+}
+
+void PISATargetLowering::emitExpandAtomicRMW(AtomicRMWInst *A) const {
+ // Generic pointer in atomicrmw points to GLOBAL or SHARED (PRIVATE is UB per
+ // OpenCL). Probe the real address space at runtime with pisa_isaddr_shared
+ // and branch; AtomicExpandPass then re-walks and expands each arm
+ // independently (global -> native, shared -> CAS loop + hoisted fences).
+ LLVMContext &Ctx = A->getContext();
+ Function *F = A->getFunction();
+ IRBuilder<> IR(A);
+
+ BasicBlock *OrigBB = A->getParent();
+ BasicBlock *JoinBB = OrigBB->splitBasicBlock(A->getNextNode());
+
+ Type *I32 = IR.getInt32Ty();
+ Value *IsShared = IR.CreateIntrinsic(I32, Intrinsic::pisa_isaddr_shared,
+ {A->getPointerOperand()});
+ Value *SharedCmp = IR.CreateICmpNE(IsShared, ConstantInt::get(I32, 0));
+
+ BasicBlock *GlobalBB = BasicBlock::Create(Ctx, "", F, JoinBB);
+ BasicBlock *SharedBB = BasicBlock::Create(Ctx, "", F, JoinBB);
+
+ IR.CreateCondBr(SharedCmp, SharedBB, GlobalBB);
+ OrigBB->getTerminator()->eraseFromParent();
+
+ IR.SetInsertPoint(JoinBB->begin());
+ PHINode *Res = IR.CreatePHI(A->getType(), 2);
+
+ auto EmitArm = [&](BasicBlock *BB, unsigned AS) {
+ IR.SetInsertPoint(BB);
+ Value *Cast = IR.CreateAddrSpaceCast(A->getPointerOperand(),
+ PointerType::get(Ctx, AS));
+ AtomicRMWInst *NewA = IR.CreateAtomicRMW(
+ A->getOperation(), Cast, A->getValOperand(), A->getAlign(),
+ A->getOrdering(), A->getSyncScopeID());
+ Res->addIncoming(NewA, IR.GetInsertBlock());
+ IR.CreateBr(JoinBB);
+ };
+ EmitArm(GlobalBB, GlobalAS);
+ EmitArm(SharedBB, SharedAS);
+
+ A->replaceAllUsesWith(Res);
+ A->eraseFromParent();
+}
+
+void PISATargetLowering::emitExpandAtomicStore(StoreInst *SI) const {
+ // Reached for every illegal-width atomic store (integer, FP, or pointer).
+ // PISA's xchg/CAS expansion operates on integers, so cast the value to an
+ // integer of the same width and rewrite the store as an integer atomicrmw
+ // xchg while preserving the sync scope. Place it in a fresh block so the
+ // AtomicExpand driver loop revisits and CAS-expands it with hoisted,
+ // scope-narrowed fences (it does not re-walk ops created in the block
+ // currently being processed).
+ BasicBlock *BB = SI->getParent();
+ BB->splitBasicBlock(SI->getIterator());
+
+ IRBuilder<> IR(SI);
+ Value *Val = SI->getValueOperand();
+ Type *IntTy =
+ IR.getIntNTy(SI->getDataLayout().getTypeSizeInBits(Val->getType()));
+ Value *IntVal =
+ Val->getType() == IntTy ? Val : IR.CreateBitOrPointerCast(Val, IntTy);
+ AtomicOrdering Ord = SI->getOrdering() == AtomicOrdering::Unordered
+ ? AtomicOrdering::Monotonic
+ : SI->getOrdering();
+ IR.CreateAtomicRMW(AtomicRMWInst::Xchg, SI->getPointerOperand(), IntVal,
+ SI->getAlign(), Ord, SI->getSyncScopeID());
+ SI->eraseFromParent();
+}
+
+void PISATargetLowering::emitExpandAtomicLoad(LoadInst *LI) const {
+ // Emulate an illegal-width atomic load with an integer cmpxchg (PISA's CAS
+ // needs scalar operands) using zero for both compare and new value, then cast
+ // the loaded bits back to the original type. The integer representation keeps
+ // FP/pointer loads compatible with cmpxchg while preserving the sync scope.
+ //
+ // Note the deliberate load/store asymmetry: the load's emulating cmpxchg
+ // keeps the original ordering and scope (it is not an AtomicRMWInst, so the
+ // fence gate does not hoist/narrow it), whereas an illegal store becomes a
+ // relaxed CAS loop bracketed by hoisted fences in emitExpandAtomicStore.
+ IRBuilder<> IR(LI);
+ Type *Ty = LI->getType();
+ unsigned BW = LI->getDataLayout().getTypeSizeInBits(Ty);
+ Type *IntTy = IR.getIntNTy(BW);
+ AtomicOrdering Ord = LI->getOrdering() == AtomicOrdering::Unordered
+ ? AtomicOrdering::Monotonic
+ : LI->getOrdering();
+ Constant *Zero = ConstantInt::get(IntTy, 0);
+ Value *Pair = IR.CreateAtomicCmpXchg(
+ LI->getPointerOperand(), Zero, Zero, LI->getAlign(), Ord,
+ AtomicCmpXchgInst::getStrongestFailureOrdering(Ord),
+ LI->getSyncScopeID());
+ Value *Loaded = IR.CreateExtractValue(Pair, 0);
+ if (Loaded->getType() != Ty)
+ Loaded = IR.CreateBitOrPointerCast(Loaded, Ty);
+ LI->replaceAllUsesWith(Loaded);
+ LI->eraseFromParent();
+}
diff --git a/llvm/lib/Target/PISA/PISAISelLowering.h b/llvm/lib/Target/PISA/PISAISelLowering.h
new file mode 100644
index 00000000000000..08096962b55cb1
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAISelLowering.h
@@ -0,0 +1,112 @@
+//===-- PISAISelLowering.h - PISA DAG Lowering Interface ------------------===//
+//
+// 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_PISA_PISAISELLOWERING_H
+#define LLVM_LIB_TARGET_PISA_PISAISELLOWERING_H
+
+#include "llvm/CodeGen/TargetLowering.h"
+
+namespace llvm {
+class PISASubtarget;
+
+class PISATargetLowering : public TargetLowering {
+public:
+ explicit PISATargetLowering(const TargetMachine &TM,
+ const PISASubtarget &STI);
+
+ bool isCheapToSpeculateCttz(Type *Ty) const override;
+
+ bool isCheapToSpeculateCtlz(Type *Ty) const override;
+
+ bool isReassocProfitable(MachineRegisterInfo &MRI, Register N0,
+ Register N1) const override;
+
+ // Stop IRTranslator breaking up FMA instrs to preserve types information.
+ bool isFMAFasterThanFMulAndFAdd(const MachineFunction &MF,
+ EVT) const override {
+ return true;
+ }
+ bool isFMAFasterThanFMulAndFAdd(const MachineFunction &MF,
+ LLT) const override {
+ return true;
+ }
+ bool isFMAFasterThanFMulAndFAdd(const Function &F, Type *) const override {
+ return true;
+ }
+
+ // This is to prevent sexts of non-i64 vector indices which are generated
+ // within general IRTranslator hence type generation for it is omitted.
+ unsigned getVectorIdxWidth(const DataLayout &DL) const override { return 32; }
+
+ unsigned getNumRegistersForCallingConv(LLVMContext &Context,
+ CallingConv::ID CC,
+ EVT VT) const override;
+ MVT getRegisterTypeForCallingConv(LLVMContext &Context, CallingConv::ID CC,
+ EVT VT) const override;
+ void getTgtMemIntrinsic(SmallVectorImpl<IntrinsicInfo> &Infos,
+ const CallBase &I, MachineFunction &MF,
+ unsigned Intrinsic) const override;
+ LLT getOptimalMemOpLLT(const MemOp &Op,
+ const AttributeList &FuncAttributes) const override;
+
+ bool useFTZ(const MachineFunction &MF) const;
+
+ bool areJTsAllowed(const Function *Fn) const override { return false; }
+ bool isShuffleMaskLegal(ArrayRef<int> /*Mask*/, EVT /*VT*/) const override {
+ return false;
+ }
+ ConstraintType getConstraintType(StringRef Constraint) const override;
+
+ std::pair<unsigned, const TargetRegisterClass *>
+ getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
+ StringRef Constraint, MVT VT) const override;
+
+ MachineMemOperand::Flags
+ getTargetMMOFlags(const Instruction &I) const override;
+
+ bool isFMADLegal(const SelectionDAG &DAG, const SDNode *N) const override {
+ return false;
+ }
+ bool isFMADLegal(const MachineInstr &MI, const LLT Ty) const override {
+ return false;
+ }
+
+ void computeKnownBitsForTargetInstr(GISelValueTracking &Analysis, Register R,
+ KnownBits &Known,
+ const APInt &DemandedElts,
+ const MachineRegisterInfo &MRI,
+ unsigned Depth = 0) const override;
+
+ // --- Atomic legalization: driven by AtomicExpandPass ---
+ AtomicExpansionKind
+ shouldExpandAtomicRMWInIR(const AtomicRMWInst *RMW) const override;
+ AtomicExpansionKind
+ shouldExpandAtomicCmpXchgInIR(const AtomicCmpXchgInst *CI) const override;
+ AtomicExpansionKind shouldExpandAtomicLoadInIR(LoadInst *LI) const override;
+ AtomicExpansionKind shouldExpandAtomicStoreInIR(StoreInst *SI) const override;
+
+ bool shouldInsertFencesForAtomic(const Instruction *I) const override;
+
+ // Seed the CAS emulation loop with a plain load. Private/shared/global paths
+ // add the synchronization they need outside the loop, so the initial load
+ // does not need to be a redundant relaxed atomic load.
+ bool shouldIssueAtomicLoadForAtomicEmulationLoop() const override {
+ return false;
+ }
+
+ Instruction *emitLeadingFence(IRBuilderBase &Builder, Instruction *Inst,
+ AtomicOrdering Ord) const override;
+ Instruction *emitTrailingFence(IRBuilderBase &Builder, Instruction *Inst,
+ AtomicOrdering Ord) const override;
+ void emitExpandAtomicRMW(AtomicRMWInst *AI) const override;
+ void emitExpandAtomicStore(StoreInst *SI) const override;
+ void emitExpandAtomicLoad(LoadInst *LI) const override;
+};
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_PISAISELLOWERING_H
diff --git a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
new file mode 100644
index 00000000000000..0597d75b67edff
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
@@ -0,0 +1,3513 @@
+//===-- PISALegalizerInfo.cpp --- PISA Legalization Rules -----------------===//
+//
+// 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 "PISALegalizerInfo.h"
+#include "PISA.h"
+#include "PISASubtarget.h"
+#include "PISATargetMachine.h"
+#include "llvm/ADT/bit.h"
+#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerHelper.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/MachineInstr.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetOpcodes.h"
+#include "llvm/IR/IntrinsicsPISA.h"
+#include "llvm/IR/PISAIntrinsicUtils.h"
+#include "llvm/Support/PISAAddrSpace.h"
+
+using namespace llvm;
+using namespace llvm::LegalizeActions;
+using namespace llvm::LegalizeMutations;
+using namespace llvm::LegalityPredicates;
+
+namespace {
+constexpr ElementCount EC0 = ElementCount::getFixed(0);
+constexpr ElementCount EC2 = ElementCount::getFixed(2);
+constexpr ElementCount EC4 = ElementCount::getFixed(4);
+
+// integer types
+constexpr LLT I1 = LLT(LLT::Kind::INTEGER, EC0, 1);
+constexpr LLT I8 = LLT(LLT::Kind::INTEGER, EC0, 8);
+constexpr LLT I16 = LLT(LLT::Kind::INTEGER, EC0, 16);
+constexpr LLT I32 = LLT(LLT::Kind::INTEGER, EC0, 32);
+constexpr LLT I64 = LLT(LLT::Kind::INTEGER, EC0, 64);
+constexpr LLT I128 = LLT(LLT::Kind::INTEGER, EC0, 128);
+
+constexpr LLT V2I8 = LLT::fixed_vector(2, I8);
+constexpr LLT V2I16 = LLT::fixed_vector(2, I16);
+constexpr LLT V2I32 = LLT::fixed_vector(2, I32);
+
+constexpr LLT V4I8 = LLT::fixed_vector(4, I8);
+
+// floating-point types
+constexpr LLT BF16 = LLT::bfloat16();
+constexpr LLT F16 = LLT::float16();
+constexpr LLT F32 = LLT::float32();
+constexpr LLT F64 = LLT::float64();
+
+// return true if natively supported type
+static bool isLegalType(LLT Ty, bool Vector = true) {
+ auto EltSize = Ty.getScalarSizeInBits();
+ if (Ty.isVector() && !Vector)
+ return false;
+ if (Ty.isVector()) {
+ auto NumElts = Ty.getNumElements();
+ if (EltSize == 32)
+ return NumElts <= 8 || NumElts == 16 || NumElts == 32 || NumElts == 64;
+ if (!llvm::isPowerOf2_32(EltSize) || EltSize < 8 || EltSize > 64)
+ return false;
+ return NumElts <= 4;
+ }
+ if (!llvm::isPowerOf2_32(EltSize) || EltSize < 8 || EltSize > 128)
+ return false;
+ return true;
+}
+
+LegalityPredicate is3x8BitVector(unsigned TypeIdx) {
+ return [=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[TypeIdx];
+ return Ty.isVector() && Ty.getScalarSizeInBits() == 8 &&
+ Ty.getNumElements() == 3;
+ };
+}
+
+LegalityPredicate isWiderThan2x16BitVector(unsigned TypeIdx) {
+ return [=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[TypeIdx];
+ return Ty.isVector() && Ty.getScalarSizeInBits() == 16 &&
+ Ty.getNumElements() > 2;
+ };
+}
+
+LegalityPredicate isFloatingPointType(unsigned TypeIdx) {
+ return [=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[TypeIdx];
+ return Ty.getScalarType().isFloat();
+ };
+}
+LegalizeMutation changeElementTypeToInteger(unsigned TypeIdx) {
+ return [=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[TypeIdx];
+ auto NewEltTy = LLT::integer(Ty.getScalarSizeInBits());
+ auto NewTy = Ty.isVector()
+ ? LLT::fixed_vector(Ty.getNumElements(), NewEltTy)
+ : NewEltTy;
+ return std::pair(TypeIdx, NewTy);
+ };
+}
+} // namespace
+
+/// Returns true if the given G_LOAD instruction operates on a vector of 5-7
+/// elements each of 32 bits and should be widened for better hardware
+/// utilization. Potentially can be used for other memory types.
+static bool shouldWidenLoad(unsigned int Opcode, const LLT Ty,
+ unsigned AddressSpace, uint64_t Alignbits) {
+ if (AddressSpace == static_cast<unsigned>(PISAAS::AddressSpace::SHARED)) {
+ return (Opcode == TargetOpcode::G_LOAD && Ty.isVector() &&
+ Ty.getScalarSizeInBits() == 32 && Ty.getNumElements() >= 5 &&
+ Ty.getNumElements() <= 7);
+ }
+ if (AddressSpace == static_cast<unsigned>(PISAAS::AddressSpace::GLOBAL) ||
+ AddressSpace == static_cast<unsigned>(PISAAS::AddressSpace::CONSTANT)) {
+ return (Opcode == TargetOpcode::G_LOAD && Ty.isVector() &&
+ Ty.getScalarSizeInBits() == 32 && Ty.getNumElements() >= 5 &&
+ Ty.getNumElements() <= 7 && Alignbits >= 64);
+ }
+ return false;
+}
+
+PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
+ using namespace TargetOpcode;
+
+ auto &TM = ST.getTargetLowering()->getTargetMachine();
+ auto GetPointerLlt = [&](PISAAS::AddressSpace Addrspace) {
+ uint32_t NumBits =
+ TM.getPointerSizeInBits(static_cast<unsigned>(Addrspace));
+ return LLT::pointer(static_cast<unsigned>(Addrspace), NumBits);
+ };
+
+ const LLT PrivatePtr = GetPointerLlt(PISAAS::AddressSpace::PRIVATE);
+ const LLT GlobalPtr = GetPointerLlt(PISAAS::AddressSpace::GLOBAL);
+ const LLT ConstantPtr = GetPointerLlt(PISAAS::AddressSpace::CONSTANT);
+ const LLT SharedPtr = GetPointerLlt(PISAAS::AddressSpace::SHARED);
+ const LLT GenericPtr = GetPointerLlt(PISAAS::AddressSpace::GENERIC);
+
+ const std::initializer_list<LLT> AddrSpaces64 = {GlobalPtr, ConstantPtr,
+ GenericPtr};
+ const std::initializer_list<LLT> AddrSpaces32 = {PrivatePtr, SharedPtr};
+
+ auto AllIntegers = {I8, I16, I32, I64};
+ auto AllFloats = {BF16, F16, F32, F64};
+ auto AllPtrs = {PrivatePtr, GlobalPtr, ConstantPtr, SharedPtr, GenericPtr};
+
+ getActionDefinitionsBuilder(
+ {G_FADD, G_FCONSTANT, G_FSUB, G_FMUL, G_FMINNUM, G_FMAXNUM, G_FMINIMUM,
+ G_FMAXIMUM, G_FNEG, G_FMA, G_FCEIL, G_FFLOOR, G_FRINT, G_FNEARBYINT,
+ G_INTRINSIC_ROUND, G_INTRINSIC_ROUNDEVEN, G_FSQRT, G_INTRINSIC_TRUNC})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor(AllFloats)
+ .scalarize(0);
+
+ // G_FABS is lowered to bitwise AND to clear the sign bit (strict IEEE
+ // semantics). For nnan cases, llvm.pisa.fabs is used instead which maps
+ // directly to the PISA fabs instruction.
+ getActionDefinitionsBuilder(G_FABS)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, ElementCount::getFixed(4)))
+ .fewerElementsIf(isWiderThan2x16BitVector(0),
+ changeElementCountTo(0, EC2))
+ .customFor(AllFloats)
+ .customIf([](const LegalityQuery &Q) {
+ LLT Ty = Q.Types[0];
+ return Ty.isVector() && Ty.getNumElements() == 2 &&
+ Ty.getScalarSizeInBits() == 16;
+ })
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(
+ {G_ADD, G_SUB, G_MUL, G_SDIV, G_UDIV, G_SREM, G_UREM})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({I16, I32, I64})
+ .clampScalar(0, I16, I64)
+ .widenScalarToNextPow2(0)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder({G_UMULO, G_SMULO})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .scalarize(0)
+ .minScalar(0, I16)
+ .lower();
+
+ // leave these as scalar type for now, as they are used for legalization
+ // of e.g. shufflevector, which operates on both floating and integer types
+ getActionDefinitionsBuilder({G_AND, G_OR, G_XOR})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .fewerElementsIf(is3x8BitVector(0), changeTo(0, V2I8))
+ .fewerElementsIf(isWiderThan2x16BitVector(0), changeTo(0, V2I16))
+ .bitcastIf(LegalityPredicate(([=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[0];
+ if (!Ty.isVector())
+ return false;
+ auto VecBitSize = Ty.getSizeInBits();
+ return VecBitSize == 32 || VecBitSize == 16;
+ })),
+ LegalizeMutation(([=](const LegalityQuery &Query) {
+ return std::pair(
+ 0, LLT::integer(Query.Types[0].getSizeInBits()));
+ })))
+ .legalFor({I16, I32, I64})
+ .widenScalarIf(
+ [=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[0];
+ return Ty.getSizeInBits() == 1;
+ },
+ [=](const LegalityQuery &Query) { return std::pair(0, I32); })
+ .widenScalarToNextPow2(0, 16)
+ .clampScalar(0, I16, I64)
+ .scalarize(0);
+
+ // prelegalizer rules (div_rem_to_divrem) that generate these are disabled
+ getActionDefinitionsBuilder({G_UDIVREM, G_SDIVREM}).unsupported();
+
+ getActionDefinitionsBuilder({G_SHL, G_LSHR, G_ASHR})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{I16, I32}, {I32, I32}, {I64, I32}})
+ .clampScalar(1, I32, I32)
+ .widenScalarToNextPow2(0, 16)
+ .clampScalar(0, I16, I64)
+ .scalarize(0)
+ .lower();
+
+ getActionDefinitionsBuilder(G_TRUNC)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .scalarize(0)
+ .legalFor(
+ {{I8, I16}, {I16, I32}, {I8, I32}, {I32, I64}, {I16, I64}, {I8, I64}})
+ .customIf([=](const LegalityQuery &Query) {
+ return Query.Types[0].getScalarSizeInBits() == 1 ||
+ (Query.Types[1].getScalarSizeInBits() == 128 &&
+ !(Query.Types[0].getScalarSizeInBits() > 64));
+ })
+ .alwaysLegal();
+
+ getActionDefinitionsBuilder({G_SEXT, G_ZEXT, G_ANYEXT})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .scalarize(0)
+ .legalFor(
+ {{I16, I8}, {I32, I8}, {I64, I8}, {I32, I16}, {I64, I16}, {I64, I32}})
+ .customIf([=](const LegalityQuery &Query) {
+ auto DstSize = Query.Types[0].getScalarSizeInBits();
+ auto SrcSize = Query.Types[1].getScalarSizeInBits();
+ auto UseSelect = SrcSize == 1;
+ auto UseShuffle = (SrcSize % 8 == 0 && !isPowerOf2_32(SrcSize)) ||
+ (DstSize % 8 == 0 && !isPowerOf2_32(DstSize));
+ return UseSelect || UseShuffle;
+ })
+ .clampScalar(0, I16, I64)
+ .clampScalar(1, I16, I32);
+
+ getActionDefinitionsBuilder(G_SEXT_INREG).lower();
+
+ getActionDefinitionsBuilder({G_FPTRUNC, G_INTRINSIC_FPTRUNC_ROUND})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{BF16, F32}, {F16, F32}, {BF16, F64}, {F16, F64}, {F32, F64}})
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_FPEXT)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{F32, BF16}, {F32, F16}, {F64, BF16}, {F64, F16}, {F64, F32}})
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_CTPOP)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{I16, I16}, {I32, I32}})
+ .clampScalar(0, I16, I32)
+ .clampScalar(1, I16, I32)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder({G_CTTZ, G_CTLZ})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{I16, I16}, {I32, I32}})
+ .clampScalar(0, I16, I32)
+ .clampScalar(1, I16, I32)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder({G_CTTZ_ZERO_POISON, G_CTLZ_ZERO_POISON})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{I16, I16}, {I32, I32}})
+ .clampScalar(0, I16, I32)
+ .clampScalar(1, I16, I64)
+ .maxScalar(1, I32)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_BITREVERSE)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({I32})
+ .clampScalar(0, I32, I32)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_FDIV)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .customFor({BF16, F16})
+ .legalFor({F32, F64})
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_FREM)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .customFor({BF16, F16, F32, F64})
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_CONSTANT)
+ .legalFor({I1, I8, I16, I32, I64})
+ .legalIf(isPointer(0))
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, I16, I64)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_PTR_ADD)
+ .legalIf(all(isPointer(0), sameSize(0, 1)))
+ .scalarize(0)
+ .scalarSameSizeAs(1, 0);
+
+ getActionDefinitionsBuilder({G_FLDEXP, G_STRICT_FLDEXP})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .customFor({{BF16, I32}, {F16, I32}, {F32, I32}, {F64, I32}})
+ .scalarize(0);
+
+ getActionDefinitionsBuilder({G_FSHR, G_FSHL})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{I32, I32}})
+ .scalarize(0)
+ .lower();
+
+ getActionDefinitionsBuilder({G_ROTL, G_ROTR})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ // Scalarize vectors with i32 elements to enable lowering to scalar
+ // fshl/fshr.
+ .scalarizeIf(
+ [](const LegalityQuery &Query) {
+ return Query.Types[0].isVector() &&
+ Query.Types[0].getScalarSizeInBits() == 32;
+ },
+ 0)
+ .lower();
+
+ getActionDefinitionsBuilder(G_IS_FPCLASS).scalarize(0).custom();
+
+ /////////////////////////////////////////////////////////////////////////
+
+ getActionDefinitionsBuilder(G_GLOBAL_VALUE).alwaysLegal();
+
+ getActionDefinitionsBuilder({G_INTRINSIC, G_INTRINSIC_W_SIDE_EFFECTS,
+ G_INTRINSIC_CONVERGENT,
+ G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS})
+ .alwaysLegal();
+
+ getActionDefinitionsBuilder(G_SHUFFLE_VECTOR)
+ .customIf([](const LegalityQuery &Query) {
+ auto Ty = Query.Types[0];
+ return Ty.isVector() && (Ty.getScalarSizeInBits() == 32) &&
+ isPowerOf2_32(Ty.getNumElements());
+ })
+ .lower();
+
+ getActionDefinitionsBuilder({G_MEMCPY, G_MEMCPY_INLINE, G_MEMMOVE, G_MEMSET})
+ .lower();
+
+ getActionDefinitionsBuilder(G_ADDRSPACE_CAST)
+ .scalarize(0)
+ .customIf([=](const LegalityQuery &Query) -> bool {
+ unsigned DstAS = Query.Types[0].getAddressSpace();
+ unsigned SrcAS = Query.Types[1].getAddressSpace();
+ return (DstAS != (unsigned)PISAAS::AddressSpace::GENERIC) &&
+ (SrcAS != (unsigned)PISAAS::AddressSpace::GENERIC);
+ })
+ .legalForCartesianProduct(AllPtrs, AllPtrs);
+
+ getActionDefinitionsBuilder({G_LOAD, G_STORE})
+ .bitcastIf(isFloatingPointType(0), changeElementTypeToInteger(0))
+ // Handle sub-byte types: vectors with sub-byte elements (>1 bit) are
+ // bitcast to scalar, then widened to multiple of 8 bits. Sub-byte
+ // scalars are widened directly.
+ //.widenScalar does not update MI.memoperands()[0].getType(), hence
+ .customIf([=](const LegalityQuery &Query) -> bool {
+ auto Ty = Query.Types[0];
+ if (Ty.isVector() && (Ty.getScalarSizeInBits() > 1) &&
+ (Ty.getScalarSizeInBits() < 8))
+ return true;
+ if (!Ty.isVector() && ((Ty.getSizeInBits() % 8) != 0))
+ return true;
+ return false;
+ })
+ .fewerElementsIf(
+ [=](const LegalityQuery &Query) -> bool {
+ auto EltTy = Query.Types[0];
+ auto BitSize = EltTy.getScalarSizeInBits();
+ auto NumElts = EltTy.isVector() ? EltTy.getNumElements() : 1;
+ auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+ // small (bitsize<32) vectors with non-power-of-2 elements
+ // can be broken into power-of-2 vectors that can be later
+ // upconverted to vectors of i32 for better codegen
+ return EltTy.isVector() && !isPowerOf2_32(NumElts) &&
+ BitSize != 1 && (BitSize < 32) && (BitSize < AlignInBits);
+ },
+ [=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
+ auto EltTy = Query.Types[0];
+ auto NumElts = EltTy.getNumElements();
+ auto NewNumElts = PowerOf2Ceil(NumElts) / 2;
+ return std::make_pair(
+ 0, LLT::fixed_vector(NewNumElts, EltTy.getScalarType()));
+ })
+ // split up vectors of non-standard size elements
+ .fewerElementsIf(
+ [=](const LegalityQuery &Query) -> bool {
+ auto EltTy = Query.Types[0];
+ return EltTy.isVector() &&
+ !isPowerOf2_32(EltTy.getScalarSizeInBits());
+ },
+ [=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
+ auto EltTy = Query.Types[0];
+ return std::make_pair(0, EltTy.getScalarType());
+ })
+ // cast non-^2 scalars to vectors of i8
+ .bitcastIf(
+ [=](const LegalityQuery &Query) -> bool {
+ const LLT EltTy = Query.Types[0];
+ auto NumBits = EltTy.getSizeInBits();
+ return !EltTy.isVector() && !isPowerOf2_32(NumBits);
+ },
+ [=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
+ auto Size = Query.Types[0].getSizeInBits();
+ return std::pair(0, LLT::fixed_vector(Size / 8, I8));
+ })
+ // cast scalar/vector with large bitsize into <? x i32>
+ .bitcastIf(
+ [=](const LegalityQuery &Query) -> bool {
+ const LLT EltTy = Query.Types[0];
+ auto NumBits = EltTy.getScalarSizeInBits();
+ auto IsAtomic128 =
+ EltTy.isScalar() && (NumBits == 128) &&
+ isStrongerThanMonotonic(Query.MMODescrs[0].Ordering);
+ return !IsAtomic128 && (NumBits % 32 == 0) && (NumBits > 64);
+ },
+ [=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
+ const LLT EltTy = Query.Types[0];
+ auto NumBits = EltTy.getSizeInBits();
+ return std::pair(0, LLT::fixed_vector(NumBits / 32, I32));
+ })
+ .bitcastIf(([=](const LegalityQuery &Query) -> bool {
+ auto EltTy = Query.Types[0];
+ auto BitSize = EltTy.getScalarSizeInBits();
+ auto AccSize = EltTy.getSizeInBits();
+ auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+ auto SmallVectorWithManyElements =
+ (BitSize < 32) && EltTy.isVector() &&
+ (EltTy.getNumElements() > 4);
+
+ if ((AlignInBits >= AccSize) && !SmallVectorWithManyElements)
+ return false; // all good already
+ if (AlignInBits == BitSize)
+ return false; // handled by scalarizeIf code below
+ if ((AlignInBits < AccSize) && (AccSize % AlignInBits))
+ return false; // weird size/alignment
+ if ((BitSize < 32) && (AccSize % 32 == 0) &&
+ (AlignInBits % 32 == 0))
+ return true; // will bitcast to <? x i32>
+
+ return (AlignInBits < BitSize) ||
+ ((BitSize < 32) && (AlignInBits < AccSize));
+ }),
+ [=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
+ auto EltTy = Query.Types[0];
+ auto BitSize = EltTy.getScalarSizeInBits();
+ auto AccSize = EltTy.getSizeInBits();
+ auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+
+ if ((BitSize < 32) && (AccSize % 32 == 0) &&
+ (AlignInBits % 32 == 0))
+ AlignInBits = 32;
+
+ auto NewEltTy = LLT::integer(AlignInBits);
+ auto NewNumElts = AccSize / AlignInBits;
+ auto NewTy = NewNumElts == 1
+ ? NewEltTy
+ : LLT::fixed_vector(NewNumElts, NewEltTy);
+ return std::pair(0, NewTy);
+ })
+ // bitcast <6 x i32> to <3 x i64> if alignment is sufficient
+ .bitcastIf(([=](const LegalityQuery &Query) -> bool {
+ auto EltTy = Query.Types[0];
+ return EltTy.isVector() &&
+ (EltTy.getScalarSizeInBits() == 32) &&
+ (EltTy.getNumElements() == 6) &&
+ (Query.MMODescrs[0].AlignInBits >= 64);
+ }),
+ [=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
+ auto NewTy = LLT::fixed_vector(3, LLT::integer(64));
+ return std::pair(0, NewTy);
+ })
+ // Increase the number of elements to corresponding vector of i8
+ .customIf([=](const LegalityQuery &Query) {
+ auto EltTy = Query.Types[0];
+ return EltTy.getScalarSizeInBits() == 1;
+ })
+ // expand s32 vectors with 4 < elts < 8 to have 8 elements
+ // Enabled only for shared memory where loads of OOB accesses are
+ // guaranteed to return 0
+ .customIf([=](const LegalityQuery &Query) {
+ auto EltTy = Query.Types[0];
+ auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+ return shouldWidenLoad(Query.Opcode, EltTy,
+ Query.Types[1].getAddressSpace(), AlignInBits);
+ })
+ // <4 x i8> align 1 .. needs to be broken down into 4 loads
+ .scalarizeIf(([=](const LegalityQuery &Query) -> bool {
+ auto EltTy = Query.Types[0];
+ auto BitSize = EltTy.getScalarSizeInBits();
+ auto AccSize = EltTy.getSizeInBits();
+ auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+ return ((BitSize < 32) && (AlignInBits < AccSize));
+ }),
+ 0)
+ // maximum number of each type that we can load/store
+ .clampMaxNumElements(0, PrivatePtr, 8)
+ .clampMaxNumElements(0, GlobalPtr, 4)
+ .clampMaxNumElements(0, ConstantPtr, 4)
+ .clampMaxNumElements(0, SharedPtr, 8)
+ .clampMaxNumElements(0, GenericPtr, 4)
+ .clampMaxNumElements(0, I8, 4)
+ .clampMaxNumElements(0, I16, 4)
+ .clampMaxNumElements(0, I32, 8)
+ .clampMaxNumElements(0, I64, 4)
+ .clampMaxNumElements(0, I1, 64)
+ // support odd-element vectors, e.g. <7 x i32>
+ // others, e.g. <5 x i16> have been clamped above
+ .fewerElementsIf(
+ [=](const LegalityQuery &Query) -> bool {
+ const LLT EltTy = Query.Types[0];
+ if (!EltTy.isVector())
+ return false;
+ auto NumElements = EltTy.getNumElements();
+ if (!isPowerOf2_32(NumElements))
+ return !((NumElements == 3) &&
+ ((EltTy.getScalarSizeInBits() == 32) ||
+ (EltTy.getScalarSizeInBits() == 64)));
+ return false;
+ },
+ [=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
+ const LLT EltTy = Query.Types[0];
+ auto NewNumElts = PowerOf2Ceil(EltTy.getNumElements()) / 2;
+ return std::pair(
+ 0, LLT::fixed_vector(NewNumElts, EltTy.getScalarType()));
+ })
+ // load/store of ptr requires inttoptr/ptrtoint
+ // - has to come after clamping of max elements
+ .customIf([=](const LegalityQuery &Query) {
+ return Query.Types[0].getScalarType().isPointer();
+ })
+ // default
+ .legalIf(typeInSet(1, AllPtrs));
+
+ // lower to a narrow G_LOAD + // G_SEXT/G_ZEXT.
+ getActionDefinitionsBuilder({G_SEXTLOAD, G_ZEXTLOAD}).custom();
+
+ getActionDefinitionsBuilder(G_FCANONICALIZE).legalFor(AllFloats);
+
+ getActionDefinitionsBuilder({G_FPTOSI, G_FPTOUI, G_FPTOSI_SAT, G_FPTOUI_SAT})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalForCartesianProduct(AllIntegers, AllFloats)
+ .scalarize(0)
+ .minScalar(0, I8);
+
+ getActionDefinitionsBuilder(G_LROUND)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{I32, F32}, {I64, F32}, {I32, F64}, {I64, F64}})
+ .clampScalar(0, I32, I64)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_LLROUND)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{I64, F32}, {I64, F64}})
+ .clampScalar(0, I64, I64)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder({G_SITOFP, G_UITOFP})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ // GlobalIsel built-in lowering doesn't fully support fp16 yet,
+ // so we have to custom lower it for I1 source type
+ .customIf(all(typeIs(1, I1), typeIs(0, BF16)))
+ .customIf(all(typeIs(1, I1), typeIs(0, F16)))
+ .legalForCartesianProduct(AllFloats, AllIntegers)
+ // other types should prefer built-in lowering
+ .lowerIf(typeIs(1, I1))
+ .widenScalarToNextPow2(1)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder({G_SMIN, G_SMAX, G_UMIN, G_UMAX, G_ABS})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({I16, I32, I64})
+ .minScalar(0, I16)
+ .scalarize(0)
+ .lower();
+
+ // G_PHI is legal for vector types. However, since most of
+ // the PISA operations are scalar, there will be a need for
+ // (vector) extract op. The assumption here is that extraction
+ // in the loop header will allow for better loop body codegen.
+ getActionDefinitionsBuilder(G_PHI)
+ .legalFor(AllPtrs)
+ .legalFor(AllIntegers)
+ .legalFor(AllFloats)
+ .legalFor({I1})
+ .widenScalarToNextPow2(0, 16)
+ .clampScalar(0, I16, I64)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_BITCAST)
+ // allow bitcasts between pointers and non-pointers (ptr2int/int2ptr)
+ .customIf([=](const LegalityQuery &Query) {
+ auto DstTy = Query.Types[0];
+ auto SrcTy = Query.Types[1];
+ return (DstTy.isPointer() != SrcTy.isPointer());
+ })
+ // In cases where both source and destination operands are vectors,
+ // the standard bitcast lowering expects the number of elements to be
+ // divisible by each other, e.g. <4 x i32> to <8 x i16>; use custom
+ // legalization to handle other cases, e.g. <5 x i32> to <2 x i80>
+ .customIf([=](const LegalityQuery &Query) {
+ auto DstTy = Query.Types[0];
+ auto SrcTy = Query.Types[1];
+ if (!SrcTy.isVector() || !DstTy.isVector())
+ return false;
+ unsigned SrcNumElts = SrcTy.getNumElements();
+ unsigned DstNumElts = DstTy.getNumElements();
+ return (SrcNumElts % DstNumElts != 0) && (DstNumElts % SrcNumElts != 0);
+ })
+ // Handle bitcasts between vectors with the same element count and scalar
+ // size but more than 4 elements whose total bit width is not a power of
+ // 2 (e.g. <5 x f16> to <5 x i16>, 80 bits). Decompose element-wise to
+ // avoid creating illegal G_UNMERGE_VALUES on odd-sized vectors
+ // downstream.
+ .customIf([=](const LegalityQuery &Query) {
+ auto DstTy = Query.Types[0];
+ auto SrcTy = Query.Types[1];
+ if (!SrcTy.isVector() || !DstTy.isVector())
+ return false;
+ auto DstNumElts = DstTy.isVector() ? DstTy.getNumElements() : 1;
+ auto SrcNumElts = SrcTy.isVector() ? SrcTy.getNumElements() : 1;
+ auto DstEltSize = DstTy.getScalarSizeInBits();
+ auto SrcEltSize = SrcTy.getScalarSizeInBits();
+ if (DstNumElts != SrcNumElts)
+ return false;
+ if (DstEltSize != SrcEltSize)
+ return false;
+ return DstNumElts > 4 && DstEltSize != 32 &&
+ !isPowerOf2_32(DstTy.getSizeInBits());
+ })
+ .legalIf([=](const LegalityQuery &Query) {
+ auto DstTy = Query.Types[0];
+ auto SrcTy = Query.Types[1];
+ auto DstNumElts = DstTy.isVector() ? DstTy.getNumElements() : 1;
+ auto SrcNumElts = SrcTy.isVector() ? SrcTy.getNumElements() : 1;
+ auto DstEltSize = DstTy.getScalarSizeInBits();
+ auto SrcEltSize = SrcTy.getScalarSizeInBits();
+ auto CastSize = DstTy.getSizeInBits();
+ if (DstEltSize == 32 && SrcEltSize == 32)
+ // vectors of 32bit integer <=> floats
+ return DstNumElts <= 8 || DstNumElts == 16 || DstNumElts == 32 ||
+ DstNumElts == 64;
+ if (DstNumElts > 4 || SrcNumElts > 4)
+ // can not use swizzle for copy
+ return false;
+ if (DstEltSize < 8 || SrcEltSize < 8)
+ // sub-byte types are not natively supported
+ return false;
+ if ((DstTy.isVector() && DstEltSize > 64) ||
+ (SrcTy.isVector() && SrcEltSize > 64))
+ // No vector register class exists for elements wider than 64 bits
+ // (e.g. <2 x i128>); such casts must be lowered element-wise rather
+ // than marked legal, otherwise instruction selection has no vector
+ // register class to constrain to and asserts.
+ return false;
+ if (CastSize > 128)
+ // no registers of such size
+ return DstNumElts != 1 && SrcNumElts != 1;
+ if (!llvm::isPowerOf2_32(CastSize))
+ // non-power-of-2 bitcasts can only be between 3-element vectors
+ return DstNumElts == 3 && SrcNumElts == 3;
+ return true;
+ })
+ .lower();
+
+ for (unsigned Op : {G_EXTRACT_VECTOR_ELT, G_INSERT_VECTOR_ELT}) {
+ unsigned SrcTyIdx = Op == G_EXTRACT_VECTOR_ELT ? 1 : 0;
+
+ getActionDefinitionsBuilder(Op)
+ // extend vectors of i1 to have power of two elements
+ .moreElementsIf(
+ ([=](const LegalityQuery &Query) {
+ auto DstTy = Query.Types[SrcTyIdx];
+ auto BitSize = DstTy.getSizeInBits();
+ return DstTy.isVector() && (DstTy.getScalarSizeInBits() == 1) &&
+ ((BitSize < 8) || !isPowerOf2_32(BitSize));
+ }),
+ [=](const LegalityQuery &Query) {
+ auto DstTy = Query.Types[SrcTyIdx];
+ unsigned NumElts = PowerOf2Ceil(DstTy.getNumElements());
+ NumElts = std::max(8u, NumElts);
+ return std::pair(
+ SrcTyIdx, LLT::fixed_vector(NumElts, DstTy.getScalarType()));
+ })
+ // <? x i1>
+ .customIf([=](const LegalityQuery &Query) {
+ auto EltSize = Query.Types[SrcTyIdx].getScalarSizeInBits();
+ return (EltSize == 1);
+ })
+ // increase to a multiple of elements, e.g. <5 x i16> => <8 x i16>
+ .moreElementsIf(
+ [=](const LegalityQuery &Query) {
+ auto SrcTy = Query.Types[SrcTyIdx];
+ unsigned NumElts = SrcTy.getNumElements();
+ if (SrcTy.getScalarSizeInBits() != 32)
+ return (NumElts > 4) && (NumElts % 4);
+ return (NumElts > 8) && (NumElts != 16) && (NumElts % 32);
+ },
+ [=](const LegalityQuery &Query) {
+ auto SrcTy = Query.Types[SrcTyIdx];
+ auto ScalarTy = SrcTy.getScalarType();
+ auto NumElts = PowerOf2Ceil(SrcTy.getNumElements());
+ return std::pair(SrcTyIdx, LLT::fixed_vector(NumElts, ScalarTy));
+ })
+ // cast non-s32 elements to s32 vector, e.g.
+ // <N x s8> => <N/4 x s32>, iff the index is non-constant
+ .customIf([=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[SrcTyIdx];
+ const auto EltSize = Ty.getScalarSizeInBits();
+ const auto NumElts = Ty.getNumElements();
+ // Only needed for non-s32 elements
+ if (EltSize != 8 && EltSize != 16 && EltSize != 64)
+ return false;
+ // The vector must fit into <64 x s32>, otherwise cannot bitcast
+ if (NumElts * EltSize / 32 > 64)
+ return false;
+ return true;
+ })
+ // reduce to a multiple of elements, e.g. <8 x i16> => <4 x i16>
+ // (x2)
+ // - each multiple of elements is supported natively
+ // - operation will use 'insert/extract' or swizzle
+ .fewerElementsIf(
+ [=](const LegalityQuery &Query) {
+ auto SrcTy = Query.Types[SrcTyIdx];
+ auto MaxElts = SrcTy.getScalarSizeInBits() == 32 ? 64 : 4;
+ return SrcTy.getNumElements() > MaxElts;
+ },
+ [=](const LegalityQuery &Query) {
+ auto SrcTy = Query.Types[SrcTyIdx];
+ auto ScalarTy = SrcTy.getScalarType();
+ return (SrcTy.getScalarSizeInBits() == 32)
+ ? std::pair(SrcTyIdx, LLT::fixed_vector(64, ScalarTy))
+ : std::pair(SrcTyIdx, LLT::fixed_vector(4, ScalarTy));
+ })
+ .lowerIf([=](const LegalityQuery &Query) {
+ return Query.Types[SrcTyIdx].getScalarSizeInBits() != 32;
+ })
+ .alwaysLegal();
+ }
+
+ getActionDefinitionsBuilder(G_INSERT_SUBVECTOR)
+ .customIf([=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[0];
+ return (Ty.getScalarSizeInBits() == 32 ||
+ Ty.getScalarSizeInBits() == 64);
+ })
+ .unsupported(); // no lower() implementation
+
+ getActionDefinitionsBuilder(G_EXTRACT_SUBVECTOR)
+ // A 2-element sub-vector extracted from a wider same-element vector is a
+ // nameable composite sub-register slice (.xy / .zw); ISel lowers it to a
+ // sub-register COPY. Mark it legal so the post-legalizer combiner may
+ // produce it (see build_vector_from_unmerge_lanes).
+ .legalIf([=](const LegalityQuery &Query) {
+ const LLT Dst = Query.Types[0];
+ const LLT Src = Query.Types[1];
+ return Dst.isVector() && Src.isVector() && Dst.getNumElements() == 2 &&
+ Src.getNumElements() > 2 && Src.getNumElements() <= 4 &&
+ Src.getElementType() == Dst.getElementType() &&
+ (Dst.getScalarSizeInBits() == 8 ||
+ Dst.getScalarSizeInBits() == 16);
+ })
+ .customIf([=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[0];
+ return (Ty.getScalarSizeInBits() == 32 ||
+ Ty.getScalarSizeInBits() == 64);
+ })
+ .unsupported(); // no lower() implementation
+
+ getActionDefinitionsBuilder({G_INSERT, G_EXTRACT}).lower();
+
+ getActionDefinitionsBuilder(G_CONCAT_VECTORS)
+ .legalIf([=](const LegalityQuery &Query) {
+ // vector(big) <=> vector(lit)
+ const LLT BigTy = Query.Types[0];
+ return BigTy.isVector() && (BigTy.getNumElements() <= 4) &&
+ (BigTy.getSizeInBits() <= 256); // v4s64
+ })
+ .customIf([=](const LegalityQuery &Query) {
+ // return true if we want to use 'insert', instead of swizzle
+ auto LitTy = Query.Types[1];
+ auto BigTy = Query.Types[0];
+ auto EltOk = BigTy.getScalarSizeInBits() == 32;
+ auto VecOk = LitTy.isVector() && BigTy.isVector();
+ return EltOk && VecOk &&
+ ((LitTy.getNumElements() > 4) || (BigTy.getNumElements() > 4));
+ })
+ .clampMaxNumElements(1, I8, 4)
+ .clampMaxNumElements(1, I16, 4)
+ // .clampMaxNumElements(1, I32, 32) handled by customIf.
+ .clampMaxNumElements(1, I64, 4);
+
+ getActionDefinitionsBuilder(
+ {G_VECREDUCE_SMIN, G_VECREDUCE_SMAX, G_VECREDUCE_UMIN, G_VECREDUCE_UMAX,
+ G_VECREDUCE_ADD, G_VECREDUCE_MUL, G_VECREDUCE_OR, G_VECREDUCE_AND,
+ G_VECREDUCE_XOR, G_VECREDUCE_FMUL, G_VECREDUCE_FMIN, G_VECREDUCE_FMAX,
+ G_VECREDUCE_FMINIMUM, G_VECREDUCE_FMAXIMUM})
+ // fewerElementsVectorReductions does not handle (SrcElts % DstElts != 0)
+ .moreElementsIf(([=](const LegalityQuery &Query) {
+ auto NumElts = Query.Types[1].getNumElements();
+ return NumElts > 4 && NumElts % 4 != 0;
+ }),
+ [=](const LegalityQuery &Query) {
+ auto SrcTy = Query.Types[1];
+ auto NewNumElts =
+ llvm::PowerOf2Ceil(SrcTy.getNumElements());
+ auto NewSrcTy = LLT::fixed_vector(
+ NewNumElts, SrcTy.getScalarType());
+ return std::pair(1, NewSrcTy);
+ })
+ .fewerElementsIf(vectorElementCountIsGreaterThan(1, 4),
+ changeElementCountTo(1, EC4))
+ .scalarize(1)
+ .lower();
+
+ for (unsigned Op : {G_MERGE_VALUES, G_UNMERGE_VALUES}) {
+ unsigned BigTyIdx = Op == G_UNMERGE_VALUES ? 1 : 0;
+ unsigned LitTyIdx = Op == G_UNMERGE_VALUES ? 0 : 1;
+
+ auto &Builder = getActionDefinitionsBuilder(Op);
+ Builder.customIf([=](const LegalityQuery &Query) {
+ // return true if we want to use 'extract', instead of swizzle
+ auto BigTy = Query.Types[BigTyIdx];
+ return BigTy.isVector() && (BigTy.getScalarSizeInBits() == 32) &&
+ (BigTy.getNumElements() > 4);
+ });
+
+ Builder
+ .legalIf([=](const LegalityQuery &Query) {
+ // vector(big) <=> scalar/vector(lit)
+ auto BigTy = Query.Types[BigTyIdx];
+ auto LitTy = Query.Types[LitTyIdx];
+ auto LitTyValid = LitTy.isScalar() ? BigTy.getScalarType() == LitTy
+ : LitTy.getScalarSizeInBits() >= 8;
+ // No register class exists for vectors with elements wider than
+ // 64 bits, so <N x i128> and friends must not be marked legal here
+ // (they would otherwise crash instruction selection when looking up
+ // a vector register class). Cap the vector element size at 64.
+ return LitTyValid && BigTy.isVector() &&
+ (BigTy.getScalarSizeInBits() <= 64) &&
+ (BigTy.getNumElements() <= 4) &&
+ (BigTy.getSizeInBits() <= 256); // v4s64
+ })
+ .widenScalarIf(
+ [=](const LegalityQuery &Query) {
+ auto BigTy = Query.Types[BigTyIdx];
+ auto BigTySize = BigTy.getSizeInBits();
+ return BigTy.isScalar() && BigTySize > 64 &&
+ !isPowerOf2_32(BigTySize);
+ },
+ [=](const LegalityQuery &Query) {
+ auto BigTy = Query.Types[BigTyIdx];
+ unsigned NewSizeInBits =
+ 1 << Log2_32_Ceil(BigTy.getSizeInBits() + 1);
+ return std::pair(BigTyIdx, LLT::integer(NewSizeInBits));
+ })
+ .lowerIf([=](const LegalityQuery &Query) {
+ // lower to shift/mask if conversion would
+ // result in a vector with >4 elements
+ auto BigTy = Query.Types[BigTyIdx];
+ auto LitTy = Query.Types[LitTyIdx];
+ auto NumElts = BigTy.getSizeInBits() / LitTy.getScalarSizeInBits();
+ return BigTy.isScalar() && (NumElts > 4);
+ })
+ .lowerIf(all(vectorElementCountIsGreaterThan(LitTyIdx, 4),
+ vectorElementCountIsGreaterThan(BigTyIdx, 4)))
+ .fewerElementsIf(vectorElementCountIsGreaterThan(BigTyIdx, 4),
+ changeElementCountTo(BigTyIdx, EC4))
+ .minScalarOrEltIf(scalarNarrowerThan(LitTyIdx, 16), LitTyIdx, I16)
+ .legalIf([=](const LegalityQuery &Query) {
+ return (Query.Types[BigTyIdx].isScalar() ||
+ Query.Types[BigTyIdx].isPointer()) &&
+ (Query.Types[LitTyIdx].isScalar() ||
+ Query.Types[LitTyIdx].isPointer());
+ });
+ }
+
+ getActionDefinitionsBuilder(G_BUILD_VECTOR).alwaysLegal();
+
+ getActionDefinitionsBuilder(G_IMPLICIT_DEF)
+ .legalIf([=](const LegalityQuery &Query) {
+ return isLegalType(Query.Types[0]);
+ })
+ .legalFor({I1})
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, I16, I64)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_FREEZE)
+ .legalFor(AllIntegers)
+ .legalFor(AllFloats)
+ .legalFor(AllPtrs)
+ .widenScalarToNextPow2(0)
+ .clampScalar(0, I32, I64)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_INTTOPTR)
+ // List the common cases
+ .legalForCartesianProduct(AddrSpaces64, {I64})
+ .legalForCartesianProduct(AddrSpaces32, {I32})
+ .scalarize(0)
+ // Accept any address space as long as the size matches
+ .legalIf(sameSize(0, 1))
+ .widenScalarIf(smallerThan(1, 0),
+ [](const LegalityQuery &Query) {
+ return std::pair(
+ 1, LLT::integer(Query.Types[0].getSizeInBits()));
+ })
+ .narrowScalarIf(largerThan(1, 0), [](const LegalityQuery &Query) {
+ return std::pair(1, LLT::integer(Query.Types[0].getSizeInBits()));
+ });
+
+ getActionDefinitionsBuilder(G_PTRTOINT)
+ // List the common cases
+ .legalForCartesianProduct(AddrSpaces64, {I64})
+ .legalForCartesianProduct(AddrSpaces32, {I32})
+ .scalarize(0)
+ // Accept any address space as long as the size matches
+ .legalIf(sameSize(0, 1))
+ .widenScalarIf(smallerThan(0, 1),
+ [](const LegalityQuery &Query) {
+ return std::pair(
+ 0, LLT::integer(Query.Types[1].getSizeInBits()));
+ })
+ .narrowScalarIf(largerThan(0, 1), [](const LegalityQuery &Query) {
+ return std::pair(0, LLT::integer(Query.Types[1].getSizeInBits()));
+ });
+
+ getActionDefinitionsBuilder(G_ICMP)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalIf(all(
+ typeIs(0, I1),
+ LegalityPredicates::any(isPointer(1), typeInSet(1, {I16, I32, I64}))))
+ .widenScalarToNextPow2(1)
+ .scalarize(0)
+ .clampScalar(1, I16, I64);
+
+ getActionDefinitionsBuilder(G_FCMP)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .scalarize(0)
+ .custom();
+
+ getActionDefinitionsBuilder({G_SCMP, G_UCMP})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .lower();
+
+ getActionDefinitionsBuilder(G_SELECT)
+ .legalIf(all(
+ LegalityPredicates::any(isPointer(0), typeInSet(0, {I16, I32, I64})),
+ typeIs(1, I1)))
+ .legalIf(
+ all(LegalityPredicates::any(isPointer(0), typeInSet(0, AllFloats)),
+ typeIs(1, I1)))
+ .scalarize(0)
+ .clampScalar(0, I16, I64)
+ .widenScalarToNextPow2(0);
+
+ getActionDefinitionsBuilder(
+ {G_ATOMICRMW_OR, G_ATOMICRMW_ADD, G_ATOMICRMW_AND, G_ATOMICRMW_MAX,
+ G_ATOMICRMW_MIN, G_ATOMICRMW_SUB, G_ATOMICRMW_XOR, G_ATOMICRMW_UMAX,
+ G_ATOMICRMW_UMIN, G_ATOMICRMW_UINC_WRAP, G_ATOMICRMW_UDEC_WRAP})
+ // PISA supports up to acq_rel, anything that is not included in that
+ // needs custom legalization.
+ // In other words if acq_rel provides the same or stronger guarantees
+ // than the requested ordering, then the operation is legal, otherwise
+ // it needs custom legalization.
+ // Note that this is not equivalent to isStrongerThan(Ordering, AcqRel)
+ // because memory orderings do not form a total order.
+ // For example, Acquire is neither stronger nor weaker than Release.
+ .customIf([=](const LegalityQuery &Query) {
+ return !isAtLeastOrStrongerThan(AtomicOrdering::AcquireRelease,
+ Query.MMODescrs[0].Ordering);
+ })
+ .legalForCartesianProduct({I16, I32, I64},
+ {GlobalPtr, SharedPtr, GenericPtr});
+
+ getActionDefinitionsBuilder(
+ {G_ATOMICRMW_FADD, G_ATOMICRMW_FSUB, G_ATOMICRMW_FMIN, G_ATOMICRMW_FMAX})
+ .customIf([=](const LegalityQuery &Query) {
+ return !isAtLeastOrStrongerThan(AtomicOrdering::AcquireRelease,
+ Query.MMODescrs[0].Ordering);
+ })
+ .legalForCartesianProduct(AllFloats, {GlobalPtr, SharedPtr, GenericPtr});
+
+ getActionDefinitionsBuilder(G_ATOMICRMW_XCHG)
+ .customIf([=](const LegalityQuery &Query) {
+ return !isAtLeastOrStrongerThan(AtomicOrdering::AcquireRelease,
+ Query.MMODescrs[0].Ordering);
+ })
+ .legalForCartesianProduct({I16, I32, I64, I128},
+ {GlobalPtr, SharedPtr, GenericPtr})
+ .customIf([=](const LegalityQuery &Query) {
+ return Query.Types[0].getScalarType().isPointer();
+ });
+
+ getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG_WITH_SUCCESS).lower();
+ // For cmpxchg in case of failure the strongest ordering we can do
+ // directly is 'acquire', anything stronger needs custom legalization.
+ getActionDefinitionsBuilder(G_ATOMIC_CMPXCHG)
+ .customIf([=](const LegalityQuery &Query) {
+ return !isAtLeastOrStrongerThan(AtomicOrdering::AcquireRelease,
+ Query.MMODescrs[0].Ordering) ||
+ !isAtLeastOrStrongerThan(AtomicOrdering::Acquire,
+ Query.MMODescrs[0].FailureOrdering);
+ })
+ .alwaysLegal();
+
+ getActionDefinitionsBuilder({G_UADDSAT, G_USUBSAT})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .minScalar(0, I16)
+ .scalarize(0)
+ .lower();
+
+ getActionDefinitionsBuilder({G_SADDSAT, G_SSUBSAT})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .minScalar(0, I16)
+ .legalFor({I16, I32, I64})
+ .scalarize(0)
+ .lower();
+
+ getActionDefinitionsBuilder({G_UADDO, G_USUBO, G_UADDE, G_USUBE})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .scalarize(0)
+ .clampScalar(0, I32, I32)
+ .legalFor({{I32, I1}});
+
+ getActionDefinitionsBuilder({G_SADDO, G_SSUBO, G_SADDE, G_SSUBE})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .lower();
+
+ // pointer-handling.
+ getActionDefinitionsBuilder(G_FRAME_INDEX).legalFor({PrivatePtr, SharedPtr});
+
+ // control-flow. In some cases (e.g. constants) i1 may be promoted to i32.
+ getActionDefinitionsBuilder(G_BR).alwaysLegal();
+ getActionDefinitionsBuilder(G_BRCOND).legalFor({I1, I32});
+ getActionDefinitionsBuilder(G_FENCE).alwaysLegal();
+ getActionDefinitionsBuilder({G_TRAP, G_DEBUGTRAP, G_UBSANTRAP}).alwaysLegal();
+
+ getActionDefinitionsBuilder({G_FCOS, G_FSIN, G_FTANH, G_FEXP2, G_FLOG2})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({BF16, F16, F32})
+ .scalarize(0);
+
+ getActionDefinitionsBuilder({G_FEXP, G_FEXP10, G_FLOG, G_FLOG10, G_FPOW})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .customFor({BF16, F16, F32})
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_FPOWI).lower();
+
+ getActionDefinitionsBuilder(G_FCOPYSIGN)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .lower();
+
+ getActionDefinitionsBuilder({G_SMULH, G_UMULH})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .scalarize(0)
+ .customFor({I64})
+ .lower();
+
+ getActionDefinitionsBuilder(G_BSWAP)
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .scalarize(0)
+ .customIf([=](const LegalityQuery &Query) {
+ const LLT Ty = Query.Types[0];
+ unsigned BitSize = Ty.getSizeInBits();
+ return Ty.isScalar() && (BitSize % 16 == 0);
+ })
+ .unsupported();
+
+ getActionDefinitionsBuilder(G_CONSTANT_FOLD_BARRIER)
+ .legalFor({I8, I16, I32, I64});
+
+ getActionDefinitionsBuilder({G_SBFX, G_UBFX})
+ .fewerElementsIf(vectorElementCountIsGreaterThan(0, 4),
+ changeElementCountTo(0, EC4))
+ .legalFor({{I32, I32}})
+ .clampScalar(1, I32, I32)
+ .clampScalar(0, I32, I32)
+ .scalarize(0);
+
+ getActionDefinitionsBuilder(G_DYN_STACKALLOC).legalFor({{PrivatePtr, I32}});
+
+ getActionDefinitionsBuilder({G_READSTEADYCOUNTER, G_READCYCLECOUNTER})
+ .legalFor({I64});
+
+ verify(*ST.getInstrInfo());
+}
+
+// scalarize an intrinsic instruction with vector arguments
+static SmallVector<MachineInstr *> scalarizeIntrinsic(MachineInstr &MI) {
+ Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ MachineIRBuilder B(MI);
+ auto &MRI = *B.getMRI();
+
+ SmallVector<MachineInstr *> NewMIs;
+ auto DstTy = MRI.getType(MI.getOperand(0).getReg());
+ if (!DstTy.isVector()) {
+ NewMIs.push_back(&MI);
+ return NewMIs;
+ }
+
+ SmallVector<Register, 4> VecRegs;
+ for (unsigned I = 0; I < DstTy.getNumElements(); I++) {
+ SmallVector<MachineOperand, 4> Opnds;
+ for (unsigned J = 2; J < MI.getNumOperands(); J++) { // dst, iid
+ auto Opnd = MI.getOperand(J);
+ if (Opnd.isReg()) {
+ auto ArgTy = MRI.getType(Opnd.getReg());
+ if (ArgTy.isVector()) {
+ ArgTy = ArgTy.getScalarType();
+ auto ArgReg = MRI.createGenericVirtualRegister(ArgTy);
+ B.buildExtractVectorElementConstant(ArgReg, Opnd, I);
+ Opnds.push_back(MachineOperand::CreateReg(ArgReg, false));
+ } else { // use register operand as-is
+ Opnds.push_back(Opnd);
+ }
+ } else { // use immediate operand as-is (e.g. rounding mode)
+ Opnds.push_back(Opnd);
+ }
+ }
+ auto DstReg = MRI.createGenericVirtualRegister(DstTy.getScalarType());
+ auto Res = B.buildIntrinsic(IntrinsicID, DstReg);
+ NewMIs.push_back(Res);
+ Res.setMIFlags(MI.getFlags());
+ for (auto It = Opnds.begin(), Ite = Opnds.end(); It != Ite; ++It)
+ Res.add(*It);
+ VecRegs.push_back(DstReg);
+ }
+ B.buildBuildVector(MI.getOperand(0), VecRegs);
+ MI.eraseFromParent();
+ return NewMIs;
+}
+
+// flog(x) = flog2(x) * ln(2)
+static bool legalizeGFlog(MachineInstr &MI, MachineIRBuilder &B,
+ double Log2BaseInverted) {
+ Register Dst = MI.getOperand(0).getReg();
+ Register Src = MI.getOperand(1).getReg();
+ LLT Ty = B.getMRI()->getType(Dst);
+ unsigned Flags = MI.getFlags();
+
+ auto &Semantics = getFltSemanticForLLT(Ty.getScalarType());
+ APFloat APFLog2BaseInverted(Log2BaseInverted);
+ bool LosesInfo; // ignored
+ APFLog2BaseInverted.convert(Semantics, APFloat::rmNearestTiesToEven,
+ &LosesInfo);
+
+ auto Log2Operand = B.buildFLog2(Ty, Src, Flags);
+ auto Log2BaseInvertedOperand = B.buildFConstant(Ty, APFLog2BaseInverted);
+
+ B.buildFMul(Dst, Log2Operand, Log2BaseInvertedOperand, Flags);
+ MI.eraseFromParent();
+ return true;
+}
+
+// fexp(x) = fexp2(x * log2(e))
+static bool legalizeGFexp(MachineInstr &MI, MachineIRBuilder &B,
+ double Multiplicand) {
+ Register Dst = MI.getOperand(0).getReg();
+ Register Src = MI.getOperand(1).getReg();
+ unsigned Flags = MI.getFlags();
+ LLT Ty = B.getMRI()->getType(Dst);
+
+ auto &Semantics = getFltSemanticForLLT(Ty.getScalarType());
+ APFloat APFMultiplicand(Multiplicand);
+ bool LosesInfo; // ignored
+ APFMultiplicand.convert(Semantics, APFloat::rmNearestTiesToEven, &LosesInfo);
+
+ auto K = B.buildFConstant(Ty, APFMultiplicand);
+ auto Mul = B.buildFMul(Ty, Src, K, Flags);
+ B.buildFExp2(Dst, Mul, Flags);
+ MI.eraseFromParent();
+ return true;
+}
+
+// GlobalISel doesn't currently have builtin support to legalize based on
+// condition code like the SelectionDAG path does. We can move to that approach
+// if and when it is available. For now, we custom legalize it based upon the
+// approach in TargetLowering::LegalizeSetCCCondCode().
+static bool legalizeGFcmp(MachineInstr &MI, MachineIRBuilder &B) {
+ auto Pred = static_cast<CmpInst::Predicate>(MI.getOperand(1).getPredicate());
+ Register Dst = MI.getOperand(0).getReg();
+ Register Op0 = MI.getOperand(2).getReg();
+ Register Op1 = MI.getOperand(3).getReg();
+ unsigned Flags = MI.getFlags();
+ switch (Pred) {
+ case CmpInst::FCMP_UNE:
+ case CmpInst::FCMP_OEQ:
+ case CmpInst::FCMP_OGT:
+ case CmpInst::FCMP_OGE:
+ case CmpInst::FCMP_OLT:
+ case CmpInst::FCMP_OLE:
+ // already legal
+ break;
+ case CmpInst::FCMP_ONE:
+ case CmpInst::FCMP_UEQ: {
+ // Without the explicit G_SEXT added here, the legalizer will typically
+ // G_ANYEXT the G_FCMP compare result to i16. Given that a .reg destination
+ // for fcmp is only available for 32-bit, we explicitly extend it here
+ // so we can fold the resulting select into the fcmp.
+ auto LHS =
+ B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_OGT, I1, Op0, Op1, Flags));
+ auto RHS =
+ B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_OLT, I1, Op0, Op1, Flags));
+ auto Result = B.buildOr(I32, LHS, RHS);
+ if (Pred == CmpInst::FCMP_UEQ)
+ Result = B.buildNot(I32, Result);
+ B.buildICmp(CmpInst::ICMP_EQ, Dst, Result, B.buildConstant(I32, -1));
+ MI.eraseFromParent();
+ break;
+ }
+ case CmpInst::FCMP_ORD: {
+ auto LHS =
+ B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_OEQ, I1, Op0, Op0, Flags));
+ auto RHS =
+ B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_OEQ, I1, Op1, Op1, Flags));
+ auto Result = B.buildAnd(I32, LHS, RHS);
+ B.buildICmp(CmpInst::ICMP_EQ, Dst, Result, B.buildConstant(I32, -1));
+ MI.eraseFromParent();
+ break;
+ }
+ case CmpInst::FCMP_UNO: {
+ // When checking if an op is NaN in OpenCL, the builtin generates an
+ // fcmp.uno with a non-NaN constant (usually zero). In that case, we don't
+ // need to generate two fcmps because only the non-const parameter is
+ // relevant to this comparison
+ auto Op0Cst = getFConstantVRegValWithLookThrough(Op0, *B.getMRI());
+ bool Op0IsOrdConstant = Op0Cst && !Op0Cst.value().Value.isNaN();
+
+ auto Op1Cst = getFConstantVRegValWithLookThrough(Op1, *B.getMRI());
+ bool Op1IsOrdConstant = Op1Cst && !Op1Cst.value().Value.isNaN();
+
+ if (Op0IsOrdConstant || Op1IsOrdConstant) {
+ auto Reg = Op1IsOrdConstant ? Op0 : Op1;
+ B.buildFCmp(CmpInst::FCMP_UNE, Dst, Reg, Reg, Flags);
+ } else {
+ // If the operands are both non-constant, we need to split this into two
+ // fcmps to ensure it returns false if they are unequal
+ auto LHS =
+ B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_UNE, I1, Op0, Op0, Flags));
+ auto RHS =
+ B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_UNE, I1, Op1, Op1, Flags));
+ auto Result = B.buildOr(I32, LHS, RHS);
+ B.buildICmp(CmpInst::ICMP_EQ, Dst, Result, B.buildConstant(I32, -1));
+ }
+ MI.eraseFromParent();
+ break;
+ }
+ case CmpInst::FCMP_UGT:
+ case CmpInst::FCMP_UGE:
+ case CmpInst::FCMP_ULT:
+ case CmpInst::FCMP_ULE: {
+ auto Cmp = B.buildSExt(I32, B.buildFCmp(FCmpInst::getInversePredicate(Pred),
+ I1, Op0, Op1, Flags));
+ auto Not = B.buildNot(I32, Cmp);
+ B.buildICmp(CmpInst::ICMP_EQ, Dst, Not, B.buildConstant(I32, -1));
+ MI.eraseFromParent();
+ break;
+ }
+ default:
+ llvm_unreachable("unknown predicate?");
+ }
+ return true;
+}
+
+static bool legalizeGTrunc(MachineInstr &MI, MachineIRBuilder &B) {
+ [[maybe_unused]] auto &MRI = *B.getMRI();
+ auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
+ if (DstTy.getSizeInBits() == 1) {
+ // truncate ??? to i1
+ // Since PISA does not support truncs to i1 (i8 is the minimum), we must
+ // turn it into an i1 by using an icmp instruction.
+ auto Zero = B.buildConstant(SrcTy, 0);
+ auto One = B.buildConstant(SrcTy, 1);
+ auto And = B.buildAnd(SrcTy, Src, One);
+ B.buildICmp(CmpInst::ICMP_NE, Dst, And, Zero);
+ } else {
+ // truncate i128 to ???
+ assert(SrcTy.getSizeInBits() == 128);
+ auto Unmerge = B.buildUnmerge(I64, Src);
+ if (DstTy.getSizeInBits() == 64)
+ B.buildCopy(Dst, Unmerge.getReg(0));
+ else
+ B.buildTrunc(Dst, Unmerge.getReg(0));
+ }
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGExt(MachineInstr &MI, MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
+
+ if (MRI.getType(Src).getSizeInBits() == 1) {
+ // i8 = G_*EXT i1
+ auto Zero = B.buildConstant(DstTy, 0);
+ int64_t ExtendedVal = (MI.getOpcode() == TargetOpcode::G_SEXT) ||
+ (MI.getOpcode() == TargetOpcode::G_ANYEXT)
+ ? -1
+ : 1;
+ auto One = B.buildConstant(DstTy, ExtendedVal);
+ B.buildSelect(Dst, Src, One, Zero);
+ } else {
+ // any G_*EXT where source and destination are byte size
+ auto DstSize = DstTy.getScalarSizeInBits();
+ auto SrcSize = SrcTy.getScalarSizeInBits();
+ assert((DstSize % 8 == 0) && "destination size is not byte size");
+ assert((SrcSize % 8 == 0) && "source size is not byte size");
+ auto EltSize =
+ ((DstSize % 32 == 0) && (SrcSize % 32 == 0))
+ ? 32
+ : (((DstSize % 16 == 0) && (SrcSize % 16 == 0)) ? 16 : 8);
+ unsigned NumDstElts = DstSize / EltSize;
+ unsigned NumSrcElts = SrcSize / EltSize;
+ LLT EltTy = LLT::integer(EltSize);
+ LLT VecDstTy = LLT::fixed_vector(NumDstElts, EltTy);
+
+ auto VecZero = MRI.createGenericVirtualRegister(VecDstTy);
+ SmallVector<APInt> Zeros(NumDstElts, APInt(EltSize, 0));
+ B.buildBuildVectorConstant(VecZero, Zeros);
+
+ Register VecSrc;
+ if (NumSrcElts == 1) {
+ SmallVector<Register> Ops(NumDstElts, Src);
+ VecSrc = MRI.createGenericVirtualRegister(VecDstTy);
+ B.buildBuildVector(VecSrc, Ops); // Splat scalar into vector
+ } else {
+ LLT VecSrcTy = LLT::fixed_vector(NumSrcElts, EltTy);
+ VecSrc = MRI.createGenericVirtualRegister(VecSrcTy);
+ B.buildBitcast(VecSrc, Src);
+ }
+
+ SmallVector<int> Mask;
+ for (unsigned I = 0; I < NumDstElts; I++) {
+ Mask.push_back((I < NumSrcElts) ? I
+ : MRI.getType(VecSrc).getNumElements());
+ }
+
+ auto VecDst = MRI.createGenericVirtualRegister(VecDstTy);
+ B.buildShuffleVector(VecDst, VecSrc, VecZero, Mask);
+
+ if (MI.getOpcode() == TargetOpcode::G_SEXT) {
+ auto CastReg = MRI.createGenericVirtualRegister(DstTy);
+ auto ShiftReg = MRI.createGenericVirtualRegister(DstTy);
+ auto ShiftAmt = B.buildConstant(I32, DstSize - SrcSize);
+ B.buildBitcast(CastReg, VecDst);
+ B.buildShl(ShiftReg, CastReg, ShiftAmt);
+ B.buildAShr(Dst, ShiftReg, ShiftAmt);
+ } else {
+ B.buildBitcast(Dst, VecDst);
+ }
+ }
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGItofp(MachineInstr &MI, MachineIRBuilder &B) {
+ auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
+ assert(SrcTy.isScalar() && SrcTy.getSizeInBits() == 1 &&
+ "Unexpected source type");
+ assert(DstTy.isScalar() && DstTy.getSizeInBits() == 16 &&
+ "Unexpected destination type");
+
+ unsigned Opc = MI.getOpcode();
+ assert((Opc == TargetOpcode::G_SITOFP || Opc == TargetOpcode::G_UITOFP) &&
+ "Unexpected instruction opcode");
+
+ const fltSemantics &Semantics =
+ DstTy == LLT::bfloat16() ? APFloat::BFloat() : APFloat::IEEEhalf();
+
+ auto TrueVal =
+ APFloat::getOne(Semantics, /*Negative=*/Opc == TargetOpcode::G_SITOFP);
+ auto FalseVal = APFloat::getZero(Semantics);
+
+ auto True = B.buildFConstant(DstTy, TrueVal);
+ auto False = B.buildFConstant(DstTy, FalseVal);
+ B.buildSelect(Dst, Src, True, False);
+ MI.eraseFromParent();
+ return true;
+}
+
+static void updateRegInDebugValue(Register OriginalVal, Register NewVal,
+ MachineRegisterInfo &MRI) {
+ llvm::SmallVector<MachineOperand *, 5> Opnds;
+ for (auto &Instr : MRI.use_instructions(OriginalVal)) {
+ if (!Instr.isDebugValue())
+ continue;
+ for (auto &Opnd : Instr.operands()) {
+ if (Opnd.isReg() && Opnd.getReg() == OriginalVal)
+ Opnds.push_back(&Opnd);
+ }
+ }
+ for (auto *Opnd : Opnds)
+ Opnd->setReg(NewVal);
+ return;
+}
+
+static bool legalizeGExtload(MachineInstr &MI, MachineIRBuilder &B) {
+ auto &LoadMI = cast<GExtLoad>(MI);
+ Register DstReg = LoadMI.getDstReg();
+ Register PtrReg = LoadMI.getPointerReg();
+ LLT MemTy = LoadMI.getMMO().getMemoryType();
+
+ // legalizer will create scalar type here, e.g. s16
+ LLT EltTy = LLT::integer(MemTy.getScalarSizeInBits());
+ MemTy = MemTy.isVector() ? LLT::fixed_vector(MemTy.getNumElements(), EltTy)
+ : EltTy;
+
+ // Narrow load + extension: G_{S,Z}EXTLOAD(DstTy, ptr) ->
+ // %narrow = G_LOAD MemTy, ptr
+ // DstReg = G_{S,Z}EXT DstTy, %narrow
+ auto NarrowLoad = B.buildLoad(MemTy, PtrReg, LoadMI.getMMO());
+ if (isa<GSExtLoad>(MI))
+ B.buildSExt(DstReg, NarrowLoad);
+ else
+ B.buildZExt(DstReg, NarrowLoad);
+ LoadMI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
+ LegalizerHelper &Helper) {
+ auto &MRI = *B.getMRI();
+ GISelChangeObserver &Observer = Helper.Observer;
+ auto &ValMO = MI.getOperand(0);
+ Register Val = ValMO.getReg();
+ MachineMemOperand &MMO = **MI.memoperands_begin();
+ unsigned AddressSpace = MMO.getAddrSpace();
+ LLT CurTy = MRI.getType(Val);
+ auto CurTySize = CurTy.getSizeInBits();
+
+ if (!CurTy.isVector() && ((CurTySize % 8) != 0)) {
+ // Widen sub-byte scalar load/store to multiple of 8 bits.
+ auto NewSize = (CurTySize + 7) & ~7;
+ auto NewTy = LLT::integer(NewSize);
+ if (MI.getOpcode() == TargetOpcode::G_LOAD) {
+ // For loads: widen the load and truncate result.
+ Helper.widenScalar(MI, 0, NewTy);
+ MI.memoperands()[0]->setType(NewTy);
+ } else if (CurTySize > 1) {
+ // For stores of i2+: fold through G_TRUNC/G_BITCAST chains to find a
+ // byte-sized source, avoiding G_ANYEXT from sub-byte types (which can't
+ // be legalized for sources wider than i1).
+ Register SrcReg = Val;
+ MachineInstr *Def = MRI.getVRegDef(SrcReg);
+ while (Def &&
+ (Def->getOpcode() == TargetOpcode::G_TRUNC ||
+ Def->getOpcode() == TargetOpcode::G_BITCAST) &&
+ MRI.getType(Def->getOperand(1).getReg()).getSizeInBits() <
+ NewSize) {
+ SrcReg = Def->getOperand(1).getReg();
+ Def = MRI.getVRegDef(SrcReg);
+ }
+ // If the walk stops on a cast, it is always a G_TRUNC: a G_BITCAST
+ // preserves its operand's (sub-byte) width, so it can never be the def
+ // with a source >= NewSize that ends the walk. A non-cast terminating
+ // def (e.g. G_CONSTANT) is handled by the G_INSERT widening below.
+ if (Def && Def->getOpcode() == TargetOpcode::G_TRUNC) {
+ // Found wider source via the G_TRUNC/G_BITCAST chain
+ Register WiderReg = Def->getOperand(1).getReg();
+ LLT WiderTy = MRI.getType(WiderReg);
+ Register StoreReg;
+ if (WiderTy.getSizeInBits() == NewSize)
+ StoreReg = WiderReg;
+ else
+ StoreReg = B.buildTrunc(NewTy, WiderReg).getReg(0);
+ Observer.changingInstr(MI);
+ ValMO.setReg(StoreReg);
+ MMO.setType(NewTy);
+ Observer.changedInstr(MI);
+ } else {
+ // No wider source found via G_TRUNC/G_BITCAST chain.
+ // Use G_INSERT into undef to widen without G_ANYEXT (which can't be
+ // legalized for non-byte-aligned sub-byte sources > i1).
+ Register UndefReg = B.buildUndef(NewTy).getReg(0);
+ Register WideReg = B.buildInsert(NewTy, UndefReg, Val, 0).getReg(0);
+ Observer.changingInstr(MI);
+ ValMO.setReg(WideReg);
+ MMO.setType(NewTy);
+ Observer.changedInstr(MI);
+ }
+ } else {
+ // For stores of i1: G_ANYEXT from i1 is custom-legalized (produces
+ // proper masking), so use Helper.widenScalar directly.
+ Helper.widenScalar(MI, 0, NewTy);
+ MI.memoperands()[0]->setType(NewTy);
+ }
+ } else if (CurTy.isVector() && (CurTy.getScalarSizeInBits() > 1) &&
+ (CurTy.getScalarSizeInBits() < 8)) {
+ // Vectors with sub-byte elements: bitcast to scalar, then widen to
+ // multiple of 8 bits (minimum 8).
+ // e.g. <2 x i4> -> i8, <2 x i2> -> i4 -> i8, <65 x i2> -> i130 -> i136
+ unsigned ScalarSize = CurTySize;
+ unsigned NewSize = std::max(8u, ((ScalarSize + 7) & ~7u));
+ auto NewTy = LLT::integer(NewSize);
+ if (MI.getOpcode() == TargetOpcode::G_LOAD) {
+ Register NewVal = MRI.createGenericVirtualRegister(NewTy);
+ Observer.changingInstr(MI);
+ ValMO.setReg(NewVal);
+ MMO.setType(NewTy);
+ Observer.changedInstr(MI);
+ B.setInsertPt(B.getMBB(), ++B.getInsertPt());
+ if (ScalarSize == NewSize) {
+ B.buildBitcast(Val, NewVal);
+ } else {
+ auto Trunc = B.buildTrunc(LLT::integer(ScalarSize), NewVal);
+ B.buildBitcast(Val, Trunc);
+ }
+ } else {
+ // Store: bitcast vector to scalar integer. If already byte-aligned,
+ // update MI directly. Otherwise, let the legalizer re-process as
+ // scalar sub-byte store on the next iteration.
+ if (ScalarSize == NewSize) {
+ Register NewVal = MRI.createGenericVirtualRegister(NewTy);
+ B.buildBitcast(NewVal, Val);
+ Observer.changingInstr(MI);
+ ValMO.setReg(NewVal);
+ MMO.setType(NewTy);
+ Observer.changedInstr(MI);
+ } else {
+ // Vector total size is not byte-aligned. Bitcast to scalar and
+ // insert into a wider byte-aligned integer using G_INSERT.
+ // Upper bits are don't-care for stores.
+ Register CastReg =
+ B.buildBitcast(LLT::integer(ScalarSize), Val).getReg(0);
+ Register UndefReg = B.buildUndef(NewTy).getReg(0);
+ Register NewVal = B.buildInsert(NewTy, UndefReg, CastReg, 0).getReg(0);
+ Observer.changingInstr(MI);
+ ValMO.setReg(NewVal);
+ MMO.setType(NewTy);
+ Observer.changedInstr(MI);
+ }
+ }
+ } else if (shouldWidenLoad(MI.getOpcode(), CurTy, AddressSpace,
+ MMO.getAlign().value() * 8)) {
+ assert(CurTy.getScalarSizeInBits() == 32 &&
+ "ShouldWidenLoad: Only 32-bit elements reach here");
+ assert(
+ (AddressSpace == static_cast<unsigned>(PISAAS::AddressSpace::GLOBAL) ||
+ AddressSpace == static_cast<unsigned>(PISAAS::AddressSpace::SHARED) ||
+ AddressSpace ==
+ static_cast<unsigned>(PISAAS::AddressSpace::CONSTANT)) &&
+ "ShouldWidenLoad: Only global,shared,constant loads should reach here");
+ // Get alignment in bytes
+ Align AlignInBytes = MMO.getAlign();
+ Register OriginalVal = ValMO.getReg();
+ Register NewVal;
+ auto NumElts = CurTy.getNumElements();
+ // If alignment is at least 8 bytes and number of elements is 5 or 6,
+ // widen to 3 elements of i64. Otherwise, widen to 8 elements of i32.
+ bool CanWidenToI64 = (AlignInBytes.value() >= 8) && (NumElts <= 6);
+ auto NewTy = CanWidenToI64 ? LLT::fixed_vector(3, LLT::integer(64))
+ : LLT::fixed_vector(8, LLT::integer(32));
+ auto VecN32Ty = LLT::fixed_vector(NumElts, LLT::integer(32));
+
+ // Create the new widened load/store
+ Observer.changingInstr(MI);
+ NewVal = MRI.createGenericVirtualRegister(NewTy);
+ MMO.setType(NewTy);
+ ValMO.setReg(NewVal);
+ Observer.changedInstr(MI);
+ B.setInsertPt(B.getMBB(), ++B.getInsertPt());
+ // DBG_VALUE should be associated with the original load
+ updateRegInDebugValue(OriginalVal, NewVal, MRI);
+
+ Register ResultReg;
+ if (CanWidenToI64) {
+ // Handle vectors with 5 or 6 elements of i32 with alignment >= 8 bytes
+ // Legalize to 3 elements of i64 for better hardware utilization
+ auto V6i32Ty = LLT::fixed_vector(6, LLT::integer(32));
+ auto ExtrVal = MRI.createGenericVirtualRegister(V6i32Ty);
+ // Bitcast to 6xi32 first, then extract the
+ // first 5 elements
+ B.buildBitcast(ExtrVal, NewVal);
+ ResultReg = B.buildExtractSubvector(VecN32Ty, ExtrVal, 0).getReg(0);
+ } else {
+ // Handle vectors with 5, 6, or 7 elements of i32 with alignment <= 8
+ // bytes or 7 elements. Expand them to 8 elements for better hardware
+ // utilization
+ assert(NumElts == 7 || AlignInBytes.value() < 8);
+ // After loading the 8-element vector, extract the needed elements
+ ResultReg = B.buildExtractSubvector(VecN32Ty, NewVal, 0).getReg(0);
+ }
+ if (CurTy.getScalarType().isPointer()) {
+ B.buildIntToPtr(Val, ResultReg);
+ } else {
+ B.buildCopy(Val, ResultReg);
+ }
+ } else if (CurTy.getScalarType().isPointer()) {
+ // load/store of ptr requires inttoptr/ptrtoint
+ auto EltSize = CurTy.getScalarSizeInBits();
+ LLT NewTy = CurTy.changeElementType(LLT::integer(EltSize));
+ Register NewVal = MRI.createGenericVirtualRegister(NewTy);
+ MMO.setType(NewTy);
+ ValMO.setReg(NewVal);
+ if (MI.getOpcode() == TargetOpcode::G_LOAD) {
+ B.setInsertPt(B.getMBB(), ++B.getInsertPt());
+ B.buildIntToPtr(Val, NewVal);
+ } else {
+ B.buildPtrToInt(NewVal, Val);
+ }
+ } else if (CurTy.getScalarSizeInBits() == 1) {
+ auto BitSize = CurTy.getSizeInBits();
+ auto NumEltsI8 = (BitSize + 7) / 8;
+ auto NewBitSize = NumEltsI8 * 8;
+ assert(CurTy.isVector() &&
+ "Expected only vector of i1 to reach here, scalar was extended to "
+ "i8 on widen scalars to be multiple of 8");
+ bool NoExtensionNeeded = (BitSize == NewBitSize);
+ LLT NewI8Ty = (NumEltsI8 > 1) ? LLT::fixed_vector(NumEltsI8, I8) : I8;
+ Register NewI8Val = MRI.createGenericVirtualRegister(NewI8Ty);
+ Register OriginalVal = ValMO.getReg();
+ Observer.changingInstr(MI);
+ MMO.setType(NewI8Ty);
+ ValMO.setReg(NewI8Val);
+ Observer.changedInstr(MI);
+ if (MI.getOpcode() == TargetOpcode::G_LOAD) {
+ updateRegInDebugValue(OriginalVal, NewI8Val, MRI);
+ B.setInsertPt(B.getMBB(), ++B.getInsertPt());
+ if (NoExtensionNeeded)
+ B.buildBitcast(Val, NewI8Val);
+ else {
+ LLT NewI1Ty = LLT::fixed_vector(NewBitSize, I1);
+ Register NewI1Val = MRI.createGenericVirtualRegister(NewI1Ty);
+ B.buildBitcast(NewI1Val, NewI8Val);
+ B.buildDeleteTrailingVectorElements(Val, NewI1Val);
+ }
+ } else {
+ if (NoExtensionNeeded)
+ B.buildBitcast(NewI8Val, Val);
+ else {
+ LLT NewI1Ty = LLT::fixed_vector(NewBitSize, I1);
+ Register NewI1Val = MRI.createGenericVirtualRegister(NewI1Ty);
+ B.buildPadVectorWithUndefElements(NewI1Val, Val);
+ B.buildBitcast(NewI8Val, NewI1Val);
+ }
+ }
+ } else {
+ llvm_unreachable("unhandled load/store case");
+ }
+ return true;
+}
+
+static bool legalizeGFrem(MachineInstr &MI, MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ Register DstReg = MI.getOperand(0).getReg();
+ Register Src0Reg = MI.getOperand(1).getReg();
+ Register Src1Reg = MI.getOperand(2).getReg();
+ auto Flags = MI.getFlags();
+ auto FmAfn = Flags & MachineInstr::FmAfn;
+ LLT Ty = MRI.getType(DstReg);
+
+ auto DivFlags = Flags;
+ if (FmAfn) {
+ DivFlags &= ~MachineInstr::FmAfn;
+ DivFlags |= MachineInstr::FmArcp;
+ }
+ auto Div = B.buildFDiv(Ty, Src0Reg, Src1Reg, DivFlags);
+ auto Trunc = B.buildIntrinsicTrunc(Ty, Div, Flags);
+ auto Neg = B.buildFNeg(Ty, Trunc, Flags);
+ if (!FmAfn) {
+ auto FMA = B.buildFMA(Ty, Neg, Src1Reg, Src0Reg, Flags);
+
+ auto &Semantics = getFltSemanticForLLT(Ty.getScalarType());
+ auto InfC = B.buildFConstant(Ty, APFloat::getInf(Semantics));
+
+ auto XAbs = B.buildIntrinsic(Intrinsic::pisa_fabs, {Ty})
+ .addUse(Src0Reg)
+ .setMIFlags(Flags);
+ auto YAbs = B.buildIntrinsic(Intrinsic::pisa_fabs, {Ty})
+ .addUse(Src1Reg)
+ .setMIFlags(Flags);
+ // Using pisa_fabs is safe here: the result is only compared against Inf
+ // via OEQ, which is false for any NaN regardless of signaling/quiet.
+ auto XFCmp = B.buildFCmp(FCmpInst::FCMP_OEQ, I1, XAbs, InfC, Flags);
+ auto YFCmp = B.buildFCmp(FCmpInst::FCMP_OEQ, I1, YAbs, InfC, Flags);
+ auto Sel = B.buildSelect(Ty, YFCmp, Src0Reg, FMA);
+ B.buildSelect(DstReg, XFCmp, FMA, Sel);
+ } else {
+ B.buildFMA(DstReg, Neg, Src1Reg, Src0Reg, Flags);
+ }
+ MI.eraseFromParent();
+ return true;
+}
+
+// IEEE 754 fabs: clear the sign bit via bitwise AND.
+// This is used when NaN inputs cannot be ruled out, ensuring the sign bit
+// is cleared without quieting the NaN (unlike the PISA fabs instruction).
+static bool legalizeFAbs(MachineInstr &MI, MachineIRBuilder &B) {
+ Register DstReg = MI.getOperand(0).getReg();
+ Register SrcReg = MI.getOperand(1).getReg();
+ MachineRegisterInfo &MRI = *B.getMRI();
+ LLT Ty = MRI.getType(DstReg);
+
+ // <2 x half> / <2 x bfloat>: pack into a single 32-bit AND.
+ if (Ty.isVector() && Ty.getNumElements() == 2 &&
+ Ty.getScalarSizeInBits() == 16) {
+ auto Src32 = B.buildBitcast(I32, SrcReg);
+ // 0x7FFF7FFF: clears the sign bit of each 16-bit element.
+ auto Mask = B.buildConstant(I32, 0x7FFF7FFF);
+ auto And = B.buildAnd(I32, Src32, Mask);
+ B.buildBitcast(DstReg, And);
+ MI.eraseFromParent();
+ return true;
+ }
+
+ unsigned BitWidth = Ty.getSizeInBits();
+
+ // Sign bit mask: all ones except the MSB.
+ APInt Mask = APInt::getSignedMaxValue(BitWidth);
+ LLT IntTy = LLT::integer(BitWidth);
+
+ // G_AND is only legal on (any-)scalar integer LLTs, so for typed-float
+ // operands we bitcast through the integer LLT. LLT::operator== treats an
+ // any-scalar as equal to a typed float of the same width, so distinguish
+ // by isFloat() instead of by inequality.
+ bool IsTypedFloat = Ty.isFloat();
+
+ Register IntSrc = SrcReg;
+ if (IsTypedFloat)
+ IntSrc = B.buildBitcast(IntTy, SrcReg).getReg(0);
+
+ auto MaskCst = B.buildConstant(IntTy, Mask);
+ auto And = B.buildAnd(IntTy, IntSrc, MaskCst);
+
+ if (IsTypedFloat)
+ B.buildBitcast(DstReg, And);
+ else
+ B.buildCopy(DstReg, And);
+
+ MI.eraseFromParent();
+ return true;
+}
+
+// Support for bf/hf type is limited to fdiv.fast
+// If non-afn division is requested, we extend args to float,
+// perform the division and truncate the result back to hf/bf
+static bool legalizeGFdiv(MachineInstr &MI, MachineIRBuilder &B) {
+ // natively supported
+ if (MI.getFlag(MachineInstr::FmArcp))
+ return true;
+
+ // perform converts
+ auto &MRI = *B.getMRI();
+ auto [DstReg, Src0Reg, Src1Reg] = MI.getFirst3Regs();
+
+ auto Src0Tmp = MRI.createGenericVirtualRegister(F32);
+ auto Src1Tmp = MRI.createGenericVirtualRegister(F32);
+ auto DstTmp = MRI.createGenericVirtualRegister(F32);
+
+ auto FPExt0 = B.buildFPExt(Src0Tmp, Src0Reg);
+ auto FPExt1 = B.buildFPExt(Src1Tmp, Src1Reg);
+ auto FDiv = B.buildFDiv(DstTmp, FPExt0, FPExt1);
+ B.buildFPTrunc(DstReg, FDiv);
+
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
+ MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ Register SrcReg = MI.getOperand(1).getReg();
+ Register EltReg = MI.getOperand(2).getReg();
+ Register IndexReg = MI.getOperand(3).getReg();
+
+ LLT SrcTy = MRI.getType(SrcReg);
+ LLT EltTy = MRI.getType(EltReg);
+ LLT IndexTy = MRI.getType(IndexReg);
+
+ int EltSize = EltTy.getSizeInBits();
+ int NumElts = SrcTy.getNumElements();
+
+ if (EltSize == 1) {
+ // Handle insertion to <n x i1>
+ int Size = MRI.getType(SrcReg).getSizeInBits();
+ assert(isPowerOf2_32(Size) && "need to extend source to be power of 2");
+
+ LLT ScalarTy = LLT::integer(Size);
+
+ auto SScalarReg = MRI.createGenericVirtualRegister(ScalarTy);
+ auto DScalarReg = MRI.createGenericVirtualRegister(ScalarTy);
+ auto MaskReg = MRI.createGenericVirtualRegister(ScalarTy);
+ auto NotReg = MRI.createGenericVirtualRegister(ScalarTy);
+ auto AndReg = MRI.createGenericVirtualRegister(ScalarTy);
+ auto ShiftReg = MRI.createGenericVirtualRegister(ScalarTy);
+ auto EltZExtReg = MRI.createGenericVirtualRegister(ScalarTy);
+ auto ShiftAmountReg = IndexReg;
+
+ B.buildBitcast(SScalarReg, SrcReg);
+ auto Value = getIConstantVRegValWithLookThrough(IndexReg, MRI);
+
+ if (Value.has_value()) { // constant index
+ B.buildConstant(MaskReg, 1ull << Value->Value.getZExtValue());
+ ShiftAmountReg = MRI.createGenericVirtualRegister(ScalarTy);
+ B.buildConstant(ShiftAmountReg, Value->Value.getZExtValue());
+ } else { // non-constant index
+ auto ConstReg = MRI.createGenericVirtualRegister(ScalarTy);
+ B.buildConstant(ConstReg, 1ull);
+ B.buildShl(MaskReg, ConstReg, IndexReg);
+ }
+
+ B.buildNot(NotReg, MaskReg);
+ B.buildAnd(AndReg, SScalarReg, NotReg);
+ B.buildZExt(EltZExtReg, EltReg);
+ B.buildShl(ShiftReg, EltZExtReg, ShiftAmountReg);
+ B.buildOr(DScalarReg, AndReg, ShiftReg);
+ B.buildBitcast(MI.getOperand(0), DScalarReg);
+ MI.eraseFromParent();
+ return true;
+ }
+
+ assert((EltSize == 8 || EltSize == 16 || EltSize == 64) &&
+ "unexpected element size");
+
+ // If the index is constant, narrow the vector down to 4 elements.
+ if (auto MaybeValue = getIConstantVRegValWithLookThrough(IndexReg, MRI)) {
+ if (NumElts <= 4) {
+ SmallVector<Register, 4> Elements;
+ for (int I = 0; I < NumElts; ++I)
+ Elements.push_back(MRI.createGenericVirtualRegister(EltTy));
+
+ B.buildUnmerge(Elements, SrcReg);
+ Elements[MaybeValue->Value.getZExtValue()] = EltReg;
+ B.buildBuildVector(MI.getOperand(0), Elements);
+ MI.eraseFromParent();
+ return true;
+ }
+
+ // Narrow to 4 elements.
+ auto Res = Helper.fewerElementsVector(MI, 0, LLT::fixed_vector(4, EltTy));
+ return Res != LegalizerHelper::UnableToLegalize;
+ }
+
+ if (EltSize <= 16) {
+ // Handle insertion to <n x i8> and <n x i16>, where the vector size is not
+ // a multiple of 32 bits. The vector is extended to the next multiple of 32
+ // bits, and then bitcast to a vector of i32 for the insertion. The vector
+ // cannot be narrowed here because the index is not constant, and we don't
+ // know which elements will be inserted.
+ if (NumElts * EltSize % 32 != 0) {
+ int NewNumElts = alignTo(NumElts, 32 / EltSize);
+ LLT NewVecTy = LLT::fixed_vector(NewNumElts, EltTy);
+ auto Res = Helper.moreElementsVector(MI, 0, NewVecTy);
+ if (Res == LegalizerHelper::UnableToLegalize)
+ return false;
+ NumElts = NewNumElts;
+ B.setInsertPt(*MI.getParent(), MI);
+ }
+
+ // Now the vector size is a multiple of 32 bits, we can bitcast to a vector
+ // of s32 and insert the element.
+ // When the element type is float (e.g. f16), bitcastInsertVectorElt will
+ // emit G_ZEXT on the element, but G_ZEXT of a float type is invalid.
+ // Work around by converting to integer types, delegating, and bitcasting
+ // back.
+ Register OrigDst = MI.getOperand(0).getReg();
+ Register IntDst;
+ bool NeedFloatBitcast = EltTy.isFloat();
+ if (NeedFloatBitcast) {
+ LLT IntEltTy = LLT::integer(EltSize);
+ LLT IntVecTy = LLT::fixed_vector(NumElts, IntEltTy);
+
+ // Bitcast element from float to integer.
+ Register IntElt = MRI.createGenericVirtualRegister(IntEltTy);
+ B.buildBitcast(IntElt, MI.getOperand(2).getReg());
+ MI.getOperand(2).setReg(IntElt);
+
+ // Bitcast source vector to integer element type.
+ Register IntSrc = MRI.createGenericVirtualRegister(IntVecTy);
+ B.buildBitcast(IntSrc, MI.getOperand(1).getReg());
+ MI.getOperand(1).setReg(IntSrc);
+
+ // Replace destination with integer vector type.
+ IntDst = MRI.createGenericVirtualRegister(IntVecTy);
+ MI.getOperand(0).setReg(IntDst);
+ }
+ int NewNumElts = NumElts * EltSize / 32;
+ LLT NewVecTy = NewNumElts == 1 ? I32 : LLT::fixed_vector(NewNumElts, I32);
+ auto Res = Helper.bitcastInsertVectorElt(MI, 0, NewVecTy);
+ if (NeedFloatBitcast && Res != LegalizerHelper::UnableToLegalize) {
+ MachineInstr *DefMI = MRI.getVRegDef(IntDst);
+ B.setInsertPt(*DefMI->getParent(), std::next(DefMI->getIterator()));
+ B.buildBitcast(OrigDst, IntDst);
+ }
+ return Res != LegalizerHelper::UnableToLegalize;
+ }
+
+ assert(EltSize == 64 && "unexpected element size");
+ LLT NewVecTy = LLT::fixed_vector(NumElts * 2, I32);
+
+ // Compute the low and high indices as low = index * 2, high = low + 1
+ auto One = B.buildConstant(IndexTy, 1).getReg(0);
+ auto LowIndexReg = B.buildShl(IndexTy, IndexReg, One).getReg(0);
+ auto HighIndexReg = B.buildAdd(IndexTy, LowIndexReg, One).getReg(0);
+
+ // Split the 64-bit element into two 32-bit elements
+ auto EltLowReg = MRI.createGenericVirtualRegister(I32);
+ auto EltHighReg = MRI.createGenericVirtualRegister(I32);
+ B.buildUnmerge({EltLowReg, EltHighReg}, EltReg);
+
+ // Bitcast the source vector to s32 vector
+ auto BitcastSrcReg = B.buildBitcast(NewVecTy, SrcReg).getReg(0);
+
+ // Insert the low and high parts
+ auto InsertLowReg = B.buildInsertVectorElement(NewVecTy, BitcastSrcReg,
+ EltLowReg, LowIndexReg)
+ .getReg(0);
+ auto InsertHighReg = B.buildInsertVectorElement(NewVecTy, InsertLowReg,
+ EltHighReg, HighIndexReg)
+ .getReg(0);
+
+ // Bitcast back to the original vector type
+ B.buildBitcast(MI.getOperand(0), InsertHighReg);
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGExtractVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
+ MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ Register VecReg = MI.getOperand(1).getReg();
+ LLT VecTy = MRI.getType(VecReg);
+ LLT EltTy = VecTy.getScalarType();
+ int EltSize = VecTy.getScalarSizeInBits();
+ int NumElts = VecTy.getNumElements();
+
+ if (EltSize == 1) {
+ // Handle extraction of <n x i1>
+ auto Size = MRI.getType(VecReg).getSizeInBits();
+ auto CastReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
+ B.buildBitcast(CastReg, VecReg);
+ auto ShiftReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
+ B.buildLShr(ShiftReg, CastReg, MI.getOperand(2));
+ B.buildTrunc(MI.getOperand(0), ShiftReg);
+ MI.eraseFromParent();
+ return true;
+ }
+
+ assert((EltSize == 8 || EltSize == 16 || EltSize == 64) &&
+ "unexpected element size");
+
+ // If the index is constant, narrow the vector down to 4 elements.
+ Register IndexReg = MI.getOperand(2).getReg();
+ if (auto MaybeValue = getIConstantVRegValWithLookThrough(IndexReg, MRI)) {
+ if (NumElts <= 4) {
+ Register UnmergeReg = B.buildUnmerge(EltTy, VecReg)
+ .getReg(MaybeValue->Value.getZExtValue());
+ B.buildCopy(MI.getOperand(0), UnmergeReg);
+ MI.eraseFromParent();
+ return true;
+ }
+
+ // Narrow to 4 elements.
+ auto Res = Helper.fewerElementsVector(MI, 1, LLT::fixed_vector(4, EltTy));
+ return Res != LegalizerHelper::UnableToLegalize;
+ }
+
+ // Handle extraction from <n x i8> and <n x i16>, where the vector size is not
+ // a multiple of 32 bits. The vector is extended to the next multiple of 32
+ // bits, and then bitcast to a vector of i32 for the extraction. The vector
+ // cannot be narrowed here because the index is not constant, and we don't
+ // know which elements will be extracted.
+ if (NumElts * EltSize % 32 != 0) {
+ int NewNumElts = alignTo(NumElts, 32 / EltSize);
+ LLT NewVecTy = LLT::fixed_vector(NewNumElts, EltTy);
+ auto Res = Helper.moreElementsVector(MI, 1, NewVecTy);
+ if (Res == LegalizerHelper::UnableToLegalize)
+ return false;
+ NumElts = NewNumElts;
+ }
+
+ // Now the vector size is a multiple of 32 bits, we can bitcast to a vector
+ // of s32 and extract the element.
+ // When the element type is float (e.g. f16), bitcastExtractVectorElt will
+ // emit G_TRUNC to the original element type, but G_TRUNC to a float type is
+ // invalid. Work around this by replacing the destination with an integer
+ // type, delegating to the helper, and then bitcasting back to float.
+ Register OrigDst = MI.getOperand(0).getReg();
+ Register IntDst;
+ bool NeedFloatBitcast = EltTy.isFloat();
+ if (NeedFloatBitcast) {
+ LLT IntEltTy = LLT::integer(EltSize);
+ IntDst = MRI.createGenericVirtualRegister(IntEltTy);
+ MI.getOperand(0).setReg(IntDst);
+ // Also patch the source vector to integer element type so bitcast is valid.
+ LLT IntVecTy = LLT::fixed_vector(NumElts, IntEltTy);
+ Register IntVec = MRI.createGenericVirtualRegister(IntVecTy);
+ B.buildBitcast(IntVec, MI.getOperand(1).getReg());
+ MI.getOperand(1).setReg(IntVec);
+ VecTy = IntVecTy;
+ }
+ int NewNumElts = NumElts * EltSize / 32;
+ LLT NewVecTy = NewNumElts == 1 ? I32 : LLT::fixed_vector(NewNumElts, I32);
+ auto Res = Helper.bitcastExtractVectorElt(MI, 1, NewVecTy);
+ if (NeedFloatBitcast && Res != LegalizerHelper::UnableToLegalize) {
+ // MI has been erased by the helper. IntDst now has an integer-typed def
+ // from the helper's lowered sequence. Bitcast it back to the original
+ // float type. Reset the insert point since MI was erased.
+ MachineInstr *DefMI = MRI.getVRegDef(IntDst);
+ B.setInsertPt(*DefMI->getParent(), std::next(DefMI->getIterator()));
+ B.buildBitcast(OrigDst, IntDst);
+ }
+ return Res != LegalizerHelper::UnableToLegalize;
+}
+
+static bool legalizeGBswap(MachineInstr &MI, MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ auto [Dst, Src] = MI.getFirst2Regs();
+ const LLT Ty = MRI.getType(Src);
+ unsigned BitSize = Ty.getScalarSizeInBits();
+
+ assert(BitSize % 16 == 0 && "bswap only supported for multiples of 16 bits");
+
+ // Masks for byte swapping
+ static const std::array<int, 2> SwapMask16 = {1, 0};
+ static const std::array<int, 4> SwapMask32 = {3, 2, 1, 0};
+
+ // Helper lambda for byte-swapping: returns a tuple describing how to swap
+ // bytes within each chunk. The tuple contains:
+ // - ChunkSize: the size in bits of each chunk (either 16 or 32).
+ // - ChunkByteSwapMask: the shuffle mask used to reverse the byte order within
+ // a chunk.
+ // - ChunkShuffleVecTy: the vector type used for shuffling bytes within a
+ // chunk.
+ auto GetSwapProps =
+ [&](unsigned BitSize) -> std::tuple<unsigned, ArrayRef<int>, LLT> {
+ return (BitSize % 32 == 0)
+ ? std::make_tuple(32u, ArrayRef<int>(SwapMask32), V4I8)
+ : std::make_tuple(16u, ArrayRef<int>(SwapMask16), V2I8);
+ };
+
+ auto [ChunkSize, ChunkByteSwapMask, ChunkShuffleVecTy] =
+ GetSwapProps(BitSize);
+
+ // For Src types that are multiples of 32 bits, the value is divided into
+ // 32-bit chunks. Each chunk is byte-swapped, and the resulting chunks are
+ // built into a vector in reverse order. For Src types that are multiples of
+ // 16 bits (but not 32), the value is divided into 16-bit chunks. Each chunk
+ // is byte-swapped and reassembled in reverse order.
+ if (BitSize == 16 || BitSize == 32) {
+ assert(ChunkSize == BitSize &&
+ "Single chunk case: ChunkSize must equal BitSize");
+ auto VecReg = B.buildBitcast(ChunkShuffleVecTy, Src);
+ auto ShufReg = B.buildShuffleVector(ChunkShuffleVecTy, VecReg, VecReg,
+ ChunkByteSwapMask);
+ B.buildBitcast(Dst, ShufReg);
+ } else {
+ unsigned NumChunks = BitSize / ChunkSize;
+ LLT ChunkTy = LLT::integer(ChunkSize);
+ LLT VecTy = LLT::fixed_vector(NumChunks, ChunkTy);
+ auto VecReg = B.buildBitcast(VecTy, Src);
+
+ SmallVector<Register, 8> SwappedChunks;
+ for (int I = NumChunks - 1; I >= 0; --I) {
+ auto Index = B.buildConstant(I32, I);
+ auto ChunkReg = B.buildExtractVectorElement(ChunkTy, VecReg, Index);
+ auto ChunkVec = B.buildBitcast(ChunkShuffleVecTy, ChunkReg);
+ auto SwappedVec = B.buildShuffleVector(ChunkShuffleVecTy, ChunkVec,
+ ChunkVec, ChunkByteSwapMask);
+ auto SwappedChunk = B.buildBitcast(ChunkTy, SwappedVec);
+ SwappedChunks.push_back(SwappedChunk.getReg(0));
+ }
+
+ auto FinalVec = B.buildBuildVector(VecTy, SwappedChunks);
+ B.buildBitcast(Dst, FinalVec);
+ }
+
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGFpow(MachineInstr &MI, MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+
+ auto [Dst, Src0, Src1] = MI.getFirst3Regs();
+ auto DstTy = MRI.getType(Dst);
+ assert(DstTy.isScalar() &&
+ (DstTy.getSizeInBits() == 32 || DstTy.getSizeInBits() == 16));
+
+ unsigned Flags = MI.getFlags();
+
+ // can only do approximation of pow()
+ auto AllowApprox = MI.getFlag(MachineInstr::FmAfn);
+ if (!AllowApprox)
+ llvm_unreachable("not implemented (fpow)");
+
+ auto LogReg = MRI.createGenericVirtualRegister(DstTy);
+ auto MulReg = MRI.createGenericVirtualRegister(DstTy);
+ auto FExp2Reg = Dst;
+
+ B.buildFLog2(LogReg, Src0, Flags);
+ B.buildFMul(MulReg, LogReg, Src1, Flags);
+ B.buildFExp2(FExp2Reg, MulReg, Flags);
+
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGFldexp(MachineInstr &MI, MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ auto [Dst, Src0, Src1] = MI.getFirst3Regs();
+ unsigned Flags = MI.getFlags();
+
+ LLT XTy = MRI.getType(Src0);
+ LLT NTy = MRI.getType(Src1);
+ LLT Src1Ty = MRI.getType(Src1);
+
+ auto AllowApprox =
+ XTy.getSizeInBits() <= 32 && MI.getFlag(MachineInstr::FmAfn);
+ auto IsBFloat16 = XTy == LLT::bfloat16();
+ if (AllowApprox) {
+ auto RegLLT = MRI.getType(Dst);
+ auto FpReg = MRI.createGenericVirtualRegister(RegLLT);
+ auto ExpReg = MRI.createGenericVirtualRegister(RegLLT);
+
+ B.buildSITOFP(FpReg, Src1);
+ B.buildFExp2(ExpReg, FpReg, Flags);
+ B.buildFMul(Dst, Src0, ExpReg, Flags);
+
+ MI.eraseFromParent();
+ return true;
+ }
+
+ int NClampRangeVal, NShiftVal, NDivBy3ShiftVal, NDivBy3MulVal;
+ if (XTy.getSizeInBits() == 16 && !IsBFloat16) {
+ NClampRangeVal = 14;
+ NShiftVal = 10;
+ NDivBy3ShiftVal = 8;
+ NDivBy3MulVal = 0x56;
+ } else if (XTy.getSizeInBits() == 32 || IsBFloat16) {
+ NClampRangeVal = 126;
+ NShiftVal = IsBFloat16 ? 7 : 23;
+ NDivBy3ShiftVal = 16;
+ NDivBy3MulVal = 0x5556;
+ } else {
+ // double precision
+ NClampRangeVal = 1022;
+ NShiftVal = 52;
+ NDivBy3ShiftVal = 16;
+ NDivBy3MulVal = 0x5556;
+ }
+
+ // Limit range of n (such that all inputs can be handled correctly)
+ // For FP32, |n|>128+126+23 will definitely lead to overflow/underflow
+ // |n|<=126*3 is a sufficiently wide range for n (and FP32 x)
+ // For FP64, |n|>1024+1022+52 will definitely lead to overflow/underflow
+ // |n|<=1022*3 is a sufficiently wide range for n (and FP64 x)
+
+ auto ClampMax = B.buildConstant(NTy, -NClampRangeVal * 3);
+ auto NClampedMax = B.buildSMax(NTy, Src1, ClampMax);
+ auto ClampMin = B.buildConstant(NTy, NClampRangeVal * 3);
+ auto NClamped = B.buildSMin(NTy, NClampedMax, ClampMin);
+
+ auto AddConst = B.buildConstant(NTy, (NClampRangeVal + 1) * 3);
+ auto N = B.buildAdd(NTy, NClamped, AddConst);
+ if (XTy.getSizeInBits() == 16 && !IsBFloat16) {
+ NTy = I16;
+ N = B.buildTrunc(NTy, N);
+ }
+
+ // for fp16, n/3 performed as a 8x8-bit->16-bit integer MUL and SHR by 8.
+ // for others, n/3, performed as a 16x16-bit->32-bit integer MUL and SHR by 16
+ // (both LSHR or ASHR work, n is positive at this point)
+ auto MulConst = B.buildConstant(NTy, NDivBy3MulVal);
+ auto NMul = B.buildMul(NTy, N, MulConst);
+ auto ShrConst = B.buildConstant(I32, NDivBy3ShiftVal);
+ auto K0 = B.buildLShr(NTy, NMul, ShrConst);
+
+ auto NMinusK0 = B.buildSub(NTy, N, K0);
+ auto K1 = B.buildSub(NTy, NMinusK0, K0);
+
+ if (XTy.getSizeInBits() == 64) {
+ NTy = I64;
+ K0 = B.buildZExt(NTy, K0);
+ K1 = B.buildZExt(NTy, K1);
+ } else if (IsBFloat16) {
+ NTy = I16;
+ K0 = B.buildTrunc(NTy, K0);
+ K1 = B.buildTrunc(NTy, K1);
+ }
+
+ auto ShlConst = B.buildConstant(I32, NShiftVal);
+ auto SK0I = B.buildShl(NTy, K0, ShlConst);
+ auto SK1I = B.buildShl(NTy, K1, ShlConst);
+ auto SK0 = B.buildBitcast(XTy, SK0I);
+ auto SK1 = B.buildBitcast(XTy, SK1I);
+
+ SrcOp SwapperX(Src0), SwapperSK1(SK1);
+ if (XTy.getSizeInBits() > 16) {
+ // Swap Src0 with SK1 if n is sufficiently small for SK1 * SK0 * SK0 not to
+ // overflow (inf). This prevents a potential underflow that can happen with
+ // Src0 * SK0 * SK0.
+ int SmallThresholdVal = NClampRangeVal / 3;
+ auto SmallThresholdConst = B.buildConstant(Src1Ty, SmallThresholdVal);
+ auto Src1Abs = B.buildAbs(Src1Ty, NClamped);
+ auto IsSrc1Small = B.buildICmp(CmpInst::Predicate::ICMP_SLT, I1, Src1Abs,
+ SmallThresholdConst);
+
+ SwapperX = B.buildSelect(XTy, IsSrc1Small, SK1, Src0);
+ SwapperSK1 = B.buildSelect(XTy, IsSrc1Small, Src0, SK1);
+ }
+
+ auto Res0 = B.buildFMul(XTy, SwapperX, SK0, Flags);
+ auto Res1 = B.buildFMul(XTy, Res0, SK0, Flags);
+ B.buildFMul(Dst, Res1, SwapperSK1, Flags);
+
+ MI.eraseFromParent();
+ return true;
+}
+
+// PISA specification provides no support for 16bit fsqrt with rounding mode
+// - extend to 32bit value
+// - perform square root with rounding mode
+// - truncate to 16bit value
+static bool legalizeIntrinsicFSqrt(LegalizerHelper &Helper, MachineInstr &MI) {
+ MachineIRBuilder &B = Helper.MIRBuilder;
+ auto &MRI = *B.getMRI();
+
+ SmallVector<MachineInstr *, 4> MIs;
+ Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ if (MRI.getType(MI.getOperand(0).getReg()).isVector()) {
+ MIs = scalarizeIntrinsic(MI);
+ } else {
+ MIs.push_back(&MI);
+ }
+
+ for (auto It = MIs.begin(), Ite = MIs.end(); It != Ite; ++It) {
+ auto *MI = *It;
+ MachineIRBuilder MIB(*MI);
+
+ auto Dst = MI->getOperand(0).getReg();
+ auto Src = MI->getOperand(2).getReg();
+ auto Imm = MI->getOperand(3).getImm();
+
+ if (MRI.getType(Dst).getScalarSizeInBits() != 16)
+ continue; // already legal
+
+ auto Src32 = MRI.createGenericVirtualRegister(F32);
+ auto Dst32 = MRI.createGenericVirtualRegister(F32);
+ MIB.buildFPExt(Src32, Src);
+ MIB.buildIntrinsic(IntrinsicID, Dst32).addReg(Src32).addImm(Imm);
+ MIB.buildFPTrunc(Dst, Dst32);
+ MI->eraseFromParent();
+ }
+ return true;
+}
+
+static bool legalizeIntrinsicFDiv(LegalizerHelper &Helper, MachineInstr &MI) {
+ MachineIRBuilder &B = Helper.MIRBuilder;
+ auto &MRI = *B.getMRI();
+
+ Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ SmallVector<MachineInstr *> MIs = scalarizeIntrinsic(MI);
+
+ // fdiv only supports 32/64 width
+ for (auto It = MIs.begin(), Ite = MIs.end(); It != Ite; ++It) {
+ auto *MI = *It;
+ MachineIRBuilder MIB(*MI);
+
+ auto Dst = MI->getOperand(0).getReg();
+ auto Src0 = MI->getOperand(2).getReg();
+ auto Src1 = MI->getOperand(3).getReg();
+
+ if (MRI.getType(Dst).getScalarSizeInBits() != 16)
+ continue;
+
+ // s16 A = FDIV s16 B, s16 C
+ // => s32 B' = FEXT s16 B
+ // => s32 C' = FEXT s16 C
+ // => s32 A' = FDIV s32 B', s32 C'
+ // => s16 A = FTRUNC s32 A'
+ auto Src032 = MRI.createGenericVirtualRegister(F32);
+ auto Src132 = MRI.createGenericVirtualRegister(F32);
+ auto Dst32 = MRI.createGenericVirtualRegister(F32);
+ MIB.buildFPExt(Src032, Src0);
+ MIB.buildFPExt(Src132, Src1);
+ MIB.buildIntrinsic(IntrinsicID, Dst32)
+ .addReg(Src032)
+ .addReg(Src132)
+ .add(MI->getOperand(4));
+ MIB.buildFPTrunc(Dst, Dst32);
+
+ MI->eraseFromParent();
+ }
+ return true;
+}
+
+static SmallVector<Register> splitVectorByGrain(MachineIRBuilder &B,
+ Register Src, unsigned Grain) {
+ auto &MRI = *B.getMRI();
+ auto SrcTy = MRI.getType(Src);
+ auto EltTy = SrcTy.getScalarType();
+
+ auto SliceTy = LLT::fixed_vector(Grain, EltTy);
+
+ if (SrcTy.isScalar()) {
+ auto SliceUndef = MRI.createGenericVirtualRegister(SliceTy);
+ auto Slice = MRI.createGenericVirtualRegister(SliceTy);
+ B.buildUndef(SliceUndef);
+ B.buildInsertVectorElement(Slice, SliceUndef, Src, B.buildConstant(I32, 0));
+ return {Slice};
+ }
+
+ const unsigned NumElts = SrcTy.getNumElements();
+
+ SmallVector<Register> Elts;
+ for (unsigned I = 0; I < NumElts; I += Grain) {
+ auto Slice = MRI.createGenericVirtualRegister(SliceTy);
+ B.buildUndef(Slice);
+
+ for (unsigned J = 0; J < std::min(Grain, NumElts - I); ++J) {
+ auto Idx = B.buildConstant(I32, J).getReg(0);
+ auto Elt =
+ B.buildExtractVectorElementConstant(EltTy, Src, I + J).getReg(0);
+ Slice = B.buildInsertVectorElement(SliceTy, Slice, Elt, Idx).getReg(0);
+ }
+ Elts.push_back(Slice);
+ }
+ return Elts;
+}
+
+static void joinVectorByGrain(MachineIRBuilder &B, Register Dst,
+ ArrayRef<Register> Srcs, unsigned Grain) {
+ auto &MRI = *B.getMRI();
+ auto DstTy = MRI.getType(Dst);
+ auto EltTy = DstTy.getScalarType();
+
+ if (DstTy.isScalar()) {
+ auto Src = Srcs[0];
+ B.buildExtractVectorElementConstant(Dst, Src, 0);
+ return;
+ }
+
+ const unsigned NumElts = DstTy.getNumElements();
+
+ auto TmpDst = B.buildUndef(DstTy).getReg(0);
+
+ for (unsigned I = 0; I < NumElts; I += Grain) {
+ auto &Src = Srcs[I / Grain];
+
+ for (unsigned J = 0; J < std::min(Grain, NumElts - I); ++J) {
+ auto Idx = B.buildConstant(I32, I + J).getReg(0);
+ auto Elt = B.buildExtractVectorElementConstant(EltTy, Src, J).getReg(0);
+
+ TmpDst = B.buildInsertVectorElement(DstTy, TmpDst, Elt, Idx).getReg(0);
+ }
+ }
+
+ B.buildCopy(Dst, TmpDst);
+}
+
+static bool legalizeIntrinsicBfn(LegalizerHelper &Helper, MachineInstr &MI) {
+ MachineIRBuilder &B = Helper.MIRBuilder;
+ auto &MRI = *B.getMRI();
+ auto IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ assert(IntrinsicID == Intrinsic::pisa_bfn);
+
+ const auto BfnOpcode = MI.getOperand(2);
+
+ const auto OrigDst = MI.getOperand(0).getReg();
+ const auto OrigSrc0 = MI.getOperand(3).getReg();
+ const auto OrigSrc1 = MI.getOperand(4).getReg();
+ const auto OrigSrc2 = MI.getOperand(5).getReg();
+
+ const auto Ty = MRI.getType(OrigDst);
+
+ const auto BitWidth = Ty.getScalarSizeInBits();
+ switch (BitWidth) {
+ default:
+ llvm_unreachable("unexpected bitwidth");
+ case 8:
+ case 16: {
+ const auto Grain = 32 / BitWidth;
+ MachineIRBuilder MIB(MI);
+
+ auto Srcs0 = splitVectorByGrain(MIB, OrigSrc0, Grain);
+ auto Srcs1 = splitVectorByGrain(MIB, OrigSrc1, Grain);
+ auto Srcs2 = splitVectorByGrain(MIB, OrigSrc2, Grain);
+
+ auto GrainTy = MRI.getType(Srcs0[0]);
+ SmallVector<Register> Dsts;
+
+ for (auto [Src0, Src1, Src2] : zip(Srcs0, Srcs1, Srcs2)) {
+ auto Dst = MRI.createGenericVirtualRegister(I32);
+ auto Src0Cast = MRI.createGenericVirtualRegister(I32);
+ auto Src1Cast = MRI.createGenericVirtualRegister(I32);
+ auto Src2Cast = MRI.createGenericVirtualRegister(I32);
+
+ auto DstCast = MRI.createGenericVirtualRegister(GrainTy);
+
+ MIB.buildBitcast(Src0Cast, Src0);
+ MIB.buildBitcast(Src1Cast, Src1);
+ MIB.buildBitcast(Src2Cast, Src2);
+
+ MIB.buildIntrinsic(IntrinsicID, Dst)
+ .add(BfnOpcode)
+ .addReg(Src0Cast)
+ .addReg(Src1Cast)
+ .addReg(Src2Cast);
+
+ MIB.buildBitcast(DstCast, Dst);
+ Dsts.push_back(DstCast);
+ }
+
+ joinVectorByGrain(MIB, OrigDst, Dsts, Grain);
+ MI.eraseFromParent();
+ } break;
+ case 32:
+ scalarizeIntrinsic(MI);
+ return true;
+ case 64: {
+ auto MIs = scalarizeIntrinsic(MI);
+
+ for (auto *MI : MIs) {
+ MachineIRBuilder MIB(*MI);
+ auto Dst = MI->getOperand(0).getReg();
+ auto Src0 = MI->getOperand(3).getReg();
+ auto Src1 = MI->getOperand(4).getReg();
+ auto Src2 = MI->getOperand(5).getReg();
+
+ auto DstV2I32 = MRI.createGenericVirtualRegister(V2I32);
+ auto Src0V2I32 = MRI.createGenericVirtualRegister(V2I32);
+ auto Src1V2I32 = MRI.createGenericVirtualRegister(V2I32);
+ auto Src2V2I32 = MRI.createGenericVirtualRegister(V2I32);
+
+ MIB.buildUndef(DstV2I32);
+
+ MIB.buildBitcast(Src0V2I32, Src0);
+ MIB.buildBitcast(Src1V2I32, Src1);
+ MIB.buildBitcast(Src2V2I32, Src2);
+
+ for (int I = 0; I < 2; I++) {
+ auto DstI32 = MRI.createGenericVirtualRegister(I32);
+ auto Src0I32 = MRI.createGenericVirtualRegister(I32);
+ auto Src1I32 = MRI.createGenericVirtualRegister(I32);
+ auto Src2I32 = MRI.createGenericVirtualRegister(I32);
+ auto Idx = MRI.createGenericVirtualRegister(I32);
+
+ MIB.buildExtractVectorElementConstant(Src0I32, Src0V2I32, I);
+ MIB.buildExtractVectorElementConstant(Src1I32, Src1V2I32, I);
+ MIB.buildExtractVectorElementConstant(Src2I32, Src2V2I32, I);
+
+ MIB.buildIntrinsic(IntrinsicID, DstI32)
+ .add(MI->getOperand(2))
+ .addReg(Src0I32)
+ .addReg(Src1I32)
+ .addReg(Src2I32);
+
+ MIB.buildConstant(Idx, I);
+
+ auto DstNext = MRI.createGenericVirtualRegister(V2I32);
+ MIB.buildInsertVectorElement(DstNext, DstV2I32, DstI32, Idx);
+ DstV2I32 = DstNext;
+ }
+
+ MIB.buildBitcast(Dst, DstV2I32);
+ MI->eraseFromParent();
+ }
+ } break;
+ }
+
+ return true;
+}
+
+static bool legalizeIntrinsicRE(LegalizerHelper &Helper, MachineInstr &MI) {
+ MachineIRBuilder &B = Helper.MIRBuilder;
+
+ auto RndMode = MI.getOperand(MI.getNumOperands() - 1).getImm();
+ if (static_cast<RoundingMode>(RndMode) != RoundingMode::NearestTiesToEven)
+ return false; // only .re is supported
+
+ Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ switch (IntrinsicID) {
+ default:
+ return false;
+ case Intrinsic::pisa_log_rnd:
+ B.buildFLog(MI.getOperand(0), MI.getOperand(2), MI.getFlags());
+ break;
+ case Intrinsic::pisa_log2_rnd:
+ B.buildFLog2(MI.getOperand(0), MI.getOperand(2), MI.getFlags());
+ break;
+ case Intrinsic::pisa_log10_rnd:
+ B.buildInstr(TargetOpcode::G_FLOG10, {MI.getOperand(0)}, {MI.getOperand(2)},
+ MI.getFlags());
+ break;
+ case Intrinsic::pisa_sin_rnd:
+ B.buildInstr(TargetOpcode::G_FSIN, {MI.getOperand(0)}, {MI.getOperand(2)},
+ MI.getFlags());
+ break;
+ case Intrinsic::pisa_cos_rnd:
+ B.buildInstr(TargetOpcode::G_FCOS, {MI.getOperand(0)}, {MI.getOperand(2)},
+ MI.getFlags());
+ break;
+ case Intrinsic::pisa_tanh_rnd:
+ B.buildInstr(TargetOpcode::G_FTANH, {MI.getOperand(0)}, {MI.getOperand(2)},
+ MI.getFlags());
+ break;
+ case Intrinsic::pisa_exp_rnd:
+ B.buildInstr(TargetOpcode::G_FEXP, {MI.getOperand(0)}, {MI.getOperand(2)},
+ MI.getFlags());
+ break;
+ case Intrinsic::pisa_exp2_rnd:
+ B.buildFExp2(MI.getOperand(0), MI.getOperand(2), MI.getFlags());
+ break;
+ case Intrinsic::pisa_pow_rnd:
+ B.buildFPow(MI.getOperand(0), MI.getOperand(2), MI.getOperand(3),
+ MI.getFlags());
+ break;
+ }
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeIntrinsicI2F(LegalizerHelper &Helper, MachineInstr &MI) {
+ MachineIRBuilder &B = Helper.MIRBuilder;
+ auto &MRI = *B.getMRI();
+
+ Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ SmallVector<MachineInstr *> MIs = scalarizeIntrinsic(MI);
+
+ // @llvm.experimental.constrained.sitofp.f32.i1
+ for (auto It = MIs.begin(), Ite = MIs.end(); It != Ite; ++It) {
+ auto *MI = *It;
+ MachineIRBuilder MIB(*MI);
+
+ auto SrcReg = MI->getOperand(2).getReg();
+ auto SrcTy = MRI.getType(SrcReg);
+
+ if (SrcTy.getSizeInBits() >= 8)
+ continue;
+
+ auto ExtReg = MRI.createGenericVirtualRegister(I8);
+ if (IntrinsicID == Intrinsic::pisa_uitofp)
+ MIB.buildZExt(ExtReg, SrcReg);
+ else
+ MIB.buildSExt(ExtReg, SrcReg);
+ auto NewMI = MIB.buildIntrinsic(IntrinsicID, MI->getOperand(0).getReg())
+ .addReg(ExtReg)
+ .add(MI->getOperand(3))
+ .add(MI->getOperand(4));
+ NewMI.setMIFlags(MI->getFlags());
+ MI->eraseFromParent();
+ }
+ return true;
+}
+
+// Legalize dp4a_uu with saturation enabled:
+// dp4a_uu(acc, src1, src2, sat=true)
+// => tmp = dp4a_uu(0, src1, src2, sat=false)
+// dst = G_UADDSAT(tmp, acc)
+static bool legalizeIntrinsicDp4a(LegalizerHelper &Helper, MachineInstr &MI) {
+ unsigned Sat = MI.getOperand(5).getImm();
+ if (Sat == 0)
+ return true;
+
+ MachineIRBuilder &B = Helper.MIRBuilder;
+ auto &MRI = *B.getMRI();
+ B.setInstrAndDebugLoc(MI);
+
+ Register Dst = MI.getOperand(0).getReg();
+ Register Acc = MI.getOperand(2).getReg();
+ Register Src1 = MI.getOperand(3).getReg();
+ Register Src2 = MI.getOperand(4).getReg();
+
+ Register Zero = B.buildConstant(I32, 0).getReg(0);
+ Register Tmp = MRI.createGenericVirtualRegister(I32);
+ B.buildIntrinsic(Intrinsic::pisa_dp4a_uu, ArrayRef<Register>{Tmp})
+ .addUse(Zero)
+ .addUse(Src1)
+ .addUse(Src2)
+ .addImm(0);
+ B.buildInstr(TargetOpcode::G_UADDSAT, {DstOp(Dst)}, {SrcOp(Tmp), SrcOp(Acc)});
+
+ MI.eraseFromParent();
+ return true;
+}
+
+bool PISALegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
+ MachineInstr &MI) const {
+ Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ switch (IntrinsicID) {
+ case Intrinsic::pisa_dp4a_uu:
+ return legalizeIntrinsicDp4a(Helper, MI);
+ case Intrinsic::pisa_fsqrt_rnd:
+ return legalizeIntrinsicFSqrt(Helper, MI);
+ case Intrinsic::pisa_fdiv_rnd:
+ return legalizeIntrinsicFDiv(Helper, MI);
+ case Intrinsic::pisa_bfn:
+ return legalizeIntrinsicBfn(Helper, MI);
+ case Intrinsic::pisa_log_rnd:
+ case Intrinsic::pisa_log2_rnd:
+ case Intrinsic::pisa_log10_rnd:
+ case Intrinsic::pisa_sin_rnd:
+ case Intrinsic::pisa_cos_rnd:
+ case Intrinsic::pisa_tanh_rnd:
+ case Intrinsic::pisa_exp_rnd:
+ case Intrinsic::pisa_exp2_rnd:
+ case Intrinsic::pisa_pow_rnd:
+ return legalizeIntrinsicRE(Helper, MI);
+ case Intrinsic::pisa_ired:
+ case Intrinsic::pisa_fred:
+ case Intrinsic::pisa_frcp:
+ case Intrinsic::pisa_frsqrt:
+ case Intrinsic::pisa_fabs:
+ case Intrinsic::pisa_smad:
+ case Intrinsic::pisa_fptosi_rnd:
+ case Intrinsic::pisa_fptoui_rnd:
+ case Intrinsic::pisa_fadd:
+ case Intrinsic::pisa_fsub:
+ case Intrinsic::pisa_fmul:
+ case Intrinsic::pisa_fma:
+ case Intrinsic::pisa_ftrunc:
+ case Intrinsic::pisa_frnd_rnd:
+ scalarizeIntrinsic(MI);
+ return true;
+ case Intrinsic::pisa_sitofp:
+ case Intrinsic::pisa_uitofp:
+ return legalizeIntrinsicI2F(Helper, MI);
+ default:
+ return true;
+ }
+}
+
+static bool legalizeGConcatVectors(MachineInstr &MI, MachineIRBuilder &B) {
+ auto *MRI = B.getMRI();
+
+ auto Dst = MI.getOperand(0).getReg();
+ auto DstTy = MRI->getType(Dst);
+ assert(DstTy.getScalarSizeInBits() == 32);
+ unsigned Idx = 0;
+
+ auto TDst = MRI->createGenericVirtualRegister(DstTy);
+ B.buildInstr(TargetOpcode::IMPLICIT_DEF).addDef(TDst);
+ for (unsigned I = 1; I < MI.getNumOperands(); I++) {
+ auto Src = MI.getOperand(I).getReg();
+ auto SrcTy = B.getMRI()->getType(Src);
+ auto NewDst = MRI->createGenericVirtualRegister(DstTy);
+ B.buildInsertSubvector(NewDst, TDst, Src, Idx);
+ Idx += SrcTy.getNumElements();
+ TDst = NewDst;
+ }
+ B.buildCopy(Dst, TDst);
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGUnmergeValues(LegalizerHelper &Helper, MachineInstr &MI,
+ MachineIRBuilder &B) {
+ auto *MRI = B.getMRI();
+
+ auto Src = MI.getOperand(MI.getNumOperands() - 1).getReg();
+ auto DstTy = MRI->getType(MI.getOperand(0).getReg());
+ assert(MRI->getType(Src).getScalarSizeInBits() == 32);
+
+ unsigned Idx = 0;
+ for (unsigned I = 0; I < MI.getNumOperands() - 1; I++) {
+ auto Dst = MI.getOperand(I).getReg();
+ if (DstTy.isVector()) {
+ // <2 x s32>, <2 x s32> = G_UNMERGE_VALUES <4 x s32>
+ B.buildExtractSubvector(Dst, Src, Idx);
+ Idx += DstTy.getNumElements();
+ } else {
+ // s32, s32, s32, s32 = G_UNMERGE_VALUES <4 x s32>
+ B.buildExtractVectorElementConstant(Dst, Src, Idx);
+ Idx += 1;
+ }
+ }
+ MI.eraseFromParent();
+ return true;
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+static bool legalizeGInsertSubvector(MachineInstr &MI, MachineIRBuilder &B) {
+ auto *MRI = B.getMRI();
+
+ auto DstReg = MI.getOperand(0).getReg();
+ auto VecReg = MI.getOperand(1).getReg();
+ auto SubVecReg = MI.getOperand(2).getReg();
+ auto Idx = MI.getOperand(3).getImm();
+
+ LLT DstTy = MRI->getType(DstReg);
+
+ if (DstTy.getScalarSizeInBits() == 64) {
+ LLT VecTy = MRI->getType(VecReg);
+ LLT SubVecTy = MRI->getType(SubVecReg);
+ LLT CastedVecTy =
+ LLT::fixed_vector(2 * VecTy.getNumElements(), LLT::integer(32));
+ LLT CastedSubVecTy =
+ LLT::fixed_vector(2 * SubVecTy.getNumElements(), LLT::integer(32));
+ LLT CastedDstTy =
+ LLT::fixed_vector(2 * DstTy.getNumElements(), LLT::integer(32));
+
+ // Update vec
+ auto CastedVecReg = MRI->createGenericVirtualRegister(CastedVecTy);
+ B.buildBitcast(CastedVecReg, VecReg);
+ MI.getOperand(1).setReg(CastedVecReg);
+
+ // Update subvec
+ auto CastedSubVecReg = MRI->createGenericVirtualRegister(CastedSubVecTy);
+ B.buildBitcast(CastedSubVecReg, SubVecReg);
+ MI.getOperand(2).setReg(CastedSubVecReg);
+
+ // Update dst
+ auto CastedDstReg = MRI->createGenericVirtualRegister(CastedDstTy);
+ MI.getOperand(0).setReg(CastedDstReg);
+
+ // Update index
+ MI.getOperand(3).setImm(2 * Idx);
+
+ B.setInsertPt(B.getMBB(), ++B.getInsertPt());
+ B.buildBitcast(DstReg, CastedDstReg);
+ return legalizeGInsertSubvector(MI, B);
+ }
+
+ assert(DstTy.getScalarSizeInBits() == 32 && "Unexpected scalar size");
+
+ unsigned NumDstElems = DstTy.getNumElements();
+ if (NumDstElems <= 64)
+ return true; // Already legal
+
+ // Split destination vector into legal 32-element chunks
+ unsigned NumChunks = NumDstElems / 32;
+ LLT ChunkTy = LLT::vector(ElementCount::getFixed(32), DstTy.getScalarType());
+
+ SmallVector<Register, 4> Chunks;
+ for (unsigned I = 0; I < NumChunks; ++I)
+ Chunks.push_back(MRI->createGenericVirtualRegister(ChunkTy));
+
+ B.setInsertPt(*MI.getParent(), MI);
+ B.buildUnmerge(Chunks, VecReg);
+
+ // Determine which chunk the subvector belongs in
+ unsigned ChunkIdx = Idx / 32;
+ unsigned OffsetInChunk = Idx % 32;
+
+ // Per LLVM spec:
+ // "Idx must be a constant multiple of subvec’s known minimum vector length"
+ assert(ChunkIdx < Chunks.size() && "Subvector index out of bounds");
+ assert(OffsetInChunk + MRI->getType(SubVecReg).getNumElements() <= 32 &&
+ "Subvector spans multiple chunks");
+
+ // Insert subvector into the appropriate chunk
+ Register ModifiedChunk = MRI->createGenericVirtualRegister(ChunkTy);
+ B.buildInsertSubvector(ModifiedChunk, Chunks[ChunkIdx], SubVecReg,
+ OffsetInChunk);
+ Chunks[ChunkIdx] = ModifiedChunk;
+
+ // Rebuild the final vector
+ Register FinalVec = MRI->createGenericVirtualRegister(DstTy);
+ B.buildConcatVectors(FinalVec, Chunks);
+ B.buildCopy(DstReg, FinalVec);
+
+ MI.eraseFromParent();
+ return true;
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+static bool legalizeGExtractSubvector(MachineInstr &MI, MachineIRBuilder &B) {
+ auto *MRI = B.getMRI();
+
+ auto DstReg = MI.getOperand(0).getReg();
+ auto SrcReg = MI.getOperand(1).getReg();
+ auto Idx = MI.getOperand(2).getImm();
+
+ LLT SrcTy = MRI->getType(SrcReg);
+
+ if (SrcTy.getScalarSizeInBits() == 64) {
+ LLT DstTy = MRI->getType(DstReg);
+ LLT CastedSrcTy =
+ LLT::fixed_vector(2 * SrcTy.getNumElements(), LLT::integer(32));
+ LLT CastedDstTy =
+ LLT::fixed_vector(2 * DstTy.getNumElements(), LLT::integer(32));
+
+ // Update src
+ auto CastedSrcReg = MRI->createGenericVirtualRegister(CastedSrcTy);
+ B.buildBitcast(CastedSrcReg, SrcReg);
+ MI.getOperand(1).setReg(CastedSrcReg);
+
+ // Update dst
+ auto CastedDstReg = MRI->createGenericVirtualRegister(CastedDstTy);
+ MI.getOperand(0).setReg(CastedDstReg);
+
+ // Update index
+ MI.getOperand(2).setImm(2 * Idx);
+
+ B.setInsertPt(B.getMBB(), ++B.getInsertPt());
+ B.buildBitcast(DstReg, CastedDstReg);
+ return legalizeGExtractSubvector(MI, B);
+ }
+
+ assert(SrcTy.getScalarSizeInBits() == 32 && "Unexpected scalar size");
+
+ unsigned NumSrcElems = SrcTy.getNumElements();
+ if (NumSrcElems <= 64)
+ return true; // Already legal
+
+ // Define 32-element legal vector type
+ unsigned NumChunks = NumSrcElems / 32;
+ LLT ChunkTy = LLT::vector(ElementCount::getFixed(32), SrcTy.getScalarType());
+
+ // Create registers for each chunk
+ SmallVector<Register, 4> Chunks;
+ for (unsigned I = 0; I < NumChunks; ++I)
+ Chunks.push_back(MRI->createGenericVirtualRegister(ChunkTy));
+
+ B.setInsertPt(*MI.getParent(), MI);
+ B.buildUnmerge(Chunks, SrcReg);
+
+ // Determine chunk index and offset
+ unsigned ChunkIdx = Idx / 32;
+ unsigned OffsetInChunk = Idx % 32;
+
+ // Per LLVM spec:
+ // "Idx must be a constant multiple of the known-minimum vector length of the
+ // result type"
+ assert(ChunkIdx < Chunks.size() && "Subvector index out of bounds");
+ assert(OffsetInChunk + MRI->getType(DstReg).getNumElements() <= 32 &&
+ "Subvector spans multiple legal vector chunks");
+
+ B.buildExtractSubvector(DstReg, Chunks[ChunkIdx], OffsetInChunk);
+ MI.eraseFromParent();
+ return true;
+}
+
+static StringRef getSyncScopeStr(LLVMContext &Ctx, SyncScope::ID ScopeID) {
+ // Map dynamically assigned PISA SyncScope ID to its scope name.
+ static DenseMap<SyncScope::ID, StringRef> ScopeID2Name;
+ auto InitializeScopeID2Name = [&]() {
+ static const StringMap<StringRef> ScopeName2EncodeName = {
+ {"workgroup", "workgroup"},
+ {"gpu", "gpu"},
+ {"system", "system"},
+ // using workgroup scope (see PISAScopeSelector pass).
+ {"subgroup", "workgroup"},
+ {"workitem", "workgroup"},
+ };
+ for (const auto &[Name, EncodeName] : ScopeName2EncodeName) {
+ auto ID = Ctx.getOrInsertSyncScopeID(Name);
+ ScopeID2Name.emplace_or_assign(ID, EncodeName);
+ }
+ };
+ static llvm::once_flag InitializeScopeID2NameFlag;
+ std::call_once(InitializeScopeID2NameFlag, InitializeScopeID2Name);
+
+ // Use the original SyncScope ID to look up its scope name in the map.
+ auto It = ScopeID2Name.find(ScopeID);
+ return It != ScopeID2Name.end() ? It->second : StringRef("gpu");
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+static bool legalizeGAtomicrmw(MachineInstr &MI, MachineIRBuilder &B) {
+ const MachineMemOperand *MemOp = *MI.memoperands_begin();
+ AtomicOrdering AO = MemOp->getSuccessOrdering();
+ AtomicOrdering AOF = MemOp->getFailureOrdering();
+ if (isAtLeastOrStrongerThan(AtomicOrdering::AcquireRelease, AO) &&
+ isAtLeastOrStrongerThan(AtomicOrdering::Release, AOF))
+ return true;
+
+ auto &Ctx = B.getMF().getFunction().getContext();
+ llvm::SmallString<16> FenceScopeStr =
+ getSyncScopeStr(Ctx, MemOp->getSyncScopeID());
+ unsigned AddressSpace = MemOp->getAddrSpace();
+ if (AddressSpace == unsigned(PISAAS::AddressSpace::SHARED))
+ FenceScopeStr += "-shared";
+ else if (AddressSpace == unsigned(PISAAS::AddressSpace::GENERIC))
+ FenceScopeStr += "-generic";
+ else
+ FenceScopeStr += "-global";
+
+ B.buildFence(
+ static_cast<unsigned>(llvm::AtomicOrdering::SequentiallyConsistent),
+ Ctx.getOrInsertSyncScopeID(FenceScopeStr));
+
+ if (!isAtLeastOrStrongerThan(AtomicOrdering::AcquireRelease, AO))
+ AO = AtomicOrdering::Monotonic;
+ if (!isAtLeastOrStrongerThan(AtomicOrdering::Release, AOF))
+ AOF = AtomicOrdering::Monotonic;
+
+ MachineMemOperand *NewMemOp = B.getMF().getMachineMemOperand(
+ MemOp->getPointerInfo(), MemOp->getFlags(), MemOp->getSize(),
+ MemOp->getAlign(),
+ MMOMetadata(MemOp->getAAInfo(), MemOp->getRanges(),
+ MemOp->getMemCacheHint()),
+ MemOp->getSyncScopeID(), AO, AOF);
+
+ if (MI.getOpcode() == TargetOpcode::G_ATOMIC_CMPXCHG)
+ B.buildAtomicCmpXchg(MI.getOperand(0), MI.getOperand(1), MI.getOperand(2),
+ MI.getOperand(3), *NewMemOp)
+ .setMIFlags(MI.getFlags());
+ else
+ B.buildAtomicRMW(MI.getOpcode(), MI.getOperand(0), MI.getOperand(1),
+ MI.getOperand(2), *NewMemOp)
+ .setMIFlags(MI.getFlags());
+
+ B.buildFence(
+ static_cast<unsigned>(llvm::AtomicOrdering::SequentiallyConsistent),
+ Ctx.getOrInsertSyncScopeID(FenceScopeStr));
+
+ MI.eraseFromParent();
+ return true;
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+static bool legalizeGAtomicrmwXchg(MachineInstr &MI, MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ auto &Dst = MI.getOperand(0);
+ LLT CurTy = MRI.getType(Dst.getReg());
+ if (CurTy.getScalarType().isPointer()) {
+ auto &Src = MI.getOperand(2);
+ LLT NewTy = LLT::integer(CurTy.getScalarSizeInBits());
+ Register NewSrc = MRI.createGenericVirtualRegister(NewTy);
+ Register NewDst = MRI.createGenericVirtualRegister(NewTy);
+ B.buildPtrToInt(NewSrc, Src);
+ B.buildAtomicRMWXchg(NewDst, MI.getOperand(1).getReg(), NewSrc,
+ **MI.memoperands_begin());
+ B.buildIntToPtr(Dst, NewDst);
+ MI.eraseFromParent();
+ } else {
+ legalizeGAtomicrmw(MI, B);
+ }
+ return true;
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+static bool legalizeGShuffleVector(LegalizerHelper &Helper, MachineInstr &MI,
+ MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ auto &Dst = MI.getOperand(0);
+ auto &Src0 = MI.getOperand(1);
+ auto &Src1 = MI.getOperand(2);
+ ArrayRef<int> Mask = MI.getOperand(3).getShuffleMask();
+ assert(MRI.getType(Dst.getReg()).getScalarSizeInBits() == 32);
+ assert(isPowerOf2_32(MRI.getType(Dst.getReg()).getNumElements()));
+
+ auto UseExtract = true;
+ // indices must be consecutive
+ int PrevIdx = -1;
+ for (int Idx : Mask) {
+ if ((PrevIdx != -1) && (Idx != (PrevIdx + 1)))
+ UseExtract = false;
+ PrevIdx = Idx;
+ }
+
+ // starting index must be aligned to destination size
+ if (Mask[0] % Mask.size())
+ UseExtract = false;
+
+ // indices can not straddle the arguments
+ auto SrcSize = MRI.getType(Src0.getReg()).getNumElements();
+ if ((Mask[0] < SrcSize) && ((Mask[0] + Mask.size()) > SrcSize))
+ UseExtract = false;
+
+ if (SrcSize > 8 && SrcSize != 16 && SrcSize != 32 && (SrcSize % 64))
+ UseExtract = false;
+
+ if (UseExtract) {
+ auto Src = (Mask[0] < SrcSize) ? Src0 : Src1;
+ auto Idx = (Mask[0] < SrcSize) ? Mask[0] : Mask[0] - SrcSize;
+ B.buildExtractSubvector(Dst, Src, Idx);
+ MI.eraseFromParent();
+ return true;
+ }
+
+ // lower if unable to use extract/insert
+ auto Res = Helper.lowerShuffleVector(MI);
+ return Res != LegalizerHelper::UnableToLegalize;
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
+ MachineIRBuilder &MIRBuilder) {
+ auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
+ FPClassTest OriginalMask =
+ static_cast<FPClassTest>(MI.getOperand(2).getImm());
+ auto Mask = OriginalMask;
+ auto IsInvertedCheck = false;
+
+ if (Mask == fcNone) {
+ MIRBuilder.buildConstant(DstReg, 0);
+ MI.eraseFromParent();
+ return true;
+ }
+ if (Mask == fcAllFlags) {
+ MIRBuilder.buildConstant(DstReg, 1);
+ MI.eraseFromParent();
+ return true;
+ }
+
+ // support bfloat types
+ auto &Semantics = getFltSemanticForLLT(SrcTy.getScalarType());
+
+ unsigned BitSize = SrcTy.getScalarSizeInBits();
+ LLT IntTy = LLT::integer(BitSize);
+ if (SrcTy.isVector())
+ IntTy = LLT::vector(SrcTy.getElementCount(), IntTy);
+ auto AsInt = MIRBuilder.buildBitcast(IntTy, SrcReg);
+
+ // Various masks.
+ APInt SignBit = APInt::getSignMask(BitSize);
+ APInt ValueMask = APInt::getSignedMaxValue(BitSize); // All bits but sign.
+ APInt Inf = APFloat::getInf(Semantics).bitcastToAPInt(); // Exp and int bit.
+ APInt ExpMask = Inf;
+ APInt AllOneMantissa = APFloat::getLargest(Semantics).bitcastToAPInt() & ~Inf;
+ APInt QNaNBitMask =
+ APInt::getOneBitSet(BitSize, AllOneMantissa.getActiveBits() - 1);
+ APInt InvertionMask = APInt::getAllOnes(DstTy.getScalarSizeInBits());
+
+ auto SignBitC = MIRBuilder.buildConstant(IntTy, SignBit);
+ auto ValueMaskC = MIRBuilder.buildConstant(IntTy, ValueMask);
+ auto InfC = MIRBuilder.buildConstant(IntTy, Inf);
+ auto ExpMaskC = MIRBuilder.buildConstant(IntTy, ExpMask);
+ auto ZeroC = MIRBuilder.buildConstant(IntTy, 0);
+
+ auto Abs = MIRBuilder.buildAnd(IntTy, AsInt, ValueMaskC);
+ auto Sign =
+ MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_NE, DstTy, AsInt, Abs);
+
+ auto Res = MIRBuilder.buildConstant(DstTy, 0);
+ // Clang doesn't support capture of structured bindings:
+ LLT DstTyCopy = DstTy;
+ const auto AppendToRes = [&](MachineInstrBuilder ToAppend) {
+ Res = MIRBuilder.buildOr(DstTyCopy, Res, ToAppend);
+ };
+
+ // Tests that involve more than one class should be processed first.
+ if ((Mask & fcFinite) == fcFinite) {
+ // finite(V) ==> abs(V) u< exp_mask
+ AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, Abs,
+ ExpMaskC));
+ Mask &= ~fcFinite;
+ } else if ((Mask & fcFinite) == fcPosFinite) {
+ // finite(V) && V > 0 ==> V u< exp_mask
+ AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, AsInt,
+ ExpMaskC));
+ Mask &= ~fcPosFinite;
+ } else if ((Mask & fcFinite) == fcNegFinite) {
+ // finite(V) && V < 0 ==> abs(V) u< exp_mask && signbit == 1
+ auto Cmp = MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, Abs,
+ ExpMaskC);
+ auto And = MIRBuilder.buildAnd(DstTy, Cmp, Sign);
+ AppendToRes(And);
+ Mask &= ~fcNegFinite;
+ }
+
+ if (FPClassTest PartialCheck = Mask & (fcZero | fcSubnormal)) {
+ // fcZero | fcSubnormal => test all exponent bits are 0
+ // TODO: Handle sign bit specific cases
+ // TODO: Handle inverted case
+ if (PartialCheck == (fcZero | fcSubnormal)) {
+ auto ExpBits = MIRBuilder.buildAnd(IntTy, AsInt, ExpMaskC);
+ AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy,
+ ExpBits, ZeroC));
+ Mask &= ~PartialCheck;
+ }
+ }
+
+ if (Mask == OriginalMask) {
+ // combination of classes above did not yield any
+ // optimizations, see if inverse will be less ops
+ auto InvertedMask = (unsigned)~Mask;
+ if (llvm::popcount((unsigned)Mask) > llvm::popcount(InvertedMask)) {
+ Mask = ~Mask;
+ IsInvertedCheck = true;
+ }
+ }
+
+ // Check for individual classes.
+ if (FPClassTest PartialCheck = Mask & fcZero) {
+ if (PartialCheck == fcPosZero)
+ AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy,
+ AsInt, ZeroC));
+ else if (PartialCheck == fcZero)
+ AppendToRes(
+ MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy, Abs, ZeroC));
+ else // fcNegZero
+ AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy,
+ AsInt, SignBitC));
+ }
+
+ if (FPClassTest PartialCheck = Mask & fcSubnormal) {
+ // issubnormal(V) ==> unsigned(abs(V) - 1) u< (all mantissa bits set)
+ // issubnormal(V) && V>0 ==> unsigned(V - 1) u< (all mantissa bits set)
+ auto V = (PartialCheck == fcPosSubnormal) ? AsInt : Abs;
+ auto OneC = MIRBuilder.buildConstant(IntTy, 1);
+ auto VMinusOne = MIRBuilder.buildSub(IntTy, V, OneC);
+ auto SubnormalRes =
+ MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, VMinusOne,
+ MIRBuilder.buildConstant(IntTy, AllOneMantissa));
+ if (PartialCheck == fcNegSubnormal)
+ SubnormalRes = MIRBuilder.buildAnd(DstTy, SubnormalRes, Sign);
+ AppendToRes(SubnormalRes);
+ }
+
+ if (FPClassTest PartialCheck = Mask & fcInf) {
+ if (PartialCheck == fcPosInf)
+ AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy,
+ AsInt, InfC));
+ else if (PartialCheck == fcInf)
+ AppendToRes(
+ MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy, Abs, InfC));
+ else { // fcNegInf
+ APInt NegInf = APFloat::getInf(Semantics, true).bitcastToAPInt();
+ auto NegInfC = MIRBuilder.buildConstant(IntTy, NegInf);
+ AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy,
+ AsInt, NegInfC));
+ }
+ }
+
+ if (FPClassTest PartialCheck = Mask & fcNan) {
+ auto InfWithQnanBitC =
+ MIRBuilder.buildConstant(IntTy, std::move(Inf) | QNaNBitMask);
+ if (PartialCheck == fcNan) {
+ // isnan(V) ==> abs(V) u> int(inf)
+ AppendToRes(
+ MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_UGT, DstTy, Abs, InfC));
+ } else if (PartialCheck == fcQNan) {
+ // isquiet(V) ==> abs(V) u>= (unsigned(Inf) | quiet_bit)
+ AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_UGE, DstTy, Abs,
+ InfWithQnanBitC));
+ } else { // fcSNan
+ // issignaling(V) ==> abs(V) u> unsigned(Inf) &&
+ // abs(V) u< (unsigned(Inf) | quiet_bit)
+ auto IsNan =
+ MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_UGT, DstTy, Abs, InfC);
+ auto IsNotQnan = MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy,
+ Abs, InfWithQnanBitC);
+ AppendToRes(MIRBuilder.buildAnd(DstTy, IsNan, IsNotQnan));
+ }
+ }
+
+ if (FPClassTest PartialCheck = Mask & fcNormal) {
+ // isnormal(V) ==> (0 u< exp u< max_exp) ==> (unsigned(exp-1) u<
+ // (max_exp-1))
+ APInt ExpLSB = ExpMask & ~(ExpMask.shl(1));
+ auto ExpMinusOne = MIRBuilder.buildSub(
+ IntTy, Abs, MIRBuilder.buildConstant(IntTy, ExpLSB));
+ APInt MaxExpMinusOne = std::move(ExpMask) - ExpLSB;
+ auto NormalRes =
+ MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, ExpMinusOne,
+ MIRBuilder.buildConstant(IntTy, MaxExpMinusOne));
+ if (PartialCheck == fcNegNormal)
+ NormalRes = MIRBuilder.buildAnd(DstTy, NormalRes, Sign);
+ else if (PartialCheck == fcPosNormal) {
+ auto PosSign = MIRBuilder.buildXor(
+ DstTy, Sign, MIRBuilder.buildConstant(DstTy, InvertionMask));
+ NormalRes = MIRBuilder.buildAnd(DstTy, NormalRes, PosSign);
+ }
+ AppendToRes(NormalRes);
+ }
+
+ if (IsInvertedCheck)
+ MIRBuilder.buildNot(DstReg, Res);
+ else
+ MIRBuilder.buildCopy(DstReg, Res);
+ MI.eraseFromParent();
+ return true;
+}
+
+static bool legalizeGMulh(LegalizerHelper &Helper, MachineInstr &MI,
+ MachineIRBuilder &B) {
+ auto &MRI = *B.getMRI();
+ auto [Dst, Src0, Src1] = MI.getFirst3Regs();
+ auto IsSigned = MI.getOpcode() == TargetOpcode::G_SMULH;
+ auto DstTy = MRI.getType(Dst);
+ assert(DstTy.getSizeInBits() == 64);
+
+ auto SourceA = MRI.createGenericVirtualRegister(DstTy);
+ auto SourceB = MRI.createGenericVirtualRegister(DstTy);
+
+ auto Const32 = MRI.createGenericVirtualRegister(DstTy);
+ auto Const63 = MRI.createGenericVirtualRegister(DstTy);
+ auto Const0 = MRI.createGenericVirtualRegister(DstTy);
+ auto Mask32 = MRI.createGenericVirtualRegister(DstTy);
+ B.buildConstant(Const32, 32);
+ B.buildConstant(Const63, 63);
+ B.buildConstant(Const0, 0);
+ B.buildConstant(Mask32, 0xFFFFFFFF);
+
+ auto ASign = MRI.createGenericVirtualRegister(DstTy);
+ auto BSign = MRI.createGenericVirtualRegister(DstTy);
+ auto ResultSign = MRI.createGenericVirtualRegister(DstTy);
+ B.buildAShr(ASign, Src0, Const63);
+ B.buildAShr(BSign, Src1, Const63);
+ B.buildXor(ResultSign, ASign, BSign);
+
+ if (IsSigned) {
+ auto ASignXor = MRI.createGenericVirtualRegister(DstTy);
+ auto BSignXor = MRI.createGenericVirtualRegister(DstTy);
+ B.buildXor(ASignXor, Src0, ASign);
+ B.buildXor(BSignXor, Src1, BSign);
+ B.buildSub(SourceA, ASignXor, ASign);
+ B.buildSub(SourceB, BSignXor, BSign);
+ } else {
+ B.buildCopy(SourceA, Src0);
+ B.buildCopy(SourceB, Src1);
+ }
+
+ auto LoSrc0 = MRI.createGenericVirtualRegister(DstTy);
+ auto HiSrc0 = MRI.createGenericVirtualRegister(DstTy);
+ auto LoSrc1 = MRI.createGenericVirtualRegister(DstTy);
+ auto HiSrc1 = MRI.createGenericVirtualRegister(DstTy);
+ B.buildLShr(HiSrc0, SourceA, Const32);
+ B.buildLShr(HiSrc1, SourceB, Const32);
+ B.buildAnd(LoSrc0, SourceA, Mask32);
+ B.buildAnd(LoSrc1, SourceB, Mask32);
+
+ auto ALobLo = MRI.createGenericVirtualRegister(DstTy);
+ auto ALobHi = MRI.createGenericVirtualRegister(DstTy);
+ auto AHibLo = MRI.createGenericVirtualRegister(DstTy);
+ auto AHibHi = MRI.createGenericVirtualRegister(DstTy);
+ B.buildMul(AHibHi, HiSrc0, HiSrc1);
+ B.buildMul(AHibLo, HiSrc0, LoSrc1);
+ B.buildMul(ALobHi, LoSrc0, HiSrc1);
+ B.buildMul(ALobLo, LoSrc0, LoSrc1);
+
+ auto ALobLoHi = MRI.createGenericVirtualRegister(DstTy);
+ auto ALobHiLo = MRI.createGenericVirtualRegister(DstTy);
+ auto AHibLoSum0 = MRI.createGenericVirtualRegister(DstTy);
+ auto AHibLoSum1 = MRI.createGenericVirtualRegister(DstTy);
+ B.buildLShr(ALobLoHi, ALobLo, Const32);
+ B.buildAnd(ALobHiLo, ALobHi, Mask32);
+ B.buildAdd(AHibLoSum0, ALobLoHi, ALobHiLo);
+ B.buildAdd(AHibLoSum1, AHibLo, AHibLoSum0);
+
+ auto ALobLoMasked = MRI.createGenericVirtualRegister(DstTy);
+ auto AHibLoShiftedL = MRI.createGenericVirtualRegister(DstTy);
+ auto ALobHiShiftedR = MRI.createGenericVirtualRegister(DstTy);
+ auto AHibLoShiftedR = MRI.createGenericVirtualRegister(DstTy);
+ auto ShiftedSum = MRI.createGenericVirtualRegister(DstTy);
+ auto DstLo = MRI.createGenericVirtualRegister(DstTy);
+ auto DstHi = MRI.createGenericVirtualRegister(DstTy);
+
+ B.buildAnd(ALobLoMasked, ALobLo, Mask32);
+ B.buildShl(AHibLoShiftedL, AHibLoSum1, Const32);
+ B.buildOr(DstLo, AHibLoShiftedL, ALobLoMasked);
+ B.buildLShr(ALobHiShiftedR, ALobHi, Const32);
+ B.buildLShr(AHibLoShiftedR, AHibLoSum1, Const32);
+ B.buildAdd(ShiftedSum, ALobHiShiftedR, AHibLoShiftedR);
+ B.buildAdd(DstHi, AHibHi, ShiftedSum);
+
+ if (IsSigned) {
+ // ulong mask = -resultSign;
+ // hi = hi ^ mask;
+ // lo = lo ^ mask;
+ // lo += resultSign; // Add 1 if resultSign is negative, otherwise add 0
+ // hi += (lo < resultSign); // Adjust hi if lo overflowed
+ auto Mask = MRI.createGenericVirtualRegister(DstTy);
+ B.buildNeg(Mask, ResultSign);
+ auto HiXorMask = MRI.createGenericVirtualRegister(DstTy);
+ auto LoXorMask = MRI.createGenericVirtualRegister(DstTy);
+ B.buildXor(HiXorMask, DstHi, Mask);
+ B.buildXor(LoXorMask, DstLo, Mask);
+
+ auto LoAddResult = MRI.createGenericVirtualRegister(DstTy);
+ auto LoAddShiftResult = MRI.createGenericVirtualRegister(DstTy);
+ B.buildAdd(LoAddResult, LoXorMask, ResultSign);
+ B.buildShl(LoAddShiftResult, LoAddResult, ResultSign);
+ B.buildAdd(Dst, HiXorMask, LoAddShiftResult);
+ } else {
+ B.buildCopy(Dst, DstHi);
+ }
+ MI.eraseFromParent();
+ return true;
+}
+
+// Legalizes an addrspacecast operation between pointers in non-generic address
+// spaces. Ensures that null pointers are preserved during the cast by replacing
+// the addrspacecast with a null pointer of the destination type.
+static bool legalizeGAddrspaceCast(MachineInstr &MI, MachineIRBuilder &B) {
+ auto &Dst = MI.getOperand(0);
+ LLT DstTy = B.getMRI()->getType(Dst.getReg());
+ B.buildConstant(
+ Dst, PISATargetMachine::getNullPointerValue(DstTy.getAddressSpace()));
+ MI.eraseFromParent();
+ return true;
+}
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+static bool legalizeGBitcast(MachineInstr &MI, MachineIRBuilder &B) {
+ auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
+ auto *MRI = B.getMRI();
+
+ assert(SrcTy.getSizeInBits() == DstTy.getSizeInBits());
+ if (SrcTy.isPointer() != DstTy.isPointer()) {
+ // legalize bitcast between pointers and non-pointers
+ if (SrcTy.isPointer()) {
+ // <2 x i32> G_BITCAST (p1)
+ auto IntSize = SrcTy.getSizeInBits();
+ auto IntTy = LLT::integer(IntSize);
+ auto IntReg = MRI->createGenericVirtualRegister(IntTy);
+ B.buildPtrToInt(IntReg, SrcReg);
+ if (IntTy == DstTy)
+ B.buildCopy(DstReg, IntReg);
+ else
+ B.buildBitcast(DstReg, IntReg);
+ } else {
+ // (p1) G_BITCAST <2 x i32>
+ auto IntSize = DstTy.getSizeInBits();
+ auto IntTy = LLT::integer(IntSize);
+ auto IntReg = MRI->createGenericVirtualRegister(IntTy);
+ if (IntTy == SrcTy)
+ B.buildCopy(IntReg, SrcReg);
+ else
+ B.buildBitcast(IntReg, SrcReg);
+ B.buildIntToPtr(DstReg, IntReg);
+ }
+ } else {
+ // Legalizes a bitcast operation between vector types by decomposing the
+ // source vector into scalar elements and reassembling them into the
+ // destination vector type.
+ assert(SrcTy.isVector() && DstTy.isVector());
+ unsigned SrcScalarSize = SrcTy.getScalarSizeInBits();
+ unsigned DstScalarSize = DstTy.getScalarSizeInBits();
+
+ if (SrcScalarSize == DstScalarSize) {
+ // Same scalar size (e.g. <5 x f16> to <5 x i16>): extract each element
+ // and reinterpret as the destination scalar type.
+ SmallVector<Register, 4> DstElements;
+ for (unsigned I = 0; I < SrcTy.getNumElements(); I++) {
+ auto SrcElemReg =
+ MRI->createGenericVirtualRegister(SrcTy.getScalarType());
+ B.buildExtractVectorElementConstant(SrcElemReg, SrcReg, I);
+ auto DstElemReg =
+ MRI->createGenericVirtualRegister(DstTy.getScalarType());
+ B.buildBitcast(DstElemReg, SrcElemReg);
+ DstElements.push_back(DstElemReg);
+ }
+ B.buildBuildVector(DstReg, DstElements);
+ } else {
+ // Different scalar sizes with non-divisible element counts: decompose
+ // via GCD-sized pieces.
+ unsigned CommonPieceSize = std::gcd(DstScalarSize, SrcScalarSize);
+ SmallVector<Register, 4> ScalarPieces;
+ for (unsigned SrcElemIdx = 0; SrcElemIdx < SrcTy.getNumElements();
+ SrcElemIdx++) {
+ auto SrcElemReg =
+ MRI->createGenericVirtualRegister(SrcTy.getScalarType());
+ B.buildExtractVectorElementConstant(SrcElemReg, SrcReg, SrcElemIdx);
+ auto Unmerge =
+ B.buildUnmerge(LLT::integer(CommonPieceSize), SrcElemReg);
+ for (unsigned PieceIdx = 0; PieceIdx != Unmerge->getNumOperands() - 1;
+ PieceIdx++)
+ ScalarPieces.push_back(Unmerge.getReg(PieceIdx));
+ }
+ unsigned NumPiecesPerDstElem = DstScalarSize / CommonPieceSize;
+ SmallVector<Register, 4> DstElements;
+ for (unsigned PieceStartIdx = 0; PieceStartIdx < ScalarPieces.size();
+ PieceStartIdx += NumPiecesPerDstElem) {
+ auto DstElemReg =
+ MRI->createGenericVirtualRegister(DstTy.getScalarType());
+ SmallVector<Register> DstElemPieces(
+ ScalarPieces.begin() + PieceStartIdx,
+ ScalarPieces.begin() + PieceStartIdx + NumPiecesPerDstElem);
+ B.buildMergeValues(DstElemReg, DstElemPieces);
+ DstElements.push_back(DstElemReg);
+ }
+ B.buildBuildVector(DstReg, DstElements);
+ }
+ }
+ MI.eraseFromParent();
+ return true;
+}
+
+bool PISALegalizerInfo::legalizeCustom(
+ LegalizerHelper &Helper, MachineInstr &MI,
+ LostDebugLocObserver &LocObserver) const {
+ MachineIRBuilder &B = Helper.MIRBuilder;
+ switch (MI.getOpcode()) {
+ case TargetOpcode::G_FLOG:
+ return legalizeGFlog(MI, B, numbers::ln2f);
+ case TargetOpcode::G_FLOG10:
+ return legalizeGFlog(MI, B, numbers::ln2f / numbers::ln10f);
+ case TargetOpcode::G_FEXP:
+ return legalizeGFexp(MI, B, numbers::log2e);
+ case TargetOpcode::G_FEXP10:
+ return legalizeGFexp(MI, B, numbers::ln10f / numbers::ln2f);
+ case TargetOpcode::G_FCMP:
+ return legalizeGFcmp(MI, B);
+ case TargetOpcode::G_TRUNC:
+ return legalizeGTrunc(MI, B);
+ case TargetOpcode::G_ZEXT:
+ case TargetOpcode::G_SEXT:
+ case TargetOpcode::G_ANYEXT:
+ return legalizeGExt(MI, B);
+ case TargetOpcode::G_SITOFP:
+ case TargetOpcode::G_UITOFP:
+ return legalizeGItofp(MI, B);
+ case TargetOpcode::G_STORE:
+ case TargetOpcode::G_LOAD:
+ return legalizeGLoad(MI, B, Helper);
+ case TargetOpcode::G_SEXTLOAD:
+ case TargetOpcode::G_ZEXTLOAD:
+ return legalizeGExtload(MI, B);
+ case TargetOpcode::G_FDIV:
+ return legalizeGFdiv(MI, B);
+ case TargetOpcode::G_FREM:
+ return legalizeGFrem(MI, B);
+ case TargetOpcode::G_INSERT_VECTOR_ELT:
+ return legalizeGInsertVectorElt(Helper, MI, B);
+ case TargetOpcode::G_EXTRACT_VECTOR_ELT:
+ return legalizeGExtractVectorElt(Helper, MI, B);
+ case TargetOpcode::G_INSERT_SUBVECTOR:
+ return legalizeGInsertSubvector(MI, B);
+ case TargetOpcode::G_EXTRACT_SUBVECTOR:
+ return legalizeGExtractSubvector(MI, B);
+ case TargetOpcode::G_CONCAT_VECTORS:
+ return legalizeGConcatVectors(MI, B);
+ case TargetOpcode::G_UNMERGE_VALUES:
+ return legalizeGUnmergeValues(Helper, MI, B);
+ case TargetOpcode::G_BSWAP:
+ return legalizeGBswap(MI, B);
+ case TargetOpcode::G_FPOW:
+ return legalizeGFpow(MI, B);
+ case TargetOpcode::G_FLDEXP:
+ case TargetOpcode::G_STRICT_FLDEXP:
+ return legalizeGFldexp(MI, B);
+ case TargetOpcode::G_ATOMICRMW_XCHG:
+ return legalizeGAtomicrmwXchg(MI, B);
+ case TargetOpcode::G_SHUFFLE_VECTOR:
+ return legalizeGShuffleVector(Helper, MI, B);
+ case TargetOpcode::G_IS_FPCLASS:
+ return legalizeGIsFpclass(Helper, MI, B);
+ case TargetOpcode::G_UMULH:
+ case TargetOpcode::G_SMULH:
+ return legalizeGMulh(Helper, MI, B);
+ case TargetOpcode::G_ADDRSPACE_CAST:
+ return legalizeGAddrspaceCast(MI, B);
+ case TargetOpcode::G_BITCAST:
+ return legalizeGBitcast(MI, B);
+ case TargetOpcode::G_ATOMIC_CMPXCHG:
+ case TargetOpcode::G_ATOMICRMW_ADD:
+ case TargetOpcode::G_ATOMICRMW_SUB:
+ case TargetOpcode::G_ATOMICRMW_AND:
+ case TargetOpcode::G_ATOMICRMW_OR:
+ case TargetOpcode::G_ATOMICRMW_XOR:
+ case TargetOpcode::G_ATOMICRMW_MIN:
+ case TargetOpcode::G_ATOMICRMW_MAX:
+ case TargetOpcode::G_ATOMICRMW_UMIN:
+ case TargetOpcode::G_ATOMICRMW_UMAX:
+ case TargetOpcode::G_ATOMICRMW_UINC_WRAP:
+ case TargetOpcode::G_ATOMICRMW_UDEC_WRAP:
+ case TargetOpcode::G_ATOMICRMW_FADD:
+ case TargetOpcode::G_ATOMICRMW_FSUB:
+ case TargetOpcode::G_ATOMICRMW_FMIN:
+ case TargetOpcode::G_ATOMICRMW_FMAX:
+ return legalizeGAtomicrmw(MI, B);
+ case TargetOpcode::G_FABS:
+ return legalizeFAbs(MI, B);
+ }
+ assert(0 && "unhandled!");
+ return false;
+}
diff --git a/llvm/lib/Target/PISA/PISALegalizerInfo.h b/llvm/lib/Target/PISA/PISALegalizerInfo.h
new file mode 100644
index 00000000000000..53c8fb6b8b04b1
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISALegalizerInfo.h
@@ -0,0 +1,30 @@
+//===-- PISALegalizerInfo.h --- PISA Legalization Rules -------------------===//
+//
+// 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_PISA_PISALEGALIZERINFO_H
+#define LLVM_LIB_TARGET_PISA_PISALEGALIZERINFO_H
+
+#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
+
+namespace llvm {
+
+class LLVMContext;
+class PISASubtarget;
+
+// This class provides the information for legalizing PISA instructions.
+class PISALegalizerInfo : public LegalizerInfo {
+
+public:
+ bool legalizeIntrinsic(LegalizerHelper &Helper,
+ MachineInstr &MI) const override;
+ bool legalizeCustom(LegalizerHelper &Helper, MachineInstr &MI,
+ LostDebugLocObserver &LocObserver) const override;
+ PISALegalizerInfo(const PISASubtarget &ST);
+};
+} // namespace llvm
+#endif // LLVM_LIB_TARGET_PISA_PISALEGALIZERINFO_H
diff --git a/llvm/lib/Target/PISA/PISAMachineFunctionInfo.cpp b/llvm/lib/Target/PISA/PISAMachineFunctionInfo.cpp
new file mode 100644
index 00000000000000..290f8a15d182bd
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAMachineFunctionInfo.cpp
@@ -0,0 +1,17 @@
+//===-- PISAMachineFunctionInfo.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISAMachineFunctionInfo.h"
+#include "PISASubtarget.h"
+
+using namespace llvm;
+
+PISAMachineFunctionInfo::PISAMachineFunctionInfo(const Function &F,
+ const PISASubtarget *STI) {}
+
+PISAMachineFunctionInfo::~PISAMachineFunctionInfo() = default;
diff --git a/llvm/lib/Target/PISA/PISAMachineFunctionInfo.h b/llvm/lib/Target/PISA/PISAMachineFunctionInfo.h
new file mode 100644
index 00000000000000..34c3a881769849
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAMachineFunctionInfo.h
@@ -0,0 +1,39 @@
+//===-- PISAMachineFunctionInfo.h -----------------------------------------===//
+//
+// 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_PISA_PISAMACHINEFUNCTIONINFO_H
+#define LLVM_LIB_TARGET_PISA_PISAMACHINEFUNCTIONINFO_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/CodeGen/MachineFunction.h"
+
+namespace llvm {
+class PISASubtarget;
+
+class PISAMachineFunctionInfo : public MachineFunctionInfo {
+ using ArgInfo = std::pair<unsigned, bool>;
+ DenseMap<unsigned, ArgInfo> ArgInfos;
+
+public:
+ PISAMachineFunctionInfo(const Function &F, const PISASubtarget *STI);
+ ~PISAMachineFunctionInfo() override;
+
+ ArgInfo getArgInfo(unsigned Slot) const {
+ auto I = ArgInfos.find(Slot);
+ if (I == ArgInfos.end())
+ return {0, false};
+ return I->second;
+ }
+ void setArgInfo(unsigned Slot, unsigned ArgSize, bool IsByRef) {
+ ArgInfos[Slot] = {ArgSize, IsByRef};
+ }
+};
+
+} // end namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_PISAMACHINEFUNCTIONINFO_H
diff --git a/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp b/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
new file mode 100644
index 00000000000000..b7ab3b214d7eea
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
@@ -0,0 +1,2005 @@
+//===-- lib/CodeGen/GlobalISel/PISAPostLegalizerCombiner.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/PISAMCTargetDesc.h"
+#include "PISA.h"
+#include "PISALegalizerInfo.h"
+#include "PISATargetMachine.h"
+#include "PISAUtils.h"
+#include "llvm/CodeGen/GlobalISel/Combiner.h"
+#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
+#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
+#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h"
+#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
+#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
+#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
+#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
+#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/GlobalISel/Utils.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/IntrinsicsPISA.h"
+#include "llvm/IR/PISAIntrinsicUtils.h"
+#include "llvm/Support/AtomicOrdering.h"
+#include "llvm/Target/TargetMachine.h"
+
+#define GET_GICOMBINER_DEPS
+#include "PISAGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_DEPS
+
+#define DEBUG_TYPE "pisa-postlegalizer-combiner"
+
+using namespace llvm;
+using namespace llvm::MIPatternMatch;
+
+namespace {
+
+#define GET_GICOMBINER_TYPES
+#include "PISAGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_TYPES
+
+// integer types
+constexpr ElementCount EC0 = ElementCount::getFixed(0);
+constexpr LLT I16 = LLT(LLT::Kind::INTEGER, EC0, 16);
+constexpr LLT I32 = LLT(LLT::Kind::INTEGER, EC0, 32);
+
+class PISAPostLegalizerCombinerImpl : public Combiner {
+protected:
+ const PISAPostLegalizerCombinerImplRuleConfig &RuleConfig;
+ const PISASubtarget &STI;
+
+ // TODO: Make CombinerHelper methods const.
+ mutable CombinerHelper Helper;
+
+public:
+ PISAPostLegalizerCombinerImpl(
+ MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &KB,
+ GISelCSEInfo *CSEInfo,
+ const PISAPostLegalizerCombinerImplRuleConfig &RuleConfig,
+ const PISASubtarget &STI, MachineDominatorTree *MDT,
+ const LegalizerInfo *LI);
+
+ static const char *getName() { return "PISAGenPostLegalizeGICombiner"; }
+
+ bool tryCombineAllImpl(MachineInstr &MI) const;
+ bool tryCombineAll(MachineInstr &I) const override;
+
+ void applyBuildVectorWithConstants(MachineInstr &MI) const;
+
+ bool matchCompareSelect(MachineInstr *MI) const;
+ void applyCompareSelect(MachineInstr *MI) const;
+
+ bool
+ matchFCmpInvertedCond(MachineInstr &MI,
+ std::tuple<MachineInstr *, Register> &MatchInfo) const;
+ void
+ applyFCmpInvertedCond(MachineInstr &MI,
+ std::tuple<MachineInstr *, Register> &MatchInfo) const;
+
+ bool
+ matchShlAddToMad(MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool
+ matchFDivToRcpFMul(MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+ void applyShiftOfConstants(MachineInstr *MI) const;
+
+ bool matchRedundantMovesPost(MachineInstr &MI) const;
+ void applyRedundantMovesPost(MachineInstr &MI) const;
+
+ bool matchExtractAllToBuildVector(MachineInstr &MI,
+ Register &Replacement) const;
+ void applyExtractAllToBuildVector(MachineInstr &MI,
+ Register Replacement) const;
+ bool
+ matchOrAndToBfi(MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+ bool
+ matchShiftSubToBfe(MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchAndBitfieldToBitfield(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchV2i1ZextToBfeBfi(MachineInstr &BuildVectorMI,
+ Register &BitcastInput) const;
+ void applyV2i1ZextToBfeBfi(MachineInstr &MI, Register BitcastInput) const;
+
+ bool matchAndSelect(MachineInstr &MI, Register &Replacement) const;
+ void applyAndSelect(MachineInstr &MI, Register Replacement) const;
+
+ bool matchCmpAndAllOnes(MachineInstr &MI, GISelValueTracking *VT,
+ Register &MatchInfo) const;
+ void applyCmpAndAllOnes(MachineInstr &MI, Register &MatchInfo) const;
+
+ // Fix G_SHL/G_LSHR/G_ASHR where the shift amount is not i32.
+ // PISA legalizes shifts only as {I16,I32}, {I32,I32}, {I64,I32}; rules like
+ // mul_to_shl can introduce a shift with the value type as the amount type.
+ bool matchFixIllegalShiftAmt(MachineInstr &MI) const;
+ void applyFixIllegalShiftAmt(MachineInstr &MI) const;
+
+ bool matchSinkTrunc(MachineInstr &MI,
+ std::tuple<Register, int64_t> &MatchInfo) const;
+ void applySinkTrunc(MachineInstr &MI,
+ std::tuple<Register, int64_t> &MatchInfo) const;
+
+ bool matchTruncTrunc(MachineInstr &MI, Register &MatchInfo) const;
+ void applyTruncTrunc(MachineInstr &MI, Register &MatchInfo) const;
+
+ bool matchAbsRedMaxToRedAbsMax(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ struct Reg2MatchInfo {
+ Register Reg0, Reg1;
+ };
+ bool matchBuildRegFrom2(MachineInstr &MI, Reg2MatchInfo &MatchInfo) const;
+ void applyBuildRegFrom2(MachineInstr &MI, Reg2MatchInfo &MatchInfo) const;
+
+ bool matchBuildVectorFromUnmergeLanes(
+ MachineInstr &MI, std::tuple<Register, int64_t> &MatchInfo) const;
+ void applyBuildVectorFromUnmergeLanes(
+ MachineInstr &MI, std::tuple<Register, int64_t> &MatchInfo) const;
+
+ bool
+ matchBuildVectorConcatSubvectors(MachineInstr &MI,
+ SmallVector<Register, 4> &MatchInfo) const;
+ void
+ applyBuildVectorConcatSubvectors(MachineInstr &MI,
+ SmallVector<Register, 4> &MatchInfo) const;
+
+ bool
+ matchShiftTrueFalse(MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchAddInt8Reduction(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchUnmergeBitcastBuildVectorToBitcast(MachineInstr &MI,
+ Register &MatchInfo) const;
+ void applyUnmergeBitcastBuildVectorToBitcast(MachineInstr &MI,
+ Register &MatchInfo) const;
+
+ bool matchRedundantFence(MachineInstr &MI, MachineInstr *&PrevFence) const;
+ void applyRedundantFence(MachineInstr &MI, MachineInstr *&PrevFence) const;
+
+ bool matchMinMaxIdentityFold(MachineInstr &MI, Register &MatchInfo) const;
+
+ bool
+ matchExtractSubvectorBuildVector(MachineInstr &MI,
+ SmallVector<Register, 8> &MatchInfo) const;
+ void
+ applyExtractSubvectorBuildVector(MachineInstr &MI,
+ SmallVector<Register, 8> &MatchInfo) const;
+
+ bool matchMergeAdjacentFences(MachineInstr &MI,
+ MachineInstr *&PrevFence) const;
+ void applyMergeAdjacentFences(MachineInstr &MI,
+ MachineInstr *&PrevFence) const;
+
+ bool
+ matchExtractSubvectorPartial(MachineInstr &MI,
+ SmallVector<MachineInstr *, 8> &MatchInfo) const;
+ void applyExtractSubvectorPartial(
+ MachineInstr &MI, const SmallVector<MachineInstr *, 8> &MatchInfo) const;
+
+private:
+#define GET_GICOMBINER_CLASS_MEMBERS
+#include "PISAGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CLASS_MEMBERS
+};
+
+#define GET_GICOMBINER_IMPL
+#include "PISAGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_IMPL
+
+PISAPostLegalizerCombinerImpl::PISAPostLegalizerCombinerImpl(
+ MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &KB,
+ GISelCSEInfo *CSEInfo,
+ const PISAPostLegalizerCombinerImplRuleConfig &RuleConfig,
+ const PISASubtarget &STI, MachineDominatorTree *MDT,
+ const LegalizerInfo *LI)
+ : Combiner(MF, CInfo, &KB, CSEInfo), RuleConfig(RuleConfig), STI(STI),
+ Helper(Observer, B, /*IsPreLegalize=*/false, &KB, MDT, LI),
+#define GET_GICOMBINER_CONSTRUCTOR_INITS
+#include "PISAGenPostLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CONSTRUCTOR_INITS
+{
+}
+
+bool PISAPostLegalizerCombinerImpl::tryCombineAll(MachineInstr &MI) const {
+ return tryCombineAllImpl(MI);
+}
+
+void PISAPostLegalizerCombinerImpl::applyBuildVectorWithConstants(
+ MachineInstr &MI) const {
+ auto EltSize = MRI.getType(MI.getOperand(1).getReg()).getSizeInBits();
+ auto Size = EltSize * (MI.getNumOperands() - 1);
+ assert((Size <= 64) && "vector size too large");
+
+ auto NewReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
+ uint64_t Value = 0;
+
+ for (unsigned I = MI.getNumOperands() - 1; I > 0; I--) {
+ auto Reg = MI.getOperand(I).getReg();
+ auto CValue = getAnyConstantVRegValWithLookThrough(Reg, MRI);
+ assert(CValue.has_value() && "expected const vreg val");
+ APInt IValue = CValue->Value;
+ Value <<= EltSize;
+ Value |= IValue.getZExtValue();
+ }
+
+ B.buildConstant(NewReg, Value);
+ B.buildBitcast(MI.getOperand(0), NewReg);
+ MI.eraseFromParent();
+}
+
+// i1 C = G_CMP ne i? A, 0
+// i? S = G_SELECT i1 C, i? LHS, i? RHS
+// => S = sel.? LHS, RHS, A
+// ... or ...
+// i1 C = G_CMP eq i? A, 0
+// i? S = G_SELECT i1 C, i? LHS, i? RHS
+// => S = sel.? RHS, LHS, A
+bool PISAPostLegalizerCombinerImpl::matchCompareSelect(MachineInstr *MI) const {
+ auto *SelectMI = MI;
+ auto *CmpMI = getDefIgnoringCopies(SelectMI->getOperand(1).getReg(), MRI);
+ if (CmpMI->getOpcode() == TargetOpcode::G_ICMP) {
+ auto CC = CmpMI->getOperand(1).getPredicate();
+ auto CReg = CmpMI->getOperand(3).getReg();
+ auto CVal = getIConstantVRegValWithLookThrough(CReg, MRI);
+ if (CVal.has_value() && (CVal->Value == 0) &&
+ ((CC == CmpInst::ICMP_EQ) || (CC == CmpInst::ICMP_NE))) {
+ auto CmpTy = MRI.getType(CmpMI->getOperand(2).getReg());
+ auto SelTy = MRI.getType(SelectMI->getOperand(2).getReg());
+ if ((CmpTy.isScalar() && !CmpTy.isPointer()) &&
+ (SelTy.isScalar() && !SelTy.isPointer()) &&
+ CmpTy.getScalarSizeInBits() == SelTy.getScalarSizeInBits()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+void PISAPostLegalizerCombinerImpl::applyCompareSelect(MachineInstr *MI) const {
+ auto *SelectMI = MI;
+ auto *CmpMI = getDefIgnoringCopies(SelectMI->getOperand(1).getReg(), MRI);
+ auto CC = CmpMI->getOperand(1).getPredicate();
+ auto DstReg = SelectMI->getOperand(0).getReg();
+ auto MIB = B.buildInstr(PISA::G_PISA_SELECT)
+ .addDef(DstReg)
+ .add(CmpMI->getOperand(2));
+ if (CC == CmpInst::ICMP_NE)
+ MIB.add(SelectMI->getOperand(2)).add(SelectMI->getOperand(3));
+ else
+ MIB.add(SelectMI->getOperand(3)).add(SelectMI->getOperand(2));
+ MI->eraseFromParent();
+}
+
+// %9:_(s1) = G_FCMP floatpred(oge), %1:reg32b(s32), %2:reg32b
+// %22:_(s32) = G_CONSTANT i32 0
+// %23:_(s32) = G_CONSTANT i32 -1
+// %10:_(s32) = G_SELECT %9:_(s1), %23:_, %22:_
+// %11:_(s32) = G_CONSTANT i32 -1
+// %12:_(s32) = G_XOR %10:_, %11:_
+// %13:_(s32) = G_CONSTANT i32 -1
+// %6:_(s1) = G_ICMP intpred(eq), %12:_(s32), %13:_
+// G_BRCOND %6:_(s1), %bb.4
+// G_BR %bb.3
+// => %9:_(s1) = G_FCMP floatpred(oge), %1:reg32b(s32), %2:reg32b
+// => G_BRCOND %6:_(s1), %bb.3
+// => G_BR %bb.4
+bool PISAPostLegalizerCombinerImpl::matchFCmpInvertedCond(
+ MachineInstr &MI, std::tuple<MachineInstr *, Register> &MatchInfo) const {
+ assert(MI.getOpcode() == TargetOpcode::G_BR);
+
+ MachineInstr *BrCondMI;
+ MachineBasicBlock *MBB = MI.getParent();
+ MachineBasicBlock::iterator BrIt(MI);
+
+ if (BrIt == MBB->begin())
+ return false;
+ assert(std::next(BrIt) == MBB->end() && "expected G_BR to be a terminator");
+
+ // G_BRCOND %6:_(s1), %bb.4
+ BrCondMI = &*std::prev(BrIt);
+ if (BrCondMI->getOpcode() != TargetOpcode::G_BRCOND)
+ return false;
+
+ auto *CmpMI = MRI.getVRegDef(BrCondMI->getOperand(0).getReg());
+
+ Register FCC;
+ CmpInst::Predicate Pred;
+ // Match either:
+ // icmp eq (xor (select fcc, -1, 0), -1), -1 => NOT fcc
+ // icmp eq (select fcc, 0, -1), -1 => NOT fcc
+ // icmp eq (select fcc, -1, 0), 0 => NOT fcc (produced by
+ // not_cmp_fold simplifying the xor form above)
+ bool Matched =
+ mi_match(
+ CmpMI, MRI,
+ m_GICmp(m_Pred(Pred),
+ m_GXor(m_GISelect(m_Reg(FCC), m_AllOnesInt(), m_ZeroInt()),
+ m_AllOnesInt()),
+ m_AllOnesInt())) ||
+ mi_match(CmpMI, MRI,
+ m_GICmp(m_Pred(Pred),
+ m_GISelect(m_Reg(FCC), m_ZeroInt(), m_AllOnesInt()),
+ m_AllOnesInt())) ||
+ mi_match(CmpMI, MRI,
+ m_GICmp(m_Pred(Pred),
+ m_GISelect(m_Reg(FCC), m_AllOnesInt(), m_ZeroInt()),
+ m_ZeroInt()));
+ if (!Matched || Pred != CmpInst::ICMP_EQ)
+ return false;
+
+ // %9:_(s1) = G_FCMP floatpred(oge), %1:reg32b(s32), %2:reg32b
+ auto *FCmpMI = MRI.getVRegDef(FCC);
+ if (FCmpMI->getOpcode() != TargetOpcode::G_FCMP)
+ return false;
+
+ auto FCCTy = MRI.getType(FCC);
+ if (!(FCCTy.isScalar() && FCCTy.getScalarSizeInBits() == 1))
+ return false;
+
+ MatchInfo = std::make_tuple(BrCondMI, FCC);
+ return true;
+}
+void PISAPostLegalizerCombinerImpl::applyFCmpInvertedCond(
+ MachineInstr &MI, std::tuple<MachineInstr *, Register> &MatchInfo) const {
+ MachineInstr *BrCond;
+ Register FCC;
+ std::tie(BrCond, FCC) = MatchInfo;
+ auto *BrCondBB = BrCond->getOperand(1).getMBB();
+ auto *BrBB = MI.getOperand(0).getMBB();
+
+ Observer.changingInstr(MI);
+ MI.getOperand(0).setMBB(BrCondBB);
+ Observer.changedInstr(MI);
+
+ Observer.changingInstr(*BrCond);
+ BrCond->getOperand(0).setReg(FCC);
+ BrCond->getOperand(1).setMBB(BrBB);
+ Observer.changedInstr(*BrCond);
+}
+
+//%2:registers(s32) = G_SHL %0:reg32b, 2
+//%4:registers(s32) = G_ADD %2:registers, %3:registers
+// => %4:registers(s32) = G_PISA_SMAD %0:reg32b, 4, %3:registers
+bool PISAPostLegalizerCombinerImpl::matchShlAddToMad(
+ MachineInstr &AddMI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+
+ Register MulReg, AddReg;
+ APInt Shift;
+
+ if (!mi_match(&AddMI, MRI,
+ m_GAdd(m_GShl(m_Reg(MulReg), m_ICst(Shift)), m_Reg(AddReg))))
+ return false;
+
+ auto Ty = MRI.getType(MulReg);
+ if (Shift.getZExtValue() >= Ty.getSizeInBits())
+ return false;
+
+ MatchInfo = [Ty, &AddMI, AddReg, MulReg,
+ Shift = std::move(Shift)](MachineIRBuilder &B) {
+ auto MulOp2 = B.buildConstant(
+ Ty, APInt::getOneBitSet(Ty.getSizeInBits(), Shift.getZExtValue()));
+ B.buildIntrinsic(Intrinsic::pisa_smad, AddMI.getOperand(0).getReg())
+ .addUse(MulReg)
+ .addUse(MulOp2.getReg(0))
+ .addUse(AddReg);
+ };
+ return true;
+}
+
+// arcp G_FDIV %0, %1 that has at least one G_FADD user
+// =>
+// %rcp = arcp G_INTRINSIC intrinsic(@llvm.pisa.frcp), %1
+// %result = arcp G_FMUL %0, %rcp
+//
+// This transformation exposes G_FMUL for potential FMA fusion with the G_FADD
+// users.
+bool PISAPostLegalizerCombinerImpl::matchFDivToRcpFMul(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ // Check if this is a floating-point divide with FmArcp flag
+ if (MI.getOpcode() != TargetOpcode::G_FDIV)
+ return false;
+
+ // Must have the FmArcp flag to allow reciprocal approximation
+ if (!MI.getFlag(MachineInstr::FmArcp))
+ return false;
+
+ Register DstReg = MI.getOperand(0).getReg();
+ Register Src0Reg = MI.getOperand(1).getReg();
+ Register Src1Reg = MI.getOperand(2).getReg();
+ LLT DstTy = MRI.getType(DstReg);
+
+ // Only handle 32-bit float for now
+ if (!DstTy.isScalar() || DstTy.getSizeInBits() != 32)
+ return false;
+
+ // Check if the result of this FDIV is used by at least one FADD/FSUB
+ // This ensures we only apply the transformation when FMA fusion is possible
+ if (!llvm::any_of(MRI.use_instructions(DstReg), [](const MachineInstr &Use) {
+ return Use.getOpcode() == TargetOpcode::G_FADD ||
+ Use.getOpcode() == TargetOpcode::G_FSUB;
+ }))
+ return false;
+
+ uint16_t Flags = MI.getFlags();
+
+ MatchInfo = [=](MachineIRBuilder &B) {
+ auto Rcp = B.buildIntrinsic(Intrinsic::pisa_frcp, {DstTy})
+ .addUse(Src1Reg)
+ .setMIFlags(Flags);
+
+ // A / B -> A * RCP(B)
+ B.buildFMul(DstReg, Src0Reg, Rcp, Flags);
+ };
+ return true;
+}
+
+void PISAPostLegalizerCombinerImpl::applyShiftOfConstants(
+ MachineInstr *MI) const {
+ auto [SrcMI, SrcRegIdx] =
+ PISA::getDefIgnoringBitcasts(MI->getOperand(1).getReg(), MRI);
+ auto [ShiftMI, ShiftRegIdx] =
+ PISA::getDefIgnoringBitcasts(MI->getOperand(2).getReg(), MRI);
+
+ const auto *CImm = SrcMI->getOperand(1).getCImm();
+ auto Shift = ShiftMI->getOperand(1).getCImm()->getZExtValue();
+ int64_t NewVal = 0;
+ switch (MI->getOpcode()) {
+ default:
+ assert(0 && "unhandled shift opcode");
+ break;
+ case TargetOpcode::G_SHL:
+ NewVal = CImm->getZExtValue() << Shift;
+ break;
+ case TargetOpcode::G_ASHR:
+ NewVal = CImm->getSExtValue() >> Shift;
+ break;
+ case TargetOpcode::G_LSHR:
+ NewVal = CImm->getZExtValue() >> Shift;
+ break;
+ }
+ B.buildConstant(MI->getOperand(0), NewVal);
+ MI->eraseFromParent();
+}
+
+// A(<2x16>) = G_BITCAST ARG(32)
+// B(16), C(16) = G_UNMERGE_VALUES A(<2x16)
+// D(<2x16>) = G_BUILD_VECTOR B, C
+// E(32) = G_BITCAST D(<2x16>)
+// => E(32) = COPY ARG(32)
+bool PISAPostLegalizerCombinerImpl::matchRedundantMovesPost(
+ MachineInstr &MI) const {
+ auto &BitcastMI = MI;
+ auto DstReg = BitcastMI.getOperand(0).getReg();
+ auto SrcReg = BitcastMI.getOperand(1).getReg();
+ if (MRI.getType(DstReg).isVector() || !MRI.getType(SrcReg).isVector())
+ return false;
+
+ auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ if (BuildVecMI.getOpcode() != TargetOpcode::G_BUILD_VECTOR)
+ return false;
+
+ Register UnmergeReg = 0;
+ for (unsigned I = 1; I < BuildVecMI.getNumOperands(); I++) {
+ auto &UnmergeMI =
+ *getDefIgnoringCopies(BuildVecMI.getOperand(I).getReg(), MRI);
+ if (UnmergeMI.getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
+ return false;
+ if (I == 1) {
+ UnmergeReg =
+ UnmergeMI.getOperand(UnmergeMI.getNumOperands() - 1).getReg();
+ } else if (UnmergeReg !=
+ UnmergeMI.getOperand(UnmergeMI.getNumOperands() - 1).getReg()) {
+ // must extract indices from the same vector
+ return false;
+ }
+ // order to extracted operands should match
+ auto VecEltReg = BuildVecMI.getOperand(I).getReg();
+ if (UnmergeMI.getOperand(I - 1).getReg() != VecEltReg)
+ return false;
+ }
+
+ auto &SrcBitcastMI = *getDefIgnoringCopies(UnmergeReg, MRI);
+ if (SrcBitcastMI.getOpcode() != TargetOpcode::G_BITCAST)
+ return false;
+
+ auto SrcBitcastReg = SrcBitcastMI.getOperand(1).getReg();
+ if (MRI.getType(DstReg).getSizeInBits() !=
+ MRI.getType(SrcBitcastReg).getSizeInBits())
+ return false;
+ return true;
+}
+
+void PISAPostLegalizerCombinerImpl::applyRedundantMovesPost(
+ MachineInstr &MI) const {
+ auto &BitcastMI = MI;
+ auto DstReg = BitcastMI.getOperand(0).getReg();
+ auto SrcReg = BitcastMI.getOperand(1).getReg();
+ auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ auto &UnmergeMI =
+ *getDefIgnoringCopies(BuildVecMI.getOperand(1).getReg(), MRI);
+ auto &SrcBitcastMI = *getDefIgnoringCopies(
+ UnmergeMI.getOperand(UnmergeMI.getNumOperands() - 1).getReg(), MRI);
+ auto SrcBitcastReg = SrcBitcastMI.getOperand(1).getReg();
+ B.buildCopy(DstReg, SrcBitcastReg);
+ MI.eraseFromParent();
+}
+
+// A(s16) = G_EXTRACT_VECTOR_ELT ARG(<N x s16), 0
+// B(s16) = G_EXTRACT_VECTOR_ELT ARG(<N x s16), 1
+// ...
+// X(s16) = G_EXTRACT_VECTOR_ELT ARG(<N x s16>), N-1
+// Y(<N x s16>) = G_BUILD_VECTOR A, B, ..., X
+// => Y(<N x s16>) = COPY ARG(<N x s16>)
+//
+// Also matches the equivalent unpack-via-unmerge idiom:
+// A, B, ..., X = G_UNMERGE_VALUES ARG(<N x s16>)
+// Y(<N x s16>) = G_BUILD_VECTOR A, B, ..., X
+// => Y(<N x s16>) = COPY ARG(<N x s16>)
+bool PISAPostLegalizerCombinerImpl::matchExtractAllToBuildVector(
+ MachineInstr &MI, Register &Replacement) const {
+ auto &BuildVecMI = MI;
+ auto DstReg = BuildVecMI.getOperand(0).getReg();
+ auto SrcReg = BuildVecMI.getOperand(1).getReg();
+
+ if (MRI.getType(SrcReg).isVector())
+ return false;
+
+ // Branch A: all operands are G_EXTRACT_VECTOR_ELT from the same vector with
+ // sequential constant indices 0..N-1.
+ Register SrcVecReg;
+ bool AllExtracts = true;
+ for (unsigned I = 1; I < BuildVecMI.getNumOperands(); I++) {
+ auto EltReg = BuildVecMI.getOperand(I).getReg();
+ auto &ExtractMI = *getDefIgnoringCopies(EltReg, MRI);
+ if (ExtractMI.getOpcode() != TargetOpcode::G_EXTRACT_VECTOR_ELT) {
+ AllExtracts = false;
+ break;
+ }
+
+ if (I == 1)
+ SrcVecReg = ExtractMI.getOperand(1).getReg();
+ else if (SrcVecReg != ExtractMI.getOperand(1).getReg()) {
+ AllExtracts = false;
+ break;
+ }
+
+ auto IndexReg = ExtractMI.getOperand(2).getReg();
+ auto CValue = getIConstantVRegValWithLookThrough(IndexReg, MRI);
+ if (!CValue.has_value() || CValue->Value != (I - 1)) {
+ AllExtracts = false;
+ break;
+ }
+ }
+ if (AllExtracts) {
+ if (MRI.getType(DstReg) != MRI.getType(SrcVecReg))
+ return false;
+ Replacement = SrcVecReg;
+ return true;
+ }
+
+ // Branch B: all operands are the sequential defs of a single G_UNMERGE_VALUES
+ // whose source vector type matches the G_BUILD_VECTOR dest type.
+ unsigned NumElts = BuildVecMI.getNumOperands() - 1;
+ auto FirstDefAndReg =
+ getDefSrcRegIgnoringCopies(BuildVecMI.getOperand(1).getReg(), MRI);
+ if (!FirstDefAndReg)
+ return false;
+ auto *UnmergeMI = FirstDefAndReg->MI;
+ if (UnmergeMI->getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
+ return false;
+ if (UnmergeMI->getNumOperands() - 1 != NumElts)
+ return false; // G_UNMERGE_VALUES produces a different number of elements
+ auto UnmergeSrcReg = UnmergeMI->getOperand(NumElts).getReg();
+ if (MRI.getType(DstReg) != MRI.getType(UnmergeSrcReg))
+ return false;
+
+ for (unsigned I = 0; I < NumElts; I++) {
+ auto DefAndReg =
+ getDefSrcRegIgnoringCopies(BuildVecMI.getOperand(I + 1).getReg(), MRI);
+ if (!DefAndReg || DefAndReg->MI != UnmergeMI)
+ return false; // different producer
+ if (DefAndReg->Reg != UnmergeMI->getOperand(I).getReg())
+ return false; // out-of-order element pickup
+ }
+
+ Replacement = UnmergeSrcReg;
+ return true;
+}
+void PISAPostLegalizerCombinerImpl::applyExtractAllToBuildVector(
+ MachineInstr &MI, Register Replacement) const {
+ auto &BuildVecMI = MI;
+ auto DstReg = BuildVecMI.getOperand(0).getReg();
+ B.buildCopy(DstReg, Replacement);
+ MI.eraseFromParent();
+}
+
+// This function tries to match following pattern:
+// mask = ((1 << width) - 1) << offset;
+// dst = ((data << offset) & mask) | (base & ~mask);
+// -> bfi %data, %base, %width, %offset
+// Currently only the variant with constant masks is supported.
+bool PISAPostLegalizerCombinerImpl::matchOrAndToBfi(
+ MachineInstr &OrMI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ // BFI supports only 32 bits of width, start with this simple check to
+ // exclude unsupported shifts early.
+ auto BitWidth = MRI.getType(OrMI.getOperand(0).getReg()).getSizeInBits();
+ if (BitWidth != 32)
+ return false;
+
+ int64_t MaskA, MaskB;
+ Register A, B;
+ if (!mi_match(OrMI, MRI,
+ m_GOr(m_GAnd(m_Reg(A), m_ICst(MaskA)),
+ m_GAnd(m_Reg(B), m_ICst(MaskB)))))
+ return false;
+ if (MaskA != ~MaskB)
+ return false;
+
+ Register ShiftedData, Base;
+ unsigned Offset = 0, Width = 0;
+ if (isShiftedMask_32(MaskA, Offset, Width)) {
+ ShiftedData = A;
+ Base = B;
+ } else if (isShiftedMask_32(MaskB, Offset, Width)) {
+ ShiftedData = B;
+ Base = A;
+ } else {
+ return false;
+ }
+
+ auto [ShiftedDataMI, ShiftedDataRegIdx] =
+ PISA::getDefIgnoringBitcasts(ShiftedData, MRI);
+
+ Register Data;
+ if (!mi_match(ShiftedDataMI, MRI,
+ m_GShl(m_Reg(Data), m_SpecificICst(Offset))))
+ return false;
+
+ MatchInfo = [Data, Base, Width, Offset, &OrMI](MachineIRBuilder &B) {
+ auto WidthReg = B.buildConstant(I32, Width);
+ auto OffsetReg = B.buildConstant(I32, Offset);
+ B.buildIntrinsic(Intrinsic::pisa_bfi, {OrMI.getOperand(0)})
+ .addUse(Base)
+ .addUse(Data)
+ .addUse(WidthReg.getReg(0))
+ .addUse(OffsetReg.getReg(0));
+ };
+ return true;
+}
+
+// This function tries to match following pattern:
+// diff = bitwidth - width;
+// shift = diff - offset
+// dst = ((data << shift) >> diff);
+// -> bfe %data, %width, %offset
+bool PISAPostLegalizerCombinerImpl::matchShiftSubToBfe(
+ MachineInstr &ShrMI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ // BFE supports only 32 bits of width, start with this simple check to
+ // exclude unsupported shifts early.
+ auto BitWidth = MRI.getType(ShrMI.getOperand(0).getReg()).getSizeInBits();
+ if (BitWidth != 32)
+ return false;
+
+ auto MakeApplyHandler = [&ShrMI](Register Data, Register Width,
+ Register Offset) {
+ return [&ShrMI, Data, Width, Offset](MachineIRBuilder &B) {
+ B.buildIntrinsic(Intrinsic::pisa_ubfe, {ShrMI.getOperand(0)})
+ .addUse(Data)
+ .addUse(Width)
+ .addUse(Offset);
+ };
+ };
+
+ constexpr int64_t ShiftInstMask = 0x1f;
+
+ Register Data, Diff, Offset;
+ if (mi_match(ShrMI, MRI,
+ m_GLShr(m_GShl(m_Reg(Data), m_GSub(m_Reg(Diff), m_Reg(Offset))),
+ m_Reg(Diff)))) {
+ Register Width;
+ if (mi_match(Diff, MRI,
+ m_GSub(m_SpecificICst((int64_t)BitWidth), m_Reg(Width))) &&
+ VT->getKnownBits(Width).countMaxTrailingOnes() <= 6 &&
+ VT->getKnownBits(Offset).countMaxTrailingOnes() <= 5) {
+ // s32 %bitwidth = G_CONSTANT i32 32
+ // s32 %andwidth = G_AND i32 %width, <=63
+ // s32 %andoffset = G_AND i32 %offset, <=31
+ // s32 %diff = G_SUB i32 %bitwidth, %andwidth
+ // s32 %shift = G_SUB i32 %diff, %andoffset
+ // s32 %shl = G_SHL i32 %data, %shift
+ // s32 %shr = G_LSHR i32 %shl, %diff
+ // =>
+ // bfe %data %andwidth %andoffset
+ MatchInfo = MakeApplyHandler(Data, Width, Offset);
+ return true;
+ }
+ } else if (mi_match(
+ ShrMI, MRI,
+ m_GLShr(m_GShl(m_Reg(Data),
+ m_GAnd(m_GSub(m_Reg(Diff), m_Reg(Offset)),
+ m_SpecificICst(ShiftInstMask))),
+ m_GAnd(m_Reg(Diff), m_SpecificICst(ShiftInstMask))))) {
+ Register Width;
+ if (mi_match(Diff, MRI,
+ m_GSub(m_SpecificICst((int64_t)BitWidth), m_Reg(Width)))) {
+ if (VT->getKnownBits(Width).countMaxTrailingOnes() <= 5) {
+ // s32 %bitwidth = G_CONSTANT i32 32
+ // s32 %andwidth = G_AND i32 %width, <=31
+ // s32 %diff = G_SUB i32 %bitwidth, %andwidth
+ // s32 %shift = G_SUB i32 %diff, %offset
+ // s32 %andshift = G_AND i32 %shift, 31
+ // s32 %shl = G_SHL i32 %data, %andshift
+ // s32 %anddiff = G_AND i32 %diff, 31
+ // s32 %shr = G_LSHR i32 %shl, %anddiff
+ // =>
+ // s32 %andwidth = G_AND i32 %width, <=31
+ // bfe %data %andwidth %offset
+ MatchInfo = MakeApplyHandler(Data, Width, Offset);
+ return true;
+ }
+ // Masking %diff with 0x1f is equivalent to masking %width with 0x1f.
+ // We have to explicitly mask width with 0x1f to preserve behavior as
+ // bfe masks width with 0x3f by default.
+ // s32 %bitwidth = G_CONSTANT i32 32
+ // s32 %diff = G_SUB i32 %bitwidth, %width
+ // s32 %shift = G_SUB i32 %diff, %offset
+ // s32 %andshift = G_AND i32 %shift, 31
+ // s32 %shl = G_SHL i32 %data, %andshift
+ // s32 %anddiff = G_AND i32 %diff, 31
+ // s32 %shr = G_LSHR i32 %shl, %anddiff
+ // =>
+ // s32 %andwidth = G_AND i32 %width, 31
+ // bfe %data %andwidth %offset
+ auto WidthType = MRI.getType(Width);
+ MatchInfo = [Data, Width, WidthType, Offset,
+ &ShrMI](MachineIRBuilder &B) {
+ auto Mask = B.buildConstant(WidthType, ShiftInstMask);
+ auto MaskedWidth = B.buildAnd(WidthType, Width, Mask);
+ B.buildIntrinsic(Intrinsic::pisa_ubfe, {ShrMI.getOperand(0)})
+ .addUse(Data)
+ .addUse(MaskedWidth->getOperand(0).getReg())
+ .addUse(Offset);
+ };
+ return true;
+ }
+ }
+ return false;
+}
+
+// s32 %maskedwidth = G_AND %width, 63
+// s32 %maskedoffset = G_AND %offset, 31
+// bfe %data %maskedwidth %maskedoffset
+// =>
+// bfe %data %width %offset
+bool PISAPostLegalizerCombinerImpl::matchAndBitfieldToBitfield(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ Register Data, Width, Offset;
+ Intrinsic::ID II;
+ if (auto *MIIntrinsic = dyn_cast<GIntrinsic>(&MI)) {
+ II = MIIntrinsic->getIntrinsicID();
+ if (II == Intrinsic::pisa_ubfe) {
+ Data = MI.getOperand(2).getReg();
+ Width = MI.getOperand(3).getReg();
+ Offset = MI.getOperand(4).getReg();
+ } else if (II == Intrinsic::pisa_bfi) {
+ Data = MI.getOperand(2).getReg();
+ Width = MI.getOperand(4).getReg();
+ Offset = MI.getOperand(5).getReg();
+ } else {
+ return false;
+ }
+ } else {
+ // G_[SU]BFX, convert it to pisa_[su]bfe
+ II = MI.getOpcode() == TargetOpcode::G_UBFX ? Intrinsic::pisa_ubfe
+ : Intrinsic::pisa_sbfe;
+ Data = MI.getOperand(1).getReg();
+ Width = MI.getOperand(3).getReg();
+ Offset = MI.getOperand(2).getReg();
+ }
+
+ Register UnmaskedWidth{}, UnmaskedOffset{};
+ if (VT->getKnownBits(Width).countMaxTrailingOnes() >= 6 &&
+ mi_match(Width, MRI, m_GAnd(m_Reg(UnmaskedWidth), m_Reg()))) {
+ Width = UnmaskedWidth;
+ }
+ if (VT->getKnownBits(Offset).countMaxTrailingOnes() >= 5 &&
+ mi_match(Offset, MRI, m_GAnd(m_Reg(UnmaskedOffset), m_Reg()))) {
+ Offset = UnmaskedOffset;
+ }
+ if (!UnmaskedWidth.isValid() && !UnmaskedOffset.isValid())
+ return false; // No match, exit.
+
+ if (II == Intrinsic::pisa_ubfe || II == Intrinsic::pisa_sbfe) {
+ MatchInfo = [&MI, II, Data, Width, Offset](MachineIRBuilder &B) {
+ B.buildIntrinsic(II, {MI.getOperand(0)})
+ .addUse(Data)
+ .addUse(Width)
+ .addUse(Offset);
+ };
+ } else {
+ MatchInfo = [&MI, Data, Width, Offset](MachineIRBuilder &B) {
+ B.buildIntrinsic(Intrinsic::pisa_bfi, {MI.getOperand(0)})
+ .addUse(Data)
+ .addUse(MI.getOperand(3).getReg())
+ .addUse(Width)
+ .addUse(Offset);
+ };
+ }
+ return true;
+}
+
+// This transforms an unoptimal pattern for fcmp + zext i1->i8 on <2 x i8>
+// result types (e.g. from code that uses native i8 element vectors):
+// %14:_(<2 x s16>) = G_BITCAST %CmpRes:_(s32)
+// %20:_(s16), %21:_(s16) = G_UNMERGE_VALUES %14:_(<2 x s16>)
+// %22:_(s8) = G_TRUNC %20:_(s16)
+// %23:_(s8) = G_TRUNC %21:_(s16)
+// %17:_(<2 x s8>) = G_BUILD_VECTOR %22:_(s8), %23:_(s8)
+// %28:_(s16) = G_CONSTANT i16 257
+// %19:_(<2 x s8>) = G_BITCAST %28:_(s16)
+// %24:_(s16) = G_BITCAST %17:_(<2 x s8>)
+// %25:_(s16) = G_BITCAST %19:_(<2 x s8>)
+// %26:_(s16) = G_AND %24:_, %25:_
+// %res:_(<2 x s8>) = G_BITCAST %26:_(s16)
+// => bfe %1, %CmpRes, 1, 0
+// bfe %2, %CmpRes, 1, 16
+// bfi %3, %1, %2, 8
+// trunc.16.32 %res, %3
+// If the only user of the result is a G_STORE, we modify it to store
+// an s16 instead of <2xs8>. Otherwise, we insert a G_BITCAST.
+// Note: sub-byte types (i1, i4) are now promoted to i16, so this pattern
+// applies only to code that explicitly uses native <2 x i8> element types.
+bool PISAPostLegalizerCombinerImpl::matchV2i1ZextToBfeBfi(
+ MachineInstr &BitcastMI, Register &BitcastInput) const {
+
+ auto VectorElementSize =
+ MRI.getType(BitcastMI.getOperand(0).getReg()).getScalarSizeInBits();
+ // We only support an i8 zext
+ if (VectorElementSize != 8)
+ return false;
+
+ // Match automatically up to the G_AND instructions.
+ // The mask constant 257 (= 0x0101 = {1,1} as two i8 lanes) may appear in
+ // two forms depending on whether build_vector_with_constants has already
+ // folded G_BUILD_VECTOR(i8 1, i8 1) -> G_CONSTANT i16 257:
+ // original: G_BITCAST(G_BITCAST(G_CONSTANT i?? 257))
+ // folded: G_CONSTANT i16 257 (bare constant, no bitcasts)
+ Register UpperCmpRes, LowerCmpRes;
+ bool Matched =
+ mi_match(BitcastMI, MRI,
+ m_GBitcast(m_GAnd(
+ m_GBitcast(m_GBuildVector(m_GTrunc(m_Reg(UpperCmpRes)),
+ m_GTrunc(m_Reg(LowerCmpRes)))),
+ m_GBitcast(m_GBitcast(m_SpecificICst(257)))))) ||
+ mi_match(BitcastMI, MRI,
+ m_GBitcast(m_GAnd(
+ m_GBitcast(m_GBuildVector(m_GTrunc(m_Reg(UpperCmpRes)),
+ m_GTrunc(m_Reg(LowerCmpRes)))),
+ m_SpecificICst(257))));
+ if (!Matched)
+ return false;
+
+ /*
+ Verify that
+ - the extracted halves of the comparison result are part of
+ G_UNMERGE_VALUES instructions
+ - the extracted halves are at the correct position in the unmerge
+ instructions, e.g. the UpperCmpRes reg should be at index 0
+ - both G_UNMERGE_VALUES instructions use the same input
+ Note that this way, we both support two unmerges with two unused values (see
+ code example), and one unmerge instruction with both values being used
+ (i.e. UpperUnmerge =?= LowerUnmerge)
+ */
+ auto *UpperUnmergeInstr = MRI.getUniqueVRegDef(UpperCmpRes);
+ auto *LowerUnmergeInstr = MRI.getUniqueVRegDef(LowerCmpRes);
+ if (!UpperUnmergeInstr || !LowerUnmergeInstr ||
+ UpperUnmergeInstr->getOpcode() != TargetOpcode::G_UNMERGE_VALUES ||
+ LowerUnmergeInstr->getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
+ return false;
+
+ if (UpperUnmergeInstr->getOperand(0).getReg() != UpperCmpRes)
+ return false;
+
+ if (LowerUnmergeInstr->getOperand(1).getReg() != LowerCmpRes)
+ return false;
+
+ auto BitcastRes = UpperUnmergeInstr->getOperand(2);
+ if (!BitcastRes.isIdenticalTo(LowerUnmergeInstr->getOperand(2)))
+ return false;
+
+ // Verify that this inst is a G_BITCAST, and get the input register
+ auto *BitcastInst = MRI.getUniqueVRegDef(BitcastRes.getReg());
+ if (!BitcastInst || BitcastInst->getOpcode() != TargetOpcode::G_BITCAST)
+ return false;
+
+ BitcastInput = BitcastInst->getOperand(1).getReg();
+ return true;
+}
+
+void PISAPostLegalizerCombinerImpl::applyV2i1ZextToBfeBfi(
+ MachineInstr &BitcastMI, Register BitcastInput) const {
+
+ auto *MRI = B.getMRI();
+ auto Bfe1Res = MRI->createGenericVirtualRegister(I32);
+ auto Bfe2Res = MRI->createGenericVirtualRegister(I32);
+ auto BfiRes = MRI->createGenericVirtualRegister(I32);
+
+ auto Zero = B.buildConstant(I32, 0);
+ auto One = B.buildConstant(I32, 1);
+ auto Eight = B.buildConstant(I32, 8);
+ auto Sixteen = B.buildConstant(I32, 16);
+
+ B.buildIntrinsic(Intrinsic::pisa_ubfe, {DstOp(Bfe1Res)})
+ .addUse(BitcastInput)
+ .addUse(One.getReg(0)) // Width
+ .addUse(Zero.getReg(0)); // Offset
+
+ B.buildIntrinsic(Intrinsic::pisa_ubfe, {DstOp(Bfe2Res)})
+ .addUse(BitcastInput)
+ .addUse(One.getReg(0)) // Width
+ .addUse(Sixteen.getReg(0)); // Offset
+
+ B.buildIntrinsic(Intrinsic::pisa_bfi, {DstOp(BfiRes)})
+ .addUse(Bfe1Res)
+ .addUse(Bfe2Res)
+ .addUse(Eight.getReg(0)) // Width
+ .addUse(Eight.getReg(0)); // Offset
+
+ /*
+ Peephole optimisation:
+ The example input given above continues with a G_STORE instr
+ as the only user of our newly created G_TRUNC:
+ [...]
+ %52:_(s8) = G_TRUNC %48:_(s16)
+ %10:_(<2 x s8>) = G_BUILD_VECTOR %51:_(s8), %52:_(s8)
+ G_STORE %10:_(s16), %4:_(p1)
+
+ If we would just bitcast the G_TRUNC result back to
+ <2 x s8> to match all expected types in that case, the resulting
+ PISA code inserts an unnecessary mov into a different register
+
+ To avoid that, check if the next user is only a G_STORE instruction.
+ In that case, we can just modify the type of the stored register
+ to s16 and change the MachineMemOperand's type to s16 as well.
+
+ In all other cases, insert a G_BITCAST to <2 x s8>.
+ */
+
+ auto StoreInput = BitcastMI.getOperand(0).getReg();
+ if (MRI->hasOneNonDBGUse(StoreInput) &&
+ MRI->use_instr_nodbg_begin(StoreInput)->getOpcode() ==
+ TargetOpcode::G_STORE) {
+ // Our only use is a G_STORE
+ B.buildInstr(TargetOpcode::G_TRUNC).addDef(StoreInput).addUse(BfiRes);
+
+ MRI->setType(StoreInput, I16);
+ for (auto *MemOp : MRI->use_instr_nodbg_begin(StoreInput)->memoperands())
+ MemOp->setType(I16);
+ } else {
+ // We either have multiple users or the following user is not a G_STORE
+ // Insert a bitcast to <2 x s8> so that nothing breaks
+ auto TruncRes = MRI->createGenericVirtualRegister(I16);
+ B.buildInstr(TargetOpcode::G_TRUNC).addDef(TruncRes).addUse(BfiRes);
+
+ B.buildInstr(TargetOpcode::G_BITCAST).addDef(StoreInput).addUse(TruncRes);
+ }
+ BitcastMI.eraseFromParent();
+}
+
+// %374:_(s16) = G_CONSTANT i16 1
+// %375:_(s16) = G_AND %321:_, %374:_
+// %421:_(s16) = G_CONSTANT i16 0
+// %422:_(s16) = G_CONSTANT i16 1
+// %405:_(s16) = G_PISA_SELECT %375:_, %422:_, %421:_
+// => %374:_(s16) = G_CONSTANT i16 1
+// => %375:_(s16) = G_AND %321:_, %374:_
+// => %405:_(s16) = COPY %375
+bool PISAPostLegalizerCombinerImpl::matchAndSelect(
+ MachineInstr &MI, Register &Replacement) const {
+ auto &SelectMI = MI;
+
+ auto Const0 =
+ getIConstantVRegValWithLookThrough(SelectMI.getOperand(3).getReg(), MRI);
+ auto Const1 =
+ getIConstantVRegValWithLookThrough(SelectMI.getOperand(2).getReg(), MRI);
+ if (!Const0.has_value() || !Const1.has_value())
+ return false;
+ if ((Const0->Value != 0) || (Const1->Value != 1))
+ return false;
+
+ auto &AndMI = *getDefIgnoringCopies(SelectMI.getOperand(1).getReg(), MRI);
+ if (AndMI.getOpcode() != TargetOpcode::G_AND)
+ return false;
+ auto AndConst =
+ getIConstantVRegValWithLookThrough(AndMI.getOperand(2).getReg(), MRI);
+ if (!AndConst.has_value() || (AndConst->Value != 1))
+ return false;
+
+ if (MRI.getType(AndMI.getOperand(0).getReg()) !=
+ MRI.getType(SelectMI.getOperand(0).getReg()))
+ return false;
+
+ Replacement = AndMI.getOperand(0).getReg();
+ return true;
+}
+void PISAPostLegalizerCombinerImpl::applyAndSelect(MachineInstr &MI,
+ Register Replacement) const {
+
+ B.buildCopy(MI.getOperand(0).getReg(), Replacement);
+ MI.eraseFromParent();
+}
+
+// %23:_(s32) = G_CONSTANT i32 8
+// %17:_(s8) = G_TRUNC %1:reg32b(s32)
+// %24:_(s32) = G_LSHR %1:reg32b, %23:_(s32)
+// %25:_(s8) = G_TRUNC %24:_(s32)
+// %28:_(s8) = G_TRUNC %2:reg32b(s32)
+// %31:_(s32) = G_LSHR %2:reg32b, %23:_(s32)
+// %32:_(s8) = G_TRUNC %31:_(s32)
+///%149:_(<4 x s8>) = G_BUILD_VECTOR %17:_(s8), %25:_(s8), %28:_(s8), %32:_(s8)
+// %141:_(s32) = G_BITCAST %149:_(<4 x s8>)
+// => %A = G_AND %1, 0xFFFF
+// => %B = G_SHL %2, 16
+// => %141 = G_OR %A, %B
+bool PISAPostLegalizerCombinerImpl::matchBuildRegFrom2(
+ MachineInstr &MI, Reg2MatchInfo &MatchInfo) const {
+ auto &BitcastMI = MI;
+
+ auto DstTy = MRI.getType(BitcastMI.getOperand(0).getReg());
+ if (DstTy.isVector() || (DstTy.getScalarSizeInBits() != 32))
+ return false;
+ auto SrcReg = BitcastMI.getOperand(1).getReg();
+ auto SrcTy = MRI.getType(SrcReg);
+ if (!SrcTy.isVector() || (SrcTy.getScalarSizeInBits() != 8))
+ return false;
+ auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ if (BuildVecMI.getOpcode() != TargetOpcode::G_BUILD_VECTOR)
+ return false;
+
+ for (auto I = 0; I < 2; I++) {
+ auto LoReg = BuildVecMI.getOperand(1 + (I * 2)).getReg();
+ auto HiReg = BuildVecMI.getOperand(2 + (I * 2)).getReg();
+ auto &LoTruncMI = *getDefIgnoringCopies(LoReg, MRI);
+ auto &HiTruncMI = *getDefIgnoringCopies(HiReg, MRI);
+ if (LoTruncMI.getOpcode() != TargetOpcode::G_TRUNC)
+ return false;
+ if (HiTruncMI.getOpcode() != TargetOpcode::G_TRUNC)
+ return false;
+ auto &HiShiftMI =
+ *getDefIgnoringCopies(HiTruncMI.getOperand(1).getReg(), MRI);
+ if (HiShiftMI.getOpcode() != TargetOpcode::G_LSHR)
+ return false;
+ if (LoTruncMI.getOperand(1).getReg() != HiShiftMI.getOperand(1).getReg())
+ return false;
+ auto ShiftConst = getIConstantVRegValWithLookThrough(
+ HiShiftMI.getOperand(2).getReg(), MRI);
+ if (!ShiftConst.has_value() || (ShiftConst->Value != 8))
+ return false;
+ if (I == 0)
+ MatchInfo.Reg0 = LoTruncMI.getOperand(1).getReg();
+ else
+ MatchInfo.Reg1 = LoTruncMI.getOperand(1).getReg();
+ }
+ return true;
+}
+void PISAPostLegalizerCombinerImpl::applyBuildRegFrom2(
+ MachineInstr &MI, Reg2MatchInfo &MatchInfo) const {
+ auto MaskReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ auto Mask = B.buildConstant(MaskReg, 0xFFFF);
+ auto ShiftReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ auto Shift = B.buildConstant(ShiftReg, 16);
+
+ auto AReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ auto BReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+
+ auto RegSize = MRI.getType(MatchInfo.Reg0).getSizeInBits();
+ assert((RegSize == 16 || RegSize == 32 || RegSize == 64) &&
+ "only supporting 16/32/64bit registers");
+ Register ASrcReg, BSrcReg;
+ if (RegSize == 16) {
+ ASrcReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ BSrcReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ B.buildZExt(ASrcReg, MatchInfo.Reg0);
+ B.buildZExt(BSrcReg, MatchInfo.Reg1);
+ } else if (RegSize == 64) {
+ ASrcReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ BSrcReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ B.buildTrunc(ASrcReg, MatchInfo.Reg0);
+ B.buildTrunc(BSrcReg, MatchInfo.Reg1);
+ } else {
+ ASrcReg = MatchInfo.Reg0;
+ BSrcReg = MatchInfo.Reg1;
+ }
+ B.buildAnd(AReg, ASrcReg, Mask);
+ B.buildShl(BReg, BSrcReg, Shift);
+ B.buildOr(MI.getOperand(0).getReg(), AReg, BReg);
+ MI.eraseFromParent();
+}
+
+// Match a G_BUILD_VECTOR whose two elements are consecutive lanes (base,
+// base+1) of a single wider source vector, produced by G_UNMERGE_VALUES.
+// Such a build is really a sub-vector slice of that source; recording
+// (source, base) lets applyBuildVectorFromUnmergeLanes rewrite it to a
+// G_EXTRACT_SUBVECTOR, which ISel lowers to a composite sub-register COPY.
+bool PISAPostLegalizerCombinerImpl::matchBuildVectorFromUnmergeLanes(
+ MachineInstr &MI, std::tuple<Register, int64_t> &MatchInfo) const {
+ Register DstReg = MI.getOperand(0).getReg();
+ LLT DstTy = MRI.getType(DstReg);
+ // Only nameable 2-element pairs (map to .xy / .zw) are handled.
+ if (!DstTy.isVector() || DstTy.getNumElements() != 2)
+ return false;
+ const unsigned NumElts = 2;
+
+ const MachineInstr *Unmerge = nullptr;
+ Register SrcReg;
+ int64_t BaseLane = -1;
+ for (unsigned I = 0; I < NumElts; ++I) {
+ auto Def = getDefSrcRegIgnoringCopies(MI.getOperand(1 + I).getReg(), MRI);
+ if (!Def)
+ return false;
+ const MachineInstr *D = Def->MI;
+ if (D->getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
+ return false;
+ // The source operand of G_UNMERGE_VALUES is its last operand; the ones
+ // before it are the per-lane results.
+ const unsigned NumLanes = D->getNumOperands() - 1;
+ int Lane = -1;
+ for (unsigned J = 0; J < NumLanes; ++J)
+ if (D->getOperand(J).getReg() == Def->Reg) {
+ Lane = (int)J;
+ break;
+ }
+ if (Lane < 0)
+ return false;
+ if (I == 0) {
+ Unmerge = D;
+ SrcReg = D->getOperand(NumLanes).getReg();
+ BaseLane = Lane;
+ } else if (D != Unmerge || Lane != BaseLane + (int)I) {
+ return false;
+ }
+ }
+
+ // Base must form a nameable composite sub-register (.xy at 0, .zw at 2).
+ if (BaseLane != 0 && BaseLane != 2)
+ return false;
+
+ LLT SrcTy = MRI.getType(SrcReg);
+ // The .xy/.zw composite sub-registers are only defined on 4-lane register
+ // classes (Reg*bx4). On wider vectors (v5-v8, v16, ...) the elements have
+ // per-lane sub-registers only; a .zw view there is not a real sub-register
+ // and would be mis-lowered. Restrict to <=4-lane sources (the intended
+ // 3-4-element scope) and let wider vectors keep the element-wise path.
+ if (!SrcTy.isVector() || SrcTy.getElementType() != DstTy.getElementType() ||
+ SrcTy.getNumElements() <= NumElts || SrcTy.getNumElements() > 4 ||
+ (uint64_t)BaseLane + NumElts > SrcTy.getNumElements())
+ return false;
+
+ MatchInfo = std::make_tuple(SrcReg, BaseLane);
+ return true;
+}
+
+void PISAPostLegalizerCombinerImpl::applyBuildVectorFromUnmergeLanes(
+ MachineInstr &MI, std::tuple<Register, int64_t> &MatchInfo) const {
+ auto [SrcReg, BaseLane] = MatchInfo;
+ B.buildExtractSubvector(MI.getOperand(0).getReg(), SrcReg, BaseLane);
+ MI.eraseFromParent();
+}
+
+// Match a 4-element G_BUILD_VECTOR that concatenates two whole 2-element
+// sub-vectors: elements [0,1] are lanes 0,1 of source A and elements [2,3] are
+// lanes 0,1 of source B (each produced by G_UNMERGE_VALUES). Such a build is a
+// concatenation; recording (A, B) lets the apply rewrite it to
+// G_CONCAT_VECTORS, which ISel writes into the .xy / .zw slices directly.
+bool PISAPostLegalizerCombinerImpl::matchBuildVectorConcatSubvectors(
+ MachineInstr &MI, SmallVector<Register, 4> &MatchInfo) const {
+ Register DstReg = MI.getOperand(0).getReg();
+ LLT DstTy = MRI.getType(DstReg);
+ if (!DstTy.isVector() || DstTy.getNumElements() != 4)
+ return false;
+ const unsigned M = 2; // sub-vector width -> nameable .xy / .zw
+ const unsigned K = DstTy.getNumElements() / M;
+ MatchInfo.clear();
+ for (unsigned G = 0; G < K; ++G) {
+ const MachineInstr *Unmerge = nullptr;
+ Register SrcReg;
+ for (unsigned J = 0; J < M; ++J) {
+ auto Def = getDefSrcRegIgnoringCopies(
+ MI.getOperand(1 + G * M + J).getReg(), MRI);
+ if (!Def)
+ return false;
+ const MachineInstr *D = Def->MI;
+ if (D->getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
+ return false;
+ const unsigned NumLanes = D->getNumOperands() - 1;
+ int Lane = -1;
+ for (unsigned L = 0; L < NumLanes; ++L)
+ if (D->getOperand(L).getReg() == Def->Reg) {
+ Lane = (int)L;
+ break;
+ }
+ // Each source must be consumed whole and in order (lane J at position J).
+ if (Lane != (int)J)
+ return false;
+ if (J == 0) {
+ Unmerge = D;
+ SrcReg = D->getOperand(NumLanes).getReg();
+ } else if (D != Unmerge) {
+ return false;
+ }
+ }
+ LLT SrcTy = MRI.getType(SrcReg);
+ if (!SrcTy.isVector() || SrcTy.getNumElements() != M ||
+ SrcTy.getElementType() != DstTy.getElementType())
+ return false;
+ MatchInfo.push_back(SrcReg);
+ }
+ return MatchInfo.size() == K;
+}
+
+void PISAPostLegalizerCombinerImpl::applyBuildVectorConcatSubvectors(
+ MachineInstr &MI, SmallVector<Register, 4> &MatchInfo) const {
+ B.buildConcatVectors(MI.getOperand(0).getReg(), MatchInfo);
+ MI.eraseFromParent();
+}
+
+// if NumSignBits == BitWidth:
+// %24:_(s32) = G_CONSTANT i32 0
+// %25:_(s32) = G_CONSTANT i32 1
+// %26:_(s32) = G_AND %18:_, %25:_
+// %9:_(s1) = G_ICMP intpred(ne), %26:_(s32), %24:_
+// => %24:_(s32) = G_CONSTANT i32 0
+// %26:_(s32) = COPY %18:_(s32)
+// %9:_(s1) = G_ICMP intpred(ne), %26:_(s32), %24:_
+bool PISAPostLegalizerCombinerImpl::matchCmpAndAllOnes(
+ MachineInstr &MI, GISelValueTracking *VT, Register &MatchInfo) const {
+ Register SrcReg;
+ CmpInst::Predicate Pred;
+ if (!mi_match(MI, MRI,
+ m_GICmp(m_Pred(Pred), m_GAnd(m_Reg(SrcReg), m_SpecificICst(1)),
+ m_SpecificICst(0))) ||
+ (Pred != CmpInst::ICMP_NE))
+ return false;
+
+ auto BitWidth = MRI.getType(SrcReg).getSizeInBits();
+ if (BitWidth < 16)
+ return false;
+
+ if (VT->computeNumSignBits(SrcReg) < BitWidth)
+ return false;
+
+ MatchInfo = SrcReg;
+ return true;
+}
+void PISAPostLegalizerCombinerImpl::applyCmpAndAllOnes(
+ MachineInstr &MI, Register &MatchInfo) const {
+ Observer.changingInstr(MI);
+ MI.getOperand(2).setReg(MatchInfo);
+ Observer.changedInstr(MI);
+}
+
+// %11:_(s16) = G_TRUNC %8:_(s32)
+// %12:_(s16) = G_CONSTANT i16 1
+// %13:_(s16) = G_AND %11:_, %12:_
+//=> %12:_(s32) = G_CONSTANT i32 1
+// %13:_(s32) = G_AND %8:_, %12:_
+// %11:_(s16) = G_TRUNC %13:_(s32)
+bool PISAPostLegalizerCombinerImpl::matchFixIllegalShiftAmt(
+ MachineInstr &MI) const {
+ assert(MI.getOpcode() == TargetOpcode::G_SHL ||
+ MI.getOpcode() == TargetOpcode::G_LSHR ||
+ MI.getOpcode() == TargetOpcode::G_ASHR);
+ return MRI.getType(MI.getOperand(2).getReg()) != I32;
+}
+
+void PISAPostLegalizerCombinerImpl::applyFixIllegalShiftAmt(
+ MachineInstr &MI) const {
+ Register ShAmtReg = MI.getOperand(2).getReg();
+ LLT ShAmtTy = MRI.getType(ShAmtReg);
+ MachineIRBuilder MIB(MI);
+ Register NewShAmt = (ShAmtTy.getSizeInBits() > 32)
+ ? MIB.buildTrunc(I32, ShAmtReg).getReg(0)
+ : MIB.buildZExt(I32, ShAmtReg).getReg(0);
+ Observer.changingInstr(MI);
+ MI.getOperand(2).setReg(NewShAmt);
+ Observer.changedInstr(MI);
+}
+
+bool PISAPostLegalizerCombinerImpl::matchSinkTrunc(
+ MachineInstr &MI, std::tuple<Register, int64_t> &MatchInfo) const {
+ const unsigned Opcode = MI.getOpcode();
+ assert(Opcode == TargetOpcode::G_AND || Opcode == TargetOpcode::G_OR ||
+ Opcode == TargetOpcode::G_XOR);
+
+ int64_t Mask;
+ Register SrcReg;
+ if (!mi_match(MI, MRI,
+ m_BinOp(Opcode, m_GTrunc(m_Reg(SrcReg)), m_ICst(Mask))))
+ return false;
+
+ // We don't want to create 64-bit boolean operations
+ auto BitWidth = MRI.getType(SrcReg).getSizeInBits();
+ if (BitWidth > 32)
+ return false;
+
+ MatchInfo = std::make_tuple(SrcReg, Mask);
+ return true;
+}
+void PISAPostLegalizerCombinerImpl::applySinkTrunc(
+ MachineInstr &MI, std::tuple<Register, int64_t> &MatchInfo) const {
+ int64_t Mask;
+ Register SrcReg;
+ std::tie(SrcReg, Mask) = MatchInfo;
+ auto SrcTy = MRI.getType(SrcReg);
+ auto MaskReg = MRI.createGenericVirtualRegister(SrcTy);
+ auto DstReg = MRI.createGenericVirtualRegister(SrcTy);
+ B.buildConstant(MaskReg, Mask);
+ B.buildInstr(MI.getOpcode()).addDef(DstReg).addUse(SrcReg).addUse(MaskReg);
+ B.buildInstr(TargetOpcode::G_TRUNC)
+ .addDef(MI.getOperand(0).getReg())
+ .addUse(DstReg);
+ MI.eraseFromParent();
+}
+
+// %22:_(s16) = G_TRUNC %26:_(s32)
+// %2:_(s8) = G_TRUNC %22:_(s16)
+//=> %2:_(s8) = G_TRUNC %26:_(s32)
+bool PISAPostLegalizerCombinerImpl::matchTruncTrunc(MachineInstr &MI,
+ Register &MatchInfo) const {
+ Register SrcReg;
+ if (!mi_match(MI, MRI, m_GTrunc(m_GTrunc(m_Reg(SrcReg)))))
+ return false;
+
+ MatchInfo = SrcReg;
+ return true;
+}
+void PISAPostLegalizerCombinerImpl::applyTruncTrunc(MachineInstr &MI,
+ Register &MatchInfo) const {
+ Observer.changingInstr(MI);
+ MI.getOperand(1).setReg(MatchInfo);
+ Observer.changedInstr(MI);
+}
+
+// %1:registers(s32) = G_ABS %0:reg32b
+// %4:registers(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.pisa.ired),
+// IRedOp::UMAX, %1:registers(s32), %2:reg32b(s32), %3:reg32b(s32)
+// => %1:registers(s32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.pisa.ired),
+// IRedOp::ABSMAX, %0:registers(s32), %2:reg32b(s32), %3:reg32b(s32)
+bool PISAPostLegalizerCombinerImpl::matchAbsRedMaxToRedAbsMax(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ unsigned AbsOpcode;
+ unsigned TargetInstOpCode = 0;
+ unsigned TargetOpType = 0;
+ unsigned IntrID = 0;
+ switch (MI.getOpcode()) {
+ default:
+ return false;
+ case TargetOpcode::G_INTRINSIC_CONVERGENT: {
+ auto II = cast<GIntrinsic>(MI).getIntrinsicID();
+ if (II == Intrinsic::pisa_ired) {
+ if (MI.getOperand(2).getImm() != pisa::IRedOp::UMAX)
+ return false;
+ AbsOpcode = TargetOpcode::G_ABS;
+ IntrID = Intrinsic::pisa_ired;
+ TargetOpType = pisa::IRedOp::ABSMAX;
+ } else if (II == Intrinsic::pisa_fred) {
+ if (MI.getOperand(2).getImm() != pisa::FRedOp::MAX)
+ return false;
+ AbsOpcode = TargetOpcode::G_INTRINSIC;
+ IntrID = Intrinsic::pisa_fred;
+ TargetOpType = pisa::FRedOp::ABSMAX;
+ } else
+ return false;
+ break;
+ }
+ }
+ unsigned SrcOpIdx = IntrID ? 3 : 2;
+ auto [SrcMI, SrcRegIdx] =
+ PISA::getDefIgnoringBitcasts(MI.getOperand(SrcOpIdx).getReg(), MRI);
+ MachineInstr *SrcMIPtr = SrcMI;
+ if (SrcMIPtr->getOpcode() != AbsOpcode)
+ return false;
+ if (AbsOpcode == TargetOpcode::G_INTRINSIC &&
+ cast<GIntrinsic>(*SrcMIPtr).getIntrinsicID() != Intrinsic::pisa_fabs)
+ return false;
+ // For G_INTRINSIC, source reg is operand 2; for generic opcodes, operand 1.
+ unsigned AbsSrcIdx = AbsOpcode == TargetOpcode::G_INTRINSIC ? 2 : 1;
+ if (IntrID)
+ MatchInfo = [SrcMIPtr, AbsSrcIdx, &MI, IntrID,
+ TargetOpType](MachineIRBuilder &B) {
+ auto MIB = B.buildIntrinsic(IntrID, {MI.getOperand(0)});
+ MIB.addImm(TargetOpType);
+ MIB.addUse(SrcMIPtr->getOperand(AbsSrcIdx).getReg());
+ MIB.addUse(MI.getOperand(4).getReg());
+ MIB.addUse(MI.getOperand(5).getReg());
+ // pisa_fred carries a trailing i1 nanp operand; pisa_ired does not.
+ if (IntrID == Intrinsic::pisa_fred)
+ MIB.add(MI.getOperand(6)); // copy nanp from original
+ MIB.setMIFlags(MI.getFlags());
+ };
+ else
+ MatchInfo = [SrcMIPtr, &MI, TargetInstOpCode,
+ TargetOpType](MachineIRBuilder &B) {
+ B.buildInstr(TargetInstOpCode)
+ .addDef(MI.getOperand(0).getReg())
+ .addImm(TargetOpType)
+ .addUse(SrcMIPtr->getOperand(1).getReg())
+ .addUse(MI.getOperand(3).getReg())
+ .addUse(MI.getOperand(4).getReg())
+ .add(MI.getOperand(5)) // copy nanp from original
+ .setMIFlags(MI.getFlags());
+ };
+ return true;
+}
+
+// %45:_(s32) = G_CONSTANT i32 -1
+// %60:_(s32) = G_CONSTANT i32 0
+// %46:_(s32) = G_SELECT %10:_(s1), %45:_, %60:_
+// %47:_(s32) = G_SELECT %35:_(s1), %45:_, %60:_
+// %48:_(s32) = G_AND %46:_, %47:_
+// %59:_(s32) = G_CONSTANT i32 31
+// %58:_(s32) = G_SHL %48:_, %59:_(s32)
+// %14:_(s32) = G_ASHR %58:_, %59:_(s32)
+// => %14:_(s32) = G_AND %46:_, %47:_
+bool PISAPostLegalizerCombinerImpl::matchShiftTrueFalse(
+ MachineInstr &AShrMI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+
+ // Result can be only all zeros or all ones. But if sources are also only
+ // all zeros/ones, then shl/ashr is a redundant artifact from legalization.
+
+ auto DstReg = AShrMI.getOperand(0).getReg();
+ auto DstTy = MRI.getType(DstReg);
+ auto ShiftAmount = DstTy.getScalarSizeInBits() - 1;
+
+ Register SrcReg;
+ if (!mi_match(AShrMI, MRI,
+ m_GAShr(m_GShl(m_Reg(SrcReg), m_SpecificICst(ShiftAmount)),
+ m_SpecificICst(ShiftAmount))))
+ return false;
+
+ std::function<bool(Register)> Match = [&](Register Reg) {
+ auto CValue = getIConstantVRegValWithLookThrough(Reg, MRI);
+ if (CValue.has_value())
+ return CValue->Value.isZero() || CValue->Value.isAllOnes();
+
+ auto *MI = getDefIgnoringCopies(Reg, MRI);
+ if (!MI)
+ return false;
+
+ switch (MI->getOpcode()) {
+ case TargetOpcode::G_TRUNC:
+ return Match(MI->getOperand(1).getReg());
+ case TargetOpcode::G_AND:
+ case TargetOpcode::G_OR:
+ case TargetOpcode::G_XOR:
+ return Match(MI->getOperand(1).getReg()) &&
+ Match(MI->getOperand(2).getReg());
+ case TargetOpcode::G_SELECT:
+ case PISA::G_PISA_SELECT:
+ return Match(MI->getOperand(2).getReg()) &&
+ Match(MI->getOperand(3).getReg());
+ default:
+ return false;
+ }
+ };
+
+ if (!Match(SrcReg))
+ return false;
+
+ MatchInfo = [DstReg, SrcReg, this](MachineIRBuilder &B) {
+ if (MRI.hasOneNonDBGUse(SrcReg)) {
+ auto *MI = getDefIgnoringCopies(SrcReg, MRI);
+ if (MI) {
+ MI->getOperand(0).setReg(DstReg);
+ return;
+ }
+ }
+
+ B.buildCopy(DstReg, SrcReg);
+ };
+ return true;
+}
+
+// %54:_(s8), %55:_(s8), %56:_(s8), %57:_(s8) = G_UNMERGE_VALUES %21:_(<4 x s8>)
+// %39:_(s16) = G_ANYEXT %54:_(s8)
+// %40:_(s16) = G_ANYEXT %55:_(s8)
+// %41:_(s16) = G_ADD %39:_, %40:_
+// %36:_(s16) = G_ANYEXT %56:_(s8)
+// %37:_(s16) = G_ANYEXT %57:_(s8)
+// %38:_(s16) = G_ADD %36:_, %37:_
+// %35:_(s16) = G_ADD %41:_, %38:_
+// => %40:_(s32) = G_INTRINSIC intrinsic(@llvm.pisa.dp4a.uu), 0, %21, 0x01010101
+// %35:_(s16) = G_TRUNC %40:_(s32)
+bool PISAPostLegalizerCombinerImpl::matchAddInt8Reduction(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+
+ auto Dst = MI.getOperand(0).getReg();
+ auto DstTy = MRI.getType(Dst);
+ if (!DstTy.isScalar() || DstTy.getSizeInBits() > 32)
+ return false;
+
+ std::array<Register, 4> Srcs;
+ // Helper to match anyext or zext of a register
+ auto MatchExt = [](Register &Reg) {
+ return m_any_of(m_GAnyExt(m_Reg(Reg)), m_GZExt(m_Reg(Reg)));
+ };
+ if (!mi_match(MI, MRI,
+ m_any_of(m_GAdd(m_OneNonDBGUse(m_GAdd(MatchExt(Srcs[0]),
+ MatchExt(Srcs[1]))),
+ m_OneNonDBGUse(m_GAdd(MatchExt(Srcs[2]),
+ MatchExt(Srcs[3])))),
+ m_GAdd(m_OneNonDBGUse(m_GAdd(
+ m_OneNonDBGUse(m_GAdd(MatchExt(Srcs[0]),
+ MatchExt(Srcs[1]))),
+ MatchExt(Srcs[2]))),
+ MatchExt(Srcs[3])))))
+ return false;
+
+ // All registers must be unique.
+ SmallSet<Register, 4> Unique(Srcs.begin(), Srcs.end());
+ if (Unique.size() < Srcs.size())
+ return false;
+
+ // All register must be i8 from the same unmerge instruction.
+ auto GetUnmerge = [=](Register &Reg) {
+ auto *UnmergeMI = MRI.getVRegDef(Reg);
+ return UnmergeMI->getOpcode() == TargetOpcode::G_UNMERGE_VALUES ? UnmergeMI
+ : nullptr;
+ };
+
+ MachineInstr *UnmergeMI = GetUnmerge(Srcs[0]);
+ if (!UnmergeMI)
+ return false;
+ for (unsigned I = 1; I < Srcs.size(); ++I) {
+ if (UnmergeMI != GetUnmerge(Srcs[I]))
+ return false;
+ }
+
+ auto VectorReg =
+ UnmergeMI->getOperand(UnmergeMI->getNumOperands() - 1).getReg();
+ if (MRI.getType(VectorReg) !=
+ LLT::vector(ElementCount::getFixed(4), LLT::integer(8)))
+ return false;
+
+ MatchInfo = [=](MachineIRBuilder &B) {
+ auto Acc = MRI.createGenericVirtualRegister(I32);
+ auto X = MRI.createGenericVirtualRegister(I32);
+ auto Y = MRI.createGenericVirtualRegister(I32);
+
+ B.buildConstant(Acc, 0);
+ B.buildBitcast(X, VectorReg);
+ B.buildConstant(Y, 0x01010101);
+
+ // Check if dst can be directly used, or result must be truncated.
+ if (DstTy == I32) {
+ B.buildIntrinsic(Intrinsic::pisa_dp4a_uu, {DstOp(Dst)})
+ .addUse(Acc)
+ .addUse(X)
+ .addUse(Y)
+ .addImm(0);
+ } else {
+ auto Dst32 = MRI.createGenericVirtualRegister(I32);
+ B.buildIntrinsic(Intrinsic::pisa_dp4a_uu, {DstOp(Dst32)})
+ .addUse(Acc)
+ .addUse(X)
+ .addUse(Y)
+ .addImm(0);
+ B.buildTrunc(Dst, Dst32);
+ }
+ };
+ return true;
+}
+
+// %370:_(i16), %371:_(i16) = G_UNMERGE_VALUES %366:_(<2 x i16>)
+// %378:_(f16) = G_BITCAST %370:_(i16)
+// %379:_(f16) = G_BITCAST %371:_(i16)
+// %37:_(<2 x f16>) = G_BUILD_VECTOR %378:_(f16), %379:_(f16)
+// => %37:_(<2 x f16>) = G_BITCAST %366:_(<2 x i16>)
+bool PISAPostLegalizerCombinerImpl::matchUnmergeBitcastBuildVectorToBitcast(
+ MachineInstr &MI, Register &MatchInfo) const {
+ Register UnmergeSrc;
+ for (unsigned I = 1, E = MI.getNumOperands(); I < E; I++) {
+ auto [SrcMI, SrcRegIdx] =
+ PISA::getDefIgnoringBitcasts(MI.getOperand(I).getReg(), MRI, true);
+ if (!SrcMI || SrcMI->getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
+ return false;
+ if (SrcMI->getNumOperands() != MI.getNumOperands())
+ return false;
+ auto SrcReg = SrcMI->getOperand(SrcMI->getNumOperands() - 1).getReg();
+ if (SrcRegIdx != (I - 1))
+ return false;
+ if (I == 1)
+ UnmergeSrc = SrcReg;
+ else if (UnmergeSrc != SrcReg)
+ return false;
+ }
+ MatchInfo = UnmergeSrc;
+ return true;
+}
+void PISAPostLegalizerCombinerImpl::applyUnmergeBitcastBuildVectorToBitcast(
+ MachineInstr &MI, Register &MatchInfo) const {
+ auto DstReg = MI.getOperand(0).getReg();
+ if (MRI.getType(DstReg) != MRI.getType(MatchInfo))
+ B.buildBitcast(MI.getOperand(0).getReg(), MatchInfo);
+ else
+ B.buildCopy(MI.getOperand(0).getReg(), MatchInfo);
+ MI.eraseFromParent();
+}
+
+// Return true if the G_FENCE corresponds to a subgroup or workitem scope.
+// These are execution barriers, not memory ordering fences, and should
+// be preserved.
+static bool isSubgroupFence(const MachineInstr &MI) {
+ auto ScopeID = static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
+ if (ScopeID == SyncScope::SingleThread)
+ return true;
+ auto &Ctx = MI.getMF()->getFunction().getContext();
+ if (auto Name = Ctx.getSyncScopeName(ScopeID))
+ return Name->starts_with("subgroup") || Name->starts_with("workitem");
+ return false;
+}
+
+// Extract the base scope (e.g. "workgroup") from a sync scope name that may
+// include an address space suffix (e.g. "workgroup-shared", "workgroup-global",
+// "workgroup-generic", or just "workgroup").
+static StringRef getBaseScopeName(const MachineInstr &MI) {
+ auto ScopeID = static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
+ auto &Ctx = MI.getMF()->getFunction().getContext();
+ auto ScopeName = Ctx.getSyncScopeName(ScopeID);
+ if (!ScopeName)
+ return StringRef();
+
+ for (StringRef Suffix : {"-shared", "-global", "-generic"})
+ if (ScopeName->ends_with(Suffix))
+ return ScopeName->drop_back(Suffix.size());
+ return *ScopeName;
+}
+
+bool PISAPostLegalizerCombinerImpl::matchRedundantFence(
+ MachineInstr &MI, MachineInstr *&PrevFence) const {
+ assert(MI.getOpcode() == TargetOpcode::G_FENCE);
+
+ if (isSubgroupFence(MI))
+ return false;
+
+ unsigned Scope = MI.getOperand(1).getImm();
+
+ auto It = MI.getIterator();
+ const MachineBasicBlock &MBB = *MI.getParent();
+ while (It != MBB.begin()) {
+ --It;
+ if (It->getOpcode() == TargetOpcode::G_FENCE) {
+ if (It->getOperand(1).getImm() == Scope) {
+ PrevFence = &*It;
+ return true;
+ }
+ // TODO: A wider scope subsumes a narrower one on the same addrspace.
+ // E.g. for `fence.global.gpu; fence.global.workgroup` we could drop
+ // the workgroup fence.
+ return false;
+ }
+ // TODO: Skip memory instructions with scope that do not interfere.
+ if (It->mayLoadOrStore() || It->hasUnmodeledSideEffects())
+ return false;
+ }
+ return false;
+}
+
+void PISAPostLegalizerCombinerImpl::applyRedundantFence(
+ MachineInstr &MI, MachineInstr *&PrevFence) const {
+ assert(PrevFence && "Expected a preceding compatible fence");
+
+ auto CurOrd = static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
+ auto PrevOrd = static_cast<AtomicOrdering>(PrevFence->getOperand(0).getImm());
+
+ // Pick the stronger ordering (e.g. choose seq_cst over acquire).
+ // Acquire and release cannot be compared; use acq_rel for the
+ // resulting fence.
+ auto Merged = getMergedAtomicOrdering(PrevOrd, CurOrd);
+ PrevFence->getOperand(0).setImm(static_cast<int64_t>(Merged));
+ MI.eraseFromParent();
+}
+
+// %28:_(<8 x i32>) = G_BUILD_VECTOR %91:_(i32), %92:_(i32), ...
+// %37:_(<4 x i32>) = G_EXTRACT_SUBVECTOR %28:_(<8 x i32>), 0
+// => %37:_(<4 x i32>) = G_BUILD_VECTOR %91:_(i32), %92:_(i32), ...
+bool PISAPostLegalizerCombinerImpl::matchExtractSubvectorBuildVector(
+ MachineInstr &MI, SmallVector<Register, 8> &MatchInfo) const {
+ assert(MI.getOpcode() == TargetOpcode::G_EXTRACT_SUBVECTOR);
+ if (!MI.getOperand(2).isImm())
+ return false;
+ auto *BV = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
+ if (!BV || BV->getOpcode() != TargetOpcode::G_BUILD_VECTOR)
+ return false;
+ uint64_t Offset = MI.getOperand(2).getImm();
+ unsigned NumDstElts = MRI.getType(MI.getOperand(0).getReg()).getNumElements();
+ if (NumDstElts != 4)
+ return false;
+ unsigned NumSrcElts = BV->getNumOperands() - 1; // operand 0 is the dst
+ if (Offset + NumDstElts > NumSrcElts)
+ return false;
+ MatchInfo.clear();
+ for (unsigned I = 0; I < NumDstElts; ++I)
+ MatchInfo.push_back(BV->getOperand(1 + Offset + I).getReg());
+ return true;
+}
+
+void PISAPostLegalizerCombinerImpl::applyExtractSubvectorBuildVector(
+ MachineInstr &MI, SmallVector<Register, 8> &MatchInfo) const {
+ Register DstReg = MI.getOperand(0).getReg();
+ B.setInstrAndDebugLoc(MI);
+ B.buildBuildVector(DstReg, MatchInfo);
+ MI.eraseFromParent();
+}
+
+// Match adjacent G_FENCE instructions that have the same memory ordering and
+// same base scope but differ only in address space. These can be merged into
+// a single generic-address-space fence.
+bool PISAPostLegalizerCombinerImpl::matchMergeAdjacentFences(
+ MachineInstr &MI, MachineInstr *&PrevFence) const {
+ assert(MI.getOpcode() == TargetOpcode::G_FENCE);
+
+ if (isSubgroupFence(MI))
+ return false;
+
+ auto Order = static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
+ auto BaseScopeNameA = getBaseScopeName(MI);
+ if (BaseScopeNameA.empty())
+ return false;
+
+ auto It = MI.getIterator();
+ const MachineBasicBlock &MBB = *MI.getParent();
+ while (It != MBB.begin()) {
+ --It;
+ if (It->getOpcode() == TargetOpcode::G_FENCE) {
+ auto ItOrder = static_cast<AtomicOrdering>(It->getOperand(0).getImm());
+ if (ItOrder != Order)
+ return false;
+
+ if (It->getOperand(1).getImm() == MI.getOperand(1).getImm())
+ return false;
+
+ auto BaseScopeNameIt = getBaseScopeName(*It);
+ if (BaseScopeNameIt.empty() || BaseScopeNameIt != BaseScopeNameA)
+ return false;
+
+ PrevFence = &*It;
+ return true;
+ }
+ if (It->mayLoadOrStore() || It->hasUnmodeledSideEffects())
+ return false;
+ }
+ return false;
+}
+
+// Merge two fences with same ordering and same base scope but different
+// address spaces into a single fence with generic address space.
+void PISAPostLegalizerCombinerImpl::applyMergeAdjacentFences(
+ MachineInstr &MI, MachineInstr *&PrevFence) const {
+ assert(PrevFence && "Expected a preceding compatible fence");
+
+ StringRef BaseScopeName = getBaseScopeName(*PrevFence);
+ assert(!BaseScopeName.empty() && "Expected a named sync scope");
+ std::string GenericName = (BaseScopeName + "-generic").str();
+ auto &Ctx = MI.getMF()->getFunction().getContext();
+ auto GenericID = Ctx.getOrInsertSyncScopeID(GenericName);
+ PrevFence->getOperand(1).setImm(static_cast<int64_t>(GenericID));
+
+ MI.eraseFromParent();
+}
+
+// %29:_(<64 x s32>) = IMPLICIT_DEF
+// %30:_(<64 x s32>) = G_INSERT_SUBVECTOR %29:_, %7:_(<8 x s32>), 0
+// %31:_(<64 x s32>) = G_INSERT_SUBVECTOR %30:_, %10:_(<8 x s32>), 8
+// %32:_(<64 x s32>) = G_INSERT_SUBVECTOR %31:_, %13:_(<8 x s32>), 16
+// %33:_(<64 x s32>) = G_INSERT_SUBVECTOR %32:_, %16:_(<8 x s32>), 24
+// %34:_(<64 x s32>) = G_INSERT_SUBVECTOR %33:_, %19:_(<8 x s32>), 32
+// %35:_(<64 x s32>) = G_INSERT_SUBVECTOR %34:_, %22:_(<8 x s32>), 40
+// %36:_(<64 x s32>) = G_INSERT_SUBVECTOR %35:_, %25:_(<8 x s32>), 48
+// %37:_(<64 x s32>) = G_INSERT_SUBVECTOR %36:_, %28:_(<8 x s32>), 56
+// %4:_(<32 x s32>) = G_EXTRACT_SUBVECTOR %37:_(<64 x s32>), 32
+// =>
+// %IMP:_(<32 x s32>) = IMPLICIT_DEF
+// %34:_(<32 x s32>) = G_INSERT_SUBVECTOR %IMP:_, %19:_(<8 x s32>), 0
+// %35:_(<32 x s32>) = G_INSERT_SUBVECTOR %34:_, %22:_(<8 x s32>), 8
+// %36:_(<32 x s32>) = G_INSERT_SUBVECTOR %35:_, %25:_(<8 x s32>), 16
+// %37:_(<32 x s32>) = G_INSERT_SUBVECTOR %36:_, %28:_(<8 x s32>), 24
+// %4:_(<32 x s32>) = COPY %37:_(<32 x s32>)
+bool PISAPostLegalizerCombinerImpl::matchExtractSubvectorPartial(
+ MachineInstr &MI, SmallVector<MachineInstr *, 8> &MatchInfo) const {
+ assert(MI.getOpcode() == TargetOpcode::G_EXTRACT_SUBVECTOR);
+
+ Register SrcReg = MI.getOperand(1).getReg();
+ uint64_t ExtractIdx = MI.getOperand(2).getImm();
+ LLT DstTy = MRI.getType(MI.getOperand(0).getReg());
+ LLT SrcTy = MRI.getType(SrcReg);
+ unsigned DstNumElts = DstTy.getNumElements();
+
+ if (DstTy.getScalarSizeInBits() != 32)
+ return false;
+ if (DstNumElts < 8 || DstNumElts == SrcTy.getNumElements())
+ return false;
+
+ // Walk the chain of G_INSERT_SUBVECTOR instructions feeding the source.
+ // Collect inserts that are fully contained within the extracted range.
+ MatchInfo.clear();
+ MachineInstr *Cur = MRI.getVRegDef(SrcReg);
+ while (Cur && Cur->getOpcode() == TargetOpcode::G_INSERT_SUBVECTOR) {
+ uint64_t InsIdx = Cur->getOperand(3).getImm();
+ Register InsSubReg = Cur->getOperand(2).getReg();
+ unsigned InsNumElts = MRI.getType(InsSubReg).getNumElements();
+
+ // Reject if insert starts before/extend past the extract window.
+ if (InsIdx < ExtractIdx && InsIdx + InsNumElts > ExtractIdx)
+ return false;
+ if (InsIdx >= ExtractIdx && InsIdx + InsNumElts > ExtractIdx + DstNumElts)
+ return false;
+
+ if (InsIdx >= ExtractIdx)
+ MatchInfo.push_back(Cur);
+ Cur = MRI.getVRegDef(Cur->getOperand(1).getReg());
+ }
+
+ // Base of the chain must be IMPLICIT_DEF and we need at least one insert.
+ if (MatchInfo.empty() || !Cur ||
+ Cur->getOpcode() != TargetOpcode::IMPLICIT_DEF)
+ return false;
+ return true;
+}
+
+void PISAPostLegalizerCombinerImpl::applyExtractSubvectorPartial(
+ MachineInstr &MI, const SmallVector<MachineInstr *, 8> &MatchInfo) const {
+ Register DstReg = MI.getOperand(0).getReg();
+ uint64_t ExtractIdx = MI.getOperand(2).getImm();
+ LLT DstTy = MRI.getType(DstReg);
+
+ // Rebuild in reverse order (innermost/earliest insert first).
+ B.setInstrAndDebugLoc(MI);
+ Register AccReg = B.buildUndef(DstTy).getReg(0);
+
+ for (const MachineInstr *Ins : llvm::reverse(MatchInfo)) {
+ Register InsSubReg = Ins->getOperand(2).getReg();
+ uint64_t InsIdx = Ins->getOperand(3).getImm();
+ uint64_t NewIdx = InsIdx - ExtractIdx;
+ AccReg = B.buildInsertSubvector(DstTy, AccReg, InsSubReg, NewIdx).getReg(0);
+ }
+
+ B.buildCopy(DstReg, AccReg);
+ MI.eraseFromParent();
+}
+
+// umin(x, UINT_MAX) -> x, umax(x, 0) -> x
+// smin(x, INT_MAX) -> x, smax(x, INT_MIN) -> x
+bool PISAPostLegalizerCombinerImpl::matchMinMaxIdentityFold(
+ MachineInstr &MI, Register &MatchInfo) const {
+ assert(MI.getOpcode() == TargetOpcode::G_UMIN ||
+ MI.getOpcode() == TargetOpcode::G_UMAX ||
+ MI.getOpcode() == TargetOpcode::G_SMIN ||
+ MI.getOpcode() == TargetOpcode::G_SMAX);
+ Register LHS = MI.getOperand(1).getReg();
+ Register RHS = MI.getOperand(2).getReg();
+ auto IsIdentity = [&](const APInt &Val) -> bool {
+ switch (MI.getOpcode()) {
+ case TargetOpcode::G_UMIN:
+ return Val.isAllOnes();
+ case TargetOpcode::G_UMAX:
+ return Val.isZero();
+ case TargetOpcode::G_SMIN:
+ return Val.isMaxSignedValue();
+ case TargetOpcode::G_SMAX:
+ return Val.isMinSignedValue();
+ default:
+ return false;
+ }
+ };
+ if (auto C = getIConstantVRegValWithLookThrough(RHS, MRI))
+ if (IsIdentity(C->Value)) {
+ MatchInfo = LHS;
+ return true;
+ }
+ if (auto C = getIConstantVRegValWithLookThrough(LHS, MRI))
+ if (IsIdentity(C->Value)) {
+ MatchInfo = RHS;
+ return true;
+ }
+ return false;
+}
+
+// Pass boilerplate
+// ================
+
+class PISAPostLegalizerCombiner : public MachineFunctionPass {
+ PISAPostLegalizerCombinerImplRuleConfig RuleConfig;
+
+public:
+ static char ID;
+
+ PISAPostLegalizerCombiner();
+
+ StringRef getPassName() const override { return "PISAPostLegalizerCombiner"; }
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+} // end anonymous namespace
+
+void PISAPostLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.setPreservesCFG();
+ getSelectionDAGFallbackAnalysisUsage(AU);
+ AU.addRequired<GISelValueTrackingAnalysisLegacy>();
+ AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
+ AU.addRequired<MachineDominatorTreeWrapperPass>();
+ AU.addPreserved<MachineDominatorTreeWrapperPass>();
+ MachineFunctionPass::getAnalysisUsage(AU);
+}
+
+PISAPostLegalizerCombiner::PISAPostLegalizerCombiner()
+ : MachineFunctionPass(ID) {
+ initializePISAPostLegalizerCombinerPass(*PassRegistry::getPassRegistry());
+ if (!RuleConfig.parseCommandLineOption())
+ report_fatal_error("Invalid rule identifier");
+}
+
+bool PISAPostLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+ if (MF.getProperties().hasProperty(
+ MachineFunctionProperties::Property::FailedISel))
+ return false;
+
+ const Function &F = MF.getFunction();
+ bool EnableOpt =
+ MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
+
+ GISelValueTracking *KB =
+ &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
+ MachineDominatorTree *MDT =
+ &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+ CombinerInfo CInfo(
+ /*AllowIllegalOps=*/true, /*ShouldLegalizeIllegal=*/false,
+ /*LegalizerInfo=*/nullptr, EnableOpt, F.hasOptSize(), F.hasMinSize());
+
+ const PISASubtarget &STI = MF.getSubtarget<PISASubtarget>();
+ PISAPostLegalizerCombinerImpl Impl(MF, CInfo, *KB, /*CSEInfo=*/nullptr,
+ RuleConfig, STI, MDT,
+ STI.getLegalizerInfo());
+ return Impl.combineMachineInstrs();
+}
+
+char PISAPostLegalizerCombiner::ID = 0;
+INITIALIZE_PASS_BEGIN(PISAPostLegalizerCombiner, DEBUG_TYPE,
+ "Combine PISA machine instrs after legalization", false,
+ false)
+INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
+INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass)
+INITIALIZE_PASS_END(PISAPostLegalizerCombiner, DEBUG_TYPE,
+ "Combine PISA machine instrs after legalization", false,
+ false)
+
+namespace llvm {
+FunctionPass *createPISAPostLegalizerCombiner() {
+ return new PISAPostLegalizerCombiner();
+}
+} // end namespace llvm
diff --git a/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp b/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
new file mode 100644
index 00000000000000..7a9cf7a4b3205e
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
@@ -0,0 +1,1664 @@
+//===-- lib/CodeGen/GlobalISel/PISAPreLegalizerCombiner.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "MCTargetDesc/PISAMCTargetDesc.h"
+#include "PISA.h"
+#include "PISALegalizerInfo.h"
+#include "PISATargetMachine.h"
+#include "PISAUtils.h"
+#include "llvm/ADT/FloatingPointMode.h"
+#include "llvm/ADT/bit.h"
+#include "llvm/CodeGen/GlobalISel/CSEInfo.h"
+#include "llvm/CodeGen/GlobalISel/Combiner.h"
+#include "llvm/CodeGen/GlobalISel/CombinerHelper.h"
+#include "llvm/CodeGen/GlobalISel/CombinerInfo.h"
+#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h"
+#include "llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h"
+#include "llvm/CodeGen/GlobalISel/GISelChangeObserver.h"
+#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
+#include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h"
+#include "llvm/CodeGen/GlobalISel/MIPatternMatch.h"
+#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
+#include "llvm/CodeGen/GlobalISel/Utils.h"
+#include "llvm/CodeGen/MachineDominators.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/IntrinsicsPISA.h"
+#include "llvm/Target/TargetMachine.h"
+
+#define GET_GICOMBINER_DEPS
+#include "PISAGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_DEPS
+
+#define DEBUG_TYPE "pisa-prelegalizer-combiner"
+
+using namespace llvm;
+using namespace llvm::MIPatternMatch;
+
+namespace {
+
+#define GET_GICOMBINER_TYPES
+#include "PISAGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_TYPES
+
+class PISAPreLegalizerCombinerImpl : public Combiner {
+protected:
+ const PISAPreLegalizerCombinerImplRuleConfig &RuleConfig;
+ const PISASubtarget &STI;
+ MachineDominatorTree *MDT;
+
+ // TODO: Make CombinerHelper methods const.
+ mutable CombinerHelper Helper;
+
+public:
+ PISAPreLegalizerCombinerImpl(
+ MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &KB,
+ GISelCSEInfo *CSEInfo,
+ const PISAPreLegalizerCombinerImplRuleConfig &RuleConfig,
+ const PISASubtarget &STI, MachineDominatorTree *MDT,
+ const LegalizerInfo *LI);
+
+ static const char *getName() { return "PISAGenPreLegalizeGICombiner"; }
+
+ bool tryCombineAllImpl(MachineInstr &MI) const;
+ bool tryCombineAll(MachineInstr &I) const override;
+
+ void applyTruncatedLoad(MachineInstr &MI) const;
+
+ bool matchTruncatedStore(MachineInstr &MI) const;
+ void applyTruncatedStore(MachineInstr &MI) const;
+
+ bool matchExtendedLoad(MachineInstr &MI) const;
+ void applyExtendedLoad(MachineInstr &MI) const;
+
+ bool matchSimplifyNonPowerOf2LoadStoreChain(
+ MachineInstr &MI,
+ SmallVector<std::pair<MachineInstr *, unsigned>, 8> &Loads,
+ MachineInstr *&SizeModificationOp) const;
+ void applySimplifyNonPowerOf2LoadStoreChain(
+ MachineInstr &MI,
+ SmallVector<std::pair<MachineInstr *, unsigned>, 8> &Loads,
+ MachineInstr *&SizeModificationOp) const;
+
+ bool matchExpandNonPowerOf2LoadStore(MachineInstr &MI) const;
+ void applyExpandNonPowerOf2LoadStore(MachineInstr &MI) const;
+
+ bool matchTruncatedShift(MachineInstr &MI) const;
+ void applyTruncatedShift(MachineInstr &MI) const;
+
+ bool matchRedundantMovesPre(MachineInstr &MI) const;
+ void applyRedundantMovesPre(MachineInstr &MI) const;
+
+ bool
+ matchRcpSqrtToRsqrt(MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+ bool
+ matchSubFloorToFrc(MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchLaneIdLeftShiftChain(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchZExtAndToAndZExt(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchExtractInsertToBitcast(MachineInstr &MI, Register &) const;
+ void applyExtractInsertToBitcast(MachineInstr &MI, Register) const;
+
+ bool matchExtractBuildVectorToBitcast(MachineInstr &MI, Register &) const;
+ void applyExtractBuildVectorToBitcast(MachineInstr &MI, Register) const;
+
+ bool matchReducePredicates(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchCmpInt1(MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+ bool matchSelectTruncOneZero(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const;
+
+private:
+#define GET_GICOMBINER_CLASS_MEMBERS
+#include "PISAGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CLASS_MEMBERS
+};
+
+#define GET_GICOMBINER_IMPL
+#include "PISAGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_IMPL
+
+PISAPreLegalizerCombinerImpl::PISAPreLegalizerCombinerImpl(
+ MachineFunction &MF, CombinerInfo &CInfo, GISelValueTracking &KB,
+ GISelCSEInfo *CSEInfo,
+ const PISAPreLegalizerCombinerImplRuleConfig &RuleConfig,
+ const PISASubtarget &STI, MachineDominatorTree *MDT,
+ const LegalizerInfo *LI)
+ : Combiner(MF, CInfo, &KB, CSEInfo), RuleConfig(RuleConfig), STI(STI),
+ MDT(MDT), Helper(Observer, B, /*IsPreLegalize=*/true, &KB, MDT, LI),
+#define GET_GICOMBINER_CONSTRUCTOR_INITS
+#include "PISAGenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CONSTRUCTOR_INITS
+{
+}
+
+bool PISAPreLegalizerCombinerImpl::tryCombineAll(MachineInstr &MI) const {
+ // G_FPTRUNC to bf16 needs special constant folding because
+ // getFltSemanticForLLT() doesn't support BFloat16 yet.
+ if (MI.getOpcode() == TargetOpcode::G_FPTRUNC &&
+ MRI.getType(MI.getOperand(0).getReg()).getScalarType().isBFloat16()) {
+ const ConstantFP *Cst = nullptr;
+ if (mi_match(MI.getOperand(1).getReg(), MRI, m_GFCst(Cst))) {
+ APFloat Result(Cst->getValue());
+ bool Unused;
+ Result.convert(APFloat::BFloat(), APFloat::rmNearestTiesToEven, &Unused);
+ const ConstantFP *NewCst = ConstantFP::get(B.getContext(), Result);
+ MachineIRBuilder Builder(MI);
+ Builder.buildFConstant(MI.getOperand(0), *NewCst);
+ MI.eraseFromParent();
+ return true;
+ }
+ }
+
+ if (tryCombineAllImpl(MI))
+ return true;
+
+ return false;
+}
+
+void PISAPreLegalizerCombinerImpl::applyTruncatedLoad(MachineInstr &MI) const {
+ auto *LoadMI = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
+ auto *MMO = LoadMI->memoperands()[0];
+ auto Dst = MI.getOperand(0);
+ auto Addr = LoadMI->getOperand(1);
+ auto *NewMMO = MI.getMF()->getMachineMemOperand(MMO, MMO->getOffset(),
+ MRI.getType(Dst.getReg()));
+ B.buildLoad(Dst, Addr, *NewMMO);
+ MI.eraseFromParent();
+}
+
+bool PISAPreLegalizerCombinerImpl::matchExpandNonPowerOf2LoadStore(
+ MachineInstr &MI) const {
+ assert(MI.getOpcode() == TargetOpcode::G_LOAD ||
+ MI.getOpcode() == TargetOpcode::G_STORE);
+ GLoadStore &LS = cast<GLoadStore>(MI);
+
+ auto Size = LS.getMemSizeInBits().getValue();
+
+ if (isPowerOf2_32(Size))
+ return false;
+
+ if (LS.getMMO().getMemoryType().isVector())
+ return false;
+
+ // Check if this is a load and only has zext uses that are handled by the
+ // extended load pattern - if yes, we want to handle it via said pattern.
+ if (MI.getOpcode() == TargetOpcode::G_LOAD) {
+ for (auto &Use : MRI.use_operands(MI.getOperand(0).getReg())) {
+ auto *Inst = Use.getParent();
+ if (Inst->getOpcode() != TargetOpcode::G_ZEXT ||
+ !matchExtendedLoad(*Inst))
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
+ MachineInstr &MI) const {
+ assert(MI.getOpcode() == TargetOpcode::G_LOAD ||
+ MI.getOpcode() == TargetOpcode::G_STORE);
+
+ GLoadStore &LS = cast<GLoadStore>(MI);
+
+ auto PointerReg = LS.getPointerReg();
+ auto ValueReg = LS.getOperand(0).getReg();
+ auto &MMO = LS.getMMO();
+
+ /// The remaining size in Bits that still has to be loaded/stored
+ ssize_t Size = LS.getMemSizeInBits().getValue();
+ // Support sizes that are not a multiple of 8 by "promoting" them to the next
+ // multiple of 8. If the size is already a multiple of 8, it is not modified
+ Size = (Size + 7) & ~7;
+
+ unsigned Offset = 0;
+ const auto SizeTy = LLT::integer(Size);
+
+ // If the size was changed to the next power of 8 and we are storing a value,
+ // we need to modify the register's type as well.
+ // A similar check is needed for loads, but this is done at the end
+ if (MI.getOpcode() == TargetOpcode::G_STORE &&
+ SizeTy != MRI.getType(ValueReg)) {
+ auto NewValueReg = MRI.createGenericVirtualRegister(SizeTy);
+ B.buildZExt(NewValueReg, ValueReg);
+ ValueReg = NewValueReg;
+ }
+
+ /// The register holding the loaded value at the end
+ Register LoadRes;
+ while (Size > 0) {
+ auto OpSize = bit_floor(static_cast<size_t>(Size));
+ const auto ShiftAmount = Offset * 8;
+
+ const LLT OpTy = LLT::integer(OpSize);
+
+ auto *NewMMO =
+ MI.getMF()->getMachineMemOperand(&MMO, MMO.getOffset() + Offset, OpTy);
+
+ /// Stores the (potentially modified) pointer register
+ auto AddrReg = PointerReg;
+ // We might need to change the store offset
+ if (Offset != 0) {
+ auto NewAddr = MRI.cloneVirtualRegister(AddrReg);
+
+ // Get the pointer size from the pointer register type
+ const LLT PtrTy = MRI.getType(AddrReg);
+ const LLT IntTy = LLT::integer(PtrTy.getSizeInBits());
+ auto Const = MRI.createGenericVirtualRegister(IntTy);
+ B.buildConstant(Const, Offset);
+ B.buildPtrAdd(NewAddr, AddrReg, Const);
+
+ AddrReg = NewAddr;
+ }
+
+ if (MI.getOpcode() == TargetOpcode::G_STORE) {
+ auto Res = ValueReg;
+ // If we're not storing from the start, we need to shift our value first
+ if (Offset != 0) {
+ auto ShrRes = MRI.createGenericVirtualRegister(SizeTy);
+ auto ShiftConst = MRI.createGenericVirtualRegister(SizeTy);
+ B.buildConstant(ShiftConst, ShiftAmount);
+ B.buildLShr(ShrRes, ValueReg, ShiftConst);
+ Res = ShrRes;
+ }
+
+ // Next, truncate it to the correct size, if needed
+ if (SizeTy != OpTy) {
+ auto TruncRes = MRI.createGenericVirtualRegister(OpTy);
+ B.buildTrunc(TruncRes, Res);
+ Res = TruncRes;
+ }
+
+ B.buildStore(Res, AddrReg, *NewMMO);
+ } else {
+ // When loading, do the same thing but basically in reverse
+ // First, load the value
+ auto LoadedValReg = MRI.createGenericVirtualRegister(OpTy);
+ B.buildLoad(LoadedValReg, AddrReg, *NewMMO);
+ auto Res = LoadedValReg;
+
+ // Extend it to the correct size, if needed
+ if (SizeTy != OpTy) {
+ auto ZextRes = MRI.createGenericVirtualRegister(SizeTy);
+ B.buildZExt(ZextRes, LoadedValReg);
+ Res = ZextRes;
+ }
+
+ // If we're not loading the first Bytes, then we need to shift it
+ if (Offset != 0) {
+ auto ShiftRes = MRI.createGenericVirtualRegister(SizeTy);
+
+ auto ShiftConst = MRI.createGenericVirtualRegister(SizeTy);
+ B.buildConstant(ShiftConst, ShiftAmount);
+
+ B.buildShl(ShiftRes, Res, ShiftConst);
+ Res = ShiftRes;
+ }
+
+ if (!LoadRes.isValid()) {
+ LoadRes = MRI.createGenericVirtualRegister(SizeTy);
+ B.buildConstant(LoadRes, 0);
+ }
+
+ auto NewLoadRes = MRI.createGenericVirtualRegister(SizeTy);
+ B.buildOr(NewLoadRes, LoadRes, Res);
+ LoadRes = NewLoadRes;
+ }
+
+ Offset += OpSize / 8;
+ Size -= OpSize;
+ }
+
+ if (MI.getOpcode() == TargetOpcode::G_LOAD) {
+ // If we modified the original size of the load (to support sizes that are
+ // not a multiple of 8), we need to truncate it to the correct size again,
+ // in order not to break any following instructions
+ if (SizeTy != MRI.getType(ValueReg)) {
+ auto TruncRes = MRI.createGenericVirtualRegister(MRI.getType(ValueReg));
+ B.buildTrunc(TruncRes, LoadRes);
+ LoadRes = TruncRes;
+ }
+ MRI.replaceRegWith(ValueReg, LoadRes);
+ }
+
+ MI.eraseFromParent();
+}
+
+/// If the value we store resulted from a G_LOAD that the rule above expanded,
+/// then we can use the individual load values directly instead of merging into
+/// one integer, and then splitting it again. Size modifications between the
+/// loads and store (i.e. SEXT/ZEXT/TRUNC) are also supported by
+/// truncating/extending the relevant load results (or, in the case of
+/// truncation, ignoring some loads entirely)
+///
+/// Below is a simple example where we simply load and store an i56:
+///
+/// bb.1.entry:
+/// %0:reg32b(p0) = functionParameter_32b 0
+/// -> %3:_(s56) = G_ZEXTLOAD %0:reg32b(p0) ::
+/// (load (s32) from %ir.dst)
+/// %4:_(s56) = G_CONSTANT i56 0
+/// %7:_(s32) = G_CONSTANT i32 4
+/// %6:reg32b(p0) = G_PTR_ADD %0:reg32b, %7:_(s32)
+/// -> %9:_(s56) = G_ZEXTLOAD %6:reg32b(p0) ::
+/// (load (s16) from %ir.dst + 4, align 4)
+/// %11:_(s56) = G_CONSTANT i56 32
+/// %10:_(s56) = G_SHL %9:_, %11:_(s56)
+/// %12:_(s56) = G_OR %3:_, %10:_
+/// %14:_(s32) = G_CONSTANT i32 6
+/// %13:reg32b(p0) = G_PTR_ADD %0:reg32b, %14:_(s32)
+/// -> %16:_(s56) = G_ZEXTLOAD %13:reg32b(p0) ::
+/// (load (s8) from %ir.dst + 6, align 2, basealign 4)
+/// %18:_(s56) = G_CONSTANT i56 48
+/// %17:_(s56) = G_SHL %16:_, %18:_(s56)
+/// %19:_(s56) = G_OR %12:_, %17:_
+/// G_STORE %19:_(s56), %0:reg32b(p0) ::
+/// (store (s56) into %ir.dst, align 4)
+/// ret
+///
+/// @param Loads stores all the load instructions that we find along the way.
+/// The second parameter in the pair is the offset from which the value was
+/// loaded
+/// @param SizeModificationOp stores the opcode to the instruction that modified
+/// the size of the integer after loading and before storing, if it exists.
+/// Possible opcode values are null (none), G_SEXT, G_SEXT_INREG, G_ZEXT, and
+/// G_TRUNC
+bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
+ MachineInstr &MI,
+ SmallVector<std::pair<MachineInstr *, unsigned>, 8> &Loads,
+ MachineInstr *&SizeModificationOp) const {
+ GLoadStore &StoreInst = cast<GLoadStore>(MI);
+
+ auto ValueReg = StoreInst.getOperand(0).getReg();
+
+ /// This has the integer size at the beginning, and each individual load
+ /// decreases the value by its load size. At the end, this must be zero.
+ /// NB: We use the size of the store here, promoted to the next multiple
+ /// of 8. This size might be modified later if we find a G_*EXT/G_TRUNC.
+ unsigned ValueSize = StoreInst.getMemSize().getValue() * 8;
+
+ /// This stores the size of the last load instruction in Bytes, s.t. we can
+ /// verify that the current load is larger than the previous one
+ unsigned LastSize = 0;
+
+ SizeModificationOp = nullptr;
+
+ // First, we need to verify the entire "chain" of loads, s.t. we know that
+ // there are no further modifications to the value that we want to store.
+ // If there is a truncation/extension instruction in between, make note of
+ // that, and continue while verifying the load chain using the load size
+ // (i.e. size before *ext/trunc)
+ MachineInstr *NextChainInst = MRI.getVRegDef(ValueReg);
+ if (NextChainInst->getOpcode() == TargetOpcode::G_TRUNC ||
+ NextChainInst->getOpcode() == TargetOpcode::G_ZEXT ||
+ NextChainInst->getOpcode() == TargetOpcode::G_SEXT ||
+ NextChainInst->getOpcode() == TargetOpcode::G_SEXT_INREG) {
+ /// The "actual" size that was used during loading
+ unsigned LoadSize = MRI.getType(NextChainInst->getOperand(1).getReg())
+ .getScalarSizeInBits();
+
+ // We also use G_TRUNC in case the loaded integer was not a multiple of 8.
+ // In that case, ValueSize (which was already promoted to the next
+ // multiple of 8) would be equal to the total number of Bytes loaded from
+ // memory. If not, we need to remember this instruction, and change the
+ // expected size to the one that is actually used during loading.
+ if (ValueSize != LoadSize ||
+ NextChainInst->getOpcode() != TargetOpcode::G_TRUNC) {
+ SizeModificationOp = NextChainInst;
+ ValueSize = LoadSize;
+ }
+
+ // Either way, skip this instruction
+ NextChainInst = MRI.getVRegDef(NextChainInst->getOperand(1).getReg());
+
+ // If we loaded an integer that's not a multiple of 8, after which an
+ // *ext/trunc operation is done before storing, then there is another
+ // G_TRUNC in the way. Simply ignore that one.
+ // Example when doing load i38, zext -> i40, store i40:
+ // [...]
+ // %15:_(s38) = G_TRUNC %14:_(s40)
+ // %3:_(s40) = G_ZEXT %15:_(s38)
+ // G_STORE %3:_(s40), %1:reg32b(p0)
+ // [...]
+ if (NextChainInst->getOpcode() == TargetOpcode::G_TRUNC) {
+ if (LoadSize % 8 == 0)
+ return false;
+ ValueSize = MRI.getType(NextChainInst->getOperand(1).getReg())
+ .getScalarSizeInBits();
+ NextChainInst = MRI.getVRegDef(NextChainInst->getOperand(1).getReg());
+ }
+ }
+
+ while (ValueSize != 0) {
+ // We either start at an G_OR, or at a G_LOAD/G_ZEXTLOAD (meaning we're
+ // done)
+ if (NextChainInst->getOpcode() == TargetOpcode::G_OR) {
+ MachineInstr *NextOr, *ZextLoadMI;
+ int64_t LoadShift;
+ // First, match the Or-masking
+ if (!mi_match(NextChainInst, MRI,
+ m_GOr(m_MInstr(NextOr),
+ m_GShl(m_MInstr(ZextLoadMI), m_ICst(LoadShift)))))
+ return false;
+
+ if (ZextLoadMI->getOpcode() != TargetOpcode::G_ZEXTLOAD)
+ return false;
+
+ // The shift amount matches the offset used in the load instruction
+ MachineInstr *PtrAddInst =
+ MRI.getVRegDef(ZextLoadMI->getOperand(1).getReg());
+ if (PtrAddInst->getOpcode() != TargetOpcode::G_PTR_ADD)
+ return false;
+
+ auto LoadOffset =
+ getIConstantVRegVal(PtrAddInst->getOperand(2).getReg(), MRI);
+ if (!LoadOffset.has_value() || LoadOffset.value() != LoadShift / 8)
+ return false;
+
+ // Verify the LLT that was loaded
+ LLT LoadTy = cast<GZExtLoad>(ZextLoadMI)->getMMO().getType();
+ if (!LoadTy.isScalar() || LoadTy.getScalarSizeInBits() <= LastSize ||
+ ValueSize <= LastSize)
+ return false;
+
+ LastSize = LoadTy.getScalarSizeInBits();
+ ValueSize -= LastSize;
+ NextChainInst = NextOr;
+
+ Loads.push_back({ZextLoadMI, static_cast<unsigned>(
+ LoadOffset.value().getZExtValue())});
+ } else if (NextChainInst->getOpcode() == TargetOpcode::G_ZEXTLOAD ||
+ NextChainInst->getOpcode() == TargetOpcode::G_LOAD) {
+ const auto &LoadInst = cast<GLoadStore>(*NextChainInst);
+ const LLT LoadTy = LoadInst.getMMO().getType();
+ if (!LoadTy.isScalar())
+ return false;
+
+ // This is the last load instruction of the chain, so it must match the
+ // remaining value in ValueSize
+ auto LoadSize = LoadTy.getScalarSizeInBits();
+ if (ValueSize % 8 == 0 && LoadSize != ValueSize)
+ return false;
+
+ // To avoid endless loops with single loads, sort out the bad cases here
+ if (Loads.empty()) {
+ // We just load and store a value, there is nothing to optimize here
+ if (!SizeModificationOp)
+ return false;
+
+ // There is no optimization potential for zero extensions or simple
+ // truncations, only for sign extension
+ if (SizeModificationOp->getOpcode() == TargetOpcode::G_ZEXT ||
+ SizeModificationOp->getOpcode() == TargetOpcode::G_TRUNC)
+ return false;
+
+ auto SizeAfterModificationOp =
+ MRI.getType(SizeModificationOp->getOperand(0).getReg())
+ .getScalarSizeInBits();
+
+ // If the size does not significantly change after sign extension, then
+ // there's nothing to do for us here, the code is already optimal
+ if ((isPowerOf2_32(SizeAfterModificationOp)
+ ? SizeAfterModificationOp
+ : NextPowerOf2(SizeAfterModificationOp)) == LoadSize)
+ return false;
+
+ // If the G_SEXT source size equals the load size and the source is
+ // byte-aligned, applying this transformation would reconstruct the
+ // same G_SEXT(G_LOAD) pattern, causing an infinite loop. When the
+ // source size is not byte-aligned, the apply inserts in-place
+ // shl/ashr to align it first, producing a different pattern.
+ auto SextSrcSize =
+ MRI.getType(SizeModificationOp->getOperand(1).getReg())
+ .getScalarSizeInBits();
+ if (LoadSize == SextSrcSize && SextSrcSize % 8 == 0)
+ return false;
+
+ // TODO: revisit
+ // store(sext i63 (load i8)) results in infinite loop.
+ if (!isPowerOf2_32(SizeAfterModificationOp) &&
+ (SizeAfterModificationOp != LoadSize))
+ return false;
+ }
+
+ // Done
+ Loads.push_back({NextChainInst, 0});
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return false;
+}
+void PISAPreLegalizerCombinerImpl::applySimplifyNonPowerOf2LoadStoreChain(
+ MachineInstr &MI,
+ SmallVector<std::pair<MachineInstr *, unsigned>, 8> &Loads,
+ MachineInstr *&SizeModificationOp) const {
+ // If the chain is valid, erase our single G_STORE and convert it into
+ // multiple G_STOREs using the individually loaded values
+ GLoadStore &StoreInst = cast<GLoadStore>(MI);
+
+ auto PointerReg = StoreInst.getPointerReg();
+ auto &MMO = StoreInst.getMMO();
+
+ unsigned StoreSizeInBytes = StoreInst.getMemSize().getValue();
+
+ for (auto [Index, Pair] : enumerate(Loads)) {
+ auto [LoadMI, Offset] = Pair;
+
+ Register Value = LoadMI->getOperand(0).getReg();
+ LLT LoadTy = cast<GLoadStore>(LoadMI)->getMMO().getType();
+
+ Register Res = Value;
+ if (LoadTy.getSizeInBytes() + Offset <= StoreSizeInBytes) {
+ if (LoadMI->getOpcode() == TargetOpcode::G_ZEXTLOAD) {
+ // Since they are ZextLoad instructions, we need to change the size
+ // back to the loaded size. This instruction will not be visible in
+ // PISA later, but is required here for type correctness.
+ Res = MRI.createGenericVirtualRegister(LoadTy);
+ B.buildTrunc(Res, Value);
+ }
+
+ // This is the last load (i.e. the first entry in the vector) and the
+ // load was extended, so we need to sext/zext this value before
+ // storing
+ if (Index == 0 && SizeModificationOp != nullptr) {
+ auto Opcode = SizeModificationOp->getOpcode();
+ assert(Opcode == TargetOpcode::G_ZEXT ||
+ Opcode == TargetOpcode::G_SEXT ||
+ Opcode == TargetOpcode::G_SEXT_INREG);
+
+ if (Opcode == TargetOpcode::G_SEXT_INREG ||
+ Opcode == TargetOpcode::G_SEXT) {
+ // SEXT is a little trickier
+ unsigned StoreSizeInBits = StoreInst.getMemSizeInBits().getValue();
+ unsigned OriginalSize =
+ Opcode == TargetOpcode::G_SEXT_INREG
+ ? (SizeModificationOp->getOperand(2).getImm())
+ : MRI.getType(SizeModificationOp->getOperand(1).getReg())
+ .getScalarSizeInBits();
+
+ // First, do we have to sign extend this value in-place using
+ // shifts?
+ if (OriginalSize % 8 != 0) {
+ auto ExtendBy = 8 - (OriginalSize % 8);
+
+ auto ConstReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ B.buildConstant(ConstReg, ExtendBy);
+
+ if (LoadTy.getSizeInBits() < OriginalSize + ExtendBy) {
+ LoadTy = LLT::integer(OriginalSize + ExtendBy);
+ auto ZExtRes = MRI.createGenericVirtualRegister(LoadTy);
+ B.buildZExt(ZExtRes, Res);
+ Res = ZExtRes;
+ }
+
+ auto ShlRes = MRI.cloneVirtualRegister(Res);
+ B.buildShl(ShlRes, Res, ConstReg);
+
+ auto AShrRes = MRI.cloneVirtualRegister(ShlRes);
+ B.buildAShr(AShrRes, ShlRes, ConstReg);
+
+ Res = AShrRes;
+ // Update the size to reflect the extension
+ OriginalSize = (OriginalSize + 7) & ~7;
+ }
+
+ // Second, do we still have to SEXT it further?
+ if (StoreSizeInBits > OriginalSize) {
+ LoadTy = LLT::integer(StoreSizeInBits);
+ auto SextRes = MRI.createGenericVirtualRegister(LoadTy);
+ B.buildSExt(SextRes, Res);
+
+ Res = SextRes;
+ }
+ } else {
+ // G_ZEXT, simple
+ auto NewSize = StoreSizeInBytes - Offset;
+ assert(NewSize > LoadTy.getSizeInBytes());
+
+ LoadTy = LLT::integer(NewSize * 8);
+ auto ExtRes = MRI.createGenericVirtualRegister(LoadTy);
+ B.buildZExt(ExtRes, Res);
+
+ Res = ExtRes;
+ }
+ }
+ } else {
+ assert(SizeModificationOp->getOpcode() == TargetOpcode::G_TRUNC);
+
+ // The value was truncated before storing. There are two cases now:
+ // - we need to truncate this value to the correct size
+ // - we need to ignore this one, as it is "out of range"
+ if (Offset >= StoreSizeInBytes) {
+ continue;
+ }
+ LoadTy = LLT::integer((StoreSizeInBytes - Offset) * 8);
+
+ Res = MRI.createGenericVirtualRegister(LoadTy);
+ B.buildTrunc(Res, Value);
+ }
+
+ auto *NewMMO = MI.getMF()->getMachineMemOperand(
+ &MMO, MMO.getOffset() + Offset, LoadTy);
+
+ /// Stores the (potentially modified) pointer register
+ auto AddrReg = PointerReg;
+
+ // Add the offset to the pointer reg if the offset is not zero
+ if (Offset != 0) {
+ auto NewPointerReg =
+ MRI.createGenericVirtualRegister(MRI.getType(AddrReg));
+
+ // Get the pointer size from the pointer register type
+ const LLT PtrTy = MRI.getType(AddrReg);
+ const LLT IntTy = LLT::integer(PtrTy.getSizeInBits());
+ auto CstReg = MRI.createGenericVirtualRegister(IntTy);
+ B.buildConstant(CstReg, Offset);
+
+ B.buildPtrAdd(NewPointerReg, AddrReg, CstReg);
+
+ AddrReg = NewPointerReg;
+ }
+ B.buildStore(Res, AddrReg, *NewMMO);
+ }
+
+ MI.eraseFromParent();
+ return;
+}
+
+// Matches following pattern:
+// (s24) = G_TRUNC (s32)
+// G_STORE (s24), ptr
+bool PISAPreLegalizerCombinerImpl::matchTruncatedStore(MachineInstr &MI) const {
+ auto &Store = cast<GStore>(MI);
+ auto *TruncMI = getDefIgnoringCopies(Store.getValueReg(), MRI);
+ if (!TruncMI)
+ return false;
+ if (TruncMI->getOpcode() != TargetOpcode::G_TRUNC)
+ return false;
+ if (!isPowerOf2_32(
+ MRI.getType(TruncMI->getOperand(1).getReg()).getSizeInBits()))
+ return false;
+
+ auto Size = Store.getMemSizeInBits().getValue();
+ auto Align = Store.getMMO().getAlign().value();
+
+ if (isPowerOf2_32(Size))
+ return false;
+
+ auto NextPow2 = NextPowerOf2(Size);
+ auto TruncSize = MRI.getType(TruncMI->getOperand(1).getReg()).getSizeInBits();
+ if (TruncSize != NextPow2)
+ return false;
+ auto Remainder = NextPow2 - Size;
+ if (NextPow2 % Remainder != 0)
+ return false;
+
+ if (Remainder % 8 != 0 || Remainder > 64 || !isPowerOf2_32(Remainder))
+ return false;
+
+ // We will create a vector store of smaller elements, which has to be aligned
+ // to the bit width of the vector element.
+ if (Align % (Remainder / 8) != 0)
+ return false;
+
+ auto VecSize = NextPow2 / Remainder;
+ if (VecSize > 4)
+ return false;
+
+ return true;
+}
+
+// Changes the type of a non-power-of-2 store to a vector of smaller elements,
+// if it comes from a trunc instruction.
+void PISAPreLegalizerCombinerImpl::applyTruncatedStore(MachineInstr &MI) const {
+ auto &Store = cast<GStore>(MI);
+
+ auto *TruncMI = getDefIgnoringCopies(Store.getValueReg(), MRI);
+ assert(TruncMI && TruncMI->getOpcode() == TargetOpcode::G_TRUNC);
+
+ auto TruncVal = TruncMI->getOperand(1);
+ auto Size = Store.getMemSizeInBits().getValue();
+ auto NextPow2 = NextPowerOf2(Size);
+ auto Remainder = NextPow2 - Size;
+ auto VecSize = NextPow2 / Remainder;
+ auto VecSizeSmall = Size / Remainder;
+ auto VecType = LLT::fixed_vector(VecSize, LLT::integer(Remainder));
+ auto VecTypeSmall = LLT::fixed_vector(VecSizeSmall, LLT::integer(Remainder));
+ auto TmpDst = MRI.createGenericVirtualRegister(VecType);
+ auto TmpDstSmall = MRI.createGenericVirtualRegister(VecTypeSmall);
+ B.buildBitcast(TmpDst, TruncVal);
+ B.buildShuffleVector(TmpDstSmall, TmpDst, B.buildUndef(VecType), {0, 1, 2});
+
+ auto Addr = Store.getPointerReg();
+ auto &MMO = Store.getMMO();
+ auto *NewMMO =
+ MI.getMF()->getMachineMemOperand(&MMO, MMO.getOffset(), VecTypeSmall);
+ B.buildStore(TmpDstSmall, Addr, *NewMMO);
+ MI.eraseFromParent();
+}
+
+// Matches a G_ZEXT that extends the result of a load that has a non-power-of-2
+// type. It might be profitable to do a wider load and mask out the bits.
+// This saves us from using second load instruction and few arithmetic
+// instructions (shl, and, or) to zero extend.
+bool PISAPreLegalizerCombinerImpl::matchExtendedLoad(MachineInstr &MI) const {
+ auto *DefMI = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
+ if (!DefMI)
+ return false;
+ if (DefMI->getOpcode() != TargetOpcode::G_LOAD)
+ return false;
+ GLoad &Load = cast<GLoad>(*DefMI);
+
+ auto Size = Load.getMemSizeInBits().getValue();
+ auto Align = Load.getMMO().getAlign().value();
+
+ if (isPowerOf2_32(Size))
+ return false;
+
+ auto ZextSize = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
+ if (!isPowerOf2_32(ZextSize))
+ return false;
+
+ if (ZextSize > 64)
+ return false;
+
+ if (Align % (ZextSize / 8) != 0)
+ return false;
+
+ return true;
+}
+
+void PISAPreLegalizerCombinerImpl::applyExtendedLoad(MachineInstr &MI) const {
+ GLoad *LoadMI =
+ cast<GLoad>(getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI));
+ assert(LoadMI);
+
+ auto DestSize = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
+ auto LoadSize = LoadMI->getMemSizeInBits().getValue();
+
+ APInt Mask = APInt::getLowBitsSet(DestSize, LoadSize);
+
+ auto NewLoadSize = LLT::integer(DestSize);
+ auto LoadDst = MRI.createGenericVirtualRegister(NewLoadSize);
+
+ auto Addr = LoadMI->getOperand(1);
+ auto &MMO = LoadMI->getMMO();
+ auto *NewMMO = MI.getMF()->getMachineMemOperand(&MMO, MMO.getOffset(),
+ MRI.getType(LoadDst));
+
+ B.buildLoad(LoadDst, Addr, *NewMMO);
+ auto MaskReg = MRI.createGenericVirtualRegister(LLT::integer(DestSize));
+ B.buildConstant(MaskReg, Mask.getZExtValue());
+ B.buildAnd(MI.getOperand(0).getReg(), LoadDst, MaskReg);
+ MI.eraseFromParent();
+}
+
+// i32 %lo = G_TRUNC i32 a 16
+// i32 %shift = G_LSHR i32 a 16
+// %hi = G_TRUNC i32 %shift to i16
+// => %1 = G_BITCAST i32 a to <2 x 16>
+// => %lo = G_EXTRACT_VECTOR_ELT <2 x 16> %1, 0
+// => %hi = G_EXTRACT_VECTOR_ELT <2 x 16> %1, 1
+bool PISAPreLegalizerCombinerImpl::matchTruncatedShift(MachineInstr &MI) const {
+ auto &TruncMI = MI;
+ auto &LshMI = *getDefIgnoringCopies(TruncMI.getOperand(1).getReg(), MRI);
+ if (LshMI.getOpcode() == TargetOpcode::G_LSHR) {
+ auto WideTy = MRI.getType(TruncMI.getOperand(1).getReg());
+ auto NarrowTy = MRI.getType(TruncMI.getOperand(0).getReg());
+ auto WideSize = WideTy.getScalarSizeInBits();
+ auto NarrowSize = NarrowTy.getScalarSizeInBits();
+ if (WideTy.isScalar() && NarrowTy.isScalar()) {
+ if (isPowerOf2_32(WideSize) && isPowerOf2_32(NarrowSize)) {
+ auto ShiftReg = LshMI.getOperand(2).getReg();
+ if (auto Shift = getIConstantVRegValWithLookThrough(ShiftReg, MRI)) {
+ if (Shift.has_value()) {
+ auto ShiftVal = Shift->Value.getZExtValue();
+ if ((NarrowSize + ShiftVal) == WideSize)
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+void PISAPreLegalizerCombinerImpl::applyTruncatedShift(MachineInstr &MI) const {
+ auto &TruncMI = MI;
+ auto &LshMI = *getDefIgnoringCopies(TruncMI.getOperand(1).getReg(), MRI);
+ auto WideTy = MRI.getType(TruncMI.getOperand(1).getReg());
+ auto NarrowTy = MRI.getType(TruncMI.getOperand(0).getReg());
+ auto WideSize = WideTy.getScalarSizeInBits();
+ auto NarrowSize = NarrowTy.getScalarSizeInBits();
+
+ auto VecLen = WideSize / NarrowSize;
+ auto VecTy = LLT::fixed_vector(VecLen, NarrowTy);
+ auto VecReg = MRI.createGenericVirtualRegister(VecTy);
+
+ // trunc to extract high part
+ auto BitCast = B.buildBitcast(VecReg, LshMI.getOperand(1));
+ B.buildExtractVectorElementConstant(TruncMI.getOperand(0), VecReg,
+ VecLen - 1);
+
+ // find trunc to extract low part (if any)
+ auto ShiftSrc = LshMI.getOperand(1).getReg();
+ for (auto &UseMI : MRI.use_instructions(ShiftSrc)) {
+ if (UseMI.getOpcode() == TargetOpcode::G_TRUNC) {
+ auto DstSize = MRI.getType(UseMI.getOperand(0).getReg()).getSizeInBits();
+ if (DstSize == NarrowSize) {
+ assert(MDT && "machine dominator pass must be available");
+ if (!MDT->dominates(&UseMI, &TruncMI) &&
+ !MDT->dominates(&TruncMI, &UseMI))
+ continue; // can not optimize out low part
+ MachineIRBuilder MIB(UseMI);
+ auto Lo = MIB.buildExtractVectorElementConstant(UseMI.getOperand(0),
+ VecReg, 0);
+ if (MDT->dominates(&UseMI, &TruncMI))
+ BitCast->moveBefore(Lo);
+ UseMI.eraseFromParent();
+ }
+ }
+ }
+ TruncMI.eraseFromParent();
+}
+
+// A(16) = G_EXTRACT_VECTOR_ELT ARG(32), 0
+// B(16) = G_EXTRACT_VECTOR_ELT ARG(32), 1
+// C(<2x16>) = G_BUILD_VECTOR A, B
+// D(32) = G_BITCAST C(<2x16>)
+// => D(32) = COPY ARG(32)
+bool PISAPreLegalizerCombinerImpl::matchRedundantMovesPre(
+ MachineInstr &MI) const {
+ auto &BitcastMI = MI;
+ auto DstReg = BitcastMI.getOperand(0).getReg();
+ auto SrcReg = BitcastMI.getOperand(1).getReg();
+ if (MRI.getType(DstReg).isVector() || !MRI.getType(SrcReg).isVector())
+ return false;
+
+ auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ if (BuildVecMI.getOpcode() != TargetOpcode::G_BUILD_VECTOR)
+ return false;
+
+ unsigned Mask = 0;
+ Register VecReg = 0;
+ for (unsigned I = 1; I < BuildVecMI.getNumOperands(); I++) {
+ auto &ExtractMI =
+ *getDefIgnoringCopies(BuildVecMI.getOperand(I).getReg(), MRI);
+ if (ExtractMI.getOpcode() != TargetOpcode::G_EXTRACT_VECTOR_ELT)
+ return false;
+ if (I == 1) {
+ VecReg = ExtractMI.getOperand(1).getReg();
+ } else if (VecReg != ExtractMI.getOperand(1).getReg()) {
+ // must extract indices from the same vector
+ return false;
+ }
+ auto IndexReg = ExtractMI.getOperand(2).getReg();
+ auto Index = getIConstantVRegValWithLookThrough(IndexReg, MRI);
+ if (!Index.has_value())
+ return false;
+ // indices must be in the same order
+ if (Index->Value.getZExtValue() != (I - 1))
+ return false;
+ Mask |= 1 << Index->Value.getZExtValue();
+ }
+ // check that all indices have been extracted
+ if (Mask != (1u << MRI.getType(SrcReg).getNumElements()) - 1)
+ return false;
+
+ // do not consider creating <2x16> from e.g. <3x16>
+ if (MRI.getType(DstReg).getSizeInBits() !=
+ MRI.getType(VecReg).getSizeInBits())
+ return false;
+
+ return true;
+}
+void PISAPreLegalizerCombinerImpl::applyRedundantMovesPre(
+ MachineInstr &MI) const {
+ auto &BitcastMI = MI;
+ auto DstReg = BitcastMI.getOperand(0).getReg();
+ auto SrcReg = BitcastMI.getOperand(1).getReg();
+ auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ auto &ExtractMI =
+ *getDefIgnoringCopies(BuildVecMI.getOperand(1).getReg(), MRI);
+ auto VecReg = ExtractMI.getOperand(1).getReg();
+
+ if (MRI.getType(DstReg) == MRI.getType(VecReg))
+ B.buildCopy(DstReg, VecReg);
+ else
+ B.buildBitcast(DstReg, VecReg);
+ MI.eraseFromParent();
+}
+
+/// Returns divisor if operation is frcp intrinsic or fdiv with dividend equal
+/// to constant one.
+static MachineInstr *getReciprocalDivisor(MachineInstr *MI,
+ MachineRegisterInfo &MRI) {
+ if (MI->getOpcode() == TargetOpcode::G_FDIV) {
+ // Check for 1.0 / x pattern. Avoid m_GFCstOrSplat because
+ // getFConstantVRegValWithLookThrough loses bfloat16 semantics.
+ Register Dividend = MI->getOperand(1).getReg();
+ auto *DivDefMI = getDefIgnoringCopies(Dividend, MRI);
+ if (!DivDefMI || DivDefMI->getOpcode() != TargetOpcode::G_FCONSTANT)
+ return nullptr;
+ if (!DivDefMI->getOperand(1).getFPImm()->isExactlyValue(1.0))
+ return nullptr;
+ return getDefIgnoringCopies(MI->getOperand(2).getReg(), MRI);
+ }
+
+ auto *GI = dyn_cast<GIntrinsic>(MI);
+ if (GI && GI->is(Intrinsic::pisa_frcp))
+ return getDefIgnoringCopies(MI->getOperand(2).getReg(), MRI);
+
+ return nullptr;
+}
+
+// 1/(sqrt(x)) -> frsqrt(x)
+// frcp(sqrt(x)) -> frsqrt(x)
+// sqrt(1/(x)) -> frsqrt(x)
+// sqrt(frcp(x)) -> frsqrt(x)
+// 1/(fabs(sqrt(x))) -> fabs(frsqrt(x))
+// frcp(fabs(sqrt(x))) -> fabs(frsqrt(x))
+bool PISAPreLegalizerCombinerImpl::matchRcpSqrtToRsqrt(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ auto GetFrcpSrc = [=](MachineInstr *MI) -> MachineInstr * {
+ if (!MI || !MI->getFlag(MachineInstr::FmContract))
+ return nullptr;
+ return getReciprocalDivisor(MI, MRI);
+ };
+ auto GetSqrtSrc = [=](MachineInstr *MI) -> MachineInstr * {
+ if (!MI || !MI->getFlag(MachineInstr::FmContract))
+ return nullptr;
+ if (MI->getOpcode() == TargetOpcode::G_FSQRT)
+ return getDefIgnoringCopies(MI->getOperand(1).getReg(), MRI);
+ return nullptr;
+ };
+
+ bool WrapInFAbs = false;
+ MachineInstr *Divisor = GetFrcpSrc(&MI);
+ if (Divisor) {
+ // result of sqrt cannot be snan, so Intrinsic::fabs is replaced with
+ // Intrinsic::pisa_fabs. We won't get G_FABS here.
+ auto *GI = dyn_cast<GIntrinsic>(Divisor);
+ if (GI && GI->is(Intrinsic::pisa_fabs)) {
+ WrapInFAbs = true;
+ Divisor = getDefIgnoringCopies(GI->getOperand(2).getReg(), MRI);
+ }
+ }
+
+ MachineInstr *InnerMI = GetSqrtSrc(Divisor);
+ if (!InnerMI) {
+ WrapInFAbs = false;
+ InnerMI = GetFrcpSrc(GetSqrtSrc(&MI));
+ }
+ if (!InnerMI)
+ return false;
+
+ LLT DstTy = MRI.getType(MI.getOperand(0).getReg());
+ MatchInfo = [InnerMI, WrapInFAbs, DstTy, &MI](MachineIRBuilder &B) {
+ Register Src = InnerMI->getOperand(0).getReg();
+ if (!WrapInFAbs) {
+ B.buildIntrinsic(Intrinsic::pisa_frsqrt, {MI.getOperand(0)})
+ .addUse(Src)
+ .setMIFlags(MI.getFlags());
+ return;
+ }
+ // 1/|sqrt(x)| == |frsqrt(x)|
+ auto Frsqrt = B.buildIntrinsic(Intrinsic::pisa_frsqrt, {DstTy})
+ .addUse(Src)
+ .setMIFlags(MI.getFlags());
+ B.buildIntrinsic(Intrinsic::pisa_fabs, {MI.getOperand(0)})
+ .addUse(Frsqrt.getReg(0))
+ .setMIFlags(MI.getFlags());
+ };
+ return true;
+}
+
+// s32 %floor = G_FFLOOR %x
+// s32 %dst = G_FSUB %x, %floor
+// => s32 %dst = frc %x
+bool PISAPreLegalizerCombinerImpl::matchSubFloorToFrc(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ Register XReg, FloorReg;
+
+ bool IsPlainFSub = MI.getOpcode() == TargetOpcode::G_FSUB;
+ if (IsPlainFSub) {
+ XReg = MI.getOperand(1).getReg();
+ FloorReg = MI.getOperand(2).getReg();
+ } else if (auto *GI = dyn_cast<GIntrinsic>(&MI);
+ GI && GI->is(Intrinsic::pisa_fsub)) {
+ unsigned NumOps = MI.getNumOperands();
+ auto Round = static_cast<RoundingMode>(MI.getOperand(NumOps - 2).getImm());
+ int64_t Sat = MI.getOperand(NumOps - 1).getImm();
+ if (Round != RoundingMode::TowardZero || Sat)
+ return false;
+ XReg = MI.getOperand(2).getReg();
+ FloorReg = MI.getOperand(3).getReg();
+ } else {
+ return false;
+ }
+
+ auto BitWidth = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
+ if (BitWidth != 32)
+ return false;
+
+ MachineInstr *FloorMI = getDefIgnoringCopies(FloorReg, MRI);
+ if (!FloorMI || FloorMI->getOpcode() != TargetOpcode::G_FFLOOR)
+ return false;
+
+ if (IsPlainFSub && (!MI.getFlag(MachineInstr::FmContract) ||
+ !FloorMI->getFlag(MachineInstr::FmContract)))
+ return false;
+
+ if (FloorMI->getOperand(1).getReg() != XReg)
+ return false;
+
+ MatchInfo = [&MI, XReg](MachineIRBuilder &B) {
+ B.buildIntrinsic(Intrinsic::pisa_frc, {MI.getOperand(0)})
+ .addUse(XReg)
+ .setMIFlags(MI.getFlags());
+ };
+ return true;
+}
+
+// Fold `shl (zext (shl lane_id, S1)), S2` to `zext (shl lane_id, S1+S2)`.
+// lane_id currently fits in 5 bits.
+// The second shift could only appear from irtranslator pass.
+//
+// Pattern:
+// %5:_(s32) = G_CONSTANT i32 3
+// %4:_(s32) = G_INTRINSIC intrinsic(@llvm.pisa.lane.id)
+// %6:_(s32) = nuw nsw G_SHL %4:_, %5:_(s32)
+// %7:_(s64) = nneg G_ZEXT %6:_(s32)
+// %31:_(s64) = G_CONSTANT i64 2
+// %9:_(s64) = nuw nsw G_SHL %7:_, %31:_(s64)
+// =>
+// %4:_(s32) = G_INTRINSIC intrinsic(@llvm.pisa.lane.id)
+// %5:_(s32) = G_CONSTANT i32 5
+// %6:_(s32) = nuw nsw G_SHL %4:_, %5:_(s32)
+// %7:_(s64) = nneg G_ZEXT %6:_(s32)
+bool PISAPreLegalizerCombinerImpl::matchLaneIdLeftShiftChain(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ int64_t ShiftImm1, ShiftImm2;
+ MachineInstr *ZExt = nullptr;
+ MachineInstr *Intr = nullptr;
+ if (!mi_match(MI, MRI, m_GShl(m_MInstr(ZExt), m_ICst(ShiftImm1))))
+ return false;
+ if (!mi_match(ZExt, MRI, m_GZExt(m_GShl(m_MInstr(Intr), m_ICst(ShiftImm2)))))
+ return false;
+ auto *LaneId = dyn_cast<GIntrinsic>(Intr);
+ if (!LaneId || LaneId->getIntrinsicID() != Intrinsic::pisa_lane_id)
+ return false;
+
+ auto LaneIdTy = MRI.getType(LaneId->getOperand(0).getReg());
+ constexpr int MaxNumBitsInLaneId = 5;
+ if ((ShiftImm1 + ShiftImm2) >=
+ (static_cast<int>(LaneIdTy.getSizeInBits()) - MaxNumBitsInLaneId - 1))
+ return false;
+
+ MatchInfo = [=, &MI](MachineIRBuilder &B) {
+ auto NewShlReg = MRI.createGenericVirtualRegister(LaneIdTy);
+ B.buildShl(NewShlReg, LaneId->getOperand(0),
+ B.buildConstant(LaneIdTy, ShiftImm1 + ShiftImm2), MI.getFlags());
+ B.buildZExt(MI.getOperand(0), NewShlReg, ZExt->getFlags());
+ };
+ return true;
+}
+
+// G_AND (G_ZEXT x), C -> G_ZEXT (G_AND x, trunc(C)).
+// CodeGenPrepare can form the widened mask, but keeping the mask narrow lets
+// address folding see the original arithmetic shape.
+bool PISAPreLegalizerCombinerImpl::matchZExtAndToAndZExt(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ Register ZExtReg = MI.getOperand(1).getReg();
+ Register MaskReg = MI.getOperand(2).getReg();
+ auto *ZExtMI = getDefIgnoringCopies(ZExtReg, MRI);
+ auto Mask = getIConstantVRegValWithLookThrough(MaskReg, MRI);
+
+ if ((!ZExtMI || ZExtMI->getOpcode() != TargetOpcode::G_ZEXT ||
+ !Mask.has_value())) {
+ std::swap(ZExtReg, MaskReg);
+ ZExtMI = getDefIgnoringCopies(ZExtReg, MRI);
+ Mask = getIConstantVRegValWithLookThrough(MaskReg, MRI);
+ }
+
+ if (!ZExtMI || ZExtMI->getOpcode() != TargetOpcode::G_ZEXT ||
+ !Mask.has_value())
+ return false;
+
+ Register DstReg = MI.getOperand(0).getReg();
+ Register SrcReg = ZExtMI->getOperand(1).getReg();
+ LLT DstTy = MRI.getType(DstReg);
+ LLT SrcTy = MRI.getType(SrcReg);
+ if (!DstTy.isScalar() || !SrcTy.isScalar() || SrcTy.getSizeInBits() != 32 ||
+ DstTy.getSizeInBits() != 64)
+ return false;
+
+ APInt NarrowMask = Mask->Value.zextOrTrunc(SrcTy.getSizeInBits());
+ unsigned ZExtFlags = ZExtMI->getFlags();
+ MatchInfo = [=](MachineIRBuilder &B) {
+ auto NarrowMaskReg = B.buildConstant(SrcTy, NarrowMask);
+ auto NarrowAnd = B.buildAnd(SrcTy, SrcReg, NarrowMaskReg);
+ B.buildZExt(DstReg, NarrowAnd, ZExtFlags);
+ };
+ return true;
+}
+
+// Given the following pattern
+//
+// %144:_(s8) = G_EXTRACT_VECTOR_ELT %4:_(<32 x s8>), %145:_(s32)
+// %146:_(s32) = G_ZEXT %144:_(s8)
+// %147:_(s32) = nuw G_SHL %146:_, %23:_(s32)
+// %148:_(s32) = disjoint G_OR %143:_, %147:
+//
+// record the following info using BaseReg (%148) as input
+// - SrcVecReg = %4
+// - SrcVecIdx = 1 << %145
+// - bits of s32 (typeof(BaseReg)) written to by above - return value
+static uint64_t getCoveredBits(Register BaseReg, Register &SrcVecReg,
+ uint64_t *SrcVecIdx, MachineRegisterInfo &MRI) {
+ auto &SrcMI = *getDefIgnoringCopies(BaseReg, MRI);
+ switch (SrcMI.getOpcode()) {
+ case TargetOpcode::G_EXTRACT_VECTOR_ELT: {
+ auto VecReg = SrcMI.getOperand(1).getReg();
+ if (MRI.getType(VecReg).getNumElements() > 64)
+ return 0; // not supported
+ auto IdxReg = SrcMI.getOperand(2).getReg();
+ if (SrcVecReg && (SrcVecReg != VecReg))
+ return 0; // extracting from different source vector ?
+ SrcVecReg = VecReg;
+ auto Index = getIConstantVRegValWithLookThrough(IdxReg, MRI);
+ if (!Index.has_value())
+ return 0; // not a constant
+ *SrcVecIdx |= (1ull << Index->Value.getZExtValue());
+ return ~0;
+ } break;
+ case TargetOpcode::G_ZEXT: {
+ auto SrcReg = SrcMI.getOperand(1).getReg();
+ auto &ExtractMI = *getDefIgnoringCopies(SrcReg, MRI);
+ if (ExtractMI.getOpcode() != TargetOpcode::G_EXTRACT_VECTOR_ELT)
+ return 0;
+ if (getCoveredBits(SrcReg, SrcVecReg, SrcVecIdx, MRI))
+ return (1ull << MRI.getType(SrcReg).getScalarSizeInBits()) - 1;
+ } break;
+ case TargetOpcode::G_SHL: {
+ auto SrcReg = SrcMI.getOperand(1).getReg();
+ auto ShiftReg = SrcMI.getOperand(2).getReg();
+ auto &ExtMI = *getDefIgnoringCopies(SrcReg, MRI);
+ if (ExtMI.getOpcode() != TargetOpcode::G_ZEXT)
+ return 0;
+ auto Shift = getIConstantVRegValWithLookThrough(ShiftReg, MRI);
+ if (!Shift.has_value())
+ return 0; // not a constant
+ auto ShiftValue = Shift->Value.getZExtValue();
+ // make sure we are not swapping bits
+ auto OldSrcVecIdx = *SrcVecIdx;
+ auto Bits = getCoveredBits(SrcReg, SrcVecReg, SrcVecIdx, MRI);
+ auto BitSet = llvm::countr_zero(*SrcVecIdx & ~OldSrcVecIdx);
+ auto Offset = (BitSet * MRI.getType(SrcVecReg).getScalarSizeInBits()) %
+ MRI.getType(SrcReg).getSizeInBits();
+ if (ShiftValue != Offset)
+ return 0; // will not insert at proper offset
+ return Bits << ShiftValue;
+ } break;
+ case TargetOpcode::G_OR: {
+ auto LHSReg = SrcMI.getOperand(1).getReg();
+ auto RHSReg = SrcMI.getOperand(2).getReg();
+ auto LHSOp = getDefIgnoringCopies(LHSReg, MRI)->getOpcode();
+ auto RHSOp = getDefIgnoringCopies(RHSReg, MRI)->getOpcode();
+ if ((LHSOp != TargetOpcode::G_SHL) && (LHSOp != TargetOpcode::G_ZEXT) &&
+ (LHSOp != TargetOpcode::G_OR))
+ return 0;
+ if ((RHSOp != TargetOpcode::G_SHL) && (RHSOp != TargetOpcode::G_ZEXT) &&
+ (RHSOp != TargetOpcode::G_OR))
+ return 0;
+ auto LHSBits = getCoveredBits(LHSReg, SrcVecReg, SrcVecIdx, MRI);
+ auto RHSBits = getCoveredBits(RHSReg, SrcVecReg, SrcVecIdx, MRI);
+ if (LHSBits && RHSBits)
+ return LHSBits | RHSBits;
+ } break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+bool PISAPreLegalizerCombinerImpl::matchExtractInsertToBitcast(
+ MachineInstr &MI, Register &SaveReg) const {
+ auto &BuildVectorMI = MI;
+ auto DstReg = BuildVectorMI.getOperand(0).getReg();
+ auto BuildVectorTy = MRI.getType(DstReg);
+ auto NumElts = BuildVectorTy.getNumElements();
+
+ Register SrcVecReg; // G_EXTRACT_VECTOR_ELT
+ uint64_t SrcVecIdx = 0; // index of G_EXTRACT_VECTOR_ELT
+ for (unsigned I = 0; I < NumElts; I++) {
+ auto BuildVectorEltReg = BuildVectorMI.getOperand(I + 1).getReg();
+ auto Bits = getCoveredBits(BuildVectorEltReg, SrcVecReg, &SrcVecIdx, MRI);
+ if (!Bits)
+ return false; // did not match
+ if (BuildVectorTy.getSizeInBits() != MRI.getType(SrcVecReg).getSizeInBits())
+ return false;
+ if (!((1ull << I) & SrcVecIdx))
+ return false; // indices are not in ascending order
+ auto EltSize = BuildVectorTy.getScalarSizeInBits();
+ uint64_t Mask = (EltSize == 64) ? (uint64_t)-1ll : (1ull << EltSize) - 1;
+ if (Mask != Bits)
+ return false; // did not cover full element
+ }
+ auto SrcVecTy = MRI.getType(SrcVecReg);
+ uint64_t Mask = (1ull << SrcVecTy.getNumElements()) - 1;
+ if (Mask != SrcVecIdx)
+ return false; // did not extract all indices
+
+ SaveReg = SrcVecReg;
+ return true;
+}
+void PISAPreLegalizerCombinerImpl::applyExtractInsertToBitcast(
+ MachineInstr &MI, Register SrcVecReg) const {
+ auto DstReg = MI.getOperand(0).getReg();
+ if (MRI.getType(DstReg) == MRI.getType(SrcVecReg))
+ B.buildCopy(DstReg, SrcVecReg);
+ else
+ B.buildBitcast(DstReg, SrcVecReg);
+ MI.eraseFromParent();
+}
+
+// %23:_(<4 x s8>) = G_BITCAST %1:_(s32)
+// %2:_(s8) = G_EXTRACT_VECTOR_ELT %23:_(<4 x s8>), %25:_(s32)
+// %7:_(s32) = G_LSHR %1:_, %6:_(s32)
+// %8:_(s8) = G_TRUNC %7:_(s32)
+// %12:_(s32) = G_LSHR %1:_, %11:_(s32)
+// %13:_(s8) = G_TRUNC %12:_(s32)
+// %24:_(s32) = G_CONSTANT i32 3
+// %18:_(s8) = G_EXTRACT_VECTOR_ELT %23:_(<4 x s8>), %24:_(s32)
+// %19:_(<4 x s8>) = G_BUILD_VECTOR %2:_(s8), %8:_(s8), %13:_(s8), %18:_(s8)
+// => %19:_(<4 x s8>) = G_BITCAST %1:_(s32)
+bool PISAPreLegalizerCombinerImpl::matchExtractBuildVectorToBitcast(
+ MachineInstr &MI, Register &SaveReg) const {
+ auto &BuildMI = MI;
+ auto DstTy = MRI.getType(BuildMI.getOperand(0).getReg());
+
+ Register CastSrcReg; // G_BITCAST %1
+ for (unsigned I = 1; I < BuildMI.getNumOperands(); I++) {
+ auto EltReg = BuildMI.getOperand(I).getReg();
+ auto &ExtMI = *getDefIgnoringCopies(EltReg, MRI);
+ if (ExtMI.getOpcode() == TargetOpcode::G_EXTRACT_VECTOR_ELT) {
+ auto NumReg = ExtMI.getOperand(2).getReg();
+ auto NumValue = getIConstantVRegValWithLookThrough(NumReg, MRI);
+ if (!NumValue.has_value() || (NumValue->Value != (I - 1)))
+ return false;
+ auto &CastMI = *getDefIgnoringCopies(ExtMI.getOperand(1).getReg(), MRI);
+ if (CastMI.getOpcode() != TargetOpcode::G_BITCAST)
+ return false;
+ auto CastReg = CastMI.getOperand(1).getReg();
+ if (CastSrcReg && (CastSrcReg != CastReg))
+ return false;
+ if (DstTy.getSizeInBits() != MRI.getType(CastReg).getSizeInBits())
+ return false;
+ CastSrcReg = CastReg;
+ } else if (ExtMI.getOpcode() == TargetOpcode::G_TRUNC) {
+ auto &ShiftMI = *getDefIgnoringCopies(ExtMI.getOperand(1).getReg(), MRI);
+ if (ShiftMI.getOpcode() != TargetOpcode::G_LSHR)
+ return false;
+ auto ShiftValue = getIConstantVRegValWithLookThrough(
+ ShiftMI.getOperand(2).getReg(), MRI);
+ if (!ShiftValue.has_value() ||
+ (ShiftValue->Value != ((I - 1) * DstTy.getScalarSizeInBits())))
+ return false;
+ auto ShiftReg = ShiftMI.getOperand(1).getReg();
+ if (CastSrcReg && (CastSrcReg != ShiftReg))
+ return false;
+ CastSrcReg = ShiftReg;
+ } else
+ return false;
+ }
+ SaveReg = CastSrcReg;
+ return true;
+}
+void PISAPreLegalizerCombinerImpl::applyExtractBuildVectorToBitcast(
+ MachineInstr &MI, Register CastSrcReg) const {
+ auto DstReg = MI.getOperand(0).getReg();
+ B.buildBitcast(DstReg, CastSrcReg);
+ MI.eraseFromParent();
+}
+
+// Reduce predicates in s32.
+// %12:_(s1) = G_CONSTANT i1 true
+// %3:_(<3 x s1>) = G_FCMP floatpred(oeq), %0:regv3_32b(<3 x s32>), %1:_
+// %4:_(s1) = G_EXTRACT_VECTOR_ELT %3:_(<3 x s1>), %5:_(s32)
+// %6:_(s1) = G_EXTRACT_VECTOR_ELT %3:_(<3 x s1>), %7:_(s32)
+// %8:_(s1) = G_EXTRACT_VECTOR_ELT %3:_(<3 x s1>), %9:_(s32)
+// %10:_(s1) = G_AND %4:_, %6:_
+// %11:_(s1) = G_AND %10:_, %8:_
+// %13:_(s1) = G_ICMP intpred(eq), %11:_(s1), %12:_
+// => %3:_(<3 x s1>) = G_FCMP floatpred(oeq), %0:regv3_32b(<3 x s32>), %1:_
+// %18:_(<3 x s32>) = G_SEXT %3:_(<3 x s1>)
+// %20:_(s32) = G_CONSTANT i32 0
+// %19:_(s32) = G_EXTRACT_VECTOR_ELT %18:_(<3 x s32>), %20:_(s32)
+// %23:_(s32) = G_CONSTANT i32 1
+// %21:_(s32) = G_EXTRACT_VECTOR_ELT %18:_(<3 x s32>), %23:_(s32)
+// %22:_(s32) = G_AND %19:_, %21:_
+// %26:_(s32) = G_CONSTANT i32 2
+// %24:_(s32) = G_EXTRACT_VECTOR_ELT %18:_(<3 x s32>), %26:_(s32)
+// %25:_(s32) = G_AND %22:_, %24:_
+// %27:_(s32) = G_CONSTANT i32 -1
+// %13:_(s1) = G_ICMP intpred(eq), %25:_(s32), %27:_
+// Supports patterns with sext too:
+// %13:_(s8) = G_CONSTANT i8 -1
+// %3:_(<3 x s1>) = G_FCMP floatpred(oeq), %0:regv3_32b(<3 x s32>), %1:_
+// %4:_(<3 x s8>) = G_SEXT %3:_(<3 x s1>)
+// %5:_(s8) = G_EXTRACT_VECTOR_ELT %4:_(<3 x s8>), %6:_(s32)
+// %7:_(s8) = G_EXTRACT_VECTOR_ELT %4:_(<3 x s8>), %8:_(s32)
+// %9:_(s8) = G_EXTRACT_VECTOR_ELT %4:_(<3 x s8>), %10:_(s32)
+// %11:_(s8) = G_AND %5:_, %7:_
+// %12:_(s8) = G_AND %11:_, %9:_
+// %14:_(s1) = G_ICMP intpred(eq), %12:_(s8), %13:_
+bool PISAPreLegalizerCombinerImpl::matchReducePredicates(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+
+ auto DstReg = MI.getOperand(0).getReg();
+ if (!MRI.hasOneUse(DstReg))
+ return false;
+
+ auto DstTy = MRI.getType(DstReg);
+ if (!DstTy.isScalar() || (DstTy.getSizeInBits() == 32))
+ return false;
+
+ // If type is greater than s1, it must be interpreted as true/false.
+ if ((DstTy.getSizeInBits() > 1) &&
+ !(mi_match(*MRI.use_instr_begin(DstReg), MRI,
+ m_GICmp(m_Pred(), m_Reg(),
+ m_any_of(m_SpecificICst(0), m_SpecificICst(-1))))))
+ return false;
+
+ MachineInstr *VecCmpMI = nullptr;
+ APInt Mask;
+ auto ReductionOpcode = MI.getOpcode();
+
+ std::function<bool(MachineInstr *)> Match = [&](MachineInstr *MI) {
+ if (!MI)
+ return false;
+ // Next step in reduction?
+ Register LHS, RHS, Dst = MI->getOperand(0).getReg();
+ if (mi_match(Dst, MRI,
+ m_OneUse(m_BinOp(ReductionOpcode, m_Reg(LHS), m_Reg(RHS))))) {
+ return Match(getDefIgnoringCopies(LHS, MRI)) &&
+ Match(getDefIgnoringCopies(RHS, MRI));
+ }
+ // Extract from vector?
+ int64_t Idx;
+ if (mi_match(Dst, MRI,
+ m_OneUse(m_BinOp(TargetOpcode::G_EXTRACT_VECTOR_ELT,
+ m_Reg(LHS), m_ICst(Idx))))) {
+ if (!Match(getDefIgnoringCopies(LHS, MRI)))
+ return false;
+ Mask.setBit(Idx);
+ return true;
+ }
+ // Sext?
+ if (MI->getOpcode() == TargetOpcode::G_SEXT) {
+ return Match(getDefIgnoringCopies(MI->getOperand(1).getReg(), MRI));
+ }
+ // Cmp?
+ if (mi_match(Dst, MRI,
+ m_any_of(m_GICmp(m_Pred(), m_Reg(), m_Reg()),
+ m_GFCmp(m_Pred(), m_Reg(), m_Reg())))) {
+ if (VecCmpMI)
+ return VecCmpMI == MI;
+ auto VecType = MRI.getType(MI->getOperand(0).getReg());
+ if (!VecType.isFixedVector())
+ return false;
+ // Found cmp producing vector of predicates.
+ VecCmpMI = MI;
+ Mask = APInt::getZero(VecType.getNumElements());
+ return true;
+ }
+ return false;
+ };
+
+ if (!Match(&MI) || !Mask.isAllOnes())
+ return false;
+
+ MatchInfo = [DstReg, Mask = std::move(Mask), VecCmpMI, ReductionOpcode,
+ this](MachineIRBuilder &B) {
+ const LLT S32 = LLT::integer(32);
+
+ // Build reduction in s32.
+ auto ExtendedVector = MRI.createGenericVirtualRegister(
+ LLT::fixed_vector(Mask.getBitWidth(), S32));
+ B.buildSExt(ExtendedVector, VecCmpMI->getOperand(0).getReg());
+
+ Register Reduction = MRI.createGenericVirtualRegister(S32);
+ B.buildExtractVectorElement(Reduction, ExtendedVector,
+ B.buildConstant(S32, 0));
+
+ for (unsigned I = 1; I < Mask.getBitWidth(); ++I) {
+ auto SecondSrc = MRI.createGenericVirtualRegister(S32);
+ auto Dst = MRI.createGenericVirtualRegister(S32);
+ B.buildExtractVectorElement(SecondSrc, ExtendedVector,
+ B.buildConstant(S32, I));
+ B.buildInstr(ReductionOpcode, {Dst}, {Reduction, SecondSrc});
+ Reduction = Dst;
+ }
+
+ // Replace next use if possible.
+ auto UseMI = MRI.use_instr_begin(DstReg);
+ switch (UseMI->getOpcode()) {
+ case TargetOpcode::G_ICMP: {
+ auto Pred = (CmpInst::Predicate)UseMI->getOperand(1).getPredicate();
+ if (mi_match(UseMI->getOperand(3).getReg(), MRI, m_SpecificICst(-1)))
+ Pred = CmpInst::getInversePredicate(Pred);
+ B.buildICmp(Pred, UseMI->getOperand(0).getReg(), Reduction,
+ B.buildConstant(S32, 0));
+ UseMI->eraseFromParent();
+ } break;
+ default: {
+ B.buildICmp(CmpInst::ICMP_NE, DstReg, Reduction, B.buildConstant(S32, 0));
+ } break;
+ }
+ };
+ return true;
+}
+
+// %9:_(s1) = G_CONSTANT i1 true
+// %8:_(s1) = G_ICMP intpred(eq), %15:_(s32), %22:_
+// %10:_(s1) = G_ICMP intpred(eq), %8:_(s1), %9:_
+// %11:_(s32) = G_SEXT %10:_(s1)
+// => %8:_(s1) = G_ICMP intpred(eq), %15:_(s32), %22:_
+// %11:_(s32) = G_SEXT %8:_(s1)
+// or
+// %12:_(s1) = G_CONSTANT i1 true
+// %11:_(s1) = G_AND %10:_, %35:_
+// %13:_(s1) = G_ICMP intpred(eq), %11:_(s1), %12:_
+// %14:_(s32) = G_SEXT %13:_(s1)
+// => %11:_(s1) = G_AND %10:_, %35:_
+// %14:_(s32) = G_SEXT %11:_(s1)
+bool PISAPreLegalizerCombinerImpl::matchCmpInt1(
+ MachineInstr &ICmpMI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+
+ MachineInstr *PrevMI = nullptr;
+ CmpInst::Predicate Pred;
+ int64_t IsTrue;
+
+ if (!mi_match(
+ ICmpMI, MRI,
+ m_GICmp(m_Pred(Pred),
+ m_all_of(m_SpecificType(LLT::integer(1)), m_MInstr(PrevMI)),
+ m_ICst(IsTrue))))
+ return false;
+
+ if (Pred != CmpInst::ICMP_EQ)
+ return false;
+
+ if (!PrevMI)
+ return false;
+
+ auto DstReg = ICmpMI.getOperand(0).getReg();
+ MatchInfo = [DstReg, PrevMI, IsTrue, this](MachineIRBuilder &B) {
+ auto PrevDstReg = PrevMI->getOperand(0).getReg();
+
+ if (IsTrue) {
+ if (MRI.hasOneUse(PrevDstReg))
+ PrevMI->getOperand(0).setReg(DstReg);
+ else
+ B.buildCopy(DstReg, PrevDstReg);
+ } else {
+ B.buildNot(DstReg, PrevDstReg);
+ }
+ };
+ return true;
+}
+
+// Pass boilerplate
+// ================
+
+class PISAPreLegalizerCombiner : public MachineFunctionPass {
+ PISAPreLegalizerCombinerImplRuleConfig RuleConfig;
+
+public:
+ static char ID;
+
+ PISAPreLegalizerCombiner();
+
+ StringRef getPassName() const override { return "PISAPreLegalizerCombiner"; }
+
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+} // end anonymous namespace
+
+void PISAPreLegalizerCombiner::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired<TargetPassConfig>();
+ AU.setPreservesCFG();
+ getSelectionDAGFallbackAnalysisUsage(AU);
+ AU.addRequired<GISelValueTrackingAnalysisLegacy>();
+ AU.addPreserved<GISelValueTrackingAnalysisLegacy>();
+ AU.addRequired<MachineDominatorTreeWrapperPass>();
+ AU.addPreserved<MachineDominatorTreeWrapperPass>();
+
+ AU.addRequired<GISelCSEAnalysisWrapperPass>();
+ AU.addPreserved<GISelCSEAnalysisWrapperPass>();
+ MachineFunctionPass::getAnalysisUsage(AU);
+}
+
+PISAPreLegalizerCombiner::PISAPreLegalizerCombiner() : MachineFunctionPass(ID) {
+ initializePISAPreLegalizerCombinerPass(*PassRegistry::getPassRegistry());
+ if (!RuleConfig.parseCommandLineOption())
+ report_fatal_error("Invalid rule identifier");
+}
+
+bool PISAPreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+ if (MF.getProperties().hasProperty(
+ MachineFunctionProperties::Property::FailedISel))
+ return false;
+
+ auto &TPC = getAnalysis<TargetPassConfig>();
+ const Function &F = MF.getFunction();
+ bool EnableOpt =
+ MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
+ GISelValueTracking *KB =
+ &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
+ MachineDominatorTree *MDT =
+ &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+ CombinerInfo CInfo(
+ /*AllowIllegalOps=*/true, /*ShouldLegalizeIllegal=*/false,
+ /*LegalizerInfo=*/nullptr, EnableOpt, F.hasOptSize(), F.hasMinSize());
+ // Enable CSE.
+ GISelCSEAnalysisWrapper &Wrapper =
+ getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
+ auto *CSEInfo = &Wrapper.get(TPC.getCSEConfig());
+
+ const PISASubtarget &STI = MF.getSubtarget<PISASubtarget>();
+ PISAPreLegalizerCombinerImpl Impl(MF, CInfo, *KB, CSEInfo, RuleConfig, STI,
+ MDT, STI.getLegalizerInfo());
+ return Impl.combineMachineInstrs();
+}
+
+// select(trunc(wide -> i1), N 1, N 0) => trunc(and(wide, 1), N)
+// Avoids introducing an illegal i1 compare when lowering bool-to-int.
+bool PISAPreLegalizerCombinerImpl::matchSelectTruncOneZero(
+ MachineInstr &MI,
+ std::function<void(MachineIRBuilder &)> &MatchInfo) const {
+ assert(MI.getOpcode() == TargetOpcode::G_SELECT);
+ GSelect &Sel = cast<GSelect>(MI);
+
+ // Condition must be i1.
+ Register Cond = Sel.getCondReg();
+ if (MRI.getType(Cond) != LLT::scalar(1))
+ return false;
+
+ // Condition must come from a G_TRUNC.
+ MachineInstr *TruncMI = getDefIgnoringCopies(Cond, MRI);
+ if (!TruncMI || TruncMI->getOpcode() != TargetOpcode::G_TRUNC)
+ return false;
+
+ Register Wide = TruncMI->getOperand(1).getReg();
+ LLT WideTy = MRI.getType(Wide);
+ LLT DstTy = MRI.getType(Sel.getReg(0));
+
+ // Only handle scalar integers where dst fits in the wide source.
+ if (!DstTy.isScalar() || !WideTy.isScalar())
+ return false;
+ if (DstTy.getScalarSizeInBits() >= WideTy.getScalarSizeInBits())
+ return false;
+
+ // True value must be 1, false value must be 0.
+ auto TrueOpt = getIConstantVRegValWithLookThrough(Sel.getTrueReg(), MRI);
+ auto FalseOpt = getIConstantVRegValWithLookThrough(Sel.getFalseReg(), MRI);
+ if (!TrueOpt || !FalseOpt)
+ return false;
+ if (!TrueOpt->Value.isOne() || !FalseOpt->Value.isZero())
+ return false;
+
+ Register DstReg = Sel.getReg(0);
+ MatchInfo = [=](MachineIRBuilder &B) {
+ auto One = B.buildConstant(WideTy, 1);
+ auto And = B.buildAnd(WideTy, Wide, One);
+ B.buildTrunc(DstReg, And);
+ };
+ return true;
+}
+
+char PISAPreLegalizerCombiner::ID = 0;
+INITIALIZE_PASS_BEGIN(PISAPreLegalizerCombiner, DEBUG_TYPE,
+ "Combine PISA machine instrs before legalization", false,
+ false)
+INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
+INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
+INITIALIZE_PASS_DEPENDENCY(MachineDominatorTreeWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
+INITIALIZE_PASS_END(PISAPreLegalizerCombiner, DEBUG_TYPE,
+ "Combine PISA machine instrs before legalization", false,
+ false)
+
+namespace llvm {
+FunctionPass *createPISAPreLegalizerCombiner() {
+ return new PISAPreLegalizerCombiner();
+}
+} // end namespace llvm
diff --git a/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp b/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
new file mode 100644
index 00000000000000..fb8fa3f69ed172
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
@@ -0,0 +1,53 @@
+//===-- PISARegisterBankInfo.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
+//
+//===----------------------------------------------------------------------===//
+
+#include "PISARegisterBankInfo.h"
+#include "PISARegisterInfo.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/RegisterBank.h"
+
+#define GET_REGINFO_ENUM
+#include "PISAGenRegisterInfo.inc"
+
+#define GET_TARGET_REGBANK_IMPL
+#include "PISAGenRegisterBank.inc"
+
+using namespace llvm;
+
+const RegisterBankInfo::InstructionMapping &
+PISARegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
+ const RegisterBankInfo::InstructionMapping &Mapping = getInstrMappingImpl(MI);
+
+ if (Mapping.isValid())
+ return Mapping;
+
+ const MachineFunction &MF = *MI.getParent()->getParent();
+ const MachineRegisterInfo &MRI = MF.getRegInfo();
+ const TargetRegisterInfo *TRI = MRI.getTargetRegisterInfo();
+
+ SmallVector<const ValueMapping *, 8> OpdsMapping(MI.getNumOperands());
+
+ for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
+ auto &MO = MI.getOperand(Idx);
+
+ if (MO.isReg() && MO.getReg().isValid()) {
+ unsigned Size = getSizeInBits(MO.getReg(), MRI, *TRI);
+ OpdsMapping[Idx] = &getValueMapping(0, Size, PISA::RegistersRegBank);
+ }
+ }
+
+ return getInstructionMapping(DefaultMappingID, 1,
+ getOperandsMapping(OpdsMapping),
+ MI.getNumOperands());
+}
+
+const RegisterBank &PISARegisterBankInfo::getRegBankFromRegClass(
+ const TargetRegisterClass & /* RC */, LLT /* Ty */) const {
+ return PISA::RegistersRegBank;
+}
diff --git a/llvm/lib/Target/PISA/PISARegisterBankInfo.h b/llvm/lib/Target/PISA/PISARegisterBankInfo.h
new file mode 100644
index 00000000000000..705fa93c8c5004
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISARegisterBankInfo.h
@@ -0,0 +1,37 @@
+//===-- PISARegisterBankInfo.h --------------------------------------------===//
+//
+// 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_PISA_PISAREGISTERBANKINFO_H
+#define LLVM_LIB_TARGET_PISA_PISAREGISTERBANKINFO_H
+
+#include "llvm/CodeGen/RegisterBankInfo.h"
+
+#define GET_REGBANK_DECLARATIONS
+#include "PISAGenRegisterBank.inc"
+
+namespace llvm {
+
+class TargetRegisterInfo;
+
+class PISAGenRegisterBankInfo : public RegisterBankInfo {
+protected:
+#define GET_TARGET_REGBANK_CLASS
+#include "PISAGenRegisterBank.inc"
+};
+
+// This class provides the information for the target register banks.
+class PISARegisterBankInfo final : public PISAGenRegisterBankInfo {
+public:
+ const RegisterBank &getRegBankFromRegClass(const TargetRegisterClass &RC,
+ LLT Ty) const override;
+
+ const InstructionMapping &
+ getInstrMapping(const MachineInstr &MI) const override;
+};
+} // namespace llvm
+#endif // LLVM_LIB_TARGET_PISA_PISAREGISTERBANKINFO_H
diff --git a/llvm/lib/Target/PISA/PISASubtarget.cpp b/llvm/lib/Target/PISA/PISASubtarget.cpp
index 84d48180cae2e4..bfc7c7390c2cc7 100644
--- a/llvm/lib/Target/PISA/PISASubtarget.cpp
+++ b/llvm/lib/Target/PISA/PISASubtarget.cpp
@@ -8,7 +8,11 @@
#include "PISASubtarget.h"
#include "PISA.h"
+#include "PISALegalizerInfo.h"
+#include "PISARegisterBankInfo.h"
#include "PISATargetMachine.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/TargetParser/Host.h"
using namespace llvm;
@@ -21,10 +25,28 @@ using namespace llvm;
PISASubtarget::PISASubtarget(const Triple &TT, const std::string &CPU,
const std::string &FS, const PISATargetMachine &TM)
: PISAGenSubtargetInfo(TT, CPU, /*TuneCPU=*/CPU, FS), InstrInfo(*this),
- FrameLowering(initSubtargetDependencies(CPU, FS)) {}
+ FrameLowering(initSubtargetDependencies(CPU, FS)), TLInfo(TM, *this) {
+
+ CallLoweringInfo = std::make_unique<PISACallLowering>(TLInfo);
+ InlineAsmLoweringInfo = std::make_unique<InlineAsmLowering>(&TLInfo);
+ Legalizer = std::make_unique<PISALegalizerInfo>(*this);
+ RegBankInfo = std::make_unique<PISARegisterBankInfo>();
+ LLT::setUseExtended(true); // enable bfloat support
+ // The instruction selector is created in a subsequent change.
+}
PISASubtarget &PISASubtarget::initSubtargetDependencies(StringRef CPU,
StringRef FS) {
ParseSubtargetFeatures(CPU, /*TuneCPU=*/CPU, FS);
+ if (CPU.empty())
+ CPU = PISA::stripCPUPrefix(PISA::getDefaultCPUName());
+ PISATarget = PISA::getPISATargetInfo(CPU);
return *this;
}
+
+// Determine compatibility of instruction's PISA target, specified via
+// "let Predicates = []", vs. platform's target, specified via -mcpu=
+bool PISASubtarget::supportsPISATarget(StringRef Name) const {
+ auto InstrPISATarget = PISA::getPISATargetInfo(Name);
+ return isCompatiblePISATargetInfo(PISATarget, InstrPISATarget);
+}
diff --git a/llvm/lib/Target/PISA/PISASubtarget.h b/llvm/lib/Target/PISA/PISASubtarget.h
index 9f64ffa7f7ef0d..857be0afadf0a3 100644
--- a/llvm/lib/Target/PISA/PISASubtarget.h
+++ b/llvm/lib/Target/PISA/PISASubtarget.h
@@ -9,11 +9,21 @@
#ifndef LLVM_LIB_TARGET_PISA_PISASUBTARGET_H
#define LLVM_LIB_TARGET_PISA_PISASUBTARGET_H
+#include "PISACallLowering.h"
#include "PISAFrameLowering.h"
+#include "PISAISelLowering.h"
#include "PISAInstrInfo.h"
+#include "llvm/CodeGen/GlobalISel/CallLowering.h"
+#include "llvm/CodeGen/GlobalISel/InlineAsmLowering.h"
+#include "llvm/CodeGen/GlobalISel/InstructionSelector.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h"
+#include "llvm/CodeGen/RegisterBankInfo.h"
+#include "llvm/CodeGen/SelectionDAGTargetInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/DataLayout.h"
+#include "llvm/Support/PISAAddrSpace.h"
#include "llvm/Target/TargetMachine.h"
+#include "llvm/TargetParser/PISATargetParser.h"
#define GET_SUBTARGETINFO_HEADER
#include "PISAGenSubtargetInfo.inc"
@@ -21,9 +31,10 @@
namespace llvm {
class StringRef;
class PISATargetMachine;
-
class PISASubtarget : public PISAGenSubtargetInfo {
private:
+ PISA::PISATargetInfo PISATarget;
+
// Bool members for features defined in PISAFeatures.td.
#define GET_SUBTARGETINFO_MACRO(ATTRIBUTE, DEFAULT, GETTER) \
bool ATTRIBUTE = DEFAULT;
@@ -31,6 +42,14 @@ class PISASubtarget : public PISAGenSubtargetInfo {
PISAInstrInfo InstrInfo;
PISAFrameLowering FrameLowering;
+ PISATargetLowering TLInfo;
+
+ // GlobalISel related APIs.
+ std::unique_ptr<CallLowering> CallLoweringInfo;
+ std::unique_ptr<InlineAsmLowering> InlineAsmLoweringInfo;
+ std::unique_ptr<RegisterBankInfo> RegBankInfo;
+ std::unique_ptr<LegalizerInfo> Legalizer;
+ std::unique_ptr<InstructionSelector> InstSelector;
public:
// This constructor initializes the data members to match that
@@ -44,10 +63,35 @@ class PISASubtarget : public PISAGenSubtargetInfo {
// PISAFeatures.td.
void ParseSubtargetFeatures(StringRef CPU, StringRef TuneCPU, StringRef FS);
+ StringRef getPISATargetName() const { return PISATarget.Name; };
+ bool supportsPISATarget(StringRef Name) const;
+
+ bool shouldPrefetchAddressSpace(unsigned AS) const override {
+ return AS == static_cast<unsigned>(PISAAS::AddressSpace::GLOBAL);
+ }
+
+ const CallLowering *getCallLowering() const override {
+ return CallLoweringInfo.get();
+ }
+ const InlineAsmLowering *getInlineAsmLowering() const override {
+ return InlineAsmLoweringInfo.get();
+ }
+ const RegisterBankInfo *getRegBankInfo() const override {
+ return RegBankInfo.get();
+ }
+ const LegalizerInfo *getLegalizerInfo() const override {
+ return Legalizer.get();
+ }
+ InstructionSelector *getInstructionSelector() const override {
+ return InstSelector.get();
+ }
const PISAInstrInfo *getInstrInfo() const override { return &InstrInfo; }
const PISAFrameLowering *getFrameLowering() const override {
return &FrameLowering;
}
+ const PISATargetLowering *getTargetLowering() const override {
+ return &TLInfo;
+ }
const PISARegisterInfo *getRegisterInfo() const override {
return &InstrInfo.getRegisterInfo();
}
diff --git a/llvm/lib/Target/PISA/PISATargetMachine.cpp b/llvm/lib/Target/PISA/PISATargetMachine.cpp
index 79bb260240b3a8..f4b3e163a3b8dc 100644
--- a/llvm/lib/Target/PISA/PISATargetMachine.cpp
+++ b/llvm/lib/Target/PISA/PISATargetMachine.cpp
@@ -8,12 +8,24 @@
#include "PISATargetMachine.h"
#include "PISA.h"
+#include "PISAMachineFunctionInfo.h"
+#include "PISATargetObjectFile.h"
#include "TargetInfo/PISATargetInfo.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
+#include "llvm/Analysis/TargetTransformInfo.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/TargetPassConfig.h"
#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/IntrinsicsPISA.h"
+#include "llvm/InitializePasses.h"
#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Support/PISAAddrSpace.h"
#include "llvm/Target/TargetOptions.h"
using namespace llvm;
@@ -22,6 +34,18 @@ using namespace llvm;
extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializePISATarget() {
// Register the target.
RegisterTargetMachine<PISATargetMachine> Y(getThePISATarget());
+
+ PassRegistry &PR = *PassRegistry::getPassRegistry();
+ initializeGlobalISel(PR);
+ initializePISAPreLegalizerCombinerPass(PR);
+ initializePISAPostLegalizerCombinerPass(PR);
+}
+
+MachineFunctionInfo *PISATargetMachine::createMachineFunctionInfo(
+ BumpPtrAllocator &Allocator, const Function &F,
+ const TargetSubtargetInfo *STI) const {
+ return PISAMachineFunctionInfo::create<PISAMachineFunctionInfo>(
+ Allocator, F, static_cast<const PISASubtarget *>(STI));
}
static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
@@ -30,6 +54,9 @@ static Reloc::Model getEffectiveRelocModel(std::optional<Reloc::Model> RM) {
return *RM;
}
+// Pin PISATargetObjectFile's vtables to this file.
+PISATargetObjectFile::~PISATargetObjectFile() {}
+
PISATargetMachine::PISATargetMachine(const Target &T, const Triple &TT,
StringRef CPU, StringRef FS,
const TargetOptions &Options,
@@ -39,7 +66,7 @@ PISATargetMachine::PISATargetMachine(const Target &T, const Triple &TT,
: CodeGenTargetMachineImpl(T, TT.computeDataLayout(), TT, CPU, FS, Options,
getEffectiveRelocModel(RM),
getEffectiveCodeModel(CM, CodeModel::Small), OL),
- TLOF(std::make_unique<TargetLoweringObjectFileELF>()),
+ TLOF(std::make_unique<PISATargetObjectFile>()),
Subtarget(TT, CPU.str(), FS.str(), *this) {
initAsmInfo();
setGlobalISel(true);
@@ -62,21 +89,120 @@ PISATargetMachine::getSubtargetImpl(const Function &F) const {
Key.append(FS);
auto &I = SubtargetMap[Key];
- if (!I)
+ if (!I) {
I = std::make_unique<PISASubtarget>(TargetTriple, CPU.str(), FS.str(),
*this);
+ }
return I.get();
}
+unsigned PISATargetMachine::getAssumedAddrSpace(const Value *V) const {
+ const auto *Ld = dyn_cast<LoadInst>(V);
+ if (!Ld || Ld->getPointerOperand()->getType()->getPointerAddressSpace() !=
+ unsigned(PISAAS::AddressSpace::CONSTANT))
+ return ~0U;
+ return unsigned(PISAAS::AddressSpace::GLOBAL);
+}
+
+std::pair<const Value *, unsigned>
+PISATargetMachine::getPredicatedAddrSpace(const Value *V) const {
+ auto *II = dyn_cast<IntrinsicInst>(V);
+ if (!II)
+ return std::make_pair(nullptr, -1);
+
+ switch (II->getIntrinsicID()) {
+ case Intrinsic::pisa_isaddr_private:
+ return std::make_pair(II->getArgOperand(0),
+ unsigned(PISAAS::AddressSpace::PRIVATE));
+ case Intrinsic::pisa_isaddr_global:
+ return std::make_pair(II->getArgOperand(0),
+ unsigned(PISAAS::AddressSpace::GLOBAL));
+ case Intrinsic::pisa_isaddr_shared:
+ return std::make_pair(II->getArgOperand(0),
+ unsigned(PISAAS::AddressSpace::SHARED));
+ default:
+ break;
+ }
+ return std::make_pair(nullptr, -1);
+}
+
+TargetTransformInfo
+PISATargetMachine::getTargetTransformInfo(const Function &F) const {
+ return TargetTransformInfo(F.getDataLayout());
+}
+
namespace {
+// PISA Code Generator Pass Configuration Options.
+//
+// PISA is a virtual-register-only target: it maintains virtual registers
+// throughout the pipeline and does not run register allocation. This
+// configuration wires up the GlobalISel selection stages and disables the
+// standard machine passes that assume physical registers exist.
class PISAPassConfig : public TargetPassConfig {
public:
PISAPassConfig(PISATargetMachine &TM, PassManagerBase &PM)
- : TargetPassConfig(TM, PM) {}
+ : TargetPassConfig(TM, PM) {
+ disablePass(&GCLoweringID);
+ disablePass(&ShadowStackGCLoweringID);
+ }
PISATargetMachine &getPISATargetMachine() const {
return getTM<PISATargetMachine>();
}
+
+ void addIRPasses() override {
+ TargetPassConfig::addIRPasses();
+
+ // Disable passes that assume physical registers exist.
+ disablePass(&PrologEpilogCodeInserterID);
+ disablePass(&MachineLateInstrsCleanupID);
+ disablePass(&MachineCopyPropagationID);
+ disablePass(&TailDuplicateLegacyID);
+ disablePass(&StackMapLivenessID);
+ disablePass(&LiveDebugValuesID);
+ disablePass(&PostRAMachineSinkingID);
+ disablePass(&PostRASchedulerID);
+ disablePass(&FuncletLayoutID);
+ disablePass(&PatchableFunctionID);
+ disablePass(&ShrinkWrapID);
+ disablePass(&RemoveLoadsIntoFakeUsesID);
+ disablePass(&GCMachineCodeAnalysisID);
+ }
+
+ bool addIRTranslator() override {
+ addPass(new IRTranslatorLegacy(getOptLevel()));
+ return false;
+ }
+
+ void addPreLegalizeMachineIR() override {
+ if (getOptLevel() != CodeGenOptLevel::None)
+ addPass(createPISAPreLegalizerCombiner());
+ }
+
+ bool addLegalizeMachineIR() override {
+ addPass(new LegalizerLegacy());
+ return false;
+ }
+
+ void addPreRegBankSelect() override {
+ if (getOptLevel() != CodeGenOptLevel::None) {
+ addPass(&MachineCSELegacyID);
+ addPass(createPISAPostLegalizerCombiner());
+ }
+ }
+
+ bool addRegBankSelect() override {
+ addPass(new RegBankSelect());
+ return false;
+ }
+
+ // Instruction selection (addGlobalInstructionSelect) is added in a
+ // subsequent change together with the PISA instruction selector.
+
+ // PISA does not allocate physical registers.
+ FunctionPass *createTargetRegisterAllocator(bool) override { return nullptr; }
+ bool addRegAssignAndRewriteFast() override { return false; }
+ bool addRegAssignAndRewriteOptimized() override { return false; }
};
} // namespace
diff --git a/llvm/lib/Target/PISA/PISATargetMachine.h b/llvm/lib/Target/PISA/PISATargetMachine.h
index a378e4f20401ae..5e98087d2272fd 100644
--- a/llvm/lib/Target/PISA/PISATargetMachine.h
+++ b/llvm/lib/Target/PISA/PISATargetMachine.h
@@ -12,6 +12,7 @@
#include "PISASubtarget.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/CodeGen/CodeGenTargetMachineImpl.h"
+#include "llvm/Support/PISAAddrSpace.h"
#include <memory>
#include <optional>
@@ -33,13 +34,40 @@ class PISATargetMachine : public CodeGenTargetMachineImpl {
// target-specific attributes of each function.
const PISASubtarget *getSubtargetImpl() const = delete;
- TargetPassConfig *createPassConfig(PassManagerBase &PM) override;
+ TargetTransformInfo getTargetTransformInfo(const Function &F) const override;
+ TargetPassConfig *createPassConfig(PassManagerBase &PM) override;
bool usesPhysRegsForValues() const override { return false; }
TargetLoweringObjectFile *getObjFileLowering() const override {
return TLOF.get();
}
+
+ MachineFunctionInfo *
+ createMachineFunctionInfo(BumpPtrAllocator &Allocator, const Function &F,
+ const TargetSubtargetInfo *STI) const override;
+
+ static int64_t getNullPointerValue(unsigned AS) {
+ return (AS == (unsigned)PISAAS::AddressSpace::PRIVATE ||
+ AS == (unsigned)PISAAS::AddressSpace::SHARED)
+ ? -1
+ : 0;
+ }
+
+ bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DstAS) const override {
+ auto Sas = (SrcAS == (unsigned)PISAAS::AddressSpace::GLOBAL) ||
+ (SrcAS == (unsigned)PISAAS::AddressSpace::CONSTANT) ||
+ (SrcAS == (unsigned)PISAAS::AddressSpace::GENERIC);
+ auto Das = (DstAS == (unsigned)PISAAS::AddressSpace::GLOBAL) ||
+ (DstAS == (unsigned)PISAAS::AddressSpace::CONSTANT) ||
+ (DstAS == (unsigned)PISAAS::AddressSpace::GENERIC);
+ return Sas && Das;
+ }
+
+ unsigned getAssumedAddrSpace(const Value *V) const override;
+
+ std::pair<const Value *, unsigned>
+ getPredicatedAddrSpace(const Value *V) const override;
};
} // namespace llvm
diff --git a/llvm/lib/Target/PISA/PISATargetObjectFile.h b/llvm/lib/Target/PISA/PISATargetObjectFile.h
new file mode 100644
index 00000000000000..360103ed3f6ecf
--- /dev/null
+++ b/llvm/lib/Target/PISA/PISATargetObjectFile.h
@@ -0,0 +1,59 @@
+//===-- PISATargetObjectFile.h - PISA Object Info -------------------------===//
+//
+// 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_PISA_PISATARGETOBJECTFILE_H
+#define LLVM_LIB_TARGET_PISA_PISATARGETOBJECTFILE_H
+
+#include "llvm/IR/GlobalValue.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCSection.h"
+#include "llvm/MC/SectionKind.h"
+#include "llvm/Target/TargetLoweringObjectFile.h"
+#include "llvm/Target/TargetMachine.h"
+
+namespace llvm {
+
+class PISATargetObjectFile : public TargetLoweringObjectFile {
+public:
+ ~PISATargetObjectFile() override;
+
+ void Initialize(MCContext &Ctx, const TargetMachine &TM) override {
+ TargetLoweringObjectFile::Initialize(Ctx, TM);
+ }
+ // All words in a PISA module (excepting the first 5 ones) are a linear
+ // sequence of instructions in a specific order. We put all the instructions
+ // in the single text section.
+ MCSection *getSectionForConstant(const DataLayout &DL, SectionKind Kind,
+ const Constant *C, Align &Alignment,
+ const Function *F) const override {
+ return TextSection;
+ }
+ MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind,
+ const TargetMachine &TM) const override {
+ return TextSection;
+ }
+ MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
+ const TargetMachine &TM) const override {
+ return TextSection;
+ }
+ // PISA doesn't want '\01' suppression mangling to strip the leading '\01'.
+ // A named variable should just be passed along as is.
+ MCSymbol *getTargetSymbol(const GlobalValue *GV,
+ const TargetMachine &TM) const override {
+ SmallString<128> Name;
+ if (GV->hasName())
+ Name = GV->getName();
+ else
+ TM.getNameWithPrefix(Name, GV, getMangler());
+ return getContext().getOrCreateSymbol(Name);
+ }
+};
+
+} // end namespace llvm
+
+#endif // LLVM_LIB_TARGET_PISA_PISATARGETOBJECTFILE_H
diff --git a/llvm/lib/TargetParser/CMakeLists.txt b/llvm/lib/TargetParser/CMakeLists.txt
index cb45571583d23d..29c058677dab62 100644
--- a/llvm/lib/TargetParser/CMakeLists.txt
+++ b/llvm/lib/TargetParser/CMakeLists.txt
@@ -23,6 +23,7 @@ add_llvm_component_library(LLVMTargetParser
Host.cpp
LoongArchTargetParser.cpp
NVPTXTargetParser.cpp
+ PISATargetParser.cpp
PPCTargetParser.cpp
RISCVISAInfo.cpp
RISCVTargetParser.cpp
diff --git a/llvm/lib/TargetParser/PISATargetParser.cpp b/llvm/lib/TargetParser/PISATargetParser.cpp
new file mode 100644
index 00000000000000..8b84d7eecd3ef7
--- /dev/null
+++ b/llvm/lib/TargetParser/PISATargetParser.cpp
@@ -0,0 +1,24 @@
+//===-- PISATargetParser.cpp - PISA target parsing defines ----------------===//
+//
+// 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/TargetParser/PISATargetParser.h"
+#include "llvm/TargetParser/TargetParser.h"
+
+#define GET_SUBTARGETFEATURES_ENUM
+#define GET_SUBTARGETFEATURES_KV
+#include "llvm/TargetParser/PISAGenTargetFeatures.inc"
+
+void llvm::PISA::fillFeatureMap(StringRef CPU, StringMap<bool> &Features) {
+ PISATargetInfo Info = getPISATargetInfo(stripCPUPrefix(CPU));
+ if (Info.Name.empty())
+ return;
+ if (std::optional<StringMap<bool>> Default = getCPUDefaultTargetFeatures(
+ Info.Name, BasicPISASubTypeKV, BasicPISAFeatureKV))
+ for (const auto &KV : *Default)
+ Features[KV.first()] = KV.second;
+}
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir b/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
new file mode 100644
index 00000000000000..3c2e0376c6fda1
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
@@ -0,0 +1,45 @@
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# Regression test to make sure truncating double into i8 doesn't try to produce an 8 bit float.
+--- |
+ target triple = "pisa"
+
+ define pisa_kernel void @test(i8 %id) {
+ %b = bitcast <1 x double> zeroinitializer to <8 x i8>
+ %elem = extractelement <8 x i8> %b, i8 %id
+ store i8 %elem, ptr addrspace(1) null, align 1
+ ret void
+ }
+...
+---
+name: test
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ ; CHECK-LABEL: {{^}}name: test
+ ; CHECK: [[CONST:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK: [[BITCAST:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[CONST]](i64)
+ ; CHECK: G_EXTRACT_VECTOR_ELT [[BITCAST]](<2 x i32>)
+ %0:reg8b(i8) = loadParam_i8 0, 0
+ %2:_(i64) = G_FCONSTANT double 0.000000e+00
+ %6:_(p1) = G_CONSTANT i64 0
+ %20:_(i8) = G_TRUNC %2:_(i64)
+ %21:_(i8) = G_CONSTANT i8 0
+ %28:_(<4 x i8>) = G_BUILD_VECTOR %20:_(i8), %21:_(i8), %21:_(i8), %21:_(i8)
+ %29:_(<4 x i8>) = G_BUILD_VECTOR %21:_(i8), %21:_(i8), %21:_(i8), %21:_(i8)
+ %5:_(i32) = G_ZEXT %0:reg8b(i8)
+ %18:_(i32) = G_BITCAST %28:_(<4 x i8>)
+ %19:_(i32) = G_BITCAST %29:_(<4 x i8>)
+ %7:_(<2 x i32>) = G_BUILD_VECTOR %18:_(i32), %19:_(i32)
+ %8:_(i32) = G_CONSTANT i32 2
+ %9:_(i32) = G_LSHR %5:_, %8:_(i32)
+ %10:_(i32) = G_EXTRACT_VECTOR_ELT %7:_(<2 x i32>), %9:_(i32)
+ %11:_(i32) = G_CONSTANT i32 3
+ %12:_(i32) = G_AND %5:_, %11:_
+ %14:_(i32) = G_SHL %12:_, %11:_(i32)
+ %15:_(i32) = G_LSHR %10:_, %14:_(i32)
+ %4:_(i8) = G_TRUNC %15:_(i32)
+ G_STORE %4:_(i8), %6:_(p1) :: (store (i8) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir
new file mode 100644
index 00000000000000..b0dec696aeedee
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir
@@ -0,0 +1,48 @@
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+--- |
+ define i32 @combine_abs_max_i32(i32 %data, i32 %mask, i32 %passthru) {
+ %a = call i32 @llvm.abs.i32(i32 %data, i1 false)
+ %val = call i32 @llvm.pisa.ired.i32(i8 4, i32 %a, i32 %mask, i32 %passthru)
+ ret i32 %val
+ }
+
+ define i16 @combine_abs_max_i16(i16 %data, i32 %mask, i16 %passthru) {
+ %a = call i16 @llvm.abs.i16(i16 %data, i1 false)
+ %val = call i16 @llvm.pisa.ired.i16(i8 4, i16 %a, i32 %mask, i16 %passthru)
+ ret i16 %val
+ }
+...
+---
+name: combine_abs_max_i32
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+ bb.1 (%ir-block.0):
+ ; CHECK-LABEL: name: combine_abs_max_i32
+ ; CHECK: G_INTRINSIC_CONVERGENT intrinsic(@llvm.pisa.ired), 8, %0(i32), %1(i32), %2(i32)
+ %0:reg32b(i32) = functionParameter_i32 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:reg32b(i32) = functionParameter_i32 2
+ %3:registers(i32) = G_ABS %0
+ %4:registers(i32) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.pisa.ired), 4, %3(i32), %1(i32), %2(i32)
+ %5:reg32b(i32) = COPY %4(i32)
+ retValue_i32_r %5(i32)
+...
+---
+name: combine_abs_max_i16
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+ bb.1 (%ir-block.0):
+ ; CHECK-LABEL: name: combine_abs_max_i16
+ ; CHECK: G_INTRINSIC_CONVERGENT intrinsic(@llvm.pisa.ired), 8, %0(i16), %1(i32), %2(i16)
+ %0:reg16b(i16) = functionParameter_i16 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:reg16b(i16) = functionParameter_i16 2
+ %3:registers(i16) = G_ABS %0
+ %4:registers(i16) = G_INTRINSIC_CONVERGENT intrinsic(@llvm.pisa.ired), 4, %3(i16), %1(i32), %2(i16)
+ %5:reg16b(i16) = COPY %4(i16)
+ retValue_i16_r %5(i16)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir
new file mode 100644
index 00000000000000..534a6a345f4fd7
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir
@@ -0,0 +1,89 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck -check-prefix=COMBINER %s
+--- |
+ target triple = "pisa"
+
+ define i32 @test_cmp_and_all_ones(i32 %dst_width, i32 %dst_height, i32 %conv, i32 %conv2) {
+ b0:
+ %cmp = icmp sle i32 %dst_width, %conv
+ %cmp4 = icmp sle i32 %dst_height, %conv2
+ %or.1 = or i1 %cmp, %cmp4
+ br i1 %or.1, label %b1, label %b2
+
+ b1: ; preds = %b0
+ ret i32 0
+
+ b2: ; preds = %b0
+ ret i32 1
+ }
+...
+---
+name: test_cmp_and_all_ones
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ ; COMBINER-LABEL: name: test_cmp_and_all_ones
+ ; COMBINER: bb.0.b0:
+ ; COMBINER-NEXT: successors: %bb.1(0x40000000), %bb.2(0x40000000)
+ ; COMBINER-NEXT: {{ $}}
+ ; COMBINER-NEXT: %0:reg32b(i32) = functionParameter_i32 0
+ ; COMBINER-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; COMBINER-NEXT: %2:reg32b(i32) = functionParameter_i32 2
+ ; COMBINER-NEXT: %3:reg32b(i32) = functionParameter_i32 3
+ ; COMBINER-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; COMBINER-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; COMBINER-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(sgt), %0(i32), %2
+ ; COMBINER-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(sgt), %1(i32), %3
+ ; COMBINER-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; COMBINER-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 -1
+ ; COMBINER-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C3]], [[C2]]
+ ; COMBINER-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; COMBINER-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 -1
+ ; COMBINER-NEXT: [[SELECT1:%[0-9]+]]:_(i32) = G_SELECT [[ICMP1]](i1), [[C5]], [[C4]]
+ ; COMBINER-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[SELECT]], [[SELECT1]]
+ ; COMBINER-NEXT: [[C6:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; COMBINER-NEXT: [[ICMP2:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND]](i32), [[C6]]
+ ; COMBINER-NEXT: G_BRCOND [[ICMP2]](i1), %bb.2
+ ; COMBINER-NEXT: G_BR %bb.1
+ ; COMBINER-NEXT: {{ $}}
+ ; COMBINER-NEXT: bb.1.b1:
+ ; COMBINER-NEXT: [[COPY:%[0-9]+]]:reg32b(i32) = COPY [[C1]](i32)
+ ; COMBINER-NEXT: retValue_i32_r [[COPY]](i32)
+ ; COMBINER-NEXT: {{ $}}
+ ; COMBINER-NEXT: bb.2.b2:
+ ; COMBINER-NEXT: [[COPY1:%[0-9]+]]:reg32b(i32) = COPY [[C]](i32)
+ ; COMBINER-NEXT: retValue_i32_r [[COPY1]](i32)
+ bb.1.b0:
+ successors: %bb.2(0x40000000), %bb.3(0x40000000)
+
+ %0:reg32b(i32) = functionParameter_i32 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:reg32b(i32) = functionParameter_i32 2
+ %3:reg32b(i32) = functionParameter_i32 3
+ %7:_(i32) = G_CONSTANT i32 1
+ %9:_(i32) = G_CONSTANT i32 0
+ %4:_(i1) = G_ICMP intpred(sgt), %0(i32), %2
+ %5:_(i1) = G_ICMP intpred(sgt), %1(i32), %3
+ %16:_(i32) = G_CONSTANT i32 0
+ %17:_(i32) = G_CONSTANT i32 -1
+ %13:_(i32) = G_SELECT %4(i1), %17, %16
+ %18:_(i32) = G_CONSTANT i32 0
+ %19:_(i32) = G_CONSTANT i32 -1
+ %14:_(i32) = G_SELECT %5(i1), %19, %18
+ %15:_(i32) = G_AND %13, %14
+ %20:_(i32) = G_CONSTANT i32 0
+ %21:_(i32) = G_CONSTANT i32 1
+ %22:_(i32) = G_AND %15, %21
+ %6:_(i1) = G_ICMP intpred(ne), %22(i32), %20
+ G_BRCOND %6(i1), %bb.3
+ G_BR %bb.2
+
+ bb.2.b1:
+ %10:reg32b(i32) = COPY %9(i32)
+ retValue_i32_r %10(i32)
+
+ bb.3.b2:
+ %8:reg32b(i32) = COPY %7(i32)
+ retValue_i32_r %8(i32)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir
new file mode 100644
index 00000000000000..08a8dd687113a6
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir
@@ -0,0 +1,88 @@
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+--- |
+ define void @test_extract_build_vector(ptr %addr, ptr %addr2) {
+ %rv = load <4 x i32>, ptr %addr, align 32
+ store <4 x i32> %rv, ptr %addr2, align 32
+ ret void
+ }
+ ; Negative test: extract 2-element subvector from 8-element build_vector.
+ ; matchExtractSubvectorBuildVector requires NumDstElts == 4, so this must NOT fold.
+ define void @test_no_fold_2dst(ptr %addr) { ret void }
+ ; Negative test: source is G_LOAD, not G_BUILD_VECTOR; must NOT fold.
+ define void @test_no_fold_non_build_vector(ptr %addr, ptr %addr2) {
+ %rv = load <8 x i32>, ptr %addr, align 32
+ store <4 x i32> undef, ptr %addr2, align 16
+ ret void
+ }
+...
+---
+name: test_extract_build_vector
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: {{^}}name: test_extract_build_vector
+ ; CHECK: (<4 x i32>) = G_LOAD %0(p0)
+ ; CHECK-NEXT: G_STORE %{{[0-9]+}}(<4 x i32>)
+ %0:reg32b(p0) = functionParameter_i32 0
+ %1:reg32b(p0) = functionParameter_i32 1
+ %20:registers(<4 x i32>) = G_LOAD %0:reg32b(p0) :: (load (<4 x i32>) from %ir.addr, align 32, addrspace 0)
+ %37:registers(i32) = G_CONSTANT i32 0
+ %21:registers(i32) = G_EXTRACT_VECTOR_ELT %20:registers(<4 x i32>), %37:registers(i32)
+ %38:registers(i32) = G_CONSTANT i32 1
+ %22:registers(i32) = G_EXTRACT_VECTOR_ELT %20:registers(<4 x i32>), %38:registers(i32)
+ %39:registers(i32) = G_CONSTANT i32 2
+ %23:registers(i32) = G_EXTRACT_VECTOR_ELT %20:registers(<4 x i32>), %39:registers(i32)
+ %40:registers(i32) = G_CONSTANT i32 3
+ %24:registers(i32) = G_EXTRACT_VECTOR_ELT %20:registers(<4 x i32>), %40:registers(i32)
+ %3:registers(<4 x i32>) = G_BUILD_VECTOR %21:registers(i32), %22:registers(i32), %23:registers(i32), %24:registers(i32)
+ G_STORE %3:registers(<4 x i32>), %1:reg32b(p0) :: (store (<4 x i32>) into %ir.addr2, align 32, addrspace 0)
+ ret
+---
+# Negative test: NumDstElts(2) != 4, so matchExtractSubvectorBuildVector must
+# return false and the G_EXTRACT_SUBVECTOR must survive unchanged.
+name: test_no_fold_2dst
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: {{^}}name: test_no_fold_2dst
+ ; CHECK: G_BUILD_VECTOR
+ ; CHECK: G_EXTRACT_SUBVECTOR
+ %0:reg32b(p0) = functionParameter_i32 0
+ %10:registers(i32) = G_CONSTANT i32 1
+ %11:registers(i32) = G_CONSTANT i32 2
+ %12:registers(i32) = G_CONSTANT i32 3
+ %13:registers(i32) = G_CONSTANT i32 4
+ %14:registers(i32) = G_CONSTANT i32 5
+ %15:registers(i32) = G_CONSTANT i32 6
+ %16:registers(i32) = G_CONSTANT i32 7
+ %17:registers(i32) = G_CONSTANT i32 8
+ ; Build an 8-element vector.
+ %20:registers(<8 x i32>) = G_BUILD_VECTOR %10:registers(i32), %11:registers(i32), %12:registers(i32), %13:registers(i32), %14:registers(i32), %15:registers(i32), %16:registers(i32), %17:registers(i32)
+ ; Extract a 2-element subvector starting at offset 0. NumDstElts=2 != 4, no fold.
+ %30:registers(<2 x i32>) = G_EXTRACT_SUBVECTOR %20:registers(<8 x i32>), 0
+ G_STORE %30:registers(<2 x i32>), %0:reg32b(p0) :: (store (<2 x i32>) into %ir.addr, addrspace 0)
+ ret
+---
+# Negative test: source is a G_LOAD (not G_BUILD_VECTOR), so
+# matchExtractSubvectorBuildVector must return false (BV == null check).
+name: test_no_fold_non_build_vector
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: {{^}}name: test_no_fold_non_build_vector
+ ; CHECK-NOT: G_BUILD_VECTOR
+ ; CHECK: G_EXTRACT_SUBVECTOR
+ %0:reg32b(p0) = functionParameter_i32 0
+ %1:reg32b(p0) = functionParameter_i32 1
+ ; Load an 8-element vector from memory (not a G_BUILD_VECTOR).
+ %20:registers(<8 x i32>) = G_LOAD %0:reg32b(p0) :: (load (<8 x i32>) from %ir.addr, align 32, addrspace 0)
+ ; Extract 4 elements at offset 0. Source is not G_BUILD_VECTOR: no fold.
+ %30:registers(<4 x i32>) = G_EXTRACT_SUBVECTOR %20:registers(<8 x i32>), 0
+ G_STORE %30:registers(<4 x i32>), %1:reg32b(p0) :: (store (<4 x i32>) into %ir.addr2, addrspace 0)
+ ret
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir
new file mode 100644
index 00000000000000..b5434203547c21
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir
@@ -0,0 +1,18 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+---
+name: test_fadd_constant
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: test_fadd_constant
+ ; CHECK: %res:reg32b(f32) = G_FCONSTANT float 1.000000e+00
+ ; CHECK-NEXT: %ret:reg32b(i32) = G_BITCAST %res(f32)
+ ; CHECK-NEXT: retValue_i32_r %ret(i32)
+ %lhs:reg32b(f32) = G_FCONSTANT float 0.0
+ %rhs:reg32b(f32) = G_FCONSTANT float 1.0
+ %res:reg32b(f32) = G_FADD %lhs:reg32b, %rhs:reg32b
+ %ret:reg32b(i32) = G_BITCAST %res(f32)
+ retValue_i32_r %ret(i32)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
new file mode 100644
index 00000000000000..0658e26af55123
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
@@ -0,0 +1,74 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# This test exercises the `NoVectors` early-break inside
+# PISA::getDefIgnoringBitcasts(). The combine
+# unmerge_bitcast_buildvector_to_bitcast calls getDefIgnoringBitcasts with
+# NoVectors=true while walking each G_BUILD_VECTOR operand. When the walk
+# encounters a G_BITCAST whose source or destination is a vector type,
+# the helper must take the early `break` branch on the line:
+# if (NoVectors && (DstTy.isVector() || SrcTy.isVector())) break;
+# The walker then stops at that G_BITCAST and the matcher's G_UNMERGE_VALUES
+# check fails so the combine does not fire.
+#
+# The combine must NOT replace the G_BUILD_VECTOR. The body-level CHECK lines
+# below verify that the explicit G_BUILD_VECTOR remains.
+
+# The shifts_of_constants combine should fold the hinted shift into one constant.
+# The body-level CHECK lines below verify the folded G_CONSTANT result.
+
+--- |
+ define <2 x i16> @build_vector_with_vector_bitcast_in_chain(i16 %a, i16 %b) {
+ ret <2 x i16> zeroinitializer
+ }
+
+ define i16 @shift_of_constants_via_assert_sext_hints(i16 %unused) {
+ ret i16 0
+ }
+...
+---
+name: build_vector_with_vector_bitcast_in_chain
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: build_vector_with_vector_bitcast_in_chain
+ ; CHECK: %0:reg16b(i16) = functionParameter_i16 0
+ ; CHECK-NEXT: %1:reg16b(i16) = functionParameter_i16 1
+ ; CHECK-NEXT: %bv:_(<2 x i16>) = G_BUILD_VECTOR %0(i16), %1(i16)
+ ; CHECK-NEXT: %ret:regv2_16b(<2 x i16>) = COPY %bv(<2 x i16>)
+ ; CHECK-NEXT: retValue_v2i16_r %ret(<2 x i16>)
+ %0:reg16b(i16) = functionParameter_i16 0
+ %1:reg16b(i16) = functionParameter_i16 1
+ ; Two scalar-from-vector bitcasts whose source type is the vector
+ ; <2 x i8>. getDefIgnoringBitcasts(NoVectors=true) immediately hits
+ ; the `break` because SrcTy.isVector() is true.
+ %va:_(<2 x i8>) = G_BITCAST %0:reg16b(i16)
+ %vb:_(<2 x i8>) = G_BITCAST %1:reg16b(i16)
+ %a16:_(i16) = G_BITCAST %va:_(<2 x i8>)
+ %b16:_(i16) = G_BITCAST %vb:_(<2 x i8>)
+ %bv:_(<2 x i16>) = G_BUILD_VECTOR %a16:_(i16), %b16:_(i16)
+ %ret:regv2_16b(<2 x i16>) = COPY %bv:_(<2 x i16>)
+ retValue_v2i16_r %ret:regv2_16b(<2 x i16>)
+...
+
+---
+name: shift_of_constants_via_assert_sext_hints
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: shift_of_constants_via_assert_sext_hints
+ ; CHECK: %r:_(i16) = G_CONSTANT i16 4
+ ; CHECK-NEXT: %ret:reg16b(i16) = COPY %r(i16)
+ ; CHECK-NEXT: retValue_i16_r %ret(i16)
+ %c1:_(i16) = G_CONSTANT i16 1
+ %h1:_(i16) = G_ASSERT_SEXT %c1:_(i16), 8
+ %c2:_(i16) = G_CONSTANT i16 2
+ %h2:_(i16) = G_ASSERT_SEXT %c2:_(i16), 8
+ %r:_(i16) = G_SHL %h1:_(i16), %h2:_(i16)
+ %ret:reg16b(i16) = COPY %r:_(i16)
+ retValue_i16_r %ret:reg16b(i16)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-local-id-range-trunc-zext.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-local-id-range-trunc-zext.mir
new file mode 100644
index 00000000000000..959aaa318bf7fe
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-local-id-range-trunc-zext.mir
@@ -0,0 +1,92 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+--- |
+ ; Test that computeKnownBitsForTargetInstr correctly propagates range
+ ; attribute information from pisa.local.id intrinsics.
+ ; The range attribute [0, 65536) guarantees top 16 bits are zero,
+ ; which enables the elimination of G_TRUNC + G_ZEXT patterns.
+ ;
+ ; This test verifies that:
+ ; 1. computeKnownBitsForTargetInstr() in PISAISelLowering.cpp correctly
+ ; extracts and provides the range attribute information
+ ; 2. The known_bits_simplifications combiner group (which includes zext_trunc_fold)
+ ; uses this information to optimize away redundant TRUNC+ZEXT operations
+
+ declare i32 @llvm.pisa.local.id.x()
+
+ define i32 @test_local_id_trunc_zext() {
+ %id = call i32 @llvm.pisa.local.id.x()
+ %trunc = trunc i32 %id to i16
+ %zext = zext i16 %trunc to i32
+ ret i32 %zext
+ }
+
+ define i32 @test_local_id_y_trunc_zext() {
+ %id = call i32 @llvm.pisa.local.id.y()
+ %trunc = trunc i32 %id to i16
+ %zext = zext i16 %trunc to i32
+ ret i32 %zext
+ }
+
+ define i32 @test_local_id_z_trunc_zext() {
+ %id = call i32 @llvm.pisa.local.id.z()
+ %trunc = trunc i32 %id to i16
+ %zext = zext i16 %trunc to i32
+ ret i32 %zext
+ }
+
+...
+---
+name: test_local_id_trunc_zext
+legalized: false
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ ; CHECK-LABEL: name: test_local_id_trunc_zext
+ ; CHECK: [[LOCAL_ID:%[0-9]+]]:_(i32) = G_INTRINSIC intrinsic(@llvm.pisa.local.id.x)
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg32b(i32) = COPY [[LOCAL_ID]](i32)
+ ; CHECK-NEXT: retValue_i32_r [[COPY]](i32)
+ %0:_(i32) = G_INTRINSIC intrinsic(@llvm.pisa.local.id.x)
+ %1:_(i16) = G_TRUNC %0(i32)
+ %2:_(i32) = G_ZEXT %1(i16)
+ %3:reg32b(i32) = COPY %2(i32)
+ retValue_i32_r %3(i32)
+
+...
+---
+name: test_local_id_y_trunc_zext
+legalized: false
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ ; CHECK-LABEL: name: test_local_id_y_trunc_zext
+ ; CHECK: [[LOCAL_ID:%[0-9]+]]:_(i32) = G_INTRINSIC intrinsic(@llvm.pisa.local.id.y)
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg32b(i32) = COPY [[LOCAL_ID]](i32)
+ ; CHECK-NEXT: retValue_i32_r [[COPY]](i32)
+ %0:_(i32) = G_INTRINSIC intrinsic(@llvm.pisa.local.id.y)
+ %1:_(i16) = G_TRUNC %0(i32)
+ %2:_(i32) = G_ZEXT %1(i16)
+ %3:reg32b(i32) = COPY %2(i32)
+ retValue_i32_r %3(i32)
+
+...
+---
+name: test_local_id_z_trunc_zext
+legalized: false
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ ; CHECK-LABEL: name: test_local_id_z_trunc_zext
+ ; CHECK: [[LOCAL_ID:%[0-9]+]]:_(i32) = G_INTRINSIC intrinsic(@llvm.pisa.local.id.z)
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg32b(i32) = COPY [[LOCAL_ID]](i32)
+ ; CHECK-NEXT: retValue_i32_r [[COPY]](i32)
+ %0:_(i32) = G_INTRINSIC intrinsic(@llvm.pisa.local.id.z)
+ %1:_(i16) = G_TRUNC %0(i32)
+ %2:_(i32) = G_ZEXT %1(i16)
+ %3:reg32b(i32) = COPY %2(i32)
+ retValue_i32_r %3(i32)
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir
new file mode 100644
index 00000000000000..572f6c3ab288d5
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir
@@ -0,0 +1,52 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# Test that p2i_to_i2p_fixed combine rule correctly handles pointer address
+# spaces. A COPY between pointers of different address spaces is illegal.
+# The fold should only apply when source and destination pointer types match
+# exactly (same address space).
+
+--- |
+ target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
+
+ @gv = external addrspace(1) constant i8
+
+ define void @fold_same_addrspace() { ret void }
+ define void @no_fold_different_addrspace() { ret void }
+...
+
+# Same address space: p1 -> i64 -> p1. Should fold to COPY.
+---
+name: fold_same_addrspace
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: fold_same_addrspace
+ ; CHECK: [[GV:%[0-9]+]]:_(p1) = G_GLOBAL_VALUE @gv
+ ; CHECK-NEXT: G_STORE [[GV]](p1), [[GV]](p1) :: (store (i8), addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:_(p1) = G_GLOBAL_VALUE @gv
+ %1:_(i64) = G_PTRTOINT %0(p1)
+ %2:_(p1) = G_INTTOPTR %1(i64)
+ G_STORE %2(p1), %2(p1) :: (store (i8), addrspace 1)
+ ret
+...
+
+# Different non-generic address spaces: p1 -> i64 -> p2. Should NOT fold
+# to COPY because p1 != p2 even though both are 64-bit pointers.
+---
+name: no_fold_different_addrspace
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: no_fold_different_addrspace
+ ; CHECK: [[GV:%[0-9]+]]:_(p1) = G_GLOBAL_VALUE @gv
+ ; CHECK-NEXT: [[PTRTOINT:%[0-9]+]]:_(i64) = G_PTRTOINT [[GV]](p1)
+ ; CHECK-NEXT: [[INTTOPTR:%[0-9]+]]:_(p2) = G_INTTOPTR [[PTRTOINT]](i64)
+ ; CHECK-NEXT: G_STORE [[INTTOPTR]](p2), [[GV]](p1) :: (store (i8), addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:_(p1) = G_GLOBAL_VALUE @gv
+ %1:_(i64) = G_PTRTOINT %0(p1)
+ %2:_(p2) = G_INTTOPTR %1(i64)
+ G_STORE %2(p2), %0(p1) :: (store (i8), addrspace 1)
+ ret
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
new file mode 100644
index 00000000000000..694e978edc5f52
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
@@ -0,0 +1,160 @@
+# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# Tests for PISAPreLegalizerCombinerImpl::matchSelectTruncOneZero.
+#
+# The combiner rewrites:
+# select(trunc(wide -> i1), N 1, N 0) => trunc(and(wide, 1), N)
+# to avoid introducing an illegal i1 compare when lowering bool-to-int.
+
+--- |
+ define i16 @test_select_trunc_one_zero_match(i32 %wide) { ret i16 0 }
+ define i16 @test_no_fold_cond_from_icmp(i32 %a) { ret i16 0 }
+ define i16 @test_no_fold_wrong_true_val(i32 %wide) { ret i16 0 }
+ define i16 @test_no_fold_wrong_false_val(i32 %wide) { ret i16 0 }
+ define i32 @test_no_fold_dst_geq_wide(i16 %wide) { ret i32 0 }
+ define i16 @test_no_fold_nonconstant_true(i32 %wide, i16 %t) { ret i16 0 }
+...
+
+# ============================================================================
+# Positive test: condition is i1 from G_TRUNC, true=1, false=0, DstTy < WideTy.
+# Expected transform: select(trunc(i32 to i1), i16 1, i16 0) => trunc(i32 & 1, i16)
+# ============================================================================
+---
+name: test_select_trunc_one_zero_match
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_select_trunc_one_zero_match
+ ; CHECK: %wide:reg32b(i32) = functionParameter_i32 0
+ ; CHECK: [[ONE32:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND %wide, [[ONE32]]
+ ; CHECK-NEXT: %sel:_(i16) = G_TRUNC [[AND]](i32)
+ ; CHECK-NEXT: %result:reg16b(i16) = COPY %sel(i16)
+ ; CHECK-NEXT: retValue_i16_r %result(i16)
+ %wide:reg32b(i32) = functionParameter_i32 0
+ %i1:_(i1) = G_TRUNC %wide(i32)
+ %one:_(i16) = G_CONSTANT i16 1
+ %zero:_(i16) = G_CONSTANT i16 0
+ %sel:_(i16) = G_SELECT %i1(i1), %one(i16), %zero(i16)
+ %result:reg16b(i16) = COPY %sel(i16)
+ retValue_i16_r %result(i16)
+
+# ============================================================================
+# Negative: condition is from G_ICMP (not G_TRUNC).
+# Covers matchSelectTruncOneZero line 1788: TruncMI->getOpcode() != G_TRUNC.
+# matchSelectTruncOneZero does NOT fire; no G_AND should appear.
+# ============================================================================
+---
+name: test_no_fold_cond_from_icmp
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_no_fold_cond_from_icmp
+ ; CHECK: %a:reg32b(i32) = functionParameter_i32 0
+ ; CHECK: G_ICMP
+ ; CHECK-NOT: G_AND
+ ; CHECK: retValue_i16_r
+ %a:reg32b(i32) = functionParameter_i32 0
+ %zero32:_(i32) = G_CONSTANT i32 0
+ %cond:_(i1) = G_ICMP intpred(ne), %a(i32), %zero32(i32)
+ %one:_(i16) = G_CONSTANT i16 1
+ %zero:_(i16) = G_CONSTANT i16 0
+ %sel:_(i16) = G_SELECT %cond(i1), %one(i16), %zero(i16)
+ %result:reg16b(i16) = COPY %sel(i16)
+ retValue_i16_r %result(i16)
+
+# ============================================================================
+# Negative: true value is 2, not 1.
+# Covers matchSelectTruncOneZero line 1806: !TrueOpt->Value.isOne().
+# matchSelectTruncOneZero does NOT fire; no G_AND should appear from it.
+# ============================================================================
+---
+name: test_no_fold_wrong_true_val
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_no_fold_wrong_true_val
+ ; CHECK: %wide:reg32b(i32) = functionParameter_i32 0
+ ; CHECK: G_TRUNC %wide(i32)
+ ; CHECK-NOT: G_AND %wide
+ ; CHECK: retValue_i16_r
+ %wide:reg32b(i32) = functionParameter_i32 0
+ %i1:_(i1) = G_TRUNC %wide(i32)
+ %two:_(i16) = G_CONSTANT i16 2
+ %zero:_(i16) = G_CONSTANT i16 0
+ %sel:_(i16) = G_SELECT %i1(i1), %two(i16), %zero(i16)
+ %result:reg16b(i16) = COPY %sel(i16)
+ retValue_i16_r %result(i16)
+
+# ============================================================================
+# Negative: false value is 3, not 0.
+# Covers matchSelectTruncOneZero line 1806: !FalseOpt->Value.isZero().
+# No fold; G_SELECT survives.
+# ============================================================================
+---
+name: test_no_fold_wrong_false_val
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_no_fold_wrong_false_val
+ ; CHECK: %wide:reg32b(i32) = functionParameter_i32 0
+ ; CHECK: G_TRUNC %wide(i32)
+ ; CHECK: G_SELECT %i1(i1), %one, %three
+ %wide:reg32b(i32) = functionParameter_i32 0
+ %i1:_(i1) = G_TRUNC %wide(i32)
+ %one:_(i16) = G_CONSTANT i16 1
+ %three:_(i16) = G_CONSTANT i16 3
+ %sel:_(i16) = G_SELECT %i1(i1), %one(i16), %three(i16)
+ %result:reg16b(i16) = COPY %sel(i16)
+ retValue_i16_r %result(i16)
+
+# ============================================================================
+# Negative: DstTy (i32) >= WideTy (i16).
+# Covers matchSelectTruncOneZero line 1798:
+# DstTy.getScalarSizeInBits() >= WideTy.getScalarSizeInBits()
+# No fold from matchSelectTruncOneZero; no G_AND %wide should appear.
+# ============================================================================
+---
+name: test_no_fold_dst_geq_wide
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_no_fold_dst_geq_wide
+ ; CHECK: %wide:reg16b(i16) = functionParameter_i16 0
+ ; CHECK: G_TRUNC %wide(i16)
+ ; CHECK-NOT: G_AND %wide
+ ; CHECK: retValue_i32_r
+ %wide:reg16b(i16) = functionParameter_i16 0
+ %i1:_(i1) = G_TRUNC %wide(i16)
+ %one:_(i32) = G_CONSTANT i32 1
+ %zero:_(i32) = G_CONSTANT i32 0
+ %sel:_(i32) = G_SELECT %i1(i1), %one(i32), %zero(i32)
+ %result:reg32b(i32) = COPY %sel(i32)
+ retValue_i32_r %result(i32)
+
+# ============================================================================
+# Negative: true operand is not a constant register.
+# Covers matchSelectTruncOneZero line 1804: !TrueOpt.
+# No fold; G_SELECT survives with the non-constant true operand.
+# ============================================================================
+---
+name: test_no_fold_nonconstant_true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_no_fold_nonconstant_true
+ ; CHECK: G_TRUNC
+ ; CHECK: G_SELECT {{.*}}, %true_val,
+ %wide:reg32b(i32) = functionParameter_i32 0
+ %true_val:reg16b(i16) = functionParameter_i16 1
+ %i1:_(i1) = G_TRUNC %wide(i32)
+ %zero:_(i16) = G_CONSTANT i16 0
+ %sel:_(i16) = G_SELECT %i1(i1), %true_val(i16), %zero(i16)
+ %result:reg16b(i16) = COPY %sel(i16)
+ retValue_i16_r %result(i16)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir
new file mode 100644
index 00000000000000..d492c5fd6c6532
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir
@@ -0,0 +1,91 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck -check-prefix=COMBINER %s
+--- |
+
+ define i8 @test_trunc_and_trunc(i32 %a) {
+ %x = trunc i32 %a to i16
+ %y = and i16 %x, 7
+ %z = trunc i16 %y to i8
+ ret i8 %z
+ }
+
+ define i8 @test_trunc_or_trunc(i32 %a) {
+ %x = trunc i32 %a to i16
+ %y = or i16 %x, 7
+ %z = trunc i16 %y to i8
+ ret i8 %z
+ }
+
+ define i8 @test_trunc_xor_trunc(i32 %a) {
+ %x = trunc i32 %a to i16
+ %y = xor i16 %x, 7
+ %z = trunc i16 %y to i8
+ ret i8 %z
+ }
+...
+---
+name: test_trunc_and_trunc
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ ; COMBINER-LABEL: name: test_trunc_and_trunc
+ ; COMBINER: %0:reg32b(i32) = functionParameter_i32 0
+ ; COMBINER-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 7
+ ; COMBINER-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND %0, [[C]]
+ ; COMBINER-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC [[AND]](i32)
+ ; COMBINER-NEXT: [[COPY:%[0-9]+]]:reg8b(i8) = COPY [[TRUNC]](i8)
+ ; COMBINER-NEXT: retValue_i8_r [[COPY]](i8)
+ %0:reg32b(i32) = functionParameter_i32 0
+ %2:_(i16) = G_CONSTANT i16 7
+ %1:_(i16) = G_TRUNC %0(i32)
+ %3:_(i16) = G_AND %1, %2
+ %4:_(i8) = G_TRUNC %3(i16)
+ %5:reg8b(i8) = COPY %4(i8)
+ retValue_i8_r %5(i8)
+...
+---
+name: test_trunc_or_trunc
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ ; COMBINER-LABEL: name: test_trunc_or_trunc
+ ; COMBINER: %0:reg32b(i32) = functionParameter_i32 0
+ ; COMBINER-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 7
+ ; COMBINER-NEXT: [[OR:%[0-9]+]]:_(i32) = G_OR %0, [[C]]
+ ; COMBINER-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC [[OR]](i32)
+ ; COMBINER-NEXT: [[COPY:%[0-9]+]]:reg8b(i8) = COPY [[TRUNC]](i8)
+ ; COMBINER-NEXT: retValue_i8_r [[COPY]](i8)
+ %0:reg32b(i32) = functionParameter_i32 0
+ %2:_(i16) = G_CONSTANT i16 7
+ %1:_(i16) = G_TRUNC %0(i32)
+ %3:_(i16) = G_OR %1, %2
+ %4:_(i8) = G_TRUNC %3(i16)
+ %5:reg8b(i8) = COPY %4(i8)
+ retValue_i8_r %5(i8)
+...
+---
+name: test_trunc_xor_trunc
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ ; COMBINER-LABEL: name: test_trunc_xor_trunc
+ ; COMBINER: %0:reg32b(i32) = functionParameter_i32 0
+ ; COMBINER-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 7
+ ; COMBINER-NEXT: [[XOR:%[0-9]+]]:_(i32) = G_XOR %0, [[C]]
+ ; COMBINER-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC [[XOR]](i32)
+ ; COMBINER-NEXT: [[COPY:%[0-9]+]]:reg8b(i8) = COPY [[TRUNC]](i8)
+ ; COMBINER-NEXT: retValue_i8_r [[COPY]](i8)
+ %0:reg32b(i32) = functionParameter_i32 0
+ %2:_(i16) = G_CONSTANT i16 7
+ %1:_(i16) = G_TRUNC %0(i32)
+ %3:_(i16) = G_XOR %1, %2
+ %4:_(i8) = G_TRUNC %3(i16)
+ %5:reg8b(i8) = COPY %4(i8)
+ retValue_i8_r %5(i8)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
new file mode 100644
index 00000000000000..b5abbdcfdb7479
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
@@ -0,0 +1,56 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# Exercises PISAPreLegalizerCombinerImpl::applyTruncatedShift. The combine
+# rule trunc_shift_to_vector_extract turns:
+# %lo = trunc i32 %x to i16
+# %sh = lshr i32 %x, 16
+# %hi = trunc i32 %sh to i16
+# into:
+# %1 = bitcast i32 %x to <2 x i16>
+# %hi = extractelement <2 x i16> %1, 1
+# %lo = extractelement <2 x i16> %1, 0
+#
+# This test specifically covers the inner loop body that, after matching
+# the high-part trunc as the root, walks `LshMI.getOperand(1)`'s users to
+# find and rewrite the matching low-part G_TRUNC. The body of the
+# `if (DstSize == NarrowSize)` is the location flagged in the coverage
+# report for this file; both the lo and hi extracts must appear in the
+# output.
+
+--- |
+ define i16 @lo_and_hi_truncs(i32 %x) {
+ ret i16 0
+ }
+...
+---
+name: lo_and_hi_truncs
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: lo_and_hi_truncs
+ ; CHECK: %0:reg32b(i32) = functionParameter_i32 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(<2 x i16>) = G_BITCAST %0(i32)
+ ; CHECK-NEXT: %lo:_(i16) = G_EXTRACT_VECTOR_ELT [[BITCAST]](<2 x i16>), [[C]](i32)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: %hi:_(i16) = G_EXTRACT_VECTOR_ELT [[BITCAST]](<2 x i16>), [[C1]](i32)
+ ; CHECK-NEXT: %lo32:_(i32) = G_ANYEXT %lo(i16)
+ ; CHECK-NEXT: %hi32:_(i32) = G_ANYEXT %hi(i16)
+ ; CHECK-NEXT: %sum:_(i32) = G_ADD %lo32, %hi32
+ ; CHECK-NEXT: %ret16:_(i16) = G_TRUNC %sum(i32)
+ ; CHECK-NEXT: %r:reg16b(i16) = COPY %ret16(i16)
+ ; CHECK-NEXT: retValue_i16_r %r(i16)
+ %0:reg32b(i32) = functionParameter_i32 0
+ %shamt:_(i32) = G_CONSTANT i32 16
+ %sh:_(i32) = G_LSHR %0:reg32b(i32), %shamt:_(i32)
+ %lo:_(i16) = G_TRUNC %0:reg32b(i32)
+ %hi:_(i16) = G_TRUNC %sh:_(i32)
+ %lo32:_(i32) = G_ANYEXT %lo:_(i16)
+ %hi32:_(i32) = G_ANYEXT %hi:_(i16)
+ %sum:_(i32) = G_ADD %lo32:_(i32), %hi32:_(i32)
+ %ret16:_(i16) = G_TRUNC %sum:_(i32)
+ %r:reg16b(i16) = COPY %ret16:_(i16)
+ retValue_i16_r %r:reg16b(i16)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir b/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
new file mode 100644
index 00000000000000..86d7ce2b0bbc93
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
@@ -0,0 +1,63 @@
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# Tests for PISAPostLegalizerCombinerImpl::matchFixIllegalShiftAmt /
+# applyFixIllegalShiftAmt.
+#
+# The post-legalizer combiner fires when a G_SHL/G_LSHR/G_ASHR has a shift
+# amount whose type is not i32. mul_to_shl and similar rules can introduce
+# such shifts. applyFixIllegalShiftAmt corrects the amount type by:
+# - inserting G_TRUNC if the amount type is wider than i32 (e.g. i64)
+# - inserting G_ZEXT if the amount type is narrower than i32 (e.g. i16)
+
+--- |
+ define i64 @test_shl_i64_i64_amt(i64 %val, i64 %amt) { ret i64 0 }
+ define i32 @test_ashr_i32_i16_amt(i32 %val, i16 %amt) { ret i32 0 }
+...
+
+# ============================================================================
+# Trunc path: shift amount is i64 (>32 bits) -> G_TRUNC inserted to i32.
+# Covers applyFixIllegalShiftAmt line 1673: MIB.buildTrunc(I32, ShAmtReg)
+# ============================================================================
+---
+name: test_shl_i64_i64_amt
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_shl_i64_i64_amt
+ ; CHECK: %val:reg64b(i64) = functionParameter_i64 0
+ ; CHECK-NEXT: %amt:reg64b(i64) = functionParameter_i64 1
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i32) = G_TRUNC %amt(i64)
+ ; CHECK-NEXT: %result:_(i64) = G_SHL %val, [[TRUNC]](i32)
+ ; CHECK-NEXT: %r:reg64b(i64) = COPY %result(i64)
+ ; CHECK-NEXT: retValue_i64_r %r(i64)
+ %val:reg64b(i64) = functionParameter_i64 0
+ %amt:reg64b(i64) = functionParameter_i64 1
+ %result:_(i64) = G_SHL %val(i64), %amt(i64)
+ %r:reg64b(i64) = COPY %result(i64)
+ retValue_i64_r %r:reg64b(i64)
+
+# ============================================================================
+# ZExt path: shift amount is i16 (<32 bits) -> G_ZEXT inserted to i32.
+# Covers applyFixIllegalShiftAmt line 1674: MIB.buildZExt(I32, ShAmtReg)
+# ============================================================================
+---
+name: test_ashr_i32_i16_amt
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_ashr_i32_i16_amt
+ ; CHECK: %val:reg32b(i32) = functionParameter_i32 0
+ ; CHECK-NEXT: %amt:reg16b(i16) = functionParameter_i16 1
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i32) = G_ZEXT %amt(i16)
+ ; CHECK-NEXT: %result:_(i32) = G_ASHR %val, [[ZEXT]](i32)
+ ; CHECK-NEXT: %r:reg32b(i32) = COPY %result(i32)
+ ; CHECK-NEXT: retValue_i32_r %r(i32)
+ %val:reg32b(i32) = functionParameter_i32 0
+ %amt:reg16b(i16) = functionParameter_i16 1
+ %result:_(i32) = G_ASHR %val(i32), %amt(i16)
+ %r:reg32b(i32) = COPY %result(i32)
+ retValue_i32_r %r:reg32b(i32)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir
new file mode 100644
index 00000000000000..adc32984beca65
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir
@@ -0,0 +1,76 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+# Sub-byte scalar G_STORE paths unreachable from .ll: the prelegalizer combiner
+# rewrites every sub-byte store value into a single G_TRUNC, so only hand-written
+# MIR keeps the original def and covers the remaining branches.
+
+---
+# G_CONSTANT source: no wider def to fold, so the value is widened via G_INSERT
+# into an undef byte rather than an illegal G_ANYEXT from i4.
+name: store_i4_no_chain_uses_insert
+body: |
+ bb.0:
+ liveins: $reg64b_0
+
+ ; CHECK-LABEL: name: store_i4_no_chain_uses_insert
+ ; CHECK: liveins: $reg64b_0
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %ptr:_(p1) = COPY $reg64b_0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i16) = G_CONSTANT i16 7
+ ; CHECK-NEXT: [[DEF:%[0-9]+]]:_(i16) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i16) = G_CONSTANT i16 -16
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i16) = G_AND [[DEF]], [[C1]]
+ ; CHECK-NEXT: [[OR:%[0-9]+]]:_(i16) = G_OR [[AND]], [[C]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC [[OR]](i16)
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i8) = COPY [[TRUNC]](i8)
+ ; CHECK-NEXT: G_STORE [[COPY]](i8), %ptr(p1) :: (store (i8), addrspace 1)
+ %ptr:_(p1) = COPY $reg64b_0
+ %val:_(i4) = G_CONSTANT i4 7
+ G_STORE %val(i4), %ptr(p1) :: (store (i4), addrspace 1)
+...
+
+---
+# Multi-level G_TRUNC chain (i16 -> i6 -> i4): the walk skips the sub-byte
+# intermediate (i6) and truncates the byte-or-wider source (i16) directly.
+name: store_i4_multilevel_trunc_chain
+body: |
+ bb.0:
+ liveins: $reg64b_0, $reg16b_0
+
+ ; CHECK-LABEL: name: store_i4_multilevel_trunc_chain
+ ; CHECK: liveins: $reg64b_0, $reg16b_0
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %ptr:_(p1) = COPY $reg64b_0
+ ; CHECK-NEXT: %w:_(i16) = COPY $reg16b_0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC %w(i16)
+ ; CHECK-NEXT: G_STORE [[TRUNC]](i8), %ptr(p1) :: (store (i8), addrspace 1)
+ %ptr:_(p1) = COPY $reg64b_0
+ %w:_(i16) = COPY $reg16b_0
+ %t6:_(i6) = G_TRUNC %w(i16)
+ %val:_(i4) = G_TRUNC %t6(i6)
+ G_STORE %val(i4), %ptr(p1) :: (store (i4), addrspace 1)
+...
+
+---
+# Same chain walk reached through G_BITCAST links, exercising the G_BITCAST arm
+# while folding to the wider i16 source.
+name: store_i4_bitcast_chain
+body: |
+ bb.0:
+ liveins: $reg64b_0, $reg16b_0
+
+ ; CHECK-LABEL: name: store_i4_bitcast_chain
+ ; CHECK: liveins: $reg64b_0, $reg16b_0
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %ptr:_(p1) = COPY $reg64b_0
+ ; CHECK-NEXT: %w:_(i16) = COPY $reg16b_0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC %w(i16)
+ ; CHECK-NEXT: G_STORE [[TRUNC]](i8), %ptr(p1) :: (store (i8), addrspace 1)
+ %ptr:_(p1) = COPY $reg64b_0
+ %w:_(i16) = COPY $reg16b_0
+ %t4:_(i4) = G_TRUNC %w(i16)
+ %v2i2:_(<2 x i2>) = G_BITCAST %t4(i4)
+ %val:_(i4) = G_BITCAST %v2i2(<2 x i2>)
+ G_STORE %val(i4), %ptr(p1) :: (store (i4), addrspace 1)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalize-threeway-cmp.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalize-threeway-cmp.mir
new file mode 100644
index 00000000000000..5b9b37652e6af0
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalize-threeway-cmp.mir
@@ -0,0 +1,90 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -O0 -mtriple=pisa -run-pass=legalizer -global-isel-abort=1 -verify-machineinstrs %s -o - | FileCheck %s
+---
+name: test_ucmp
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_ucmp
+ ; CHECK: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i1) = G_CONSTANT i1 false
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i1) = G_CONSTANT i1 false
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i16) = G_SELECT [[C1]](i1), [[C3]], [[C4]]
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(i16) = G_SELECT [[C2]](i1), [[C5]], [[SELECT]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC [[SELECT1]](i16)
+ ; CHECK-NEXT: G_STORE [[TRUNC]](i8), [[C]](p1) :: (store (i8) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:_(i64) = G_CONSTANT i64 0
+ %2:_(p1) = G_CONSTANT i64 0
+ %1:_(i8) = G_UCMP %0(i64), %0
+ G_STORE %1(i8), %2(p1) :: (store (i8) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+
+...
+---
+name: test_scmp
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_scmp
+ ; CHECK: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i1) = G_CONSTANT i1 false
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i1) = G_CONSTANT i1 false
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i16) = G_SELECT [[C1]](i1), [[C3]], [[C4]]
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(i16) = G_SELECT [[C2]](i1), [[C5]], [[SELECT]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC [[SELECT1]](i16)
+ ; CHECK-NEXT: G_STORE [[TRUNC]](i8), [[C]](p1) :: (store (i8) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:_(i64) = G_CONSTANT i64 0
+ %2:_(p1) = G_CONSTANT i64 0
+ %1:_(i8) = G_SCMP %0(i64), %0
+ G_STORE %1(i8), %2(p1) :: (store (i8) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+
+...
+---
+name: test_scmpv
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_scmpv
+ ; CHECK: %0:regv4_32b(<4 x i32>) = loadParam_v4i32 0, 0
+ ; CHECK-NEXT: %1:regv4_32b(<4 x i32>) = loadParam_v4i32 1, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[UV:%[0-9]+]]:_(i32), [[UV1:%[0-9]+]]:_(i32), [[UV2:%[0-9]+]]:_(i32), [[UV3:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %0(<4 x i32>)
+ ; CHECK-NEXT: [[UV4:%[0-9]+]]:_(i32), [[UV5:%[0-9]+]]:_(i32), [[UV6:%[0-9]+]]:_(i32), [[UV7:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %1(<4 x i32>)
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(sgt), [[UV]](i32), [[UV4]]
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(sgt), [[UV1]](i32), [[UV5]]
+ ; CHECK-NEXT: [[ICMP2:%[0-9]+]]:_(i1) = G_ICMP intpred(sgt), [[UV2]](i32), [[UV6]]
+ ; CHECK-NEXT: [[ICMP3:%[0-9]+]]:_(i1) = G_ICMP intpred(sgt), [[UV3]](i32), [[UV7]]
+ ; CHECK-NEXT: [[UV8:%[0-9]+]]:_(i32), [[UV9:%[0-9]+]]:_(i32), [[UV10:%[0-9]+]]:_(i32), [[UV11:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %0(<4 x i32>)
+ ; CHECK-NEXT: [[UV12:%[0-9]+]]:_(i32), [[UV13:%[0-9]+]]:_(i32), [[UV14:%[0-9]+]]:_(i32), [[UV15:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %1(<4 x i32>)
+ ; CHECK-NEXT: [[ICMP4:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[UV8]](i32), [[UV12]]
+ ; CHECK-NEXT: [[ICMP5:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[UV9]](i32), [[UV13]]
+ ; CHECK-NEXT: [[ICMP6:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[UV10]](i32), [[UV14]]
+ ; CHECK-NEXT: [[ICMP7:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[UV11]](i32), [[UV15]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C2]], [[C1]]
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(i32) = G_SELECT [[ICMP1]](i1), [[C2]], [[C1]]
+ ; CHECK-NEXT: [[SELECT2:%[0-9]+]]:_(i32) = G_SELECT [[ICMP2]](i1), [[C2]], [[C1]]
+ ; CHECK-NEXT: [[SELECT3:%[0-9]+]]:_(i32) = G_SELECT [[ICMP3]](i1), [[C2]], [[C1]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 -1
+ ; CHECK-NEXT: [[SELECT4:%[0-9]+]]:_(i32) = G_SELECT [[ICMP4]](i1), [[C3]], [[SELECT]]
+ ; CHECK-NEXT: [[SELECT5:%[0-9]+]]:_(i32) = G_SELECT [[ICMP5]](i1), [[C3]], [[SELECT1]]
+ ; CHECK-NEXT: [[SELECT6:%[0-9]+]]:_(i32) = G_SELECT [[ICMP6]](i1), [[C3]], [[SELECT2]]
+ ; CHECK-NEXT: [[SELECT7:%[0-9]+]]:_(i32) = G_SELECT [[ICMP7]](i1), [[C3]], [[SELECT3]]
+ ; CHECK-NEXT: [[BUILDVEC:%[0-9]+]]:_(<4 x i32>) = G_BUILD_VECTOR [[SELECT4]](i32), [[SELECT5]](i32), [[SELECT6]](i32), [[SELECT7]](i32)
+ ; CHECK-NEXT: G_STORE [[BUILDVEC]](<4 x i32>), [[C]](p1) :: (store (<4 x i32>) into `ptr addrspace(1) null`, align 4, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:regv4_32b(<4 x i32>) = loadParam_v4i32 0, 0
+ %1:regv4_32b(<4 x i32>) = loadParam_v4i32 1, 0
+ %3:_(p1) = G_CONSTANT i64 0
+ %2:_(<4 x i32>) = G_SCMP %0(<4 x i32>), %1
+ G_STORE %2(<4 x i32>), %3(p1) :: (store (<4 x i32>) into `ptr addrspace(1) null`, align 4, addrspace 1)
+ ret
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir
new file mode 100644
index 00000000000000..8543f42709ca5a
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir
@@ -0,0 +1,182 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
+---
+name: test_2xi16_or
+body: |
+ bb.1.entry:
+ ; LEGAL-LABEL: name: test_2xi16_or
+ ; LEGAL: %0:regv2_16b(<2 x i16>) = functionParameter_v2i16 0
+ ; LEGAL-NEXT: %1:regv2_16b(<2 x i16>) = functionParameter_v2i16 1
+ ; LEGAL-NEXT: %2:regv2_16b(<2 x i16>) = functionParameter_v2i16 2
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST %0(<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(i32) = G_BITCAST %1(<2 x i16>)
+ ; LEGAL-NEXT: [[OR:%[0-9]+]]:_(i32) = G_OR [[BITCAST]], [[BITCAST1]]
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[OR]](i32)
+ ; LEGAL-NEXT: [[BITCAST3:%[0-9]+]]:_(i32) = G_BITCAST [[BITCAST2]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST4:%[0-9]+]]:_(i32) = G_BITCAST %2(<2 x i16>)
+ ; LEGAL-NEXT: [[OR1:%[0-9]+]]:_(i32) = G_OR [[BITCAST3]], [[BITCAST4]]
+ ; LEGAL-NEXT: [[BITCAST5:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[OR1]](i32)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv2_16b(<2 x i16>) = COPY [[BITCAST5]](<2 x i16>)
+ ; LEGAL-NEXT: retValue_v2i16_r [[COPY]](<2 x i16>)
+ %0:regv2_16b(<2 x i16>) = functionParameter_v2i16 0
+ %1:regv2_16b(<2 x i16>) = functionParameter_v2i16 1
+ %2:regv2_16b(<2 x i16>) = functionParameter_v2i16 2
+ %3:_(<2 x i16>) = G_OR %0, %1
+ %4:_(<2 x i16>) = G_OR %3, %2
+ %5:regv2_16b(<2 x i16>) = COPY %4
+ retValue_v2i16_r %5
+
+...
+---
+name: test_3xi16_or
+body: |
+ bb.1.entry:
+ ; LEGAL-LABEL: name: test_3xi16_or
+ ; LEGAL: %0:regv3_16b(<3 x i16>) = functionParameter_v3i16 0
+ ; LEGAL-NEXT: %1:regv3_16b(<3 x i16>) = functionParameter_v3i16 1
+ ; LEGAL-NEXT: %2:regv3_16b(<3 x i16>) = functionParameter_v3i16 2
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i16), [[UV1:%[0-9]+]]:_(i16), [[UV2:%[0-9]+]]:_(i16) = G_UNMERGE_VALUES %0(<3 x i16>)
+ ; LEGAL-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<2 x i16>) = G_BUILD_VECTOR [[UV]](i16), [[UV1]](i16)
+ ; LEGAL-NEXT: [[UV3:%[0-9]+]]:_(i16), [[UV4:%[0-9]+]]:_(i16), [[UV5:%[0-9]+]]:_(i16) = G_UNMERGE_VALUES %1(<3 x i16>)
+ ; LEGAL-NEXT: [[BUILD_VECTOR1:%[0-9]+]]:_(<2 x i16>) = G_BUILD_VECTOR [[UV3]](i16), [[UV4]](i16)
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST [[BUILD_VECTOR]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(i32) = G_BITCAST [[BUILD_VECTOR1]](<2 x i16>)
+ ; LEGAL-NEXT: [[OR:%[0-9]+]]:_(i32) = G_OR [[BITCAST]], [[BITCAST1]]
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[OR]](i32)
+ ; LEGAL-NEXT: [[OR1:%[0-9]+]]:_(i16) = G_OR [[UV2]], [[UV5]]
+ ; LEGAL-NEXT: [[UV6:%[0-9]+]]:_(i16), [[UV7:%[0-9]+]]:_(i16), [[UV8:%[0-9]+]]:_(i16) = G_UNMERGE_VALUES %2(<3 x i16>)
+ ; LEGAL-NEXT: [[BUILD_VECTOR2:%[0-9]+]]:_(<2 x i16>) = G_BUILD_VECTOR [[UV6]](i16), [[UV7]](i16)
+ ; LEGAL-NEXT: [[BITCAST3:%[0-9]+]]:_(i32) = G_BITCAST [[BITCAST2]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST4:%[0-9]+]]:_(i32) = G_BITCAST [[BUILD_VECTOR2]](<2 x i16>)
+ ; LEGAL-NEXT: [[OR2:%[0-9]+]]:_(i32) = G_OR [[BITCAST3]], [[BITCAST4]]
+ ; LEGAL-NEXT: [[BITCAST5:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[OR2]](i32)
+ ; LEGAL-NEXT: [[OR3:%[0-9]+]]:_(i16) = G_OR [[OR1]], [[UV8]]
+ ; LEGAL-NEXT: [[UV9:%[0-9]+]]:_(i16), [[UV10:%[0-9]+]]:_(i16) = G_UNMERGE_VALUES [[BITCAST5]](<2 x i16>)
+ ; LEGAL-NEXT: [[BUILD_VECTOR3:%[0-9]+]]:_(<3 x i16>) = G_BUILD_VECTOR [[UV9]](i16), [[UV10]](i16), [[OR3]](i16)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv3_16b(<3 x i16>) = COPY [[BUILD_VECTOR3]](<3 x i16>)
+ ; LEGAL-NEXT: retValue_v3i16_r [[COPY]](<3 x i16>)
+ %0:regv3_16b(<3 x i16>) = functionParameter_v3i16 0
+ %1:regv3_16b(<3 x i16>) = functionParameter_v3i16 1
+ %2:regv3_16b(<3 x i16>) = functionParameter_v3i16 2
+ %3:_(<3 x i16>) = G_OR %0, %1
+ %4:_(<3 x i16>) = G_OR %3, %2
+ %5:regv3_16b(<3 x i16>) = COPY %4
+ retValue_v3i16_r %5
+
+...
+---
+name: test_4xi16_or
+body: |
+ bb.1.entry:
+ ; LEGAL-LABEL: name: test_4xi16_or
+ ; LEGAL: %0:regv4_16b(<4 x i16>) = functionParameter_v4i16 0
+ ; LEGAL-NEXT: %1:regv4_16b(<4 x i16>) = functionParameter_v4i16 1
+ ; LEGAL-NEXT: %2:regv4_16b(<4 x i16>) = functionParameter_v4i16 2
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(<2 x i16>), [[UV1:%[0-9]+]]:_(<2 x i16>) = G_UNMERGE_VALUES %0(<4 x i16>)
+ ; LEGAL-NEXT: [[UV2:%[0-9]+]]:_(<2 x i16>), [[UV3:%[0-9]+]]:_(<2 x i16>) = G_UNMERGE_VALUES %1(<4 x i16>)
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST [[UV]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(i32) = G_BITCAST [[UV2]](<2 x i16>)
+ ; LEGAL-NEXT: [[OR:%[0-9]+]]:_(i32) = G_OR [[BITCAST]], [[BITCAST1]]
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[OR]](i32)
+ ; LEGAL-NEXT: [[BITCAST3:%[0-9]+]]:_(i32) = G_BITCAST [[UV1]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST4:%[0-9]+]]:_(i32) = G_BITCAST [[UV3]](<2 x i16>)
+ ; LEGAL-NEXT: [[OR1:%[0-9]+]]:_(i32) = G_OR [[BITCAST3]], [[BITCAST4]]
+ ; LEGAL-NEXT: [[BITCAST5:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[OR1]](i32)
+ ; LEGAL-NEXT: [[UV4:%[0-9]+]]:_(<2 x i16>), [[UV5:%[0-9]+]]:_(<2 x i16>) = G_UNMERGE_VALUES %2(<4 x i16>)
+ ; LEGAL-NEXT: [[BITCAST6:%[0-9]+]]:_(i32) = G_BITCAST [[BITCAST2]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST7:%[0-9]+]]:_(i32) = G_BITCAST [[UV4]](<2 x i16>)
+ ; LEGAL-NEXT: [[OR2:%[0-9]+]]:_(i32) = G_OR [[BITCAST6]], [[BITCAST7]]
+ ; LEGAL-NEXT: [[BITCAST8:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[OR2]](i32)
+ ; LEGAL-NEXT: [[BITCAST9:%[0-9]+]]:_(i32) = G_BITCAST [[BITCAST5]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST10:%[0-9]+]]:_(i32) = G_BITCAST [[UV5]](<2 x i16>)
+ ; LEGAL-NEXT: [[OR3:%[0-9]+]]:_(i32) = G_OR [[BITCAST9]], [[BITCAST10]]
+ ; LEGAL-NEXT: [[BITCAST11:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[OR3]](i32)
+ ; LEGAL-NEXT: [[CONCAT_VECTORS:%[0-9]+]]:_(<4 x i16>) = G_CONCAT_VECTORS [[BITCAST8]](<2 x i16>), [[BITCAST11]](<2 x i16>)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv4_16b(<4 x i16>) = COPY [[CONCAT_VECTORS]](<4 x i16>)
+ ; LEGAL-NEXT: retValue_v4i16_r [[COPY]](<4 x i16>)
+ %0:regv4_16b(<4 x i16>) = functionParameter_v4i16 0
+ %1:regv4_16b(<4 x i16>) = functionParameter_v4i16 1
+ %2:regv4_16b(<4 x i16>) = functionParameter_v4i16 2
+ %3:_(<4 x i16>) = G_OR %0, %1
+ %4:_(<4 x i16>) = G_OR %3, %2
+ %5:regv4_16b(<4 x i16>) = COPY %4
+ retValue_v4i16_r %5
+
+...
+---
+name: test_2xi16_and
+body: |
+ bb.1.entry:
+ ; LEGAL-LABEL: name: test_2xi16_and
+ ; LEGAL: %0:regv2_16b(<2 x i16>) = functionParameter_v2i16 0
+ ; LEGAL-NEXT: %1:regv2_16b(<2 x i16>) = functionParameter_v2i16 1
+ ; LEGAL-NEXT: %2:regv2_16b(<2 x i16>) = functionParameter_v2i16 2
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST %0(<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(i32) = G_BITCAST %1(<2 x i16>)
+ ; LEGAL-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[BITCAST]], [[BITCAST1]]
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[AND]](i32)
+ ; LEGAL-NEXT: [[BITCAST3:%[0-9]+]]:_(i32) = G_BITCAST [[BITCAST2]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST4:%[0-9]+]]:_(i32) = G_BITCAST %2(<2 x i16>)
+ ; LEGAL-NEXT: [[AND1:%[0-9]+]]:_(i32) = G_AND [[BITCAST3]], [[BITCAST4]]
+ ; LEGAL-NEXT: [[BITCAST5:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[AND1]](i32)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv2_16b(<2 x i16>) = COPY [[BITCAST5]](<2 x i16>)
+ ; LEGAL-NEXT: retValue_v2i16_r [[COPY]](<2 x i16>)
+ %0:regv2_16b(<2 x i16>) = functionParameter_v2i16 0
+ %1:regv2_16b(<2 x i16>) = functionParameter_v2i16 1
+ %2:regv2_16b(<2 x i16>) = functionParameter_v2i16 2
+ %3:_(<2 x i16>) = G_AND %0, %1
+ %4:_(<2 x i16>) = G_AND %3, %2
+ %5:regv2_16b(<2 x i16>) = COPY %4
+ retValue_v2i16_r %5
+...
+---
+name: test_2xi16_xor
+body: |
+ bb.1.entry:
+ ; LEGAL-LABEL: name: test_2xi16_xor
+ ; LEGAL: %0:regv2_16b(<2 x i16>) = functionParameter_v2i16 0
+ ; LEGAL-NEXT: %1:regv2_16b(<2 x i16>) = functionParameter_v2i16 1
+ ; LEGAL-NEXT: %2:regv2_16b(<2 x i16>) = functionParameter_v2i16 2
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST %0(<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(i32) = G_BITCAST %1(<2 x i16>)
+ ; LEGAL-NEXT: [[XOR:%[0-9]+]]:_(i32) = G_XOR [[BITCAST]], [[BITCAST1]]
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[XOR]](i32)
+ ; LEGAL-NEXT: [[BITCAST3:%[0-9]+]]:_(i32) = G_BITCAST [[BITCAST2]](<2 x i16>)
+ ; LEGAL-NEXT: [[BITCAST4:%[0-9]+]]:_(i32) = G_BITCAST %2(<2 x i16>)
+ ; LEGAL-NEXT: [[XOR1:%[0-9]+]]:_(i32) = G_XOR [[BITCAST3]], [[BITCAST4]]
+ ; LEGAL-NEXT: [[BITCAST5:%[0-9]+]]:_(<2 x i16>) = G_BITCAST [[XOR1]](i32)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv2_16b(<2 x i16>) = COPY [[BITCAST5]](<2 x i16>)
+ ; LEGAL-NEXT: retValue_v2i16_r [[COPY]](<2 x i16>)
+ %0:regv2_16b(<2 x i16>) = functionParameter_v2i16 0
+ %1:regv2_16b(<2 x i16>) = functionParameter_v2i16 1
+ %2:regv2_16b(<2 x i16>) = functionParameter_v2i16 2
+ %3:_(<2 x i16>) = G_XOR %0, %1
+ %4:_(<2 x i16>) = G_XOR %3, %2
+ %5:regv2_16b(<2 x i16>) = COPY %4
+ retValue_v2i16_r %5
+...
+---
+name: test_2xi32_and
+body: |
+ bb.1.entry:
+ ; LEGAL-LABEL: name: test_2xi32_and
+ ; LEGAL: %0:regv2_32b(<2 x i32>) = functionParameter_v2i32 0
+ ; LEGAL-NEXT: %1:regv2_32b(<2 x i32>) = functionParameter_v2i32 1
+ ; LEGAL-NEXT: %2:regv2_32b(<2 x i32>) = functionParameter_v2i32 2
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i32), [[UV1:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %0(<2 x i32>)
+ ; LEGAL-NEXT: [[UV2:%[0-9]+]]:_(i32), [[UV3:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %1(<2 x i32>)
+ ; LEGAL-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[UV]], [[UV2]]
+ ; LEGAL-NEXT: [[AND1:%[0-9]+]]:_(i32) = G_AND [[UV1]], [[UV3]]
+ ; LEGAL-NEXT: [[UV4:%[0-9]+]]:_(i32), [[UV5:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %2(<2 x i32>)
+ ; LEGAL-NEXT: [[AND2:%[0-9]+]]:_(i32) = G_AND [[AND]], [[UV4]]
+ ; LEGAL-NEXT: [[AND3:%[0-9]+]]:_(i32) = G_AND [[AND1]], [[UV5]]
+ ; LEGAL-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<2 x i32>) = G_BUILD_VECTOR [[AND2]](i32), [[AND3]](i32)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv2_32b(<2 x i32>) = COPY [[BUILD_VECTOR]](<2 x i32>)
+ ; LEGAL-NEXT: retValue_v2i32_r [[COPY]](<2 x i32>)
+ %0:regv2_32b(<2 x i32>) = functionParameter_v2i32 0
+ %1:regv2_32b(<2 x i32>) = functionParameter_v2i32 1
+ %2:regv2_32b(<2 x i32>) = functionParameter_v2i32 2
+ %3:_(<2 x i32>) = G_AND %0, %1
+ %4:_(<2 x i32>) = G_AND %3, %2
+ %5:regv2_32b(<2 x i32>) = COPY %4
+ retValue_v2i32_r %5
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir
new file mode 100644
index 00000000000000..411243b1a87f6b
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir
@@ -0,0 +1,40 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
+
+# Check that G_CONCAT_VECTORS of vectors larger than 4 elts are legalized.
+--- |
+ define pisa_kernel void @test_concat_clamp_elts(ptr addrspace(1) writeonly captures(none) initializes((0, 3)) %A) local_unnamed_addr {
+ %L6 = load <3 x i16>, ptr addrspace(1) null, align 4294967296
+ %bc = bitcast <3 x i16> %L6 to <2 x i24>
+ %1 = extractelement <2 x i24> %bc, i64 0
+ %2 = zext <3 x i16> %L6 to <3 x i32>
+ %3 = bitcast <3 x i32> %2 to <6 x i16>
+ %A9.sroa.0.sroa.0.0.vec.expand = shufflevector <6 x i16> %3, <6 x i16> poison, <12 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison, i32 poison>
+ %A9.sroa.0.sroa.0.0.vecblend = shufflevector <12 x i16> %A9.sroa.0.sroa.0.0.vec.expand, <12 x i16> <i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 poison, i16 undef, i16 undef, i16 undef, i16 undef, i16 undef, i16 undef>, <12 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 18, i32 19, i32 20, i32 21, i32 22, i32 23>
+ store i24 %1, ptr addrspace(1) %A, align 4
+ store <12 x i16> %A9.sroa.0.sroa.0.0.vecblend, ptr addrspace(1) null, align 4294967296
+ ret void
+ }
+...
+---
+name: test_concat_clamp_elts
+tracksRegLiveness: true
+body: |
+ bb.1 (%ir-block.0):
+ %0:reg64b(p1) = loadParam_i64 0, 0
+ %2:_(p1) = G_CONSTANT i64 0
+ %5:_(i32) = G_CONSTANT i32 0
+ %10:_(<12 x i16>) = G_IMPLICIT_DEF
+ %1:_(<3 x i16>) = G_LOAD %2:_(p1) :: (load (<3 x i16>) from `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ %3:_(<2 x i24>) = G_BITCAST %1:_(<3 x i16>)
+ %4:_(i24) = G_EXTRACT_VECTOR_ELT %3:_(<2 x i24>), %5:_(i32)
+ %6:_(<3 x i32>) = G_ZEXT %1:_(<3 x i16>)
+ %7:_(<6 x i16>) = G_BITCAST %6:_(<3 x i32>)
+ %14:_(<6 x i16>) = G_IMPLICIT_DEF
+ ; LEGAL-NOT: %{{[0-9]+}}:_(<12 x i16>) = G_CONCAT_VECTORS
+ %15:_(<12 x i16>) = G_CONCAT_VECTORS %7:_(<6 x i16>), %14:_(<6 x i16>)
+ %13:_(<12 x i16>) = G_SHUFFLE_VECTOR %15:_(<12 x i16>), %10:_, shufflemask(0, 1, 2, 3, 4, 5, undef, undef, undef, undef, undef, undef)
+ %16:_(<3 x i8>) = G_BITCAST %4:_(i24)
+ G_STORE %16:_(<3 x i8>), %0:reg64b(p1) :: (store (<3 x i8>) into %ir.A, align 4, addrspace 1)
+ G_STORE %13:_(<12 x i16>), %2:_(p1) :: (store (<12 x i16>) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ ret
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir
new file mode 100644
index 00000000000000..e8fa9c84f0adb8
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir
@@ -0,0 +1,158 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+
+---
+name: test_constant_s32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_s32
+ ; CHECK: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 5
+ ; CHECK-NEXT: $reg32b_0 = COPY [[C]](i32)
+ %0:_(i32) = G_CONSTANT i32 5
+ $reg32b_0 = COPY %0
+...
+---
+name: test_constant_s64
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_s64
+ ; CHECK: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 -6148914691236517206
+ ; CHECK-NEXT: $reg32bx2 = COPY [[C]](i64)
+ %0:_(i64) = G_CONSTANT i64 -6148914691236517206
+ $reg32bx2 = COPY %0
+
+...
+---
+name: test_constant_s96
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_s96
+ ; CHECK: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 -6148886058121296224
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 -33334
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(i128) = G_MERGE_VALUES [[C]](i64), [[C1]](i64)
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i96) = G_TRUNC [[MV]](i128)
+ ; CHECK-NEXT: $reg32bx3 = COPY [[TRUNC]](i96)
+ %0:_(i96) = G_CONSTANT i96 -614891469095018605312352
+ $reg32bx3 = COPY %0
+
+...
+
+---
+name: test_constant_s7
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_s7
+ ; CHECK: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 2
+ ; CHECK-NEXT: $reg32b_0 = COPY [[C]](i32)
+ %0:_(i7) = G_CONSTANT i7 2
+ %1:_(i32) = G_ANYEXT %0
+ $reg32b_0 = COPY %1
+...
+
+---
+name: test_constant_s8
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_s8
+ ; CHECK: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 8
+ ; CHECK-NEXT: $reg32b_0 = COPY [[C]](i32)
+ %0:_(i8) = G_CONSTANT i8 8
+ %1:_(i32) = G_ANYEXT %0
+ $reg32b_0 = COPY %1
+...
+
+---
+name: test_constant_s16
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_s16
+ ; CHECK: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 15
+ ; CHECK-NEXT: $reg32b_0 = COPY [[C]](i32)
+ %0:_(i16) = G_CONSTANT i16 15
+ %1:_(i32) = G_ANYEXT %0
+ $reg32b_0 = COPY %1
+...
+
+
+---
+name: test_constant_s112
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_s112
+ ; CHECK: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 100
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(i128) = G_MERGE_VALUES [[C]](i64), [[C1]](i64)
+ ; CHECK-NEXT: $reg32bx4 = COPY [[MV]](i128)
+ %0:_(i112) = G_CONSTANT i112 100
+ %1:_(i128) = G_ANYEXT %0
+ $reg32bx4 = COPY %1
+...
+
+---
+name: test_constant_s128
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_s128
+ ; CHECK: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 5
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(i128) = G_MERGE_VALUES [[C]](i64), [[C1]](i64)
+ ; CHECK-NEXT: $reg32bx4 = COPY [[MV]](i128)
+ %0:_(i128) = G_CONSTANT i128 5
+ $reg32bx4 = COPY %0
+...
+
+---
+name: test_constant_p4
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_p4
+ ; CHECK: [[C:%[0-9]+]]:_(p4) = G_CONSTANT i32 0
+ ; CHECK-NEXT: $reg32b_0 = COPY [[C]](p4)
+ %0:_(p4) = G_CONSTANT i32 0
+ $reg32b_0 = COPY %0
+...
+
+---
+name: test_constant_p1
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_p1
+ ; CHECK: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: $reg64b_0 = COPY [[C]](p1)
+ %0:_(p1) = G_CONSTANT i64 0
+ $reg64b_0 = COPY %0
+...
+
+---
+name: test_constant_p3
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_p3
+ ; CHECK: [[C:%[0-9]+]]:_(p3) = G_CONSTANT i32 0
+ ; CHECK-NEXT: $reg32b_0 = COPY [[C]](p3)
+ %0:_(p3) = G_CONSTANT i32 0
+ $reg32b_0 = COPY %0
+...
+
+---
+name: test_constant_p0
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_constant_p0
+ ; CHECK: [[C:%[0-9]+]]:_(p0) = G_CONSTANT i64 0
+ ; CHECK-NEXT: $reg64b_0 = COPY [[C]](p0)
+ %0:_(p0) = G_CONSTANT i64 0
+ $reg64b_0 = COPY %0
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir
new file mode 100644
index 00000000000000..71079bbb50b918
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir
@@ -0,0 +1,15 @@
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+
+---
+name: extract_subvector_s64
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: extract_subvector_s64
+ ; CHECK: [[SUBVECTOR_S32:%[0-9]+]]:_(<4 x i32>) = G_EXTRACT_SUBVECTOR {{%[0-9]+}}(<32 x i32>)
+ ; CHECK: [[SUBVECTOR_S64:%[0-9]+]]:_(<2 x i64>) = G_BITCAST [[SUBVECTOR_S32]](<4 x i32>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(i64) = G_CONSTANT i64 1
+ %2:_(<16 x i64>) = G_BUILD_VECTOR %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64)
+ %3:_(<2 x i64>) = G_EXTRACT_SUBVECTOR %2:_(<16 x i64>), 8
+ G_STORE %3:_(<2 x i64>), %0:_(p1) :: (store (<2 x i64>))
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
new file mode 100644
index 00000000000000..81147be2a538cb
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
@@ -0,0 +1,52 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# Check that i32 vectors with an illegal number of elements are extended to the nearest legal size before extracting the element using the G_EXTRACT_VECTOR_ELT instruction.
+
+--- |
+ define pisa_kernel i32 @test_v10_32b(<10 x i32> %vec) {
+ %elem = extractelement <10 x i32> %vec, i32 5
+ ret i32 %elem
+ }
+
+ define pisa_kernel i32 @test_v20_32b(<20 x i32> %vec) {
+ %elem = extractelement <20 x i32> %vec, i32 10
+ ret i32 %elem
+ }
+...
+---
+name: test_v10_32b
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: {{^}}name: test_v10_32b
+ %2:regv2_32b(<2 x i32>) = loadParam_v2i32 0, 0
+ %3:regv2_32b(<2 x i32>) = loadParam_v2i32 0, 8
+ %4:regv2_32b(<2 x i32>) = loadParam_v2i32 0, 16
+ %5:regv2_32b(<2 x i32>) = loadParam_v2i32 0, 24
+ %6:regv2_32b(<2 x i32>) = loadParam_v2i32 0, 32
+ %1:_(<10 x i32>) = G_CONCAT_VECTORS %2:regv2_32b(<2 x i32>), %3:regv2_32b(<2 x i32>), %4:regv2_32b(<2 x i32>), %5:regv2_32b(<2 x i32>), %6:regv2_32b(<2 x i32>)
+ %8:_(i32) = G_CONSTANT i32 5
+ ; CHECK: G_EXTRACT_VECTOR_ELT
+ ; CHECK-SAME: <16 x i32>
+ %7:_(i32) = G_EXTRACT_VECTOR_ELT %1:_(<10 x i32>), %8:_(i32)
+ %9:reg32b(i32) = COPY %7:_(i32)
+ retValue_i32_r %9:reg32b(i32)
+...
+---
+name: test_v20_32b
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: {{^}}name: test_v20_32b
+ %2:regv4_32b(<4 x i32>) = loadParam_v4i32 0, 0
+ %3:regv4_32b(<4 x i32>) = loadParam_v4i32 0, 16
+ %4:regv4_32b(<4 x i32>) = loadParam_v4i32 0, 32
+ %5:regv4_32b(<4 x i32>) = loadParam_v4i32 0, 48
+ %6:regv4_32b(<4 x i32>) = loadParam_v4i32 0, 64
+ %1:_(<20 x i32>) = G_CONCAT_VECTORS %2:regv4_32b(<4 x i32>), %3:regv4_32b(<4 x i32>), %4:regv4_32b(<4 x i32>), %5:regv4_32b(<4 x i32>), %6:regv4_32b(<4 x i32>)
+ %8:_(i32) = G_CONSTANT i32 10
+ ; CHECK: G_EXTRACT_VECTOR_ELT
+ ; CHECK-SAME: <32 x i32>
+ %7:_(i32) = G_EXTRACT_VECTOR_ELT %1:_(<20 x i32>), %8:_(i32)
+ %9:reg32b(i32) = COPY %7:_(i32)
+ retValue_i32_r %9:reg32b(i32)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir
new file mode 100644
index 00000000000000..b51cc3ccf27b07
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir
@@ -0,0 +1,411 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+---
+name: test_afn_bfloat
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_afn_bfloat
+ ; CHECK: %0:reg16b(bf16) = functionParameter_i16 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[SITOFP:%[0-9]+]]:_(bf16) = G_SITOFP %1(i32)
+ ; CHECK-NEXT: [[FEXP2_:%[0-9]+]]:_(bf16) = afn G_FEXP2 [[SITOFP]]
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(bf16) = afn G_FMUL %0, [[FEXP2_]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg16b(i16) = G_BITCAST [[FMUL]](bf16)
+ ; CHECK-NEXT: retValue_i16_r [[COPY]](i16)
+ %0:reg16b(bf16) = functionParameter_i16 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(bf16) = afn G_FLDEXP %0:reg16b, %1:reg32b(i32)
+ %3:reg16b(i16) = G_BITCAST %2:_(bf16)
+ retValue_i16_r %3:reg16b(i16)
+...
+
+---
+name: test_afn_half
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_afn_half
+ ; CHECK: %0:reg16b(f16) = functionParameter_i16 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[SITOFP:%[0-9]+]]:_(f16) = G_SITOFP %1(i32)
+ ; CHECK-NEXT: [[FEXP2_:%[0-9]+]]:_(f16) = afn G_FEXP2 [[SITOFP]]
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(f16) = afn G_FMUL %0, [[FEXP2_]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg16b(i16) = G_BITCAST [[FMUL]](f16)
+ ; CHECK-NEXT: retValue_i16_r [[COPY]](i16)
+ %0:reg16b(f16) = functionParameter_i16 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(f16) = afn G_FLDEXP %0:reg16b, %1:reg32b(i32)
+ %3:reg16b(i16) = G_BITCAST %2:_(f16)
+ retValue_i16_r %3:reg16b(i16)
+...
+
+---
+name: test_afn_double
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_afn_double
+ ; CHECK: %0:reg64b(f64) = functionParameter_i64 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -3066
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 3066
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 3069
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 21846
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i32) = G_MUL [[ADD]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 16
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i32) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i32) = G_SUB [[ADD]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i32) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[LSHR]](i32)
+ ; CHECK-NEXT: [[ZEXT1:%[0-9]+]]:_(i64) = G_ZEXT [[SUB1]](i32)
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 52
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i64) = G_SHL [[ZEXT]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i64) = G_SHL [[ZEXT1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(f64) = G_BITCAST [[SHL]](i64)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(f64) = G_BITCAST [[SHL1]](i64)
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i32) = G_CONSTANT i32 340
+ ; CHECK-NEXT: [[ABS:%[0-9]+]]:_(i32) = G_ABS [[SMIN]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[ABS]](i32), [[C6]]
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(f64) = G_SELECT [[ICMP]](i1), [[BITCAST1]], %0
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(f64) = G_SELECT [[ICMP]](i1), %0, [[BITCAST1]]
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(f64) = afn G_FMUL [[SELECT]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(f64) = afn G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(f64) = afn G_FMUL [[FMUL1]], [[SELECT1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg64b(i64) = G_BITCAST [[FMUL2]](f64)
+ ; CHECK-NEXT: retValue_i64_r [[COPY]](i64)
+ %0:reg64b(f64) = functionParameter_i64 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(f64) = afn G_FLDEXP %0:reg64b, %1:reg32b(i32)
+ %3:reg64b(i64) = G_BITCAST %2:_(f64)
+ retValue_i64_r %3:reg64b(i64)
+...
+
+---
+name: test_bfloat
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_bfloat
+ ; CHECK: %0:reg16b(bf16) = functionParameter_i16 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -378
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 378
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 381
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 21846
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i32) = G_MUL [[ADD]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 16
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i32) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i32) = G_SUB [[ADD]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i32) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC [[LSHR]](i32)
+ ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(i16) = G_TRUNC [[SUB1]](i32)
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 7
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i16) = G_SHL [[TRUNC]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i16) = G_SHL [[TRUNC1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(bf16) = G_BITCAST [[SHL]](i16)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(bf16) = G_BITCAST [[SHL1]](i16)
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(bf16) = G_FMUL %0, [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(bf16) = G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(bf16) = G_FMUL [[FMUL1]], [[BITCAST1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg16b(i16) = G_BITCAST [[FMUL2]](bf16)
+ ; CHECK-NEXT: retValue_i16_r [[COPY]](i16)
+ %0:reg16b(bf16) = functionParameter_i16 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(bf16) = G_FLDEXP %0:reg16b, %1:reg32b(i32)
+ %3:reg16b(i16) = G_BITCAST %2:_(bf16)
+ retValue_i16_r %3:reg16b(i16)
+...
+
+---
+name: test_half
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_half
+ ; CHECK: %0:reg16b(f16) = functionParameter_i16 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -42
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 42
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 45
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC [[ADD]](i32)
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i16) = G_CONSTANT i16 86
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i16) = G_MUL [[TRUNC]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 8
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i16) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i16) = G_SUB [[TRUNC]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i16) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 10
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i16) = G_SHL [[LSHR]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i16) = G_SHL [[SUB1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(f16) = G_BITCAST [[SHL]](i16)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(f16) = G_BITCAST [[SHL1]](i16)
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(f16) = G_FMUL %0, [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(f16) = G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(f16) = G_FMUL [[FMUL1]], [[BITCAST1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg16b(i16) = G_BITCAST [[FMUL2]](f16)
+ ; CHECK-NEXT: retValue_i16_r [[COPY]](i16)
+ %0:reg16b(f16) = functionParameter_i16 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(f16) = G_FLDEXP %0:reg16b, %1:reg32b(i32)
+ %3:reg16b(i16) = G_BITCAST %2:_(f16)
+ retValue_i16_r %3:reg16b(i16)
+...
+
+---
+name: test_float
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_float
+ ; CHECK: %0:reg32b(f32) = functionParameter_i32 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -378
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 378
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 381
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 21846
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i32) = G_MUL [[ADD]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 16
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i32) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i32) = G_SUB [[ADD]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i32) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 23
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i32) = G_SHL [[LSHR]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i32) = G_SHL [[SUB1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(f32) = G_BITCAST [[SHL]](i32)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(f32) = G_BITCAST [[SHL1]](i32)
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i32) = G_CONSTANT i32 42
+ ; CHECK-NEXT: [[ABS:%[0-9]+]]:_(i32) = G_ABS [[SMIN]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[ABS]](i32), [[C6]]
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(f32) = G_SELECT [[ICMP]](i1), [[BITCAST1]], %0
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(f32) = G_SELECT [[ICMP]](i1), %0, [[BITCAST1]]
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(f32) = G_FMUL [[SELECT]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(f32) = G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(f32) = G_FMUL [[FMUL1]], [[SELECT1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg32b(i32) = G_BITCAST [[FMUL2]](f32)
+ ; CHECK-NEXT: retValue_i32_r [[COPY]](i32)
+ %0:reg32b(f32) = functionParameter_i32 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(f32) = G_FLDEXP %0:reg32b, %1:reg32b(i32)
+ %3:reg32b(i32) = G_BITCAST %2:_(f32)
+ retValue_i32_r %3:reg32b(i32)
+...
+
+---
+name: test_double
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_double
+ ; CHECK: %0:reg64b(f64) = functionParameter_i64 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -3066
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 3066
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 3069
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 21846
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i32) = G_MUL [[ADD]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 16
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i32) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i32) = G_SUB [[ADD]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i32) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[LSHR]](i32)
+ ; CHECK-NEXT: [[ZEXT1:%[0-9]+]]:_(i64) = G_ZEXT [[SUB1]](i32)
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 52
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i64) = G_SHL [[ZEXT]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i64) = G_SHL [[ZEXT1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(f64) = G_BITCAST [[SHL]](i64)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(f64) = G_BITCAST [[SHL1]](i64)
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i32) = G_CONSTANT i32 340
+ ; CHECK-NEXT: [[ABS:%[0-9]+]]:_(i32) = G_ABS [[SMIN]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[ABS]](i32), [[C6]]
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(f64) = G_SELECT [[ICMP]](i1), [[BITCAST1]], %0
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(f64) = G_SELECT [[ICMP]](i1), %0, [[BITCAST1]]
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(f64) = G_FMUL [[SELECT]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(f64) = G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(f64) = G_FMUL [[FMUL1]], [[SELECT1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg64b(i64) = G_BITCAST [[FMUL2]](f64)
+ ; CHECK-NEXT: retValue_i64_r [[COPY]](i64)
+ %0:reg64b(f64) = functionParameter_i64 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(f64) = G_FLDEXP %0:reg64b, %1:reg32b(i32)
+ %3:reg64b(i64) = G_BITCAST %2:_(f64)
+ retValue_i64_r %3:reg64b(i64)
+...
+
+---
+name: test_strict_bfloat
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_strict_bfloat
+ ; CHECK: %0:reg16b(bf16) = functionParameter_i16 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -378
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 378
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 381
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 21846
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i32) = G_MUL [[ADD]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 16
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i32) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i32) = G_SUB [[ADD]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i32) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC [[LSHR]](i32)
+ ; CHECK-NEXT: [[TRUNC1:%[0-9]+]]:_(i16) = G_TRUNC [[SUB1]](i32)
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 7
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i16) = G_SHL [[TRUNC]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i16) = G_SHL [[TRUNC1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(bf16) = G_BITCAST [[SHL]](i16)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(bf16) = G_BITCAST [[SHL1]](i16)
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(bf16) = G_FMUL %0, [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(bf16) = G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(bf16) = G_FMUL [[FMUL1]], [[BITCAST1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg16b(i16) = G_BITCAST [[FMUL2]](bf16)
+ ; CHECK-NEXT: retValue_i16_r [[COPY]](i16)
+ %0:reg16b(bf16) = functionParameter_i16 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(bf16) = G_STRICT_FLDEXP %0:reg16b, %1:reg32b(i32)
+ %3:reg16b(i16) = G_BITCAST %2:_(bf16)
+ retValue_i16_r %3:reg16b(i16)
+...
+
+---
+name: test_strict_half
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_strict_half
+ ; CHECK: %0:reg16b(f16) = functionParameter_i16 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -42
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 42
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 45
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC [[ADD]](i32)
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i16) = G_CONSTANT i16 86
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i16) = G_MUL [[TRUNC]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 8
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i16) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i16) = G_SUB [[TRUNC]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i16) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 10
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i16) = G_SHL [[LSHR]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i16) = G_SHL [[SUB1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(f16) = G_BITCAST [[SHL]](i16)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(f16) = G_BITCAST [[SHL1]](i16)
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(f16) = G_FMUL %0, [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(f16) = G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(f16) = G_FMUL [[FMUL1]], [[BITCAST1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg16b(i16) = G_BITCAST [[FMUL2]](f16)
+ ; CHECK-NEXT: retValue_i16_r [[COPY]](i16)
+ %0:reg16b(f16) = functionParameter_i16 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(f16) = G_STRICT_FLDEXP %0:reg16b, %1:reg32b(i32)
+ %3:reg16b(i16) = G_BITCAST %2:_(f16)
+ retValue_i16_r %3:reg16b(i16)
+...
+
+---
+name: test_strict_float
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_strict_float
+ ; CHECK: %0:reg32b(f32) = functionParameter_i32 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -378
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 378
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 381
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 21846
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i32) = G_MUL [[ADD]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 16
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i32) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i32) = G_SUB [[ADD]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i32) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 23
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i32) = G_SHL [[LSHR]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i32) = G_SHL [[SUB1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(f32) = G_BITCAST [[SHL]](i32)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(f32) = G_BITCAST [[SHL1]](i32)
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i32) = G_CONSTANT i32 42
+ ; CHECK-NEXT: [[ABS:%[0-9]+]]:_(i32) = G_ABS [[SMIN]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[ABS]](i32), [[C6]]
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(f32) = G_SELECT [[ICMP]](i1), [[BITCAST1]], %0
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(f32) = G_SELECT [[ICMP]](i1), %0, [[BITCAST1]]
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(f32) = G_FMUL [[SELECT]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(f32) = G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(f32) = G_FMUL [[FMUL1]], [[SELECT1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg32b(i32) = G_BITCAST [[FMUL2]](f32)
+ ; CHECK-NEXT: retValue_i32_r [[COPY]](i32)
+ %0:reg32b(f32) = functionParameter_i32 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(f32) = G_STRICT_FLDEXP %0:reg32b, %1:reg32b(i32)
+ %3:reg32b(i32) = G_BITCAST %2:_(f32)
+ retValue_i32_r %3:reg32b(i32)
+...
+
+---
+name: test_strict_double
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_strict_double
+ ; CHECK: %0:reg64b(f64) = functionParameter_i64 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 -3066
+ ; CHECK-NEXT: [[SMAX:%[0-9]+]]:_(i32) = G_SMAX %1, [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 3066
+ ; CHECK-NEXT: [[SMIN:%[0-9]+]]:_(i32) = G_SMIN [[SMAX]], [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 3069
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[SMIN]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 21846
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i32) = G_MUL [[ADD]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 16
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i32) = G_LSHR [[MUL]], [[C4]](i32)
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i32) = G_SUB [[ADD]], [[LSHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i32) = G_SUB [[SUB]], [[LSHR]]
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[LSHR]](i32)
+ ; CHECK-NEXT: [[ZEXT1:%[0-9]+]]:_(i64) = G_ZEXT [[SUB1]](i32)
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 52
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i64) = G_SHL [[ZEXT]], [[C5]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i64) = G_SHL [[ZEXT1]], [[C5]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(f64) = G_BITCAST [[SHL]](i64)
+ ; CHECK-NEXT: [[BITCAST1:%[0-9]+]]:_(f64) = G_BITCAST [[SHL1]](i64)
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i32) = G_CONSTANT i32 340
+ ; CHECK-NEXT: [[ABS:%[0-9]+]]:_(i32) = G_ABS [[SMIN]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(slt), [[ABS]](i32), [[C6]]
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(f64) = G_SELECT [[ICMP]](i1), [[BITCAST1]], %0
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(f64) = G_SELECT [[ICMP]](i1), %0, [[BITCAST1]]
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(f64) = G_FMUL [[SELECT]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL1:%[0-9]+]]:_(f64) = G_FMUL [[FMUL]], [[BITCAST]]
+ ; CHECK-NEXT: [[FMUL2:%[0-9]+]]:_(f64) = G_FMUL [[FMUL1]], [[SELECT1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg64b(i64) = G_BITCAST [[FMUL2]](f64)
+ ; CHECK-NEXT: retValue_i64_r [[COPY]](i64)
+ %0:reg64b(f64) = functionParameter_i64 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:_(f64) = G_STRICT_FLDEXP %0:reg64b, %1:reg32b(i32)
+ %3:reg64b(i64) = G_BITCAST %2:_(f64)
+ retValue_i64_r %3:reg64b(i64)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir
new file mode 100644
index 00000000000000..eef864f65c4ca8
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir
@@ -0,0 +1,48 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+# Verify that G_FREEZE is legalized successfully into two i64 parts.
+# NOTE: The intermediate widening to i128 is an internal step, and does not appear in the final output.
+
+--- |
+ define pisa_kernel void @test_freeze_s96() {
+ %a = load i96, ptr addrspace(1) null, align 16
+ %a.fr = freeze i96 %a
+ %lo = trunc i96 %a.fr to i32
+ %sh = lshr i96 %a.fr, 64
+ %hi = trunc i96 %sh to i32
+ store i32 %lo, ptr addrspace(1) null, align 4
+ store i32 %hi, ptr addrspace(1) null, align 4
+ ret void
+ }
+...
+---
+name: test_freeze_s96
+legalized: false
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ %1:_(p1) = G_CONSTANT i64 0
+ %4:_(i96) = G_CONSTANT i96 64
+ %8:_(i64) = G_LOAD %1(p1) :: (load (i64) from `ptr addrspace(1) null`, align 16, addrspace 1)
+ %80:_(i96) = G_ZEXT %8
+ %11:_(p1) = G_CONSTANT i64 8
+ %14:_(i32) = G_LOAD %11(p1) :: (load (i32) from `ptr addrspace(1) null` + 8, align 8, basealign 16, addrspace 1)
+ %140:_(i96) = G_ZEXT %14
+ %16:_(i96) = G_CONSTANT i96 64
+ %15:_(i96) = G_SHL %140, %16(i96)
+ %17:_(i96) = G_OR %80, %15
+ %2:_(i96) = G_FREEZE %17
+ %3:_(i32) = G_TRUNC %2(i96)
+ %5:_(i96) = G_LSHR %2, %4(i96)
+ %6:_(i32) = G_TRUNC %5(i96)
+
+ ; CHECK-LABEL: {{^}}name: test_freeze_s96
+ ; CHECK-NOT: (i96) = G_FREEZE
+ ; CHECK: (i64) = G_FREEZE
+ ; CHECK: (i64) = G_FREEZE
+
+ G_STORE %3(i32), %1(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ G_STORE %6(i32), %1(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir
new file mode 100644
index 00000000000000..bcaf76d7451110
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir
@@ -0,0 +1,116 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name: test_frem_afn
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_frem_afn
+ ; CHECK: [[DEF:%[0-9]+]]:_(f16) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(f16) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[FDIV:%[0-9]+]]:_(f16) = arcp G_FDIV [[DEF]], [[DEF1]]
+ ; CHECK-NEXT: [[INTRINSIC_TRUNC:%[0-9]+]]:_(f16) = afn G_INTRINSIC_TRUNC [[FDIV]]
+ ; CHECK-NEXT: [[FNEG:%[0-9]+]]:_(f16) = afn G_FNEG [[INTRINSIC_TRUNC]]
+ ; CHECK-NEXT: [[FMA:%[0-9]+]]:_(f16) = afn G_FMA [[FNEG]], [[DEF1]], [[DEF]]
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i16) = G_BITCAST [[FMA]](f16)
+ ; CHECK-NEXT: G_STORE [[BITCAST]](i16), [[C]](p1) :: (store (i16) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ %0:_(f16) = G_IMPLICIT_DEF
+ %1:_(f16) = G_IMPLICIT_DEF
+ %2:_(f16) = afn G_FREM %0, %1
+ %3:_(p1) = G_CONSTANT i64 0
+ G_STORE %2:_(f16), %3:_(p1) :: (store (i16) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+...
+
+---
+name: test_frem_fp16
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_frem_fp16
+ ; CHECK: [[DEF:%[0-9]+]]:_(f16) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(f16) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[FPEXT:%[0-9]+]]:_(f32) = G_FPEXT [[DEF]](f16)
+ ; CHECK-NEXT: [[FPEXT1:%[0-9]+]]:_(f32) = G_FPEXT [[DEF1]](f16)
+ ; CHECK-NEXT: [[FDIV:%[0-9]+]]:_(f32) = G_FDIV [[FPEXT]], [[FPEXT1]]
+ ; CHECK-NEXT: [[FPTRUNC:%[0-9]+]]:_(f16) = G_FPTRUNC [[FDIV]](f32)
+ ; CHECK-NEXT: [[INTRINSIC_TRUNC:%[0-9]+]]:_(f16) = G_INTRINSIC_TRUNC [[FPTRUNC]]
+ ; CHECK-NEXT: [[FNEG:%[0-9]+]]:_(f16) = G_FNEG [[INTRINSIC_TRUNC]]
+ ; CHECK-NEXT: [[FMA:%[0-9]+]]:_(f16) = G_FMA [[FNEG]], [[DEF1]], [[DEF]]
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(f16) = G_FCONSTANT half +inf
+ ; CHECK-NEXT: [[INT:%[0-9]+]]:_(f16) = G_INTRINSIC intrinsic(@llvm.pisa.fabs), [[DEF]](f16)
+ ; CHECK-NEXT: [[INT1:%[0-9]+]]:_(f16) = G_INTRINSIC intrinsic(@llvm.pisa.fabs), [[DEF1]](f16)
+ ; CHECK-NEXT: [[FCMP:%[0-9]+]]:_(i1) = G_FCMP floatpred(oeq), [[INT]](f16), [[C]]
+ ; CHECK-NEXT: [[FCMP1:%[0-9]+]]:_(i1) = G_FCMP floatpred(oeq), [[INT1]](f16), [[C]]
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(f16) = G_SELECT [[FCMP1]](i1), [[DEF]], [[FMA]]
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(f16) = G_SELECT [[FCMP]](i1), [[FMA]], [[SELECT]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i16) = G_BITCAST [[SELECT1]](f16)
+ ; CHECK-NEXT: G_STORE [[BITCAST]](i16), [[C1]](p1) :: (store (i16) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ %0:_(f16) = G_IMPLICIT_DEF
+ %1:_(f16) = G_IMPLICIT_DEF
+ %2:_(f16) = G_FREM %0, %1
+ %3:_(p1) = G_CONSTANT i64 0
+ G_STORE %2:_(f16), %3:_(p1) :: (store (i16) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+
+...
+
+---
+name: test_frem_fp32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_frem_fp32
+ ; CHECK: [[DEF:%[0-9]+]]:_(f32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(f32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[FDIV:%[0-9]+]]:_(f32) = G_FDIV [[DEF]], [[DEF1]]
+ ; CHECK-NEXT: [[INTRINSIC_TRUNC:%[0-9]+]]:_(f32) = G_INTRINSIC_TRUNC [[FDIV]]
+ ; CHECK-NEXT: [[FNEG:%[0-9]+]]:_(f32) = G_FNEG [[INTRINSIC_TRUNC]]
+ ; CHECK-NEXT: [[FMA:%[0-9]+]]:_(f32) = G_FMA [[FNEG]], [[DEF1]], [[DEF]]
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(f32) = G_FCONSTANT float +inf
+ ; CHECK-NEXT: [[INT:%[0-9]+]]:_(f32) = G_INTRINSIC intrinsic(@llvm.pisa.fabs), [[DEF]](f32)
+ ; CHECK-NEXT: [[INT1:%[0-9]+]]:_(f32) = G_INTRINSIC intrinsic(@llvm.pisa.fabs), [[DEF1]](f32)
+ ; CHECK-NEXT: [[FCMP:%[0-9]+]]:_(i1) = G_FCMP floatpred(oeq), [[INT]](f32), [[C]]
+ ; CHECK-NEXT: [[FCMP1:%[0-9]+]]:_(i1) = G_FCMP floatpred(oeq), [[INT1]](f32), [[C]]
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(f32) = G_SELECT [[FCMP1]](i1), [[DEF]], [[FMA]]
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(f32) = G_SELECT [[FCMP]](i1), [[FMA]], [[SELECT]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST [[SELECT1]](f32)
+ ; CHECK-NEXT: G_STORE [[BITCAST]](i32), [[C1]](p1) :: (store (i32) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ %0:_(f32) = G_IMPLICIT_DEF
+ %1:_(f32) = G_IMPLICIT_DEF
+ %2:_(f32) = G_FREM %0, %1
+ %3:_(p1) = G_CONSTANT i64 0
+ G_STORE %2:_(f32), %3:_(p1) :: (store (i32) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+
+...
+
+---
+name: test_frem_fp64
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_frem_fp64
+ ; CHECK: [[DEF:%[0-9]+]]:_(f64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(f64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[FDIV:%[0-9]+]]:_(f64) = G_FDIV [[DEF]], [[DEF1]]
+ ; CHECK-NEXT: [[INTRINSIC_TRUNC:%[0-9]+]]:_(f64) = G_INTRINSIC_TRUNC [[FDIV]]
+ ; CHECK-NEXT: [[FNEG:%[0-9]+]]:_(f64) = G_FNEG [[INTRINSIC_TRUNC]]
+ ; CHECK-NEXT: [[FMA:%[0-9]+]]:_(f64) = G_FMA [[FNEG]], [[DEF1]], [[DEF]]
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(f64) = G_FCONSTANT double +inf
+ ; CHECK-NEXT: [[INT:%[0-9]+]]:_(f64) = G_INTRINSIC intrinsic(@llvm.pisa.fabs), [[DEF]](f64)
+ ; CHECK-NEXT: [[INT1:%[0-9]+]]:_(f64) = G_INTRINSIC intrinsic(@llvm.pisa.fabs), [[DEF1]](f64)
+ ; CHECK-NEXT: [[FCMP:%[0-9]+]]:_(i1) = G_FCMP floatpred(oeq), [[INT]](f64), [[C]]
+ ; CHECK-NEXT: [[FCMP1:%[0-9]+]]:_(i1) = G_FCMP floatpred(oeq), [[INT1]](f64), [[C]]
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(f64) = G_SELECT [[FCMP1]](i1), [[DEF]], [[FMA]]
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(f64) = G_SELECT [[FCMP]](i1), [[FMA]], [[SELECT]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i64) = G_BITCAST [[SELECT1]](f64)
+ ; CHECK-NEXT: G_STORE [[BITCAST]](i64), [[C1]](p1) :: (store (i64) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ %0:_(f64) = G_IMPLICIT_DEF
+ %1:_(f64) = G_IMPLICIT_DEF
+ %2:_(f64) = G_FREM %0, %1
+ %3:_(p1) = G_CONSTANT i64 0
+ G_STORE %2:_(f64), %3:_(p1) :: (store (i64) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir
new file mode 100644
index 00000000000000..4f6217576f36de
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir
@@ -0,0 +1,35 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name: test_nonpow2_uitofp
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_nonpow2_uitofp
+ ; CHECK: [[DEF:%[0-9]+]]:_(i32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 16777215
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[DEF]], [[C]]
+ ; CHECK-NEXT: [[UITOFP:%[0-9]+]]:_(f32) = G_UITOFP [[AND]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST [[UITOFP]](f32)
+ ; CHECK-NEXT: G_STORE [[BITCAST]](i32), %2:_(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ %1:_(i24) = G_IMPLICIT_DEF
+ %2:_(f32) = G_UITOFP %1:_(i24)
+ G_STORE %2:_(f32), %3:_(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+...
+
+---
+name: test_nonpow2_sitofp
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_nonpow2_sitofp
+ ; CHECK: [[DEF:%[0-9]+]]:_(i32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 8
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i32) = G_SHL [[DEF]], [[C]](i32)
+ ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i32) = G_ASHR [[SHL]], [[C]](i32)
+ ; CHECK-NEXT: [[SITOFP:%[0-9]+]]:_(f32) = G_SITOFP [[ASHR]](i32)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(i32) = G_BITCAST [[SITOFP]](f32)
+ ; CHECK-NEXT: G_STORE [[BITCAST]](i32), %2:_(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ %1:_(i24) = G_IMPLICIT_DEF
+ %2:_(f32) = G_SITOFP %1:_(i24)
+ G_STORE %2:_(f32), %3:_(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir
new file mode 100644
index 00000000000000..6d310a0e5c7bbf
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir
@@ -0,0 +1,443 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - -verify-machineinstrs | FileCheck %s
+
+---
+name: test_s1
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s1
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC %0(i64)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i16) = G_AND [[TRUNC]], [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i16), [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C2]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C4]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i1) = G_CONSTANT i1 0
+ %1:_(i1) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i1), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s4
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s4
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC %0(i64)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i16) = G_CONSTANT i16 15
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i16) = G_AND [[TRUNC]], [[C1]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i16), [[C]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C2]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C4]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i4) = G_CONSTANT i4 0
+ %1:_(i4) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i4), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s8
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s8
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC %0(i64)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i16) = G_CONSTANT i16 255
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i16) = G_AND [[TRUNC]], [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i16), [[C1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C2]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C4]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i8) = G_CONSTANT i8 0
+ %1:_(i8) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i8), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s14
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s14
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC %0(i64)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i16) = G_CONSTANT i16 16383
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i16) = G_AND [[TRUNC]], [[C1]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i16), [[C]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C2]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C4]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i14) = G_CONSTANT i14 0
+ %1:_(i14) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i14), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s16
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s16
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i16) = G_TRUNC %0(i64)
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[TRUNC]](i16), [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C1]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C3]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i16) = G_CONSTANT i16 0
+ %1:_(i16) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i16), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s24
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s24
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i32) = G_TRUNC %0(i64)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 16777215
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[TRUNC]], [[C1]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i32), [[C]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C2]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C4]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i24) = G_CONSTANT i24 0
+ %1:_(i24) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i24), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s32
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s32
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i32) = G_TRUNC %0(i64)
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[TRUNC]](i32), [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C1]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C3]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i32) = G_CONSTANT i32 0
+ %1:_(i32) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i32), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s48
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s48
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 281474976710655
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i64) = G_AND %0, [[C1]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i64), [[C]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C2]], [[C3]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C4]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i48) = G_CONSTANT i48 0
+ %1:_(i48) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i48), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s62
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s62
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i64) = G_CONSTANT i64 4611686018427387903
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i64) = G_AND %0, [[C2]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i64), [[C]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i16) = G_SELECT [[ICMP]](i1), [[C3]], [[C4]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i8) = G_TRUNC [[SELECT]](i16)
+ ; CHECK-NEXT: G_STORE [[TRUNC]](i8), [[C1]](p1) :: (store (i8) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i62) = G_CONSTANT i62 0
+ %5:_(p1) = G_CONSTANT i64 0
+ %1:_(i62) = G_TRUNC %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i62), %2
+ %4:_(i8) = G_ZEXT %3(i1)
+ G_STORE %4(i8), %5(p1) :: (store (i8) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+
+---
+name: test_s64
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s64
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), %0(i64), [[C]]
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i32) = G_SELECT [[ICMP]](i1), [[C1]], [[C2]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT]](i32), [[C3]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i64) = G_CONSTANT i64 0
+ %3:_(i1) = G_ICMP intpred(ugt), %0(i64), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s80
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s80
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i64) = G_ASHR %0, [[C2]](i32)
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i64) = G_CONSTANT i64 65535
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i64) = G_AND [[ASHR]], [[C3]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), %0(i64), [[C]]
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i64), [[C1]]
+ ; CHECK-NEXT: [[ICMP2:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[AND]](i64), [[C1]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i16) = G_SELECT [[ICMP]](i1), [[C5]], [[C4]]
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C7:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(i16) = G_SELECT [[ICMP1]](i1), [[C7]], [[C6]]
+ ; CHECK-NEXT: [[SELECT2:%[0-9]+]]:_(i16) = G_SELECT [[ICMP2]](i1), [[SELECT]], [[SELECT1]]
+ ; CHECK-NEXT: [[C8:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C9:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK-NEXT: [[AND1:%[0-9]+]]:_(i16) = G_AND [[SELECT2]], [[C9]]
+ ; CHECK-NEXT: [[ICMP3:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND1]](i16), [[C8]]
+ ; CHECK-NEXT: [[C10:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C11:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT3:%[0-9]+]]:_(i32) = G_SELECT [[ICMP3]](i1), [[C10]], [[C11]]
+ ; CHECK-NEXT: [[C12:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT3]](i32), [[C12]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i80) = G_CONSTANT i80 0
+ %1:_(i80) = G_SEXT %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i80), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s128
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s128
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i64) = G_ASHR %0, [[C2]](i32)
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), %0(i64), [[C]]
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[ASHR]](i64), [[C1]]
+ ; CHECK-NEXT: [[ICMP2:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[ASHR]](i64), [[C1]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i16) = G_SELECT [[ICMP]](i1), [[C4]], [[C3]]
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(i16) = G_SELECT [[ICMP1]](i1), [[C6]], [[C5]]
+ ; CHECK-NEXT: [[SELECT2:%[0-9]+]]:_(i16) = G_SELECT [[ICMP2]](i1), [[SELECT]], [[SELECT1]]
+ ; CHECK-NEXT: [[C7:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C8:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i16) = G_AND [[SELECT2]], [[C8]]
+ ; CHECK-NEXT: [[ICMP3:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND]](i16), [[C7]]
+ ; CHECK-NEXT: [[C9:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C10:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT3:%[0-9]+]]:_(i32) = G_SELECT [[ICMP3]](i1), [[C9]], [[C10]]
+ ; CHECK-NEXT: [[C11:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT3]](i32), [[C11]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i128) = G_CONSTANT i128 0
+ %1:_(i128) = G_SEXT %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i128), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
+
+---
+name: test_s160
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_s160
+ ; CHECK: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i64) = G_ASHR %0, [[C4]](i32)
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i64) = G_CONSTANT i64 -1
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i64) = G_CONSTANT i64 4294967295
+ ; CHECK-NEXT: [[C7:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i64) = G_AND [[ASHR]], [[C5]]
+ ; CHECK-NEXT: [[AND1:%[0-9]+]]:_(i64) = G_AND [[ASHR]], [[C6]]
+ ; CHECK-NEXT: [[AND2:%[0-9]+]]:_(i64) = G_AND [[ASHR]], [[C7]]
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), %0(i64), [[C]]
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND]](i64), [[C1]]
+ ; CHECK-NEXT: [[ICMP2:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[AND]](i64), [[C1]]
+ ; CHECK-NEXT: [[C8:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C9:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i16) = G_SELECT [[ICMP]](i1), [[C9]], [[C8]]
+ ; CHECK-NEXT: [[C10:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C11:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(i16) = G_SELECT [[ICMP1]](i1), [[C11]], [[C10]]
+ ; CHECK-NEXT: [[SELECT2:%[0-9]+]]:_(i16) = G_SELECT [[ICMP2]](i1), [[SELECT]], [[SELECT1]]
+ ; CHECK-NEXT: [[ICMP3:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND1]](i64), [[C2]]
+ ; CHECK-NEXT: [[ICMP4:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[AND1]](i64), [[C2]]
+ ; CHECK-NEXT: [[C12:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C13:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT3:%[0-9]+]]:_(i16) = G_SELECT [[ICMP3]](i1), [[C13]], [[C12]]
+ ; CHECK-NEXT: [[SELECT4:%[0-9]+]]:_(i16) = G_SELECT [[ICMP4]](i1), [[SELECT2]], [[SELECT3]]
+ ; CHECK-NEXT: [[ICMP5:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[AND2]](i64), [[C3]]
+ ; CHECK-NEXT: [[ICMP6:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[AND2]](i64), [[C3]]
+ ; CHECK-NEXT: [[C14:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C15:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT5:%[0-9]+]]:_(i16) = G_SELECT [[ICMP5]](i1), [[C15]], [[C14]]
+ ; CHECK-NEXT: [[SELECT6:%[0-9]+]]:_(i16) = G_SELECT [[ICMP6]](i1), [[SELECT4]], [[SELECT5]]
+ ; CHECK-NEXT: [[C16:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C17:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK-NEXT: [[AND3:%[0-9]+]]:_(i16) = G_AND [[SELECT6]], [[C17]]
+ ; CHECK-NEXT: [[ICMP7:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND3]](i16), [[C16]]
+ ; CHECK-NEXT: [[C18:%[0-9]+]]:_(i32) = G_CONSTANT i32 13
+ ; CHECK-NEXT: [[C19:%[0-9]+]]:_(i32) = G_CONSTANT i32 17
+ ; CHECK-NEXT: [[SELECT7:%[0-9]+]]:_(i32) = G_SELECT [[ICMP7]](i1), [[C18]], [[C19]]
+ ; CHECK-NEXT: [[C20:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: G_STORE [[SELECT7]](i32), [[C20]](p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ; CHECK-NEXT: ret
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %2:_(i160) = G_CONSTANT i160 0
+ %1:_(i160) = G_SEXT %0(i64)
+ %3:_(i1) = G_ICMP intpred(ugt), %1(i160), %2
+ %6:_(i32) = G_CONSTANT i32 13
+ %7:_(i32) = G_CONSTANT i32 17
+ %8:_(i32) = G_SELECT %3, %6, %7
+ %5:_(p1) = G_CONSTANT i64 0
+ G_STORE %8(i32), %5(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir
new file mode 100644
index 00000000000000..5b7bb46ca33086
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir
@@ -0,0 +1,367 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+
+---
+name: test_implicit_def_s1
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_implicit_def_s1
+ ; CHECK: [[DEF:%[0-9]+]]:_(i32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32b_0 = COPY [[DEF]](i32)
+ %0:_(i1) = G_IMPLICIT_DEF
+ %1:_(i32) = G_ANYEXT %0
+ $reg32b_0 = COPY %1
+...
+
+---
+name: test_implicit_def_s5
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s5
+ ; CHECK: [[DEF:%[0-9]+]]:_(i16) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg16b_0 = COPY [[DEF]](i16)
+ %0:_(i5) = G_IMPLICIT_DEF
+ %1:_(i16) = G_ANYEXT %0
+ $reg16b_0 = COPY %1
+...
+
+---
+name: test_implicit_def_s8
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s8
+ ; CHECK: [[DEF:%[0-9]+]]:_(i16) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg16b_0 = COPY [[DEF]](i16)
+ %0:_(i8) = G_IMPLICIT_DEF
+ %1:_(i16) = G_ANYEXT %0
+ $reg16b_0 = COPY %1
+...
+
+---
+name: test_implicit_def_s16
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s16
+ ; CHECK: [[DEF:%[0-9]+]]:_(i16) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg16b_0 = COPY [[DEF]](i16)
+ %0:_(i16) = G_IMPLICIT_DEF
+ $reg16b_0 = COPY %0
+...
+
+---
+name: test_implicit_def_s24
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s24
+ ; CHECK: [[DEF:%[0-9]+]]:_(i32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32b_0 = COPY [[DEF]](i32)
+ %0:_(i24) = G_IMPLICIT_DEF
+ %1:_(i32) = G_ANYEXT %0
+ $reg32b_0 = COPY %1
+...
+
+---
+name: test_implicit_def_s32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s32
+ ; CHECK: [[DEF:%[0-9]+]]:_(i32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32b_0 = COPY [[DEF]](i32)
+ %0:_(i32) = G_IMPLICIT_DEF
+ $reg32b_0 = COPY %0
+...
+
+---
+name: test_implicit_def_s40
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s40
+ ; CHECK: [[DEF:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg64b_0 = COPY [[DEF]](i64)
+ %0:_(i40) = G_IMPLICIT_DEF
+ %1:_(i64) = G_ANYEXT %0
+ $reg64b_0 = COPY %1
+...
+
+---
+name: test_implicit_def_s64
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s64
+ ; CHECK: [[DEF:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg64b_0 = COPY [[DEF]](i64)
+ %0:_(i64) = G_IMPLICIT_DEF
+ $reg64b_0 = COPY %0
+...
+
+---
+name: test_implicit_def_s72
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s72
+ ; CHECK: [[DEF:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i64) = COPY [[DEF]](i64)
+ ; CHECK-NEXT: $reg64b_0 = COPY [[COPY]](i64)
+ %0:_(i72) = G_IMPLICIT_DEF
+ %1:_(i64) = G_TRUNC %0
+ $reg64b_0 = COPY %1
+...
+
+---
+name: test_implicit_def_s96
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s96
+ ; CHECK: [[DEF:%[0-9]+]]:_(i128) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i96) = G_TRUNC [[DEF]](i128)
+ ; CHECK-NEXT: $reg32bx3 = COPY [[TRUNC]](i96)
+ %0:_(i96) = G_IMPLICIT_DEF
+ $reg32bx3 = COPY %0
+...
+
+---
+name: test_implicit_def_s128
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s128
+ ; CHECK: [[DEF:%[0-9]+]]:_(i128) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg64bx2 = COPY [[DEF]](i128)
+ %0:_(i128) = G_IMPLICIT_DEF
+ $reg64bx2 = COPY %0
+...
+
+---
+name: test_implicit_def_s160
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s160
+ ; CHECK: [[DEF:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF2:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF3:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(i256) = G_MERGE_VALUES [[DEF]](i64), [[DEF1]](i64), [[DEF2]](i64), [[DEF3]](i64)
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i160) = G_TRUNC [[MV]](i256)
+ ; CHECK-NEXT: $reg32bx5 = COPY [[TRUNC]](i160)
+ %0:_(i160) = G_IMPLICIT_DEF
+ $reg32bx5 = COPY %0
+...
+
+---
+name: test_implicit_def_s256
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_s256
+ ; CHECK: [[DEF:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF2:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF3:%[0-9]+]]:_(i64) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(i256) = G_MERGE_VALUES [[DEF]](i64), [[DEF1]](i64), [[DEF2]](i64), [[DEF3]](i64)
+ ; CHECK-NEXT: $reg64bx4 = COPY [[MV]](i256)
+ %0:_(i256) = G_IMPLICIT_DEF
+ $reg64bx4 = COPY %0
+...
+
+---
+name: test_implicit_def_v2i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v2i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx2 = COPY [[DEF]](<2 x i32>)
+ %0:_(<2 x i32>) = G_IMPLICIT_DEF
+ $reg32bx2 = COPY %0
+...
+
+
+---
+name: test_implicit_def_v3i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v3i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<3 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx3 = COPY [[DEF]](<3 x i32>)
+ %0:_(<3 x i32>) = G_IMPLICIT_DEF
+ $reg32bx3 = COPY %0
+...
+
+---
+name: test_implicit_def_v4i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v4i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<4 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx4 = COPY [[DEF]](<4 x i32>)
+ %0:_(<4 x i32>) = G_IMPLICIT_DEF
+ $reg32bx4 = COPY %0
+...
+
+---
+name: test_implicit_def_v5i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v5i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<5 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx5 = COPY [[DEF]](<5 x i32>)
+ %0:_(<5 x i32>) = G_IMPLICIT_DEF
+ $reg32bx5 = COPY %0
+...
+
+---
+name: test_implicit_def_v6i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v6i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<6 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx6 = COPY [[DEF]](<6 x i32>)
+ %0:_(<6 x i32>) = G_IMPLICIT_DEF
+ $reg32bx6 = COPY %0
+...
+
+---
+name: test_implicit_def_v7i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v7i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<7 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx7 = COPY [[DEF]](<7 x i32>)
+ %0:_(<7 x i32>) = G_IMPLICIT_DEF
+ $reg32bx7 = COPY %0
+...
+
+---
+name: test_implicit_def_v8i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v8i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<8 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx8 = COPY [[DEF]](<8 x i32>)
+ %0:_(<8 x i32>) = G_IMPLICIT_DEF
+ $reg32bx8 = COPY %0
+...
+
+---
+name: test_implicit_def_v16i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v16i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<16 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx16 = COPY [[DEF]](<16 x i32>)
+ %0:_(<16 x i32>) = G_IMPLICIT_DEF
+ $reg32bx16 = COPY %0
+...
+
+---
+name: test_implicit_def_v32i32
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v32i32
+ ; CHECK: [[DEF:%[0-9]+]]:_(<32 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx32 = COPY [[DEF]](<32 x i32>)
+ %0:_(<32 x i32>) = G_IMPLICIT_DEF
+ $reg32bx32 = COPY %0
+...
+
+---
+name: test_implicit_def_v2i1
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v2i1
+ ; CHECK: [[DEF:%[0-9]+]]:_(i32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(i32) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<2 x i32>) = G_BUILD_VECTOR [[DEF]](i32), [[DEF1]](i32)
+ ; CHECK-NEXT: $reg32bx2 = COPY [[BUILD_VECTOR]](<2 x i32>)
+ %0:_(<2 x i1>) = G_IMPLICIT_DEF
+ %1:_(<2 x i32>) = G_ANYEXT %0
+ $reg32bx2 = COPY %1
+...
+
+---
+name: test_implicit_def_v2i8
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v2i8
+ ; CHECK: [[DEF:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32bx2 = COPY [[DEF]](<2 x i32>)
+ %0:_(<2 x i8>) = G_IMPLICIT_DEF
+ %1:_(<2 x i32>) = G_ANYEXT %0
+ $reg32bx2 = COPY %1
+...
+
+---
+name: test_implicit_def_v2i16
+body: |
+ bb.0:
+
+ ; CHECK-LABEL: name: test_implicit_def_v2i16
+ ; CHECK: [[DEF:%[0-9]+]]:_(<2 x i16>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32b_0 = COPY [[DEF]](<2 x i16>)
+ %0:_(<2 x i16>) = G_IMPLICIT_DEF
+ $reg32b_0 = COPY %0
+...
+
+---
+name: test_implicit_def_p4
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_implicit_def_p4
+ ; CHECK: [[DEF:%[0-9]+]]:_(p4) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32b_0 = COPY [[DEF]](p4)
+ %0:_(p4) = G_IMPLICIT_DEF
+ $reg32b_0 = COPY %0
+...
+
+---
+name: test_implicit_def_p1
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_implicit_def_p1
+ ; CHECK: [[DEF:%[0-9]+]]:_(p1) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg64b_0 = COPY [[DEF]](p1)
+ %0:_(p1) = G_IMPLICIT_DEF
+ $reg64b_0 = COPY %0
+...
+
+---
+name: test_implicit_def_p3
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_implicit_def_p3
+ ; CHECK: [[DEF:%[0-9]+]]:_(p3) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg32b_0 = COPY [[DEF]](p3)
+ %0:_(p3) = G_IMPLICIT_DEF
+ $reg32b_0 = COPY %0
+...
+
+---
+name: test_implicit_def_p0
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_implicit_def_p0
+ ; CHECK: [[DEF:%[0-9]+]]:_(p0) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: $reg64b_0 = COPY [[DEF]](p0)
+ %0:_(p0) = G_IMPLICIT_DEF
+ $reg64b_0 = COPY %0
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir
new file mode 100644
index 00000000000000..c25457854eda1a
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir
@@ -0,0 +1,16 @@
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+
+---
+name: insert_subvector_s64
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: insert_subvector_s64
+ ; CHECK: [[SUBVECTOR_S32:%[0-9]+]]:_(<4 x i32>) = G_BITCAST %3(<2 x i64>)
+ ; CHECK: [[VECTOR_S32:%[0-9]+]]:_(<32 x i32>) = G_INSERT_SUBVECTOR {{%[0-9]+}}, [[SUBVECTOR_S32]](<4 x i32>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(i64) = G_CONSTANT i64 1
+ %2:_(<16 x i64>) = G_BUILD_VECTOR %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64), %1:_(i64)
+ %3:_(<2 x i64>) = G_BUILD_VECTOR %1:_(i64), %1:_(i64)
+ %4:_(<16 x i64>) = G_INSERT_SUBVECTOR %2:_(<16 x i64>), %3:_(<2 x i64>), 8
+ G_STORE %4:_(<16 x i64>), %0:_(p1) :: (store (<16 x i64>))
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-non-power-of-2-overfetch.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-non-power-of-2-overfetch.mir
new file mode 100644
index 00000000000000..67fda242698417
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-non-power-of-2-overfetch.mir
@@ -0,0 +1,388 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
+# Check that loads/stores of vectors of <n x i32> elements with n
+# being [5,7] elements is resolved to a load of <8 x i32> for smaller
+# alignments. However, when alignment is > 8 bytes for slm and constant
+# buffer, and > 32 bytes for global memory, then [5, 6] elements
+# is legalized to <3 x i64> to minimize overfetch. The original
+# <n x i32> result is then extracted from this new result.
+--- |
+ target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-G1"
+
+ define <5 x i32> @load_shared_5xi32(ptr addrspace(3) %arg) {
+ %1 = load <5 x i32>, ptr addrspace(3) %arg, align 4
+ ret <5 x i32> %1
+ }
+
+ define <5 x i32> @load_shared_5xi32_8bytes_alignment(ptr addrspace(3) %arg) {
+ %1 = load <5 x i32>, ptr addrspace(3) %arg, align 8
+ ret <5 x i32> %1
+ }
+
+ define <6 x i32> @load_shared_6xi32(ptr addrspace(3) %arg) {
+ %1 = load <6 x i32>, ptr addrspace(3) %arg, align 4
+ ret <6 x i32> %1
+ }
+
+ define <6 x i32> @load_shared_6xi32_8bytes_alignment(ptr addrspace(3) %arg) {
+ %1 = load <6 x i32>, ptr addrspace(3) %arg, align 8
+ ret <6 x i32> %1
+ }
+
+ define <7 x i32> @load_shared_7xi32(ptr addrspace(3) %arg) {
+ %1 = load <7 x i32>, ptr addrspace(3) %arg, align 8
+ ret <7 x i32> %1
+ }
+
+ define <5 x i32> @load_global_5xi32(ptr addrspace(1) %arg) {
+ %1 = load <5 x i32>, ptr addrspace(1) %arg, align 4
+ ret <5 x i32> %1
+ }
+
+ define <5 x i32> @load_global_5xi32_32bytes_alignment(ptr addrspace(1) %arg) {
+ %1 = load <5 x i32>, ptr addrspace(1) %arg, align 32
+ ret <5 x i32> %1
+ }
+
+ define <6 x i32> @load_global_6xi32(ptr addrspace(1) %arg) {
+ %1 = load <6 x i32>, ptr addrspace(1) %arg, align 4
+ ret <6 x i32> %1
+ }
+
+ define <6 x i32> @load_global_6xi32_32bytes_alignment(ptr addrspace(1) %arg) {
+ %1 = load <6 x i32>, ptr addrspace(1) %arg, align 32
+ ret <6 x i32> %1
+ }
+
+ define <7 x i32> @load_global_7xi32(ptr addrspace(1) %arg) {
+ %1 = load <7 x i32>, ptr addrspace(1) %arg, align 32
+ ret <7 x i32> %1
+ }
+
+ define <5 x i32> @load_constant_5xi32(ptr addrspace(2) %arg) {
+ %1 = load <5 x i32>, ptr addrspace(2) %arg, align 4
+ ret <5 x i32> %1
+ }
+
+ define <5 x i32> @load_constant_5xi32_8bytes_alignment(ptr addrspace(2) %arg) {
+ %1 = load <5 x i32>, ptr addrspace(2) %arg, align 8
+ ret <5 x i32> %1
+ }
+
+ define <6 x i32> @load_constant_6xi32(ptr addrspace(2) %arg) {
+ %1 = load <6 x i32>, ptr addrspace(2) %arg, align 4
+ ret <6 x i32> %1
+ }
+
+ define <6 x i32> @load_constant_6xi32_8bytes_alignment(ptr addrspace(2) %arg) {
+ %1 = load <6 x i32>, ptr addrspace(2) %arg, align 8
+ ret <6 x i32> %1
+ }
+
+ define <7 x i32> @load_constant_7xi32(ptr addrspace(2) %arg) {
+ %1 = load <7 x i32>, ptr addrspace(2) %arg, align 8
+ ret <7 x i32> %1
+ }
+...
+---
+name: load_shared_5xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_shared_5xi32
+ ; LEGAL: %0:reg32b(p3) = functionParameter_i32 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<8 x i32>) = G_LOAD %0(p3) :: (load (<8 x i32>) from %ir.arg, align 4, addrspace 3)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<5 x i32>) = G_EXTRACT_SUBVECTOR [[LOAD]](<8 x i32>), 0
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<5 x i32>) = COPY [[EXTRACT_SUBVECTOR]](<5 x i32>)
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:regv5_32b(<5 x i32>) = COPY [[COPY]](<5 x i32>)
+ ; LEGAL-NEXT: retValue_v5i32_r [[COPY1]](<5 x i32>)
+ %0:reg32b(p3) = functionParameter_i32 0
+ %1:_(<5 x i32>) = G_LOAD %0(p3) :: (load (<5 x i32>) from %ir.arg, align 4, addrspace 3)
+ %2:regv5_32b(<5 x i32>) = COPY %1(<5 x i32>)
+ retValue_v5i32_r %2(<5 x i32>)
+...
+---
+name: load_shared_5xi32_8bytes_alignment
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_shared_5xi32_8bytes_alignment
+ ; LEGAL: %0:reg32b(p3) = functionParameter_i32 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<3 x i64>) = G_LOAD %0(p3) :: (load (<3 x i64>) from %ir.arg, align 8, addrspace 3)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i64), [[UV1:%[0-9]+]]:_(i64), [[UV2:%[0-9]+]]:_(i64) = G_UNMERGE_VALUES [[LOAD]](<3 x i64>)
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV]](i64)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV1]](i64)
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV2]](i64)
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(<6 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[DEF]], [[BITCAST]](<2 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<2 x i32>), 2
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR2:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR1]], [[BITCAST2]](<2 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<6 x i32>) = COPY [[INSERT_SUBVECTOR2]](<6 x i32>)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<5 x i32>) = G_EXTRACT_SUBVECTOR [[COPY]](<6 x i32>), 0
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:_(<5 x i32>) = COPY [[EXTRACT_SUBVECTOR]](<5 x i32>)
+ ; LEGAL-NEXT: [[COPY2:%[0-9]+]]:regv5_32b(<5 x i32>) = COPY [[COPY1]](<5 x i32>)
+ ; LEGAL-NEXT: retValue_v5i32_r [[COPY2]](<5 x i32>)
+ %0:reg32b(p3) = functionParameter_i32 0
+ %1:_(<5 x i32>) = G_LOAD %0(p3) :: (load (<5 x i32>) from %ir.arg, align 8, addrspace 3)
+ %2:regv5_32b(<5 x i32>) = COPY %1(<5 x i32>)
+ retValue_v5i32_r %2(<5 x i32>)
+...
+---
+name: load_shared_6xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_shared_6xi32
+ ; LEGAL: %0:reg32b(p3) = functionParameter_i32 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<8 x i32>) = G_LOAD %0(p3) :: (load (<8 x i32>) from %ir.arg, align 4, addrspace 3)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<6 x i32>) = G_EXTRACT_SUBVECTOR [[LOAD]](<8 x i32>), 0
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<6 x i32>) = COPY [[EXTRACT_SUBVECTOR]](<6 x i32>)
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:regv6_32b(<6 x i32>) = COPY [[COPY]](<6 x i32>)
+ ; LEGAL-NEXT: retValue_v6i32_r [[COPY1]](<6 x i32>)
+ %0:reg32b(p3) = functionParameter_i32 0
+ %1:_(<6 x i32>) = G_LOAD %0(p3) :: (load (<6 x i32>) from %ir.arg, align 4, addrspace 3)
+ %2:regv6_32b(<6 x i32>) = COPY %1(<6 x i32>)
+ retValue_v6i32_r %2(<6 x i32>)
+...
+---
+name: load_shared_6xi32_8bytes_alignment
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_shared_6xi32_8bytes_alignment
+ ; LEGAL: %0:reg32b(p3) = functionParameter_i32 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<3 x i64>) = G_LOAD %0(p3) :: (load (<3 x i64>) from %ir.arg, align 8, addrspace 3)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i64), [[UV1:%[0-9]+]]:_(i64), [[UV2:%[0-9]+]]:_(i64) = G_UNMERGE_VALUES [[LOAD]](<3 x i64>)
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV]](i64)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV1]](i64)
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV2]](i64)
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(<6 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[DEF]], [[BITCAST]](<2 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<2 x i32>), 2
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR2:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR1]], [[BITCAST2]](<2 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<6 x i32>) = COPY [[INSERT_SUBVECTOR2]](<6 x i32>)
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:regv6_32b(<6 x i32>) = COPY [[COPY]](<6 x i32>)
+ ; LEGAL-NEXT: retValue_v6i32_r [[COPY1]](<6 x i32>)
+ %0:reg32b(p3) = functionParameter_i32 0
+ %1:_(<6 x i32>) = G_LOAD %0(p3) :: (load (<6 x i32>) from %ir.arg, align 8, addrspace 3)
+ %2:regv6_32b(<6 x i32>) = COPY %1(<6 x i32>)
+ retValue_v6i32_r %2(<6 x i32>)
+...
+---
+name: load_shared_7xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_shared_7xi32
+ ; LEGAL: %0:reg32b(p3) = functionParameter_i32 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<8 x i32>) = G_LOAD %0(p3) :: (load (<8 x i32>) from %ir.arg, align 8, addrspace 3)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<7 x i32>) = G_EXTRACT_SUBVECTOR [[LOAD]](<8 x i32>), 0
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<7 x i32>) = COPY [[EXTRACT_SUBVECTOR]](<7 x i32>)
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:regv7_32b(<7 x i32>) = COPY [[COPY]](<7 x i32>)
+ ; LEGAL-NEXT: retValue_v7i32_r [[COPY1]](<7 x i32>)
+ %0:reg32b(p3) = functionParameter_i32 0
+ %1:_(<7 x i32>) = G_LOAD %0(p3) :: (load (<7 x i32>) from %ir.arg, align 8, addrspace 3)
+ %2:regv7_32b(<7 x i32>) = COPY %1(<7 x i32>)
+ retValue_v7i32_r %2(<7 x i32>)
+...
+---
+name: load_global_5xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_global_5xi32
+ ; LEGAL: %0:reg64b(p1) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<4 x i32>) = G_LOAD %0(p1) :: (load (<4 x i32>) from %ir.arg, align 4, addrspace 1)
+ ; LEGAL-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 16
+ ; LEGAL-NEXT: [[PTR_ADD:%[0-9]+]]:_(p1) = nuw inbounds G_PTR_ADD %0, [[C]](i64)
+ ; LEGAL-NEXT: [[LOAD1:%[0-9]+]]:_(i32) = G_LOAD [[PTR_ADD]](p1) :: (load (i32) from %ir.arg + 16, addrspace 1)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i32), [[UV1:%[0-9]+]]:_(i32), [[UV2:%[0-9]+]]:_(i32), [[UV3:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[LOAD]](<4 x i32>)
+ ; LEGAL-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<5 x i32>) = G_BUILD_VECTOR [[UV]](i32), [[UV1]](i32), [[UV2]](i32), [[UV3]](i32), [[LOAD1]](i32)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv5_32b(<5 x i32>) = COPY [[BUILD_VECTOR]](<5 x i32>)
+ ; LEGAL-NEXT: retValue_v5i32_r [[COPY]](<5 x i32>)
+ %0:reg64b(p1) = functionParameter_i64 0
+ %1:_(<5 x i32>) = G_LOAD %0(p1) :: (load (<5 x i32>) from %ir.arg, align 4, addrspace 1)
+ %2:regv5_32b(<5 x i32>) = COPY %1(<5 x i32>)
+ retValue_v5i32_r %2(<5 x i32>)
+...
+---
+name: load_global_5xi32_32bytes_alignment
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_global_5xi32_32bytes_alignment
+ ; LEGAL: %0:reg64b(p1) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<3 x i64>) = G_LOAD %0(p1) :: (load (<3 x i64>) from %ir.arg, align 32, addrspace 1)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i64), [[UV1:%[0-9]+]]:_(i64), [[UV2:%[0-9]+]]:_(i64) = G_UNMERGE_VALUES [[LOAD]](<3 x i64>)
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV]](i64)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV1]](i64)
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV2]](i64)
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(<6 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[DEF]], [[BITCAST]](<2 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<2 x i32>), 2
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR2:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR1]], [[BITCAST2]](<2 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<6 x i32>) = COPY [[INSERT_SUBVECTOR2]](<6 x i32>)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<5 x i32>) = G_EXTRACT_SUBVECTOR [[COPY]](<6 x i32>), 0
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:_(<5 x i32>) = COPY [[EXTRACT_SUBVECTOR]](<5 x i32>)
+ ; LEGAL-NEXT: [[COPY2:%[0-9]+]]:regv5_32b(<5 x i32>) = COPY [[COPY1]](<5 x i32>)
+ ; LEGAL-NEXT: retValue_v5i32_r [[COPY2]](<5 x i32>)
+ %0:reg64b(p1) = functionParameter_i64 0
+ %1:_(<5 x i32>) = G_LOAD %0(p1) :: (load (<5 x i32>) from %ir.arg, align 32, addrspace 1)
+ %2:regv5_32b(<5 x i32>) = COPY %1(<5 x i32>)
+ retValue_v5i32_r %2(<5 x i32>)
+...
+---
+name: load_global_6xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_global_6xi32
+ ; LEGAL: %0:reg64b(p1) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<4 x i32>) = G_LOAD %0(p1) :: (load (<4 x i32>) from %ir.arg, align 4, addrspace 1)
+ ; LEGAL-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 16
+ ; LEGAL-NEXT: [[PTR_ADD:%[0-9]+]]:_(p1) = nuw inbounds G_PTR_ADD %0, [[C]](i64)
+ ; LEGAL-NEXT: [[LOAD1:%[0-9]+]]:_(<2 x i32>) = G_LOAD [[PTR_ADD]](p1) :: (load (<2 x i32>) from %ir.arg + 16, align 4, addrspace 1)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i32), [[UV1:%[0-9]+]]:_(i32), [[UV2:%[0-9]+]]:_(i32), [[UV3:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[LOAD]](<4 x i32>)
+ ; LEGAL-NEXT: [[UV4:%[0-9]+]]:_(i32), [[UV5:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[LOAD1]](<2 x i32>)
+ ; LEGAL-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<6 x i32>) = G_BUILD_VECTOR [[UV]](i32), [[UV1]](i32), [[UV2]](i32), [[UV3]](i32), [[UV4]](i32), [[UV5]](i32)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv6_32b(<6 x i32>) = COPY [[BUILD_VECTOR]](<6 x i32>)
+ ; LEGAL-NEXT: retValue_v6i32_r [[COPY]](<6 x i32>)
+ %0:reg64b(p1) = functionParameter_i64 0
+ %1:_(<6 x i32>) = G_LOAD %0(p1) :: (load (<6 x i32>) from %ir.arg, align 4, addrspace 1)
+ %2:regv6_32b(<6 x i32>) = COPY %1(<6 x i32>)
+ retValue_v6i32_r %2(<6 x i32>)
+...
+---
+name: load_global_6xi32_32bytes_alignment
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_global_6xi32_32bytes_alignment
+ ; LEGAL: %0:reg64b(p1) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<3 x i64>) = G_LOAD %0(p1) :: (load (<3 x i64>) from %ir.arg, align 32, addrspace 1)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i64), [[UV1:%[0-9]+]]:_(i64), [[UV2:%[0-9]+]]:_(i64) = G_UNMERGE_VALUES [[LOAD]](<3 x i64>)
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV]](i64)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV1]](i64)
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV2]](i64)
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(<6 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[DEF]], [[BITCAST]](<2 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<2 x i32>), 2
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR2:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR1]], [[BITCAST2]](<2 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<6 x i32>) = COPY [[INSERT_SUBVECTOR2]](<6 x i32>)
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:regv6_32b(<6 x i32>) = COPY [[COPY]](<6 x i32>)
+ ; LEGAL-NEXT: retValue_v6i32_r [[COPY1]](<6 x i32>)
+ %0:reg64b(p1) = functionParameter_i64 0
+ %1:_(<6 x i32>) = G_LOAD %0(p1) :: (load (<6 x i32>) from %ir.arg, align 32, addrspace 1)
+ %2:regv6_32b(<6 x i32>) = COPY %1(<6 x i32>)
+ retValue_v6i32_r %2(<6 x i32>)
+...
+---
+name: load_global_7xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_global_7xi32
+ ; LEGAL: %0:reg64b(p1) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<8 x i32>) = G_LOAD %0(p1) :: (load (<8 x i32>) from %ir.arg, addrspace 1)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<7 x i32>) = G_EXTRACT_SUBVECTOR [[LOAD]](<8 x i32>), 0
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<7 x i32>) = COPY [[EXTRACT_SUBVECTOR]](<7 x i32>)
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:regv7_32b(<7 x i32>) = COPY [[COPY]](<7 x i32>)
+ ; LEGAL-NEXT: retValue_v7i32_r [[COPY1]](<7 x i32>)
+ %0:reg64b(p1) = functionParameter_i64 0
+ %1:_(<7 x i32>) = G_LOAD %0(p1) :: (load (<7 x i32>) from %ir.arg, align 32, addrspace 1)
+ %2:regv7_32b(<7 x i32>) = COPY %1(<7 x i32>)
+ retValue_v7i32_r %2(<7 x i32>)
+...
+---
+name: load_constant_5xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_constant_5xi32
+ ; LEGAL: %0:reg64b(p2) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<4 x i32>) = G_LOAD %0(p2) :: (load (<4 x i32>) from %ir.arg, align 4, addrspace 2)
+ ; LEGAL-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 16
+ ; LEGAL-NEXT: [[PTR_ADD:%[0-9]+]]:_(p2) = nuw inbounds G_PTR_ADD %0, [[C]](i64)
+ ; LEGAL-NEXT: [[LOAD1:%[0-9]+]]:_(i32) = G_LOAD [[PTR_ADD]](p2) :: (load (i32) from %ir.arg + 16, addrspace 2)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i32), [[UV1:%[0-9]+]]:_(i32), [[UV2:%[0-9]+]]:_(i32), [[UV3:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[LOAD]](<4 x i32>)
+ ; LEGAL-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<5 x i32>) = G_BUILD_VECTOR [[UV]](i32), [[UV1]](i32), [[UV2]](i32), [[UV3]](i32), [[LOAD1]](i32)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv5_32b(<5 x i32>) = COPY [[BUILD_VECTOR]](<5 x i32>)
+ ; LEGAL-NEXT: retValue_v5i32_r [[COPY]](<5 x i32>)
+ %0:reg64b(p2) = functionParameter_i64 0
+ %1:_(<5 x i32>) = G_LOAD %0(p2) :: (load (<5 x i32>) from %ir.arg, align 4, addrspace 2)
+ %2:regv5_32b(<5 x i32>) = COPY %1(<5 x i32>)
+ retValue_v5i32_r %2(<5 x i32>)
+...
+---
+name: load_constant_5xi32_8bytes_alignment
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_constant_5xi32_8bytes_alignment
+ ; LEGAL: %0:reg64b(p2) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<3 x i64>) = G_LOAD %0(p2) :: (load (<3 x i64>) from %ir.arg, align 8, addrspace 2)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i64), [[UV1:%[0-9]+]]:_(i64), [[UV2:%[0-9]+]]:_(i64) = G_UNMERGE_VALUES [[LOAD]](<3 x i64>)
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV]](i64)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV1]](i64)
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV2]](i64)
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(<6 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[DEF]], [[BITCAST]](<2 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<2 x i32>), 2
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR2:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR1]], [[BITCAST2]](<2 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<6 x i32>) = COPY [[INSERT_SUBVECTOR2]](<6 x i32>)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<5 x i32>) = G_EXTRACT_SUBVECTOR [[COPY]](<6 x i32>), 0
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:_(<5 x i32>) = COPY [[EXTRACT_SUBVECTOR]](<5 x i32>)
+ ; LEGAL-NEXT: [[COPY2:%[0-9]+]]:regv5_32b(<5 x i32>) = COPY [[COPY1]](<5 x i32>)
+ ; LEGAL-NEXT: retValue_v5i32_r [[COPY2]](<5 x i32>)
+ %0:reg64b(p2) = functionParameter_i64 0
+ %1:_(<5 x i32>) = G_LOAD %0(p2) :: (load (<5 x i32>) from %ir.arg, align 8, addrspace 2)
+ %2:regv5_32b(<5 x i32>) = COPY %1(<5 x i32>)
+ retValue_v5i32_r %2(<5 x i32>)
+...
+---
+name: load_constant_6xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_constant_6xi32
+ ; LEGAL: %0:reg64b(p2) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<4 x i32>) = G_LOAD %0(p2) :: (load (<4 x i32>) from %ir.arg, align 4, addrspace 2)
+ ; LEGAL-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 16
+ ; LEGAL-NEXT: [[PTR_ADD:%[0-9]+]]:_(p2) = nuw inbounds G_PTR_ADD %0, [[C]](i64)
+ ; LEGAL-NEXT: [[LOAD1:%[0-9]+]]:_(<2 x i32>) = G_LOAD [[PTR_ADD]](p2) :: (load (<2 x i32>) from %ir.arg + 16, align 4, addrspace 2)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i32), [[UV1:%[0-9]+]]:_(i32), [[UV2:%[0-9]+]]:_(i32), [[UV3:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[LOAD]](<4 x i32>)
+ ; LEGAL-NEXT: [[UV4:%[0-9]+]]:_(i32), [[UV5:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[LOAD1]](<2 x i32>)
+ ; LEGAL-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<6 x i32>) = G_BUILD_VECTOR [[UV]](i32), [[UV1]](i32), [[UV2]](i32), [[UV3]](i32), [[UV4]](i32), [[UV5]](i32)
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:regv6_32b(<6 x i32>) = COPY [[BUILD_VECTOR]](<6 x i32>)
+ ; LEGAL-NEXT: retValue_v6i32_r [[COPY]](<6 x i32>)
+ %0:reg64b(p2) = functionParameter_i64 0
+ %1:_(<6 x i32>) = G_LOAD %0(p2) :: (load (<6 x i32>) from %ir.arg, align 4, addrspace 2)
+ %2:regv6_32b(<6 x i32>) = COPY %1(<6 x i32>)
+ retValue_v6i32_r %2(<6 x i32>)
+...
+---
+name: load_constant_6xi32_8bytes_alignment
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_constant_6xi32_8bytes_alignment
+ ; LEGAL: %0:reg64b(p2) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<3 x i64>) = G_LOAD %0(p2) :: (load (<3 x i64>) from %ir.arg, align 8, addrspace 2)
+ ; LEGAL-NEXT: [[UV:%[0-9]+]]:_(i64), [[UV1:%[0-9]+]]:_(i64), [[UV2:%[0-9]+]]:_(i64) = G_UNMERGE_VALUES [[LOAD]](<3 x i64>)
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV]](i64)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV1]](i64)
+ ; LEGAL-NEXT: [[BITCAST2:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[UV2]](i64)
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(<6 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[DEF]], [[BITCAST]](<2 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<2 x i32>), 2
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR2:%[0-9]+]]:_(<6 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR1]], [[BITCAST2]](<2 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<6 x i32>) = COPY [[INSERT_SUBVECTOR2]](<6 x i32>)
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:regv6_32b(<6 x i32>) = COPY [[COPY]](<6 x i32>)
+ ; LEGAL-NEXT: retValue_v6i32_r [[COPY1]](<6 x i32>)
+ %0:reg64b(p2) = functionParameter_i64 0
+ %1:_(<6 x i32>) = G_LOAD %0(p2) :: (load (<6 x i32>) from %ir.arg, align 8, addrspace 2)
+ %2:regv6_32b(<6 x i32>) = COPY %1(<6 x i32>)
+ retValue_v6i32_r %2(<6 x i32>)
+...
+---
+name: load_constant_7xi32
+body: |
+ bb.1 (%ir-block.0):
+ ; LEGAL-LABEL: name: load_constant_7xi32
+ ; LEGAL: %0:reg64b(p2) = functionParameter_i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<8 x i32>) = G_LOAD %0(p2) :: (load (<8 x i32>) from %ir.arg, align 8, addrspace 2)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<7 x i32>) = G_EXTRACT_SUBVECTOR [[LOAD]](<8 x i32>), 0
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<7 x i32>) = COPY [[EXTRACT_SUBVECTOR]](<7 x i32>)
+ ; LEGAL-NEXT: [[COPY1:%[0-9]+]]:regv7_32b(<7 x i32>) = COPY [[COPY]](<7 x i32>)
+ ; LEGAL-NEXT: retValue_v7i32_r [[COPY1]](<7 x i32>)
+ %0:reg64b(p2) = functionParameter_i64 0
+ %1:_(<7 x i32>) = G_LOAD %0(p2) :: (load (<7 x i32>) from %ir.arg, align 8, addrspace 2)
+ %2:regv7_32b(<7 x i32>) = COPY %1(<7 x i32>)
+ retValue_v7i32_r %2(<7 x i32>)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir
new file mode 100644
index 00000000000000..5a29c576890e8e
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir
@@ -0,0 +1,98 @@
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+
+---
+name: test_load_128
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_load_128
+ ; CHECK: {{%[0-9]+}}:_(<4 x i32>) = G_LOAD {{%[0-9]+}}(p1) :: (load (<4 x i32>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<128 x i1>) = G_LOAD %0:_(p1) :: (load (<128 x i1>))
+ %2:_(i32) = G_CONSTANT i32 0
+ %3:_(i1) = G_EXTRACT_VECTOR_ELT %1:_(<128 x i1>), %2:_(i32)
+ G_STORE %3:_(i1), %0:_(p1) :: (store (i1))
+...
+---
+name: test_load_192
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_load_192
+ ; CHECK: {{%[0-9]+}}:_(<3 x i64>) = G_LOAD {{%[0-9]+}}(p1) :: (load (<3 x i64>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<192 x i1>) = G_LOAD %0:_(p1) :: (load (<192 x i1>))
+ %2:_(i32) = G_CONSTANT i32 0
+ %3:_(i1) = G_EXTRACT_VECTOR_ELT %1:_(<192 x i1>), %2:_(i32)
+ G_STORE %3:_(i1), %0:_(p1) :: (store (i1))
+...
+---
+name: test_load_256
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_load_256
+ ; CHECK: {{%[0-9]+}}:_(<8 x i32>) = G_LOAD {{%[0-9]+}}(p1) :: (load (<8 x i32>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<256 x i1>) = G_LOAD %0:_(p1) :: (load (<256 x i1>))
+ %2:_(i32) = G_CONSTANT i32 0
+ %3:_(i1) = G_EXTRACT_VECTOR_ELT %1:_(<256 x i1>), %2:_(i32)
+ G_STORE %3:_(i1), %0:_(p1) :: (store (i1))
+...
+---
+name: test_load_512
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_load_512
+ ; CHECK: {{%[0-9]+}}:_(<8 x i32>) = G_LOAD {{%[0-9]+}}(p1) :: (load (<8 x i32>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<512 x i1>) = G_LOAD %0:_(p1) :: (load (<512 x i1>))
+ %2:_(i32) = G_CONSTANT i32 0
+ %3:_(i1) = G_EXTRACT_VECTOR_ELT %1:_(<512 x i1>), %2:_(i32)
+ G_STORE %3:_(i1), %0:_(p1) :: (store (i1))
+...
+---
+name: test_store_192
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_store_192
+ ; CHECK: G_STORE {{%[0-9]+}}(<3 x i64>), {{%[0-9]+}}(p1) :: (store (<3 x i64>)
+ %0:_(p1) = G_CONSTANT i64 16
+ %1:_(i1) = G_CONSTANT i1 1
+ %2:_(<64 x i1>) = G_BUILD_VECTOR %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1)
+ %3:_(<192 x i1>) = G_CONCAT_VECTORS %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>)
+ G_STORE %3:_(<192 x i1>), %0:_(p1) :: (store (<192 x i1>))
+...
+---
+name: test_store_128
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_store_128
+ ; CHECK: G_STORE {{%[0-9]+}}(<4 x i32>), {{%[0-9]+}}(p1) :: (store (<4 x i32>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(i1) = G_CONSTANT i1 1
+ %2:_(<64 x i1>) = G_BUILD_VECTOR %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1)
+ %3:_(<128 x i1>) = G_CONCAT_VECTORS %2:_(<64 x i1>), %2:_(<64 x i1>)
+ G_STORE %3:_(<128 x i1>), %0:_(p1) :: (store (<128 x i1>))
+...
+---
+name: test_store_256
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_store_256
+ ; CHECK: G_STORE {{%[0-9]+}}(<8 x i32>), {{%[0-9]+}}(p1) :: (store (<8 x i32>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(i1) = G_CONSTANT i1 1
+ %2:_(<64 x i1>) = G_BUILD_VECTOR %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1)
+ %3:_(<256 x i1>) = G_CONCAT_VECTORS %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>)
+ G_STORE %3:_(<256 x i1>), %0:_(p1) :: (store (<256 x i1>))
+...
+---
+name: test_store_512
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_store_512
+ ; CHECK: G_STORE {{%[0-9]+}}(<8 x i32>), {{%[0-9]+}}(p1) :: (store (<8 x i32>)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(i1) = G_CONSTANT i1 1
+ %2:_(<64 x i1>) = G_BUILD_VECTOR %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1), %1:_(i1)
+ %3:_(<512 x i1>) = G_CONCAT_VECTORS %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>), %2:_(<64 x i1>)
+ G_STORE %3:_(<512 x i1>), %0:_(p1) :: (store (<512 x i1>))
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir
new file mode 100644
index 00000000000000..dea8bb21f3cc56
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir
@@ -0,0 +1,54 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
+
+# Check that loads/stores of vectors of non-2^n elements are resolved to legal
+# instructions. As of creating this test, they are split apart into multiple
+# scalar loads/stores. Those scalar loads/stores are then conveniently bitcasted
+# to vectors of i8 which are more manageable.
+
+---
+name: test_store
+body: |
+ bb.0:
+
+ ; LEGAL-LABEL: name: test_store
+ ; LEGAL: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(i8) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[DEF1:%[0-9]+]]:_(i8) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[DEF2:%[0-9]+]]:_(i8) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[DEF3:%[0-9]+]]:_(i8) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[DEF4:%[0-9]+]]:_(i8) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[DEF5:%[0-9]+]]:_(i8) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<2 x i8>) = G_BUILD_VECTOR [[DEF]](i8), [[DEF1]](i8)
+ ; LEGAL-NEXT: G_STORE [[BUILD_VECTOR]](<2 x i8>), [[C]](p1) :: (store (<2 x i8>) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ ; LEGAL-NEXT: [[C1:%[0-9]+]]:_(p1) = G_CONSTANT i64 2
+ ; LEGAL-NEXT: G_STORE [[DEF2]](i8), [[C1]](p1) :: (store (i8) into `ptr addrspace(1) null` + 2, align 2, basealign 4294967296, addrspace 1)
+ ; LEGAL-NEXT: [[C2:%[0-9]+]]:_(p1) = G_CONSTANT i64 3
+ ; LEGAL-NEXT: G_STORE [[DEF3]](i8), [[C2]](p1) :: (store (i8) into `ptr addrspace(1) null` + 3, basealign 4294967296, addrspace 1)
+ ; LEGAL-NEXT: [[C3:%[0-9]+]]:_(p1) = G_CONSTANT i64 4
+ ; LEGAL-NEXT: G_STORE [[DEF4]](i8), [[C3]](p1) :: (store (i8) into `ptr addrspace(1) null` + 4, align 4, basealign 4294967296, addrspace 1)
+ ; LEGAL-NEXT: [[C4:%[0-9]+]]:_(p1) = G_CONSTANT i64 5
+ ; LEGAL-NEXT: G_STORE [[DEF5]](i8), [[C4]](p1) :: (store (i8) into `ptr addrspace(1) null` + 5, basealign 4294967296, addrspace 1)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<2 x i24>) = G_IMPLICIT_DEF
+ G_STORE %1:_(<2 x i24>), %0:_(p1) :: (store (<2 x i24>) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+...
+---
+name: test_load
+body: |
+ bb.0:
+
+ ; LEGAL-LABEL: name: test_load
+ ; LEGAL: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<2 x i8>) = G_LOAD [[C]](p1) :: (load (<2 x i8>) from `ptr addrspace(1) null`, align 32, addrspace 1)
+ ; LEGAL-NEXT: [[C1:%[0-9]+]]:_(p1) = G_CONSTANT i64 2
+ ; LEGAL-NEXT: [[LOAD1:%[0-9]+]]:_(i8) = G_LOAD [[C1]](p1) :: (load (i8) from `ptr addrspace(1) null` + 2, align 2, basealign 32, addrspace 1)
+ ; LEGAL-NEXT: G_STORE [[LOAD]](<2 x i8>), [[C]](p1) :: (store (<2 x i8>) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ ; LEGAL-NEXT: [[C2:%[0-9]+]]:_(p1) = G_CONSTANT i64 2
+ ; LEGAL-NEXT: G_STORE [[LOAD1]](i8), [[C2]](p1) :: (store (i8) into `ptr addrspace(1) null` + 2, align 2, basealign 4294967296, addrspace 1)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<2 x i24>) = G_LOAD %0:_(p1) :: (load (<2 x i24>) from `ptr addrspace(1) null`, align 32, addrspace 1)
+ %2:_(i24), %3:_(i24) = G_UNMERGE_VALUES %1:_(<2 x i24>)
+ G_STORE %2:_(i24), %0:_(p1) :: (store (i24) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-vec-of-large-ints.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-vec-of-large-ints.mir
new file mode 100644
index 00000000000000..168f7907827c86
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-vec-of-large-ints.mir
@@ -0,0 +1,84 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
+# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
+
+# Check that loads/stores of vectors of 32n-bit width elements wider than 64 bits are resolved to vectors of 32-bit elements.
+# Check that if (alignment < element bit width) scalarization is performed instead.
+
+---
+name: test_store
+body: |
+ bb.0:
+
+ ; LEGAL-LABEL: name: test_store
+ ; LEGAL: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(i128) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[DEF1:%[0-9]+]]:_(i128) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<4 x i32>) = G_BITCAST [[DEF]](i128)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<4 x i32>) = G_BITCAST [[DEF1]](i128)
+ ; LEGAL-NEXT: [[DEF2:%[0-9]+]]:_(<8 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<8 x i32>) = G_INSERT_SUBVECTOR [[DEF2]], [[BITCAST]](<4 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<8 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<4 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<8 x i32>) = COPY [[INSERT_SUBVECTOR1]](<8 x i32>)
+ ; LEGAL-NEXT: G_STORE [[COPY]](<8 x i32>), [[C]](p1) :: (store (<8 x i32>) into `ptr addrspace(1) null`, addrspace 1)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<2 x i128>) = G_IMPLICIT_DEF
+ G_STORE %1:_(<2 x i128>), %0:_(p1) :: (store (<2 x i128>) into `ptr addrspace(1) null`, align 32, addrspace 1)
+...
+---
+name: test_load
+body: |
+ bb.0:
+
+ ; LEGAL-LABEL: name: test_load
+ ; LEGAL: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; LEGAL-NEXT: [[LOAD:%[0-9]+]]:_(<8 x i32>) = G_LOAD [[C]](p1) :: (load (<8 x i32>) from `ptr addrspace(1) null`, addrspace 1)
+ ; LEGAL-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<4 x i32>) = G_EXTRACT_SUBVECTOR [[LOAD]](<8 x i32>), 0
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(i128) = G_BITCAST [[EXTRACT_SUBVECTOR]](<4 x i32>)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<4 x i32>) = G_BITCAST [[BITCAST]](i128)
+ ; LEGAL-NEXT: G_STORE [[BITCAST1]](<4 x i32>), [[C]](p1) :: (store (<4 x i32>) into `ptr addrspace(1) null`, align 32, addrspace 1)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<2 x i128>) = G_LOAD %0:_(p1) :: (load (<2 x i128>) from `ptr addrspace(1) null`, align 32, addrspace 1)
+ %2:_(i128), %3:_(i128) = G_UNMERGE_VALUES %1:_(<2 x i128>)
+ G_STORE %2:_(i128), %0:_(p1) :: (store (i128) into `ptr addrspace(1) null`, align 32, addrspace 1)
+
+...
+---
+name: test_store_align4
+body: |
+ bb.0:
+
+ ; LEGAL-LABEL: name: test_store_align4
+ ; LEGAL: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(i128) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[DEF1:%[0-9]+]]:_(i128) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<4 x i32>) = G_BITCAST [[DEF]](i128)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<4 x i32>) = G_BITCAST [[DEF1]](i128)
+ ; LEGAL-NEXT: [[DEF2:%[0-9]+]]:_(<8 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<8 x i32>) = G_INSERT_SUBVECTOR [[DEF2]], [[BITCAST]](<4 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<8 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<4 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<8 x i32>) = COPY [[INSERT_SUBVECTOR1]](<8 x i32>)
+ ; LEGAL-NEXT: G_STORE [[COPY]](<8 x i32>), [[C]](p1) :: (store (<8 x i32>) into `ptr addrspace(1) null`, align 4, addrspace 1)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<2 x i128>) = G_IMPLICIT_DEF
+ G_STORE %1:_(<2 x i128>), %0:_(p1) :: (store (<2 x i128>) into `ptr addrspace(1) null`, align 4, addrspace 1)
+...
+---
+name: test_store_align8
+body: |
+ bb.0:
+
+ ; LEGAL-LABEL: name: test_store_align8
+ ; LEGAL: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; LEGAL-NEXT: [[DEF:%[0-9]+]]:_(i128) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[DEF1:%[0-9]+]]:_(i128) = G_IMPLICIT_DEF
+ ; LEGAL-NEXT: [[BITCAST:%[0-9]+]]:_(<4 x i32>) = G_BITCAST [[DEF]](i128)
+ ; LEGAL-NEXT: [[BITCAST1:%[0-9]+]]:_(<4 x i32>) = G_BITCAST [[DEF1]](i128)
+ ; LEGAL-NEXT: [[DEF2:%[0-9]+]]:_(<8 x i32>) = IMPLICIT_DEF
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<8 x i32>) = G_INSERT_SUBVECTOR [[DEF2]], [[BITCAST]](<4 x i32>), 0
+ ; LEGAL-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<8 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BITCAST1]](<4 x i32>), 4
+ ; LEGAL-NEXT: [[COPY:%[0-9]+]]:_(<8 x i32>) = COPY [[INSERT_SUBVECTOR1]](<8 x i32>)
+ ; LEGAL-NEXT: G_STORE [[COPY]](<8 x i32>), [[C]](p1) :: (store (<8 x i32>) into `ptr addrspace(1) null`, align 8, addrspace 1)
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(<2 x i128>) = G_IMPLICIT_DEF
+ G_STORE %1:_(<2 x i128>), %0:_(p1) :: (store (<2 x i128>) into `ptr addrspace(1) null`, align 8, addrspace 1)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
new file mode 100644
index 00000000000000..e9b14060413a3d
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
@@ -0,0 +1,139 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+# Test that legalizeGMulh correctly handles carry propagation in the
+# schoolbook 64-bit multiply-high expansion.
+#
+# The key invariant: when summing the cross-term lower halves and the
+# carry from a_lo*b_lo, a carry can propagate out of bit 31 into the
+# upper 64 bits. A buggy implementation that shifts each cross-term
+# independently (P_hh + (P_lh>>32) + (P_hl>>32)) drops this carry.
+
+--- |
+ define i64 @test_umulh_i64(i64 %a, i64 %b) {
+ ret i64 0
+ }
+ define i64 @test_smulh_i64(i64 %a, i64 %b) {
+ ret i64 0
+ }
+...
+
+# --- G_UMULH i64 ---
+# Verify that the legalized sequence includes carry-aware cross-term
+# accumulation: the adds feeding the final result must include the
+# lower-half sums (AHibLoSum0/AHibLoSum1), not just independent shifts
+# of each cross-term product.
+---
+name: test_umulh_i64
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; Split into 32-bit halves
+ ; Four schoolbook products
+ ; Carry-aware cross-term accumulation (the fix):
+ ; Final upper-64 computation uses SUM1 (not raw HALB):
+ ; CHECK-LABEL: name: test_umulh_i64
+ ; CHECK: %0:reg64b(i64) = functionParameter_i64 0
+ ; CHECK-NEXT: %1:reg64b(i64) = functionParameter_i64 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 4294967295
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(i64) = COPY %0(i64)
+ ; CHECK-NEXT: [[COPY1:%[0-9]+]]:_(i64) = COPY %1(i64)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i64) = G_LSHR [[COPY]], [[C1]](i32)
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR1:%[0-9]+]]:_(i64) = G_LSHR [[COPY1]], [[C2]](i32)
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i64) = G_AND [[COPY]], [[C]]
+ ; CHECK-NEXT: [[AND1:%[0-9]+]]:_(i64) = G_AND [[COPY1]], [[C]]
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i64) = G_MUL [[LSHR]], [[LSHR1]]
+ ; CHECK-NEXT: [[MUL1:%[0-9]+]]:_(i64) = G_MUL [[LSHR]], [[AND1]]
+ ; CHECK-NEXT: [[MUL2:%[0-9]+]]:_(i64) = G_MUL [[AND]], [[LSHR1]]
+ ; CHECK-NEXT: [[MUL3:%[0-9]+]]:_(i64) = G_MUL [[AND]], [[AND1]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR2:%[0-9]+]]:_(i64) = G_LSHR [[MUL3]], [[C3]](i32)
+ ; CHECK-NEXT: [[AND2:%[0-9]+]]:_(i64) = G_AND [[MUL2]], [[C]]
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i64) = G_ADD [[LSHR2]], [[AND2]]
+ ; CHECK-NEXT: [[ADD1:%[0-9]+]]:_(i64) = G_ADD [[MUL1]], [[ADD]]
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR3:%[0-9]+]]:_(i64) = G_LSHR [[MUL2]], [[C4]](i32)
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR4:%[0-9]+]]:_(i64) = G_LSHR [[ADD1]], [[C5]](i32)
+ ; CHECK-NEXT: [[ADD2:%[0-9]+]]:_(i64) = G_ADD [[LSHR3]], [[LSHR4]]
+ ; CHECK-NEXT: [[ADD3:%[0-9]+]]:_(i64) = G_ADD [[MUL]], [[ADD2]]
+ ; CHECK-NEXT: [[COPY2:%[0-9]+]]:_(i64) = COPY [[ADD3]](i64)
+ ; CHECK-NEXT: [[COPY3:%[0-9]+]]:reg64b(i64) = COPY [[COPY2]](i64)
+ ; CHECK-NEXT: retValue_i64_r [[COPY3]](i64)
+ %0:reg64b(i64) = functionParameter_i64 0
+ %1:reg64b(i64) = functionParameter_i64 1
+ %2:_(i64) = G_UMULH %0, %1
+ %3:reg64b(i64) = COPY %2(i64)
+ retValue_i64_r %3(i64)
+...
+
+# --- G_SMULH i64 ---
+# Verify the signed path: operands are converted to absolute values,
+# then the same carry-aware multiply-high is performed, followed by
+# conditional negation.
+---
+name: test_smulh_i64
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; Sign extraction and abs conversion
+ ; Same schoolbook multiply with carries on absolute values
+ ; Carry-aware accumulation must be present
+ ; Upper-64 uses SUM1 (carry-aware)
+ ; Conditional negation for signed result
+ ; CHECK-LABEL: name: test_smulh_i64
+ ; CHECK: %0:reg64b(i64) = functionParameter_i64 0
+ ; CHECK-NEXT: %1:reg64b(i64) = functionParameter_i64 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 4294967295
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i64) = G_ASHR %0, [[C1]](i32)
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[ASHR1:%[0-9]+]]:_(i64) = G_ASHR %1, [[C2]](i32)
+ ; CHECK-NEXT: [[XOR:%[0-9]+]]:_(i64) = G_XOR [[ASHR]], [[ASHR1]]
+ ; CHECK-NEXT: [[XOR1:%[0-9]+]]:_(i64) = G_XOR %0, [[ASHR]]
+ ; CHECK-NEXT: [[XOR2:%[0-9]+]]:_(i64) = G_XOR %1, [[ASHR1]]
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i64) = G_SUB [[XOR1]], [[ASHR]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i64) = G_SUB [[XOR2]], [[ASHR1]]
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i64) = G_LSHR [[SUB]], [[C3]](i32)
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR1:%[0-9]+]]:_(i64) = G_LSHR [[SUB1]], [[C4]](i32)
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i64) = G_AND [[SUB]], [[C]]
+ ; CHECK-NEXT: [[AND1:%[0-9]+]]:_(i64) = G_AND [[SUB1]], [[C]]
+ ; CHECK-NEXT: [[MUL:%[0-9]+]]:_(i64) = G_MUL [[LSHR]], [[LSHR1]]
+ ; CHECK-NEXT: [[MUL1:%[0-9]+]]:_(i64) = G_MUL [[LSHR]], [[AND1]]
+ ; CHECK-NEXT: [[MUL2:%[0-9]+]]:_(i64) = G_MUL [[AND]], [[LSHR1]]
+ ; CHECK-NEXT: [[MUL3:%[0-9]+]]:_(i64) = G_MUL [[AND]], [[AND1]]
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR2:%[0-9]+]]:_(i64) = G_LSHR [[MUL3]], [[C5]](i32)
+ ; CHECK-NEXT: [[AND2:%[0-9]+]]:_(i64) = G_AND [[MUL2]], [[C]]
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i64) = G_ADD [[LSHR2]], [[AND2]]
+ ; CHECK-NEXT: [[ADD1:%[0-9]+]]:_(i64) = G_ADD [[MUL1]], [[ADD]]
+ ; CHECK-NEXT: [[AND3:%[0-9]+]]:_(i64) = G_AND [[MUL3]], [[C]]
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i64) = G_SHL [[ADD1]], [[C6]](i32)
+ ; CHECK-NEXT: [[OR:%[0-9]+]]:_(i64) = G_OR [[SHL]], [[AND3]]
+ ; CHECK-NEXT: [[C7:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR3:%[0-9]+]]:_(i64) = G_LSHR [[MUL2]], [[C7]](i32)
+ ; CHECK-NEXT: [[C8:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[LSHR4:%[0-9]+]]:_(i64) = G_LSHR [[ADD1]], [[C8]](i32)
+ ; CHECK-NEXT: [[ADD2:%[0-9]+]]:_(i64) = G_ADD [[LSHR3]], [[LSHR4]]
+ ; CHECK-NEXT: [[ADD3:%[0-9]+]]:_(i64) = G_ADD [[MUL]], [[ADD2]]
+ ; CHECK-NEXT: [[C9:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[SUB2:%[0-9]+]]:_(i64) = G_SUB [[C9]], [[XOR]]
+ ; CHECK-NEXT: [[XOR3:%[0-9]+]]:_(i64) = G_XOR [[ADD3]], [[SUB2]]
+ ; CHECK-NEXT: [[XOR4:%[0-9]+]]:_(i64) = G_XOR [[OR]], [[SUB2]]
+ ; CHECK-NEXT: [[ADD4:%[0-9]+]]:_(i64) = G_ADD [[XOR4]], [[XOR]]
+ ; CHECK-NEXT: [[TRUNC:%[0-9]+]]:_(i32) = G_TRUNC [[XOR]](i64)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i64) = G_SHL [[ADD4]], [[TRUNC]](i32)
+ ; CHECK-NEXT: [[ADD5:%[0-9]+]]:_(i64) = G_ADD [[XOR3]], [[SHL1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg64b(i64) = COPY [[ADD5]](i64)
+ ; CHECK-NEXT: retValue_i64_r [[COPY]](i64)
+ %0:reg64b(i64) = functionParameter_i64 0
+ %1:reg64b(i64) = functionParameter_i64 1
+ %2:_(i64) = G_SMULH %0, %1
+ %3:reg64b(i64) = COPY %2(i64)
+ retValue_i64_r %3(i64)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir
new file mode 100644
index 00000000000000..0aaafc45bec2a6
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir
@@ -0,0 +1,88 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+# Verify G_UMULH lowering on non-power-of-2 type.
+
+--- |
+ ; ModuleID = 'reduced.ll'
+ source_filename = "reduced.ll"
+ target datalayout = "e-p:64:64-p1:64:64-p2:64:64-p3:32:32-p4:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-G1-A4"
+
+ define pisa_kernel void @test_umulh_s24(<3 x i8> %call19) local_unnamed_addr {
+ %1 = bitcast <3 x i8> %call19 to i24
+ %2 = udiv i24 %1, 42
+ %B13 = zext nneg i24 %2 to i32
+ store i32 %B13, ptr addrspace(1) null, align 4294967296
+ ret void
+ }
+ define pisa_kernel void @test_umulh_i64(i64 %a, i64 %b) {
+ ret void
+ }
+ define pisa_kernel void @test_smulh_i64(i64 %a, i64 %b) {
+ ret void
+ }
+...
+---
+name: test_umulh_s24
+legalized: false
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1 (%ir-block.0):
+ %1:reg8b(i8) = loadParam_i8 0, 0
+ %2:reg8b(i8) = loadParam_i8 0, 1
+ %3:reg8b(i8) = loadParam_i8 0, 2
+ %0:_(<3 x i8>) = G_BUILD_VECTOR %1(i8), %2(i8), %3(i8)
+ %8:_(p1) = G_CONSTANT i64 0
+ %4:_(i24) = G_BITCAST %0(<3 x i8>)
+ %10:_(i24) = G_CONSTANT i24 -3994575
+ %12:_(i24) = G_CONSTANT i24 5
+ %14:_(i24) = G_UMULH %4, %10
+ %15:_(i24) = G_LSHR %14, %12(i24)
+ %7:_(i32) = nneg G_ZEXT %15(i24)
+
+ ; CHECK-LABEL: {{^}}name: test_umulh_s24
+ ; CHECK-NOT: G_UMULH
+
+ G_STORE %7(i32), %8(p1) :: (store (i32) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ ret
+...
+---
+# G_UMULH i64: verifies that the legalizer expands G_UMULH on i64 via custom
+# legalization, replacing it with an arithmetic multiplication sequence.
+name: test_umulh_i64
+legalized: false
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %1:reg64b(i64) = loadParam_i64 1, 0
+ %ptr:_(p1) = G_CONSTANT i64 0
+ ; CHECK-LABEL: {{^}}name: test_umulh_i64
+ ; CHECK-NOT: G_UMULH
+ ; CHECK: G_MUL
+ %2:_(i64) = G_UMULH %0(i64), %1(i64)
+ G_STORE %2(i64), %ptr(p1) :: (store (i64) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+
+...
+---
+# G_SMULH i64: verifies that the legalizer expands G_SMULH on i64 via custom
+# legalization. The signed path also emits G_NEG and XOR adjustments not
+# present in the unsigned path.
+name: test_smulh_i64
+legalized: false
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %1:reg64b(i64) = loadParam_i64 1, 0
+ %ptr:_(p1) = G_CONSTANT i64 0
+ ; CHECK-LABEL: {{^}}name: test_smulh_i64
+ ; CHECK-NOT: G_SMULH
+ ; CHECK: G_MUL
+ %2:_(i64) = G_SMULH %0(i64), %1(i64)
+ G_STORE %2(i64), %ptr(p1) :: (store (i64) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir
new file mode 100644
index 00000000000000..fac84023771fab
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir
@@ -0,0 +1,66 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# Check that i5 phi operand is extended to i16.
+--- |
+ target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
+ target triple = "pisa"
+
+ %"class.sycl::_V1::vec" = type { <4 x float> }
+
+ define pisa_kernel void @_ZTSZZ13MatchSiftDataR8SiftDataS0_RN4sycl3_V15queueERfENKUlRNS2_7handlerEE4_clES7_EUlNS2_7nd_itemILi3EEEE_(ptr addrspace(3) %0, i1 %1) {
+ br i1 %1, label %.preheader, label %._crit_edge29
+
+ .preheader: ; preds = %2
+ br label %3
+
+ ._crit_edge29: ; preds = %2
+ ret void
+
+ 3: ; preds = %.preheader, %3
+ %lsr.iv = phi i5 [ %lsr.iv.next2, %3 ], [ 0, %.preheader ]
+ %4 = zext i5 %lsr.iv to i32
+ %5 = getelementptr %"class.sycl::_V1::vec", ptr addrspace(3) %0, i32 %4
+ %lsr.iv.next2 = add i5 %lsr.iv, 1
+ br label %3
+ }
+
+...
+---
+name: _ZTSZZ13MatchSiftDataR8SiftDataS0_RN4sycl3_V15queueERfENKUlRNS2_7handlerEE4_clES7_EUlNS2_7nd_itemILi3EEEE_
+tracksRegLiveness: true
+body: |
+ bb.1 (%ir-block.2):
+ successors: %bb.2(0x40000000), %bb.3(0x40000000)
+
+ %0:reg32b(p3) = loadParam_i32 0, 0
+ %2:reg8b(i8) = loadParam_i8 1, 0
+ %1:reg8b(i1) = G_TRUNC %2(i8)
+ %9:_(i5) = G_CONSTANT i5 1
+ %11:_(i5) = G_CONSTANT i5 0
+ %12:_(i1) = G_CONSTANT i1 true
+ %13:_(i1) = G_XOR %1, %12
+ G_BRCOND %13(i1), %bb.3
+ G_BR %bb.2
+
+ bb.2..preheader:
+ successors: %bb.4(0x80000000)
+
+ ; CHECK-LABEL: bb.{{.*}}..preheader
+ ; CHECK: [[ZERO:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+
+ G_BR %bb.4
+
+ bb.3.._crit_edge29:
+ ret
+
+ bb.4 (%ir-block.3):
+ successors: %bb.4(0x80000000)
+
+ ; CHECK: [[PHI:%[0-9]+]]:_(i16) = G_PHI [[INC:%[0-9]+]](i16), %bb.3, [[ZERO]](i16), %bb.1
+ ; CHECK: [[ONE:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK: [[INC]]:_(i16) = G_ADD [[PHI]], [[ONE]]
+
+ %3:_(i5) = G_PHI %10(i5), %bb.4, %11(i5), %bb.2
+ %10:_(i5) = G_ADD %3, %9
+ G_BR %bb.4
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir
new file mode 100644
index 00000000000000..682d8b5428f922
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir
@@ -0,0 +1,64 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# Check that i96 phi operand doesn't exit with UnableToLegalize.
+--- |
+ target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
+ target triple = "pisa"
+
+ %"class.sycl::_V1::vec" = type { <4 x float> }
+
+ define pisa_kernel void @_ZTSZZ13MatchSiftDataR8SiftDataS0_RN4sycl3_V15queueERfENKUlRNS2_7handlerEE4_clES7_EUlNS2_7nd_itemILi3EEEE_(ptr addrspace(3) %0, i1 %1) {
+ br i1 %1, label %.preheader, label %._crit_edge29
+
+ .preheader: ; preds = %2
+ br label %3
+
+ ._crit_edge29: ; preds = %2
+ ret void
+
+ 3: ; preds = %.preheader, %3
+ %lsr.iv = phi i96 [ %lsr.iv.next2, %3 ], [ 0, %.preheader ]
+ %4 = trunc i96 %lsr.iv to i32
+ %5 = getelementptr %"class.sycl::_V1::vec", ptr addrspace(3) %0, i32 %4
+ %lsr.iv.next2 = add i96 %lsr.iv, 1
+ br label %3
+ }
+
+...
+---
+name: _ZTSZZ13MatchSiftDataR8SiftDataS0_RN4sycl3_V15queueERfENKUlRNS2_7handlerEE4_clES7_EUlNS2_7nd_itemILi3EEEE_
+tracksRegLiveness: true
+body: |
+ bb.1 (%ir-block.2):
+ successors: %bb.2(0x40000000), %bb.3(0x40000000)
+
+ %0:reg32b(p3) = loadParam_i32 0, 0
+ %2:reg8b(i8) = loadParam_i8 1, 0
+ %1:reg8b(i1) = G_TRUNC %2(i8)
+ %9:_(i96) = G_CONSTANT i96 1
+ %11:_(i96) = G_CONSTANT i96 0
+ %12:_(i1) = G_CONSTANT i1 true
+ %13:_(i1) = G_XOR %1, %12
+ G_BRCOND %13(i1), %bb.3
+ G_BR %bb.2
+
+ bb.2..preheader:
+ successors: %bb.4(0x80000000)
+
+
+ G_BR %bb.4
+
+ bb.3.._crit_edge29:
+ ret
+
+ bb.4 (%ir-block.3):
+ successors: %bb.4(0x80000000)
+ ; i96 phi inst ends up split into two i64 insts.
+ ; CHECK: %{{[0-9]+}}:_(i64) = G_PHI [[UNMERG_0:%[0-9]+]](i64), %bb.3, %{{[0-9]+}}(i64), %bb.1
+ ; CHECK: %{{[0-9]+}}:_(i64) = G_PHI [[UNMERG_1:%[0-9]+]](i64), %bb.3, %{{[0-9]+}}(i64), %bb.1
+ ; CHECK: [[UNMERG_0]]:_(i64), [[UNMERG_1]]:_(i64) = G_UNMERGE_VALUES %{{[0-9]+}}(i128)
+
+ %3:_(i96) = G_PHI %10(i96), %bb.4, %11(i96), %bb.2
+ %10:_(i96) = G_ADD %3, %9
+ G_BR %bb.4
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir
new file mode 100644
index 00000000000000..9c8dcbd2e9b2df
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir
@@ -0,0 +1,33 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+--- |
+ target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
+ target triple = "pisa"
+
+ define pisa_kernel void @test(<4 x bfloat> %arg) {
+ entry:
+ br label %for.body
+
+ for.body: ; preds = %for.body, %entry
+ %y.05 = phi <4 x bfloat> [ %arg, %entry ], [ %y.05, %for.body ]
+ br label %for.body
+ }
+
+...
+---
+name: test
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK: [[VEC:%[0-9]+]]:_(<4 x i16>) = COPY
+ ; CHECK: {{%[0-9]+}}:_(i16), {{%[0-9]+}}:_(i16), {{%[0-9]+}}:_(i16), {{%[0-9]+}}:_(i16) = G_UNMERGE_VALUES [[VEC]]
+
+ %0:regv4_16b = functionParameter_v4i16 0
+ %56:_(<4 x i16>) = COPY %0:regv4_16b
+
+ bb.2.for.body:
+ ; CHECK-COUNT-4: {{%[0-9]+}}:_(i16) = G_PHI
+ %26:_(<4 x i16>) = G_PHI %26:_(<4 x i16>), %bb.2, %56:_(<4 x i16>), %bb.1
+ G_BR %bb.2
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir
new file mode 100644
index 00000000000000..11a6fa78ed52b7
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir
@@ -0,0 +1,33 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# Check that i24 operands in phi inst are extended to i32.
+
+--- |
+ target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
+ target triple = "pisa"
+
+ define pisa_kernel void @_ZTSN4sycl3_V16detail9reduction7MainKrnIN39reduction_without_identity_param_common6kernelIiLb1ELb0EN16reduction_common19op_without_identityIiEENS0_5rangeILi1EEELNS6_14test_case_typeE3ELi2EEELNS2_8strategyE3EJEEE() {
+ entry:
+ br label %for.body
+
+ for.body: ; preds = %for.body, %entry
+ %LocalSum.sroa = phi i24 [ %LocalSum.sroa, %for.body ], [ 1, %entry ]
+ br label %for.body
+ }
+
+...
+---
+name: _ZTSN4sycl3_V16detail9reduction7MainKrnIN39reduction_without_identity_param_common6kernelIiLb1ELb0EN16reduction_common19op_without_identityIiEENS0_5rangeILi1EEELNS6_14test_case_typeE3ELi2EEELNS2_8strategyE3EJEEE
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK: [[ONE:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+
+ %1:_(i24) = G_CONSTANT i24 1
+
+ bb.2.for.body:
+ ; CHECK: [[V:%[0-9]+]]:_(i32) = G_PHI [[V]](i32), %bb.1, [[ONE]](i32), %bb.0
+
+ %0:_(i24) = G_PHI %0(i24), %bb.2, %1(i24), %bb.1
+ G_BR %bb.2
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir
new file mode 100644
index 00000000000000..66b742daeeb5d4
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir
@@ -0,0 +1,38 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# Check that i40 operands in select inst are extended to i64.
+
+--- |
+ target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
+ target triple = "pisa"
+
+ define pisa_kernel void @_ZTS22broadcast_group_kernelILi1EN4util11custom_typeEE(i1 %ov) {
+ entry:
+ %local_var.sroa.insert.shift = select i1 %ov, i40 0, i40 4294967296
+ %agg.tmp1.sroa.insert.insert = zext i40 %local_var.sroa.insert.shift to i64
+ %call = call i64 @_Z22__spirv_GroupBroadcastjmm(i64 %agg.tmp1.sroa.insert.insert)
+ ret void
+ }
+
+ declare i64 @_Z22__spirv_GroupBroadcastjmm(i64)
+
+...
+---
+name: _ZTS22broadcast_group_kernelILi1EN4util11custom_typeEE
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ %1:reg8b(i8) = loadParam_i8 0, 0
+ %0:reg8b(i1) = G_TRUNC %1(i8)
+
+ ; CHECK: [[Op0:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK: [[Op1:%[0-9]+]]:_(i64) = G_CONSTANT i64 4294967296
+ ; CHECK: :_(i64) = G_SELECT %{{[0-9]+}}(i1), [[Op0]], [[Op1]]
+
+ %3:_(i40) = G_CONSTANT i40 0
+ %4:_(i40) = G_CONSTANT i40 4294967296
+ %2:_(i40) = G_SELECT %0(i1), %3, %4
+ %5:_(i64) = G_ZEXT %2(i40)
+ %6:reg64b(i64) = functionCall_i64_r @_Z22__spirv_GroupBroadcastjmm, %5(i64)
+ ret
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
new file mode 100644
index 00000000000000..ade58cf506a0af
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
@@ -0,0 +1,24 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
+
+# Check that alignment too large to store in 32-bit value isn't truncated.
+--- |
+ define void @test_concat_clamp_elts(ptr %addr) {
+ %vec.expand = shufflevector <6 x i16> undef, <6 x i16> undef, <12 x i32> <i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef>
+ store <12 x i16> %vec.expand, ptr addrspace(1) null, align 4294967296
+ ret void
+ }
+...
+---
+name: test_concat_clamp_elts
+tracksRegLiveness: true
+body: |
+ bb.1:
+ ; LEGAL-NOT: G_STORE %{{[0-9]+}}(i16)
+ ; LEGAL: G_STORE %{{[0-9]+}}(<4 x i32>)
+ %0:_(<6 x i16>) = G_IMPLICIT_DEF
+ %1:_(<6 x i16>) = G_IMPLICIT_DEF
+ %2:_(<12 x i16>) = G_CONCAT_VECTORS %0:_(<6 x i16>), %1:_(<6 x i16>)
+ %3:_(p1) = G_CONSTANT i64 0
+ G_STORE %2:_(<12 x i16>), %3:_(p1) :: (store (<12 x i16>) into `ptr addrspace(1) null`, align 4294967296, addrspace 1)
+ ret
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir
new file mode 100644
index 00000000000000..79df8faa0f77ed
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir
@@ -0,0 +1,515 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+# Checks that sequence produced by Expand IR instructions for udiv128 is legalized:
+--- |
+ define pisa_kernel void @test_udiv128(i64 %0, i64 %1) local_unnamed_addr {
+ .preheader3_udiv-special-cases:
+ %2 = tail call i32 @llvm.pisa.local.size.x()
+ %3 = zext i64 %0 to i128
+ %4 = add nuw nsw i128 %3, 1
+ %5 = zext i64 %1 to i128
+ %umax = tail call i128 @llvm.umax.i128(i128 %4, i128 %5)
+ %6 = icmp uge i64 %0, %1
+ %umin = zext i1 %6 to i128
+ %7 = add nuw nsw i128 %umin, %5
+ %8 = sub nsw i128 %umax, %7
+ %9 = zext nneg i32 %2 to i128
+ %10 = freeze i128 %9
+ %11 = freeze i128 %8
+ %12 = icmp eq i128 %10, 0
+ %13 = icmp eq i128 %11, 0
+ %14 = or i1 %12, %13
+ %15 = call i128 @llvm.ctlz.i128(i128 %10, i1 true)
+ %16 = call i128 @llvm.ctlz.i128(i128 %11, i1 true)
+ %17 = sub i128 %15, %16
+ %18 = icmp ugt i128 %17, 127
+ %19 = select i1 %14, i1 true, i1 %18
+ %20 = icmp eq i128 %17, 127
+ %21 = select i1 %19, i128 0, i128 %11
+ %22 = select i1 %19, i1 true, i1 %20
+ br i1 %22, label %udiv-end, label %udiv-bb1
+
+ udiv-bb1: ; preds = %.preheader3_udiv-special-cases
+ %23 = call { i128, i1 } @llvm.uadd.with.overflow.i128(i128 %17, i128 1)
+ %math = extractvalue { i128, i1 } %23, 0
+ %ov = extractvalue { i128, i1 } %23, 1
+ %24 = sub i128 127, %17
+ %25 = shl i128 %11, %24
+ br i1 %ov, label %udiv-loop-exit, label %udiv-preheader
+
+ udiv-preheader: ; preds = %udiv-bb1
+ %26 = lshr i128 %11, %math
+ %27 = add i128 %10, -1
+ br label %udiv-do-while
+
+ udiv-do-while: ; preds = %udiv-do-while, %udiv-preheader
+ %28 = phi i128 [ 0, %udiv-preheader ], [ %39, %udiv-do-while ]
+ %29 = phi i128 [ %math, %udiv-preheader ], [ %42, %udiv-do-while ]
+ %30 = phi i128 [ %26, %udiv-preheader ], [ %41, %udiv-do-while ]
+ %31 = phi i128 [ %25, %udiv-preheader ], [ %36, %udiv-do-while ]
+ %32 = shl i128 %30, 1
+ %33 = lshr i128 %31, 127
+ %34 = or i128 %32, %33
+ %35 = shl i128 %31, 1
+ %36 = or i128 %28, %35
+ %37 = sub i128 %27, %34
+ %38 = ashr i128 %37, 127
+ %39 = and i128 %38, 1
+ %40 = and i128 %38, %10
+ %41 = sub i128 %34, %40
+ %42 = add i128 %29, -1
+ %43 = icmp eq i128 %42, 0
+ br i1 %43, label %udiv-loop-exit, label %udiv-do-while
+
+ udiv-loop-exit: ; preds = %udiv-do-while, %udiv-bb1
+ %44 = phi i128 [ 0, %udiv-bb1 ], [ %39, %udiv-do-while ]
+ %45 = phi i128 [ %25, %udiv-bb1 ], [ %36, %udiv-do-while ]
+ %46 = shl i128 %45, 1
+ %47 = or i128 %44, %46
+ br label %udiv-end
+
+ udiv-end: ; preds = %udiv-loop-exit, %.preheader3_udiv-special-cases
+ %48 = phi i128 [ %47, %udiv-loop-exit ], [ %21, %.preheader3_udiv-special-cases ]
+ %49 = zext i1 %6 to i128
+ %50 = add i128 %48, %49
+ %51 = trunc i128 %50 to i64
+ store i64 %51, ptr addrspace(3) null, align 2147483648
+ ret void
+ }
+
+ ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+ declare range(i32 1, 65537) i32 @llvm.pisa.local.size.x() #0
+
+ ; Function Attrs: nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none)
+ declare i128 @llvm.umax.i128(i128, i128) #1
+
+ ; Function Attrs: nocallback nofree nosync nounwind speculatable willreturn memory(none)
+ declare i128 @llvm.ctlz.i128(i128, i1 immarg) #2
+
+ ; Function Attrs: nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none)
+ declare { i128, i1 } @llvm.uadd.with.overflow.i128(i128, i128) #1
+
+ attributes #0 = { nocallback nofree nosync nounwind willreturn memory(none) }
+ attributes #1 = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
+ attributes #2 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+...
+---
+name: test_udiv128
+alignment: 1
+tracksRegLiveness: true
+body: |
+ ; CHECK-LABEL: name: test_udiv128
+ ; CHECK: bb.0..preheader3_udiv-special-cases:
+ ; CHECK-NEXT: successors: %bb.5(0x40000000), %bb.1(0x40000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: %0:reg64b(i64) = loadParam_i64 0, 0
+ ; CHECK-NEXT: %1:reg64b(i64) = loadParam_i64 1, 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i64) = G_CONSTANT i64 1
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C3:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C4:%[0-9]+]]:_(i64) = G_CONSTANT i64 127
+ ; CHECK-NEXT: [[C5:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C6:%[0-9]+]]:_(i64) = G_CONSTANT i64 -1
+ ; CHECK-NEXT: [[C7:%[0-9]+]]:_(i64) = G_CONSTANT i64 -1
+ ; CHECK-NEXT: [[C8:%[0-9]+]]:_(p3) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[INT:%[0-9]+]]:_(i32) = G_INTRINSIC intrinsic(@llvm.pisa.local.size.x)
+ ; CHECK-NEXT: [[C9:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[UV:%[0-9]+]]:_(i32), [[UV1:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %0(i64)
+ ; CHECK-NEXT: [[UV2:%[0-9]+]]:_(i32), [[UV3:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C]](i64)
+ ; CHECK-NEXT: [[UADDO:%[0-9]+]]:_(i32), [[UADDO1:%[0-9]+]]:_(i1) = G_UADDO [[UV]], [[UV2]]
+ ; CHECK-NEXT: [[UADDE:%[0-9]+]]:_(i32), [[UADDE1:%[0-9]+]]:_(i1) = G_UADDE [[UV1]], [[UV3]], [[UADDO1]]
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[UADDO]](i32), [[UADDE]](i32)
+ ; CHECK-NEXT: [[UV4:%[0-9]+]]:_(i32), [[UV5:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C9]](i64)
+ ; CHECK-NEXT: [[UV6:%[0-9]+]]:_(i32), [[UV7:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C1]](i64)
+ ; CHECK-NEXT: [[UADDE2:%[0-9]+]]:_(i32), [[UADDE3:%[0-9]+]]:_(i1) = G_UADDE [[UV4]], [[UV6]], [[UADDE1]]
+ ; CHECK-NEXT: [[UADDE4:%[0-9]+]]:_(i32), [[UADDE5:%[0-9]+]]:_(i1) = G_UADDE [[UV5]], [[UV7]], [[UADDE3]]
+ ; CHECK-NEXT: [[MV1:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[UADDE2]](i32), [[UADDE4]](i32)
+ ; CHECK-NEXT: [[C10:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[MV]](i64), %1
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[MV1]](i64), [[C10]]
+ ; CHECK-NEXT: [[ICMP2:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[MV1]](i64), [[C10]]
+ ; CHECK-NEXT: [[C11:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C12:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT:%[0-9]+]]:_(i16) = G_SELECT [[ICMP]](i1), [[C12]], [[C11]]
+ ; CHECK-NEXT: [[C13:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C14:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT1:%[0-9]+]]:_(i16) = G_SELECT [[ICMP1]](i1), [[C14]], [[C13]]
+ ; CHECK-NEXT: [[SELECT2:%[0-9]+]]:_(i16) = G_SELECT [[ICMP2]](i1), [[SELECT]], [[SELECT1]]
+ ; CHECK-NEXT: [[C15:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C16:%[0-9]+]]:_(i16) = G_CONSTANT i16 1
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i16) = G_AND [[SELECT2]], [[C16]]
+ ; CHECK-NEXT: [[ICMP3:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND]](i16), [[C15]]
+ ; CHECK-NEXT: [[SELECT3:%[0-9]+]]:_(i64) = G_SELECT [[ICMP3]](i1), [[MV]], %1
+ ; CHECK-NEXT: [[SELECT4:%[0-9]+]]:_(i64) = G_SELECT [[ICMP3]](i1), [[MV1]], [[C10]]
+ ; CHECK-NEXT: [[ICMP4:%[0-9]+]]:_(i1) = G_ICMP intpred(uge), %0(i64), %1
+ ; CHECK-NEXT: [[C17:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C18:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C19:%[0-9]+]]:_(i64) = G_CONSTANT i64 1
+ ; CHECK-NEXT: [[C20:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[SELECT5:%[0-9]+]]:_(i64) = G_SELECT [[ICMP4]](i1), [[C19]], [[C17]]
+ ; CHECK-NEXT: [[SELECT6:%[0-9]+]]:_(i64) = G_SELECT [[ICMP4]](i1), [[C20]], [[C18]]
+ ; CHECK-NEXT: [[UV8:%[0-9]+]]:_(i32), [[UV9:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[SELECT5]](i64)
+ ; CHECK-NEXT: [[UV10:%[0-9]+]]:_(i32), [[UV11:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES %1(i64)
+ ; CHECK-NEXT: [[UADDO2:%[0-9]+]]:_(i32), [[UADDO3:%[0-9]+]]:_(i1) = G_UADDO [[UV8]], [[UV10]]
+ ; CHECK-NEXT: [[UADDE6:%[0-9]+]]:_(i32), [[UADDE7:%[0-9]+]]:_(i1) = G_UADDE [[UV9]], [[UV11]], [[UADDO3]]
+ ; CHECK-NEXT: [[UV12:%[0-9]+]]:_(i32), [[UV13:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[SELECT6]](i64)
+ ; CHECK-NEXT: [[UV14:%[0-9]+]]:_(i32), [[UV15:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C10]](i64)
+ ; CHECK-NEXT: [[UADDE8:%[0-9]+]]:_(i32), [[UADDE9:%[0-9]+]]:_(i1) = G_UADDE [[UV12]], [[UV14]], [[UADDE7]]
+ ; CHECK-NEXT: [[UADDE10:%[0-9]+]]:_(i32), [[UADDE11:%[0-9]+]]:_(i1) = G_UADDE [[UV13]], [[UV15]], [[UADDE9]]
+ ; CHECK-NEXT: [[UV16:%[0-9]+]]:_(i32), [[UV17:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[SELECT3]](i64)
+ ; CHECK-NEXT: [[USUBO:%[0-9]+]]:_(i32), [[USUBO1:%[0-9]+]]:_(i1) = G_USUBO [[UV16]], [[UADDO2]]
+ ; CHECK-NEXT: [[USUBE:%[0-9]+]]:_(i32), [[USUBE1:%[0-9]+]]:_(i1) = G_USUBE [[UV17]], [[UADDE6]], [[USUBO1]]
+ ; CHECK-NEXT: [[MV2:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[USUBO]](i32), [[USUBE]](i32)
+ ; CHECK-NEXT: [[UV18:%[0-9]+]]:_(i32), [[UV19:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[SELECT4]](i64)
+ ; CHECK-NEXT: [[USUBE2:%[0-9]+]]:_(i32), [[USUBE3:%[0-9]+]]:_(i1) = G_USUBE [[UV18]], [[UADDE8]], [[USUBE1]]
+ ; CHECK-NEXT: [[USUBE4:%[0-9]+]]:_(i32), [[USUBE5:%[0-9]+]]:_(i1) = G_USUBE [[UV19]], [[UADDE10]], [[USUBE3]]
+ ; CHECK-NEXT: [[MV3:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[USUBE2]](i32), [[USUBE4]](i32)
+ ; CHECK-NEXT: [[FREEZE:%[0-9]+]]:_(i32) = G_FREEZE [[INT]]
+ ; CHECK-NEXT: [[C21:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[MV4:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[FREEZE]](i32), [[C21]](i32)
+ ; CHECK-NEXT: [[C22:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[FREEZE1:%[0-9]+]]:_(i64) = G_FREEZE [[MV2]]
+ ; CHECK-NEXT: [[FREEZE2:%[0-9]+]]:_(i64) = G_FREEZE [[MV3]]
+ ; CHECK-NEXT: [[C23:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[XOR:%[0-9]+]]:_(i64) = G_XOR [[MV4]], [[C2]]
+ ; CHECK-NEXT: [[XOR1:%[0-9]+]]:_(i64) = G_XOR [[C22]], [[C3]]
+ ; CHECK-NEXT: [[OR:%[0-9]+]]:_(i64) = G_OR [[XOR]], [[XOR1]]
+ ; CHECK-NEXT: [[ICMP5:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[OR]](i64), [[C23]]
+ ; CHECK-NEXT: [[C24:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[XOR2:%[0-9]+]]:_(i64) = G_XOR [[FREEZE1]], [[C2]]
+ ; CHECK-NEXT: [[XOR3:%[0-9]+]]:_(i64) = G_XOR [[FREEZE2]], [[C3]]
+ ; CHECK-NEXT: [[OR1:%[0-9]+]]:_(i64) = G_OR [[XOR2]], [[XOR3]]
+ ; CHECK-NEXT: [[ICMP6:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[OR1]](i64), [[C24]]
+ ; CHECK-NEXT: [[C25:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[C26:%[0-9]+]]:_(i32) = G_CONSTANT i32 -1
+ ; CHECK-NEXT: [[SELECT7:%[0-9]+]]:_(i32) = G_SELECT [[ICMP5]](i1), [[C26]], [[C25]]
+ ; CHECK-NEXT: [[C27:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[C28:%[0-9]+]]:_(i32) = G_CONSTANT i32 -1
+ ; CHECK-NEXT: [[SELECT8:%[0-9]+]]:_(i32) = G_SELECT [[ICMP6]](i1), [[C28]], [[C27]]
+ ; CHECK-NEXT: [[OR2:%[0-9]+]]:_(i32) = G_OR [[SELECT7]], [[SELECT8]]
+ ; CHECK-NEXT: [[C29:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[ICMP7:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[C22]](i64), [[C29]]
+ ; CHECK-NEXT: [[C30:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP8:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[C21]](i32), [[C30]]
+ ; CHECK-NEXT: [[CTLZ_ZERO_UNDEF:%[0-9]+]]:_(i32) = G_CTLZ_ZERO_POISON [[FREEZE]](i32)
+ ; CHECK-NEXT: [[C31:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD [[CTLZ_ZERO_UNDEF]], [[C31]]
+ ; CHECK-NEXT: [[CTLZ_ZERO_UNDEF1:%[0-9]+]]:_(i32) = G_CTLZ_ZERO_POISON [[C21]](i32)
+ ; CHECK-NEXT: [[SELECT9:%[0-9]+]]:_(i32) = G_SELECT [[ICMP8]](i1), [[ADD]], [[CTLZ_ZERO_UNDEF1]]
+ ; CHECK-NEXT: [[C32:%[0-9]+]]:_(i32) = G_CONSTANT i32 64
+ ; CHECK-NEXT: [[ADD1:%[0-9]+]]:_(i32) = G_ADD [[SELECT9]], [[C32]]
+ ; CHECK-NEXT: [[UV20:%[0-9]+]]:_(i32), [[UV21:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C22]](i64)
+ ; CHECK-NEXT: [[C33:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP9:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[UV21]](i32), [[C33]]
+ ; CHECK-NEXT: [[CTLZ_ZERO_UNDEF2:%[0-9]+]]:_(i32) = G_CTLZ_ZERO_POISON [[UV20]](i32)
+ ; CHECK-NEXT: [[C34:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[ADD2:%[0-9]+]]:_(i32) = G_ADD [[CTLZ_ZERO_UNDEF2]], [[C34]]
+ ; CHECK-NEXT: [[CTLZ_ZERO_UNDEF3:%[0-9]+]]:_(i32) = G_CTLZ_ZERO_POISON [[UV21]](i32)
+ ; CHECK-NEXT: [[SELECT10:%[0-9]+]]:_(i32) = G_SELECT [[ICMP9]](i1), [[ADD2]], [[CTLZ_ZERO_UNDEF3]]
+ ; CHECK-NEXT: [[SELECT11:%[0-9]+]]:_(i32) = G_SELECT [[ICMP7]](i1), [[ADD1]], [[SELECT10]]
+ ; CHECK-NEXT: [[C35:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[C36:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C37:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[ICMP10:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[FREEZE2]](i64), [[C37]]
+ ; CHECK-NEXT: [[UV22:%[0-9]+]]:_(i32), [[UV23:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[FREEZE1]](i64)
+ ; CHECK-NEXT: [[C38:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP11:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[UV23]](i32), [[C38]]
+ ; CHECK-NEXT: [[CTLZ_ZERO_UNDEF4:%[0-9]+]]:_(i32) = G_CTLZ_ZERO_POISON [[UV22]](i32)
+ ; CHECK-NEXT: [[C39:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[ADD3:%[0-9]+]]:_(i32) = G_ADD [[CTLZ_ZERO_UNDEF4]], [[C39]]
+ ; CHECK-NEXT: [[CTLZ_ZERO_UNDEF5:%[0-9]+]]:_(i32) = G_CTLZ_ZERO_POISON [[UV23]](i32)
+ ; CHECK-NEXT: [[SELECT12:%[0-9]+]]:_(i32) = G_SELECT [[ICMP11]](i1), [[ADD3]], [[CTLZ_ZERO_UNDEF5]]
+ ; CHECK-NEXT: [[C40:%[0-9]+]]:_(i32) = G_CONSTANT i32 64
+ ; CHECK-NEXT: [[ADD4:%[0-9]+]]:_(i32) = G_ADD [[SELECT12]], [[C40]]
+ ; CHECK-NEXT: [[UV24:%[0-9]+]]:_(i32), [[UV25:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[FREEZE2]](i64)
+ ; CHECK-NEXT: [[C41:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP12:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[UV25]](i32), [[C41]]
+ ; CHECK-NEXT: [[CTLZ_ZERO_UNDEF6:%[0-9]+]]:_(i32) = G_CTLZ_ZERO_POISON [[UV24]](i32)
+ ; CHECK-NEXT: [[C42:%[0-9]+]]:_(i32) = G_CONSTANT i32 32
+ ; CHECK-NEXT: [[ADD5:%[0-9]+]]:_(i32) = G_ADD [[CTLZ_ZERO_UNDEF6]], [[C42]]
+ ; CHECK-NEXT: [[CTLZ_ZERO_UNDEF7:%[0-9]+]]:_(i32) = G_CTLZ_ZERO_POISON [[UV25]](i32)
+ ; CHECK-NEXT: [[SELECT13:%[0-9]+]]:_(i32) = G_SELECT [[ICMP12]](i1), [[ADD5]], [[CTLZ_ZERO_UNDEF7]]
+ ; CHECK-NEXT: [[SELECT14:%[0-9]+]]:_(i32) = G_SELECT [[ICMP10]](i1), [[ADD4]], [[SELECT13]]
+ ; CHECK-NEXT: [[C43:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[C44:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[USUBO2:%[0-9]+]]:_(i32), [[USUBO3:%[0-9]+]]:_(i1) = G_USUBO [[SELECT11]], [[SELECT14]]
+ ; CHECK-NEXT: [[USUBE6:%[0-9]+]]:_(i32), [[USUBE7:%[0-9]+]]:_(i1) = G_USUBE [[C35]], [[C43]], [[USUBO3]]
+ ; CHECK-NEXT: [[MV5:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[USUBO2]](i32), [[USUBE6]](i32)
+ ; CHECK-NEXT: [[UV26:%[0-9]+]]:_(i32), [[UV27:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C36]](i64)
+ ; CHECK-NEXT: [[UV28:%[0-9]+]]:_(i32), [[UV29:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C44]](i64)
+ ; CHECK-NEXT: [[USUBE8:%[0-9]+]]:_(i32), [[USUBE9:%[0-9]+]]:_(i1) = G_USUBE [[UV26]], [[UV28]], [[USUBE7]]
+ ; CHECK-NEXT: [[USUBE10:%[0-9]+]]:_(i32), [[USUBE11:%[0-9]+]]:_(i1) = G_USUBE [[UV27]], [[UV29]], [[USUBE9]]
+ ; CHECK-NEXT: [[MV6:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[USUBE8]](i32), [[USUBE10]](i32)
+ ; CHECK-NEXT: [[ICMP13:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[MV5]](i64), [[C4]]
+ ; CHECK-NEXT: [[ICMP14:%[0-9]+]]:_(i1) = G_ICMP intpred(ugt), [[MV6]](i64), [[C5]]
+ ; CHECK-NEXT: [[ICMP15:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[MV6]](i64), [[C5]]
+ ; CHECK-NEXT: [[C45:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C46:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT15:%[0-9]+]]:_(i16) = G_SELECT [[ICMP13]](i1), [[C46]], [[C45]]
+ ; CHECK-NEXT: [[C47:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[C48:%[0-9]+]]:_(i16) = G_CONSTANT i16 -1
+ ; CHECK-NEXT: [[SELECT16:%[0-9]+]]:_(i16) = G_SELECT [[ICMP14]](i1), [[C48]], [[C47]]
+ ; CHECK-NEXT: [[SELECT17:%[0-9]+]]:_(i16) = G_SELECT [[ICMP15]](i1), [[SELECT15]], [[SELECT16]]
+ ; CHECK-NEXT: [[ANYEXT:%[0-9]+]]:_(i32) = G_ANYEXT [[SELECT17]](i16)
+ ; CHECK-NEXT: [[FREEZE3:%[0-9]+]]:_(i32) = G_FREEZE [[ANYEXT]]
+ ; CHECK-NEXT: [[OR3:%[0-9]+]]:_(i32) = G_OR [[OR2]], [[FREEZE3]]
+ ; CHECK-NEXT: [[C49:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[C50:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[AND1:%[0-9]+]]:_(i32) = G_AND [[OR3]], [[C50]]
+ ; CHECK-NEXT: [[ICMP16:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND1]](i32), [[C49]]
+ ; CHECK-NEXT: [[C51:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[XOR4:%[0-9]+]]:_(i64) = G_XOR [[MV5]], [[C4]]
+ ; CHECK-NEXT: [[XOR5:%[0-9]+]]:_(i64) = G_XOR [[MV6]], [[C5]]
+ ; CHECK-NEXT: [[OR4:%[0-9]+]]:_(i64) = G_OR [[XOR4]], [[XOR5]]
+ ; CHECK-NEXT: [[ICMP17:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[OR4]](i64), [[C51]]
+ ; CHECK-NEXT: [[SELECT18:%[0-9]+]]:_(i64) = G_SELECT [[ICMP16]](i1), [[C2]], [[FREEZE1]]
+ ; CHECK-NEXT: [[C52:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[C53:%[0-9]+]]:_(i32) = G_CONSTANT i32 -1
+ ; CHECK-NEXT: [[SELECT19:%[0-9]+]]:_(i32) = G_SELECT [[ICMP17]](i1), [[C53]], [[C52]]
+ ; CHECK-NEXT: [[FREEZE4:%[0-9]+]]:_(i32) = G_FREEZE [[SELECT19]]
+ ; CHECK-NEXT: [[OR5:%[0-9]+]]:_(i32) = G_OR [[OR3]], [[FREEZE4]]
+ ; CHECK-NEXT: [[C54:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[C55:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[AND2:%[0-9]+]]:_(i32) = G_AND [[OR5]], [[C55]]
+ ; CHECK-NEXT: [[ICMP18:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND2]](i32), [[C54]]
+ ; CHECK-NEXT: G_BRCOND [[ICMP18]](i1), %bb.5
+ ; CHECK-NEXT: G_BR %bb.1
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.1.udiv-bb1:
+ ; CHECK-NEXT: successors: %bb.4(0x40000000), %bb.2(0x40000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[UV30:%[0-9]+]]:_(i32), [[UV31:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C]](i64)
+ ; CHECK-NEXT: [[UV32:%[0-9]+]]:_(i32), [[UV33:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C1]](i64)
+ ; CHECK-NEXT: [[UADDO4:%[0-9]+]]:_(i32), [[UADDO5:%[0-9]+]]:_(i1) = G_UADDO [[USUBO2]], [[UV30]]
+ ; CHECK-NEXT: [[UADDE12:%[0-9]+]]:_(i32), [[UADDE13:%[0-9]+]]:_(i1) = G_UADDE [[USUBE6]], [[UV31]], [[UADDO5]]
+ ; CHECK-NEXT: [[UADDE14:%[0-9]+]]:_(i32), [[UADDE15:%[0-9]+]]:_(i1) = G_UADDE [[USUBE8]], [[UV32]], [[UADDE13]]
+ ; CHECK-NEXT: [[UADDE16:%[0-9]+]]:_(i32), [[UADDE17:%[0-9]+]]:_(i1) = G_UADDE [[USUBE10]], [[UV33]], [[UADDE15]]
+ ; CHECK-NEXT: [[UV34:%[0-9]+]]:_(i32), [[UV35:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C4]](i64)
+ ; CHECK-NEXT: [[USUBO4:%[0-9]+]]:_(i32), [[USUBO5:%[0-9]+]]:_(i1) = G_USUBO [[UV34]], [[USUBO2]]
+ ; CHECK-NEXT: [[C56:%[0-9]+]]:_(i32) = G_CONSTANT i32 64
+ ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(i32) = G_SUB [[USUBO4]], [[C56]]
+ ; CHECK-NEXT: [[SUB1:%[0-9]+]]:_(i32) = G_SUB [[C56]], [[USUBO4]]
+ ; CHECK-NEXT: [[C57:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP19:%[0-9]+]]:_(i1) = G_ICMP intpred(ult), [[USUBO4]](i32), [[C56]]
+ ; CHECK-NEXT: [[ICMP20:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[USUBO4]](i32), [[C57]]
+ ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(i64) = G_SHL [[FREEZE1]], [[USUBO4]](i32)
+ ; CHECK-NEXT: [[LSHR:%[0-9]+]]:_(i64) = G_LSHR [[FREEZE1]], [[SUB1]](i32)
+ ; CHECK-NEXT: [[SHL1:%[0-9]+]]:_(i64) = G_SHL [[FREEZE2]], [[USUBO4]](i32)
+ ; CHECK-NEXT: [[OR6:%[0-9]+]]:_(i64) = G_OR [[LSHR]], [[SHL1]]
+ ; CHECK-NEXT: [[C58:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[SHL2:%[0-9]+]]:_(i64) = G_SHL [[FREEZE1]], [[SUB]](i32)
+ ; CHECK-NEXT: [[SELECT20:%[0-9]+]]:_(i64) = G_SELECT [[ICMP19]](i1), [[SHL]], [[C58]]
+ ; CHECK-NEXT: [[SELECT21:%[0-9]+]]:_(i64) = G_SELECT [[ICMP19]](i1), [[OR6]], [[SHL2]]
+ ; CHECK-NEXT: [[SELECT22:%[0-9]+]]:_(i64) = G_SELECT [[ICMP20]](i1), [[FREEZE2]], [[SELECT21]]
+ ; CHECK-NEXT: G_BRCOND [[UADDE17]](i1), %bb.4
+ ; CHECK-NEXT: G_BR %bb.2
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.2.udiv-preheader:
+ ; CHECK-NEXT: successors: %bb.3(0x80000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[C59:%[0-9]+]]:_(i32) = G_CONSTANT i32 64
+ ; CHECK-NEXT: [[SUB2:%[0-9]+]]:_(i32) = G_SUB [[UADDO4]], [[C59]]
+ ; CHECK-NEXT: [[SUB3:%[0-9]+]]:_(i32) = G_SUB [[C59]], [[UADDO4]]
+ ; CHECK-NEXT: [[C60:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP21:%[0-9]+]]:_(i1) = G_ICMP intpred(ult), [[UADDO4]](i32), [[C59]]
+ ; CHECK-NEXT: [[ICMP22:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[UADDO4]](i32), [[C60]]
+ ; CHECK-NEXT: [[LSHR1:%[0-9]+]]:_(i64) = G_LSHR [[FREEZE2]], [[UADDO4]](i32)
+ ; CHECK-NEXT: [[LSHR2:%[0-9]+]]:_(i64) = G_LSHR [[FREEZE1]], [[UADDO4]](i32)
+ ; CHECK-NEXT: [[SHL3:%[0-9]+]]:_(i64) = G_SHL [[FREEZE2]], [[SUB3]](i32)
+ ; CHECK-NEXT: [[OR7:%[0-9]+]]:_(i64) = G_OR [[LSHR2]], [[SHL3]]
+ ; CHECK-NEXT: [[C61:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[LSHR3:%[0-9]+]]:_(i64) = G_LSHR [[FREEZE2]], [[SUB2]](i32)
+ ; CHECK-NEXT: [[SELECT23:%[0-9]+]]:_(i64) = G_SELECT [[ICMP21]](i1), [[OR7]], [[LSHR3]]
+ ; CHECK-NEXT: [[SELECT24:%[0-9]+]]:_(i64) = G_SELECT [[ICMP22]](i1), [[FREEZE1]], [[SELECT23]]
+ ; CHECK-NEXT: [[SELECT25:%[0-9]+]]:_(i64) = G_SELECT [[ICMP21]](i1), [[LSHR1]], [[C61]]
+ ; CHECK-NEXT: [[UV36:%[0-9]+]]:_(i32), [[UV37:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C6]](i64)
+ ; CHECK-NEXT: [[UADDO6:%[0-9]+]]:_(i32), [[UADDO7:%[0-9]+]]:_(i1) = G_UADDO [[FREEZE]], [[UV36]]
+ ; CHECK-NEXT: [[UADDE18:%[0-9]+]]:_(i32), [[UADDE19:%[0-9]+]]:_(i1) = G_UADDE [[C21]], [[UV37]], [[UADDO7]]
+ ; CHECK-NEXT: [[UV38:%[0-9]+]]:_(i32), [[UV39:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C22]](i64)
+ ; CHECK-NEXT: [[UV40:%[0-9]+]]:_(i32), [[UV41:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C7]](i64)
+ ; CHECK-NEXT: [[UADDE20:%[0-9]+]]:_(i32), [[UADDE21:%[0-9]+]]:_(i1) = G_UADDE [[UV38]], [[UV40]], [[UADDE19]]
+ ; CHECK-NEXT: [[UADDE22:%[0-9]+]]:_(i32), [[UADDE23:%[0-9]+]]:_(i1) = G_UADDE [[UV39]], [[UV41]], [[UADDE21]]
+ ; CHECK-NEXT: [[MV7:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[UADDO4]](i32), [[UADDE12]](i32)
+ ; CHECK-NEXT: [[MV8:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[UADDE14]](i32), [[UADDE16]](i32)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.3.udiv-do-while:
+ ; CHECK-NEXT: successors: %bb.4(0x04000000), %bb.3(0x7c000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[PHI:%[0-9]+]]:_(i64) = G_PHI [[C2]](i64), %bb.2, %180(i64), %bb.3
+ ; CHECK-NEXT: [[PHI1:%[0-9]+]]:_(i64) = G_PHI [[C3]](i64), %bb.2, %181(i64), %bb.3
+ ; CHECK-NEXT: [[PHI2:%[0-9]+]]:_(i64) = G_PHI [[MV7]](i64), %bb.2, %130(i64), %bb.3
+ ; CHECK-NEXT: [[PHI3:%[0-9]+]]:_(i64) = G_PHI [[MV8]](i64), %bb.2, %132(i64), %bb.3
+ ; CHECK-NEXT: [[PHI4:%[0-9]+]]:_(i64) = G_PHI [[SELECT24]](i64), %bb.2, %152(i64), %bb.3
+ ; CHECK-NEXT: [[PHI5:%[0-9]+]]:_(i64) = G_PHI [[SELECT25]](i64), %bb.2, %154(i64), %bb.3
+ ; CHECK-NEXT: [[PHI6:%[0-9]+]]:_(i64) = G_PHI [[SELECT20]](i64), %bb.2, %215(i64), %bb.3
+ ; CHECK-NEXT: [[PHI7:%[0-9]+]]:_(i64) = G_PHI [[SELECT22]](i64), %bb.2, %216(i64), %bb.3
+ ; CHECK-NEXT: [[C62:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[SHL4:%[0-9]+]]:_(i64) = G_SHL [[PHI4]], [[C62]](i32)
+ ; CHECK-NEXT: [[C63:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[SHL5:%[0-9]+]]:_(i64) = G_SHL [[PHI5]], [[C63]](i32)
+ ; CHECK-NEXT: [[C64:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[LSHR4:%[0-9]+]]:_(i64) = G_LSHR [[PHI4]], [[C64]](i32)
+ ; CHECK-NEXT: [[OR8:%[0-9]+]]:_(i64) = G_OR [[SHL5]], [[LSHR4]]
+ ; CHECK-NEXT: [[C65:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[LSHR5:%[0-9]+]]:_(i64) = G_LSHR [[PHI7]], [[C65]](i32)
+ ; CHECK-NEXT: [[C66:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[OR9:%[0-9]+]]:_(i64) = G_OR [[SHL4]], [[LSHR5]]
+ ; CHECK-NEXT: [[OR10:%[0-9]+]]:_(i64) = G_OR [[OR8]], [[C66]]
+ ; CHECK-NEXT: [[C67:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[SHL6:%[0-9]+]]:_(i64) = G_SHL [[PHI6]], [[C67]](i32)
+ ; CHECK-NEXT: [[C68:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[SHL7:%[0-9]+]]:_(i64) = G_SHL [[PHI7]], [[C68]](i32)
+ ; CHECK-NEXT: [[C69:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[LSHR6:%[0-9]+]]:_(i64) = G_LSHR [[PHI6]], [[C69]](i32)
+ ; CHECK-NEXT: [[OR11:%[0-9]+]]:_(i64) = G_OR [[SHL7]], [[LSHR6]]
+ ; CHECK-NEXT: [[OR12:%[0-9]+]]:_(i64) = G_OR [[PHI]], [[SHL6]]
+ ; CHECK-NEXT: [[OR13:%[0-9]+]]:_(i64) = G_OR [[PHI1]], [[OR11]]
+ ; CHECK-NEXT: [[UV42:%[0-9]+]]:_(i32), [[UV43:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[OR9]](i64)
+ ; CHECK-NEXT: [[USUBO6:%[0-9]+]]:_(i32), [[USUBO7:%[0-9]+]]:_(i1) = G_USUBO [[UADDO6]], [[UV42]]
+ ; CHECK-NEXT: [[USUBE12:%[0-9]+]]:_(i32), [[USUBE13:%[0-9]+]]:_(i1) = G_USUBE [[UADDE18]], [[UV43]], [[USUBO7]]
+ ; CHECK-NEXT: [[UV44:%[0-9]+]]:_(i32), [[UV45:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[OR10]](i64)
+ ; CHECK-NEXT: [[USUBE14:%[0-9]+]]:_(i32), [[USUBE15:%[0-9]+]]:_(i1) = G_USUBE [[UADDE20]], [[UV44]], [[USUBE13]]
+ ; CHECK-NEXT: [[USUBE16:%[0-9]+]]:_(i32), [[USUBE17:%[0-9]+]]:_(i1) = G_USUBE [[UADDE22]], [[UV45]], [[USUBE15]]
+ ; CHECK-NEXT: [[MV9:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[USUBE14]](i32), [[USUBE16]](i32)
+ ; CHECK-NEXT: [[C70:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[ASHR:%[0-9]+]]:_(i64) = G_ASHR [[MV9]], [[C70]](i32)
+ ; CHECK-NEXT: [[C71:%[0-9]+]]:_(i32) = G_CONSTANT i32 63
+ ; CHECK-NEXT: [[ASHR1:%[0-9]+]]:_(i64) = G_ASHR [[MV9]], [[C71]](i32)
+ ; CHECK-NEXT: [[AND3:%[0-9]+]]:_(i64) = G_AND [[ASHR]], [[C]]
+ ; CHECK-NEXT: [[AND4:%[0-9]+]]:_(i64) = G_AND [[ASHR1]], [[C1]]
+ ; CHECK-NEXT: [[AND5:%[0-9]+]]:_(i64) = G_AND [[ASHR]], [[MV4]]
+ ; CHECK-NEXT: [[AND6:%[0-9]+]]:_(i64) = G_AND [[ASHR1]], [[C22]]
+ ; CHECK-NEXT: [[UV46:%[0-9]+]]:_(i32), [[UV47:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[OR9]](i64)
+ ; CHECK-NEXT: [[UV48:%[0-9]+]]:_(i32), [[UV49:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[AND5]](i64)
+ ; CHECK-NEXT: [[USUBO8:%[0-9]+]]:_(i32), [[USUBO9:%[0-9]+]]:_(i1) = G_USUBO [[UV46]], [[UV48]]
+ ; CHECK-NEXT: [[USUBE18:%[0-9]+]]:_(i32), [[USUBE19:%[0-9]+]]:_(i1) = G_USUBE [[UV47]], [[UV49]], [[USUBO9]]
+ ; CHECK-NEXT: [[MV10:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[USUBO8]](i32), [[USUBE18]](i32)
+ ; CHECK-NEXT: [[UV50:%[0-9]+]]:_(i32), [[UV51:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[OR10]](i64)
+ ; CHECK-NEXT: [[UV52:%[0-9]+]]:_(i32), [[UV53:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[AND6]](i64)
+ ; CHECK-NEXT: [[USUBE20:%[0-9]+]]:_(i32), [[USUBE21:%[0-9]+]]:_(i1) = G_USUBE [[UV50]], [[UV52]], [[USUBE19]]
+ ; CHECK-NEXT: [[USUBE22:%[0-9]+]]:_(i32), [[USUBE23:%[0-9]+]]:_(i1) = G_USUBE [[UV51]], [[UV53]], [[USUBE21]]
+ ; CHECK-NEXT: [[MV11:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[USUBE20]](i32), [[USUBE22]](i32)
+ ; CHECK-NEXT: [[UV54:%[0-9]+]]:_(i32), [[UV55:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[PHI2]](i64)
+ ; CHECK-NEXT: [[UV56:%[0-9]+]]:_(i32), [[UV57:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C6]](i64)
+ ; CHECK-NEXT: [[UADDO8:%[0-9]+]]:_(i32), [[UADDO9:%[0-9]+]]:_(i1) = G_UADDO [[UV54]], [[UV56]]
+ ; CHECK-NEXT: [[UADDE24:%[0-9]+]]:_(i32), [[UADDE25:%[0-9]+]]:_(i1) = G_UADDE [[UV55]], [[UV57]], [[UADDO9]]
+ ; CHECK-NEXT: [[MV12:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[UADDO8]](i32), [[UADDE24]](i32)
+ ; CHECK-NEXT: [[UV58:%[0-9]+]]:_(i32), [[UV59:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[PHI3]](i64)
+ ; CHECK-NEXT: [[UV60:%[0-9]+]]:_(i32), [[UV61:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[C7]](i64)
+ ; CHECK-NEXT: [[UADDE26:%[0-9]+]]:_(i32), [[UADDE27:%[0-9]+]]:_(i1) = G_UADDE [[UV58]], [[UV60]], [[UADDE25]]
+ ; CHECK-NEXT: [[UADDE28:%[0-9]+]]:_(i32), [[UADDE29:%[0-9]+]]:_(i1) = G_UADDE [[UV59]], [[UV61]], [[UADDE27]]
+ ; CHECK-NEXT: [[MV13:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[UADDE26]](i32), [[UADDE28]](i32)
+ ; CHECK-NEXT: [[C72:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[XOR6:%[0-9]+]]:_(i64) = G_XOR [[MV12]], [[C2]]
+ ; CHECK-NEXT: [[XOR7:%[0-9]+]]:_(i64) = G_XOR [[MV13]], [[C3]]
+ ; CHECK-NEXT: [[OR14:%[0-9]+]]:_(i64) = G_OR [[XOR6]], [[XOR7]]
+ ; CHECK-NEXT: [[ICMP23:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[OR14]](i64), [[C72]]
+ ; CHECK-NEXT: G_BRCOND [[ICMP23]](i1), %bb.4
+ ; CHECK-NEXT: G_BR %bb.3
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.4.udiv-loop-exit:
+ ; CHECK-NEXT: successors: %bb.5(0x80000000)
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: [[PHI8:%[0-9]+]]:_(i64) = G_PHI [[C2]](i64), %bb.1, [[AND3]](i64), %bb.3
+ ; CHECK-NEXT: [[PHI9:%[0-9]+]]:_(i64) = G_PHI [[SELECT20]](i64), %bb.1, [[OR12]](i64), %bb.3
+ ; CHECK-NEXT: [[C73:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[SHL8:%[0-9]+]]:_(i64) = G_SHL [[PHI9]], [[C73]](i32)
+ ; CHECK-NEXT: [[OR15:%[0-9]+]]:_(i64) = G_OR [[PHI8]], [[SHL8]]
+ ; CHECK-NEXT: {{ $}}
+ ; CHECK-NEXT: bb.5.udiv-end:
+ ; CHECK-NEXT: [[PHI10:%[0-9]+]]:_(i64) = G_PHI [[OR15]](i64), %bb.4, [[SELECT18]](i64), %bb.0
+ ; CHECK-NEXT: [[C74:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C75:%[0-9]+]]:_(i64) = G_CONSTANT i64 1
+ ; CHECK-NEXT: [[SELECT26:%[0-9]+]]:_(i64) = G_SELECT [[ICMP4]](i1), [[C75]], [[C74]]
+ ; CHECK-NEXT: [[UV62:%[0-9]+]]:_(i32), [[UV63:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[PHI10]](i64)
+ ; CHECK-NEXT: [[UV64:%[0-9]+]]:_(i32), [[UV65:%[0-9]+]]:_(i32) = G_UNMERGE_VALUES [[SELECT26]](i64)
+ ; CHECK-NEXT: [[UADDO10:%[0-9]+]]:_(i32), [[UADDO11:%[0-9]+]]:_(i1) = G_UADDO [[UV62]], [[UV64]]
+ ; CHECK-NEXT: [[UADDE30:%[0-9]+]]:_(i32), [[UADDE31:%[0-9]+]]:_(i1) = G_UADDE [[UV63]], [[UV65]], [[UADDO11]]
+ ; CHECK-NEXT: [[MV14:%[0-9]+]]:_(i64) = G_MERGE_VALUES [[UADDO10]](i32), [[UADDE30]](i32)
+ ; CHECK-NEXT: G_STORE [[MV14]](i64), [[C8]](p3) :: (store (i64) into `ptr addrspace(3) null`, align 2147483648, addrspace 3)
+ ; CHECK-NEXT: ret
+ bb.1..preheader3_udiv-special-cases:
+ %0:reg64b(i64) = loadParam_i64 0, 0
+ %1:reg64b(i64) = loadParam_i64 1, 0
+ %4:_(i128) = G_CONSTANT i128 1
+ %15:_(i128) = G_CONSTANT i128 0
+ %22:_(i128) = G_CONSTANT i128 127
+ %34:_(i128) = G_CONSTANT i128 -1
+ %60:_(p3) = G_CONSTANT i32 0
+ %2:_(i32) = G_INTRINSIC intrinsic(@llvm.pisa.local.size.x)
+ %3:_(i128) = G_ZEXT %0(i64)
+ %5:_(i128) = nuw nsw G_ADD %3, %4
+ %6:_(i128) = G_ZEXT %1(i64)
+ %7:_(i128) = G_UMAX %5, %6
+ %8:_(i1) = G_ICMP intpred(uge), %0(i64), %1
+ %9:_(i128) = G_ZEXT %8(i1)
+ %10:_(i128) = nuw nsw G_ADD %9, %6
+ %11:_(i128) = nsw G_SUB %7, %10
+ %61:_(i32) = G_FREEZE %2
+ %12:_(i128) = G_ZEXT %61(i32)
+ %14:_(i128) = G_FREEZE %11
+ %16:_(i1) = G_ICMP intpred(eq), %12(i128), %15
+ %17:_(i1) = G_ICMP intpred(eq), %14(i128), %15
+ %18:_(i1) = G_OR %16, %17
+ %19:_(i128) = G_CTLZ_ZERO_POISON %12(i128)
+ %20:_(i128) = G_CTLZ_ZERO_POISON %14(i128)
+ %21:_(i128) = G_SUB %19, %20
+ %23:_(i1) = G_ICMP intpred(ugt), %21(i128), %22
+ %63:_(i1) = G_FREEZE %23
+ %24:_(i1) = G_OR %18, %63
+ %26:_(i1) = G_ICMP intpred(eq), %21(i128), %22
+ %27:_(i128) = G_SELECT %24(i1), %15, %14
+ %65:_(i1) = G_FREEZE %26
+ %28:_(i1) = G_OR %24, %65
+ G_BRCOND %28(i1), %bb.6
+ G_BR %bb.2
+
+ bb.2.udiv-bb1:
+ %29:_(i128), %30:_(i1) = G_UADDO %21, %4
+ %31:_(i128) = G_SUB %22, %21
+ %32:_(i128) = G_SHL %14, %31(i128)
+ G_BRCOND %30(i1), %bb.5
+ G_BR %bb.3
+
+ bb.3.udiv-preheader:
+ %33:_(i128) = G_LSHR %14, %29(i128)
+ %35:_(i128) = G_ADD %12, %34
+
+ bb.4.udiv-do-while:
+ successors: %bb.5(0x04000000), %bb.4(0x7c000000)
+
+ %36:_(i128) = G_PHI %15(i128), %bb.3, %47(i128), %bb.4
+ %37:_(i128) = G_PHI %29(i128), %bb.3, %50(i128), %bb.4
+ %38:_(i128) = G_PHI %33(i128), %bb.3, %49(i128), %bb.4
+ %39:_(i128) = G_PHI %32(i128), %bb.3, %44(i128), %bb.4
+ %40:_(i128) = G_SHL %38, %4(i128)
+ %41:_(i128) = G_LSHR %39, %22(i128)
+ %42:_(i128) = G_OR %40, %41
+ %43:_(i128) = G_SHL %39, %4(i128)
+ %44:_(i128) = G_OR %36, %43
+ %45:_(i128) = G_SUB %35, %42
+ %46:_(i128) = G_ASHR %45, %22(i128)
+ %47:_(i128) = G_AND %46, %4
+ %48:_(i128) = G_AND %46, %12
+ %49:_(i128) = G_SUB %42, %48
+ %50:_(i128) = G_ADD %37, %34
+ %51:_(i1) = G_ICMP intpred(eq), %50(i128), %15
+ G_BRCOND %51(i1), %bb.5
+ G_BR %bb.4
+
+ bb.5.udiv-loop-exit:
+ %52:_(i128) = G_PHI %15(i128), %bb.2, %47(i128), %bb.4
+ %53:_(i128) = G_PHI %32(i128), %bb.2, %44(i128), %bb.4
+ %54:_(i128) = G_SHL %53, %4(i128)
+ %55:_(i128) = G_OR %52, %54
+
+ bb.6.udiv-end:
+ %56:_(i128) = G_PHI %55(i128), %bb.5, %27(i128), %bb.1
+ %57:_(i128) = G_ZEXT %8(i1)
+ %58:_(i128) = G_ADD %56, %57
+ %59:_(i64) = G_TRUNC %58(i128)
+ G_STORE %59(i64), %60(p3) :: (store (i64) into `ptr addrspace(3) null`, align 2147483648, addrspace 3)
+ ret
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir
new file mode 100644
index 00000000000000..e95a5b0a44db98
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir
@@ -0,0 +1,53 @@
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+
+# Verify UNMERGE lowering case where both SrcTy and DstTy are vectors with over 4 elements.
+
+---
+name: test_unmerge_vector
+legalized: false
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: {{^}}name: test_unmerge_vector
+ ; CHECK: [[REG0:%[0-9]+]]:regv2_32b(<2 x s32>) = loadParam_v2i32 0, 0
+ ; CHECK: [[REG1:%[0-9]+]]:regv2_32b(<2 x s32>) = loadParam_v2i32 0, 8
+ ; CHECK: [[REG2:%[0-9]+]]:regv2_32b(<2 x s32>) = loadParam_v2i32 0, 16
+ %2:regv2_32b(<2 x s32>) = loadParam_v2i32 0, 0
+ %3:regv2_32b(<2 x s32>) = loadParam_v2i32 0, 8
+ %4:regv2_32b(<2 x s32>) = loadParam_v2i32 0, 16
+
+ ; CHECK: [[LO0:%[0-9]+]]:_(s32), [[HI0:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[REG0]]
+ ; CHECK: [[LO1:%[0-9]+]]:_(s32), [[HI1:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[REG1]]
+ ; CHECK: [[LO2:%[0-9]+]]:_(s32), [[HI2:%[0-9]+]]:_(s32) = G_UNMERGE_VALUES [[REG2]]
+ %1:_(<6 x s32>) = G_CONCAT_VECTORS %2(<2 x s32>), %3(<2 x s32>), %4(<2 x s32>)
+
+ ; CHECK: [[BC0:%[0-9]+]]:_(<4 x i8>) = G_BITCAST [[LO0]]
+ ; CHECK: [[BC1:%[0-9]+]]:_(<4 x i8>) = G_BITCAST [[HI0]]
+ ; CHECK: [[BC2:%[0-9]+]]:_(<4 x i8>) = G_BITCAST [[LO1]]
+ ; CHECK: [[BC3:%[0-9]+]]:_(<4 x i8>) = G_BITCAST [[HI1]]
+ ; CHECK: [[BC4:%[0-9]+]]:_(<4 x i8>) = G_BITCAST [[LO2]]
+ ; CHECK: [[BC5:%[0-9]+]]:_(<4 x i8>) = G_BITCAST [[HI2]]
+ %0:_(<24 x i8>) = G_BITCAST %1(<6 x s32>)
+
+ ; CHECK: [[B0_0:%[0-9]+]]:_(i8), [[B0_1:%[0-9]+]]:_(i8), [[B0_2:%[0-9]+]]:_(i8), [[B0_3:%[0-9]+]]:_(i8) = G_UNMERGE_VALUES [[BC0]]
+ ; CHECK: [[B1_0:%[0-9]+]]:_(i8), [[B1_1:%[0-9]+]]:_(i8), [[B1_2:%[0-9]+]]:_(i8), [[B1_3:%[0-9]+]]:_(i8) = G_UNMERGE_VALUES [[BC1]]
+ ; CHECK: [[B2_0:%[0-9]+]]:_(i8), [[B2_1:%[0-9]+]]:_(i8), [[B2_2:%[0-9]+]]:_(i8), [[B2_3:%[0-9]+]]:_(i8) = G_UNMERGE_VALUES [[BC2]]
+ ; CHECK: [[B3_0:%[0-9]+]]:_(i8), [[B3_1:%[0-9]+]]:_(i8), [[B3_2:%[0-9]+]]:_(i8), [[B3_3:%[0-9]+]]:_(i8) = G_UNMERGE_VALUES [[BC3]]
+ ; CHECK: [[B4_0:%[0-9]+]]:_(i8), [[B4_1:%[0-9]+]]:_(i8), [[B4_2:%[0-9]+]]:_(i8), [[B4_3:%[0-9]+]]:_(i8) = G_UNMERGE_VALUES [[BC4]]
+ ; CHECK: [[B5_0:%[0-9]+]]:_(i8), [[B5_1:%[0-9]+]]:_(i8), [[B5_2:%[0-9]+]]:_(i8), [[B5_3:%[0-9]+]]:_(i8) = G_UNMERGE_VALUES [[BC5]]
+ %5:_(<6 x i8>), %6:_(<6 x i8>), %7:_(<6 x i8>), %8:_(<6 x i8>) = G_UNMERGE_VALUES %0(<24 x i8>)
+ %9:_(i48) = G_BITCAST %5(<6 x i8>)
+ %10:_(i48) = G_BITCAST %6(<6 x i8>)
+ %11:_(i48) = G_BITCAST %7(<6 x i8>)
+ %12:_(i48) = G_BITCAST %8(<6 x i8>)
+ %13:_(<4 x i48>) = G_BUILD_VECTOR %9(i48), %10(i48), %11(i48), %12(i48)
+ %14:_(<3 x i64>) = G_BITCAST %13(<4 x i48>)
+
+ ; CHECK-NOT: {{.*}} <24 x i8>
+ ; CHECK-NOT: {{.*}} <6 x i8>
+
+ %15:_(p1) = G_CONSTANT i64 0
+ G_STORE %14(<3 x i64>), %15(p1) :: (store (<3 x i64>) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
new file mode 100644
index 00000000000000..b491bd641184e8
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
@@ -0,0 +1,30 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# Ensure that G_ZEXT with large destination types is handled without causing a crash.
+
+--- |
+ define i128 @test_zext_big(i16 %arg) {
+ %ret = zext i16 %arg to i128
+ ret i128 %ret
+ }
+...
+---
+name: test_zext_big
+tracksRegLiveness: true
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: test_zext_big
+ ; CHECK: %0:reg16b(i16) = functionParameter_i16 0
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i16) = G_CONSTANT i16 0
+ ; CHECK-NEXT: [[MV:%[0-9]+]]:_(i64) = G_MERGE_VALUES %0(i16), [[C]](i16), [[C]](i16), [[C]](i16)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[MV1:%[0-9]+]]:_(i128) = G_MERGE_VALUES [[MV]](i64), [[C1]](i64)
+ ; CHECK-NEXT: [[BITCAST:%[0-9]+]]:_(<2 x i64>) = G_BITCAST [[MV1]](i128)
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:regv2_64b(<2 x i64>) = COPY [[BITCAST]](<2 x i64>)
+ ; CHECK-NEXT: retValue_v2i64_r [[COPY]](<2 x i64>)
+ %0:reg16b(i16) = functionParameter_i16 0
+ %1:_(i128) = G_ZEXT %0:reg16b(i16)
+ %2:_(<2 x i64>) = G_BITCAST %1:_(i128)
+ %3:regv2_64b(<2 x i64>) = COPY %2:_(<2 x i64>)
+ retValue_v2i64_r %3:regv2_64b(<2 x i64>)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir b/llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir
new file mode 100644
index 00000000000000..6ffd5feac885dc
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir
@@ -0,0 +1,21 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+---
+name: test_bfloat
+tracksRegLiveness: true
+body: |
+ bb.1.entry:
+ ; CHECK-LABEL: name: test_bfloat
+ ; CHECK: %0:reg16b(bf16) = functionParameter_i16 0
+ ; CHECK-NEXT: %1:reg16b(bf16) = functionParameter_i16 1
+ ; ------------vvvvvvvvvvvvvvvv- This handle was generated thanks to updating update_mir_test_checks.py
+ ; CHECK-NEXT: [[FMUL:%[0-9]+]]:_(bf16) = G_FMUL %0, %1
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg16b(i16) = G_BITCAST [[FMUL]](bf16)
+ ; CHECK-NEXT: retValue_i16_r [[COPY]](i16)
+ ; CHECK-NOT: error: {{.*}} expected a machine instruction
+ %0:reg16b(bf16) = functionParameter_i16 0
+ %1:reg16b(bf16) = functionParameter_i16 1
+ %2:_(bf16) = G_FMUL %0:reg16b, %1:reg16b(bf16)
+ %3:reg16b(i16) = G_BITCAST %2:_(bf16)
+ retValue_i16_r %3:reg16b(i16)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
new file mode 100644
index 00000000000000..3f43b136219cae
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
@@ -0,0 +1,67 @@
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# Tests the compare_select PostLegalizerCombiner rule:
+# %cond = G_ICMP intpred(ne), %x(s32), 0
+# %res = G_SELECT %cond(s1), %a(s32), %b(s32)
+# =>
+# %res = G_PISA_SELECT %x(s32), %a(s32), %b(s32)
+
+--- |
+ define i32 @compare_select_ne(i32 %x, i32 %a, i32 %b) {
+ %cond = icmp ne i32 %x, 0
+ %res = select i1 %cond, i32 %a, i32 %b
+ ret i32 %res
+ }
+
+ define i32 @compare_select_eq(i32 %x, i32 %a, i32 %b) {
+ %cond = icmp eq i32 %x, 0
+ %res = select i1 %cond, i32 %a, i32 %b
+ ret i32 %res
+ }
+...
+---
+name: compare_select_ne
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: compare_select_ne
+ ; CHECK: %0:reg32b(i32) = functionParameter_i32 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: %2:reg32b(i32) = functionParameter_i32 2
+ ; CHECK-NEXT: %5:_(i32) = G_PISA_SELECT %0, %1, %2
+ ; CHECK-NEXT: %6:reg32b(i32) = COPY %5(i32)
+ ; CHECK-NEXT: retValue_i32_r %6(i32)
+ %0:reg32b(i32) = functionParameter_i32 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:reg32b(i32) = functionParameter_i32 2
+ %3:_(i32) = G_CONSTANT i32 0
+ %4:_(i1) = G_ICMP intpred(ne), %0(i32), %3(i32)
+ %5:_(i32) = G_SELECT %4(i1), %1(i32), %2(i32)
+ %6:reg32b(i32) = COPY %5(i32)
+ retValue_i32_r %6:reg32b(i32)
+...
+---
+name: compare_select_eq
+legalized: true
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: compare_select_eq
+ ; CHECK: %0:reg32b(i32) = functionParameter_i32 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: %2:reg32b(i32) = functionParameter_i32 2
+ ; CHECK-NEXT: %5:_(i32) = G_PISA_SELECT %0, %2, %1
+ ; CHECK-NEXT: %6:reg32b(i32) = COPY %5(i32)
+ ; CHECK-NEXT: retValue_i32_r %6(i32)
+ %0:reg32b(i32) = functionParameter_i32 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %2:reg32b(i32) = functionParameter_i32 2
+ %3:_(i32) = G_CONSTANT i32 0
+ %4:_(i1) = G_ICMP intpred(eq), %0(i32), %3(i32)
+ %5:_(i32) = G_SELECT %4(i1), %1(i32), %2(i32)
+ %6:reg32b(i32) = COPY %5(i32)
+ retValue_i32_r %6:reg32b(i32)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-extract-subvector-partial.mir b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-extract-subvector-partial.mir
new file mode 100644
index 00000000000000..2c8f60aee0b89f
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-extract-subvector-partial.mir
@@ -0,0 +1,38 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# G_EXTRACT_SUBVECTOR fed by a chain of G_INSERT_SUBVECTOR into IMPLICIT_DEF,
+# where the extracted window fully contains the inserts. The postlegalizer
+# combiner rewrites the extract into a smaller insert chain with rebased
+# indices (applyExtractSubvectorPartial). Unlike the shufflevector-lowered
+# shape in combine-extract-subvector-partial.ll (whose chain base is the
+# generic G_IMPLICIT_DEF, which the matcher rejects), this hand-written chain
+# feeds a non-generic IMPLICIT_DEF, so the apply path fires.
+#
+# Here the extract of the upper 8 elements (index 8) sees two <4 x i32> inserts
+# at indices 8 and 12; both are fully inside [8, 16), so the combine fires and
+# rebases them to 0 and 4 in a fresh <8 x i32> chain.
+
+---
+name: extract_subvector_partial_upper
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: extract_subvector_partial_upper
+ ; CHECK: [[C:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 7
+ ; CHECK-NEXT: [[BUILD_VECTOR:%[0-9]+]]:_(<4 x i32>) = G_BUILD_VECTOR [[C1]](i32), [[C1]](i32), [[C1]](i32), [[C1]](i32)
+ ; CHECK-NEXT: [[BUILD_VECTOR1:%[0-9]+]]:_(<4 x i32>) = G_BUILD_VECTOR [[C1]](i32), [[C1]](i32), [[C1]](i32), [[C1]](i32)
+ ; CHECK-NEXT: [[DEF:%[0-9]+]]:_(<8 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[INSERT_SUBVECTOR:%[0-9]+]]:_(<8 x i32>) = G_INSERT_SUBVECTOR [[DEF]], [[BUILD_VECTOR]](<4 x i32>), 0
+ ; CHECK-NEXT: [[INSERT_SUBVECTOR1:%[0-9]+]]:_(<8 x i32>) = G_INSERT_SUBVECTOR [[INSERT_SUBVECTOR]], [[BUILD_VECTOR1]](<4 x i32>), 4
+ ; CHECK-NEXT: G_STORE [[INSERT_SUBVECTOR1]](<8 x i32>), [[C]](p1) :: (store (<8 x i32>))
+ %0:_(p1) = G_CONSTANT i64 0
+ %1:_(i32) = G_CONSTANT i32 7
+ %2:_(<16 x i32>) = IMPLICIT_DEF
+ %3:_(<4 x i32>) = G_BUILD_VECTOR %1:_(i32), %1:_(i32), %1:_(i32), %1:_(i32)
+ %4:_(<4 x i32>) = G_BUILD_VECTOR %1:_(i32), %1:_(i32), %1:_(i32), %1:_(i32)
+ %5:_(<16 x i32>) = G_INSERT_SUBVECTOR %2:_(<16 x i32>), %3:_(<4 x i32>), 8
+ %6:_(<16 x i32>) = G_INSERT_SUBVECTOR %5:_(<16 x i32>), %4:_(<4 x i32>), 12
+ %7:_(<8 x i32>) = G_EXTRACT_SUBVECTOR %6:_(<16 x i32>), 8
+ G_STORE %7:_(<8 x i32>), %0:_(p1) :: (store (<8 x i32>))
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
new file mode 100644
index 00000000000000..e0ab946e993ca2
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
@@ -0,0 +1,52 @@
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# Test that the PostLegalizer does not infinite-loop on shl(or x, const), const.
+#
+# When commute_shift is enabled together with hoist_logic_op_with_same_opcode_hands
+# in the PostLegalizer, the two rules form an infinite cycle on the OR variant:
+#
+# commute_shift:
+# shl (or x, c1), c2 -> or (shl x, c2), (shl c1, c2)
+#
+# hoist_logic_op_with_same_opcode_hands:
+# or (shl x, c2), (shl c1, c2) -> shl (or x, c1), c2
+#
+# Both created SHLs share the same shift-amount register, so
+# hoist_logic_op_with_same_opcode_hands recognises them as "same opcode,
+# same extra operand" and re-folds the OR through the SHL - recreating
+# the original input to commute_shift.
+#
+# commute_shift is therefore excluded from the PostLegalizer
+# (pisa_post_removed_combines).
+
+--- |
+ define i32 @shl_or_const(i32 %x) {
+ %or = or i32 %x, 4
+ %shl = shl i32 %or, 2
+ ret i32 %shl
+ }
+...
+---
+name: shl_or_const
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+ bb.1:
+ ; The pattern must survive the PostLegalizer without hanging.
+ ; With commute_shift excluded, the shl(or) stays as-is.
+ ;
+ ; CHECK-LABEL: name: shl_or_const
+ ; CHECK: %0:reg32b(i32) = functionParameter_i32 0
+ ; CHECK: %c1:reg32b(i32) = G_CONSTANT i32 4
+ ; CHECK: %c2:reg32b(i32) = G_CONSTANT i32 2
+ ; CHECK: %or:reg32b(i32) = G_OR %0, %c1
+ ; CHECK: %shl:reg32b(i32) = G_SHL %or, %c2(i32)
+ ; CHECK: retValue_i32_r %shl(i32)
+ %0:reg32b(i32) = functionParameter_i32 0
+ %c1:reg32b(i32) = G_CONSTANT i32 4
+ %c2:reg32b(i32) = G_CONSTANT i32 2
+ %or:reg32b(i32) = G_OR %0:reg32b, %c1:reg32b
+ %shl:reg32b(i32) = G_SHL %or:reg32b, %c2:reg32b(i32)
+ retValue_i32_r %shl(i32)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir b/llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir
new file mode 100644
index 00000000000000..e194f452990c4c
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir
@@ -0,0 +1,29 @@
+# RUN: llc -march=pisa -run-pass=postrapseudos -verify-machineinstrs %s -o - | FileCheck -check-prefix=PSEUDO %s
+# RUN-CRASHES: llc -march=pisa -start-before=postrapseudos -verify-machineinstrs %s -o - | FileCheck -check-prefix=ASM %s
+--- |
+ define i32 @test_copy_pred(i32 %arg) {
+ ret i32 %arg
+ }
+...
+---
+name: test_copy_pred
+legalized: true
+regBankSelected: true
+tracksRegLiveness: true
+body: |
+ bb.1:
+ ; PSEUDO-LABEL: {{^}}name: test_copy_pred
+ ; ASM-LABEL: @test_copy_pred
+ ; PSEUDO: %pred:pred(i1) = G_TRUNC %0(i32)
+ ; PSEUDO: %4:reg16b(i16) = sel_16_iip %pred(i1), 1, 0
+ ; PSEUDO: %copy:pred(i1) = ucmp_ne_16b_pri %4(i16), 0
+ ; PSEUDO: %rv:reg32b(i32) = G_ANYEXT %copy(i1)
+ ; ASM: mov.16b %r{{[0-9]+}}, 0
+ ; ASM: and.16b %r{{[0-9]+}}, %r{{[0-9]+}}, 1
+ %0:reg32b(i32) = functionParameter_i32 0
+ %pred:pred(i1) = G_TRUNC %0(i32)
+ %copy:pred(i1) = COPY %pred(i1)
+ %rv:reg32b(i32) = G_ANYEXT %copy(i1)
+ retValue_i32_r %rv(i32)
+
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
new file mode 100644
index 00000000000000..456a006bded3bd
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
@@ -0,0 +1,181 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --validate-debuginfo False --version 6
+# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+# The reduce_predicates PreLegalizerCombiner rule rewrites an AND/OR/XOR
+# reduction over the per-lane results of a vector compare into an explicit
+# s32 reduction. Reducing extract_vector_elt lanes of a <N x i1> G_ICMP with
+# G_AND / G_OR / G_XOR exercises each reduction-opcode arm of the rewrite.
+#
+# When the reduction result is wider than i1 and its single use is a G_ICMP
+# against 0 or -1, the rewrite folds that compare into the s32 reduction,
+# inverting the predicate for the -1 case (eq -> ne) and leaving it unchanged
+# for the 0 case.
+
+--- |
+ define i32 @reduce_and(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
+ define i32 @reduce_or(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
+ define i32 @reduce_xor(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
+ define i32 @reduce_and_use_icmp_neg1(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
+ define i32 @reduce_and_use_icmp_zero(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
+...
+---
+name: reduce_and
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: reduce_and
+ ; CHECK: [[DEF:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(<2 x i1>) = G_ICMP intpred(eq), [[DEF]](<2 x i32>), [[DEF1]]
+ ; CHECK-NEXT: [[SEXT:%[0-9]+]]:_(<2 x i32>) = G_SEXT [[ICMP]](<2 x i1>)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[EVEC:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C]](i32)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[EVEC1:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C1]](i32)
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[EVEC]], [[EVEC1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND]](i32), [[C2]]
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:reg32b(i32) = G_ZEXT [[ICMP1]](i1)
+ ; CHECK-NEXT: retValue_i32_r [[ZEXT]](i32)
+ %0:_(<2 x i32>) = G_IMPLICIT_DEF
+ %1:_(<2 x i32>) = G_IMPLICIT_DEF
+ %2:_(<2 x i1>) = G_ICMP intpred(eq), %0(<2 x i32>), %1
+ %3:_(i32) = G_CONSTANT i32 0
+ %4:_(i32) = G_CONSTANT i32 1
+ %5:_(i1) = G_EXTRACT_VECTOR_ELT %2(<2 x i1>), %3(i32)
+ %6:_(i1) = G_EXTRACT_VECTOR_ELT %2(<2 x i1>), %4(i32)
+ %7:_(i1) = G_AND %5, %6
+ %8:reg32b(i32) = G_ZEXT %7(i1)
+ retValue_i32_r %8:reg32b(i32)
+...
+---
+name: reduce_or
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: reduce_or
+ ; CHECK: [[DEF:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(<2 x i1>) = G_ICMP intpred(eq), [[DEF]](<2 x i32>), [[DEF1]]
+ ; CHECK-NEXT: [[SEXT:%[0-9]+]]:_(<2 x i32>) = G_SEXT [[ICMP]](<2 x i1>)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[EVEC:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C]](i32)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[EVEC1:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C1]](i32)
+ ; CHECK-NEXT: [[OR:%[0-9]+]]:_(i32) = G_OR [[EVEC]], [[EVEC1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[OR]](i32), [[C2]]
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:reg32b(i32) = G_ZEXT [[ICMP1]](i1)
+ ; CHECK-NEXT: retValue_i32_r [[ZEXT]](i32)
+ %0:_(<2 x i32>) = G_IMPLICIT_DEF
+ %1:_(<2 x i32>) = G_IMPLICIT_DEF
+ %2:_(<2 x i1>) = G_ICMP intpred(eq), %0(<2 x i32>), %1
+ %3:_(i32) = G_CONSTANT i32 0
+ %4:_(i32) = G_CONSTANT i32 1
+ %5:_(i1) = G_EXTRACT_VECTOR_ELT %2(<2 x i1>), %3(i32)
+ %6:_(i1) = G_EXTRACT_VECTOR_ELT %2(<2 x i1>), %4(i32)
+ %7:_(i1) = G_OR %5, %6
+ %8:reg32b(i32) = G_ZEXT %7(i1)
+ retValue_i32_r %8:reg32b(i32)
+...
+---
+name: reduce_xor
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: reduce_xor
+ ; CHECK: [[DEF:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(<2 x i1>) = G_ICMP intpred(eq), [[DEF]](<2 x i32>), [[DEF1]]
+ ; CHECK-NEXT: [[SEXT:%[0-9]+]]:_(<2 x i32>) = G_SEXT [[ICMP]](<2 x i1>)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[EVEC:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C]](i32)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[EVEC1:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C1]](i32)
+ ; CHECK-NEXT: [[XOR:%[0-9]+]]:_(i32) = G_XOR [[EVEC]], [[EVEC1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[XOR]](i32), [[C2]]
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:reg32b(i32) = G_ZEXT [[ICMP1]](i1)
+ ; CHECK-NEXT: retValue_i32_r [[ZEXT]](i32)
+ %0:_(<2 x i32>) = G_IMPLICIT_DEF
+ %1:_(<2 x i32>) = G_IMPLICIT_DEF
+ %2:_(<2 x i1>) = G_ICMP intpred(eq), %0(<2 x i32>), %1
+ %3:_(i32) = G_CONSTANT i32 0
+ %4:_(i32) = G_CONSTANT i32 1
+ %5:_(i1) = G_EXTRACT_VECTOR_ELT %2(<2 x i1>), %3(i32)
+ %6:_(i1) = G_EXTRACT_VECTOR_ELT %2(<2 x i1>), %4(i32)
+ %7:_(i1) = G_XOR %5, %6
+ %8:reg32b(i32) = G_ZEXT %7(i1)
+ retValue_i32_r %8:reg32b(i32)
+...
+---
+name: reduce_and_use_icmp_neg1
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: reduce_and_use_icmp_neg1
+ ; CHECK: [[DEF:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(<2 x i1>) = G_ICMP intpred(eq), [[DEF]](<2 x i32>), [[DEF1]]
+ ; CHECK-NEXT: [[SEXT:%[0-9]+]]:_(<2 x i32>) = G_SEXT [[ICMP]](<2 x i1>)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[EVEC:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C]](i32)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[EVEC1:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C1]](i32)
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[EVEC]], [[EVEC1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(ne), [[AND]](i32), [[C2]]
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:reg32b(i32) = G_ZEXT [[ICMP1]](i1)
+ ; CHECK-NEXT: retValue_i32_r [[ZEXT]](i32)
+ %0:_(<2 x i32>) = G_IMPLICIT_DEF
+ %1:_(<2 x i32>) = G_IMPLICIT_DEF
+ %2:_(<2 x i1>) = G_ICMP intpred(eq), %0(<2 x i32>), %1
+ %3:_(<2 x i16>) = G_SEXT %2(<2 x i1>)
+ %4:_(i32) = G_CONSTANT i32 0
+ %5:_(i32) = G_CONSTANT i32 1
+ %6:_(i16) = G_EXTRACT_VECTOR_ELT %3(<2 x i16>), %4(i32)
+ %7:_(i16) = G_EXTRACT_VECTOR_ELT %3(<2 x i16>), %5(i32)
+ %8:_(i16) = G_AND %6, %7
+ %9:_(i16) = G_CONSTANT i16 -1
+ %10:_(i1) = G_ICMP intpred(eq), %8(i16), %9(i16)
+ %11:reg32b(i32) = G_ZEXT %10(i1)
+ retValue_i32_r %11(i32)
+...
+---
+name: reduce_and_use_icmp_zero
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: reduce_and_use_icmp_zero
+ ; CHECK: [[DEF:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[DEF1:%[0-9]+]]:_(<2 x i32>) = G_IMPLICIT_DEF
+ ; CHECK-NEXT: [[ICMP:%[0-9]+]]:_(<2 x i1>) = G_ICMP intpred(eq), [[DEF]](<2 x i32>), [[DEF1]]
+ ; CHECK-NEXT: [[SEXT:%[0-9]+]]:_(<2 x i32>) = G_SEXT [[ICMP]](<2 x i1>)
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[EVEC:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C]](i32)
+ ; CHECK-NEXT: [[C1:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[EVEC1:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[SEXT]](<2 x i32>), [[C1]](i32)
+ ; CHECK-NEXT: [[AND:%[0-9]+]]:_(i32) = G_AND [[EVEC]], [[EVEC1]]
+ ; CHECK-NEXT: [[C2:%[0-9]+]]:_(i32) = G_CONSTANT i32 0
+ ; CHECK-NEXT: [[ICMP1:%[0-9]+]]:_(i1) = G_ICMP intpred(eq), [[AND]](i32), [[C2]]
+ ; CHECK-NEXT: [[ZEXT:%[0-9]+]]:reg32b(i32) = G_ZEXT [[ICMP1]](i1)
+ ; CHECK-NEXT: retValue_i32_r [[ZEXT]](i32)
+ %0:_(<2 x i32>) = G_IMPLICIT_DEF
+ %1:_(<2 x i32>) = G_IMPLICIT_DEF
+ %2:_(<2 x i1>) = G_ICMP intpred(eq), %0(<2 x i32>), %1
+ %3:_(<2 x i16>) = G_SEXT %2(<2 x i1>)
+ %4:_(i32) = G_CONSTANT i32 0
+ %5:_(i32) = G_CONSTANT i32 1
+ %6:_(i16) = G_EXTRACT_VECTOR_ELT %3(<2 x i16>), %4(i32)
+ %7:_(i16) = G_EXTRACT_VECTOR_ELT %3(<2 x i16>), %5(i32)
+ %8:_(i16) = G_AND %6, %7
+ %9:_(i16) = G_CONSTANT i16 0
+ %10:_(i1) = G_ICMP intpred(eq), %8(i16), %9(i16)
+ %11:reg32b(i32) = G_ZEXT %10(i1)
+ retValue_i32_r %11(i32)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
new file mode 100644
index 00000000000000..a8d3e9bd863b76
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
@@ -0,0 +1,49 @@
+# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+--- |
+ define i64 @zext_and_mask_rhs(i32 %x) {
+ ret i64 0
+ }
+ define i64 @zext_and_mask_lhs(i32 %x) {
+ ret i64 0
+ }
+...
+
+# --- canonical: G_AND (G_ZEXT x), C ---
+---
+name: zext_and_mask_rhs
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: zext_and_mask_rhs
+ ; The combiner rewrites G_AND(G_ZEXT(x),C) to G_ZEXT(G_AND(x,trunc(C))).
+ ; CHECK: [[AND:%[0-9]+]]:_(i32) = G_AND
+ ; CHECK: [[ZEXT:%[0-9]+]]:_(i64) = G_ZEXT [[AND]](i32)
+ ; CHECK: retValue_i64_r
+ %0:reg32b(i32) = functionParameter_i32 0
+ %1:_(i64) = G_ZEXT %0:reg32b(i32)
+ %mask:_(i64) = G_CONSTANT i64 255
+ %2:_(i64) = G_AND %1:_(i64), %mask:_(i64)
+ %3:reg64b(i64) = COPY %2:_(i64)
+ retValue_i64_r %3:reg64b(i64)
+...
+
+# --- swapped: G_AND C, (G_ZEXT x) -- exercises operand-swap branch ---
+---
+name: zext_and_mask_lhs
+tracksRegLiveness: true
+isSSA: true
+body: |
+ bb.1:
+ ; CHECK-LABEL: name: zext_and_mask_lhs
+ ; CHECK: [[AND2:%[0-9]+]]:_(i32) = G_AND
+ ; CHECK: [[ZEXT2:%[0-9]+]]:_(i64) = G_ZEXT [[AND2]](i32)
+ ; CHECK: retValue_i64_r
+ %0:reg32b(i32) = functionParameter_i32 0
+ %1:_(i64) = G_ZEXT %0:reg32b(i32)
+ %mask:_(i64) = G_CONSTANT i64 65535
+ %2:_(i64) = G_AND %mask:_(i64), %1:_(i64)
+ %3:reg64b(i64) = COPY %2:_(i64)
+ retValue_i64_r %3:reg64b(i64)
+...
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir b/llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir
new file mode 100644
index 00000000000000..c383a57135cfa3
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir
@@ -0,0 +1,37 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
+# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name: retain_extract_subvector
+body: |
+ bb.0:
+ ; CHECK-LABEL: name: retain_extract_subvector
+ ; CHECK: %0:reg32b(p3) = functionParameter_i32 0
+ ; CHECK-NEXT: %1:reg32b(i32) = functionParameter_i32 1
+ ; CHECK-NEXT: [[C:%[0-9]+]]:_(i32) = G_CONSTANT i32 1
+ ; CHECK-NEXT: [[ADD:%[0-9]+]]:_(i32) = G_ADD %1, [[C]]
+ ; CHECK-NEXT: [[LOAD:%[0-9]+]]:_(<8 x i32>) = G_LOAD %0(p3) :: (load (<8 x i32>))
+ ; CHECK-NEXT: [[EXTRACT_SUBVECTOR:%[0-9]+]]:_(<2 x i32>) = G_EXTRACT_SUBVECTOR [[LOAD]](<8 x i32>), 2
+ ; CHECK-NEXT: [[EVEC:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[EXTRACT_SUBVECTOR]](<2 x i32>), %1(i32)
+ ; CHECK-NEXT: [[EVEC1:%[0-9]+]]:_(i32) = G_EXTRACT_VECTOR_ELT [[EXTRACT_SUBVECTOR]](<2 x i32>), [[ADD]](i32)
+ ; CHECK-NEXT: [[ADD1:%[0-9]+]]:_(i32) = G_ADD [[EVEC]], [[EVEC1]]
+ ; CHECK-NEXT: [[COPY:%[0-9]+]]:reg32b(i32) = COPY [[ADD1]](i32)
+ ; CHECK-NEXT: retValue_i32_r [[COPY]](i32)
+ %0:reg32b(p3) = functionParameter_i32 0
+ %1:reg32b(i32) = functionParameter_i32 1
+ %4:_(i32) = G_CONSTANT i32 1
+ %6:_(i32) = G_CONSTANT i32 2
+ %8:_(i32) = G_CONSTANT i32 3
+ %10:_(i32) = G_CONSTANT i32 4
+ %5:_(i32) = G_ADD %1, %4
+ %7:_(i32) = G_ADD %1, %6
+ %9:_(i32) = G_ADD %1, %8
+ %11:_(i32) = G_ADD %1, %10
+ %23:_(<8 x i32>) = G_LOAD %0(p3) :: (load (<8 x i32>))
+ %24:_(<2 x i32>) = G_EXTRACT_SUBVECTOR %23(<8 x i32>), 2
+ %13:_(i32) = G_EXTRACT_VECTOR_ELT %24(<2 x i32>), %1(i32)
+ %14:_(i32) = G_EXTRACT_VECTOR_ELT %24(<2 x i32>), %5(i32)
+ %18:_(i32) = G_ADD %13, %14
+ %22:reg32b(i32) = COPY %18(i32)
+ retValue_i32_r %22(i32)
+...
>From 472ca27ec11a474faa97fe9fe4f1b3a5402f18f9 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Wed, 5 Aug 2026 17:39:33 -0700
Subject: [PATCH 02/14] Fix buildbot issues
---
llvm/lib/Target/AMDGPU/SIFrameLowering.cpp | 1 +
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 1 +
2 files changed, 2 insertions(+)
diff --git a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
index fad04e0c12dde6..88dd2585381ab3 100644
--- a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
@@ -1091,6 +1091,7 @@ bool SIFrameLowering::isSupportedStackID(TargetStackID::Value ID) const {
case TargetStackID::ScalableVector:
case TargetStackID::ScalablePredicateVector:
case TargetStackID::WasmLocal:
+ case TargetStackID::PISAShared:
return false;
}
llvm_unreachable("Invalid TargetStackID::Value");
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 35a36497d4f654..fa1240da879ca3 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -2725,6 +2725,7 @@ bool RISCVFrameLowering::isSupportedStackID(TargetStackID::Value ID) const {
case TargetStackID::SGPRSpill:
case TargetStackID::WasmLocal:
case TargetStackID::ScalablePredicateVector:
+ case TargetStackID::PISAShared:
return false;
}
llvm_unreachable("Invalid TargetStackID::Value");
>From e40e9c5a808b485886ec992487d4142b9e654dd3 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 05:42:47 +0000
Subject: [PATCH 03/14] Do not use auto
---
.../llvm/TargetParser/PISATargetParser.h | 3 +-
llvm/lib/Target/PISA/PISACallLowering.cpp | 143 +--
llvm/lib/Target/PISA/PISAISelLowering.cpp | 41 +-
llvm/lib/Target/PISA/PISALegalizerInfo.cpp | 1012 +++++++++--------
.../lib/Target/PISA/PISAMachineFunctionInfo.h | 2 +-
.../Target/PISA/PISAPostLegalizerCombiner.cpp | 364 +++---
.../Target/PISA/PISAPreLegalizerCombiner.cpp | 405 +++----
llvm/lib/Target/PISA/PISARegisterBankInfo.cpp | 2 +-
llvm/lib/Target/PISA/PISASubtarget.cpp | 2 +-
llvm/lib/Target/PISA/PISATargetMachine.cpp | 6 +-
llvm/lib/Target/PISA/PISATargetMachine.h | 4 +-
llvm/lib/TargetParser/PISATargetParser.cpp | 2 +-
12 files changed, 1032 insertions(+), 954 deletions(-)
diff --git a/llvm/include/llvm/TargetParser/PISATargetParser.h b/llvm/include/llvm/TargetParser/PISATargetParser.h
index e9d5cd5bb1d4ad..d78319f801914f 100644
--- a/llvm/include/llvm/TargetParser/PISATargetParser.h
+++ b/llvm/include/llvm/TargetParser/PISATargetParser.h
@@ -42,7 +42,8 @@ inline PISATargetInfo getPISATargetInfo(StringRef Name) {
#include "PISATargetParser.def"
#undef PISA_TARGET
};
- auto *It = llvm::find_if(Info, [&Name](const PISATargetInfo &Entry) {
+ const PISATargetInfo *It =
+ llvm::find_if(Info, [&Name](const PISATargetInfo &Entry) {
return Entry.Name == Name;
});
return It == std::end(Info) ? DefaultInfo : *It;
diff --git a/llvm/lib/Target/PISA/PISACallLowering.cpp b/llvm/lib/Target/PISA/PISACallLowering.cpp
index 6928ed63d3aabb..1868290306b12a 100644
--- a/llvm/lib/Target/PISA/PISACallLowering.cpp
+++ b/llvm/lib/Target/PISA/PISACallLowering.cpp
@@ -34,13 +34,13 @@ bool PISACallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
if (VRegs.size() > 1)
return false;
if (Val) {
- auto &DL = MIRBuilder.getDataLayout();
- const auto &STI = MIRBuilder.getMF().getSubtarget();
+ const DataLayout &DL = MIRBuilder.getDataLayout();
+ const TargetSubtargetInfo &STI = MIRBuilder.getMF().getSubtarget();
unsigned Op = 0;
- auto *Ty = Val->getType();
- auto VReg = VRegs[0];
+ Type *Ty = Val->getType();
+ Register VReg = VRegs[0];
if (Ty->isVectorTy()) {
- auto *VTy = cast<FixedVectorType>(Ty);
+ FixedVectorType *VTy = cast<FixedVectorType>(Ty);
unsigned NumElts = VTy->getNumElements();
unsigned EltSize = DL.getTypeSizeInBits(Ty->getScalarType());
switch (EltSize) {
@@ -139,16 +139,21 @@ bool PISACallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
case 1: // change i1 to i16 (see lowerCall())
{
const LLT I16 = LLT::integer(16);
- auto Dst = MIRBuilder.getMRI()->createGenericVirtualRegister(I16);
- auto &MF = MIRBuilder.getMF();
- auto &F = MF.getFunction();
+ Register Dst =
+ MIRBuilder.getMRI()->createGenericVirtualRegister(I16);
+ MachineFunction &MF = MIRBuilder.getMF();
+ Function &F = MF.getFunction();
const DataLayout &DL = MF.getDataLayout();
ArgInfo RetInfo(VReg, *Val, 0);
setArgFlags(RetInfo, AttributeList::ReturnIndex, DL, F);
- auto Sext = llvm::any_of(
- RetInfo.Flags, [](const auto &Flag) { return Flag.isSExt(); });
- auto Zext = llvm::any_of(
- RetInfo.Flags, [](const auto &Flag) { return Flag.isZExt(); });
+ bool Sext = llvm::any_of(RetInfo.Flags,
+ [](const ISD::ArgFlagsTy &Flag) {
+ return Flag.isSExt();
+ });
+ bool Zext = llvm::any_of(RetInfo.Flags,
+ [](const ISD::ArgFlagsTy &Flag) {
+ return Flag.isZExt();
+ });
if (Sext) {
MIRBuilder.buildSExt(Dst, VReg);
} else if (Zext) {
@@ -185,8 +190,8 @@ bool PISACallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
// there is an expectation of no register class being assigned.
// Having an extra copy here eliminates the problem; copy itself
// will be removed during instruction selection.
- auto *MRI = MIRBuilder.getMRI();
- auto Tmp = MRI->createGenericVirtualRegister(MRI->getType(VReg));
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
+ Register Tmp = MRI->createGenericVirtualRegister(MRI->getType(VReg));
MIRBuilder.buildCopy(Tmp, VReg);
MIRBuilder.buildInstr(Op).addUse(Tmp).constrainAllUses(
MIRBuilder.getTII(), *STI.getRegisterInfo(), *STI.getRegBankInfo());
@@ -200,18 +205,19 @@ bool PISACallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
const Function &F,
ArrayRef<ArrayRef<Register>> VRegs,
FunctionLoweringInfo &FLI) const {
- auto *MRI = MIRBuilder.getMRI();
- auto &MF = MIRBuilder.getMF();
- auto &Ctx = F.getContext();
- auto *MFInfo = MF.getInfo<PISAMachineFunctionInfo>();
- auto &DL = F.getParent()->getDataLayout();
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
+ MachineFunction &MF = MIRBuilder.getMF();
+ LLVMContext &Ctx = F.getContext();
+ PISAMachineFunctionInfo *MFInfo = MF.getInfo<PISAMachineFunctionInfo>();
+ const DataLayout &DL = F.getParent()->getDataLayout();
bool IsKernel = (F.getCallingConv() == CallingConv::PISA_KERNEL);
- for (const auto [i, Arg] : llvm::enumerate(F.args())) {
+ for (const Argument &Arg : F.args()) {
+ unsigned i = Arg.getArgNo();
assert(VRegs[i].size() == 1 && "Formal arg has multiple vregs");
ArgInfo OrigArg{VRegs[i], Arg, static_cast<unsigned>(i)};
setArgFlags(OrigArg, i + AttributeList::FirstArgIndex, DL, F);
- auto *ArgType = OrigArg.OrigValue->getType();
+ Type *ArgType = OrigArg.OrigValue->getType();
const bool IsByRef = ArgType->isPointerTy() && OrigArg.Flags[0].isByRef();
const unsigned ArgSize = IsByRef
? OrigArg.Flags[0].getByRefSize()
@@ -228,11 +234,11 @@ bool PISACallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
loadParamWithOpcode(MIRBuilder, F, VRegs[i][0], ArgType, Op,
Arg.getArgNo(), 0);
} else if (IsKernel && ArgType->isVectorTy()) {
- auto *VectorTy = cast<FixedVectorType>(ArgType);
- auto NumElts = VectorTy->getNumElements();
- auto *EltTy = VectorTy->getElementType();
- auto EltSize = DL.getTypeSizeInBits(EltTy);
- auto Split = (NumElts > 4) || ((NumElts == 3) && (EltSize != 32)) ||
+ FixedVectorType *VectorTy = cast<FixedVectorType>(ArgType);
+ unsigned NumElts = VectorTy->getNumElements();
+ Type *EltTy = VectorTy->getElementType();
+ unsigned EltSize = DL.getTypeSizeInBits(EltTy);
+ bool Split = (NumElts > 4) || ((NumElts == 3) && (EltSize != 32)) ||
((NumElts == 4) && (EltSize == 64)) || (NumElts == 1);
if (Split) {
// handle odd-sized and large kernel args, e.g.
@@ -241,17 +247,17 @@ bool PISACallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
// loadParam_8b @[arg+1] loadParam_v4_32b @[arg+16]
// loadParam_8b @[arg+2] buildVector(<8 x i32>)
// buildVector(<3 x i8>) bitcast(<16 x i16)
- auto TargetReg = VRegs[i][0];
+ Register TargetReg = VRegs[i][0];
- const auto *EltRegClass =
+ const TargetRegisterClass *EltRegClass =
(EltSize == 8
? &PISA::Reg8bRegClass
: (EltSize == 16 ? &PISA::Reg16bRegClass
: (EltSize == 32 ? &PISA::Reg32bRegClass
: &PISA::Reg64bRegClass)));
- auto TotalSize = NumElts * EltSize;
- auto EltLLT = LLT::integer(EltSize);
- auto I32 = LLT::integer(32);
+ unsigned TotalSize = NumElts * EltSize;
+ LLT EltLLT = LLT::integer(EltSize);
+ LLT I32 = LLT::integer(32);
if (NumElts <= 4) {
// do not group
} else if (TotalSize % 128 == 0) { // 4 x i32
@@ -279,16 +285,16 @@ bool PISACallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
SmallVector<Register, 4> Regs;
for (unsigned I = 0; I < NumElts; I++) {
- auto Reg = MRI->createGenericVirtualRegister(EltLLT);
+ Register Reg = MRI->createGenericVirtualRegister(EltLLT);
MRI->setRegClass(Reg, EltRegClass);
Op = getLoadParamOpcode(MIRBuilder, F, Reg, EltTy);
loadParamWithOpcode(MIRBuilder, F, Reg, EltTy, Op, Arg.getArgNo(),
I * EltLLT.getSizeInBytes());
if (EltTy->isPointerTy()) {
// ld.param loads a scalar value, so convert to ptr here
- auto AS = cast<PointerType>(EltTy)->getAddressSpace();
- auto PtrLLT = LLT::pointer(AS, EltSize);
- auto CastReg = MRI->createGenericVirtualRegister(PtrLLT);
+ unsigned AS = cast<PointerType>(EltTy)->getAddressSpace();
+ LLT PtrLLT = LLT::pointer(AS, EltSize);
+ Register CastReg = MRI->createGenericVirtualRegister(PtrLLT);
MRI->setRegClass(CastReg, EltRegClass);
MIRBuilder.buildIntToPtr(CastReg, Reg);
Regs.push_back(CastReg);
@@ -296,11 +302,11 @@ bool PISACallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
// ld.param loads an integer value; bitcast to float so that
// G_BUILD_VECTOR element types match the result vector element
// type.
- auto FloatLLT = EltTy->isBFloatTy() ? LLT::bfloat16()
- : EltSize == 16 ? LLT::float16()
- : EltSize == 32 ? LLT::float32()
- : LLT::float64();
- auto CastReg = MRI->createGenericVirtualRegister(FloatLLT);
+ LLT FloatLLT = EltTy->isBFloatTy() ? LLT::bfloat16()
+ : EltSize == 16 ? LLT::float16()
+ : EltSize == 32 ? LLT::float32()
+ : LLT::float64();
+ Register CastReg = MRI->createGenericVirtualRegister(FloatLLT);
MRI->setRegClass(CastReg, EltRegClass);
MIRBuilder.buildBitcast(CastReg, Reg);
Regs.push_back(CastReg);
@@ -339,18 +345,19 @@ void PISACallLowering::loadParamWithOpcode(MachineIRBuilder &MIRBuilder,
const Register &VReg, Type *ArgType,
unsigned Opcode, unsigned ArgNo,
unsigned Offset) const {
- auto *MRI = MIRBuilder.getMRI();
- auto &DL = F.getParent()->getDataLayout();
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
+ const DataLayout &DL = F.getParent()->getDataLayout();
bool IsKernel = (F.getCallingConv() == CallingConv::PISA_KERNEL);
- const auto BitSize = DL.getTypeSizeInBits(ArgType->getScalarType());
+ const unsigned BitSize = DL.getTypeSizeInBits(ArgType->getScalarType());
- auto VReg16 = VReg;
+ Register VReg16 = VReg;
if (BitSize == 1) { // load arg into i16
VReg16 = MRI->createGenericVirtualRegister(LLT::integer(16));
MRI->setRegClass(VReg16, &PISA::Reg16bRegClass);
}
- auto MIB = MIRBuilder.buildInstr(Opcode).addDef(VReg16).addImm(ArgNo);
+ MachineInstrBuilder MIB =
+ MIRBuilder.buildInstr(Opcode).addDef(VReg16).addImm(ArgNo);
if (IsKernel)
MIB.addImm(Offset);
@@ -361,7 +368,7 @@ void PISACallLowering::loadParamWithOpcode(MachineIRBuilder &MIRBuilder,
if (IsKernel)
if (MDNode *MD = F.getMetadata("kernel_arg_name"))
if (ArgNo < MD->getNumOperands())
- if (auto *S = dyn_cast<MDString>(MD->getOperand(ArgNo)))
+ if (MDString *S = dyn_cast<MDString>(MD->getOperand(ArgNo)))
if (!S->getString().empty())
MIB.addExternalSymbol(
MIRBuilder.getMF().createExternalSymbolName(S->getString()));
@@ -375,14 +382,14 @@ unsigned PISACallLowering::getLoadParamOpcode(MachineIRBuilder &MIRBuilder,
const Function &F,
const Register &VReg,
Type *ArgType) const {
- auto *MRI = MIRBuilder.getMRI();
- auto &MF = MIRBuilder.getMF();
- const auto *TRI = static_cast<const PISARegisterInfo *>(
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
+ MachineFunction &MF = MIRBuilder.getMF();
+ const PISARegisterInfo *TRI = static_cast<const PISARegisterInfo *>(
MF.getSubtarget().getRegisterInfo());
bool IsKernel = (F.getCallingConv() == CallingConv::PISA_KERNEL);
unsigned Op = 0;
- auto &DL = F.getParent()->getDataLayout();
+ const DataLayout &DL = F.getParent()->getDataLayout();
const unsigned ParamScalar[2][4] = {
// [isKernel][8/16/32/64]
@@ -407,7 +414,7 @@ unsigned PISACallLowering::getLoadParamOpcode(MachineIRBuilder &MIRBuilder,
{PISA::loadParam_v2i32, PISA::loadParam_v3i32, PISA::loadParam_v4i32},
{PISA::loadParam_v2i64, PISA::loadParam_v3i64, 0}}};
- const auto BitSize = DL.getTypeSizeInBits(ArgType->getScalarType());
+ const unsigned BitSize = DL.getTypeSizeInBits(ArgType->getScalarType());
// Calculate the argument size in bytes.
if (ArgType->isIntegerTy()) {
switch (BitSize) {
@@ -454,8 +461,8 @@ unsigned PISACallLowering::getLoadParamOpcode(MachineIRBuilder &MIRBuilder,
MRI->setRegClass(VReg, &PISA::Reg64bRegClass);
Op = ParamScalar[IsKernel][3];
} else if (ArgType->isVectorTy()) {
- auto *VectorTy = cast<FixedVectorType>(ArgType);
- auto NumElts = VectorTy->getNumElements();
+ FixedVectorType *VectorTy = cast<FixedVectorType>(ArgType);
+ unsigned NumElts = VectorTy->getNumElements();
assert(((((BitSize == 8) || (BitSize == 16) || (BitSize == 64)) &&
((NumElts >= 2) && (NumElts <= 4))) ||
((BitSize == 32) &&
@@ -533,12 +540,12 @@ bool PISACallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
}
MachineInstrBuilder MIB;
- const auto *TRI = static_cast<const PISARegisterInfo *>(
+ const PISARegisterInfo *TRI = static_cast<const PISARegisterInfo *>(
MIRBuilder.getMF().getSubtarget().getRegisterInfo());
if (IsIndirectCall) {
Register CalleeReg = Info.Callee.getReg();
- auto *MRI = MIRBuilder.getMRI();
+ MachineRegisterInfo *MRI = MIRBuilder.getMRI();
LLT CalleeTy = MRI->getType(CalleeReg);
Register CalleeI64Reg = MRI->createGenericVirtualRegister(LLT::integer(64));
@@ -553,18 +560,20 @@ bool PISACallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
// promote i1 args to i16
SmallVector<Register, 8> ArgRegs;
- for (const auto &Arg : Info.OrigArgs) {
+ for (const ArgInfo &Arg : Info.OrigArgs) {
// Currently call args should have single vregs.
if (Arg.Regs.size() > 1)
return false;
if (Arg.Ty->getScalarSizeInBits() == 1) {
const LLT I16 = LLT::integer(16);
- auto Reg = MIRBuilder.getMRI()->createGenericVirtualRegister(I16);
+ Register Reg = MIRBuilder.getMRI()->createGenericVirtualRegister(I16);
MIRBuilder.getMRI()->setRegClass(Reg, TRI->getRegClassFromLLT(I16));
- auto Sext = llvm::any_of(Arg.Flags,
- [](const auto &Flag) { return Flag.isSExt(); });
- auto Zext = llvm::any_of(Arg.Flags,
- [](const auto &Flag) { return Flag.isZExt(); });
+ bool Sext = llvm::any_of(Arg.Flags, [](const ISD::ArgFlagsTy &Flag) {
+ return Flag.isSExt();
+ });
+ bool Zext = llvm::any_of(Arg.Flags, [](const ISD::ArgFlagsTy &Flag) {
+ return Flag.isZExt();
+ });
if (Sext) {
MIRBuilder.buildSExt(Reg, Arg.Regs[0]);
} else if (Zext) {
@@ -578,10 +587,10 @@ bool PISACallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
}
}
- auto MutateI1 = false;
+ bool MutateI1 = false;
if (!Info.OrigRet.Ty->isVoidTy()) {
// select the call op according to return type and build MI
- auto RetLLT =
+ LLT RetLLT =
llvm::getLLTForType(*Info.OrigRet.Ty, MIRBuilder.getDataLayout());
unsigned CallOp = 0;
if (RetLLT.isScalar() || RetLLT.isPointer()) {
@@ -612,8 +621,8 @@ bool PISACallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
break;
}
} else if (RetLLT.isVector()) {
- auto TypeBitSize = RetLLT.getElementType().getSizeInBits();
- auto NumElts = RetLLT.getNumElements();
+ uint64_t TypeBitSize = RetLLT.getElementType().getSizeInBits();
+ uint16_t NumElts = RetLLT.getNumElements();
switch (TypeBitSize) {
case 8:
switch (NumElts) {
@@ -726,7 +735,7 @@ bool PISACallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
// set Ret register class
assert(!Info.OrigRet.Regs.empty());
Register ResVReg = Info.OrigRet.Regs[0];
- auto OrigRetLLT = RetLLT;
+ LLT OrigRetLLT = RetLLT;
if (MutateI1) { // will return i16 (see lowerReturn())
RetLLT = LLT::integer(16);
ResVReg = MIRBuilder.getMRI()->createGenericVirtualRegister(RetLLT);
@@ -750,7 +759,7 @@ bool PISACallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
}
// add function args into MI if any
- for (auto Reg : ArgRegs) {
+ for (Register Reg : ArgRegs) {
MIB.addUse(Reg);
}
diff --git a/llvm/lib/Target/PISA/PISAISelLowering.cpp b/llvm/lib/Target/PISA/PISAISelLowering.cpp
index 377303af97321e..51f00605d39691 100644
--- a/llvm/lib/Target/PISA/PISAISelLowering.cpp
+++ b/llvm/lib/Target/PISA/PISAISelLowering.cpp
@@ -20,6 +20,7 @@
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/PISAAddrSpace.h"
+#include <functional>
#include <type_traits>
#define DEBUG_TYPE "pisa-lower"
@@ -193,7 +194,7 @@ SyncScope::ID hoistedFenceScope(Instruction *Inst) {
SyncScope::ID SSID = getAtomicSyncScopeID(Inst).value_or(SyncScope::System);
// Shared memory has no meaningful system scope, so narrow the scope used for
// hoisted fences to the widest valid shared-memory scope.
- if (const auto *RMW = dyn_cast<AtomicRMWInst>(Inst))
+ if (const AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(Inst))
if (RMW->getPointerAddressSpace() == SharedAS && SSID == SyncScope::System)
return Inst->getContext().getOrInsertSyncScopeID("gpu-shared");
return SSID;
@@ -287,15 +288,16 @@ bool PISATargetLowering::isCheapToSpeculateCtlz(Type *Ty) const { return true; }
bool PISATargetLowering::isReassocProfitable(MachineRegisterInfo &MRI,
Register N0, Register N1) const {
- auto GetOneDefInst = [&MRI](Register N) -> MachineInstr * {
- auto *Def = MRI.getOneDef(N);
+ std::function<MachineInstr *(Register)> GetOneDefInst =
+ [&MRI](Register N) -> MachineInstr * {
+ MachineOperand *Def = MRI.getOneDef(N);
if (Def)
return Def->getParent();
return nullptr;
};
- auto *I0 = GetOneDefInst(N0);
- auto *I1 = GetOneDefInst(N1);
+ MachineInstr *I0 = GetOneDefInst(N0);
+ MachineInstr *I1 = GetOneDefInst(N1);
if (I0 && I1) {
// Prevent reassociating the following pattern
// (add (mul a, b), (add (mul c, d), e))
@@ -306,8 +308,8 @@ bool PISATargetLowering::isReassocProfitable(MachineRegisterInfo &MRI,
std::swap(I0, I1);
if (I0->getOpcode() == TargetOpcode::G_MUL &&
I1->getOpcode() == TargetOpcode::G_ADD) {
- auto *NI0 = GetOneDefInst(I1->getOperand(1).getReg());
- auto *NI1 = GetOneDefInst(I1->getOperand(2).getReg());
+ MachineInstr *NI0 = GetOneDefInst(I1->getOperand(1).getReg());
+ MachineInstr *NI1 = GetOneDefInst(I1->getOperand(2).getReg());
if (NI0 && NI1 &&
(NI0->getOpcode() == TargetOpcode::G_MUL ||
NI1->getOpcode() == TargetOpcode::G_MUL)) {
@@ -348,7 +350,7 @@ void PISATargetLowering::getTgtMemIntrinsic(
Info.flags |= MachineMemOperand::MOLoad | MachineMemOperand::MOStore;
Info.flags |= getTargetMMOFlags(I);
// syncscope("<target-scope>") support for atomics
- if (auto *ConstInt = dyn_cast<ConstantInt>(I.getArgOperand(3)))
+ if (ConstantInt *ConstInt = dyn_cast<ConstantInt>(I.getArgOperand(3)))
Info.order = static_cast<llvm::AtomicOrdering>(ConstInt->getZExtValue());
Infos.push_back(Info);
return;
@@ -360,10 +362,10 @@ void PISATargetLowering::getTgtMemIntrinsic(
LLT PISATargetLowering::getOptimalMemOpLLT(
const MemOp &Op, const AttributeList &FuncAttributes) const {
- auto I8 = LLT::integer(8);
- auto I16 = LLT::integer(16);
- auto I32 = LLT::integer(32);
- auto I64 = LLT::integer(64);
+ LLT I8 = LLT::integer(8);
+ LLT I16 = LLT::integer(16);
+ LLT I32 = LLT::integer(32);
+ LLT I64 = LLT::integer(64);
if (Op.size() >= 16 && Op.isAligned(Align(8)))
return LLT::fixed_vector(2, I64);
@@ -438,8 +440,8 @@ PISATargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
break;
case 'r': {
// FIXME: we already have a method to get the register class from LLT
- auto VectorSize = VT.isVector() ? VT.getVectorNumElements() : 1;
- auto ElementSize = VT.getScalarSizeInBits();
+ unsigned VectorSize = VT.isVector() ? VT.getVectorNumElements() : 1;
+ unsigned ElementSize = VT.getScalarSizeInBits();
assert(VectorSize >= 1 &&
(ElementSize == 32 ? VectorSize <= 8 || VectorSize == 16 ||
VectorSize == 32 || VectorSize == 64
@@ -479,8 +481,8 @@ PISATargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
MachineMemOperand::Flags
PISATargetLowering::getTargetMMOFlags(const Instruction &I) const {
MachineMemOperand::Flags Flags = MachineMemOperand::MONone;
- if (auto Hint = PISA::getCacheCtrlFromMMRA(I)) {
- auto HintValue = *Hint & 0xF;
+ if (std::optional<unsigned> Hint = PISA::getCacheCtrlFromMMRA(I)) {
+ unsigned HintValue = *Hint & 0xF;
Flags |= static_cast<MachineMemOperand::Flags>(HintValue << 6);
}
return Flags;
@@ -502,7 +504,7 @@ void PISATargetLowering::computeKnownBitsForTargetInstr(
if (Intrinsic::isOverloaded(IID))
return;
- auto *Ctx = &MI->getMF()->getFunction().getContext();
+ LLVMContext *Ctx = &MI->getMF()->getFunction().getContext();
FunctionType *FT = Intrinsic::getType(*Ctx, IID);
AttributeList Attrs = Intrinsic::getAttributes(*Ctx, IID, FT);
@@ -587,7 +589,7 @@ bool PISATargetLowering::shouldInsertFencesForAtomic(
const Instruction *I) const {
// Use AtomicExpand fence splitting only for RMWs that become CAS loops.
// Native atomics keep their ordering.
- if (const auto *RMW = dyn_cast<AtomicRMWInst>(I))
+ if (const AtomicRMWInst *RMW = dyn_cast<AtomicRMWInst>(I))
return computeRMWExpansion(RMW) == AtomicExpansionKind::CmpXChg;
return false;
}
@@ -640,7 +642,8 @@ void PISATargetLowering::emitExpandAtomicRMW(AtomicRMWInst *A) const {
IR.SetInsertPoint(JoinBB->begin());
PHINode *Res = IR.CreatePHI(A->getType(), 2);
- auto EmitArm = [&](BasicBlock *BB, unsigned AS) {
+ std::function<void(BasicBlock *, unsigned)> EmitArm =
+ [&](BasicBlock *BB, unsigned AS) {
IR.SetInsertPoint(BB);
Value *Cast = IR.CreateAddrSpaceCast(A->getPointerOperand(),
PointerType::get(Ctx, AS));
diff --git a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
index 0597d75b67edff..ae721452113235 100644
--- a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
+++ b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
@@ -53,11 +53,11 @@ constexpr LLT F64 = LLT::float64();
// return true if natively supported type
static bool isLegalType(LLT Ty, bool Vector = true) {
- auto EltSize = Ty.getScalarSizeInBits();
+ unsigned EltSize = Ty.getScalarSizeInBits();
if (Ty.isVector() && !Vector)
return false;
if (Ty.isVector()) {
- auto NumElts = Ty.getNumElements();
+ uint16_t NumElts = Ty.getNumElements();
if (EltSize == 32)
return NumElts <= 8 || NumElts == 16 || NumElts == 32 || NumElts == 64;
if (!llvm::isPowerOf2_32(EltSize) || EltSize < 8 || EltSize > 64)
@@ -94,8 +94,8 @@ LegalityPredicate isFloatingPointType(unsigned TypeIdx) {
LegalizeMutation changeElementTypeToInteger(unsigned TypeIdx) {
return [=](const LegalityQuery &Query) {
const LLT Ty = Query.Types[TypeIdx];
- auto NewEltTy = LLT::integer(Ty.getScalarSizeInBits());
- auto NewTy = Ty.isVector()
+ llvm::LLT NewEltTy = LLT::integer(Ty.getScalarSizeInBits());
+ llvm::LLT NewTy = Ty.isVector()
? LLT::fixed_vector(Ty.getNumElements(), NewEltTy)
: NewEltTy;
return std::pair(TypeIdx, NewTy);
@@ -125,8 +125,9 @@ static bool shouldWidenLoad(unsigned int Opcode, const LLT Ty,
PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
using namespace TargetOpcode;
- auto &TM = ST.getTargetLowering()->getTargetMachine();
- auto GetPointerLlt = [&](PISAAS::AddressSpace Addrspace) {
+ const llvm::TargetMachine &TM = ST.getTargetLowering()->getTargetMachine();
+ std::function<LLT(PISAAS::AddressSpace)> GetPointerLlt =
+ [&](PISAAS::AddressSpace Addrspace) {
uint32_t NumBits =
TM.getPointerSizeInBits(static_cast<unsigned>(Addrspace));
return LLT::pointer(static_cast<unsigned>(Addrspace), NumBits);
@@ -142,9 +143,10 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
GenericPtr};
const std::initializer_list<LLT> AddrSpaces32 = {PrivatePtr, SharedPtr};
- auto AllIntegers = {I8, I16, I32, I64};
- auto AllFloats = {BF16, F16, F32, F64};
- auto AllPtrs = {PrivatePtr, GlobalPtr, ConstantPtr, SharedPtr, GenericPtr};
+ std::initializer_list<llvm::LLT> AllIntegers = {I8, I16, I32, I64};
+ std::initializer_list<llvm::LLT> AllFloats = {BF16, F16, F32, F64};
+ std::initializer_list<llvm::LLT> AllPtrs = {
+ PrivatePtr, GlobalPtr, ConstantPtr, SharedPtr, GenericPtr};
getActionDefinitionsBuilder(
{G_FADD, G_FCONSTANT, G_FSUB, G_FMUL, G_FMINNUM, G_FMAXNUM, G_FMINIMUM,
@@ -198,7 +200,7 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
const LLT Ty = Query.Types[0];
if (!Ty.isVector())
return false;
- auto VecBitSize = Ty.getSizeInBits();
+ llvm::TypeSize VecBitSize = Ty.getSizeInBits();
return VecBitSize == 32 || VecBitSize == 16;
})),
LegalizeMutation(([=](const LegalityQuery &Query) {
@@ -249,10 +251,10 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
.legalFor(
{{I16, I8}, {I32, I8}, {I64, I8}, {I32, I16}, {I64, I16}, {I64, I32}})
.customIf([=](const LegalityQuery &Query) {
- auto DstSize = Query.Types[0].getScalarSizeInBits();
- auto SrcSize = Query.Types[1].getScalarSizeInBits();
- auto UseSelect = SrcSize == 1;
- auto UseShuffle = (SrcSize % 8 == 0 && !isPowerOf2_32(SrcSize)) ||
+ unsigned DstSize = Query.Types[0].getScalarSizeInBits();
+ unsigned SrcSize = Query.Types[1].getScalarSizeInBits();
+ bool UseSelect = SrcSize == 1;
+ bool UseShuffle = (SrcSize % 8 == 0 && !isPowerOf2_32(SrcSize)) ||
(DstSize % 8 == 0 && !isPowerOf2_32(DstSize));
return UseSelect || UseShuffle;
})
@@ -369,7 +371,7 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
getActionDefinitionsBuilder(G_SHUFFLE_VECTOR)
.customIf([](const LegalityQuery &Query) {
- auto Ty = Query.Types[0];
+ llvm::LLT Ty = Query.Types[0];
return Ty.isVector() && (Ty.getScalarSizeInBits() == 32) &&
isPowerOf2_32(Ty.getNumElements());
})
@@ -395,7 +397,7 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
// scalars are widened directly.
//.widenScalar does not update MI.memoperands()[0].getType(), hence
.customIf([=](const LegalityQuery &Query) -> bool {
- auto Ty = Query.Types[0];
+ llvm::LLT Ty = Query.Types[0];
if (Ty.isVector() && (Ty.getScalarSizeInBits() > 1) &&
(Ty.getScalarSizeInBits() < 8))
return true;
@@ -405,10 +407,10 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
})
.fewerElementsIf(
[=](const LegalityQuery &Query) -> bool {
- auto EltTy = Query.Types[0];
- auto BitSize = EltTy.getScalarSizeInBits();
- auto NumElts = EltTy.isVector() ? EltTy.getNumElements() : 1;
- auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+ llvm::LLT EltTy = Query.Types[0];
+ unsigned BitSize = EltTy.getScalarSizeInBits();
+ int NumElts = EltTy.isVector() ? EltTy.getNumElements() : 1;
+ uint64_t AlignInBits = Query.MMODescrs[0].AlignInBits;
// small (bitsize<32) vectors with non-power-of-2 elements
// can be broken into power-of-2 vectors that can be later
// upconverted to vectors of i32 for better codegen
@@ -416,55 +418,55 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
BitSize != 1 && (BitSize < 32) && (BitSize < AlignInBits);
},
[=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
- auto EltTy = Query.Types[0];
- auto NumElts = EltTy.getNumElements();
- auto NewNumElts = PowerOf2Ceil(NumElts) / 2;
+ llvm::LLT EltTy = Query.Types[0];
+ uint16_t NumElts = EltTy.getNumElements();
+ uint64_t NewNumElts = PowerOf2Ceil(NumElts) / 2;
return std::make_pair(
0, LLT::fixed_vector(NewNumElts, EltTy.getScalarType()));
})
// split up vectors of non-standard size elements
.fewerElementsIf(
[=](const LegalityQuery &Query) -> bool {
- auto EltTy = Query.Types[0];
+ llvm::LLT EltTy = Query.Types[0];
return EltTy.isVector() &&
!isPowerOf2_32(EltTy.getScalarSizeInBits());
},
[=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
- auto EltTy = Query.Types[0];
+ llvm::LLT EltTy = Query.Types[0];
return std::make_pair(0, EltTy.getScalarType());
})
// cast non-^2 scalars to vectors of i8
.bitcastIf(
[=](const LegalityQuery &Query) -> bool {
const LLT EltTy = Query.Types[0];
- auto NumBits = EltTy.getSizeInBits();
+ llvm::TypeSize NumBits = EltTy.getSizeInBits();
return !EltTy.isVector() && !isPowerOf2_32(NumBits);
},
[=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
- auto Size = Query.Types[0].getSizeInBits();
+ llvm::TypeSize Size = Query.Types[0].getSizeInBits();
return std::pair(0, LLT::fixed_vector(Size / 8, I8));
})
// cast scalar/vector with large bitsize into <? x i32>
.bitcastIf(
[=](const LegalityQuery &Query) -> bool {
const LLT EltTy = Query.Types[0];
- auto NumBits = EltTy.getScalarSizeInBits();
- auto IsAtomic128 =
+ unsigned NumBits = EltTy.getScalarSizeInBits();
+ bool IsAtomic128 =
EltTy.isScalar() && (NumBits == 128) &&
isStrongerThanMonotonic(Query.MMODescrs[0].Ordering);
return !IsAtomic128 && (NumBits % 32 == 0) && (NumBits > 64);
},
[=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
const LLT EltTy = Query.Types[0];
- auto NumBits = EltTy.getSizeInBits();
+ llvm::TypeSize NumBits = EltTy.getSizeInBits();
return std::pair(0, LLT::fixed_vector(NumBits / 32, I32));
})
.bitcastIf(([=](const LegalityQuery &Query) -> bool {
- auto EltTy = Query.Types[0];
- auto BitSize = EltTy.getScalarSizeInBits();
- auto AccSize = EltTy.getSizeInBits();
- auto AlignInBits = Query.MMODescrs[0].AlignInBits;
- auto SmallVectorWithManyElements =
+ llvm::LLT EltTy = Query.Types[0];
+ unsigned BitSize = EltTy.getScalarSizeInBits();
+ llvm::TypeSize AccSize = EltTy.getSizeInBits();
+ uint64_t AlignInBits = Query.MMODescrs[0].AlignInBits;
+ bool SmallVectorWithManyElements =
(BitSize < 32) && EltTy.isVector() &&
(EltTy.getNumElements() > 4);
@@ -482,54 +484,54 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
((BitSize < 32) && (AlignInBits < AccSize));
}),
[=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
- auto EltTy = Query.Types[0];
- auto BitSize = EltTy.getScalarSizeInBits();
- auto AccSize = EltTy.getSizeInBits();
- auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+ llvm::LLT EltTy = Query.Types[0];
+ unsigned BitSize = EltTy.getScalarSizeInBits();
+ llvm::TypeSize AccSize = EltTy.getSizeInBits();
+ uint64_t AlignInBits = Query.MMODescrs[0].AlignInBits;
if ((BitSize < 32) && (AccSize % 32 == 0) &&
(AlignInBits % 32 == 0))
AlignInBits = 32;
- auto NewEltTy = LLT::integer(AlignInBits);
- auto NewNumElts = AccSize / AlignInBits;
- auto NewTy = NewNumElts == 1
+ llvm::LLT NewEltTy = LLT::integer(AlignInBits);
+ uint64_t NewNumElts = AccSize / AlignInBits;
+ llvm::LLT NewTy = NewNumElts == 1
? NewEltTy
: LLT::fixed_vector(NewNumElts, NewEltTy);
return std::pair(0, NewTy);
})
// bitcast <6 x i32> to <3 x i64> if alignment is sufficient
.bitcastIf(([=](const LegalityQuery &Query) -> bool {
- auto EltTy = Query.Types[0];
+ llvm::LLT EltTy = Query.Types[0];
return EltTy.isVector() &&
(EltTy.getScalarSizeInBits() == 32) &&
(EltTy.getNumElements() == 6) &&
(Query.MMODescrs[0].AlignInBits >= 64);
}),
[=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
- auto NewTy = LLT::fixed_vector(3, LLT::integer(64));
+ llvm::LLT NewTy = LLT::fixed_vector(3, LLT::integer(64));
return std::pair(0, NewTy);
})
// Increase the number of elements to corresponding vector of i8
.customIf([=](const LegalityQuery &Query) {
- auto EltTy = Query.Types[0];
+ llvm::LLT EltTy = Query.Types[0];
return EltTy.getScalarSizeInBits() == 1;
})
// expand s32 vectors with 4 < elts < 8 to have 8 elements
// Enabled only for shared memory where loads of OOB accesses are
// guaranteed to return 0
.customIf([=](const LegalityQuery &Query) {
- auto EltTy = Query.Types[0];
- auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+ llvm::LLT EltTy = Query.Types[0];
+ uint64_t AlignInBits = Query.MMODescrs[0].AlignInBits;
return shouldWidenLoad(Query.Opcode, EltTy,
Query.Types[1].getAddressSpace(), AlignInBits);
})
// <4 x i8> align 1 .. needs to be broken down into 4 loads
.scalarizeIf(([=](const LegalityQuery &Query) -> bool {
- auto EltTy = Query.Types[0];
- auto BitSize = EltTy.getScalarSizeInBits();
- auto AccSize = EltTy.getSizeInBits();
- auto AlignInBits = Query.MMODescrs[0].AlignInBits;
+ llvm::LLT EltTy = Query.Types[0];
+ unsigned BitSize = EltTy.getScalarSizeInBits();
+ llvm::TypeSize AccSize = EltTy.getSizeInBits();
+ uint64_t AlignInBits = Query.MMODescrs[0].AlignInBits;
return ((BitSize < 32) && (AlignInBits < AccSize));
}),
0)
@@ -551,7 +553,7 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
const LLT EltTy = Query.Types[0];
if (!EltTy.isVector())
return false;
- auto NumElements = EltTy.getNumElements();
+ uint16_t NumElements = EltTy.getNumElements();
if (!isPowerOf2_32(NumElements))
return !((NumElements == 3) &&
((EltTy.getScalarSizeInBits() == 32) ||
@@ -560,7 +562,7 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
},
[=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
const LLT EltTy = Query.Types[0];
- auto NewNumElts = PowerOf2Ceil(EltTy.getNumElements()) / 2;
+ uint64_t NewNumElts = PowerOf2Ceil(EltTy.getNumElements()) / 2;
return std::pair(
0, LLT::fixed_vector(NewNumElts, EltTy.getScalarType()));
})
@@ -635,8 +637,8 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
getActionDefinitionsBuilder(G_BITCAST)
// allow bitcasts between pointers and non-pointers (ptr2int/int2ptr)
.customIf([=](const LegalityQuery &Query) {
- auto DstTy = Query.Types[0];
- auto SrcTy = Query.Types[1];
+ llvm::LLT DstTy = Query.Types[0];
+ llvm::LLT SrcTy = Query.Types[1];
return (DstTy.isPointer() != SrcTy.isPointer());
})
// In cases where both source and destination operands are vectors,
@@ -644,8 +646,8 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
// divisible by each other, e.g. <4 x i32> to <8 x i16>; use custom
// legalization to handle other cases, e.g. <5 x i32> to <2 x i80>
.customIf([=](const LegalityQuery &Query) {
- auto DstTy = Query.Types[0];
- auto SrcTy = Query.Types[1];
+ llvm::LLT DstTy = Query.Types[0];
+ llvm::LLT SrcTy = Query.Types[1];
if (!SrcTy.isVector() || !DstTy.isVector())
return false;
unsigned SrcNumElts = SrcTy.getNumElements();
@@ -658,14 +660,14 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
// avoid creating illegal G_UNMERGE_VALUES on odd-sized vectors
// downstream.
.customIf([=](const LegalityQuery &Query) {
- auto DstTy = Query.Types[0];
- auto SrcTy = Query.Types[1];
+ llvm::LLT DstTy = Query.Types[0];
+ llvm::LLT SrcTy = Query.Types[1];
if (!SrcTy.isVector() || !DstTy.isVector())
return false;
- auto DstNumElts = DstTy.isVector() ? DstTy.getNumElements() : 1;
- auto SrcNumElts = SrcTy.isVector() ? SrcTy.getNumElements() : 1;
- auto DstEltSize = DstTy.getScalarSizeInBits();
- auto SrcEltSize = SrcTy.getScalarSizeInBits();
+ int DstNumElts = DstTy.isVector() ? DstTy.getNumElements() : 1;
+ int SrcNumElts = SrcTy.isVector() ? SrcTy.getNumElements() : 1;
+ unsigned DstEltSize = DstTy.getScalarSizeInBits();
+ unsigned SrcEltSize = SrcTy.getScalarSizeInBits();
if (DstNumElts != SrcNumElts)
return false;
if (DstEltSize != SrcEltSize)
@@ -674,13 +676,13 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
!isPowerOf2_32(DstTy.getSizeInBits());
})
.legalIf([=](const LegalityQuery &Query) {
- auto DstTy = Query.Types[0];
- auto SrcTy = Query.Types[1];
- auto DstNumElts = DstTy.isVector() ? DstTy.getNumElements() : 1;
- auto SrcNumElts = SrcTy.isVector() ? SrcTy.getNumElements() : 1;
- auto DstEltSize = DstTy.getScalarSizeInBits();
- auto SrcEltSize = SrcTy.getScalarSizeInBits();
- auto CastSize = DstTy.getSizeInBits();
+ llvm::LLT DstTy = Query.Types[0];
+ llvm::LLT SrcTy = Query.Types[1];
+ int DstNumElts = DstTy.isVector() ? DstTy.getNumElements() : 1;
+ int SrcNumElts = SrcTy.isVector() ? SrcTy.getNumElements() : 1;
+ unsigned DstEltSize = DstTy.getScalarSizeInBits();
+ unsigned SrcEltSize = SrcTy.getScalarSizeInBits();
+ llvm::TypeSize CastSize = DstTy.getSizeInBits();
if (DstEltSize == 32 && SrcEltSize == 32)
// vectors of 32bit integer <=> floats
return DstNumElts <= 8 || DstNumElts == 16 || DstNumElts == 32 ||
@@ -715,13 +717,13 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
// extend vectors of i1 to have power of two elements
.moreElementsIf(
([=](const LegalityQuery &Query) {
- auto DstTy = Query.Types[SrcTyIdx];
- auto BitSize = DstTy.getSizeInBits();
+ llvm::LLT DstTy = Query.Types[SrcTyIdx];
+ llvm::TypeSize BitSize = DstTy.getSizeInBits();
return DstTy.isVector() && (DstTy.getScalarSizeInBits() == 1) &&
((BitSize < 8) || !isPowerOf2_32(BitSize));
}),
[=](const LegalityQuery &Query) {
- auto DstTy = Query.Types[SrcTyIdx];
+ llvm::LLT DstTy = Query.Types[SrcTyIdx];
unsigned NumElts = PowerOf2Ceil(DstTy.getNumElements());
NumElts = std::max(8u, NumElts);
return std::pair(
@@ -729,30 +731,30 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
})
// <? x i1>
.customIf([=](const LegalityQuery &Query) {
- auto EltSize = Query.Types[SrcTyIdx].getScalarSizeInBits();
+ unsigned EltSize = Query.Types[SrcTyIdx].getScalarSizeInBits();
return (EltSize == 1);
})
// increase to a multiple of elements, e.g. <5 x i16> => <8 x i16>
.moreElementsIf(
[=](const LegalityQuery &Query) {
- auto SrcTy = Query.Types[SrcTyIdx];
+ llvm::LLT SrcTy = Query.Types[SrcTyIdx];
unsigned NumElts = SrcTy.getNumElements();
if (SrcTy.getScalarSizeInBits() != 32)
return (NumElts > 4) && (NumElts % 4);
return (NumElts > 8) && (NumElts != 16) && (NumElts % 32);
},
[=](const LegalityQuery &Query) {
- auto SrcTy = Query.Types[SrcTyIdx];
- auto ScalarTy = SrcTy.getScalarType();
- auto NumElts = PowerOf2Ceil(SrcTy.getNumElements());
+ llvm::LLT SrcTy = Query.Types[SrcTyIdx];
+ llvm::LLT ScalarTy = SrcTy.getScalarType();
+ uint64_t NumElts = PowerOf2Ceil(SrcTy.getNumElements());
return std::pair(SrcTyIdx, LLT::fixed_vector(NumElts, ScalarTy));
})
// cast non-s32 elements to s32 vector, e.g.
// <N x s8> => <N/4 x s32>, iff the index is non-constant
.customIf([=](const LegalityQuery &Query) {
const LLT Ty = Query.Types[SrcTyIdx];
- const auto EltSize = Ty.getScalarSizeInBits();
- const auto NumElts = Ty.getNumElements();
+ const unsigned EltSize = Ty.getScalarSizeInBits();
+ const uint16_t NumElts = Ty.getNumElements();
// Only needed for non-s32 elements
if (EltSize != 8 && EltSize != 16 && EltSize != 64)
return false;
@@ -767,13 +769,13 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
// - operation will use 'insert/extract' or swizzle
.fewerElementsIf(
[=](const LegalityQuery &Query) {
- auto SrcTy = Query.Types[SrcTyIdx];
- auto MaxElts = SrcTy.getScalarSizeInBits() == 32 ? 64 : 4;
+ llvm::LLT SrcTy = Query.Types[SrcTyIdx];
+ int MaxElts = SrcTy.getScalarSizeInBits() == 32 ? 64 : 4;
return SrcTy.getNumElements() > MaxElts;
},
[=](const LegalityQuery &Query) {
- auto SrcTy = Query.Types[SrcTyIdx];
- auto ScalarTy = SrcTy.getScalarType();
+ llvm::LLT SrcTy = Query.Types[SrcTyIdx];
+ llvm::LLT ScalarTy = SrcTy.getScalarType();
return (SrcTy.getScalarSizeInBits() == 32)
? std::pair(SrcTyIdx, LLT::fixed_vector(64, ScalarTy))
: std::pair(SrcTyIdx, LLT::fixed_vector(4, ScalarTy));
@@ -824,10 +826,10 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
})
.customIf([=](const LegalityQuery &Query) {
// return true if we want to use 'insert', instead of swizzle
- auto LitTy = Query.Types[1];
- auto BigTy = Query.Types[0];
- auto EltOk = BigTy.getScalarSizeInBits() == 32;
- auto VecOk = LitTy.isVector() && BigTy.isVector();
+ llvm::LLT LitTy = Query.Types[1];
+ llvm::LLT BigTy = Query.Types[0];
+ bool EltOk = BigTy.getScalarSizeInBits() == 32;
+ bool VecOk = LitTy.isVector() && BigTy.isVector();
return EltOk && VecOk &&
((LitTy.getNumElements() > 4) || (BigTy.getNumElements() > 4));
})
@@ -843,14 +845,14 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
G_VECREDUCE_FMINIMUM, G_VECREDUCE_FMAXIMUM})
// fewerElementsVectorReductions does not handle (SrcElts % DstElts != 0)
.moreElementsIf(([=](const LegalityQuery &Query) {
- auto NumElts = Query.Types[1].getNumElements();
+ uint16_t NumElts = Query.Types[1].getNumElements();
return NumElts > 4 && NumElts % 4 != 0;
}),
[=](const LegalityQuery &Query) {
- auto SrcTy = Query.Types[1];
- auto NewNumElts =
+ llvm::LLT SrcTy = Query.Types[1];
+ uint64_t NewNumElts =
llvm::PowerOf2Ceil(SrcTy.getNumElements());
- auto NewSrcTy = LLT::fixed_vector(
+ llvm::LLT NewSrcTy = LLT::fixed_vector(
NewNumElts, SrcTy.getScalarType());
return std::pair(1, NewSrcTy);
})
@@ -863,10 +865,10 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
unsigned BigTyIdx = Op == G_UNMERGE_VALUES ? 1 : 0;
unsigned LitTyIdx = Op == G_UNMERGE_VALUES ? 0 : 1;
- auto &Builder = getActionDefinitionsBuilder(Op);
+ llvm::LegalizeRuleSet &Builder = getActionDefinitionsBuilder(Op);
Builder.customIf([=](const LegalityQuery &Query) {
// return true if we want to use 'extract', instead of swizzle
- auto BigTy = Query.Types[BigTyIdx];
+ llvm::LLT BigTy = Query.Types[BigTyIdx];
return BigTy.isVector() && (BigTy.getScalarSizeInBits() == 32) &&
(BigTy.getNumElements() > 4);
});
@@ -874,9 +876,9 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
Builder
.legalIf([=](const LegalityQuery &Query) {
// vector(big) <=> scalar/vector(lit)
- auto BigTy = Query.Types[BigTyIdx];
- auto LitTy = Query.Types[LitTyIdx];
- auto LitTyValid = LitTy.isScalar() ? BigTy.getScalarType() == LitTy
+ llvm::LLT BigTy = Query.Types[BigTyIdx];
+ llvm::LLT LitTy = Query.Types[LitTyIdx];
+ bool LitTyValid = LitTy.isScalar() ? BigTy.getScalarType() == LitTy
: LitTy.getScalarSizeInBits() >= 8;
// No register class exists for vectors with elements wider than
// 64 bits, so <N x i128> and friends must not be marked legal here
@@ -889,13 +891,13 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
})
.widenScalarIf(
[=](const LegalityQuery &Query) {
- auto BigTy = Query.Types[BigTyIdx];
- auto BigTySize = BigTy.getSizeInBits();
+ llvm::LLT BigTy = Query.Types[BigTyIdx];
+ llvm::TypeSize BigTySize = BigTy.getSizeInBits();
return BigTy.isScalar() && BigTySize > 64 &&
!isPowerOf2_32(BigTySize);
},
[=](const LegalityQuery &Query) {
- auto BigTy = Query.Types[BigTyIdx];
+ llvm::LLT BigTy = Query.Types[BigTyIdx];
unsigned NewSizeInBits =
1 << Log2_32_Ceil(BigTy.getSizeInBits() + 1);
return std::pair(BigTyIdx, LLT::integer(NewSizeInBits));
@@ -903,9 +905,9 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
.lowerIf([=](const LegalityQuery &Query) {
// lower to shift/mask if conversion would
// result in a vector with >4 elements
- auto BigTy = Query.Types[BigTyIdx];
- auto LitTy = Query.Types[LitTyIdx];
- auto NumElts = BigTy.getSizeInBits() / LitTy.getScalarSizeInBits();
+ llvm::LLT BigTy = Query.Types[BigTyIdx];
+ llvm::LLT LitTy = Query.Types[LitTyIdx];
+ uint64_t NumElts = BigTy.getSizeInBits() / LitTy.getScalarSizeInBits();
return BigTy.isScalar() && (NumElts > 4);
})
.lowerIf(all(vectorElementCountIsGreaterThan(LitTyIdx, 4),
@@ -1150,10 +1152,10 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
static SmallVector<MachineInstr *> scalarizeIntrinsic(MachineInstr &MI) {
Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
MachineIRBuilder B(MI);
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
SmallVector<MachineInstr *> NewMIs;
- auto DstTy = MRI.getType(MI.getOperand(0).getReg());
+ llvm::LLT DstTy = MRI.getType(MI.getOperand(0).getReg());
if (!DstTy.isVector()) {
NewMIs.push_back(&MI);
return NewMIs;
@@ -1163,12 +1165,12 @@ static SmallVector<MachineInstr *> scalarizeIntrinsic(MachineInstr &MI) {
for (unsigned I = 0; I < DstTy.getNumElements(); I++) {
SmallVector<MachineOperand, 4> Opnds;
for (unsigned J = 2; J < MI.getNumOperands(); J++) { // dst, iid
- auto Opnd = MI.getOperand(J);
+ llvm::MachineOperand Opnd = MI.getOperand(J);
if (Opnd.isReg()) {
- auto ArgTy = MRI.getType(Opnd.getReg());
+ llvm::LLT ArgTy = MRI.getType(Opnd.getReg());
if (ArgTy.isVector()) {
ArgTy = ArgTy.getScalarType();
- auto ArgReg = MRI.createGenericVirtualRegister(ArgTy);
+ llvm::Register ArgReg = MRI.createGenericVirtualRegister(ArgTy);
B.buildExtractVectorElementConstant(ArgReg, Opnd, I);
Opnds.push_back(MachineOperand::CreateReg(ArgReg, false));
} else { // use register operand as-is
@@ -1178,12 +1180,12 @@ static SmallVector<MachineInstr *> scalarizeIntrinsic(MachineInstr &MI) {
Opnds.push_back(Opnd);
}
}
- auto DstReg = MRI.createGenericVirtualRegister(DstTy.getScalarType());
- auto Res = B.buildIntrinsic(IntrinsicID, DstReg);
+ llvm::Register DstReg = MRI.createGenericVirtualRegister(DstTy.getScalarType());
+ llvm::MachineInstrBuilder Res = B.buildIntrinsic(IntrinsicID, DstReg);
NewMIs.push_back(Res);
Res.setMIFlags(MI.getFlags());
- for (auto It = Opnds.begin(), Ite = Opnds.end(); It != Ite; ++It)
- Res.add(*It);
+ for (MachineOperand &Opnd : Opnds)
+ Res.add(Opnd);
VecRegs.push_back(DstReg);
}
B.buildBuildVector(MI.getOperand(0), VecRegs);
@@ -1199,14 +1201,14 @@ static bool legalizeGFlog(MachineInstr &MI, MachineIRBuilder &B,
LLT Ty = B.getMRI()->getType(Dst);
unsigned Flags = MI.getFlags();
- auto &Semantics = getFltSemanticForLLT(Ty.getScalarType());
+ const llvm::fltSemantics &Semantics = getFltSemanticForLLT(Ty.getScalarType());
APFloat APFLog2BaseInverted(Log2BaseInverted);
bool LosesInfo; // ignored
APFLog2BaseInverted.convert(Semantics, APFloat::rmNearestTiesToEven,
&LosesInfo);
- auto Log2Operand = B.buildFLog2(Ty, Src, Flags);
- auto Log2BaseInvertedOperand = B.buildFConstant(Ty, APFLog2BaseInverted);
+ llvm::MachineInstrBuilder Log2Operand = B.buildFLog2(Ty, Src, Flags);
+ llvm::MachineInstrBuilder Log2BaseInvertedOperand = B.buildFConstant(Ty, APFLog2BaseInverted);
B.buildFMul(Dst, Log2Operand, Log2BaseInvertedOperand, Flags);
MI.eraseFromParent();
@@ -1221,13 +1223,13 @@ static bool legalizeGFexp(MachineInstr &MI, MachineIRBuilder &B,
unsigned Flags = MI.getFlags();
LLT Ty = B.getMRI()->getType(Dst);
- auto &Semantics = getFltSemanticForLLT(Ty.getScalarType());
+ const llvm::fltSemantics &Semantics = getFltSemanticForLLT(Ty.getScalarType());
APFloat APFMultiplicand(Multiplicand);
bool LosesInfo; // ignored
APFMultiplicand.convert(Semantics, APFloat::rmNearestTiesToEven, &LosesInfo);
- auto K = B.buildFConstant(Ty, APFMultiplicand);
- auto Mul = B.buildFMul(Ty, Src, K, Flags);
+ llvm::MachineInstrBuilder K = B.buildFConstant(Ty, APFMultiplicand);
+ llvm::MachineInstrBuilder Mul = B.buildFMul(Ty, Src, K, Flags);
B.buildFExp2(Dst, Mul, Flags);
MI.eraseFromParent();
return true;
@@ -1238,7 +1240,7 @@ static bool legalizeGFexp(MachineInstr &MI, MachineIRBuilder &B,
// if and when it is available. For now, we custom legalize it based upon the
// approach in TargetLowering::LegalizeSetCCCondCode().
static bool legalizeGFcmp(MachineInstr &MI, MachineIRBuilder &B) {
- auto Pred = static_cast<CmpInst::Predicate>(MI.getOperand(1).getPredicate());
+ llvm::CmpInst::Predicate Pred = static_cast<CmpInst::Predicate>(MI.getOperand(1).getPredicate());
Register Dst = MI.getOperand(0).getReg();
Register Op0 = MI.getOperand(2).getReg();
Register Op1 = MI.getOperand(3).getReg();
@@ -1258,11 +1260,11 @@ static bool legalizeGFcmp(MachineInstr &MI, MachineIRBuilder &B) {
// G_ANYEXT the G_FCMP compare result to i16. Given that a .reg destination
// for fcmp is only available for 32-bit, we explicitly extend it here
// so we can fold the resulting select into the fcmp.
- auto LHS =
+ llvm::MachineInstrBuilder LHS =
B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_OGT, I1, Op0, Op1, Flags));
- auto RHS =
+ llvm::MachineInstrBuilder RHS =
B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_OLT, I1, Op0, Op1, Flags));
- auto Result = B.buildOr(I32, LHS, RHS);
+ llvm::MachineInstrBuilder Result = B.buildOr(I32, LHS, RHS);
if (Pred == CmpInst::FCMP_UEQ)
Result = B.buildNot(I32, Result);
B.buildICmp(CmpInst::ICMP_EQ, Dst, Result, B.buildConstant(I32, -1));
@@ -1270,11 +1272,11 @@ static bool legalizeGFcmp(MachineInstr &MI, MachineIRBuilder &B) {
break;
}
case CmpInst::FCMP_ORD: {
- auto LHS =
+ llvm::MachineInstrBuilder LHS =
B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_OEQ, I1, Op0, Op0, Flags));
- auto RHS =
+ llvm::MachineInstrBuilder RHS =
B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_OEQ, I1, Op1, Op1, Flags));
- auto Result = B.buildAnd(I32, LHS, RHS);
+ llvm::MachineInstrBuilder Result = B.buildAnd(I32, LHS, RHS);
B.buildICmp(CmpInst::ICMP_EQ, Dst, Result, B.buildConstant(I32, -1));
MI.eraseFromParent();
break;
@@ -1284,23 +1286,25 @@ static bool legalizeGFcmp(MachineInstr &MI, MachineIRBuilder &B) {
// fcmp.uno with a non-NaN constant (usually zero). In that case, we don't
// need to generate two fcmps because only the non-const parameter is
// relevant to this comparison
- auto Op0Cst = getFConstantVRegValWithLookThrough(Op0, *B.getMRI());
+ std::optional<llvm::FPValueAndVReg> Op0Cst =
+ getFConstantVRegValWithLookThrough(Op0, *B.getMRI());
bool Op0IsOrdConstant = Op0Cst && !Op0Cst.value().Value.isNaN();
- auto Op1Cst = getFConstantVRegValWithLookThrough(Op1, *B.getMRI());
+ std::optional<llvm::FPValueAndVReg> Op1Cst =
+ getFConstantVRegValWithLookThrough(Op1, *B.getMRI());
bool Op1IsOrdConstant = Op1Cst && !Op1Cst.value().Value.isNaN();
if (Op0IsOrdConstant || Op1IsOrdConstant) {
- auto Reg = Op1IsOrdConstant ? Op0 : Op1;
+ llvm::Register Reg = Op1IsOrdConstant ? Op0 : Op1;
B.buildFCmp(CmpInst::FCMP_UNE, Dst, Reg, Reg, Flags);
} else {
// If the operands are both non-constant, we need to split this into two
// fcmps to ensure it returns false if they are unequal
- auto LHS =
+ llvm::MachineInstrBuilder LHS =
B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_UNE, I1, Op0, Op0, Flags));
- auto RHS =
+ llvm::MachineInstrBuilder RHS =
B.buildSExt(I32, B.buildFCmp(CmpInst::FCMP_UNE, I1, Op1, Op1, Flags));
- auto Result = B.buildOr(I32, LHS, RHS);
+ llvm::MachineInstrBuilder Result = B.buildOr(I32, LHS, RHS);
B.buildICmp(CmpInst::ICMP_EQ, Dst, Result, B.buildConstant(I32, -1));
}
MI.eraseFromParent();
@@ -1310,9 +1314,10 @@ static bool legalizeGFcmp(MachineInstr &MI, MachineIRBuilder &B) {
case CmpInst::FCMP_UGE:
case CmpInst::FCMP_ULT:
case CmpInst::FCMP_ULE: {
- auto Cmp = B.buildSExt(I32, B.buildFCmp(FCmpInst::getInversePredicate(Pred),
- I1, Op0, Op1, Flags));
- auto Not = B.buildNot(I32, Cmp);
+ llvm::MachineInstrBuilder Cmp = B.buildSExt(
+ I32, B.buildFCmp(FCmpInst::getInversePredicate(Pred), I1, Op0, Op1,
+ Flags));
+ llvm::MachineInstrBuilder Not = B.buildNot(I32, Cmp);
B.buildICmp(CmpInst::ICMP_EQ, Dst, Not, B.buildConstant(I32, -1));
MI.eraseFromParent();
break;
@@ -1324,20 +1329,22 @@ static bool legalizeGFcmp(MachineInstr &MI, MachineIRBuilder &B) {
}
static bool legalizeGTrunc(MachineInstr &MI, MachineIRBuilder &B) {
- [[maybe_unused]] auto &MRI = *B.getMRI();
- auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
+ [[maybe_unused]] llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ Register Dst, Src;
+ LLT DstTy, SrcTy;
+ std::tie(Dst, DstTy, Src, SrcTy) = MI.getFirst2RegLLTs();
if (DstTy.getSizeInBits() == 1) {
// truncate ??? to i1
// Since PISA does not support truncs to i1 (i8 is the minimum), we must
// turn it into an i1 by using an icmp instruction.
- auto Zero = B.buildConstant(SrcTy, 0);
- auto One = B.buildConstant(SrcTy, 1);
- auto And = B.buildAnd(SrcTy, Src, One);
+ llvm::MachineInstrBuilder Zero = B.buildConstant(SrcTy, 0);
+ llvm::MachineInstrBuilder One = B.buildConstant(SrcTy, 1);
+ llvm::MachineInstrBuilder And = B.buildAnd(SrcTy, Src, One);
B.buildICmp(CmpInst::ICMP_NE, Dst, And, Zero);
} else {
// truncate i128 to ???
assert(SrcTy.getSizeInBits() == 128);
- auto Unmerge = B.buildUnmerge(I64, Src);
+ llvm::MachineInstrBuilder Unmerge = B.buildUnmerge(I64, Src);
if (DstTy.getSizeInBits() == 64)
B.buildCopy(Dst, Unmerge.getReg(0));
else
@@ -1348,25 +1355,27 @@ static bool legalizeGTrunc(MachineInstr &MI, MachineIRBuilder &B) {
}
static bool legalizeGExt(MachineInstr &MI, MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
- auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ Register Dst, Src;
+ LLT DstTy, SrcTy;
+ std::tie(Dst, DstTy, Src, SrcTy) = MI.getFirst2RegLLTs();
if (MRI.getType(Src).getSizeInBits() == 1) {
// i8 = G_*EXT i1
- auto Zero = B.buildConstant(DstTy, 0);
+ llvm::MachineInstrBuilder Zero = B.buildConstant(DstTy, 0);
int64_t ExtendedVal = (MI.getOpcode() == TargetOpcode::G_SEXT) ||
(MI.getOpcode() == TargetOpcode::G_ANYEXT)
? -1
: 1;
- auto One = B.buildConstant(DstTy, ExtendedVal);
+ llvm::MachineInstrBuilder One = B.buildConstant(DstTy, ExtendedVal);
B.buildSelect(Dst, Src, One, Zero);
} else {
// any G_*EXT where source and destination are byte size
- auto DstSize = DstTy.getScalarSizeInBits();
- auto SrcSize = SrcTy.getScalarSizeInBits();
+ unsigned DstSize = DstTy.getScalarSizeInBits();
+ unsigned SrcSize = SrcTy.getScalarSizeInBits();
assert((DstSize % 8 == 0) && "destination size is not byte size");
assert((SrcSize % 8 == 0) && "source size is not byte size");
- auto EltSize =
+ int EltSize =
((DstSize % 32 == 0) && (SrcSize % 32 == 0))
? 32
: (((DstSize % 16 == 0) && (SrcSize % 16 == 0)) ? 16 : 8);
@@ -1375,7 +1384,7 @@ static bool legalizeGExt(MachineInstr &MI, MachineIRBuilder &B) {
LLT EltTy = LLT::integer(EltSize);
LLT VecDstTy = LLT::fixed_vector(NumDstElts, EltTy);
- auto VecZero = MRI.createGenericVirtualRegister(VecDstTy);
+ llvm::Register VecZero = MRI.createGenericVirtualRegister(VecDstTy);
SmallVector<APInt> Zeros(NumDstElts, APInt(EltSize, 0));
B.buildBuildVectorConstant(VecZero, Zeros);
@@ -1396,13 +1405,13 @@ static bool legalizeGExt(MachineInstr &MI, MachineIRBuilder &B) {
: MRI.getType(VecSrc).getNumElements());
}
- auto VecDst = MRI.createGenericVirtualRegister(VecDstTy);
+ llvm::Register VecDst = MRI.createGenericVirtualRegister(VecDstTy);
B.buildShuffleVector(VecDst, VecSrc, VecZero, Mask);
if (MI.getOpcode() == TargetOpcode::G_SEXT) {
- auto CastReg = MRI.createGenericVirtualRegister(DstTy);
- auto ShiftReg = MRI.createGenericVirtualRegister(DstTy);
- auto ShiftAmt = B.buildConstant(I32, DstSize - SrcSize);
+ llvm::Register CastReg = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ShiftReg = MRI.createGenericVirtualRegister(DstTy);
+ llvm::MachineInstrBuilder ShiftAmt = B.buildConstant(I32, DstSize - SrcSize);
B.buildBitcast(CastReg, VecDst);
B.buildShl(ShiftReg, CastReg, ShiftAmt);
B.buildAShr(Dst, ShiftReg, ShiftAmt);
@@ -1415,7 +1424,9 @@ static bool legalizeGExt(MachineInstr &MI, MachineIRBuilder &B) {
}
static bool legalizeGItofp(MachineInstr &MI, MachineIRBuilder &B) {
- auto [Dst, DstTy, Src, SrcTy] = MI.getFirst2RegLLTs();
+ Register Dst, Src;
+ LLT DstTy, SrcTy;
+ std::tie(Dst, DstTy, Src, SrcTy) = MI.getFirst2RegLLTs();
assert(SrcTy.isScalar() && SrcTy.getSizeInBits() == 1 &&
"Unexpected source type");
assert(DstTy.isScalar() && DstTy.getSizeInBits() == 16 &&
@@ -1428,12 +1439,12 @@ static bool legalizeGItofp(MachineInstr &MI, MachineIRBuilder &B) {
const fltSemantics &Semantics =
DstTy == LLT::bfloat16() ? APFloat::BFloat() : APFloat::IEEEhalf();
- auto TrueVal =
+ llvm::APFloat TrueVal =
APFloat::getOne(Semantics, /*Negative=*/Opc == TargetOpcode::G_SITOFP);
- auto FalseVal = APFloat::getZero(Semantics);
+ llvm::APFloat FalseVal = APFloat::getZero(Semantics);
- auto True = B.buildFConstant(DstTy, TrueVal);
- auto False = B.buildFConstant(DstTy, FalseVal);
+ llvm::MachineInstrBuilder True = B.buildFConstant(DstTy, TrueVal);
+ llvm::MachineInstrBuilder False = B.buildFConstant(DstTy, FalseVal);
B.buildSelect(Dst, Src, True, False);
MI.eraseFromParent();
return true;
@@ -1442,21 +1453,21 @@ static bool legalizeGItofp(MachineInstr &MI, MachineIRBuilder &B) {
static void updateRegInDebugValue(Register OriginalVal, Register NewVal,
MachineRegisterInfo &MRI) {
llvm::SmallVector<MachineOperand *, 5> Opnds;
- for (auto &Instr : MRI.use_instructions(OriginalVal)) {
+ for (llvm::MachineInstr &Instr : MRI.use_instructions(OriginalVal)) {
if (!Instr.isDebugValue())
continue;
- for (auto &Opnd : Instr.operands()) {
+ for (llvm::MachineOperand &Opnd : Instr.operands()) {
if (Opnd.isReg() && Opnd.getReg() == OriginalVal)
Opnds.push_back(&Opnd);
}
}
- for (auto *Opnd : Opnds)
+ for (llvm::MachineOperand *Opnd : Opnds)
Opnd->setReg(NewVal);
return;
}
static bool legalizeGExtload(MachineInstr &MI, MachineIRBuilder &B) {
- auto &LoadMI = cast<GExtLoad>(MI);
+ llvm::GExtLoad &LoadMI = cast<GExtLoad>(MI);
Register DstReg = LoadMI.getDstReg();
Register PtrReg = LoadMI.getPointerReg();
LLT MemTy = LoadMI.getMMO().getMemoryType();
@@ -1469,7 +1480,7 @@ static bool legalizeGExtload(MachineInstr &MI, MachineIRBuilder &B) {
// Narrow load + extension: G_{S,Z}EXTLOAD(DstTy, ptr) ->
// %narrow = G_LOAD MemTy, ptr
// DstReg = G_{S,Z}EXT DstTy, %narrow
- auto NarrowLoad = B.buildLoad(MemTy, PtrReg, LoadMI.getMMO());
+ llvm::MachineInstrBuilder NarrowLoad = B.buildLoad(MemTy, PtrReg, LoadMI.getMMO());
if (isa<GSExtLoad>(MI))
B.buildSExt(DstReg, NarrowLoad);
else
@@ -1480,19 +1491,19 @@ static bool legalizeGExtload(MachineInstr &MI, MachineIRBuilder &B) {
static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
LegalizerHelper &Helper) {
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
GISelChangeObserver &Observer = Helper.Observer;
- auto &ValMO = MI.getOperand(0);
+ llvm::MachineOperand &ValMO = MI.getOperand(0);
Register Val = ValMO.getReg();
MachineMemOperand &MMO = **MI.memoperands_begin();
unsigned AddressSpace = MMO.getAddrSpace();
LLT CurTy = MRI.getType(Val);
- auto CurTySize = CurTy.getSizeInBits();
+ llvm::TypeSize CurTySize = CurTy.getSizeInBits();
if (!CurTy.isVector() && ((CurTySize % 8) != 0)) {
// Widen sub-byte scalar load/store to multiple of 8 bits.
- auto NewSize = (CurTySize + 7) & ~7;
- auto NewTy = LLT::integer(NewSize);
+ uint64_t NewSize = (CurTySize + 7) & ~7;
+ llvm::LLT NewTy = LLT::integer(NewSize);
if (MI.getOpcode() == TargetOpcode::G_LOAD) {
// For loads: widen the load and truncate result.
Helper.widenScalar(MI, 0, NewTy);
@@ -1552,7 +1563,7 @@ static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
// e.g. <2 x i4> -> i8, <2 x i2> -> i4 -> i8, <65 x i2> -> i130 -> i136
unsigned ScalarSize = CurTySize;
unsigned NewSize = std::max(8u, ((ScalarSize + 7) & ~7u));
- auto NewTy = LLT::integer(NewSize);
+ llvm::LLT NewTy = LLT::integer(NewSize);
if (MI.getOpcode() == TargetOpcode::G_LOAD) {
Register NewVal = MRI.createGenericVirtualRegister(NewTy);
Observer.changingInstr(MI);
@@ -1563,7 +1574,7 @@ static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
if (ScalarSize == NewSize) {
B.buildBitcast(Val, NewVal);
} else {
- auto Trunc = B.buildTrunc(LLT::integer(ScalarSize), NewVal);
+ llvm::MachineInstrBuilder Trunc = B.buildTrunc(LLT::integer(ScalarSize), NewVal);
B.buildBitcast(Val, Trunc);
}
} else {
@@ -1605,13 +1616,13 @@ static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
Align AlignInBytes = MMO.getAlign();
Register OriginalVal = ValMO.getReg();
Register NewVal;
- auto NumElts = CurTy.getNumElements();
+ uint16_t NumElts = CurTy.getNumElements();
// If alignment is at least 8 bytes and number of elements is 5 or 6,
// widen to 3 elements of i64. Otherwise, widen to 8 elements of i32.
bool CanWidenToI64 = (AlignInBytes.value() >= 8) && (NumElts <= 6);
- auto NewTy = CanWidenToI64 ? LLT::fixed_vector(3, LLT::integer(64))
+ llvm::LLT NewTy = CanWidenToI64 ? LLT::fixed_vector(3, LLT::integer(64))
: LLT::fixed_vector(8, LLT::integer(32));
- auto VecN32Ty = LLT::fixed_vector(NumElts, LLT::integer(32));
+ llvm::LLT VecN32Ty = LLT::fixed_vector(NumElts, LLT::integer(32));
// Create the new widened load/store
Observer.changingInstr(MI);
@@ -1627,8 +1638,8 @@ static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
if (CanWidenToI64) {
// Handle vectors with 5 or 6 elements of i32 with alignment >= 8 bytes
// Legalize to 3 elements of i64 for better hardware utilization
- auto V6i32Ty = LLT::fixed_vector(6, LLT::integer(32));
- auto ExtrVal = MRI.createGenericVirtualRegister(V6i32Ty);
+ llvm::LLT V6i32Ty = LLT::fixed_vector(6, LLT::integer(32));
+ llvm::Register ExtrVal = MRI.createGenericVirtualRegister(V6i32Ty);
// Bitcast to 6xi32 first, then extract the
// first 5 elements
B.buildBitcast(ExtrVal, NewVal);
@@ -1648,7 +1659,7 @@ static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
}
} else if (CurTy.getScalarType().isPointer()) {
// load/store of ptr requires inttoptr/ptrtoint
- auto EltSize = CurTy.getScalarSizeInBits();
+ unsigned EltSize = CurTy.getScalarSizeInBits();
LLT NewTy = CurTy.changeElementType(LLT::integer(EltSize));
Register NewVal = MRI.createGenericVirtualRegister(NewTy);
MMO.setType(NewTy);
@@ -1660,9 +1671,9 @@ static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
B.buildPtrToInt(NewVal, Val);
}
} else if (CurTy.getScalarSizeInBits() == 1) {
- auto BitSize = CurTy.getSizeInBits();
- auto NumEltsI8 = (BitSize + 7) / 8;
- auto NewBitSize = NumEltsI8 * 8;
+ llvm::TypeSize BitSize = CurTy.getSizeInBits();
+ uint64_t NumEltsI8 = (BitSize + 7) / 8;
+ uint64_t NewBitSize = NumEltsI8 * 8;
assert(CurTy.isVector() &&
"Expected only vector of i1 to reach here, scalar was extended to "
"i8 on widen scalars to be multiple of 8");
@@ -1702,39 +1713,39 @@ static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
}
static bool legalizeGFrem(MachineInstr &MI, MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
Register DstReg = MI.getOperand(0).getReg();
Register Src0Reg = MI.getOperand(1).getReg();
Register Src1Reg = MI.getOperand(2).getReg();
- auto Flags = MI.getFlags();
- auto FmAfn = Flags & MachineInstr::FmAfn;
+ unsigned Flags = MI.getFlags();
+ unsigned FmAfn = Flags & MachineInstr::FmAfn;
LLT Ty = MRI.getType(DstReg);
- auto DivFlags = Flags;
+ unsigned DivFlags = Flags;
if (FmAfn) {
DivFlags &= ~MachineInstr::FmAfn;
DivFlags |= MachineInstr::FmArcp;
}
- auto Div = B.buildFDiv(Ty, Src0Reg, Src1Reg, DivFlags);
- auto Trunc = B.buildIntrinsicTrunc(Ty, Div, Flags);
- auto Neg = B.buildFNeg(Ty, Trunc, Flags);
+ llvm::MachineInstrBuilder Div = B.buildFDiv(Ty, Src0Reg, Src1Reg, DivFlags);
+ llvm::MachineInstrBuilder Trunc = B.buildIntrinsicTrunc(Ty, Div, Flags);
+ llvm::MachineInstrBuilder Neg = B.buildFNeg(Ty, Trunc, Flags);
if (!FmAfn) {
- auto FMA = B.buildFMA(Ty, Neg, Src1Reg, Src0Reg, Flags);
+ llvm::MachineInstrBuilder FMA = B.buildFMA(Ty, Neg, Src1Reg, Src0Reg, Flags);
- auto &Semantics = getFltSemanticForLLT(Ty.getScalarType());
- auto InfC = B.buildFConstant(Ty, APFloat::getInf(Semantics));
+ const llvm::fltSemantics &Semantics = getFltSemanticForLLT(Ty.getScalarType());
+ llvm::MachineInstrBuilder InfC = B.buildFConstant(Ty, APFloat::getInf(Semantics));
- auto XAbs = B.buildIntrinsic(Intrinsic::pisa_fabs, {Ty})
+ llvm::MachineInstrBuilder XAbs = B.buildIntrinsic(Intrinsic::pisa_fabs, {Ty})
.addUse(Src0Reg)
.setMIFlags(Flags);
- auto YAbs = B.buildIntrinsic(Intrinsic::pisa_fabs, {Ty})
+ llvm::MachineInstrBuilder YAbs = B.buildIntrinsic(Intrinsic::pisa_fabs, {Ty})
.addUse(Src1Reg)
.setMIFlags(Flags);
// Using pisa_fabs is safe here: the result is only compared against Inf
// via OEQ, which is false for any NaN regardless of signaling/quiet.
- auto XFCmp = B.buildFCmp(FCmpInst::FCMP_OEQ, I1, XAbs, InfC, Flags);
- auto YFCmp = B.buildFCmp(FCmpInst::FCMP_OEQ, I1, YAbs, InfC, Flags);
- auto Sel = B.buildSelect(Ty, YFCmp, Src0Reg, FMA);
+ llvm::MachineInstrBuilder XFCmp = B.buildFCmp(FCmpInst::FCMP_OEQ, I1, XAbs, InfC, Flags);
+ llvm::MachineInstrBuilder YFCmp = B.buildFCmp(FCmpInst::FCMP_OEQ, I1, YAbs, InfC, Flags);
+ llvm::MachineInstrBuilder Sel = B.buildSelect(Ty, YFCmp, Src0Reg, FMA);
B.buildSelect(DstReg, XFCmp, FMA, Sel);
} else {
B.buildFMA(DstReg, Neg, Src1Reg, Src0Reg, Flags);
@@ -1755,10 +1766,10 @@ static bool legalizeFAbs(MachineInstr &MI, MachineIRBuilder &B) {
// <2 x half> / <2 x bfloat>: pack into a single 32-bit AND.
if (Ty.isVector() && Ty.getNumElements() == 2 &&
Ty.getScalarSizeInBits() == 16) {
- auto Src32 = B.buildBitcast(I32, SrcReg);
+ llvm::MachineInstrBuilder Src32 = B.buildBitcast(I32, SrcReg);
// 0x7FFF7FFF: clears the sign bit of each 16-bit element.
- auto Mask = B.buildConstant(I32, 0x7FFF7FFF);
- auto And = B.buildAnd(I32, Src32, Mask);
+ llvm::MachineInstrBuilder Mask = B.buildConstant(I32, 0x7FFF7FFF);
+ llvm::MachineInstrBuilder And = B.buildAnd(I32, Src32, Mask);
B.buildBitcast(DstReg, And);
MI.eraseFromParent();
return true;
@@ -1780,8 +1791,8 @@ static bool legalizeFAbs(MachineInstr &MI, MachineIRBuilder &B) {
if (IsTypedFloat)
IntSrc = B.buildBitcast(IntTy, SrcReg).getReg(0);
- auto MaskCst = B.buildConstant(IntTy, Mask);
- auto And = B.buildAnd(IntTy, IntSrc, MaskCst);
+ llvm::MachineInstrBuilder MaskCst = B.buildConstant(IntTy, Mask);
+ llvm::MachineInstrBuilder And = B.buildAnd(IntTy, IntSrc, MaskCst);
if (IsTypedFloat)
B.buildBitcast(DstReg, And);
@@ -1801,16 +1812,17 @@ static bool legalizeGFdiv(MachineInstr &MI, MachineIRBuilder &B) {
return true;
// perform converts
- auto &MRI = *B.getMRI();
- auto [DstReg, Src0Reg, Src1Reg] = MI.getFirst3Regs();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ Register DstReg, Src0Reg, Src1Reg;
+ std::tie(DstReg, Src0Reg, Src1Reg) = MI.getFirst3Regs();
- auto Src0Tmp = MRI.createGenericVirtualRegister(F32);
- auto Src1Tmp = MRI.createGenericVirtualRegister(F32);
- auto DstTmp = MRI.createGenericVirtualRegister(F32);
+ llvm::Register Src0Tmp = MRI.createGenericVirtualRegister(F32);
+ llvm::Register Src1Tmp = MRI.createGenericVirtualRegister(F32);
+ llvm::Register DstTmp = MRI.createGenericVirtualRegister(F32);
- auto FPExt0 = B.buildFPExt(Src0Tmp, Src0Reg);
- auto FPExt1 = B.buildFPExt(Src1Tmp, Src1Reg);
- auto FDiv = B.buildFDiv(DstTmp, FPExt0, FPExt1);
+ llvm::MachineInstrBuilder FPExt0 = B.buildFPExt(Src0Tmp, Src0Reg);
+ llvm::MachineInstrBuilder FPExt1 = B.buildFPExt(Src1Tmp, Src1Reg);
+ llvm::MachineInstrBuilder FDiv = B.buildFDiv(DstTmp, FPExt0, FPExt1);
B.buildFPTrunc(DstReg, FDiv);
MI.eraseFromParent();
@@ -1819,7 +1831,7 @@ static bool legalizeGFdiv(MachineInstr &MI, MachineIRBuilder &B) {
static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
Register SrcReg = MI.getOperand(1).getReg();
Register EltReg = MI.getOperand(2).getReg();
Register IndexReg = MI.getOperand(3).getReg();
@@ -1838,24 +1850,24 @@ static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
LLT ScalarTy = LLT::integer(Size);
- auto SScalarReg = MRI.createGenericVirtualRegister(ScalarTy);
- auto DScalarReg = MRI.createGenericVirtualRegister(ScalarTy);
- auto MaskReg = MRI.createGenericVirtualRegister(ScalarTy);
- auto NotReg = MRI.createGenericVirtualRegister(ScalarTy);
- auto AndReg = MRI.createGenericVirtualRegister(ScalarTy);
- auto ShiftReg = MRI.createGenericVirtualRegister(ScalarTy);
- auto EltZExtReg = MRI.createGenericVirtualRegister(ScalarTy);
- auto ShiftAmountReg = IndexReg;
+ llvm::Register SScalarReg = MRI.createGenericVirtualRegister(ScalarTy);
+ llvm::Register DScalarReg = MRI.createGenericVirtualRegister(ScalarTy);
+ llvm::Register MaskReg = MRI.createGenericVirtualRegister(ScalarTy);
+ llvm::Register NotReg = MRI.createGenericVirtualRegister(ScalarTy);
+ llvm::Register AndReg = MRI.createGenericVirtualRegister(ScalarTy);
+ llvm::Register ShiftReg = MRI.createGenericVirtualRegister(ScalarTy);
+ llvm::Register EltZExtReg = MRI.createGenericVirtualRegister(ScalarTy);
+ llvm::Register ShiftAmountReg = IndexReg;
B.buildBitcast(SScalarReg, SrcReg);
- auto Value = getIConstantVRegValWithLookThrough(IndexReg, MRI);
+ std::optional<llvm::ValueAndVReg> Value = getIConstantVRegValWithLookThrough(IndexReg, MRI);
if (Value.has_value()) { // constant index
B.buildConstant(MaskReg, 1ull << Value->Value.getZExtValue());
ShiftAmountReg = MRI.createGenericVirtualRegister(ScalarTy);
B.buildConstant(ShiftAmountReg, Value->Value.getZExtValue());
} else { // non-constant index
- auto ConstReg = MRI.createGenericVirtualRegister(ScalarTy);
+ llvm::Register ConstReg = MRI.createGenericVirtualRegister(ScalarTy);
B.buildConstant(ConstReg, 1ull);
B.buildShl(MaskReg, ConstReg, IndexReg);
}
@@ -1874,7 +1886,8 @@ static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
"unexpected element size");
// If the index is constant, narrow the vector down to 4 elements.
- if (auto MaybeValue = getIConstantVRegValWithLookThrough(IndexReg, MRI)) {
+ if (std::optional<llvm::ValueAndVReg> MaybeValue =
+ getIConstantVRegValWithLookThrough(IndexReg, MRI)) {
if (NumElts <= 4) {
SmallVector<Register, 4> Elements;
for (int I = 0; I < NumElts; ++I)
@@ -1888,7 +1901,8 @@ static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
}
// Narrow to 4 elements.
- auto Res = Helper.fewerElementsVector(MI, 0, LLT::fixed_vector(4, EltTy));
+ llvm::LegalizerHelper::LegalizeResult Res =
+ Helper.fewerElementsVector(MI, 0, LLT::fixed_vector(4, EltTy));
return Res != LegalizerHelper::UnableToLegalize;
}
@@ -1901,7 +1915,7 @@ static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
if (NumElts * EltSize % 32 != 0) {
int NewNumElts = alignTo(NumElts, 32 / EltSize);
LLT NewVecTy = LLT::fixed_vector(NewNumElts, EltTy);
- auto Res = Helper.moreElementsVector(MI, 0, NewVecTy);
+ llvm::LegalizerHelper::LegalizeResult Res = Helper.moreElementsVector(MI, 0, NewVecTy);
if (Res == LegalizerHelper::UnableToLegalize)
return false;
NumElts = NewNumElts;
@@ -1937,7 +1951,7 @@ static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
}
int NewNumElts = NumElts * EltSize / 32;
LLT NewVecTy = NewNumElts == 1 ? I32 : LLT::fixed_vector(NewNumElts, I32);
- auto Res = Helper.bitcastInsertVectorElt(MI, 0, NewVecTy);
+ llvm::LegalizerHelper::LegalizeResult Res = Helper.bitcastInsertVectorElt(MI, 0, NewVecTy);
if (NeedFloatBitcast && Res != LegalizerHelper::UnableToLegalize) {
MachineInstr *DefMI = MRI.getVRegDef(IntDst);
B.setInsertPt(*DefMI->getParent(), std::next(DefMI->getIterator()));
@@ -1950,23 +1964,23 @@ static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
LLT NewVecTy = LLT::fixed_vector(NumElts * 2, I32);
// Compute the low and high indices as low = index * 2, high = low + 1
- auto One = B.buildConstant(IndexTy, 1).getReg(0);
- auto LowIndexReg = B.buildShl(IndexTy, IndexReg, One).getReg(0);
- auto HighIndexReg = B.buildAdd(IndexTy, LowIndexReg, One).getReg(0);
+ llvm::Register One = B.buildConstant(IndexTy, 1).getReg(0);
+ llvm::Register LowIndexReg = B.buildShl(IndexTy, IndexReg, One).getReg(0);
+ llvm::Register HighIndexReg = B.buildAdd(IndexTy, LowIndexReg, One).getReg(0);
// Split the 64-bit element into two 32-bit elements
- auto EltLowReg = MRI.createGenericVirtualRegister(I32);
- auto EltHighReg = MRI.createGenericVirtualRegister(I32);
+ llvm::Register EltLowReg = MRI.createGenericVirtualRegister(I32);
+ llvm::Register EltHighReg = MRI.createGenericVirtualRegister(I32);
B.buildUnmerge({EltLowReg, EltHighReg}, EltReg);
// Bitcast the source vector to s32 vector
- auto BitcastSrcReg = B.buildBitcast(NewVecTy, SrcReg).getReg(0);
+ llvm::Register BitcastSrcReg = B.buildBitcast(NewVecTy, SrcReg).getReg(0);
// Insert the low and high parts
- auto InsertLowReg = B.buildInsertVectorElement(NewVecTy, BitcastSrcReg,
+ llvm::Register InsertLowReg = B.buildInsertVectorElement(NewVecTy, BitcastSrcReg,
EltLowReg, LowIndexReg)
.getReg(0);
- auto InsertHighReg = B.buildInsertVectorElement(NewVecTy, InsertLowReg,
+ llvm::Register InsertHighReg = B.buildInsertVectorElement(NewVecTy, InsertLowReg,
EltHighReg, HighIndexReg)
.getReg(0);
@@ -1978,7 +1992,7 @@ static bool legalizeGInsertVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
static bool legalizeGExtractVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
Register VecReg = MI.getOperand(1).getReg();
LLT VecTy = MRI.getType(VecReg);
LLT EltTy = VecTy.getScalarType();
@@ -1987,10 +2001,10 @@ static bool legalizeGExtractVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
if (EltSize == 1) {
// Handle extraction of <n x i1>
- auto Size = MRI.getType(VecReg).getSizeInBits();
- auto CastReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
+ llvm::TypeSize Size = MRI.getType(VecReg).getSizeInBits();
+ llvm::Register CastReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
B.buildBitcast(CastReg, VecReg);
- auto ShiftReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
+ llvm::Register ShiftReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
B.buildLShr(ShiftReg, CastReg, MI.getOperand(2));
B.buildTrunc(MI.getOperand(0), ShiftReg);
MI.eraseFromParent();
@@ -2002,7 +2016,8 @@ static bool legalizeGExtractVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
// If the index is constant, narrow the vector down to 4 elements.
Register IndexReg = MI.getOperand(2).getReg();
- if (auto MaybeValue = getIConstantVRegValWithLookThrough(IndexReg, MRI)) {
+ if (std::optional<llvm::ValueAndVReg> MaybeValue =
+ getIConstantVRegValWithLookThrough(IndexReg, MRI)) {
if (NumElts <= 4) {
Register UnmergeReg = B.buildUnmerge(EltTy, VecReg)
.getReg(MaybeValue->Value.getZExtValue());
@@ -2012,7 +2027,8 @@ static bool legalizeGExtractVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
}
// Narrow to 4 elements.
- auto Res = Helper.fewerElementsVector(MI, 1, LLT::fixed_vector(4, EltTy));
+ llvm::LegalizerHelper::LegalizeResult Res =
+ Helper.fewerElementsVector(MI, 1, LLT::fixed_vector(4, EltTy));
return Res != LegalizerHelper::UnableToLegalize;
}
@@ -2024,7 +2040,7 @@ static bool legalizeGExtractVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
if (NumElts * EltSize % 32 != 0) {
int NewNumElts = alignTo(NumElts, 32 / EltSize);
LLT NewVecTy = LLT::fixed_vector(NewNumElts, EltTy);
- auto Res = Helper.moreElementsVector(MI, 1, NewVecTy);
+ llvm::LegalizerHelper::LegalizeResult Res = Helper.moreElementsVector(MI, 1, NewVecTy);
if (Res == LegalizerHelper::UnableToLegalize)
return false;
NumElts = NewNumElts;
@@ -2052,7 +2068,7 @@ static bool legalizeGExtractVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
}
int NewNumElts = NumElts * EltSize / 32;
LLT NewVecTy = NewNumElts == 1 ? I32 : LLT::fixed_vector(NewNumElts, I32);
- auto Res = Helper.bitcastExtractVectorElt(MI, 1, NewVecTy);
+ llvm::LegalizerHelper::LegalizeResult Res = Helper.bitcastExtractVectorElt(MI, 1, NewVecTy);
if (NeedFloatBitcast && Res != LegalizerHelper::UnableToLegalize) {
// MI has been erased by the helper. IntDst now has an integer-typed def
// from the helper's lowered sequence. Bitcast it back to the original
@@ -2065,8 +2081,9 @@ static bool legalizeGExtractVectorElt(LegalizerHelper &Helper, MachineInstr &MI,
}
static bool legalizeGBswap(MachineInstr &MI, MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
- auto [Dst, Src] = MI.getFirst2Regs();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ Register Dst, Src;
+ std::tie(Dst, Src) = MI.getFirst2Regs();
const LLT Ty = MRI.getType(Src);
unsigned BitSize = Ty.getScalarSizeInBits();
@@ -2083,14 +2100,18 @@ static bool legalizeGBswap(MachineInstr &MI, MachineIRBuilder &B) {
// a chunk.
// - ChunkShuffleVecTy: the vector type used for shuffling bytes within a
// chunk.
- auto GetSwapProps =
+ std::function<std::tuple<unsigned, ArrayRef<int>, LLT>(unsigned)>
+ GetSwapProps =
[&](unsigned BitSize) -> std::tuple<unsigned, ArrayRef<int>, LLT> {
return (BitSize % 32 == 0)
? std::make_tuple(32u, ArrayRef<int>(SwapMask32), V4I8)
: std::make_tuple(16u, ArrayRef<int>(SwapMask16), V2I8);
};
- auto [ChunkSize, ChunkByteSwapMask, ChunkShuffleVecTy] =
+ unsigned ChunkSize;
+ ArrayRef<int> ChunkByteSwapMask;
+ LLT ChunkShuffleVecTy;
+ std::tie(ChunkSize, ChunkByteSwapMask, ChunkShuffleVecTy) =
GetSwapProps(BitSize);
// For Src types that are multiples of 32 bits, the value is divided into
@@ -2101,28 +2122,28 @@ static bool legalizeGBswap(MachineInstr &MI, MachineIRBuilder &B) {
if (BitSize == 16 || BitSize == 32) {
assert(ChunkSize == BitSize &&
"Single chunk case: ChunkSize must equal BitSize");
- auto VecReg = B.buildBitcast(ChunkShuffleVecTy, Src);
- auto ShufReg = B.buildShuffleVector(ChunkShuffleVecTy, VecReg, VecReg,
+ llvm::MachineInstrBuilder VecReg = B.buildBitcast(ChunkShuffleVecTy, Src);
+ llvm::MachineInstrBuilder ShufReg = B.buildShuffleVector(ChunkShuffleVecTy, VecReg, VecReg,
ChunkByteSwapMask);
B.buildBitcast(Dst, ShufReg);
} else {
unsigned NumChunks = BitSize / ChunkSize;
LLT ChunkTy = LLT::integer(ChunkSize);
LLT VecTy = LLT::fixed_vector(NumChunks, ChunkTy);
- auto VecReg = B.buildBitcast(VecTy, Src);
+ llvm::MachineInstrBuilder VecReg = B.buildBitcast(VecTy, Src);
SmallVector<Register, 8> SwappedChunks;
for (int I = NumChunks - 1; I >= 0; --I) {
- auto Index = B.buildConstant(I32, I);
- auto ChunkReg = B.buildExtractVectorElement(ChunkTy, VecReg, Index);
- auto ChunkVec = B.buildBitcast(ChunkShuffleVecTy, ChunkReg);
- auto SwappedVec = B.buildShuffleVector(ChunkShuffleVecTy, ChunkVec,
+ llvm::MachineInstrBuilder Index = B.buildConstant(I32, I);
+ llvm::MachineInstrBuilder ChunkReg = B.buildExtractVectorElement(ChunkTy, VecReg, Index);
+ llvm::MachineInstrBuilder ChunkVec = B.buildBitcast(ChunkShuffleVecTy, ChunkReg);
+ llvm::MachineInstrBuilder SwappedVec = B.buildShuffleVector(ChunkShuffleVecTy, ChunkVec,
ChunkVec, ChunkByteSwapMask);
- auto SwappedChunk = B.buildBitcast(ChunkTy, SwappedVec);
+ llvm::MachineInstrBuilder SwappedChunk = B.buildBitcast(ChunkTy, SwappedVec);
SwappedChunks.push_back(SwappedChunk.getReg(0));
}
- auto FinalVec = B.buildBuildVector(VecTy, SwappedChunks);
+ llvm::MachineInstrBuilder FinalVec = B.buildBuildVector(VecTy, SwappedChunks);
B.buildBitcast(Dst, FinalVec);
}
@@ -2131,23 +2152,24 @@ static bool legalizeGBswap(MachineInstr &MI, MachineIRBuilder &B) {
}
static bool legalizeGFpow(MachineInstr &MI, MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
- auto [Dst, Src0, Src1] = MI.getFirst3Regs();
- auto DstTy = MRI.getType(Dst);
+ Register Dst, Src0, Src1;
+ std::tie(Dst, Src0, Src1) = MI.getFirst3Regs();
+ llvm::LLT DstTy = MRI.getType(Dst);
assert(DstTy.isScalar() &&
(DstTy.getSizeInBits() == 32 || DstTy.getSizeInBits() == 16));
unsigned Flags = MI.getFlags();
// can only do approximation of pow()
- auto AllowApprox = MI.getFlag(MachineInstr::FmAfn);
+ bool AllowApprox = MI.getFlag(MachineInstr::FmAfn);
if (!AllowApprox)
llvm_unreachable("not implemented (fpow)");
- auto LogReg = MRI.createGenericVirtualRegister(DstTy);
- auto MulReg = MRI.createGenericVirtualRegister(DstTy);
- auto FExp2Reg = Dst;
+ llvm::Register LogReg = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register MulReg = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register FExp2Reg = Dst;
B.buildFLog2(LogReg, Src0, Flags);
B.buildFMul(MulReg, LogReg, Src1, Flags);
@@ -2158,21 +2180,22 @@ static bool legalizeGFpow(MachineInstr &MI, MachineIRBuilder &B) {
}
static bool legalizeGFldexp(MachineInstr &MI, MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
- auto [Dst, Src0, Src1] = MI.getFirst3Regs();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ Register Dst, Src0, Src1;
+ std::tie(Dst, Src0, Src1) = MI.getFirst3Regs();
unsigned Flags = MI.getFlags();
LLT XTy = MRI.getType(Src0);
LLT NTy = MRI.getType(Src1);
LLT Src1Ty = MRI.getType(Src1);
- auto AllowApprox =
+ bool AllowApprox =
XTy.getSizeInBits() <= 32 && MI.getFlag(MachineInstr::FmAfn);
- auto IsBFloat16 = XTy == LLT::bfloat16();
+ bool IsBFloat16 = XTy == LLT::bfloat16();
if (AllowApprox) {
- auto RegLLT = MRI.getType(Dst);
- auto FpReg = MRI.createGenericVirtualRegister(RegLLT);
- auto ExpReg = MRI.createGenericVirtualRegister(RegLLT);
+ llvm::LLT RegLLT = MRI.getType(Dst);
+ llvm::Register FpReg = MRI.createGenericVirtualRegister(RegLLT);
+ llvm::Register ExpReg = MRI.createGenericVirtualRegister(RegLLT);
B.buildSITOFP(FpReg, Src1);
B.buildFExp2(ExpReg, FpReg, Flags);
@@ -2207,13 +2230,13 @@ static bool legalizeGFldexp(MachineInstr &MI, MachineIRBuilder &B) {
// For FP64, |n|>1024+1022+52 will definitely lead to overflow/underflow
// |n|<=1022*3 is a sufficiently wide range for n (and FP64 x)
- auto ClampMax = B.buildConstant(NTy, -NClampRangeVal * 3);
- auto NClampedMax = B.buildSMax(NTy, Src1, ClampMax);
- auto ClampMin = B.buildConstant(NTy, NClampRangeVal * 3);
- auto NClamped = B.buildSMin(NTy, NClampedMax, ClampMin);
+ llvm::MachineInstrBuilder ClampMax = B.buildConstant(NTy, -NClampRangeVal * 3);
+ llvm::MachineInstrBuilder NClampedMax = B.buildSMax(NTy, Src1, ClampMax);
+ llvm::MachineInstrBuilder ClampMin = B.buildConstant(NTy, NClampRangeVal * 3);
+ llvm::MachineInstrBuilder NClamped = B.buildSMin(NTy, NClampedMax, ClampMin);
- auto AddConst = B.buildConstant(NTy, (NClampRangeVal + 1) * 3);
- auto N = B.buildAdd(NTy, NClamped, AddConst);
+ llvm::MachineInstrBuilder AddConst = B.buildConstant(NTy, (NClampRangeVal + 1) * 3);
+ llvm::MachineInstrBuilder N = B.buildAdd(NTy, NClamped, AddConst);
if (XTy.getSizeInBits() == 16 && !IsBFloat16) {
NTy = I16;
N = B.buildTrunc(NTy, N);
@@ -2222,13 +2245,13 @@ static bool legalizeGFldexp(MachineInstr &MI, MachineIRBuilder &B) {
// for fp16, n/3 performed as a 8x8-bit->16-bit integer MUL and SHR by 8.
// for others, n/3, performed as a 16x16-bit->32-bit integer MUL and SHR by 16
// (both LSHR or ASHR work, n is positive at this point)
- auto MulConst = B.buildConstant(NTy, NDivBy3MulVal);
- auto NMul = B.buildMul(NTy, N, MulConst);
- auto ShrConst = B.buildConstant(I32, NDivBy3ShiftVal);
- auto K0 = B.buildLShr(NTy, NMul, ShrConst);
+ llvm::MachineInstrBuilder MulConst = B.buildConstant(NTy, NDivBy3MulVal);
+ llvm::MachineInstrBuilder NMul = B.buildMul(NTy, N, MulConst);
+ llvm::MachineInstrBuilder ShrConst = B.buildConstant(I32, NDivBy3ShiftVal);
+ llvm::MachineInstrBuilder K0 = B.buildLShr(NTy, NMul, ShrConst);
- auto NMinusK0 = B.buildSub(NTy, N, K0);
- auto K1 = B.buildSub(NTy, NMinusK0, K0);
+ llvm::MachineInstrBuilder NMinusK0 = B.buildSub(NTy, N, K0);
+ llvm::MachineInstrBuilder K1 = B.buildSub(NTy, NMinusK0, K0);
if (XTy.getSizeInBits() == 64) {
NTy = I64;
@@ -2240,11 +2263,11 @@ static bool legalizeGFldexp(MachineInstr &MI, MachineIRBuilder &B) {
K1 = B.buildTrunc(NTy, K1);
}
- auto ShlConst = B.buildConstant(I32, NShiftVal);
- auto SK0I = B.buildShl(NTy, K0, ShlConst);
- auto SK1I = B.buildShl(NTy, K1, ShlConst);
- auto SK0 = B.buildBitcast(XTy, SK0I);
- auto SK1 = B.buildBitcast(XTy, SK1I);
+ llvm::MachineInstrBuilder ShlConst = B.buildConstant(I32, NShiftVal);
+ llvm::MachineInstrBuilder SK0I = B.buildShl(NTy, K0, ShlConst);
+ llvm::MachineInstrBuilder SK1I = B.buildShl(NTy, K1, ShlConst);
+ llvm::MachineInstrBuilder SK0 = B.buildBitcast(XTy, SK0I);
+ llvm::MachineInstrBuilder SK1 = B.buildBitcast(XTy, SK1I);
SrcOp SwapperX(Src0), SwapperSK1(SK1);
if (XTy.getSizeInBits() > 16) {
@@ -2252,17 +2275,17 @@ static bool legalizeGFldexp(MachineInstr &MI, MachineIRBuilder &B) {
// overflow (inf). This prevents a potential underflow that can happen with
// Src0 * SK0 * SK0.
int SmallThresholdVal = NClampRangeVal / 3;
- auto SmallThresholdConst = B.buildConstant(Src1Ty, SmallThresholdVal);
- auto Src1Abs = B.buildAbs(Src1Ty, NClamped);
- auto IsSrc1Small = B.buildICmp(CmpInst::Predicate::ICMP_SLT, I1, Src1Abs,
+ llvm::MachineInstrBuilder SmallThresholdConst = B.buildConstant(Src1Ty, SmallThresholdVal);
+ llvm::MachineInstrBuilder Src1Abs = B.buildAbs(Src1Ty, NClamped);
+ llvm::MachineInstrBuilder IsSrc1Small = B.buildICmp(CmpInst::Predicate::ICMP_SLT, I1, Src1Abs,
SmallThresholdConst);
SwapperX = B.buildSelect(XTy, IsSrc1Small, SK1, Src0);
SwapperSK1 = B.buildSelect(XTy, IsSrc1Small, Src0, SK1);
}
- auto Res0 = B.buildFMul(XTy, SwapperX, SK0, Flags);
- auto Res1 = B.buildFMul(XTy, Res0, SK0, Flags);
+ llvm::MachineInstrBuilder Res0 = B.buildFMul(XTy, SwapperX, SK0, Flags);
+ llvm::MachineInstrBuilder Res1 = B.buildFMul(XTy, Res0, SK0, Flags);
B.buildFMul(Dst, Res1, SwapperSK1, Flags);
MI.eraseFromParent();
@@ -2275,7 +2298,7 @@ static bool legalizeGFldexp(MachineInstr &MI, MachineIRBuilder &B) {
// - truncate to 16bit value
static bool legalizeIntrinsicFSqrt(LegalizerHelper &Helper, MachineInstr &MI) {
MachineIRBuilder &B = Helper.MIRBuilder;
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
SmallVector<MachineInstr *, 4> MIs;
Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
@@ -2285,19 +2308,18 @@ static bool legalizeIntrinsicFSqrt(LegalizerHelper &Helper, MachineInstr &MI) {
MIs.push_back(&MI);
}
- for (auto It = MIs.begin(), Ite = MIs.end(); It != Ite; ++It) {
- auto *MI = *It;
+ for (MachineInstr *MI : MIs) {
MachineIRBuilder MIB(*MI);
- auto Dst = MI->getOperand(0).getReg();
- auto Src = MI->getOperand(2).getReg();
- auto Imm = MI->getOperand(3).getImm();
+ llvm::Register Dst = MI->getOperand(0).getReg();
+ llvm::Register Src = MI->getOperand(2).getReg();
+ int64_t Imm = MI->getOperand(3).getImm();
if (MRI.getType(Dst).getScalarSizeInBits() != 16)
continue; // already legal
- auto Src32 = MRI.createGenericVirtualRegister(F32);
- auto Dst32 = MRI.createGenericVirtualRegister(F32);
+ llvm::Register Src32 = MRI.createGenericVirtualRegister(F32);
+ llvm::Register Dst32 = MRI.createGenericVirtualRegister(F32);
MIB.buildFPExt(Src32, Src);
MIB.buildIntrinsic(IntrinsicID, Dst32).addReg(Src32).addImm(Imm);
MIB.buildFPTrunc(Dst, Dst32);
@@ -2308,19 +2330,18 @@ static bool legalizeIntrinsicFSqrt(LegalizerHelper &Helper, MachineInstr &MI) {
static bool legalizeIntrinsicFDiv(LegalizerHelper &Helper, MachineInstr &MI) {
MachineIRBuilder &B = Helper.MIRBuilder;
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
SmallVector<MachineInstr *> MIs = scalarizeIntrinsic(MI);
// fdiv only supports 32/64 width
- for (auto It = MIs.begin(), Ite = MIs.end(); It != Ite; ++It) {
- auto *MI = *It;
+ for (MachineInstr *MI : MIs) {
MachineIRBuilder MIB(*MI);
- auto Dst = MI->getOperand(0).getReg();
- auto Src0 = MI->getOperand(2).getReg();
- auto Src1 = MI->getOperand(3).getReg();
+ llvm::Register Dst = MI->getOperand(0).getReg();
+ llvm::Register Src0 = MI->getOperand(2).getReg();
+ llvm::Register Src1 = MI->getOperand(3).getReg();
if (MRI.getType(Dst).getScalarSizeInBits() != 16)
continue;
@@ -2330,9 +2351,9 @@ static bool legalizeIntrinsicFDiv(LegalizerHelper &Helper, MachineInstr &MI) {
// => s32 C' = FEXT s16 C
// => s32 A' = FDIV s32 B', s32 C'
// => s16 A = FTRUNC s32 A'
- auto Src032 = MRI.createGenericVirtualRegister(F32);
- auto Src132 = MRI.createGenericVirtualRegister(F32);
- auto Dst32 = MRI.createGenericVirtualRegister(F32);
+ llvm::Register Src032 = MRI.createGenericVirtualRegister(F32);
+ llvm::Register Src132 = MRI.createGenericVirtualRegister(F32);
+ llvm::Register Dst32 = MRI.createGenericVirtualRegister(F32);
MIB.buildFPExt(Src032, Src0);
MIB.buildFPExt(Src132, Src1);
MIB.buildIntrinsic(IntrinsicID, Dst32)
@@ -2348,15 +2369,15 @@ static bool legalizeIntrinsicFDiv(LegalizerHelper &Helper, MachineInstr &MI) {
static SmallVector<Register> splitVectorByGrain(MachineIRBuilder &B,
Register Src, unsigned Grain) {
- auto &MRI = *B.getMRI();
- auto SrcTy = MRI.getType(Src);
- auto EltTy = SrcTy.getScalarType();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ llvm::LLT SrcTy = MRI.getType(Src);
+ llvm::LLT EltTy = SrcTy.getScalarType();
- auto SliceTy = LLT::fixed_vector(Grain, EltTy);
+ llvm::LLT SliceTy = LLT::fixed_vector(Grain, EltTy);
if (SrcTy.isScalar()) {
- auto SliceUndef = MRI.createGenericVirtualRegister(SliceTy);
- auto Slice = MRI.createGenericVirtualRegister(SliceTy);
+ llvm::Register SliceUndef = MRI.createGenericVirtualRegister(SliceTy);
+ llvm::Register Slice = MRI.createGenericVirtualRegister(SliceTy);
B.buildUndef(SliceUndef);
B.buildInsertVectorElement(Slice, SliceUndef, Src, B.buildConstant(I32, 0));
return {Slice};
@@ -2366,12 +2387,12 @@ static SmallVector<Register> splitVectorByGrain(MachineIRBuilder &B,
SmallVector<Register> Elts;
for (unsigned I = 0; I < NumElts; I += Grain) {
- auto Slice = MRI.createGenericVirtualRegister(SliceTy);
+ llvm::Register Slice = MRI.createGenericVirtualRegister(SliceTy);
B.buildUndef(Slice);
for (unsigned J = 0; J < std::min(Grain, NumElts - I); ++J) {
- auto Idx = B.buildConstant(I32, J).getReg(0);
- auto Elt =
+ llvm::Register Idx = B.buildConstant(I32, J).getReg(0);
+ llvm::Register Elt =
B.buildExtractVectorElementConstant(EltTy, Src, I + J).getReg(0);
Slice = B.buildInsertVectorElement(SliceTy, Slice, Elt, Idx).getReg(0);
}
@@ -2382,26 +2403,26 @@ static SmallVector<Register> splitVectorByGrain(MachineIRBuilder &B,
static void joinVectorByGrain(MachineIRBuilder &B, Register Dst,
ArrayRef<Register> Srcs, unsigned Grain) {
- auto &MRI = *B.getMRI();
- auto DstTy = MRI.getType(Dst);
- auto EltTy = DstTy.getScalarType();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ llvm::LLT DstTy = MRI.getType(Dst);
+ llvm::LLT EltTy = DstTy.getScalarType();
if (DstTy.isScalar()) {
- auto Src = Srcs[0];
+ llvm::Register Src = Srcs[0];
B.buildExtractVectorElementConstant(Dst, Src, 0);
return;
}
const unsigned NumElts = DstTy.getNumElements();
- auto TmpDst = B.buildUndef(DstTy).getReg(0);
+ llvm::Register TmpDst = B.buildUndef(DstTy).getReg(0);
for (unsigned I = 0; I < NumElts; I += Grain) {
- auto &Src = Srcs[I / Grain];
+ const llvm::Register &Src = Srcs[I / Grain];
for (unsigned J = 0; J < std::min(Grain, NumElts - I); ++J) {
- auto Idx = B.buildConstant(I32, I + J).getReg(0);
- auto Elt = B.buildExtractVectorElementConstant(EltTy, Src, J).getReg(0);
+ llvm::Register Idx = B.buildConstant(I32, I + J).getReg(0);
+ llvm::Register Elt = B.buildExtractVectorElementConstant(EltTy, Src, J).getReg(0);
TmpDst = B.buildInsertVectorElement(DstTy, TmpDst, Elt, Idx).getReg(0);
}
@@ -2412,42 +2433,45 @@ static void joinVectorByGrain(MachineIRBuilder &B, Register Dst,
static bool legalizeIntrinsicBfn(LegalizerHelper &Helper, MachineInstr &MI) {
MachineIRBuilder &B = Helper.MIRBuilder;
- auto &MRI = *B.getMRI();
- auto IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ unsigned IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
assert(IntrinsicID == Intrinsic::pisa_bfn);
- const auto BfnOpcode = MI.getOperand(2);
+ const llvm::MachineOperand BfnOpcode = MI.getOperand(2);
- const auto OrigDst = MI.getOperand(0).getReg();
- const auto OrigSrc0 = MI.getOperand(3).getReg();
- const auto OrigSrc1 = MI.getOperand(4).getReg();
- const auto OrigSrc2 = MI.getOperand(5).getReg();
+ const llvm::Register OrigDst = MI.getOperand(0).getReg();
+ const llvm::Register OrigSrc0 = MI.getOperand(3).getReg();
+ const llvm::Register OrigSrc1 = MI.getOperand(4).getReg();
+ const llvm::Register OrigSrc2 = MI.getOperand(5).getReg();
- const auto Ty = MRI.getType(OrigDst);
+ const llvm::LLT Ty = MRI.getType(OrigDst);
- const auto BitWidth = Ty.getScalarSizeInBits();
+ const unsigned BitWidth = Ty.getScalarSizeInBits();
switch (BitWidth) {
default:
llvm_unreachable("unexpected bitwidth");
case 8:
case 16: {
- const auto Grain = 32 / BitWidth;
+ const unsigned Grain = 32 / BitWidth;
MachineIRBuilder MIB(MI);
- auto Srcs0 = splitVectorByGrain(MIB, OrigSrc0, Grain);
- auto Srcs1 = splitVectorByGrain(MIB, OrigSrc1, Grain);
- auto Srcs2 = splitVectorByGrain(MIB, OrigSrc2, Grain);
+ llvm::SmallVector<llvm::Register, 12> Srcs0 = splitVectorByGrain(MIB, OrigSrc0, Grain);
+ llvm::SmallVector<llvm::Register, 12> Srcs1 = splitVectorByGrain(MIB, OrigSrc1, Grain);
+ llvm::SmallVector<llvm::Register, 12> Srcs2 = splitVectorByGrain(MIB, OrigSrc2, Grain);
- auto GrainTy = MRI.getType(Srcs0[0]);
+ llvm::LLT GrainTy = MRI.getType(Srcs0[0]);
SmallVector<Register> Dsts;
- for (auto [Src0, Src1, Src2] : zip(Srcs0, Srcs1, Srcs2)) {
- auto Dst = MRI.createGenericVirtualRegister(I32);
- auto Src0Cast = MRI.createGenericVirtualRegister(I32);
- auto Src1Cast = MRI.createGenericVirtualRegister(I32);
- auto Src2Cast = MRI.createGenericVirtualRegister(I32);
+ for (size_t I = 0; I < Srcs0.size(); ++I) {
+ Register Src0 = Srcs0[I];
+ Register Src1 = Srcs1[I];
+ Register Src2 = Srcs2[I];
+ llvm::Register Dst = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Src0Cast = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Src1Cast = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Src2Cast = MRI.createGenericVirtualRegister(I32);
- auto DstCast = MRI.createGenericVirtualRegister(GrainTy);
+ llvm::Register DstCast = MRI.createGenericVirtualRegister(GrainTy);
MIB.buildBitcast(Src0Cast, Src0);
MIB.buildBitcast(Src1Cast, Src1);
@@ -2470,19 +2494,19 @@ static bool legalizeIntrinsicBfn(LegalizerHelper &Helper, MachineInstr &MI) {
scalarizeIntrinsic(MI);
return true;
case 64: {
- auto MIs = scalarizeIntrinsic(MI);
+ llvm::SmallVector<llvm::MachineInstr*, 6> MIs = scalarizeIntrinsic(MI);
- for (auto *MI : MIs) {
+ for (llvm::MachineInstr *MI : MIs) {
MachineIRBuilder MIB(*MI);
- auto Dst = MI->getOperand(0).getReg();
- auto Src0 = MI->getOperand(3).getReg();
- auto Src1 = MI->getOperand(4).getReg();
- auto Src2 = MI->getOperand(5).getReg();
+ llvm::Register Dst = MI->getOperand(0).getReg();
+ llvm::Register Src0 = MI->getOperand(3).getReg();
+ llvm::Register Src1 = MI->getOperand(4).getReg();
+ llvm::Register Src2 = MI->getOperand(5).getReg();
- auto DstV2I32 = MRI.createGenericVirtualRegister(V2I32);
- auto Src0V2I32 = MRI.createGenericVirtualRegister(V2I32);
- auto Src1V2I32 = MRI.createGenericVirtualRegister(V2I32);
- auto Src2V2I32 = MRI.createGenericVirtualRegister(V2I32);
+ llvm::Register DstV2I32 = MRI.createGenericVirtualRegister(V2I32);
+ llvm::Register Src0V2I32 = MRI.createGenericVirtualRegister(V2I32);
+ llvm::Register Src1V2I32 = MRI.createGenericVirtualRegister(V2I32);
+ llvm::Register Src2V2I32 = MRI.createGenericVirtualRegister(V2I32);
MIB.buildUndef(DstV2I32);
@@ -2491,11 +2515,11 @@ static bool legalizeIntrinsicBfn(LegalizerHelper &Helper, MachineInstr &MI) {
MIB.buildBitcast(Src2V2I32, Src2);
for (int I = 0; I < 2; I++) {
- auto DstI32 = MRI.createGenericVirtualRegister(I32);
- auto Src0I32 = MRI.createGenericVirtualRegister(I32);
- auto Src1I32 = MRI.createGenericVirtualRegister(I32);
- auto Src2I32 = MRI.createGenericVirtualRegister(I32);
- auto Idx = MRI.createGenericVirtualRegister(I32);
+ llvm::Register DstI32 = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Src0I32 = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Src1I32 = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Src2I32 = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Idx = MRI.createGenericVirtualRegister(I32);
MIB.buildExtractVectorElementConstant(Src0I32, Src0V2I32, I);
MIB.buildExtractVectorElementConstant(Src1I32, Src1V2I32, I);
@@ -2509,7 +2533,7 @@ static bool legalizeIntrinsicBfn(LegalizerHelper &Helper, MachineInstr &MI) {
MIB.buildConstant(Idx, I);
- auto DstNext = MRI.createGenericVirtualRegister(V2I32);
+ llvm::Register DstNext = MRI.createGenericVirtualRegister(V2I32);
MIB.buildInsertVectorElement(DstNext, DstV2I32, DstI32, Idx);
DstV2I32 = DstNext;
}
@@ -2526,7 +2550,7 @@ static bool legalizeIntrinsicBfn(LegalizerHelper &Helper, MachineInstr &MI) {
static bool legalizeIntrinsicRE(LegalizerHelper &Helper, MachineInstr &MI) {
MachineIRBuilder &B = Helper.MIRBuilder;
- auto RndMode = MI.getOperand(MI.getNumOperands() - 1).getImm();
+ int64_t RndMode = MI.getOperand(MI.getNumOperands() - 1).getImm();
if (static_cast<RoundingMode>(RndMode) != RoundingMode::NearestTiesToEven)
return false; // only .re is supported
@@ -2574,28 +2598,27 @@ static bool legalizeIntrinsicRE(LegalizerHelper &Helper, MachineInstr &MI) {
static bool legalizeIntrinsicI2F(LegalizerHelper &Helper, MachineInstr &MI) {
MachineIRBuilder &B = Helper.MIRBuilder;
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
Intrinsic::ID IntrinsicID = cast<GIntrinsic>(MI).getIntrinsicID();
SmallVector<MachineInstr *> MIs = scalarizeIntrinsic(MI);
// @llvm.experimental.constrained.sitofp.f32.i1
- for (auto It = MIs.begin(), Ite = MIs.end(); It != Ite; ++It) {
- auto *MI = *It;
+ for (MachineInstr *MI : MIs) {
MachineIRBuilder MIB(*MI);
- auto SrcReg = MI->getOperand(2).getReg();
- auto SrcTy = MRI.getType(SrcReg);
+ llvm::Register SrcReg = MI->getOperand(2).getReg();
+ llvm::LLT SrcTy = MRI.getType(SrcReg);
if (SrcTy.getSizeInBits() >= 8)
continue;
- auto ExtReg = MRI.createGenericVirtualRegister(I8);
+ llvm::Register ExtReg = MRI.createGenericVirtualRegister(I8);
if (IntrinsicID == Intrinsic::pisa_uitofp)
MIB.buildZExt(ExtReg, SrcReg);
else
MIB.buildSExt(ExtReg, SrcReg);
- auto NewMI = MIB.buildIntrinsic(IntrinsicID, MI->getOperand(0).getReg())
+ llvm::MachineInstrBuilder NewMI = MIB.buildIntrinsic(IntrinsicID, MI->getOperand(0).getReg())
.addReg(ExtReg)
.add(MI->getOperand(3))
.add(MI->getOperand(4));
@@ -2615,7 +2638,7 @@ static bool legalizeIntrinsicDp4a(LegalizerHelper &Helper, MachineInstr &MI) {
return true;
MachineIRBuilder &B = Helper.MIRBuilder;
- auto &MRI = *B.getMRI();
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
B.setInstrAndDebugLoc(MI);
Register Dst = MI.getOperand(0).getReg();
@@ -2683,19 +2706,19 @@ bool PISALegalizerInfo::legalizeIntrinsic(LegalizerHelper &Helper,
}
static bool legalizeGConcatVectors(MachineInstr &MI, MachineIRBuilder &B) {
- auto *MRI = B.getMRI();
+ llvm::MachineRegisterInfo *MRI = B.getMRI();
- auto Dst = MI.getOperand(0).getReg();
- auto DstTy = MRI->getType(Dst);
+ llvm::Register Dst = MI.getOperand(0).getReg();
+ llvm::LLT DstTy = MRI->getType(Dst);
assert(DstTy.getScalarSizeInBits() == 32);
unsigned Idx = 0;
- auto TDst = MRI->createGenericVirtualRegister(DstTy);
+ llvm::Register TDst = MRI->createGenericVirtualRegister(DstTy);
B.buildInstr(TargetOpcode::IMPLICIT_DEF).addDef(TDst);
for (unsigned I = 1; I < MI.getNumOperands(); I++) {
- auto Src = MI.getOperand(I).getReg();
- auto SrcTy = B.getMRI()->getType(Src);
- auto NewDst = MRI->createGenericVirtualRegister(DstTy);
+ llvm::Register Src = MI.getOperand(I).getReg();
+ llvm::LLT SrcTy = B.getMRI()->getType(Src);
+ llvm::Register NewDst = MRI->createGenericVirtualRegister(DstTy);
B.buildInsertSubvector(NewDst, TDst, Src, Idx);
Idx += SrcTy.getNumElements();
TDst = NewDst;
@@ -2707,15 +2730,15 @@ static bool legalizeGConcatVectors(MachineInstr &MI, MachineIRBuilder &B) {
static bool legalizeGUnmergeValues(LegalizerHelper &Helper, MachineInstr &MI,
MachineIRBuilder &B) {
- auto *MRI = B.getMRI();
+ llvm::MachineRegisterInfo *MRI = B.getMRI();
- auto Src = MI.getOperand(MI.getNumOperands() - 1).getReg();
- auto DstTy = MRI->getType(MI.getOperand(0).getReg());
+ llvm::Register Src = MI.getOperand(MI.getNumOperands() - 1).getReg();
+ llvm::LLT DstTy = MRI->getType(MI.getOperand(0).getReg());
assert(MRI->getType(Src).getScalarSizeInBits() == 32);
unsigned Idx = 0;
for (unsigned I = 0; I < MI.getNumOperands() - 1; I++) {
- auto Dst = MI.getOperand(I).getReg();
+ llvm::Register Dst = MI.getOperand(I).getReg();
if (DstTy.isVector()) {
// <2 x s32>, <2 x s32> = G_UNMERGE_VALUES <4 x s32>
B.buildExtractSubvector(Dst, Src, Idx);
@@ -2732,12 +2755,12 @@ static bool legalizeGUnmergeValues(LegalizerHelper &Helper, MachineInstr &MI,
// NOLINTNEXTLINE(readability-identifier-naming)
static bool legalizeGInsertSubvector(MachineInstr &MI, MachineIRBuilder &B) {
- auto *MRI = B.getMRI();
+ llvm::MachineRegisterInfo *MRI = B.getMRI();
- auto DstReg = MI.getOperand(0).getReg();
- auto VecReg = MI.getOperand(1).getReg();
- auto SubVecReg = MI.getOperand(2).getReg();
- auto Idx = MI.getOperand(3).getImm();
+ llvm::Register DstReg = MI.getOperand(0).getReg();
+ llvm::Register VecReg = MI.getOperand(1).getReg();
+ llvm::Register SubVecReg = MI.getOperand(2).getReg();
+ int64_t Idx = MI.getOperand(3).getImm();
LLT DstTy = MRI->getType(DstReg);
@@ -2752,17 +2775,17 @@ static bool legalizeGInsertSubvector(MachineInstr &MI, MachineIRBuilder &B) {
LLT::fixed_vector(2 * DstTy.getNumElements(), LLT::integer(32));
// Update vec
- auto CastedVecReg = MRI->createGenericVirtualRegister(CastedVecTy);
+ llvm::Register CastedVecReg = MRI->createGenericVirtualRegister(CastedVecTy);
B.buildBitcast(CastedVecReg, VecReg);
MI.getOperand(1).setReg(CastedVecReg);
// Update subvec
- auto CastedSubVecReg = MRI->createGenericVirtualRegister(CastedSubVecTy);
+ llvm::Register CastedSubVecReg = MRI->createGenericVirtualRegister(CastedSubVecTy);
B.buildBitcast(CastedSubVecReg, SubVecReg);
MI.getOperand(2).setReg(CastedSubVecReg);
// Update dst
- auto CastedDstReg = MRI->createGenericVirtualRegister(CastedDstTy);
+ llvm::Register CastedDstReg = MRI->createGenericVirtualRegister(CastedDstTy);
MI.getOperand(0).setReg(CastedDstReg);
// Update index
@@ -2817,11 +2840,11 @@ static bool legalizeGInsertSubvector(MachineInstr &MI, MachineIRBuilder &B) {
// NOLINTNEXTLINE(readability-identifier-naming)
static bool legalizeGExtractSubvector(MachineInstr &MI, MachineIRBuilder &B) {
- auto *MRI = B.getMRI();
+ llvm::MachineRegisterInfo *MRI = B.getMRI();
- auto DstReg = MI.getOperand(0).getReg();
- auto SrcReg = MI.getOperand(1).getReg();
- auto Idx = MI.getOperand(2).getImm();
+ llvm::Register DstReg = MI.getOperand(0).getReg();
+ llvm::Register SrcReg = MI.getOperand(1).getReg();
+ int64_t Idx = MI.getOperand(2).getImm();
LLT SrcTy = MRI->getType(SrcReg);
@@ -2833,12 +2856,12 @@ static bool legalizeGExtractSubvector(MachineInstr &MI, MachineIRBuilder &B) {
LLT::fixed_vector(2 * DstTy.getNumElements(), LLT::integer(32));
// Update src
- auto CastedSrcReg = MRI->createGenericVirtualRegister(CastedSrcTy);
+ llvm::Register CastedSrcReg = MRI->createGenericVirtualRegister(CastedSrcTy);
B.buildBitcast(CastedSrcReg, SrcReg);
MI.getOperand(1).setReg(CastedSrcReg);
// Update dst
- auto CastedDstReg = MRI->createGenericVirtualRegister(CastedDstTy);
+ llvm::Register CastedDstReg = MRI->createGenericVirtualRegister(CastedDstTy);
MI.getOperand(0).setReg(CastedDstReg);
// Update index
@@ -2886,7 +2909,7 @@ static bool legalizeGExtractSubvector(MachineInstr &MI, MachineIRBuilder &B) {
static StringRef getSyncScopeStr(LLVMContext &Ctx, SyncScope::ID ScopeID) {
// Map dynamically assigned PISA SyncScope ID to its scope name.
static DenseMap<SyncScope::ID, StringRef> ScopeID2Name;
- auto InitializeScopeID2Name = [&]() {
+ std::function<void()> InitializeScopeID2Name = [&]() {
static const StringMap<StringRef> ScopeName2EncodeName = {
{"workgroup", "workgroup"},
{"gpu", "gpu"},
@@ -2895,8 +2918,10 @@ static StringRef getSyncScopeStr(LLVMContext &Ctx, SyncScope::ID ScopeID) {
{"subgroup", "workgroup"},
{"workitem", "workgroup"},
};
- for (const auto &[Name, EncodeName] : ScopeName2EncodeName) {
- auto ID = Ctx.getOrInsertSyncScopeID(Name);
+ for (const StringMapEntry<StringRef> &Entry : ScopeName2EncodeName) {
+ StringRef Name = Entry.first();
+ StringRef EncodeName = Entry.second;
+ SyncScope::ID ID = Ctx.getOrInsertSyncScopeID(Name);
ScopeID2Name.emplace_or_assign(ID, EncodeName);
}
};
@@ -2904,7 +2929,8 @@ static StringRef getSyncScopeStr(LLVMContext &Ctx, SyncScope::ID ScopeID) {
std::call_once(InitializeScopeID2NameFlag, InitializeScopeID2Name);
// Use the original SyncScope ID to look up its scope name in the map.
- auto It = ScopeID2Name.find(ScopeID);
+ DenseMap<SyncScope::ID, StringRef>::iterator It =
+ ScopeID2Name.find(ScopeID);
return It != ScopeID2Name.end() ? It->second : StringRef("gpu");
}
@@ -2917,7 +2943,7 @@ static bool legalizeGAtomicrmw(MachineInstr &MI, MachineIRBuilder &B) {
isAtLeastOrStrongerThan(AtomicOrdering::Release, AOF))
return true;
- auto &Ctx = B.getMF().getFunction().getContext();
+ llvm::LLVMContext &Ctx = B.getMF().getFunction().getContext();
llvm::SmallString<16> FenceScopeStr =
getSyncScopeStr(Ctx, MemOp->getSyncScopeID());
unsigned AddressSpace = MemOp->getAddrSpace();
@@ -2963,11 +2989,11 @@ static bool legalizeGAtomicrmw(MachineInstr &MI, MachineIRBuilder &B) {
// NOLINTNEXTLINE(readability-identifier-naming)
static bool legalizeGAtomicrmwXchg(MachineInstr &MI, MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
- auto &Dst = MI.getOperand(0);
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ llvm::MachineOperand &Dst = MI.getOperand(0);
LLT CurTy = MRI.getType(Dst.getReg());
if (CurTy.getScalarType().isPointer()) {
- auto &Src = MI.getOperand(2);
+ llvm::MachineOperand &Src = MI.getOperand(2);
LLT NewTy = LLT::integer(CurTy.getScalarSizeInBits());
Register NewSrc = MRI.createGenericVirtualRegister(NewTy);
Register NewDst = MRI.createGenericVirtualRegister(NewTy);
@@ -2985,15 +3011,15 @@ static bool legalizeGAtomicrmwXchg(MachineInstr &MI, MachineIRBuilder &B) {
// NOLINTNEXTLINE(readability-identifier-naming)
static bool legalizeGShuffleVector(LegalizerHelper &Helper, MachineInstr &MI,
MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
- auto &Dst = MI.getOperand(0);
- auto &Src0 = MI.getOperand(1);
- auto &Src1 = MI.getOperand(2);
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ llvm::MachineOperand &Dst = MI.getOperand(0);
+ llvm::MachineOperand &Src0 = MI.getOperand(1);
+ llvm::MachineOperand &Src1 = MI.getOperand(2);
ArrayRef<int> Mask = MI.getOperand(3).getShuffleMask();
assert(MRI.getType(Dst.getReg()).getScalarSizeInBits() == 32);
assert(isPowerOf2_32(MRI.getType(Dst.getReg()).getNumElements()));
- auto UseExtract = true;
+ bool UseExtract = true;
// indices must be consecutive
int PrevIdx = -1;
for (int Idx : Mask) {
@@ -3007,7 +3033,7 @@ static bool legalizeGShuffleVector(LegalizerHelper &Helper, MachineInstr &MI,
UseExtract = false;
// indices can not straddle the arguments
- auto SrcSize = MRI.getType(Src0.getReg()).getNumElements();
+ uint16_t SrcSize = MRI.getType(Src0.getReg()).getNumElements();
if ((Mask[0] < SrcSize) && ((Mask[0] + Mask.size()) > SrcSize))
UseExtract = false;
@@ -3015,26 +3041,28 @@ static bool legalizeGShuffleVector(LegalizerHelper &Helper, MachineInstr &MI,
UseExtract = false;
if (UseExtract) {
- auto Src = (Mask[0] < SrcSize) ? Src0 : Src1;
- auto Idx = (Mask[0] < SrcSize) ? Mask[0] : Mask[0] - SrcSize;
+ llvm::MachineOperand Src = (Mask[0] < SrcSize) ? Src0 : Src1;
+ int Idx = (Mask[0] < SrcSize) ? Mask[0] : Mask[0] - SrcSize;
B.buildExtractSubvector(Dst, Src, Idx);
MI.eraseFromParent();
return true;
}
// lower if unable to use extract/insert
- auto Res = Helper.lowerShuffleVector(MI);
+ llvm::LegalizerHelper::LegalizeResult Res = Helper.lowerShuffleVector(MI);
return Res != LegalizerHelper::UnableToLegalize;
}
// NOLINTNEXTLINE(readability-identifier-naming)
static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
MachineIRBuilder &MIRBuilder) {
- auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
+ Register DstReg, SrcReg;
+ LLT DstTy, SrcTy;
+ std::tie(DstReg, DstTy, SrcReg, SrcTy) = MI.getFirst2RegLLTs();
FPClassTest OriginalMask =
static_cast<FPClassTest>(MI.getOperand(2).getImm());
- auto Mask = OriginalMask;
- auto IsInvertedCheck = false;
+ llvm::FPClassTest Mask = OriginalMask;
+ bool IsInvertedCheck = false;
if (Mask == fcNone) {
MIRBuilder.buildConstant(DstReg, 0);
@@ -3048,13 +3076,13 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
}
// support bfloat types
- auto &Semantics = getFltSemanticForLLT(SrcTy.getScalarType());
+ const llvm::fltSemantics &Semantics = getFltSemanticForLLT(SrcTy.getScalarType());
unsigned BitSize = SrcTy.getScalarSizeInBits();
LLT IntTy = LLT::integer(BitSize);
if (SrcTy.isVector())
IntTy = LLT::vector(SrcTy.getElementCount(), IntTy);
- auto AsInt = MIRBuilder.buildBitcast(IntTy, SrcReg);
+ llvm::MachineInstrBuilder AsInt = MIRBuilder.buildBitcast(IntTy, SrcReg);
// Various masks.
APInt SignBit = APInt::getSignMask(BitSize);
@@ -3066,20 +3094,21 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
APInt::getOneBitSet(BitSize, AllOneMantissa.getActiveBits() - 1);
APInt InvertionMask = APInt::getAllOnes(DstTy.getScalarSizeInBits());
- auto SignBitC = MIRBuilder.buildConstant(IntTy, SignBit);
- auto ValueMaskC = MIRBuilder.buildConstant(IntTy, ValueMask);
- auto InfC = MIRBuilder.buildConstant(IntTy, Inf);
- auto ExpMaskC = MIRBuilder.buildConstant(IntTy, ExpMask);
- auto ZeroC = MIRBuilder.buildConstant(IntTy, 0);
+ llvm::MachineInstrBuilder SignBitC = MIRBuilder.buildConstant(IntTy, SignBit);
+ llvm::MachineInstrBuilder ValueMaskC = MIRBuilder.buildConstant(IntTy, ValueMask);
+ llvm::MachineInstrBuilder InfC = MIRBuilder.buildConstant(IntTy, Inf);
+ llvm::MachineInstrBuilder ExpMaskC = MIRBuilder.buildConstant(IntTy, ExpMask);
+ llvm::MachineInstrBuilder ZeroC = MIRBuilder.buildConstant(IntTy, 0);
- auto Abs = MIRBuilder.buildAnd(IntTy, AsInt, ValueMaskC);
- auto Sign =
+ llvm::MachineInstrBuilder Abs = MIRBuilder.buildAnd(IntTy, AsInt, ValueMaskC);
+ llvm::MachineInstrBuilder Sign =
MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_NE, DstTy, AsInt, Abs);
- auto Res = MIRBuilder.buildConstant(DstTy, 0);
+ llvm::MachineInstrBuilder Res = MIRBuilder.buildConstant(DstTy, 0);
// Clang doesn't support capture of structured bindings:
LLT DstTyCopy = DstTy;
- const auto AppendToRes = [&](MachineInstrBuilder ToAppend) {
+ const std::function<void(MachineInstrBuilder)> AppendToRes =
+ [&](MachineInstrBuilder ToAppend) {
Res = MIRBuilder.buildOr(DstTyCopy, Res, ToAppend);
};
@@ -3096,9 +3125,9 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
Mask &= ~fcPosFinite;
} else if ((Mask & fcFinite) == fcNegFinite) {
// finite(V) && V < 0 ==> abs(V) u< exp_mask && signbit == 1
- auto Cmp = MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, Abs,
+ llvm::MachineInstrBuilder Cmp = MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, Abs,
ExpMaskC);
- auto And = MIRBuilder.buildAnd(DstTy, Cmp, Sign);
+ llvm::MachineInstrBuilder And = MIRBuilder.buildAnd(DstTy, Cmp, Sign);
AppendToRes(And);
Mask &= ~fcNegFinite;
}
@@ -3108,7 +3137,7 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
// TODO: Handle sign bit specific cases
// TODO: Handle inverted case
if (PartialCheck == (fcZero | fcSubnormal)) {
- auto ExpBits = MIRBuilder.buildAnd(IntTy, AsInt, ExpMaskC);
+ llvm::MachineInstrBuilder ExpBits = MIRBuilder.buildAnd(IntTy, AsInt, ExpMaskC);
AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy,
ExpBits, ZeroC));
Mask &= ~PartialCheck;
@@ -3118,7 +3147,7 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
if (Mask == OriginalMask) {
// combination of classes above did not yield any
// optimizations, see if inverse will be less ops
- auto InvertedMask = (unsigned)~Mask;
+ unsigned InvertedMask = (unsigned)~Mask;
if (llvm::popcount((unsigned)Mask) > llvm::popcount(InvertedMask)) {
Mask = ~Mask;
IsInvertedCheck = true;
@@ -3141,10 +3170,10 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
if (FPClassTest PartialCheck = Mask & fcSubnormal) {
// issubnormal(V) ==> unsigned(abs(V) - 1) u< (all mantissa bits set)
// issubnormal(V) && V>0 ==> unsigned(V - 1) u< (all mantissa bits set)
- auto V = (PartialCheck == fcPosSubnormal) ? AsInt : Abs;
- auto OneC = MIRBuilder.buildConstant(IntTy, 1);
- auto VMinusOne = MIRBuilder.buildSub(IntTy, V, OneC);
- auto SubnormalRes =
+ llvm::MachineInstrBuilder V = (PartialCheck == fcPosSubnormal) ? AsInt : Abs;
+ llvm::MachineInstrBuilder OneC = MIRBuilder.buildConstant(IntTy, 1);
+ llvm::MachineInstrBuilder VMinusOne = MIRBuilder.buildSub(IntTy, V, OneC);
+ llvm::MachineInstrBuilder SubnormalRes =
MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, VMinusOne,
MIRBuilder.buildConstant(IntTy, AllOneMantissa));
if (PartialCheck == fcNegSubnormal)
@@ -3161,14 +3190,14 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy, Abs, InfC));
else { // fcNegInf
APInt NegInf = APFloat::getInf(Semantics, true).bitcastToAPInt();
- auto NegInfC = MIRBuilder.buildConstant(IntTy, NegInf);
+ llvm::MachineInstrBuilder NegInfC = MIRBuilder.buildConstant(IntTy, NegInf);
AppendToRes(MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_EQ, DstTy,
AsInt, NegInfC));
}
}
if (FPClassTest PartialCheck = Mask & fcNan) {
- auto InfWithQnanBitC =
+ llvm::MachineInstrBuilder InfWithQnanBitC =
MIRBuilder.buildConstant(IntTy, std::move(Inf) | QNaNBitMask);
if (PartialCheck == fcNan) {
// isnan(V) ==> abs(V) u> int(inf)
@@ -3181,10 +3210,10 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
} else { // fcSNan
// issignaling(V) ==> abs(V) u> unsigned(Inf) &&
// abs(V) u< (unsigned(Inf) | quiet_bit)
- auto IsNan =
+ llvm::MachineInstrBuilder IsNan =
MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_UGT, DstTy, Abs, InfC);
- auto IsNotQnan = MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy,
- Abs, InfWithQnanBitC);
+ llvm::MachineInstrBuilder IsNotQnan = MIRBuilder.buildICmp(
+ CmpInst::Predicate::ICMP_ULT, DstTy, Abs, InfWithQnanBitC);
AppendToRes(MIRBuilder.buildAnd(DstTy, IsNan, IsNotQnan));
}
}
@@ -3193,16 +3222,16 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
// isnormal(V) ==> (0 u< exp u< max_exp) ==> (unsigned(exp-1) u<
// (max_exp-1))
APInt ExpLSB = ExpMask & ~(ExpMask.shl(1));
- auto ExpMinusOne = MIRBuilder.buildSub(
+ llvm::MachineInstrBuilder ExpMinusOne = MIRBuilder.buildSub(
IntTy, Abs, MIRBuilder.buildConstant(IntTy, ExpLSB));
APInt MaxExpMinusOne = std::move(ExpMask) - ExpLSB;
- auto NormalRes =
+ llvm::MachineInstrBuilder NormalRes =
MIRBuilder.buildICmp(CmpInst::Predicate::ICMP_ULT, DstTy, ExpMinusOne,
MIRBuilder.buildConstant(IntTy, MaxExpMinusOne));
if (PartialCheck == fcNegNormal)
NormalRes = MIRBuilder.buildAnd(DstTy, NormalRes, Sign);
else if (PartialCheck == fcPosNormal) {
- auto PosSign = MIRBuilder.buildXor(
+ llvm::MachineInstrBuilder PosSign = MIRBuilder.buildXor(
DstTy, Sign, MIRBuilder.buildConstant(DstTy, InvertionMask));
NormalRes = MIRBuilder.buildAnd(DstTy, NormalRes, PosSign);
}
@@ -3219,34 +3248,35 @@ static bool legalizeGIsFpclass(LegalizerHelper &Helper, MachineInstr &MI,
static bool legalizeGMulh(LegalizerHelper &Helper, MachineInstr &MI,
MachineIRBuilder &B) {
- auto &MRI = *B.getMRI();
- auto [Dst, Src0, Src1] = MI.getFirst3Regs();
- auto IsSigned = MI.getOpcode() == TargetOpcode::G_SMULH;
- auto DstTy = MRI.getType(Dst);
+ llvm::MachineRegisterInfo &MRI = *B.getMRI();
+ Register Dst, Src0, Src1;
+ std::tie(Dst, Src0, Src1) = MI.getFirst3Regs();
+ bool IsSigned = MI.getOpcode() == TargetOpcode::G_SMULH;
+ llvm::LLT DstTy = MRI.getType(Dst);
assert(DstTy.getSizeInBits() == 64);
- auto SourceA = MRI.createGenericVirtualRegister(DstTy);
- auto SourceB = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register SourceA = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register SourceB = MRI.createGenericVirtualRegister(DstTy);
- auto Const32 = MRI.createGenericVirtualRegister(DstTy);
- auto Const63 = MRI.createGenericVirtualRegister(DstTy);
- auto Const0 = MRI.createGenericVirtualRegister(DstTy);
- auto Mask32 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register Const32 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register Const63 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register Const0 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register Mask32 = MRI.createGenericVirtualRegister(DstTy);
B.buildConstant(Const32, 32);
B.buildConstant(Const63, 63);
B.buildConstant(Const0, 0);
B.buildConstant(Mask32, 0xFFFFFFFF);
- auto ASign = MRI.createGenericVirtualRegister(DstTy);
- auto BSign = MRI.createGenericVirtualRegister(DstTy);
- auto ResultSign = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ASign = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register BSign = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ResultSign = MRI.createGenericVirtualRegister(DstTy);
B.buildAShr(ASign, Src0, Const63);
B.buildAShr(BSign, Src1, Const63);
B.buildXor(ResultSign, ASign, BSign);
if (IsSigned) {
- auto ASignXor = MRI.createGenericVirtualRegister(DstTy);
- auto BSignXor = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ASignXor = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register BSignXor = MRI.createGenericVirtualRegister(DstTy);
B.buildXor(ASignXor, Src0, ASign);
B.buildXor(BSignXor, Src1, BSign);
B.buildSub(SourceA, ASignXor, ASign);
@@ -3256,40 +3286,40 @@ static bool legalizeGMulh(LegalizerHelper &Helper, MachineInstr &MI,
B.buildCopy(SourceB, Src1);
}
- auto LoSrc0 = MRI.createGenericVirtualRegister(DstTy);
- auto HiSrc0 = MRI.createGenericVirtualRegister(DstTy);
- auto LoSrc1 = MRI.createGenericVirtualRegister(DstTy);
- auto HiSrc1 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register LoSrc0 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register HiSrc0 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register LoSrc1 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register HiSrc1 = MRI.createGenericVirtualRegister(DstTy);
B.buildLShr(HiSrc0, SourceA, Const32);
B.buildLShr(HiSrc1, SourceB, Const32);
B.buildAnd(LoSrc0, SourceA, Mask32);
B.buildAnd(LoSrc1, SourceB, Mask32);
- auto ALobLo = MRI.createGenericVirtualRegister(DstTy);
- auto ALobHi = MRI.createGenericVirtualRegister(DstTy);
- auto AHibLo = MRI.createGenericVirtualRegister(DstTy);
- auto AHibHi = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ALobLo = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ALobHi = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register AHibLo = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register AHibHi = MRI.createGenericVirtualRegister(DstTy);
B.buildMul(AHibHi, HiSrc0, HiSrc1);
B.buildMul(AHibLo, HiSrc0, LoSrc1);
B.buildMul(ALobHi, LoSrc0, HiSrc1);
B.buildMul(ALobLo, LoSrc0, LoSrc1);
- auto ALobLoHi = MRI.createGenericVirtualRegister(DstTy);
- auto ALobHiLo = MRI.createGenericVirtualRegister(DstTy);
- auto AHibLoSum0 = MRI.createGenericVirtualRegister(DstTy);
- auto AHibLoSum1 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ALobLoHi = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ALobHiLo = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register AHibLoSum0 = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register AHibLoSum1 = MRI.createGenericVirtualRegister(DstTy);
B.buildLShr(ALobLoHi, ALobLo, Const32);
B.buildAnd(ALobHiLo, ALobHi, Mask32);
B.buildAdd(AHibLoSum0, ALobLoHi, ALobHiLo);
B.buildAdd(AHibLoSum1, AHibLo, AHibLoSum0);
- auto ALobLoMasked = MRI.createGenericVirtualRegister(DstTy);
- auto AHibLoShiftedL = MRI.createGenericVirtualRegister(DstTy);
- auto ALobHiShiftedR = MRI.createGenericVirtualRegister(DstTy);
- auto AHibLoShiftedR = MRI.createGenericVirtualRegister(DstTy);
- auto ShiftedSum = MRI.createGenericVirtualRegister(DstTy);
- auto DstLo = MRI.createGenericVirtualRegister(DstTy);
- auto DstHi = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ALobLoMasked = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register AHibLoShiftedL = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ALobHiShiftedR = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register AHibLoShiftedR = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register ShiftedSum = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register DstLo = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register DstHi = MRI.createGenericVirtualRegister(DstTy);
B.buildAnd(ALobLoMasked, ALobLo, Mask32);
B.buildShl(AHibLoShiftedL, AHibLoSum1, Const32);
@@ -3305,15 +3335,15 @@ static bool legalizeGMulh(LegalizerHelper &Helper, MachineInstr &MI,
// lo = lo ^ mask;
// lo += resultSign; // Add 1 if resultSign is negative, otherwise add 0
// hi += (lo < resultSign); // Adjust hi if lo overflowed
- auto Mask = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register Mask = MRI.createGenericVirtualRegister(DstTy);
B.buildNeg(Mask, ResultSign);
- auto HiXorMask = MRI.createGenericVirtualRegister(DstTy);
- auto LoXorMask = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register HiXorMask = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register LoXorMask = MRI.createGenericVirtualRegister(DstTy);
B.buildXor(HiXorMask, DstHi, Mask);
B.buildXor(LoXorMask, DstLo, Mask);
- auto LoAddResult = MRI.createGenericVirtualRegister(DstTy);
- auto LoAddShiftResult = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register LoAddResult = MRI.createGenericVirtualRegister(DstTy);
+ llvm::Register LoAddShiftResult = MRI.createGenericVirtualRegister(DstTy);
B.buildAdd(LoAddResult, LoXorMask, ResultSign);
B.buildShl(LoAddShiftResult, LoAddResult, ResultSign);
B.buildAdd(Dst, HiXorMask, LoAddShiftResult);
@@ -3328,7 +3358,7 @@ static bool legalizeGMulh(LegalizerHelper &Helper, MachineInstr &MI,
// spaces. Ensures that null pointers are preserved during the cast by replacing
// the addrspacecast with a null pointer of the destination type.
static bool legalizeGAddrspaceCast(MachineInstr &MI, MachineIRBuilder &B) {
- auto &Dst = MI.getOperand(0);
+ llvm::MachineOperand &Dst = MI.getOperand(0);
LLT DstTy = B.getMRI()->getType(Dst.getReg());
B.buildConstant(
Dst, PISATargetMachine::getNullPointerValue(DstTy.getAddressSpace()));
@@ -3338,17 +3368,19 @@ static bool legalizeGAddrspaceCast(MachineInstr &MI, MachineIRBuilder &B) {
// NOLINTNEXTLINE(readability-identifier-naming)
static bool legalizeGBitcast(MachineInstr &MI, MachineIRBuilder &B) {
- auto [DstReg, DstTy, SrcReg, SrcTy] = MI.getFirst2RegLLTs();
- auto *MRI = B.getMRI();
+ Register DstReg, SrcReg;
+ LLT DstTy, SrcTy;
+ std::tie(DstReg, DstTy, SrcReg, SrcTy) = MI.getFirst2RegLLTs();
+ llvm::MachineRegisterInfo *MRI = B.getMRI();
assert(SrcTy.getSizeInBits() == DstTy.getSizeInBits());
if (SrcTy.isPointer() != DstTy.isPointer()) {
// legalize bitcast between pointers and non-pointers
if (SrcTy.isPointer()) {
// <2 x i32> G_BITCAST (p1)
- auto IntSize = SrcTy.getSizeInBits();
- auto IntTy = LLT::integer(IntSize);
- auto IntReg = MRI->createGenericVirtualRegister(IntTy);
+ llvm::TypeSize IntSize = SrcTy.getSizeInBits();
+ llvm::LLT IntTy = LLT::integer(IntSize);
+ llvm::Register IntReg = MRI->createGenericVirtualRegister(IntTy);
B.buildPtrToInt(IntReg, SrcReg);
if (IntTy == DstTy)
B.buildCopy(DstReg, IntReg);
@@ -3356,9 +3388,9 @@ static bool legalizeGBitcast(MachineInstr &MI, MachineIRBuilder &B) {
B.buildBitcast(DstReg, IntReg);
} else {
// (p1) G_BITCAST <2 x i32>
- auto IntSize = DstTy.getSizeInBits();
- auto IntTy = LLT::integer(IntSize);
- auto IntReg = MRI->createGenericVirtualRegister(IntTy);
+ llvm::TypeSize IntSize = DstTy.getSizeInBits();
+ llvm::LLT IntTy = LLT::integer(IntSize);
+ llvm::Register IntReg = MRI->createGenericVirtualRegister(IntTy);
if (IntTy == SrcTy)
B.buildCopy(IntReg, SrcReg);
else
@@ -3378,10 +3410,10 @@ static bool legalizeGBitcast(MachineInstr &MI, MachineIRBuilder &B) {
// and reinterpret as the destination scalar type.
SmallVector<Register, 4> DstElements;
for (unsigned I = 0; I < SrcTy.getNumElements(); I++) {
- auto SrcElemReg =
+ llvm::Register SrcElemReg =
MRI->createGenericVirtualRegister(SrcTy.getScalarType());
B.buildExtractVectorElementConstant(SrcElemReg, SrcReg, I);
- auto DstElemReg =
+ llvm::Register DstElemReg =
MRI->createGenericVirtualRegister(DstTy.getScalarType());
B.buildBitcast(DstElemReg, SrcElemReg);
DstElements.push_back(DstElemReg);
@@ -3394,10 +3426,10 @@ static bool legalizeGBitcast(MachineInstr &MI, MachineIRBuilder &B) {
SmallVector<Register, 4> ScalarPieces;
for (unsigned SrcElemIdx = 0; SrcElemIdx < SrcTy.getNumElements();
SrcElemIdx++) {
- auto SrcElemReg =
+ llvm::Register SrcElemReg =
MRI->createGenericVirtualRegister(SrcTy.getScalarType());
B.buildExtractVectorElementConstant(SrcElemReg, SrcReg, SrcElemIdx);
- auto Unmerge =
+ llvm::MachineInstrBuilder Unmerge =
B.buildUnmerge(LLT::integer(CommonPieceSize), SrcElemReg);
for (unsigned PieceIdx = 0; PieceIdx != Unmerge->getNumOperands() - 1;
PieceIdx++)
@@ -3407,7 +3439,7 @@ static bool legalizeGBitcast(MachineInstr &MI, MachineIRBuilder &B) {
SmallVector<Register, 4> DstElements;
for (unsigned PieceStartIdx = 0; PieceStartIdx < ScalarPieces.size();
PieceStartIdx += NumPiecesPerDstElem) {
- auto DstElemReg =
+ llvm::Register DstElemReg =
MRI->createGenericVirtualRegister(DstTy.getScalarType());
SmallVector<Register> DstElemPieces(
ScalarPieces.begin() + PieceStartIdx,
diff --git a/llvm/lib/Target/PISA/PISAMachineFunctionInfo.h b/llvm/lib/Target/PISA/PISAMachineFunctionInfo.h
index 34c3a881769849..2a60e537b8d50f 100644
--- a/llvm/lib/Target/PISA/PISAMachineFunctionInfo.h
+++ b/llvm/lib/Target/PISA/PISAMachineFunctionInfo.h
@@ -24,7 +24,7 @@ class PISAMachineFunctionInfo : public MachineFunctionInfo {
~PISAMachineFunctionInfo() override;
ArgInfo getArgInfo(unsigned Slot) const {
- auto I = ArgInfos.find(Slot);
+ DenseMap<unsigned, ArgInfo>::const_iterator I = ArgInfos.find(Slot);
if (I == ArgInfos.end())
return {0, false};
return I->second;
diff --git a/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp b/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
index b7ab3b214d7eea..a3f66f9f42e8f2 100644
--- a/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
+++ b/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
@@ -222,16 +222,16 @@ bool PISAPostLegalizerCombinerImpl::tryCombineAll(MachineInstr &MI) const {
void PISAPostLegalizerCombinerImpl::applyBuildVectorWithConstants(
MachineInstr &MI) const {
- auto EltSize = MRI.getType(MI.getOperand(1).getReg()).getSizeInBits();
- auto Size = EltSize * (MI.getNumOperands() - 1);
+ llvm::TypeSize EltSize = MRI.getType(MI.getOperand(1).getReg()).getSizeInBits();
+ llvm::TypeSize Size = EltSize * (MI.getNumOperands() - 1);
assert((Size <= 64) && "vector size too large");
- auto NewReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
+ llvm::Register NewReg = MRI.createGenericVirtualRegister(LLT::integer(Size));
uint64_t Value = 0;
for (unsigned I = MI.getNumOperands() - 1; I > 0; I--) {
- auto Reg = MI.getOperand(I).getReg();
- auto CValue = getAnyConstantVRegValWithLookThrough(Reg, MRI);
+ llvm::Register Reg = MI.getOperand(I).getReg();
+ std::optional<llvm::ValueAndVReg> CValue = getAnyConstantVRegValWithLookThrough(Reg, MRI);
assert(CValue.has_value() && "expected const vreg val");
APInt IValue = CValue->Value;
Value <<= EltSize;
@@ -251,16 +251,16 @@ void PISAPostLegalizerCombinerImpl::applyBuildVectorWithConstants(
// i? S = G_SELECT i1 C, i? LHS, i? RHS
// => S = sel.? RHS, LHS, A
bool PISAPostLegalizerCombinerImpl::matchCompareSelect(MachineInstr *MI) const {
- auto *SelectMI = MI;
- auto *CmpMI = getDefIgnoringCopies(SelectMI->getOperand(1).getReg(), MRI);
+ llvm::MachineInstr *SelectMI = MI;
+ llvm::MachineInstr *CmpMI = getDefIgnoringCopies(SelectMI->getOperand(1).getReg(), MRI);
if (CmpMI->getOpcode() == TargetOpcode::G_ICMP) {
- auto CC = CmpMI->getOperand(1).getPredicate();
- auto CReg = CmpMI->getOperand(3).getReg();
- auto CVal = getIConstantVRegValWithLookThrough(CReg, MRI);
+ unsigned CC = CmpMI->getOperand(1).getPredicate();
+ llvm::Register CReg = CmpMI->getOperand(3).getReg();
+ std::optional<llvm::ValueAndVReg> CVal = getIConstantVRegValWithLookThrough(CReg, MRI);
if (CVal.has_value() && (CVal->Value == 0) &&
((CC == CmpInst::ICMP_EQ) || (CC == CmpInst::ICMP_NE))) {
- auto CmpTy = MRI.getType(CmpMI->getOperand(2).getReg());
- auto SelTy = MRI.getType(SelectMI->getOperand(2).getReg());
+ llvm::LLT CmpTy = MRI.getType(CmpMI->getOperand(2).getReg());
+ llvm::LLT SelTy = MRI.getType(SelectMI->getOperand(2).getReg());
if ((CmpTy.isScalar() && !CmpTy.isPointer()) &&
(SelTy.isScalar() && !SelTy.isPointer()) &&
CmpTy.getScalarSizeInBits() == SelTy.getScalarSizeInBits()) {
@@ -271,11 +271,11 @@ bool PISAPostLegalizerCombinerImpl::matchCompareSelect(MachineInstr *MI) const {
return false;
}
void PISAPostLegalizerCombinerImpl::applyCompareSelect(MachineInstr *MI) const {
- auto *SelectMI = MI;
- auto *CmpMI = getDefIgnoringCopies(SelectMI->getOperand(1).getReg(), MRI);
- auto CC = CmpMI->getOperand(1).getPredicate();
- auto DstReg = SelectMI->getOperand(0).getReg();
- auto MIB = B.buildInstr(PISA::G_PISA_SELECT)
+ llvm::MachineInstr *SelectMI = MI;
+ llvm::MachineInstr *CmpMI = getDefIgnoringCopies(SelectMI->getOperand(1).getReg(), MRI);
+ unsigned CC = CmpMI->getOperand(1).getPredicate();
+ llvm::Register DstReg = SelectMI->getOperand(0).getReg();
+ llvm::MachineInstrBuilder MIB = B.buildInstr(PISA::G_PISA_SELECT)
.addDef(DstReg)
.add(CmpMI->getOperand(2));
if (CC == CmpInst::ICMP_NE)
@@ -315,7 +315,7 @@ bool PISAPostLegalizerCombinerImpl::matchFCmpInvertedCond(
if (BrCondMI->getOpcode() != TargetOpcode::G_BRCOND)
return false;
- auto *CmpMI = MRI.getVRegDef(BrCondMI->getOperand(0).getReg());
+ llvm::MachineInstr *CmpMI = MRI.getVRegDef(BrCondMI->getOperand(0).getReg());
Register FCC;
CmpInst::Predicate Pred;
@@ -343,11 +343,11 @@ bool PISAPostLegalizerCombinerImpl::matchFCmpInvertedCond(
return false;
// %9:_(s1) = G_FCMP floatpred(oge), %1:reg32b(s32), %2:reg32b
- auto *FCmpMI = MRI.getVRegDef(FCC);
+ llvm::MachineInstr *FCmpMI = MRI.getVRegDef(FCC);
if (FCmpMI->getOpcode() != TargetOpcode::G_FCMP)
return false;
- auto FCCTy = MRI.getType(FCC);
+ llvm::LLT FCCTy = MRI.getType(FCC);
if (!(FCCTy.isScalar() && FCCTy.getScalarSizeInBits() == 1))
return false;
@@ -359,8 +359,8 @@ void PISAPostLegalizerCombinerImpl::applyFCmpInvertedCond(
MachineInstr *BrCond;
Register FCC;
std::tie(BrCond, FCC) = MatchInfo;
- auto *BrCondBB = BrCond->getOperand(1).getMBB();
- auto *BrBB = MI.getOperand(0).getMBB();
+ llvm::MachineBasicBlock *BrCondBB = BrCond->getOperand(1).getMBB();
+ llvm::MachineBasicBlock *BrBB = MI.getOperand(0).getMBB();
Observer.changingInstr(MI);
MI.getOperand(0).setMBB(BrCondBB);
@@ -386,13 +386,13 @@ bool PISAPostLegalizerCombinerImpl::matchShlAddToMad(
m_GAdd(m_GShl(m_Reg(MulReg), m_ICst(Shift)), m_Reg(AddReg))))
return false;
- auto Ty = MRI.getType(MulReg);
+ llvm::LLT Ty = MRI.getType(MulReg);
if (Shift.getZExtValue() >= Ty.getSizeInBits())
return false;
MatchInfo = [Ty, &AddMI, AddReg, MulReg,
Shift = std::move(Shift)](MachineIRBuilder &B) {
- auto MulOp2 = B.buildConstant(
+ llvm::MachineInstrBuilder MulOp2 = B.buildConstant(
Ty, APInt::getOneBitSet(Ty.getSizeInBits(), Shift.getZExtValue()));
B.buildIntrinsic(Intrinsic::pisa_smad, AddMI.getOperand(0).getReg())
.addUse(MulReg)
@@ -440,7 +440,7 @@ bool PISAPostLegalizerCombinerImpl::matchFDivToRcpFMul(
uint16_t Flags = MI.getFlags();
MatchInfo = [=](MachineIRBuilder &B) {
- auto Rcp = B.buildIntrinsic(Intrinsic::pisa_frcp, {DstTy})
+ llvm::MachineInstrBuilder Rcp = B.buildIntrinsic(Intrinsic::pisa_frcp, {DstTy})
.addUse(Src1Reg)
.setMIFlags(Flags);
@@ -452,13 +452,17 @@ bool PISAPostLegalizerCombinerImpl::matchFDivToRcpFMul(
void PISAPostLegalizerCombinerImpl::applyShiftOfConstants(
MachineInstr *MI) const {
- auto [SrcMI, SrcRegIdx] =
+ MachineInstr *SrcMI;
+ unsigned SrcRegIdx;
+ std::tie(SrcMI, SrcRegIdx) =
PISA::getDefIgnoringBitcasts(MI->getOperand(1).getReg(), MRI);
- auto [ShiftMI, ShiftRegIdx] =
+ MachineInstr *ShiftMI;
+ unsigned ShiftRegIdx;
+ std::tie(ShiftMI, ShiftRegIdx) =
PISA::getDefIgnoringBitcasts(MI->getOperand(2).getReg(), MRI);
- const auto *CImm = SrcMI->getOperand(1).getCImm();
- auto Shift = ShiftMI->getOperand(1).getCImm()->getZExtValue();
+ const llvm::ConstantInt *CImm = SrcMI->getOperand(1).getCImm();
+ uint64_t Shift = ShiftMI->getOperand(1).getCImm()->getZExtValue();
int64_t NewVal = 0;
switch (MI->getOpcode()) {
default:
@@ -485,19 +489,19 @@ void PISAPostLegalizerCombinerImpl::applyShiftOfConstants(
// => E(32) = COPY ARG(32)
bool PISAPostLegalizerCombinerImpl::matchRedundantMovesPost(
MachineInstr &MI) const {
- auto &BitcastMI = MI;
- auto DstReg = BitcastMI.getOperand(0).getReg();
- auto SrcReg = BitcastMI.getOperand(1).getReg();
+ llvm::MachineInstr &BitcastMI = MI;
+ llvm::Register DstReg = BitcastMI.getOperand(0).getReg();
+ llvm::Register SrcReg = BitcastMI.getOperand(1).getReg();
if (MRI.getType(DstReg).isVector() || !MRI.getType(SrcReg).isVector())
return false;
- auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ llvm::MachineInstr &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
if (BuildVecMI.getOpcode() != TargetOpcode::G_BUILD_VECTOR)
return false;
Register UnmergeReg = 0;
for (unsigned I = 1; I < BuildVecMI.getNumOperands(); I++) {
- auto &UnmergeMI =
+ llvm::MachineInstr &UnmergeMI =
*getDefIgnoringCopies(BuildVecMI.getOperand(I).getReg(), MRI);
if (UnmergeMI.getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
return false;
@@ -510,16 +514,16 @@ bool PISAPostLegalizerCombinerImpl::matchRedundantMovesPost(
return false;
}
// order to extracted operands should match
- auto VecEltReg = BuildVecMI.getOperand(I).getReg();
+ llvm::Register VecEltReg = BuildVecMI.getOperand(I).getReg();
if (UnmergeMI.getOperand(I - 1).getReg() != VecEltReg)
return false;
}
- auto &SrcBitcastMI = *getDefIgnoringCopies(UnmergeReg, MRI);
+ llvm::MachineInstr &SrcBitcastMI = *getDefIgnoringCopies(UnmergeReg, MRI);
if (SrcBitcastMI.getOpcode() != TargetOpcode::G_BITCAST)
return false;
- auto SrcBitcastReg = SrcBitcastMI.getOperand(1).getReg();
+ llvm::Register SrcBitcastReg = SrcBitcastMI.getOperand(1).getReg();
if (MRI.getType(DstReg).getSizeInBits() !=
MRI.getType(SrcBitcastReg).getSizeInBits())
return false;
@@ -528,15 +532,15 @@ bool PISAPostLegalizerCombinerImpl::matchRedundantMovesPost(
void PISAPostLegalizerCombinerImpl::applyRedundantMovesPost(
MachineInstr &MI) const {
- auto &BitcastMI = MI;
- auto DstReg = BitcastMI.getOperand(0).getReg();
- auto SrcReg = BitcastMI.getOperand(1).getReg();
- auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
- auto &UnmergeMI =
+ llvm::MachineInstr &BitcastMI = MI;
+ llvm::Register DstReg = BitcastMI.getOperand(0).getReg();
+ llvm::Register SrcReg = BitcastMI.getOperand(1).getReg();
+ llvm::MachineInstr &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ llvm::MachineInstr &UnmergeMI =
*getDefIgnoringCopies(BuildVecMI.getOperand(1).getReg(), MRI);
- auto &SrcBitcastMI = *getDefIgnoringCopies(
+ llvm::MachineInstr &SrcBitcastMI = *getDefIgnoringCopies(
UnmergeMI.getOperand(UnmergeMI.getNumOperands() - 1).getReg(), MRI);
- auto SrcBitcastReg = SrcBitcastMI.getOperand(1).getReg();
+ llvm::Register SrcBitcastReg = SrcBitcastMI.getOperand(1).getReg();
B.buildCopy(DstReg, SrcBitcastReg);
MI.eraseFromParent();
}
@@ -554,9 +558,9 @@ void PISAPostLegalizerCombinerImpl::applyRedundantMovesPost(
// => Y(<N x s16>) = COPY ARG(<N x s16>)
bool PISAPostLegalizerCombinerImpl::matchExtractAllToBuildVector(
MachineInstr &MI, Register &Replacement) const {
- auto &BuildVecMI = MI;
- auto DstReg = BuildVecMI.getOperand(0).getReg();
- auto SrcReg = BuildVecMI.getOperand(1).getReg();
+ llvm::MachineInstr &BuildVecMI = MI;
+ llvm::Register DstReg = BuildVecMI.getOperand(0).getReg();
+ llvm::Register SrcReg = BuildVecMI.getOperand(1).getReg();
if (MRI.getType(SrcReg).isVector())
return false;
@@ -566,8 +570,8 @@ bool PISAPostLegalizerCombinerImpl::matchExtractAllToBuildVector(
Register SrcVecReg;
bool AllExtracts = true;
for (unsigned I = 1; I < BuildVecMI.getNumOperands(); I++) {
- auto EltReg = BuildVecMI.getOperand(I).getReg();
- auto &ExtractMI = *getDefIgnoringCopies(EltReg, MRI);
+ llvm::Register EltReg = BuildVecMI.getOperand(I).getReg();
+ llvm::MachineInstr &ExtractMI = *getDefIgnoringCopies(EltReg, MRI);
if (ExtractMI.getOpcode() != TargetOpcode::G_EXTRACT_VECTOR_ELT) {
AllExtracts = false;
break;
@@ -580,8 +584,8 @@ bool PISAPostLegalizerCombinerImpl::matchExtractAllToBuildVector(
break;
}
- auto IndexReg = ExtractMI.getOperand(2).getReg();
- auto CValue = getIConstantVRegValWithLookThrough(IndexReg, MRI);
+ llvm::Register IndexReg = ExtractMI.getOperand(2).getReg();
+ std::optional<llvm::ValueAndVReg> CValue = getIConstantVRegValWithLookThrough(IndexReg, MRI);
if (!CValue.has_value() || CValue->Value != (I - 1)) {
AllExtracts = false;
break;
@@ -597,21 +601,21 @@ bool PISAPostLegalizerCombinerImpl::matchExtractAllToBuildVector(
// Branch B: all operands are the sequential defs of a single G_UNMERGE_VALUES
// whose source vector type matches the G_BUILD_VECTOR dest type.
unsigned NumElts = BuildVecMI.getNumOperands() - 1;
- auto FirstDefAndReg =
+ std::optional<llvm::DefinitionAndSourceRegister> FirstDefAndReg =
getDefSrcRegIgnoringCopies(BuildVecMI.getOperand(1).getReg(), MRI);
if (!FirstDefAndReg)
return false;
- auto *UnmergeMI = FirstDefAndReg->MI;
+ llvm::MachineInstr *UnmergeMI = FirstDefAndReg->MI;
if (UnmergeMI->getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
return false;
if (UnmergeMI->getNumOperands() - 1 != NumElts)
return false; // G_UNMERGE_VALUES produces a different number of elements
- auto UnmergeSrcReg = UnmergeMI->getOperand(NumElts).getReg();
+ llvm::Register UnmergeSrcReg = UnmergeMI->getOperand(NumElts).getReg();
if (MRI.getType(DstReg) != MRI.getType(UnmergeSrcReg))
return false;
for (unsigned I = 0; I < NumElts; I++) {
- auto DefAndReg =
+ std::optional<llvm::DefinitionAndSourceRegister> DefAndReg =
getDefSrcRegIgnoringCopies(BuildVecMI.getOperand(I + 1).getReg(), MRI);
if (!DefAndReg || DefAndReg->MI != UnmergeMI)
return false; // different producer
@@ -624,8 +628,8 @@ bool PISAPostLegalizerCombinerImpl::matchExtractAllToBuildVector(
}
void PISAPostLegalizerCombinerImpl::applyExtractAllToBuildVector(
MachineInstr &MI, Register Replacement) const {
- auto &BuildVecMI = MI;
- auto DstReg = BuildVecMI.getOperand(0).getReg();
+ llvm::MachineInstr &BuildVecMI = MI;
+ llvm::Register DstReg = BuildVecMI.getOperand(0).getReg();
B.buildCopy(DstReg, Replacement);
MI.eraseFromParent();
}
@@ -640,7 +644,7 @@ bool PISAPostLegalizerCombinerImpl::matchOrAndToBfi(
std::function<void(MachineIRBuilder &)> &MatchInfo) const {
// BFI supports only 32 bits of width, start with this simple check to
// exclude unsupported shifts early.
- auto BitWidth = MRI.getType(OrMI.getOperand(0).getReg()).getSizeInBits();
+ llvm::TypeSize BitWidth = MRI.getType(OrMI.getOperand(0).getReg()).getSizeInBits();
if (BitWidth != 32)
return false;
@@ -665,7 +669,9 @@ bool PISAPostLegalizerCombinerImpl::matchOrAndToBfi(
return false;
}
- auto [ShiftedDataMI, ShiftedDataRegIdx] =
+ MachineInstr *ShiftedDataMI;
+ unsigned ShiftedDataRegIdx;
+ std::tie(ShiftedDataMI, ShiftedDataRegIdx) =
PISA::getDefIgnoringBitcasts(ShiftedData, MRI);
Register Data;
@@ -674,8 +680,8 @@ bool PISAPostLegalizerCombinerImpl::matchOrAndToBfi(
return false;
MatchInfo = [Data, Base, Width, Offset, &OrMI](MachineIRBuilder &B) {
- auto WidthReg = B.buildConstant(I32, Width);
- auto OffsetReg = B.buildConstant(I32, Offset);
+ llvm::MachineInstrBuilder WidthReg = B.buildConstant(I32, Width);
+ llvm::MachineInstrBuilder OffsetReg = B.buildConstant(I32, Offset);
B.buildIntrinsic(Intrinsic::pisa_bfi, {OrMI.getOperand(0)})
.addUse(Base)
.addUse(Data)
@@ -695,12 +701,14 @@ bool PISAPostLegalizerCombinerImpl::matchShiftSubToBfe(
std::function<void(MachineIRBuilder &)> &MatchInfo) const {
// BFE supports only 32 bits of width, start with this simple check to
// exclude unsupported shifts early.
- auto BitWidth = MRI.getType(ShrMI.getOperand(0).getReg()).getSizeInBits();
+ llvm::TypeSize BitWidth = MRI.getType(ShrMI.getOperand(0).getReg()).getSizeInBits();
if (BitWidth != 32)
return false;
- auto MakeApplyHandler = [&ShrMI](Register Data, Register Width,
- Register Offset) {
+ std::function<std::function<void(MachineIRBuilder &)>(Register, Register,
+ Register)>
+ MakeApplyHandler = [&ShrMI](Register Data, Register Width,
+ Register Offset) {
return [&ShrMI, Data, Width, Offset](MachineIRBuilder &B) {
B.buildIntrinsic(Intrinsic::pisa_ubfe, {ShrMI.getOperand(0)})
.addUse(Data)
@@ -769,11 +777,11 @@ bool PISAPostLegalizerCombinerImpl::matchShiftSubToBfe(
// =>
// s32 %andwidth = G_AND i32 %width, 31
// bfe %data %andwidth %offset
- auto WidthType = MRI.getType(Width);
+ llvm::LLT WidthType = MRI.getType(Width);
MatchInfo = [Data, Width, WidthType, Offset,
&ShrMI](MachineIRBuilder &B) {
- auto Mask = B.buildConstant(WidthType, ShiftInstMask);
- auto MaskedWidth = B.buildAnd(WidthType, Width, Mask);
+ llvm::MachineInstrBuilder Mask = B.buildConstant(WidthType, ShiftInstMask);
+ llvm::MachineInstrBuilder MaskedWidth = B.buildAnd(WidthType, Width, Mask);
B.buildIntrinsic(Intrinsic::pisa_ubfe, {ShrMI.getOperand(0)})
.addUse(Data)
.addUse(MaskedWidth->getOperand(0).getReg())
@@ -795,7 +803,7 @@ bool PISAPostLegalizerCombinerImpl::matchAndBitfieldToBitfield(
std::function<void(MachineIRBuilder &)> &MatchInfo) const {
Register Data, Width, Offset;
Intrinsic::ID II;
- if (auto *MIIntrinsic = dyn_cast<GIntrinsic>(&MI)) {
+ if (llvm::GIntrinsic *MIIntrinsic = dyn_cast<GIntrinsic>(&MI)) {
II = MIIntrinsic->getIntrinsicID();
if (II == Intrinsic::pisa_ubfe) {
Data = MI.getOperand(2).getReg();
@@ -872,7 +880,7 @@ bool PISAPostLegalizerCombinerImpl::matchAndBitfieldToBitfield(
bool PISAPostLegalizerCombinerImpl::matchV2i1ZextToBfeBfi(
MachineInstr &BitcastMI, Register &BitcastInput) const {
- auto VectorElementSize =
+ unsigned VectorElementSize =
MRI.getType(BitcastMI.getOperand(0).getReg()).getScalarSizeInBits();
// We only support an i8 zext
if (VectorElementSize != 8)
@@ -910,8 +918,8 @@ bool PISAPostLegalizerCombinerImpl::matchV2i1ZextToBfeBfi(
code example), and one unmerge instruction with both values being used
(i.e. UpperUnmerge =?= LowerUnmerge)
*/
- auto *UpperUnmergeInstr = MRI.getUniqueVRegDef(UpperCmpRes);
- auto *LowerUnmergeInstr = MRI.getUniqueVRegDef(LowerCmpRes);
+ llvm::MachineInstr *UpperUnmergeInstr = MRI.getUniqueVRegDef(UpperCmpRes);
+ llvm::MachineInstr *LowerUnmergeInstr = MRI.getUniqueVRegDef(LowerCmpRes);
if (!UpperUnmergeInstr || !LowerUnmergeInstr ||
UpperUnmergeInstr->getOpcode() != TargetOpcode::G_UNMERGE_VALUES ||
LowerUnmergeInstr->getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
@@ -923,12 +931,12 @@ bool PISAPostLegalizerCombinerImpl::matchV2i1ZextToBfeBfi(
if (LowerUnmergeInstr->getOperand(1).getReg() != LowerCmpRes)
return false;
- auto BitcastRes = UpperUnmergeInstr->getOperand(2);
+ llvm::MachineOperand BitcastRes = UpperUnmergeInstr->getOperand(2);
if (!BitcastRes.isIdenticalTo(LowerUnmergeInstr->getOperand(2)))
return false;
// Verify that this inst is a G_BITCAST, and get the input register
- auto *BitcastInst = MRI.getUniqueVRegDef(BitcastRes.getReg());
+ llvm::MachineInstr *BitcastInst = MRI.getUniqueVRegDef(BitcastRes.getReg());
if (!BitcastInst || BitcastInst->getOpcode() != TargetOpcode::G_BITCAST)
return false;
@@ -939,15 +947,15 @@ bool PISAPostLegalizerCombinerImpl::matchV2i1ZextToBfeBfi(
void PISAPostLegalizerCombinerImpl::applyV2i1ZextToBfeBfi(
MachineInstr &BitcastMI, Register BitcastInput) const {
- auto *MRI = B.getMRI();
- auto Bfe1Res = MRI->createGenericVirtualRegister(I32);
- auto Bfe2Res = MRI->createGenericVirtualRegister(I32);
- auto BfiRes = MRI->createGenericVirtualRegister(I32);
+ llvm::MachineRegisterInfo *MRI = B.getMRI();
+ llvm::Register Bfe1Res = MRI->createGenericVirtualRegister(I32);
+ llvm::Register Bfe2Res = MRI->createGenericVirtualRegister(I32);
+ llvm::Register BfiRes = MRI->createGenericVirtualRegister(I32);
- auto Zero = B.buildConstant(I32, 0);
- auto One = B.buildConstant(I32, 1);
- auto Eight = B.buildConstant(I32, 8);
- auto Sixteen = B.buildConstant(I32, 16);
+ llvm::MachineInstrBuilder Zero = B.buildConstant(I32, 0);
+ llvm::MachineInstrBuilder One = B.buildConstant(I32, 1);
+ llvm::MachineInstrBuilder Eight = B.buildConstant(I32, 8);
+ llvm::MachineInstrBuilder Sixteen = B.buildConstant(I32, 16);
B.buildIntrinsic(Intrinsic::pisa_ubfe, {DstOp(Bfe1Res)})
.addUse(BitcastInput)
@@ -985,7 +993,7 @@ void PISAPostLegalizerCombinerImpl::applyV2i1ZextToBfeBfi(
In all other cases, insert a G_BITCAST to <2 x s8>.
*/
- auto StoreInput = BitcastMI.getOperand(0).getReg();
+ llvm::Register StoreInput = BitcastMI.getOperand(0).getReg();
if (MRI->hasOneNonDBGUse(StoreInput) &&
MRI->use_instr_nodbg_begin(StoreInput)->getOpcode() ==
TargetOpcode::G_STORE) {
@@ -993,12 +1001,12 @@ void PISAPostLegalizerCombinerImpl::applyV2i1ZextToBfeBfi(
B.buildInstr(TargetOpcode::G_TRUNC).addDef(StoreInput).addUse(BfiRes);
MRI->setType(StoreInput, I16);
- for (auto *MemOp : MRI->use_instr_nodbg_begin(StoreInput)->memoperands())
+ for (llvm::MachineMemOperand *MemOp : MRI->use_instr_nodbg_begin(StoreInput)->memoperands())
MemOp->setType(I16);
} else {
// We either have multiple users or the following user is not a G_STORE
// Insert a bitcast to <2 x s8> so that nothing breaks
- auto TruncRes = MRI->createGenericVirtualRegister(I16);
+ llvm::Register TruncRes = MRI->createGenericVirtualRegister(I16);
B.buildInstr(TargetOpcode::G_TRUNC).addDef(TruncRes).addUse(BfiRes);
B.buildInstr(TargetOpcode::G_BITCAST).addDef(StoreInput).addUse(TruncRes);
@@ -1016,21 +1024,21 @@ void PISAPostLegalizerCombinerImpl::applyV2i1ZextToBfeBfi(
// => %405:_(s16) = COPY %375
bool PISAPostLegalizerCombinerImpl::matchAndSelect(
MachineInstr &MI, Register &Replacement) const {
- auto &SelectMI = MI;
+ llvm::MachineInstr &SelectMI = MI;
- auto Const0 =
+ std::optional<llvm::ValueAndVReg> Const0 =
getIConstantVRegValWithLookThrough(SelectMI.getOperand(3).getReg(), MRI);
- auto Const1 =
+ std::optional<llvm::ValueAndVReg> Const1 =
getIConstantVRegValWithLookThrough(SelectMI.getOperand(2).getReg(), MRI);
if (!Const0.has_value() || !Const1.has_value())
return false;
if ((Const0->Value != 0) || (Const1->Value != 1))
return false;
- auto &AndMI = *getDefIgnoringCopies(SelectMI.getOperand(1).getReg(), MRI);
+ llvm::MachineInstr &AndMI = *getDefIgnoringCopies(SelectMI.getOperand(1).getReg(), MRI);
if (AndMI.getOpcode() != TargetOpcode::G_AND)
return false;
- auto AndConst =
+ std::optional<llvm::ValueAndVReg> AndConst =
getIConstantVRegValWithLookThrough(AndMI.getOperand(2).getReg(), MRI);
if (!AndConst.has_value() || (AndConst->Value != 1))
return false;
@@ -1063,35 +1071,35 @@ void PISAPostLegalizerCombinerImpl::applyAndSelect(MachineInstr &MI,
// => %141 = G_OR %A, %B
bool PISAPostLegalizerCombinerImpl::matchBuildRegFrom2(
MachineInstr &MI, Reg2MatchInfo &MatchInfo) const {
- auto &BitcastMI = MI;
+ llvm::MachineInstr &BitcastMI = MI;
- auto DstTy = MRI.getType(BitcastMI.getOperand(0).getReg());
+ llvm::LLT DstTy = MRI.getType(BitcastMI.getOperand(0).getReg());
if (DstTy.isVector() || (DstTy.getScalarSizeInBits() != 32))
return false;
- auto SrcReg = BitcastMI.getOperand(1).getReg();
- auto SrcTy = MRI.getType(SrcReg);
+ llvm::Register SrcReg = BitcastMI.getOperand(1).getReg();
+ llvm::LLT SrcTy = MRI.getType(SrcReg);
if (!SrcTy.isVector() || (SrcTy.getScalarSizeInBits() != 8))
return false;
- auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ llvm::MachineInstr &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
if (BuildVecMI.getOpcode() != TargetOpcode::G_BUILD_VECTOR)
return false;
- for (auto I = 0; I < 2; I++) {
- auto LoReg = BuildVecMI.getOperand(1 + (I * 2)).getReg();
- auto HiReg = BuildVecMI.getOperand(2 + (I * 2)).getReg();
- auto &LoTruncMI = *getDefIgnoringCopies(LoReg, MRI);
- auto &HiTruncMI = *getDefIgnoringCopies(HiReg, MRI);
+ for (int I = 0; I < 2; I++) {
+ llvm::Register LoReg = BuildVecMI.getOperand(1 + (I * 2)).getReg();
+ llvm::Register HiReg = BuildVecMI.getOperand(2 + (I * 2)).getReg();
+ llvm::MachineInstr &LoTruncMI = *getDefIgnoringCopies(LoReg, MRI);
+ llvm::MachineInstr &HiTruncMI = *getDefIgnoringCopies(HiReg, MRI);
if (LoTruncMI.getOpcode() != TargetOpcode::G_TRUNC)
return false;
if (HiTruncMI.getOpcode() != TargetOpcode::G_TRUNC)
return false;
- auto &HiShiftMI =
+ llvm::MachineInstr &HiShiftMI =
*getDefIgnoringCopies(HiTruncMI.getOperand(1).getReg(), MRI);
if (HiShiftMI.getOpcode() != TargetOpcode::G_LSHR)
return false;
if (LoTruncMI.getOperand(1).getReg() != HiShiftMI.getOperand(1).getReg())
return false;
- auto ShiftConst = getIConstantVRegValWithLookThrough(
+ std::optional<llvm::ValueAndVReg> ShiftConst = getIConstantVRegValWithLookThrough(
HiShiftMI.getOperand(2).getReg(), MRI);
if (!ShiftConst.has_value() || (ShiftConst->Value != 8))
return false;
@@ -1104,15 +1112,15 @@ bool PISAPostLegalizerCombinerImpl::matchBuildRegFrom2(
}
void PISAPostLegalizerCombinerImpl::applyBuildRegFrom2(
MachineInstr &MI, Reg2MatchInfo &MatchInfo) const {
- auto MaskReg = MRI.createGenericVirtualRegister(LLT::integer(32));
- auto Mask = B.buildConstant(MaskReg, 0xFFFF);
- auto ShiftReg = MRI.createGenericVirtualRegister(LLT::integer(32));
- auto Shift = B.buildConstant(ShiftReg, 16);
+ llvm::Register MaskReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ llvm::MachineInstrBuilder Mask = B.buildConstant(MaskReg, 0xFFFF);
+ llvm::Register ShiftReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ llvm::MachineInstrBuilder Shift = B.buildConstant(ShiftReg, 16);
- auto AReg = MRI.createGenericVirtualRegister(LLT::integer(32));
- auto BReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ llvm::Register AReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ llvm::Register BReg = MRI.createGenericVirtualRegister(LLT::integer(32));
- auto RegSize = MRI.getType(MatchInfo.Reg0).getSizeInBits();
+ llvm::TypeSize RegSize = MRI.getType(MatchInfo.Reg0).getSizeInBits();
assert((RegSize == 16 || RegSize == 32 || RegSize == 64) &&
"only supporting 16/32/64bit registers");
Register ASrcReg, BSrcReg;
@@ -1154,7 +1162,8 @@ bool PISAPostLegalizerCombinerImpl::matchBuildVectorFromUnmergeLanes(
Register SrcReg;
int64_t BaseLane = -1;
for (unsigned I = 0; I < NumElts; ++I) {
- auto Def = getDefSrcRegIgnoringCopies(MI.getOperand(1 + I).getReg(), MRI);
+ std::optional<llvm::DefinitionAndSourceRegister> Def =
+ getDefSrcRegIgnoringCopies(MI.getOperand(1 + I).getReg(), MRI);
if (!Def)
return false;
const MachineInstr *D = Def->MI;
@@ -1201,7 +1210,8 @@ bool PISAPostLegalizerCombinerImpl::matchBuildVectorFromUnmergeLanes(
void PISAPostLegalizerCombinerImpl::applyBuildVectorFromUnmergeLanes(
MachineInstr &MI, std::tuple<Register, int64_t> &MatchInfo) const {
- auto [SrcReg, BaseLane] = MatchInfo;
+ Register SrcReg = std::get<0>(MatchInfo);
+ int64_t BaseLane = std::get<1>(MatchInfo);
B.buildExtractSubvector(MI.getOperand(0).getReg(), SrcReg, BaseLane);
MI.eraseFromParent();
}
@@ -1224,7 +1234,7 @@ bool PISAPostLegalizerCombinerImpl::matchBuildVectorConcatSubvectors(
const MachineInstr *Unmerge = nullptr;
Register SrcReg;
for (unsigned J = 0; J < M; ++J) {
- auto Def = getDefSrcRegIgnoringCopies(
+ std::optional<llvm::DefinitionAndSourceRegister> Def = getDefSrcRegIgnoringCopies(
MI.getOperand(1 + G * M + J).getReg(), MRI);
if (!Def)
return false;
@@ -1281,7 +1291,7 @@ bool PISAPostLegalizerCombinerImpl::matchCmpAndAllOnes(
(Pred != CmpInst::ICMP_NE))
return false;
- auto BitWidth = MRI.getType(SrcReg).getSizeInBits();
+ llvm::TypeSize BitWidth = MRI.getType(SrcReg).getSizeInBits();
if (BitWidth < 16)
return false;
@@ -1338,7 +1348,7 @@ bool PISAPostLegalizerCombinerImpl::matchSinkTrunc(
return false;
// We don't want to create 64-bit boolean operations
- auto BitWidth = MRI.getType(SrcReg).getSizeInBits();
+ llvm::TypeSize BitWidth = MRI.getType(SrcReg).getSizeInBits();
if (BitWidth > 32)
return false;
@@ -1350,9 +1360,9 @@ void PISAPostLegalizerCombinerImpl::applySinkTrunc(
int64_t Mask;
Register SrcReg;
std::tie(SrcReg, Mask) = MatchInfo;
- auto SrcTy = MRI.getType(SrcReg);
- auto MaskReg = MRI.createGenericVirtualRegister(SrcTy);
- auto DstReg = MRI.createGenericVirtualRegister(SrcTy);
+ llvm::LLT SrcTy = MRI.getType(SrcReg);
+ llvm::Register MaskReg = MRI.createGenericVirtualRegister(SrcTy);
+ llvm::Register DstReg = MRI.createGenericVirtualRegister(SrcTy);
B.buildConstant(MaskReg, Mask);
B.buildInstr(MI.getOpcode()).addDef(DstReg).addUse(SrcReg).addUse(MaskReg);
B.buildInstr(TargetOpcode::G_TRUNC)
@@ -1396,7 +1406,7 @@ bool PISAPostLegalizerCombinerImpl::matchAbsRedMaxToRedAbsMax(
default:
return false;
case TargetOpcode::G_INTRINSIC_CONVERGENT: {
- auto II = cast<GIntrinsic>(MI).getIntrinsicID();
+ unsigned II = cast<GIntrinsic>(MI).getIntrinsicID();
if (II == Intrinsic::pisa_ired) {
if (MI.getOperand(2).getImm() != pisa::IRedOp::UMAX)
return false;
@@ -1415,7 +1425,9 @@ bool PISAPostLegalizerCombinerImpl::matchAbsRedMaxToRedAbsMax(
}
}
unsigned SrcOpIdx = IntrID ? 3 : 2;
- auto [SrcMI, SrcRegIdx] =
+ MachineInstr *SrcMI;
+ unsigned SrcRegIdx;
+ std::tie(SrcMI, SrcRegIdx) =
PISA::getDefIgnoringBitcasts(MI.getOperand(SrcOpIdx).getReg(), MRI);
MachineInstr *SrcMIPtr = SrcMI;
if (SrcMIPtr->getOpcode() != AbsOpcode)
@@ -1428,7 +1440,7 @@ bool PISAPostLegalizerCombinerImpl::matchAbsRedMaxToRedAbsMax(
if (IntrID)
MatchInfo = [SrcMIPtr, AbsSrcIdx, &MI, IntrID,
TargetOpType](MachineIRBuilder &B) {
- auto MIB = B.buildIntrinsic(IntrID, {MI.getOperand(0)});
+ llvm::MachineInstrBuilder MIB = B.buildIntrinsic(IntrID, {MI.getOperand(0)});
MIB.addImm(TargetOpType);
MIB.addUse(SrcMIPtr->getOperand(AbsSrcIdx).getReg());
MIB.addUse(MI.getOperand(4).getReg());
@@ -1469,9 +1481,9 @@ bool PISAPostLegalizerCombinerImpl::matchShiftTrueFalse(
// Result can be only all zeros or all ones. But if sources are also only
// all zeros/ones, then shl/ashr is a redundant artifact from legalization.
- auto DstReg = AShrMI.getOperand(0).getReg();
- auto DstTy = MRI.getType(DstReg);
- auto ShiftAmount = DstTy.getScalarSizeInBits() - 1;
+ llvm::Register DstReg = AShrMI.getOperand(0).getReg();
+ llvm::LLT DstTy = MRI.getType(DstReg);
+ unsigned ShiftAmount = DstTy.getScalarSizeInBits() - 1;
Register SrcReg;
if (!mi_match(AShrMI, MRI,
@@ -1480,11 +1492,11 @@ bool PISAPostLegalizerCombinerImpl::matchShiftTrueFalse(
return false;
std::function<bool(Register)> Match = [&](Register Reg) {
- auto CValue = getIConstantVRegValWithLookThrough(Reg, MRI);
+ std::optional<llvm::ValueAndVReg> CValue = getIConstantVRegValWithLookThrough(Reg, MRI);
if (CValue.has_value())
return CValue->Value.isZero() || CValue->Value.isAllOnes();
- auto *MI = getDefIgnoringCopies(Reg, MRI);
+ llvm::MachineInstr *MI = getDefIgnoringCopies(Reg, MRI);
if (!MI)
return false;
@@ -1510,7 +1522,7 @@ bool PISAPostLegalizerCombinerImpl::matchShiftTrueFalse(
MatchInfo = [DstReg, SrcReg, this](MachineIRBuilder &B) {
if (MRI.hasOneNonDBGUse(SrcReg)) {
- auto *MI = getDefIgnoringCopies(SrcReg, MRI);
+ llvm::MachineInstr *MI = getDefIgnoringCopies(SrcReg, MRI);
if (MI) {
MI->getOperand(0).setReg(DstReg);
return;
@@ -1536,26 +1548,35 @@ bool PISAPostLegalizerCombinerImpl::matchAddInt8Reduction(
MachineInstr &MI,
std::function<void(MachineIRBuilder &)> &MatchInfo) const {
- auto Dst = MI.getOperand(0).getReg();
- auto DstTy = MRI.getType(Dst);
+ llvm::Register Dst = MI.getOperand(0).getReg();
+ llvm::LLT DstTy = MRI.getType(Dst);
if (!DstTy.isScalar() || DstTy.getSizeInBits() > 32)
return false;
std::array<Register, 4> Srcs;
// Helper to match anyext or zext of a register
- auto MatchExt = [](Register &Reg) {
- return m_any_of(m_GAnyExt(m_Reg(Reg)), m_GZExt(m_Reg(Reg)));
- };
if (!mi_match(MI, MRI,
- m_any_of(m_GAdd(m_OneNonDBGUse(m_GAdd(MatchExt(Srcs[0]),
- MatchExt(Srcs[1]))),
- m_OneNonDBGUse(m_GAdd(MatchExt(Srcs[2]),
- MatchExt(Srcs[3])))),
+ m_any_of(m_GAdd(
+ m_OneNonDBGUse(m_GAdd(
+ m_any_of(m_GAnyExt(m_Reg(Srcs[0])),
+ m_GZExt(m_Reg(Srcs[0]))),
+ m_any_of(m_GAnyExt(m_Reg(Srcs[1])),
+ m_GZExt(m_Reg(Srcs[1]))))),
+ m_OneNonDBGUse(m_GAdd(
+ m_any_of(m_GAnyExt(m_Reg(Srcs[2])),
+ m_GZExt(m_Reg(Srcs[2]))),
+ m_any_of(m_GAnyExt(m_Reg(Srcs[3])),
+ m_GZExt(m_Reg(Srcs[3])))))),
m_GAdd(m_OneNonDBGUse(m_GAdd(
- m_OneNonDBGUse(m_GAdd(MatchExt(Srcs[0]),
- MatchExt(Srcs[1]))),
- MatchExt(Srcs[2]))),
- MatchExt(Srcs[3])))))
+ m_OneNonDBGUse(m_GAdd(
+ m_any_of(m_GAnyExt(m_Reg(Srcs[0])),
+ m_GZExt(m_Reg(Srcs[0]))),
+ m_any_of(m_GAnyExt(m_Reg(Srcs[1])),
+ m_GZExt(m_Reg(Srcs[1]))))),
+ m_any_of(m_GAnyExt(m_Reg(Srcs[2])),
+ m_GZExt(m_Reg(Srcs[2]))))),
+ m_any_of(m_GAnyExt(m_Reg(Srcs[3])),
+ m_GZExt(m_Reg(Srcs[3])))))))
return false;
// All registers must be unique.
@@ -1564,8 +1585,8 @@ bool PISAPostLegalizerCombinerImpl::matchAddInt8Reduction(
return false;
// All register must be i8 from the same unmerge instruction.
- auto GetUnmerge = [=](Register &Reg) {
- auto *UnmergeMI = MRI.getVRegDef(Reg);
+ std::function<MachineInstr *(Register &)> GetUnmerge = [=](Register &Reg) {
+ llvm::MachineInstr *UnmergeMI = MRI.getVRegDef(Reg);
return UnmergeMI->getOpcode() == TargetOpcode::G_UNMERGE_VALUES ? UnmergeMI
: nullptr;
};
@@ -1578,16 +1599,16 @@ bool PISAPostLegalizerCombinerImpl::matchAddInt8Reduction(
return false;
}
- auto VectorReg =
+ llvm::Register VectorReg =
UnmergeMI->getOperand(UnmergeMI->getNumOperands() - 1).getReg();
if (MRI.getType(VectorReg) !=
LLT::vector(ElementCount::getFixed(4), LLT::integer(8)))
return false;
MatchInfo = [=](MachineIRBuilder &B) {
- auto Acc = MRI.createGenericVirtualRegister(I32);
- auto X = MRI.createGenericVirtualRegister(I32);
- auto Y = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Acc = MRI.createGenericVirtualRegister(I32);
+ llvm::Register X = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Y = MRI.createGenericVirtualRegister(I32);
B.buildConstant(Acc, 0);
B.buildBitcast(X, VectorReg);
@@ -1601,7 +1622,7 @@ bool PISAPostLegalizerCombinerImpl::matchAddInt8Reduction(
.addUse(Y)
.addImm(0);
} else {
- auto Dst32 = MRI.createGenericVirtualRegister(I32);
+ llvm::Register Dst32 = MRI.createGenericVirtualRegister(I32);
B.buildIntrinsic(Intrinsic::pisa_dp4a_uu, {DstOp(Dst32)})
.addUse(Acc)
.addUse(X)
@@ -1622,13 +1643,15 @@ bool PISAPostLegalizerCombinerImpl::matchUnmergeBitcastBuildVectorToBitcast(
MachineInstr &MI, Register &MatchInfo) const {
Register UnmergeSrc;
for (unsigned I = 1, E = MI.getNumOperands(); I < E; I++) {
- auto [SrcMI, SrcRegIdx] =
+ MachineInstr *SrcMI;
+ unsigned SrcRegIdx;
+ std::tie(SrcMI, SrcRegIdx) =
PISA::getDefIgnoringBitcasts(MI.getOperand(I).getReg(), MRI, true);
if (!SrcMI || SrcMI->getOpcode() != TargetOpcode::G_UNMERGE_VALUES)
return false;
if (SrcMI->getNumOperands() != MI.getNumOperands())
return false;
- auto SrcReg = SrcMI->getOperand(SrcMI->getNumOperands() - 1).getReg();
+ llvm::Register SrcReg = SrcMI->getOperand(SrcMI->getNumOperands() - 1).getReg();
if (SrcRegIdx != (I - 1))
return false;
if (I == 1)
@@ -1641,7 +1664,7 @@ bool PISAPostLegalizerCombinerImpl::matchUnmergeBitcastBuildVectorToBitcast(
}
void PISAPostLegalizerCombinerImpl::applyUnmergeBitcastBuildVectorToBitcast(
MachineInstr &MI, Register &MatchInfo) const {
- auto DstReg = MI.getOperand(0).getReg();
+ llvm::Register DstReg = MI.getOperand(0).getReg();
if (MRI.getType(DstReg) != MRI.getType(MatchInfo))
B.buildBitcast(MI.getOperand(0).getReg(), MatchInfo);
else
@@ -1653,11 +1676,12 @@ void PISAPostLegalizerCombinerImpl::applyUnmergeBitcastBuildVectorToBitcast(
// These are execution barriers, not memory ordering fences, and should
// be preserved.
static bool isSubgroupFence(const MachineInstr &MI) {
- auto ScopeID = static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
+ SyncScope::ID ScopeID =
+ static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
if (ScopeID == SyncScope::SingleThread)
return true;
- auto &Ctx = MI.getMF()->getFunction().getContext();
- if (auto Name = Ctx.getSyncScopeName(ScopeID))
+ llvm::LLVMContext &Ctx = MI.getMF()->getFunction().getContext();
+ if (std::optional<llvm::StringRef> Name = Ctx.getSyncScopeName(ScopeID))
return Name->starts_with("subgroup") || Name->starts_with("workitem");
return false;
}
@@ -1666,9 +1690,10 @@ static bool isSubgroupFence(const MachineInstr &MI) {
// include an address space suffix (e.g. "workgroup-shared", "workgroup-global",
// "workgroup-generic", or just "workgroup").
static StringRef getBaseScopeName(const MachineInstr &MI) {
- auto ScopeID = static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
- auto &Ctx = MI.getMF()->getFunction().getContext();
- auto ScopeName = Ctx.getSyncScopeName(ScopeID);
+ SyncScope::ID ScopeID =
+ static_cast<SyncScope::ID>(MI.getOperand(1).getImm());
+ llvm::LLVMContext &Ctx = MI.getMF()->getFunction().getContext();
+ std::optional<llvm::StringRef> ScopeName = Ctx.getSyncScopeName(ScopeID);
if (!ScopeName)
return StringRef();
@@ -1687,7 +1712,7 @@ bool PISAPostLegalizerCombinerImpl::matchRedundantFence(
unsigned Scope = MI.getOperand(1).getImm();
- auto It = MI.getIterator();
+ MachineBasicBlock::iterator It = MI.getIterator();
const MachineBasicBlock &MBB = *MI.getParent();
while (It != MBB.begin()) {
--It;
@@ -1712,13 +1737,13 @@ void PISAPostLegalizerCombinerImpl::applyRedundantFence(
MachineInstr &MI, MachineInstr *&PrevFence) const {
assert(PrevFence && "Expected a preceding compatible fence");
- auto CurOrd = static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
- auto PrevOrd = static_cast<AtomicOrdering>(PrevFence->getOperand(0).getImm());
+ llvm::AtomicOrdering CurOrd = static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
+ llvm::AtomicOrdering PrevOrd = static_cast<AtomicOrdering>(PrevFence->getOperand(0).getImm());
// Pick the stronger ordering (e.g. choose seq_cst over acquire).
// Acquire and release cannot be compared; use acq_rel for the
// resulting fence.
- auto Merged = getMergedAtomicOrdering(PrevOrd, CurOrd);
+ llvm::AtomicOrdering Merged = getMergedAtomicOrdering(PrevOrd, CurOrd);
PrevFence->getOperand(0).setImm(static_cast<int64_t>(Merged));
MI.eraseFromParent();
}
@@ -1731,7 +1756,7 @@ bool PISAPostLegalizerCombinerImpl::matchExtractSubvectorBuildVector(
assert(MI.getOpcode() == TargetOpcode::G_EXTRACT_SUBVECTOR);
if (!MI.getOperand(2).isImm())
return false;
- auto *BV = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
+ llvm::MachineInstr *BV = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
if (!BV || BV->getOpcode() != TargetOpcode::G_BUILD_VECTOR)
return false;
uint64_t Offset = MI.getOperand(2).getImm();
@@ -1765,24 +1790,24 @@ bool PISAPostLegalizerCombinerImpl::matchMergeAdjacentFences(
if (isSubgroupFence(MI))
return false;
- auto Order = static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
- auto BaseScopeNameA = getBaseScopeName(MI);
+ llvm::AtomicOrdering Order = static_cast<AtomicOrdering>(MI.getOperand(0).getImm());
+ llvm::StringRef BaseScopeNameA = getBaseScopeName(MI);
if (BaseScopeNameA.empty())
return false;
- auto It = MI.getIterator();
+ MachineBasicBlock::iterator It = MI.getIterator();
const MachineBasicBlock &MBB = *MI.getParent();
while (It != MBB.begin()) {
--It;
if (It->getOpcode() == TargetOpcode::G_FENCE) {
- auto ItOrder = static_cast<AtomicOrdering>(It->getOperand(0).getImm());
+ llvm::AtomicOrdering ItOrder = static_cast<AtomicOrdering>(It->getOperand(0).getImm());
if (ItOrder != Order)
return false;
if (It->getOperand(1).getImm() == MI.getOperand(1).getImm())
return false;
- auto BaseScopeNameIt = getBaseScopeName(*It);
+ llvm::StringRef BaseScopeNameIt = getBaseScopeName(*It);
if (BaseScopeNameIt.empty() || BaseScopeNameIt != BaseScopeNameA)
return false;
@@ -1804,8 +1829,8 @@ void PISAPostLegalizerCombinerImpl::applyMergeAdjacentFences(
StringRef BaseScopeName = getBaseScopeName(*PrevFence);
assert(!BaseScopeName.empty() && "Expected a named sync scope");
std::string GenericName = (BaseScopeName + "-generic").str();
- auto &Ctx = MI.getMF()->getFunction().getContext();
- auto GenericID = Ctx.getOrInsertSyncScopeID(GenericName);
+ llvm::LLVMContext &Ctx = MI.getMF()->getFunction().getContext();
+ SyncScope::ID GenericID = Ctx.getOrInsertSyncScopeID(GenericName);
PrevFence->getOperand(1).setImm(static_cast<int64_t>(GenericID));
MI.eraseFromParent();
@@ -1901,7 +1926,8 @@ bool PISAPostLegalizerCombinerImpl::matchMinMaxIdentityFold(
MI.getOpcode() == TargetOpcode::G_SMAX);
Register LHS = MI.getOperand(1).getReg();
Register RHS = MI.getOperand(2).getReg();
- auto IsIdentity = [&](const APInt &Val) -> bool {
+ std::function<bool(const APInt &)> IsIdentity =
+ [&](const APInt &Val) -> bool {
switch (MI.getOpcode()) {
case TargetOpcode::G_UMIN:
return Val.isAllOnes();
@@ -1915,12 +1941,12 @@ bool PISAPostLegalizerCombinerImpl::matchMinMaxIdentityFold(
return false;
}
};
- if (auto C = getIConstantVRegValWithLookThrough(RHS, MRI))
+ if (std::optional<llvm::ValueAndVReg> C = getIConstantVRegValWithLookThrough(RHS, MRI))
if (IsIdentity(C->Value)) {
MatchInfo = LHS;
return true;
}
- if (auto C = getIConstantVRegValWithLookThrough(LHS, MRI))
+ if (std::optional<llvm::ValueAndVReg> C = getIConstantVRegValWithLookThrough(LHS, MRI))
if (IsIdentity(C->Value)) {
MatchInfo = RHS;
return true;
diff --git a/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp b/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
index 7a9cf7a4b3205e..292652c0ed21d9 100644
--- a/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
+++ b/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
@@ -174,11 +174,11 @@ bool PISAPreLegalizerCombinerImpl::tryCombineAll(MachineInstr &MI) const {
}
void PISAPreLegalizerCombinerImpl::applyTruncatedLoad(MachineInstr &MI) const {
- auto *LoadMI = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
- auto *MMO = LoadMI->memoperands()[0];
- auto Dst = MI.getOperand(0);
- auto Addr = LoadMI->getOperand(1);
- auto *NewMMO = MI.getMF()->getMachineMemOperand(MMO, MMO->getOffset(),
+ llvm::MachineInstr *LoadMI = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
+ llvm::MachineMemOperand *MMO = LoadMI->memoperands()[0];
+ llvm::MachineOperand Dst = MI.getOperand(0);
+ llvm::MachineOperand Addr = LoadMI->getOperand(1);
+ llvm::MachineMemOperand *NewMMO = MI.getMF()->getMachineMemOperand(MMO, MMO->getOffset(),
MRI.getType(Dst.getReg()));
B.buildLoad(Dst, Addr, *NewMMO);
MI.eraseFromParent();
@@ -190,7 +190,7 @@ bool PISAPreLegalizerCombinerImpl::matchExpandNonPowerOf2LoadStore(
MI.getOpcode() == TargetOpcode::G_STORE);
GLoadStore &LS = cast<GLoadStore>(MI);
- auto Size = LS.getMemSizeInBits().getValue();
+ llvm::TypeSize Size = LS.getMemSizeInBits().getValue();
if (isPowerOf2_32(Size))
return false;
@@ -201,8 +201,8 @@ bool PISAPreLegalizerCombinerImpl::matchExpandNonPowerOf2LoadStore(
// Check if this is a load and only has zext uses that are handled by the
// extended load pattern - if yes, we want to handle it via said pattern.
if (MI.getOpcode() == TargetOpcode::G_LOAD) {
- for (auto &Use : MRI.use_operands(MI.getOperand(0).getReg())) {
- auto *Inst = Use.getParent();
+ for (llvm::MachineOperand &Use : MRI.use_operands(MI.getOperand(0).getReg())) {
+ llvm::MachineInstr *Inst = Use.getParent();
if (Inst->getOpcode() != TargetOpcode::G_ZEXT ||
!matchExtendedLoad(*Inst))
return true;
@@ -219,9 +219,9 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
GLoadStore &LS = cast<GLoadStore>(MI);
- auto PointerReg = LS.getPointerReg();
- auto ValueReg = LS.getOperand(0).getReg();
- auto &MMO = LS.getMMO();
+ llvm::Register PointerReg = LS.getPointerReg();
+ llvm::Register ValueReg = LS.getOperand(0).getReg();
+ llvm::MachineMemOperand &MMO = LS.getMMO();
/// The remaining size in Bits that still has to be loaded/stored
ssize_t Size = LS.getMemSizeInBits().getValue();
@@ -230,14 +230,14 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
Size = (Size + 7) & ~7;
unsigned Offset = 0;
- const auto SizeTy = LLT::integer(Size);
+ const llvm::LLT SizeTy = LLT::integer(Size);
// If the size was changed to the next power of 8 and we are storing a value,
// we need to modify the register's type as well.
// A similar check is needed for loads, but this is done at the end
if (MI.getOpcode() == TargetOpcode::G_STORE &&
SizeTy != MRI.getType(ValueReg)) {
- auto NewValueReg = MRI.createGenericVirtualRegister(SizeTy);
+ llvm::Register NewValueReg = MRI.createGenericVirtualRegister(SizeTy);
B.buildZExt(NewValueReg, ValueReg);
ValueReg = NewValueReg;
}
@@ -245,24 +245,24 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
/// The register holding the loaded value at the end
Register LoadRes;
while (Size > 0) {
- auto OpSize = bit_floor(static_cast<size_t>(Size));
- const auto ShiftAmount = Offset * 8;
+ uint64_t OpSize = bit_floor(static_cast<size_t>(Size));
+ const unsigned ShiftAmount = Offset * 8;
const LLT OpTy = LLT::integer(OpSize);
- auto *NewMMO =
+ llvm::MachineMemOperand *NewMMO =
MI.getMF()->getMachineMemOperand(&MMO, MMO.getOffset() + Offset, OpTy);
/// Stores the (potentially modified) pointer register
- auto AddrReg = PointerReg;
+ llvm::Register AddrReg = PointerReg;
// We might need to change the store offset
if (Offset != 0) {
- auto NewAddr = MRI.cloneVirtualRegister(AddrReg);
+ llvm::Register NewAddr = MRI.cloneVirtualRegister(AddrReg);
// Get the pointer size from the pointer register type
const LLT PtrTy = MRI.getType(AddrReg);
const LLT IntTy = LLT::integer(PtrTy.getSizeInBits());
- auto Const = MRI.createGenericVirtualRegister(IntTy);
+ llvm::Register Const = MRI.createGenericVirtualRegister(IntTy);
B.buildConstant(Const, Offset);
B.buildPtrAdd(NewAddr, AddrReg, Const);
@@ -270,11 +270,11 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
}
if (MI.getOpcode() == TargetOpcode::G_STORE) {
- auto Res = ValueReg;
+ llvm::Register Res = ValueReg;
// If we're not storing from the start, we need to shift our value first
if (Offset != 0) {
- auto ShrRes = MRI.createGenericVirtualRegister(SizeTy);
- auto ShiftConst = MRI.createGenericVirtualRegister(SizeTy);
+ llvm::Register ShrRes = MRI.createGenericVirtualRegister(SizeTy);
+ llvm::Register ShiftConst = MRI.createGenericVirtualRegister(SizeTy);
B.buildConstant(ShiftConst, ShiftAmount);
B.buildLShr(ShrRes, ValueReg, ShiftConst);
Res = ShrRes;
@@ -282,7 +282,7 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
// Next, truncate it to the correct size, if needed
if (SizeTy != OpTy) {
- auto TruncRes = MRI.createGenericVirtualRegister(OpTy);
+ llvm::Register TruncRes = MRI.createGenericVirtualRegister(OpTy);
B.buildTrunc(TruncRes, Res);
Res = TruncRes;
}
@@ -291,22 +291,22 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
} else {
// When loading, do the same thing but basically in reverse
// First, load the value
- auto LoadedValReg = MRI.createGenericVirtualRegister(OpTy);
+ llvm::Register LoadedValReg = MRI.createGenericVirtualRegister(OpTy);
B.buildLoad(LoadedValReg, AddrReg, *NewMMO);
- auto Res = LoadedValReg;
+ llvm::Register Res = LoadedValReg;
// Extend it to the correct size, if needed
if (SizeTy != OpTy) {
- auto ZextRes = MRI.createGenericVirtualRegister(SizeTy);
+ llvm::Register ZextRes = MRI.createGenericVirtualRegister(SizeTy);
B.buildZExt(ZextRes, LoadedValReg);
Res = ZextRes;
}
// If we're not loading the first Bytes, then we need to shift it
if (Offset != 0) {
- auto ShiftRes = MRI.createGenericVirtualRegister(SizeTy);
+ llvm::Register ShiftRes = MRI.createGenericVirtualRegister(SizeTy);
- auto ShiftConst = MRI.createGenericVirtualRegister(SizeTy);
+ llvm::Register ShiftConst = MRI.createGenericVirtualRegister(SizeTy);
B.buildConstant(ShiftConst, ShiftAmount);
B.buildShl(ShiftRes, Res, ShiftConst);
@@ -318,7 +318,7 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
B.buildConstant(LoadRes, 0);
}
- auto NewLoadRes = MRI.createGenericVirtualRegister(SizeTy);
+ llvm::Register NewLoadRes = MRI.createGenericVirtualRegister(SizeTy);
B.buildOr(NewLoadRes, LoadRes, Res);
LoadRes = NewLoadRes;
}
@@ -332,7 +332,7 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
// not a multiple of 8), we need to truncate it to the correct size again,
// in order not to break any following instructions
if (SizeTy != MRI.getType(ValueReg)) {
- auto TruncRes = MRI.createGenericVirtualRegister(MRI.getType(ValueReg));
+ llvm::Register TruncRes = MRI.createGenericVirtualRegister(MRI.getType(ValueReg));
B.buildTrunc(TruncRes, LoadRes);
LoadRes = TruncRes;
}
@@ -387,7 +387,7 @@ bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
MachineInstr *&SizeModificationOp) const {
GLoadStore &StoreInst = cast<GLoadStore>(MI);
- auto ValueReg = StoreInst.getOperand(0).getReg();
+ llvm::Register ValueReg = StoreInst.getOperand(0).getReg();
/// This has the integer size at the beginning, and each individual load
/// decreases the value by its load size. At the end, this must be zero.
@@ -468,7 +468,7 @@ bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
if (PtrAddInst->getOpcode() != TargetOpcode::G_PTR_ADD)
return false;
- auto LoadOffset =
+ std::optional<llvm::APInt> LoadOffset =
getIConstantVRegVal(PtrAddInst->getOperand(2).getReg(), MRI);
if (!LoadOffset.has_value() || LoadOffset.value() != LoadShift / 8)
return false;
@@ -487,14 +487,14 @@ bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
LoadOffset.value().getZExtValue())});
} else if (NextChainInst->getOpcode() == TargetOpcode::G_ZEXTLOAD ||
NextChainInst->getOpcode() == TargetOpcode::G_LOAD) {
- const auto &LoadInst = cast<GLoadStore>(*NextChainInst);
+ const llvm::GLoadStore &LoadInst = cast<GLoadStore>(*NextChainInst);
const LLT LoadTy = LoadInst.getMMO().getType();
if (!LoadTy.isScalar())
return false;
// This is the last load instruction of the chain, so it must match the
// remaining value in ValueSize
- auto LoadSize = LoadTy.getScalarSizeInBits();
+ unsigned LoadSize = LoadTy.getScalarSizeInBits();
if (ValueSize % 8 == 0 && LoadSize != ValueSize)
return false;
@@ -510,7 +510,7 @@ bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
SizeModificationOp->getOpcode() == TargetOpcode::G_TRUNC)
return false;
- auto SizeAfterModificationOp =
+ unsigned SizeAfterModificationOp =
MRI.getType(SizeModificationOp->getOperand(0).getReg())
.getScalarSizeInBits();
@@ -526,7 +526,7 @@ bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
// same G_SEXT(G_LOAD) pattern, causing an infinite loop. When the
// source size is not byte-aligned, the apply inserts in-place
// shl/ashr to align it first, producing a different pattern.
- auto SextSrcSize =
+ unsigned SextSrcSize =
MRI.getType(SizeModificationOp->getOperand(1).getReg())
.getScalarSizeInBits();
if (LoadSize == SextSrcSize && SextSrcSize % 8 == 0)
@@ -556,13 +556,15 @@ void PISAPreLegalizerCombinerImpl::applySimplifyNonPowerOf2LoadStoreChain(
// multiple G_STOREs using the individually loaded values
GLoadStore &StoreInst = cast<GLoadStore>(MI);
- auto PointerReg = StoreInst.getPointerReg();
- auto &MMO = StoreInst.getMMO();
+ llvm::Register PointerReg = StoreInst.getPointerReg();
+ llvm::MachineMemOperand &MMO = StoreInst.getMMO();
unsigned StoreSizeInBytes = StoreInst.getMemSize().getValue();
- for (auto [Index, Pair] : enumerate(Loads)) {
- auto [LoadMI, Offset] = Pair;
+ for (size_t Index = 0; Index < Loads.size(); ++Index) {
+ std::pair<MachineInstr *, unsigned> &Pair = Loads[Index];
+ MachineInstr *LoadMI = Pair.first;
+ unsigned Offset = Pair.second;
Register Value = LoadMI->getOperand(0).getReg();
LLT LoadTy = cast<GLoadStore>(LoadMI)->getMMO().getType();
@@ -581,7 +583,7 @@ void PISAPreLegalizerCombinerImpl::applySimplifyNonPowerOf2LoadStoreChain(
// load was extended, so we need to sext/zext this value before
// storing
if (Index == 0 && SizeModificationOp != nullptr) {
- auto Opcode = SizeModificationOp->getOpcode();
+ unsigned Opcode = SizeModificationOp->getOpcode();
assert(Opcode == TargetOpcode::G_ZEXT ||
Opcode == TargetOpcode::G_SEXT ||
Opcode == TargetOpcode::G_SEXT_INREG);
@@ -599,22 +601,22 @@ void PISAPreLegalizerCombinerImpl::applySimplifyNonPowerOf2LoadStoreChain(
// First, do we have to sign extend this value in-place using
// shifts?
if (OriginalSize % 8 != 0) {
- auto ExtendBy = 8 - (OriginalSize % 8);
+ unsigned ExtendBy = 8 - (OriginalSize % 8);
- auto ConstReg = MRI.createGenericVirtualRegister(LLT::integer(32));
+ llvm::Register ConstReg = MRI.createGenericVirtualRegister(LLT::integer(32));
B.buildConstant(ConstReg, ExtendBy);
if (LoadTy.getSizeInBits() < OriginalSize + ExtendBy) {
LoadTy = LLT::integer(OriginalSize + ExtendBy);
- auto ZExtRes = MRI.createGenericVirtualRegister(LoadTy);
+ llvm::Register ZExtRes = MRI.createGenericVirtualRegister(LoadTy);
B.buildZExt(ZExtRes, Res);
Res = ZExtRes;
}
- auto ShlRes = MRI.cloneVirtualRegister(Res);
+ llvm::Register ShlRes = MRI.cloneVirtualRegister(Res);
B.buildShl(ShlRes, Res, ConstReg);
- auto AShrRes = MRI.cloneVirtualRegister(ShlRes);
+ llvm::Register AShrRes = MRI.cloneVirtualRegister(ShlRes);
B.buildAShr(AShrRes, ShlRes, ConstReg);
Res = AShrRes;
@@ -625,18 +627,18 @@ void PISAPreLegalizerCombinerImpl::applySimplifyNonPowerOf2LoadStoreChain(
// Second, do we still have to SEXT it further?
if (StoreSizeInBits > OriginalSize) {
LoadTy = LLT::integer(StoreSizeInBits);
- auto SextRes = MRI.createGenericVirtualRegister(LoadTy);
+ llvm::Register SextRes = MRI.createGenericVirtualRegister(LoadTy);
B.buildSExt(SextRes, Res);
Res = SextRes;
}
} else {
// G_ZEXT, simple
- auto NewSize = StoreSizeInBytes - Offset;
+ unsigned NewSize = StoreSizeInBytes - Offset;
assert(NewSize > LoadTy.getSizeInBytes());
LoadTy = LLT::integer(NewSize * 8);
- auto ExtRes = MRI.createGenericVirtualRegister(LoadTy);
+ llvm::Register ExtRes = MRI.createGenericVirtualRegister(LoadTy);
B.buildZExt(ExtRes, Res);
Res = ExtRes;
@@ -657,21 +659,21 @@ void PISAPreLegalizerCombinerImpl::applySimplifyNonPowerOf2LoadStoreChain(
B.buildTrunc(Res, Value);
}
- auto *NewMMO = MI.getMF()->getMachineMemOperand(
+ llvm::MachineMemOperand *NewMMO = MI.getMF()->getMachineMemOperand(
&MMO, MMO.getOffset() + Offset, LoadTy);
/// Stores the (potentially modified) pointer register
- auto AddrReg = PointerReg;
+ llvm::Register AddrReg = PointerReg;
// Add the offset to the pointer reg if the offset is not zero
if (Offset != 0) {
- auto NewPointerReg =
+ llvm::Register NewPointerReg =
MRI.createGenericVirtualRegister(MRI.getType(AddrReg));
// Get the pointer size from the pointer register type
const LLT PtrTy = MRI.getType(AddrReg);
const LLT IntTy = LLT::integer(PtrTy.getSizeInBits());
- auto CstReg = MRI.createGenericVirtualRegister(IntTy);
+ llvm::Register CstReg = MRI.createGenericVirtualRegister(IntTy);
B.buildConstant(CstReg, Offset);
B.buildPtrAdd(NewPointerReg, AddrReg, CstReg);
@@ -689,8 +691,8 @@ void PISAPreLegalizerCombinerImpl::applySimplifyNonPowerOf2LoadStoreChain(
// (s24) = G_TRUNC (s32)
// G_STORE (s24), ptr
bool PISAPreLegalizerCombinerImpl::matchTruncatedStore(MachineInstr &MI) const {
- auto &Store = cast<GStore>(MI);
- auto *TruncMI = getDefIgnoringCopies(Store.getValueReg(), MRI);
+ llvm::GStore &Store = cast<GStore>(MI);
+ llvm::MachineInstr *TruncMI = getDefIgnoringCopies(Store.getValueReg(), MRI);
if (!TruncMI)
return false;
if (TruncMI->getOpcode() != TargetOpcode::G_TRUNC)
@@ -699,17 +701,17 @@ bool PISAPreLegalizerCombinerImpl::matchTruncatedStore(MachineInstr &MI) const {
MRI.getType(TruncMI->getOperand(1).getReg()).getSizeInBits()))
return false;
- auto Size = Store.getMemSizeInBits().getValue();
- auto Align = Store.getMMO().getAlign().value();
+ llvm::TypeSize Size = Store.getMemSizeInBits().getValue();
+ uint64_t Align = Store.getMMO().getAlign().value();
if (isPowerOf2_32(Size))
return false;
- auto NextPow2 = NextPowerOf2(Size);
- auto TruncSize = MRI.getType(TruncMI->getOperand(1).getReg()).getSizeInBits();
+ uint64_t NextPow2 = NextPowerOf2(Size);
+ llvm::TypeSize TruncSize = MRI.getType(TruncMI->getOperand(1).getReg()).getSizeInBits();
if (TruncSize != NextPow2)
return false;
- auto Remainder = NextPow2 - Size;
+ uint64_t Remainder = NextPow2 - Size;
if (NextPow2 % Remainder != 0)
return false;
@@ -721,7 +723,7 @@ bool PISAPreLegalizerCombinerImpl::matchTruncatedStore(MachineInstr &MI) const {
if (Align % (Remainder / 8) != 0)
return false;
- auto VecSize = NextPow2 / Remainder;
+ uint64_t VecSize = NextPow2 / Remainder;
if (VecSize > 4)
return false;
@@ -731,27 +733,27 @@ bool PISAPreLegalizerCombinerImpl::matchTruncatedStore(MachineInstr &MI) const {
// Changes the type of a non-power-of-2 store to a vector of smaller elements,
// if it comes from a trunc instruction.
void PISAPreLegalizerCombinerImpl::applyTruncatedStore(MachineInstr &MI) const {
- auto &Store = cast<GStore>(MI);
+ llvm::GStore &Store = cast<GStore>(MI);
- auto *TruncMI = getDefIgnoringCopies(Store.getValueReg(), MRI);
+ llvm::MachineInstr *TruncMI = getDefIgnoringCopies(Store.getValueReg(), MRI);
assert(TruncMI && TruncMI->getOpcode() == TargetOpcode::G_TRUNC);
- auto TruncVal = TruncMI->getOperand(1);
- auto Size = Store.getMemSizeInBits().getValue();
- auto NextPow2 = NextPowerOf2(Size);
- auto Remainder = NextPow2 - Size;
- auto VecSize = NextPow2 / Remainder;
- auto VecSizeSmall = Size / Remainder;
- auto VecType = LLT::fixed_vector(VecSize, LLT::integer(Remainder));
- auto VecTypeSmall = LLT::fixed_vector(VecSizeSmall, LLT::integer(Remainder));
- auto TmpDst = MRI.createGenericVirtualRegister(VecType);
- auto TmpDstSmall = MRI.createGenericVirtualRegister(VecTypeSmall);
+ llvm::MachineOperand TruncVal = TruncMI->getOperand(1);
+ llvm::TypeSize Size = Store.getMemSizeInBits().getValue();
+ uint64_t NextPow2 = NextPowerOf2(Size);
+ uint64_t Remainder = NextPow2 - Size;
+ uint64_t VecSize = NextPow2 / Remainder;
+ uint64_t VecSizeSmall = Size / Remainder;
+ llvm::LLT VecType = LLT::fixed_vector(VecSize, LLT::integer(Remainder));
+ llvm::LLT VecTypeSmall = LLT::fixed_vector(VecSizeSmall, LLT::integer(Remainder));
+ llvm::Register TmpDst = MRI.createGenericVirtualRegister(VecType);
+ llvm::Register TmpDstSmall = MRI.createGenericVirtualRegister(VecTypeSmall);
B.buildBitcast(TmpDst, TruncVal);
B.buildShuffleVector(TmpDstSmall, TmpDst, B.buildUndef(VecType), {0, 1, 2});
- auto Addr = Store.getPointerReg();
- auto &MMO = Store.getMMO();
- auto *NewMMO =
+ llvm::Register Addr = Store.getPointerReg();
+ llvm::MachineMemOperand &MMO = Store.getMMO();
+ llvm::MachineMemOperand *NewMMO =
MI.getMF()->getMachineMemOperand(&MMO, MMO.getOffset(), VecTypeSmall);
B.buildStore(TmpDstSmall, Addr, *NewMMO);
MI.eraseFromParent();
@@ -762,20 +764,20 @@ void PISAPreLegalizerCombinerImpl::applyTruncatedStore(MachineInstr &MI) const {
// This saves us from using second load instruction and few arithmetic
// instructions (shl, and, or) to zero extend.
bool PISAPreLegalizerCombinerImpl::matchExtendedLoad(MachineInstr &MI) const {
- auto *DefMI = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
+ llvm::MachineInstr *DefMI = getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI);
if (!DefMI)
return false;
if (DefMI->getOpcode() != TargetOpcode::G_LOAD)
return false;
GLoad &Load = cast<GLoad>(*DefMI);
- auto Size = Load.getMemSizeInBits().getValue();
- auto Align = Load.getMMO().getAlign().value();
+ llvm::TypeSize Size = Load.getMemSizeInBits().getValue();
+ uint64_t Align = Load.getMMO().getAlign().value();
if (isPowerOf2_32(Size))
return false;
- auto ZextSize = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
+ llvm::TypeSize ZextSize = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
if (!isPowerOf2_32(ZextSize))
return false;
@@ -793,21 +795,21 @@ void PISAPreLegalizerCombinerImpl::applyExtendedLoad(MachineInstr &MI) const {
cast<GLoad>(getDefIgnoringCopies(MI.getOperand(1).getReg(), MRI));
assert(LoadMI);
- auto DestSize = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
- auto LoadSize = LoadMI->getMemSizeInBits().getValue();
+ llvm::TypeSize DestSize = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
+ llvm::TypeSize LoadSize = LoadMI->getMemSizeInBits().getValue();
APInt Mask = APInt::getLowBitsSet(DestSize, LoadSize);
- auto NewLoadSize = LLT::integer(DestSize);
- auto LoadDst = MRI.createGenericVirtualRegister(NewLoadSize);
+ llvm::LLT NewLoadSize = LLT::integer(DestSize);
+ llvm::Register LoadDst = MRI.createGenericVirtualRegister(NewLoadSize);
- auto Addr = LoadMI->getOperand(1);
- auto &MMO = LoadMI->getMMO();
- auto *NewMMO = MI.getMF()->getMachineMemOperand(&MMO, MMO.getOffset(),
+ llvm::MachineOperand Addr = LoadMI->getOperand(1);
+ llvm::MachineMemOperand &MMO = LoadMI->getMMO();
+ llvm::MachineMemOperand *NewMMO = MI.getMF()->getMachineMemOperand(&MMO, MMO.getOffset(),
MRI.getType(LoadDst));
B.buildLoad(LoadDst, Addr, *NewMMO);
- auto MaskReg = MRI.createGenericVirtualRegister(LLT::integer(DestSize));
+ llvm::Register MaskReg = MRI.createGenericVirtualRegister(LLT::integer(DestSize));
B.buildConstant(MaskReg, Mask.getZExtValue());
B.buildAnd(MI.getOperand(0).getReg(), LoadDst, MaskReg);
MI.eraseFromParent();
@@ -820,19 +822,20 @@ void PISAPreLegalizerCombinerImpl::applyExtendedLoad(MachineInstr &MI) const {
// => %lo = G_EXTRACT_VECTOR_ELT <2 x 16> %1, 0
// => %hi = G_EXTRACT_VECTOR_ELT <2 x 16> %1, 1
bool PISAPreLegalizerCombinerImpl::matchTruncatedShift(MachineInstr &MI) const {
- auto &TruncMI = MI;
- auto &LshMI = *getDefIgnoringCopies(TruncMI.getOperand(1).getReg(), MRI);
+ llvm::MachineInstr &TruncMI = MI;
+ llvm::MachineInstr &LshMI = *getDefIgnoringCopies(TruncMI.getOperand(1).getReg(), MRI);
if (LshMI.getOpcode() == TargetOpcode::G_LSHR) {
- auto WideTy = MRI.getType(TruncMI.getOperand(1).getReg());
- auto NarrowTy = MRI.getType(TruncMI.getOperand(0).getReg());
- auto WideSize = WideTy.getScalarSizeInBits();
- auto NarrowSize = NarrowTy.getScalarSizeInBits();
+ llvm::LLT WideTy = MRI.getType(TruncMI.getOperand(1).getReg());
+ llvm::LLT NarrowTy = MRI.getType(TruncMI.getOperand(0).getReg());
+ unsigned WideSize = WideTy.getScalarSizeInBits();
+ unsigned NarrowSize = NarrowTy.getScalarSizeInBits();
if (WideTy.isScalar() && NarrowTy.isScalar()) {
if (isPowerOf2_32(WideSize) && isPowerOf2_32(NarrowSize)) {
- auto ShiftReg = LshMI.getOperand(2).getReg();
- if (auto Shift = getIConstantVRegValWithLookThrough(ShiftReg, MRI)) {
+ llvm::Register ShiftReg = LshMI.getOperand(2).getReg();
+ if (std::optional<llvm::ValueAndVReg> Shift =
+ getIConstantVRegValWithLookThrough(ShiftReg, MRI)) {
if (Shift.has_value()) {
- auto ShiftVal = Shift->Value.getZExtValue();
+ uint64_t ShiftVal = Shift->Value.getZExtValue();
if ((NarrowSize + ShiftVal) == WideSize)
return true;
}
@@ -843,34 +846,34 @@ bool PISAPreLegalizerCombinerImpl::matchTruncatedShift(MachineInstr &MI) const {
return false;
}
void PISAPreLegalizerCombinerImpl::applyTruncatedShift(MachineInstr &MI) const {
- auto &TruncMI = MI;
- auto &LshMI = *getDefIgnoringCopies(TruncMI.getOperand(1).getReg(), MRI);
- auto WideTy = MRI.getType(TruncMI.getOperand(1).getReg());
- auto NarrowTy = MRI.getType(TruncMI.getOperand(0).getReg());
- auto WideSize = WideTy.getScalarSizeInBits();
- auto NarrowSize = NarrowTy.getScalarSizeInBits();
+ llvm::MachineInstr &TruncMI = MI;
+ llvm::MachineInstr &LshMI = *getDefIgnoringCopies(TruncMI.getOperand(1).getReg(), MRI);
+ llvm::LLT WideTy = MRI.getType(TruncMI.getOperand(1).getReg());
+ llvm::LLT NarrowTy = MRI.getType(TruncMI.getOperand(0).getReg());
+ unsigned WideSize = WideTy.getScalarSizeInBits();
+ unsigned NarrowSize = NarrowTy.getScalarSizeInBits();
- auto VecLen = WideSize / NarrowSize;
- auto VecTy = LLT::fixed_vector(VecLen, NarrowTy);
- auto VecReg = MRI.createGenericVirtualRegister(VecTy);
+ unsigned VecLen = WideSize / NarrowSize;
+ llvm::LLT VecTy = LLT::fixed_vector(VecLen, NarrowTy);
+ llvm::Register VecReg = MRI.createGenericVirtualRegister(VecTy);
// trunc to extract high part
- auto BitCast = B.buildBitcast(VecReg, LshMI.getOperand(1));
+ llvm::MachineInstrBuilder BitCast = B.buildBitcast(VecReg, LshMI.getOperand(1));
B.buildExtractVectorElementConstant(TruncMI.getOperand(0), VecReg,
VecLen - 1);
// find trunc to extract low part (if any)
- auto ShiftSrc = LshMI.getOperand(1).getReg();
- for (auto &UseMI : MRI.use_instructions(ShiftSrc)) {
+ llvm::Register ShiftSrc = LshMI.getOperand(1).getReg();
+ for (llvm::MachineInstr &UseMI : MRI.use_instructions(ShiftSrc)) {
if (UseMI.getOpcode() == TargetOpcode::G_TRUNC) {
- auto DstSize = MRI.getType(UseMI.getOperand(0).getReg()).getSizeInBits();
+ llvm::TypeSize DstSize = MRI.getType(UseMI.getOperand(0).getReg()).getSizeInBits();
if (DstSize == NarrowSize) {
assert(MDT && "machine dominator pass must be available");
if (!MDT->dominates(&UseMI, &TruncMI) &&
!MDT->dominates(&TruncMI, &UseMI))
continue; // can not optimize out low part
MachineIRBuilder MIB(UseMI);
- auto Lo = MIB.buildExtractVectorElementConstant(UseMI.getOperand(0),
+ llvm::MachineInstrBuilder Lo = MIB.buildExtractVectorElementConstant(UseMI.getOperand(0),
VecReg, 0);
if (MDT->dominates(&UseMI, &TruncMI))
BitCast->moveBefore(Lo);
@@ -888,20 +891,20 @@ void PISAPreLegalizerCombinerImpl::applyTruncatedShift(MachineInstr &MI) const {
// => D(32) = COPY ARG(32)
bool PISAPreLegalizerCombinerImpl::matchRedundantMovesPre(
MachineInstr &MI) const {
- auto &BitcastMI = MI;
- auto DstReg = BitcastMI.getOperand(0).getReg();
- auto SrcReg = BitcastMI.getOperand(1).getReg();
+ llvm::MachineInstr &BitcastMI = MI;
+ llvm::Register DstReg = BitcastMI.getOperand(0).getReg();
+ llvm::Register SrcReg = BitcastMI.getOperand(1).getReg();
if (MRI.getType(DstReg).isVector() || !MRI.getType(SrcReg).isVector())
return false;
- auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ llvm::MachineInstr &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
if (BuildVecMI.getOpcode() != TargetOpcode::G_BUILD_VECTOR)
return false;
unsigned Mask = 0;
Register VecReg = 0;
for (unsigned I = 1; I < BuildVecMI.getNumOperands(); I++) {
- auto &ExtractMI =
+ llvm::MachineInstr &ExtractMI =
*getDefIgnoringCopies(BuildVecMI.getOperand(I).getReg(), MRI);
if (ExtractMI.getOpcode() != TargetOpcode::G_EXTRACT_VECTOR_ELT)
return false;
@@ -911,8 +914,8 @@ bool PISAPreLegalizerCombinerImpl::matchRedundantMovesPre(
// must extract indices from the same vector
return false;
}
- auto IndexReg = ExtractMI.getOperand(2).getReg();
- auto Index = getIConstantVRegValWithLookThrough(IndexReg, MRI);
+ llvm::Register IndexReg = ExtractMI.getOperand(2).getReg();
+ std::optional<llvm::ValueAndVReg> Index = getIConstantVRegValWithLookThrough(IndexReg, MRI);
if (!Index.has_value())
return false;
// indices must be in the same order
@@ -933,13 +936,13 @@ bool PISAPreLegalizerCombinerImpl::matchRedundantMovesPre(
}
void PISAPreLegalizerCombinerImpl::applyRedundantMovesPre(
MachineInstr &MI) const {
- auto &BitcastMI = MI;
- auto DstReg = BitcastMI.getOperand(0).getReg();
- auto SrcReg = BitcastMI.getOperand(1).getReg();
- auto &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
- auto &ExtractMI =
+ llvm::MachineInstr &BitcastMI = MI;
+ llvm::Register DstReg = BitcastMI.getOperand(0).getReg();
+ llvm::Register SrcReg = BitcastMI.getOperand(1).getReg();
+ llvm::MachineInstr &BuildVecMI = *getDefIgnoringCopies(SrcReg, MRI);
+ llvm::MachineInstr &ExtractMI =
*getDefIgnoringCopies(BuildVecMI.getOperand(1).getReg(), MRI);
- auto VecReg = ExtractMI.getOperand(1).getReg();
+ llvm::Register VecReg = ExtractMI.getOperand(1).getReg();
if (MRI.getType(DstReg) == MRI.getType(VecReg))
B.buildCopy(DstReg, VecReg);
@@ -956,7 +959,7 @@ static MachineInstr *getReciprocalDivisor(MachineInstr *MI,
// Check for 1.0 / x pattern. Avoid m_GFCstOrSplat because
// getFConstantVRegValWithLookThrough loses bfloat16 semantics.
Register Dividend = MI->getOperand(1).getReg();
- auto *DivDefMI = getDefIgnoringCopies(Dividend, MRI);
+ llvm::MachineInstr *DivDefMI = getDefIgnoringCopies(Dividend, MRI);
if (!DivDefMI || DivDefMI->getOpcode() != TargetOpcode::G_FCONSTANT)
return nullptr;
if (!DivDefMI->getOperand(1).getFPImm()->isExactlyValue(1.0))
@@ -964,7 +967,7 @@ static MachineInstr *getReciprocalDivisor(MachineInstr *MI,
return getDefIgnoringCopies(MI->getOperand(2).getReg(), MRI);
}
- auto *GI = dyn_cast<GIntrinsic>(MI);
+ llvm::GIntrinsic *GI = dyn_cast<GIntrinsic>(MI);
if (GI && GI->is(Intrinsic::pisa_frcp))
return getDefIgnoringCopies(MI->getOperand(2).getReg(), MRI);
@@ -980,12 +983,14 @@ static MachineInstr *getReciprocalDivisor(MachineInstr *MI,
bool PISAPreLegalizerCombinerImpl::matchRcpSqrtToRsqrt(
MachineInstr &MI,
std::function<void(MachineIRBuilder &)> &MatchInfo) const {
- auto GetFrcpSrc = [=](MachineInstr *MI) -> MachineInstr * {
+ std::function<MachineInstr *(MachineInstr *)> GetFrcpSrc =
+ [=](MachineInstr *MI) -> MachineInstr * {
if (!MI || !MI->getFlag(MachineInstr::FmContract))
return nullptr;
return getReciprocalDivisor(MI, MRI);
};
- auto GetSqrtSrc = [=](MachineInstr *MI) -> MachineInstr * {
+ std::function<MachineInstr *(MachineInstr *)> GetSqrtSrc =
+ [=](MachineInstr *MI) -> MachineInstr * {
if (!MI || !MI->getFlag(MachineInstr::FmContract))
return nullptr;
if (MI->getOpcode() == TargetOpcode::G_FSQRT)
@@ -998,7 +1003,7 @@ bool PISAPreLegalizerCombinerImpl::matchRcpSqrtToRsqrt(
if (Divisor) {
// result of sqrt cannot be snan, so Intrinsic::fabs is replaced with
// Intrinsic::pisa_fabs. We won't get G_FABS here.
- auto *GI = dyn_cast<GIntrinsic>(Divisor);
+ llvm::GIntrinsic *GI = dyn_cast<GIntrinsic>(Divisor);
if (GI && GI->is(Intrinsic::pisa_fabs)) {
WrapInFAbs = true;
Divisor = getDefIgnoringCopies(GI->getOperand(2).getReg(), MRI);
@@ -1023,7 +1028,7 @@ bool PISAPreLegalizerCombinerImpl::matchRcpSqrtToRsqrt(
return;
}
// 1/|sqrt(x)| == |frsqrt(x)|
- auto Frsqrt = B.buildIntrinsic(Intrinsic::pisa_frsqrt, {DstTy})
+ llvm::MachineInstrBuilder Frsqrt = B.buildIntrinsic(Intrinsic::pisa_frsqrt, {DstTy})
.addUse(Src)
.setMIFlags(MI.getFlags());
B.buildIntrinsic(Intrinsic::pisa_fabs, {MI.getOperand(0)})
@@ -1045,10 +1050,10 @@ bool PISAPreLegalizerCombinerImpl::matchSubFloorToFrc(
if (IsPlainFSub) {
XReg = MI.getOperand(1).getReg();
FloorReg = MI.getOperand(2).getReg();
- } else if (auto *GI = dyn_cast<GIntrinsic>(&MI);
+ } else if (llvm::GIntrinsic *GI = dyn_cast<GIntrinsic>(&MI);
GI && GI->is(Intrinsic::pisa_fsub)) {
unsigned NumOps = MI.getNumOperands();
- auto Round = static_cast<RoundingMode>(MI.getOperand(NumOps - 2).getImm());
+ llvm::RoundingMode Round = static_cast<RoundingMode>(MI.getOperand(NumOps - 2).getImm());
int64_t Sat = MI.getOperand(NumOps - 1).getImm();
if (Round != RoundingMode::TowardZero || Sat)
return false;
@@ -1058,7 +1063,7 @@ bool PISAPreLegalizerCombinerImpl::matchSubFloorToFrc(
return false;
}
- auto BitWidth = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
+ llvm::TypeSize BitWidth = MRI.getType(MI.getOperand(0).getReg()).getSizeInBits();
if (BitWidth != 32)
return false;
@@ -1107,18 +1112,18 @@ bool PISAPreLegalizerCombinerImpl::matchLaneIdLeftShiftChain(
return false;
if (!mi_match(ZExt, MRI, m_GZExt(m_GShl(m_MInstr(Intr), m_ICst(ShiftImm2)))))
return false;
- auto *LaneId = dyn_cast<GIntrinsic>(Intr);
+ llvm::GIntrinsic *LaneId = dyn_cast<GIntrinsic>(Intr);
if (!LaneId || LaneId->getIntrinsicID() != Intrinsic::pisa_lane_id)
return false;
- auto LaneIdTy = MRI.getType(LaneId->getOperand(0).getReg());
+ llvm::LLT LaneIdTy = MRI.getType(LaneId->getOperand(0).getReg());
constexpr int MaxNumBitsInLaneId = 5;
if ((ShiftImm1 + ShiftImm2) >=
(static_cast<int>(LaneIdTy.getSizeInBits()) - MaxNumBitsInLaneId - 1))
return false;
MatchInfo = [=, &MI](MachineIRBuilder &B) {
- auto NewShlReg = MRI.createGenericVirtualRegister(LaneIdTy);
+ llvm::Register NewShlReg = MRI.createGenericVirtualRegister(LaneIdTy);
B.buildShl(NewShlReg, LaneId->getOperand(0),
B.buildConstant(LaneIdTy, ShiftImm1 + ShiftImm2), MI.getFlags());
B.buildZExt(MI.getOperand(0), NewShlReg, ZExt->getFlags());
@@ -1134,8 +1139,8 @@ bool PISAPreLegalizerCombinerImpl::matchZExtAndToAndZExt(
std::function<void(MachineIRBuilder &)> &MatchInfo) const {
Register ZExtReg = MI.getOperand(1).getReg();
Register MaskReg = MI.getOperand(2).getReg();
- auto *ZExtMI = getDefIgnoringCopies(ZExtReg, MRI);
- auto Mask = getIConstantVRegValWithLookThrough(MaskReg, MRI);
+ llvm::MachineInstr *ZExtMI = getDefIgnoringCopies(ZExtReg, MRI);
+ std::optional<llvm::ValueAndVReg> Mask = getIConstantVRegValWithLookThrough(MaskReg, MRI);
if ((!ZExtMI || ZExtMI->getOpcode() != TargetOpcode::G_ZEXT ||
!Mask.has_value())) {
@@ -1159,8 +1164,8 @@ bool PISAPreLegalizerCombinerImpl::matchZExtAndToAndZExt(
APInt NarrowMask = Mask->Value.zextOrTrunc(SrcTy.getSizeInBits());
unsigned ZExtFlags = ZExtMI->getFlags();
MatchInfo = [=](MachineIRBuilder &B) {
- auto NarrowMaskReg = B.buildConstant(SrcTy, NarrowMask);
- auto NarrowAnd = B.buildAnd(SrcTy, SrcReg, NarrowMaskReg);
+ llvm::MachineInstrBuilder NarrowMaskReg = B.buildConstant(SrcTy, NarrowMask);
+ llvm::MachineInstrBuilder NarrowAnd = B.buildAnd(SrcTy, SrcReg, NarrowMaskReg);
B.buildZExt(DstReg, NarrowAnd, ZExtFlags);
};
return true;
@@ -1179,63 +1184,63 @@ bool PISAPreLegalizerCombinerImpl::matchZExtAndToAndZExt(
// - bits of s32 (typeof(BaseReg)) written to by above - return value
static uint64_t getCoveredBits(Register BaseReg, Register &SrcVecReg,
uint64_t *SrcVecIdx, MachineRegisterInfo &MRI) {
- auto &SrcMI = *getDefIgnoringCopies(BaseReg, MRI);
+ llvm::MachineInstr &SrcMI = *getDefIgnoringCopies(BaseReg, MRI);
switch (SrcMI.getOpcode()) {
case TargetOpcode::G_EXTRACT_VECTOR_ELT: {
- auto VecReg = SrcMI.getOperand(1).getReg();
+ llvm::Register VecReg = SrcMI.getOperand(1).getReg();
if (MRI.getType(VecReg).getNumElements() > 64)
return 0; // not supported
- auto IdxReg = SrcMI.getOperand(2).getReg();
+ llvm::Register IdxReg = SrcMI.getOperand(2).getReg();
if (SrcVecReg && (SrcVecReg != VecReg))
return 0; // extracting from different source vector ?
SrcVecReg = VecReg;
- auto Index = getIConstantVRegValWithLookThrough(IdxReg, MRI);
+ std::optional<llvm::ValueAndVReg> Index = getIConstantVRegValWithLookThrough(IdxReg, MRI);
if (!Index.has_value())
return 0; // not a constant
*SrcVecIdx |= (1ull << Index->Value.getZExtValue());
return ~0;
} break;
case TargetOpcode::G_ZEXT: {
- auto SrcReg = SrcMI.getOperand(1).getReg();
- auto &ExtractMI = *getDefIgnoringCopies(SrcReg, MRI);
+ llvm::Register SrcReg = SrcMI.getOperand(1).getReg();
+ llvm::MachineInstr &ExtractMI = *getDefIgnoringCopies(SrcReg, MRI);
if (ExtractMI.getOpcode() != TargetOpcode::G_EXTRACT_VECTOR_ELT)
return 0;
if (getCoveredBits(SrcReg, SrcVecReg, SrcVecIdx, MRI))
return (1ull << MRI.getType(SrcReg).getScalarSizeInBits()) - 1;
} break;
case TargetOpcode::G_SHL: {
- auto SrcReg = SrcMI.getOperand(1).getReg();
- auto ShiftReg = SrcMI.getOperand(2).getReg();
- auto &ExtMI = *getDefIgnoringCopies(SrcReg, MRI);
+ llvm::Register SrcReg = SrcMI.getOperand(1).getReg();
+ llvm::Register ShiftReg = SrcMI.getOperand(2).getReg();
+ llvm::MachineInstr &ExtMI = *getDefIgnoringCopies(SrcReg, MRI);
if (ExtMI.getOpcode() != TargetOpcode::G_ZEXT)
return 0;
- auto Shift = getIConstantVRegValWithLookThrough(ShiftReg, MRI);
+ std::optional<llvm::ValueAndVReg> Shift = getIConstantVRegValWithLookThrough(ShiftReg, MRI);
if (!Shift.has_value())
return 0; // not a constant
- auto ShiftValue = Shift->Value.getZExtValue();
+ uint64_t ShiftValue = Shift->Value.getZExtValue();
// make sure we are not swapping bits
- auto OldSrcVecIdx = *SrcVecIdx;
- auto Bits = getCoveredBits(SrcReg, SrcVecReg, SrcVecIdx, MRI);
- auto BitSet = llvm::countr_zero(*SrcVecIdx & ~OldSrcVecIdx);
- auto Offset = (BitSet * MRI.getType(SrcVecReg).getScalarSizeInBits()) %
+ uint64_t OldSrcVecIdx = *SrcVecIdx;
+ uint64_t Bits = getCoveredBits(SrcReg, SrcVecReg, SrcVecIdx, MRI);
+ int BitSet = llvm::countr_zero(*SrcVecIdx & ~OldSrcVecIdx);
+ uint64_t Offset = (BitSet * MRI.getType(SrcVecReg).getScalarSizeInBits()) %
MRI.getType(SrcReg).getSizeInBits();
if (ShiftValue != Offset)
return 0; // will not insert at proper offset
return Bits << ShiftValue;
} break;
case TargetOpcode::G_OR: {
- auto LHSReg = SrcMI.getOperand(1).getReg();
- auto RHSReg = SrcMI.getOperand(2).getReg();
- auto LHSOp = getDefIgnoringCopies(LHSReg, MRI)->getOpcode();
- auto RHSOp = getDefIgnoringCopies(RHSReg, MRI)->getOpcode();
+ llvm::Register LHSReg = SrcMI.getOperand(1).getReg();
+ llvm::Register RHSReg = SrcMI.getOperand(2).getReg();
+ unsigned LHSOp = getDefIgnoringCopies(LHSReg, MRI)->getOpcode();
+ unsigned RHSOp = getDefIgnoringCopies(RHSReg, MRI)->getOpcode();
if ((LHSOp != TargetOpcode::G_SHL) && (LHSOp != TargetOpcode::G_ZEXT) &&
(LHSOp != TargetOpcode::G_OR))
return 0;
if ((RHSOp != TargetOpcode::G_SHL) && (RHSOp != TargetOpcode::G_ZEXT) &&
(RHSOp != TargetOpcode::G_OR))
return 0;
- auto LHSBits = getCoveredBits(LHSReg, SrcVecReg, SrcVecIdx, MRI);
- auto RHSBits = getCoveredBits(RHSReg, SrcVecReg, SrcVecIdx, MRI);
+ uint64_t LHSBits = getCoveredBits(LHSReg, SrcVecReg, SrcVecIdx, MRI);
+ uint64_t RHSBits = getCoveredBits(RHSReg, SrcVecReg, SrcVecIdx, MRI);
if (LHSBits && RHSBits)
return LHSBits | RHSBits;
} break;
@@ -1247,28 +1252,28 @@ static uint64_t getCoveredBits(Register BaseReg, Register &SrcVecReg,
bool PISAPreLegalizerCombinerImpl::matchExtractInsertToBitcast(
MachineInstr &MI, Register &SaveReg) const {
- auto &BuildVectorMI = MI;
- auto DstReg = BuildVectorMI.getOperand(0).getReg();
- auto BuildVectorTy = MRI.getType(DstReg);
- auto NumElts = BuildVectorTy.getNumElements();
+ llvm::MachineInstr &BuildVectorMI = MI;
+ llvm::Register DstReg = BuildVectorMI.getOperand(0).getReg();
+ llvm::LLT BuildVectorTy = MRI.getType(DstReg);
+ uint16_t NumElts = BuildVectorTy.getNumElements();
Register SrcVecReg; // G_EXTRACT_VECTOR_ELT
uint64_t SrcVecIdx = 0; // index of G_EXTRACT_VECTOR_ELT
for (unsigned I = 0; I < NumElts; I++) {
- auto BuildVectorEltReg = BuildVectorMI.getOperand(I + 1).getReg();
- auto Bits = getCoveredBits(BuildVectorEltReg, SrcVecReg, &SrcVecIdx, MRI);
+ llvm::Register BuildVectorEltReg = BuildVectorMI.getOperand(I + 1).getReg();
+ uint64_t Bits = getCoveredBits(BuildVectorEltReg, SrcVecReg, &SrcVecIdx, MRI);
if (!Bits)
return false; // did not match
if (BuildVectorTy.getSizeInBits() != MRI.getType(SrcVecReg).getSizeInBits())
return false;
if (!((1ull << I) & SrcVecIdx))
return false; // indices are not in ascending order
- auto EltSize = BuildVectorTy.getScalarSizeInBits();
+ unsigned EltSize = BuildVectorTy.getScalarSizeInBits();
uint64_t Mask = (EltSize == 64) ? (uint64_t)-1ll : (1ull << EltSize) - 1;
if (Mask != Bits)
return false; // did not cover full element
}
- auto SrcVecTy = MRI.getType(SrcVecReg);
+ llvm::LLT SrcVecTy = MRI.getType(SrcVecReg);
uint64_t Mask = (1ull << SrcVecTy.getNumElements()) - 1;
if (Mask != SrcVecIdx)
return false; // did not extract all indices
@@ -1278,7 +1283,7 @@ bool PISAPreLegalizerCombinerImpl::matchExtractInsertToBitcast(
}
void PISAPreLegalizerCombinerImpl::applyExtractInsertToBitcast(
MachineInstr &MI, Register SrcVecReg) const {
- auto DstReg = MI.getOperand(0).getReg();
+ llvm::Register DstReg = MI.getOperand(0).getReg();
if (MRI.getType(DstReg) == MRI.getType(SrcVecReg))
B.buildCopy(DstReg, SrcVecReg);
else
@@ -1298,37 +1303,37 @@ void PISAPreLegalizerCombinerImpl::applyExtractInsertToBitcast(
// => %19:_(<4 x s8>) = G_BITCAST %1:_(s32)
bool PISAPreLegalizerCombinerImpl::matchExtractBuildVectorToBitcast(
MachineInstr &MI, Register &SaveReg) const {
- auto &BuildMI = MI;
- auto DstTy = MRI.getType(BuildMI.getOperand(0).getReg());
+ llvm::MachineInstr &BuildMI = MI;
+ llvm::LLT DstTy = MRI.getType(BuildMI.getOperand(0).getReg());
Register CastSrcReg; // G_BITCAST %1
for (unsigned I = 1; I < BuildMI.getNumOperands(); I++) {
- auto EltReg = BuildMI.getOperand(I).getReg();
- auto &ExtMI = *getDefIgnoringCopies(EltReg, MRI);
+ llvm::Register EltReg = BuildMI.getOperand(I).getReg();
+ llvm::MachineInstr &ExtMI = *getDefIgnoringCopies(EltReg, MRI);
if (ExtMI.getOpcode() == TargetOpcode::G_EXTRACT_VECTOR_ELT) {
- auto NumReg = ExtMI.getOperand(2).getReg();
- auto NumValue = getIConstantVRegValWithLookThrough(NumReg, MRI);
+ llvm::Register NumReg = ExtMI.getOperand(2).getReg();
+ std::optional<llvm::ValueAndVReg> NumValue = getIConstantVRegValWithLookThrough(NumReg, MRI);
if (!NumValue.has_value() || (NumValue->Value != (I - 1)))
return false;
- auto &CastMI = *getDefIgnoringCopies(ExtMI.getOperand(1).getReg(), MRI);
+ llvm::MachineInstr &CastMI = *getDefIgnoringCopies(ExtMI.getOperand(1).getReg(), MRI);
if (CastMI.getOpcode() != TargetOpcode::G_BITCAST)
return false;
- auto CastReg = CastMI.getOperand(1).getReg();
+ llvm::Register CastReg = CastMI.getOperand(1).getReg();
if (CastSrcReg && (CastSrcReg != CastReg))
return false;
if (DstTy.getSizeInBits() != MRI.getType(CastReg).getSizeInBits())
return false;
CastSrcReg = CastReg;
} else if (ExtMI.getOpcode() == TargetOpcode::G_TRUNC) {
- auto &ShiftMI = *getDefIgnoringCopies(ExtMI.getOperand(1).getReg(), MRI);
+ llvm::MachineInstr &ShiftMI = *getDefIgnoringCopies(ExtMI.getOperand(1).getReg(), MRI);
if (ShiftMI.getOpcode() != TargetOpcode::G_LSHR)
return false;
- auto ShiftValue = getIConstantVRegValWithLookThrough(
+ std::optional<llvm::ValueAndVReg> ShiftValue = getIConstantVRegValWithLookThrough(
ShiftMI.getOperand(2).getReg(), MRI);
if (!ShiftValue.has_value() ||
(ShiftValue->Value != ((I - 1) * DstTy.getScalarSizeInBits())))
return false;
- auto ShiftReg = ShiftMI.getOperand(1).getReg();
+ llvm::Register ShiftReg = ShiftMI.getOperand(1).getReg();
if (CastSrcReg && (CastSrcReg != ShiftReg))
return false;
CastSrcReg = ShiftReg;
@@ -1340,7 +1345,7 @@ bool PISAPreLegalizerCombinerImpl::matchExtractBuildVectorToBitcast(
}
void PISAPreLegalizerCombinerImpl::applyExtractBuildVectorToBitcast(
MachineInstr &MI, Register CastSrcReg) const {
- auto DstReg = MI.getOperand(0).getReg();
+ llvm::Register DstReg = MI.getOperand(0).getReg();
B.buildBitcast(DstReg, CastSrcReg);
MI.eraseFromParent();
}
@@ -1380,11 +1385,11 @@ bool PISAPreLegalizerCombinerImpl::matchReducePredicates(
MachineInstr &MI,
std::function<void(MachineIRBuilder &)> &MatchInfo) const {
- auto DstReg = MI.getOperand(0).getReg();
+ llvm::Register DstReg = MI.getOperand(0).getReg();
if (!MRI.hasOneUse(DstReg))
return false;
- auto DstTy = MRI.getType(DstReg);
+ llvm::LLT DstTy = MRI.getType(DstReg);
if (!DstTy.isScalar() || (DstTy.getSizeInBits() == 32))
return false;
@@ -1397,7 +1402,7 @@ bool PISAPreLegalizerCombinerImpl::matchReducePredicates(
MachineInstr *VecCmpMI = nullptr;
APInt Mask;
- auto ReductionOpcode = MI.getOpcode();
+ unsigned ReductionOpcode = MI.getOpcode();
std::function<bool(MachineInstr *)> Match = [&](MachineInstr *MI) {
if (!MI)
@@ -1429,7 +1434,7 @@ bool PISAPreLegalizerCombinerImpl::matchReducePredicates(
m_GFCmp(m_Pred(), m_Reg(), m_Reg())))) {
if (VecCmpMI)
return VecCmpMI == MI;
- auto VecType = MRI.getType(MI->getOperand(0).getReg());
+ llvm::LLT VecType = MRI.getType(MI->getOperand(0).getReg());
if (!VecType.isFixedVector())
return false;
// Found cmp producing vector of predicates.
@@ -1448,7 +1453,7 @@ bool PISAPreLegalizerCombinerImpl::matchReducePredicates(
const LLT S32 = LLT::integer(32);
// Build reduction in s32.
- auto ExtendedVector = MRI.createGenericVirtualRegister(
+ llvm::Register ExtendedVector = MRI.createGenericVirtualRegister(
LLT::fixed_vector(Mask.getBitWidth(), S32));
B.buildSExt(ExtendedVector, VecCmpMI->getOperand(0).getReg());
@@ -1457,8 +1462,8 @@ bool PISAPreLegalizerCombinerImpl::matchReducePredicates(
B.buildConstant(S32, 0));
for (unsigned I = 1; I < Mask.getBitWidth(); ++I) {
- auto SecondSrc = MRI.createGenericVirtualRegister(S32);
- auto Dst = MRI.createGenericVirtualRegister(S32);
+ llvm::Register SecondSrc = MRI.createGenericVirtualRegister(S32);
+ llvm::Register Dst = MRI.createGenericVirtualRegister(S32);
B.buildExtractVectorElement(SecondSrc, ExtendedVector,
B.buildConstant(S32, I));
B.buildInstr(ReductionOpcode, {Dst}, {Reduction, SecondSrc});
@@ -1466,10 +1471,10 @@ bool PISAPreLegalizerCombinerImpl::matchReducePredicates(
}
// Replace next use if possible.
- auto UseMI = MRI.use_instr_begin(DstReg);
+ llvm::MachineRegisterInfo::use_instr_iterator UseMI = MRI.use_instr_begin(DstReg);
switch (UseMI->getOpcode()) {
case TargetOpcode::G_ICMP: {
- auto Pred = (CmpInst::Predicate)UseMI->getOperand(1).getPredicate();
+ llvm::CmpInst::Predicate Pred = (CmpInst::Predicate)UseMI->getOperand(1).getPredicate();
if (mi_match(UseMI->getOperand(3).getReg(), MRI, m_SpecificICst(-1)))
Pred = CmpInst::getInversePredicate(Pred);
B.buildICmp(Pred, UseMI->getOperand(0).getReg(), Reduction,
@@ -1518,9 +1523,9 @@ bool PISAPreLegalizerCombinerImpl::matchCmpInt1(
if (!PrevMI)
return false;
- auto DstReg = ICmpMI.getOperand(0).getReg();
+ llvm::Register DstReg = ICmpMI.getOperand(0).getReg();
MatchInfo = [DstReg, PrevMI, IsTrue, this](MachineIRBuilder &B) {
- auto PrevDstReg = PrevMI->getOperand(0).getReg();
+ llvm::Register PrevDstReg = PrevMI->getOperand(0).getReg();
if (IsTrue) {
if (MRI.hasOneUse(PrevDstReg))
@@ -1578,7 +1583,7 @@ bool PISAPreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
MachineFunctionProperties::Property::FailedISel))
return false;
- auto &TPC = getAnalysis<TargetPassConfig>();
+ llvm::TargetPassConfig &TPC = getAnalysis<TargetPassConfig>();
const Function &F = MF.getFunction();
bool EnableOpt =
MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
@@ -1592,7 +1597,7 @@ bool PISAPreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
// Enable CSE.
GISelCSEAnalysisWrapper &Wrapper =
getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
- auto *CSEInfo = &Wrapper.get(TPC.getCSEConfig());
+ llvm::GISelCSEInfo *CSEInfo = &Wrapper.get(TPC.getCSEConfig());
const PISASubtarget &STI = MF.getSubtarget<PISASubtarget>();
PISAPreLegalizerCombinerImpl Impl(MF, CInfo, *KB, CSEInfo, RuleConfig, STI,
@@ -1629,8 +1634,10 @@ bool PISAPreLegalizerCombinerImpl::matchSelectTruncOneZero(
return false;
// True value must be 1, false value must be 0.
- auto TrueOpt = getIConstantVRegValWithLookThrough(Sel.getTrueReg(), MRI);
- auto FalseOpt = getIConstantVRegValWithLookThrough(Sel.getFalseReg(), MRI);
+ std::optional<llvm::ValueAndVReg> TrueOpt =
+ getIConstantVRegValWithLookThrough(Sel.getTrueReg(), MRI);
+ std::optional<llvm::ValueAndVReg> FalseOpt =
+ getIConstantVRegValWithLookThrough(Sel.getFalseReg(), MRI);
if (!TrueOpt || !FalseOpt)
return false;
if (!TrueOpt->Value.isOne() || !FalseOpt->Value.isZero())
@@ -1638,8 +1645,8 @@ bool PISAPreLegalizerCombinerImpl::matchSelectTruncOneZero(
Register DstReg = Sel.getReg(0);
MatchInfo = [=](MachineIRBuilder &B) {
- auto One = B.buildConstant(WideTy, 1);
- auto And = B.buildAnd(WideTy, Wide, One);
+ llvm::MachineInstrBuilder One = B.buildConstant(WideTy, 1);
+ llvm::MachineInstrBuilder And = B.buildAnd(WideTy, Wide, One);
B.buildTrunc(DstReg, And);
};
return true;
diff --git a/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp b/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
index fb8fa3f69ed172..9c4d765c1b4810 100644
--- a/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
+++ b/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
@@ -34,7 +34,7 @@ PISARegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
SmallVector<const ValueMapping *, 8> OpdsMapping(MI.getNumOperands());
for (unsigned Idx = 0; Idx < MI.getNumOperands(); ++Idx) {
- auto &MO = MI.getOperand(Idx);
+ const MachineOperand &MO = MI.getOperand(Idx);
if (MO.isReg() && MO.getReg().isValid()) {
unsigned Size = getSizeInBits(MO.getReg(), MRI, *TRI);
diff --git a/llvm/lib/Target/PISA/PISASubtarget.cpp b/llvm/lib/Target/PISA/PISASubtarget.cpp
index bfc7c7390c2cc7..3bcb384ca0c621 100644
--- a/llvm/lib/Target/PISA/PISASubtarget.cpp
+++ b/llvm/lib/Target/PISA/PISASubtarget.cpp
@@ -47,6 +47,6 @@ PISASubtarget &PISASubtarget::initSubtargetDependencies(StringRef CPU,
// Determine compatibility of instruction's PISA target, specified via
// "let Predicates = []", vs. platform's target, specified via -mcpu=
bool PISASubtarget::supportsPISATarget(StringRef Name) const {
- auto InstrPISATarget = PISA::getPISATargetInfo(Name);
+ PISA::PISATargetInfo InstrPISATarget = PISA::getPISATargetInfo(Name);
return isCompatiblePISATargetInfo(PISATarget, InstrPISATarget);
}
diff --git a/llvm/lib/Target/PISA/PISATargetMachine.cpp b/llvm/lib/Target/PISA/PISATargetMachine.cpp
index f4b3e163a3b8dc..f3fef360318768 100644
--- a/llvm/lib/Target/PISA/PISATargetMachine.cpp
+++ b/llvm/lib/Target/PISA/PISATargetMachine.cpp
@@ -88,7 +88,7 @@ PISATargetMachine::getSubtargetImpl(const Function &F) const {
SmallString<128> Key(CPU);
Key.append(FS);
- auto &I = SubtargetMap[Key];
+ std::unique_ptr<PISASubtarget> &I = SubtargetMap[Key];
if (!I) {
I = std::make_unique<PISASubtarget>(TargetTriple, CPU.str(), FS.str(),
*this);
@@ -97,7 +97,7 @@ PISATargetMachine::getSubtargetImpl(const Function &F) const {
}
unsigned PISATargetMachine::getAssumedAddrSpace(const Value *V) const {
- const auto *Ld = dyn_cast<LoadInst>(V);
+ const LoadInst *Ld = dyn_cast<LoadInst>(V);
if (!Ld || Ld->getPointerOperand()->getType()->getPointerAddressSpace() !=
unsigned(PISAAS::AddressSpace::CONSTANT))
return ~0U;
@@ -106,7 +106,7 @@ unsigned PISATargetMachine::getAssumedAddrSpace(const Value *V) const {
std::pair<const Value *, unsigned>
PISATargetMachine::getPredicatedAddrSpace(const Value *V) const {
- auto *II = dyn_cast<IntrinsicInst>(V);
+ const IntrinsicInst *II = dyn_cast<IntrinsicInst>(V);
if (!II)
return std::make_pair(nullptr, -1);
diff --git a/llvm/lib/Target/PISA/PISATargetMachine.h b/llvm/lib/Target/PISA/PISATargetMachine.h
index 5e98087d2272fd..0f6bcf8396d7a7 100644
--- a/llvm/lib/Target/PISA/PISATargetMachine.h
+++ b/llvm/lib/Target/PISA/PISATargetMachine.h
@@ -55,10 +55,10 @@ class PISATargetMachine : public CodeGenTargetMachineImpl {
}
bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DstAS) const override {
- auto Sas = (SrcAS == (unsigned)PISAAS::AddressSpace::GLOBAL) ||
+ bool Sas = (SrcAS == (unsigned)PISAAS::AddressSpace::GLOBAL) ||
(SrcAS == (unsigned)PISAAS::AddressSpace::CONSTANT) ||
(SrcAS == (unsigned)PISAAS::AddressSpace::GENERIC);
- auto Das = (DstAS == (unsigned)PISAAS::AddressSpace::GLOBAL) ||
+ bool Das = (DstAS == (unsigned)PISAAS::AddressSpace::GLOBAL) ||
(DstAS == (unsigned)PISAAS::AddressSpace::CONSTANT) ||
(DstAS == (unsigned)PISAAS::AddressSpace::GENERIC);
return Sas && Das;
diff --git a/llvm/lib/TargetParser/PISATargetParser.cpp b/llvm/lib/TargetParser/PISATargetParser.cpp
index 8b84d7eecd3ef7..ef5926c8b37a14 100644
--- a/llvm/lib/TargetParser/PISATargetParser.cpp
+++ b/llvm/lib/TargetParser/PISATargetParser.cpp
@@ -19,6 +19,6 @@ void llvm::PISA::fillFeatureMap(StringRef CPU, StringMap<bool> &Features) {
return;
if (std::optional<StringMap<bool>> Default = getCPUDefaultTargetFeatures(
Info.Name, BasicPISASubTypeKV, BasicPISAFeatureKV))
- for (const auto &KV : *Default)
+ for (const StringMapEntry<bool> &KV : *Default)
Features[KV.first()] = KV.second;
}
>From 13503187fbb814f5ff147cf348e7a28b8f2d328f Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 05:49:09 +0000
Subject: [PATCH 04/14] Change lowerReturn to use getSubtarget<PISASubtarget>()
---
llvm/lib/Target/PISA/PISACallLowering.cpp | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Target/PISA/PISACallLowering.cpp b/llvm/lib/Target/PISA/PISACallLowering.cpp
index 1868290306b12a..c7f7f0fde4261b 100644
--- a/llvm/lib/Target/PISA/PISACallLowering.cpp
+++ b/llvm/lib/Target/PISA/PISACallLowering.cpp
@@ -35,7 +35,7 @@ bool PISACallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
return false;
if (Val) {
const DataLayout &DL = MIRBuilder.getDataLayout();
- const TargetSubtargetInfo &STI = MIRBuilder.getMF().getSubtarget();
+ const PISASubtarget &STI = MIRBuilder.getMF().getSubtarget<PISASubtarget>();
unsigned Op = 0;
Type *Ty = Val->getType();
Register VReg = VRegs[0];
@@ -384,8 +384,8 @@ unsigned PISACallLowering::getLoadParamOpcode(MachineIRBuilder &MIRBuilder,
Type *ArgType) const {
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
MachineFunction &MF = MIRBuilder.getMF();
- const PISARegisterInfo *TRI = static_cast<const PISARegisterInfo *>(
- MF.getSubtarget().getRegisterInfo());
+ const PISARegisterInfo *TRI =
+ MF.getSubtarget<PISASubtarget>().getRegisterInfo();
bool IsKernel = (F.getCallingConv() == CallingConv::PISA_KERNEL);
unsigned Op = 0;
@@ -540,8 +540,9 @@ bool PISACallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
}
MachineInstrBuilder MIB;
- const PISARegisterInfo *TRI = static_cast<const PISARegisterInfo *>(
- MIRBuilder.getMF().getSubtarget().getRegisterInfo());
+ const PISARegisterInfo *TRI = MIRBuilder.getMF()
+ .getSubtarget<PISASubtarget>()
+ .getRegisterInfo();
if (IsIndirectCall) {
Register CalleeReg = Info.Callee.getReg();
>From 32bc8be0dc93cacef438dfbdfb61213c3c9e8289 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 06:13:36 +0000
Subject: [PATCH 05/14] Use -mtriple=pisa and remove -verify-machineinstrs in
legalizer-unmerge-vectors.mir
---
.../GlobalISel/build-vector-with-constants-trunc-double.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir | 2 +-
.../test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir | 2 +-
.../CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir | 2 +-
.../GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir | 2 +-
.../PISA/GlobalISel/combine-local-id-range-trunc-zext.mir | 2 +-
.../CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir | 2 +-
.../CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir | 2 +-
.../test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir | 2 +-
.../PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir | 2 +-
.../PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir | 2 +-
.../CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir | 2 +-
.../GlobalISel/legalizer-extract-vector-elt-illegal-size.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir | 2 +-
.../CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir | 2 +-
.../GlobalISel/legalizer-load-non-power-of-2-overfetch.mir | 2 +-
.../CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir | 2 +-
.../PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir | 2 +-
.../GlobalISel/legalizer-load-store-vec-of-large-ints.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir | 2 +-
.../PISA/GlobalISel/legalizer-store-long-alignment.mir | 2 +-
.../CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir | 2 +-
.../CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir | 2 +-
.../CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir | 2 +-
.../GlobalISel/postlegalizer-extract-subvector-partial.mir | 2 +-
.../PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir | 4 ++--
.../PISA/GlobalISel/prelegalizer-reduce-predicates.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir | 2 +-
llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir | 2 +-
48 files changed, 49 insertions(+), 49 deletions(-)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir b/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
index 3c2e0376c6fda1..63acc4760ca342 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# Regression test to make sure truncating double into i8 doesn't try to produce an 8 bit float.
--- |
target triple = "pisa"
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir
index b0dec696aeedee..8ef616474255cc 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-abs-iredsmax.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
--- |
define i32 @combine_abs_max_i32(i32 %data, i32 %mask, i32 %passthru) {
%a = call i32 @llvm.abs.i32(i32 %data, i1 false)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir
index 534a6a345f4fd7..35b36c9d094b75 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-cmp-and-all-ones.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck -check-prefix=COMBINER %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck -check-prefix=COMBINER %s
--- |
target triple = "pisa"
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir
index 08a8dd687113a6..7c11941aedfd0a 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-extract-build-vector.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
--- |
define void @test_extract_build_vector(ptr %addr, ptr %addr2) {
%rv = load <4 x i32>, ptr %addr, align 32
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir
index b5434203547c21..2e574bdb9d9375 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-fadd-constant.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
---
name: test_fadd_constant
legalized: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
index 0658e26af55123..babbdafe38d6ad 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# This test exercises the `NoVectors` early-break inside
# PISA::getDefIgnoringBitcasts(). The combine
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-local-id-range-trunc-zext.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-local-id-range-trunc-zext.mir
index 959aaa318bf7fe..a69655060b00bc 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-local-id-range-trunc-zext.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-local-id-range-trunc-zext.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
--- |
; Test that computeKnownBitsForTargetInstr correctly propagates range
; attribute information from pisa.local.id intrinsics.
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir
index 572f6c3ab288d5..e4c6643e4e1c79 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-p2i-to-i2p-addrspace.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# Test that p2i_to_i2p_fixed combine rule correctly handles pointer address
# spaces. A COPY between pointers of different address spaces is illegal.
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
index 694e978edc5f52..8cbb7e7fce8b81 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# Tests for PISAPreLegalizerCombinerImpl::matchSelectTruncOneZero.
#
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir
index d492c5fd6c6532..cf9e31078c2cba 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-trunc-bool-trunc.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck -check-prefix=COMBINER %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck -check-prefix=COMBINER %s
--- |
define i8 @test_trunc_and_trunc(i32 %a) {
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
index b5abbdcfdb7479..b861151d39e818 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# Exercises PISAPreLegalizerCombinerImpl::applyTruncatedShift. The combine
# rule trunc_shift_to_vector_extract turns:
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir b/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
index 86d7ce2b0bbc93..8154cd3396beb1 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# Tests for PISAPostLegalizerCombinerImpl::matchFixIllegalShiftAmt /
# applyFixIllegalShiftAmt.
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir
index adc32984beca65..c4e3464c66549a 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalize-load-store-subbyte-scalar.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Sub-byte scalar G_STORE paths unreachable from .ll: the prelegalizer combiner
# rewrites every sub-byte store value into a single G_TRUNC, so only hand-written
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir
index 8543f42709ca5a..397fa9cb0f5bb7 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-16-32-vec.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
---
name: test_2xi16_or
body: |
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir
index 411243b1a87f6b..0b0cc7e69228f6 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-concat-vectors-clamp-elts.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
# Check that G_CONCAT_VECTORS of vectors larger than 4 elts are legalized.
--- |
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir
index e8fa9c84f0adb8..c139938772652e 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-constant.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck %s
---
name: test_constant_s32
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir
index 71079bbb50b918..1fb2564aacc39c 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-subvector.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck %s
---
name: extract_subvector_s64
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
index 81147be2a538cb..6ff8503af3087b 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Check that i32 vectors with an illegal number of elements are extended to the nearest legal size before extracting the element using the G_EXTRACT_VECTOR_ELT instruction.
--- |
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir
index b51cc3ccf27b07..5cf04908219212 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-fldexp.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
---
name: test_afn_bfloat
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir
index eef864f65c4ca8..222767252dcba3 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-freeze-i96.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Verify that G_FREEZE is legalized successfully into two i64 parts.
# NOTE: The intermediate widening to i128 is an internal step, and does not appear in the final output.
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir
index bcaf76d7451110..5bb28538d0708e 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-frem.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
---
name: test_frem_afn
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir
index 4f6217576f36de..af616abbb9fbbe 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-i2f.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
---
name: test_nonpow2_uitofp
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir
index 6d310a0e5c7bbf..5db4dec81669d6 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-icmp.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - -verify-machineinstrs | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
---
name: test_s1
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir
index 5b7bb46ca33086..ede400e27d413c 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-implicit-def.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck %s
---
name: test_implicit_def_s1
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir
index c25457854eda1a..5aa145938ff915 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-insert-subvector.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck %s
---
name: insert_subvector_s64
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-non-power-of-2-overfetch.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-non-power-of-2-overfetch.mir
index 67fda242698417..78d28560cfaa0d 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-non-power-of-2-overfetch.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-non-power-of-2-overfetch.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
# Check that loads/stores of vectors of <n x i32> elements with n
# being [5,7] elements is resolved to a load of <8 x i32> for smaller
# alignments. However, when alignment is > 8 bytes for slm and constant
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir
index 5a29c576890e8e..5986846bdc9388 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-i1-vec.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck %s
---
name: test_load_128
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir
index dea8bb21f3cc56..d3f30d1f229a32 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-non-standard-vec.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
# Check that loads/stores of vectors of non-2^n elements are resolved to legal
# instructions. As of creating this test, they are split apart into multiple
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-vec-of-large-ints.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-vec-of-large-ints.mir
index 168f7907827c86..623de797c736c4 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-vec-of-large-ints.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-load-store-vec-of-large-ints.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 5
-# RUN: llc -march=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck -check-prefix=LEGAL %s
# Check that loads/stores of vectors of 32n-bit width elements wider than 64 bits are resolved to vectors of 32-bit elements.
# Check that if (alignment < element bit width) scalarization is performed instead.
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
index e9b14060413a3d..52aab7a14ffd94 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Test that legalizeGMulh correctly handles carry propagation in the
# schoolbook 64-bit multiply-high expansion.
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir
index 0aaafc45bec2a6..703d52d0df301f 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Verify G_UMULH lowering on non-power-of-2 type.
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir
index fac84023771fab..e08e0cd49f5061 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s5.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Check that i5 phi operand is extended to i16.
--- |
target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir
index 682d8b5428f922..187f18b9915295 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-s96.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Check that i96 phi operand doesn't exit with UnableToLegalize.
--- |
target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir
index 9c8dcbd2e9b2df..d1651c264769f6 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-scalarize.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
--- |
target datalayout = "e-p:32:32-p1:64:64-p2:64:64-p3:32:32-p4:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v24:32:32-v32:32:32-v48:64:64-v64:64:64-v96:128:128-v128:128:128-v192:256:256-v256:256:256-n16:32-G1"
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir
index 11a6fa78ed52b7..82e92a5803cc42 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-phi-widen.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Check that i24 operands in phi inst are extended to i32.
--- |
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir
index 66b742daeeb5d4..b9849f3dbcaf4d 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-select-widen.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Check that i40 operands in select inst are extended to i64.
--- |
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
index ade58cf506a0af..6301686e401206 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
# Check that alignment too large to store in 32-bit value isn't truncated.
--- |
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir
index 79df8faa0f77ed..cb7f9e4869fe20 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-udiv128-sequence.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Checks that sequence produced by Expand IR instructions for udiv128 is legalized:
--- |
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir
index e95a5b0a44db98..18562155aa599d 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-unmerge-vectors.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer %s -o - | FileCheck %s
# Verify UNMERGE lowering case where both SrcTy and DstTy are vectors with over 4 elements.
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
index b491bd641184e8..347d43c878e9c0 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Ensure that G_ZEXT with large destination types is handled without causing a crash.
--- |
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir b/llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir
index 6ffd5feac885dc..d9e933983a1836 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/mir-print-bfloat.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
---
name: test_bfloat
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
index 3f43b136219cae..678276d43751f5 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# Tests the compare_select PostLegalizerCombiner rule:
# %cond = G_ICMP intpred(ne), %x(s32), 0
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-extract-subvector-partial.mir b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-extract-subvector-partial.mir
index 2c8f60aee0b89f..3226ea2cb42e9c 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-extract-subvector-partial.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-extract-subvector-partial.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# G_EXTRACT_SUBVECTOR fed by a chain of G_INSERT_SUBVECTOR into IMPLICIT_DEF,
# where the extracted window fully contains the inserts. The postlegalizer
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
index e0ab946e993ca2..8ecbda48bef109 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# Test that the PostLegalizer does not infinite-loop on shl(or x, const), const.
#
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir b/llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir
index e194f452990c4c..f2665036d82f2d 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postrapseudo-i1.mir
@@ -1,5 +1,5 @@
-# RUN: llc -march=pisa -run-pass=postrapseudos -verify-machineinstrs %s -o - | FileCheck -check-prefix=PSEUDO %s
-# RUN-CRASHES: llc -march=pisa -start-before=postrapseudos -verify-machineinstrs %s -o - | FileCheck -check-prefix=ASM %s
+# RUN: llc -mtriple=pisa -run-pass=postrapseudos -verify-machineinstrs %s -o - | FileCheck -check-prefix=PSEUDO %s
+# RUN-CRASHES: llc -mtriple=pisa -start-before=postrapseudos %s -o - | FileCheck -check-prefix=ASM %s
--- |
define i32 @test_copy_pred(i32 %arg) {
ret i32 %arg
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
index 456a006bded3bd..99b4fae45cbc62 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --validate-debuginfo False --version 6
-# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# The reduce_predicates PreLegalizerCombiner rule rewrites an AND/OR/XOR
# reduction over the per-lane results of a vector compare into an explicit
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
index a8d3e9bd863b76..c18dd86a662aa6 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
@@ -1,4 +1,4 @@
-# RUN: llc -march=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
--- |
define i64 @zext_and_mask_rhs(i32 %x) {
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir b/llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir
index c383a57135cfa3..88b2fdaa9e540c 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/retain-extractvec.mir
@@ -1,5 +1,5 @@
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 6
-# RUN: llc -march=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
---
name: retain_extract_subvector
>From 32e9faedb15b748832d51ce862cea1805675edc0 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 06:24:53 +0000
Subject: [PATCH 06/14] Obtain PISARegisterInfo through PISASubtarget
---
llvm/lib/Target/PISA/PISARegisterBankInfo.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp b/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
index 9c4d765c1b4810..daa2937ab2dda4 100644
--- a/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
+++ b/llvm/lib/Target/PISA/PISARegisterBankInfo.cpp
@@ -8,6 +8,7 @@
#include "PISARegisterBankInfo.h"
#include "PISARegisterInfo.h"
+#include "PISASubtarget.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegisterBank.h"
@@ -29,7 +30,7 @@ PISARegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
const MachineFunction &MF = *MI.getParent()->getParent();
const MachineRegisterInfo &MRI = MF.getRegInfo();
- const TargetRegisterInfo *TRI = MRI.getTargetRegisterInfo();
+ const PISARegisterInfo *TRI = MF.getSubtarget<PISASubtarget>().getRegisterInfo();
SmallVector<const ValueMapping *, 8> OpdsMapping(MI.getNumOperands());
>From 4e7636f8a4d3acdd6afde5db447d87e866da0837 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 06:35:01 +0000
Subject: [PATCH 07/14] Use {} instead of std::make_pair
---
.../lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp | 2 +-
llvm/lib/Target/PISA/PISAISelLowering.cpp | 2 +-
llvm/lib/Target/PISA/PISALegalizerInfo.cpp | 5 ++---
llvm/lib/Target/PISA/PISARegisterInfo.cpp | 7 +++----
llvm/lib/Target/PISA/PISATargetMachine.cpp | 13 +++++--------
5 files changed, 12 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp b/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp
index acfaa8c58847f7..5075a96e9a9178 100644
--- a/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp
+++ b/llvm/lib/Target/PISA/MCTargetDesc/PISARegEncoder.cpp
@@ -177,7 +177,7 @@ RegEncoder::decodeVirtualRegister(MCRegister Reg) {
unsigned BankBits = (Reg >> NumRegBits) & ((1U << NumBankBits) - 1);
const char *Prefix = getPrefixFromBank(getRegBank(BankBits));
unsigned Num = Reg & ((1U << NumRegBits) - 1);
- return std::make_pair(Prefix, Num);
+ return {Prefix, Num};
}
bool RegEncoder::isVirtualRegNo(unsigned RegNo) {
diff --git a/llvm/lib/Target/PISA/PISAISelLowering.cpp b/llvm/lib/Target/PISA/PISAISelLowering.cpp
index 51f00605d39691..47d785c3d9b77f 100644
--- a/llvm/lib/Target/PISA/PISAISelLowering.cpp
+++ b/llvm/lib/Target/PISA/PISAISelLowering.cpp
@@ -472,7 +472,7 @@ PISATargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI,
}
if (RC)
- return std::make_pair(0u, RC);
+ return {0u, RC};
}
return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT);
diff --git a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
index ae721452113235..29196c9c2b635f 100644
--- a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
+++ b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
@@ -421,8 +421,7 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
llvm::LLT EltTy = Query.Types[0];
uint16_t NumElts = EltTy.getNumElements();
uint64_t NewNumElts = PowerOf2Ceil(NumElts) / 2;
- return std::make_pair(
- 0, LLT::fixed_vector(NewNumElts, EltTy.getScalarType()));
+ return {0, LLT::fixed_vector(NewNumElts, EltTy.getScalarType())};
})
// split up vectors of non-standard size elements
.fewerElementsIf(
@@ -433,7 +432,7 @@ PISALegalizerInfo::PISALegalizerInfo(const PISASubtarget &ST) {
},
[=](const LegalityQuery &Query) -> std::pair<unsigned, LLT> {
llvm::LLT EltTy = Query.Types[0];
- return std::make_pair(0, EltTy.getScalarType());
+ return {0, EltTy.getScalarType()};
})
// cast non-^2 scalars to vectors of i8
.bitcastIf(
diff --git a/llvm/lib/Target/PISA/PISARegisterInfo.cpp b/llvm/lib/Target/PISA/PISARegisterInfo.cpp
index 01eb0ca5746774..f5015c80453d5a 100644
--- a/llvm/lib/Target/PISA/PISARegisterInfo.cpp
+++ b/llvm/lib/Target/PISA/PISARegisterInfo.cpp
@@ -99,10 +99,9 @@ PISARegisterInfo::PISARegisterInfo() : PISAGenRegisterInfo(PISA::DummyReg) {
RCD->NumElements = RC->LaneMask.getNumLanes();
RCD->ScalarBitSize = getScalarBitSize(RC, RCD->NumElements);
}
- std::pair<unsigned, unsigned> Key =
- std::make_pair(RCD->NumElements, RCD->ScalarBitSize);
+ std::pair<unsigned, unsigned> Key{RCD->NumElements, RCD->ScalarBitSize};
if (!VecRegClassMap[Key])
- VecRegClassMap[std::make_pair(RCD->NumElements, RCD->ScalarBitSize)] = RC;
+ VecRegClassMap[Key] = RC;
RegClassMap[RC] = std::move(RCD);
}
@@ -266,7 +265,7 @@ PISARegisterInfo::getBitSizeFromRegClass(const TargetRegisterClass *RC) const {
const TargetRegisterClass *
PISARegisterInfo::getVectorRegClass(unsigned NumElts, unsigned BitSize) const {
- std::pair<unsigned, unsigned> P = std::make_pair(NumElts, BitSize);
+ std::pair<unsigned, unsigned> P{NumElts, BitSize};
DenseMap<std::pair<unsigned, unsigned>,
const TargetRegisterClass *>::const_iterator I =
VecRegClassMap.find(P);
diff --git a/llvm/lib/Target/PISA/PISATargetMachine.cpp b/llvm/lib/Target/PISA/PISATargetMachine.cpp
index f3fef360318768..0c10924a18076e 100644
--- a/llvm/lib/Target/PISA/PISATargetMachine.cpp
+++ b/llvm/lib/Target/PISA/PISATargetMachine.cpp
@@ -108,22 +108,19 @@ std::pair<const Value *, unsigned>
PISATargetMachine::getPredicatedAddrSpace(const Value *V) const {
const IntrinsicInst *II = dyn_cast<IntrinsicInst>(V);
if (!II)
- return std::make_pair(nullptr, -1);
+ return {nullptr, ~0U};
switch (II->getIntrinsicID()) {
case Intrinsic::pisa_isaddr_private:
- return std::make_pair(II->getArgOperand(0),
- unsigned(PISAAS::AddressSpace::PRIVATE));
+ return {II->getArgOperand(0), unsigned(PISAAS::AddressSpace::PRIVATE)};
case Intrinsic::pisa_isaddr_global:
- return std::make_pair(II->getArgOperand(0),
- unsigned(PISAAS::AddressSpace::GLOBAL));
+ return {II->getArgOperand(0), unsigned(PISAAS::AddressSpace::GLOBAL)};
case Intrinsic::pisa_isaddr_shared:
- return std::make_pair(II->getArgOperand(0),
- unsigned(PISAAS::AddressSpace::SHARED));
+ return {II->getArgOperand(0), unsigned(PISAAS::AddressSpace::SHARED)};
default:
break;
}
- return std::make_pair(nullptr, -1);
+ return {nullptr, ~0U};
}
TargetTransformInfo
>From d2df6a053e110a30a8506f55fdc74813ea28acd9 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 06:42:52 +0000
Subject: [PATCH 08/14] Remove IR sections from MIR tests
---
.../build-vector-with-constants-trunc-double.mir | 12 +-----------
.../combine-getdef-ignoring-bitcasts-novectors.mir | 9 ---------
.../GlobalISel/combine-select-trunc-one-zero.mir | 9 ---------
.../PISA/GlobalISel/combine-truncated-shift.mir | 5 -----
.../PISA/GlobalISel/fix-illegal-shift-amt.mir | 5 -----
.../legalizer-extract-vector-elt-illegal-size.mir | 11 -----------
.../CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir | 9 ---------
.../GlobalISel/legalizer-store-long-alignment.mir | 7 -------
.../CodeGen/PISA/GlobalISel/legalizer-zext-big.mir | 6 ------
.../GlobalISel/postlegalizer-compare-select.mir | 13 -------------
.../postlegalizer-no-commute-shift-loop.mir | 7 -------
.../GlobalISel/prelegalizer-reduce-predicates.mir | 7 -------
.../PISA/GlobalISel/prelegalizer-zext-and.mir | 9 ---------
13 files changed, 1 insertion(+), 108 deletions(-)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir b/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
index 63acc4760ca342..85510b53160cb6 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/build-vector-with-constants-trunc-double.mir
@@ -1,22 +1,12 @@
# RUN: llc -mtriple=pisa -run-pass=pisa-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
# Regression test to make sure truncating double into i8 doesn't try to produce an 8 bit float.
---- |
- target triple = "pisa"
-
- define pisa_kernel void @test(i8 %id) {
- %b = bitcast <1 x double> zeroinitializer to <8 x i8>
- %elem = extractelement <8 x i8> %b, i8 %id
- store i8 %elem, ptr addrspace(1) null, align 1
- ret void
- }
-...
---
name: test
legalized: true
tracksRegLiveness: true
isSSA: true
body: |
- bb.1 (%ir-block.0):
+ bb.1:
; CHECK-LABEL: {{^}}name: test
; CHECK: [[CONST:%[0-9]+]]:_(i64) = G_CONSTANT i64 0
; CHECK: [[BITCAST:%[0-9]+]]:_(<2 x i32>) = G_BITCAST [[CONST]](i64)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
index babbdafe38d6ad..62cce2c9c3fa51 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-getdef-ignoring-bitcasts-novectors.mir
@@ -17,15 +17,6 @@
# The shifts_of_constants combine should fold the hinted shift into one constant.
# The body-level CHECK lines below verify the folded G_CONSTANT result.
---- |
- define <2 x i16> @build_vector_with_vector_bitcast_in_chain(i16 %a, i16 %b) {
- ret <2 x i16> zeroinitializer
- }
-
- define i16 @shift_of_constants_via_assert_sext_hints(i16 %unused) {
- ret i16 0
- }
-...
---
name: build_vector_with_vector_bitcast_in_chain
legalized: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
index 8cbb7e7fce8b81..8c26184c523fbc 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-select-trunc-one-zero.mir
@@ -6,15 +6,6 @@
# select(trunc(wide -> i1), N 1, N 0) => trunc(and(wide, 1), N)
# to avoid introducing an illegal i1 compare when lowering bool-to-int.
---- |
- define i16 @test_select_trunc_one_zero_match(i32 %wide) { ret i16 0 }
- define i16 @test_no_fold_cond_from_icmp(i32 %a) { ret i16 0 }
- define i16 @test_no_fold_wrong_true_val(i32 %wide) { ret i16 0 }
- define i16 @test_no_fold_wrong_false_val(i32 %wide) { ret i16 0 }
- define i32 @test_no_fold_dst_geq_wide(i16 %wide) { ret i32 0 }
- define i16 @test_no_fold_nonconstant_true(i32 %wide, i16 %t) { ret i16 0 }
-...
-
# ============================================================================
# Positive test: condition is i1 from G_TRUNC, true=1, false=0, DstTy < WideTy.
# Expected transform: select(trunc(i32 to i1), i16 1, i16 0) => trunc(i32 & 1, i16)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir b/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
index b861151d39e818..8d1e293c2b199e 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/combine-truncated-shift.mir
@@ -18,11 +18,6 @@
# report for this file; both the lo and hi extracts must appear in the
# output.
---- |
- define i16 @lo_and_hi_truncs(i32 %x) {
- ret i16 0
- }
-...
---
name: lo_and_hi_truncs
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir b/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
index 8154cd3396beb1..e1061b3edb9b6e 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/fix-illegal-shift-amt.mir
@@ -9,11 +9,6 @@
# - inserting G_TRUNC if the amount type is wider than i32 (e.g. i64)
# - inserting G_ZEXT if the amount type is narrower than i32 (e.g. i16)
---- |
- define i64 @test_shl_i64_i64_amt(i64 %val, i64 %amt) { ret i64 0 }
- define i32 @test_ashr_i32_i16_amt(i32 %val, i16 %amt) { ret i32 0 }
-...
-
# ============================================================================
# Trunc path: shift amount is i64 (>32 bits) -> G_TRUNC inserted to i32.
# Covers applyFixIllegalShiftAmt line 1673: MIB.buildTrunc(I32, ShAmtReg)
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
index 6ff8503af3087b..d027e8a3e547ad 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-extract-vector-elt-illegal-size.mir
@@ -1,17 +1,6 @@
# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Check that i32 vectors with an illegal number of elements are extended to the nearest legal size before extracting the element using the G_EXTRACT_VECTOR_ELT instruction.
---- |
- define pisa_kernel i32 @test_v10_32b(<10 x i32> %vec) {
- %elem = extractelement <10 x i32> %vec, i32 5
- ret i32 %elem
- }
-
- define pisa_kernel i32 @test_v20_32b(<20 x i32> %vec) {
- %elem = extractelement <20 x i32> %vec, i32 10
- ret i32 %elem
- }
-...
---
name: test_v10_32b
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
index 52aab7a14ffd94..01b03ac02864eb 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-mulh-i64.mir
@@ -9,15 +9,6 @@
# upper 64 bits. A buggy implementation that shifts each cross-term
# independently (P_hh + (P_lh>>32) + (P_hl>>32)) drops this carry.
---- |
- define i64 @test_umulh_i64(i64 %a, i64 %b) {
- ret i64 0
- }
- define i64 @test_smulh_i64(i64 %a, i64 %b) {
- ret i64 0
- }
-...
-
# --- G_UMULH i64 ---
# Verify that the legalized sequence includes carry-aware cross-term
# accumulation: the adds feeding the final result must include the
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
index 6301686e401206..71de757dcced9d 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-store-long-alignment.mir
@@ -1,13 +1,6 @@
# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
# Check that alignment too large to store in 32-bit value isn't truncated.
---- |
- define void @test_concat_clamp_elts(ptr %addr) {
- %vec.expand = shufflevector <6 x i16> undef, <6 x i16> undef, <12 x i32> <i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef, i32 undef>
- store <12 x i16> %vec.expand, ptr addrspace(1) null, align 4294967296
- ret void
- }
-...
---
name: test_concat_clamp_elts
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
index 347d43c878e9c0..a9e34678f21c5a 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/legalizer-zext-big.mir
@@ -2,12 +2,6 @@
# RUN: llc -mtriple=pisa -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
# Ensure that G_ZEXT with large destination types is handled without causing a crash.
---- |
- define i128 @test_zext_big(i16 %arg) {
- %ret = zext i16 %arg to i128
- ret i128 %ret
- }
-...
---
name: test_zext_big
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
index 678276d43751f5..9435854c9acc82 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-compare-select.mir
@@ -6,19 +6,6 @@
# =>
# %res = G_PISA_SELECT %x(s32), %a(s32), %b(s32)
---- |
- define i32 @compare_select_ne(i32 %x, i32 %a, i32 %b) {
- %cond = icmp ne i32 %x, 0
- %res = select i1 %cond, i32 %a, i32 %b
- ret i32 %res
- }
-
- define i32 @compare_select_eq(i32 %x, i32 %a, i32 %b) {
- %cond = icmp eq i32 %x, 0
- %res = select i1 %cond, i32 %a, i32 %b
- ret i32 %res
- }
-...
---
name: compare_select_ne
legalized: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
index 8ecbda48bef109..f8539385b679c6 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/postlegalizer-no-commute-shift-loop.mir
@@ -19,13 +19,6 @@
# commute_shift is therefore excluded from the PostLegalizer
# (pisa_post_removed_combines).
---- |
- define i32 @shl_or_const(i32 %x) {
- %or = or i32 %x, 4
- %shl = shl i32 %or, 2
- ret i32 %shl
- }
-...
---
name: shl_or_const
legalized: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
index 99b4fae45cbc62..89268b8eef36d2 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-reduce-predicates.mir
@@ -11,13 +11,6 @@
# inverting the predicate for the -1 case (eq -> ne) and leaving it unchanged
# for the 0 case.
---- |
- define i32 @reduce_and(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
- define i32 @reduce_or(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
- define i32 @reduce_xor(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
- define i32 @reduce_and_use_icmp_neg1(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
- define i32 @reduce_and_use_icmp_zero(<2 x i32> %a, <2 x i32> %b) { ret i32 0 }
-...
---
name: reduce_and
tracksRegLiveness: true
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
index c18dd86a662aa6..ef1b974235fff4 100644
--- a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
+++ b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-zext-and.mir
@@ -1,14 +1,5 @@
# RUN: llc -mtriple=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
---- |
- define i64 @zext_and_mask_rhs(i32 %x) {
- ret i64 0
- }
- define i64 @zext_and_mask_lhs(i32 %x) {
- ret i64 0
- }
-...
-
# --- canonical: G_AND (G_ZEXT x), C ---
---
name: zext_and_mask_rhs
>From 1899433a181f28652d791252fbbff79111c50174 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 06:48:55 +0000
Subject: [PATCH 09/14] Use LLT::scalarOrVector
---
llvm/lib/Target/PISA/PISACallLowering.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/lib/Target/PISA/PISACallLowering.cpp b/llvm/lib/Target/PISA/PISACallLowering.cpp
index c7f7f0fde4261b..0242f42892b2b8 100644
--- a/llvm/lib/Target/PISA/PISACallLowering.cpp
+++ b/llvm/lib/Target/PISA/PISACallLowering.cpp
@@ -279,7 +279,7 @@ bool PISACallLowering::lowerFormalArguments(MachineIRBuilder &MIRBuilder,
EltRegClass = &PISA::Reg32bRegClass;
EltLLT = LLT::integer(32);
TargetReg = MRI->createGenericVirtualRegister(
- LLT::vector(ElementCount::getFixed(TotalSize / 32), I32));
+ LLT::scalarOrVector(ElementCount::getFixed(TotalSize / 32), I32));
NumElts = TotalSize / 32;
}
>From ff7cdcab176a6792f80a9dde8755e7c7580abe68 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 06:54:39 +0000
Subject: [PATCH 10/14] Replace assert with reportFatalUsageError
---
llvm/lib/Target/PISA/PISACallLowering.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Target/PISA/PISACallLowering.cpp b/llvm/lib/Target/PISA/PISACallLowering.cpp
index 0242f42892b2b8..1358e08ef8ef70 100644
--- a/llvm/lib/Target/PISA/PISACallLowering.cpp
+++ b/llvm/lib/Target/PISA/PISACallLowering.cpp
@@ -17,6 +17,7 @@
#include "llvm/CodeGen/FunctionLoweringInfo.h"
#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
#include "llvm/IR/Metadata.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ModRef.h"
using namespace llvm;
@@ -436,7 +437,7 @@ unsigned PISACallLowering::getLoadParamOpcode(MachineIRBuilder &MIRBuilder,
Op = ParamScalar[IsKernel][3];
break;
default:
- assert(false && "Bit size for call arg not supported");
+ reportFatalUsageError("Bit size for call arg not supported");
}
} else if (ArgType->isPointerTy()) {
if (BitSize == 64) {
@@ -514,7 +515,7 @@ unsigned PISACallLowering::getLoadParamOpcode(MachineIRBuilder &MIRBuilder,
Op = ParamVector[IsKernel][3][NumElts - 2];
break;
default:
- assert(false && "Bit size for call arg not supported");
+ reportFatalUsageError("Bit size for call arg not supported");
}
assert(Op && "argument type is not supported");
} else {
>From e9ad3567fc88fa1ae2590f81df823e1956a2bfc0 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 07:03:05 +0000
Subject: [PATCH 11/14] Rename legalizeGLoad to legalizeGLoadStore (it handles
both loads and stores)
---
llvm/lib/Target/PISA/PISALegalizerInfo.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
index 29196c9c2b635f..3078800c0a05fd 100644
--- a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
+++ b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
@@ -1488,8 +1488,8 @@ static bool legalizeGExtload(MachineInstr &MI, MachineIRBuilder &B) {
return true;
}
-static bool legalizeGLoad(MachineInstr &MI, MachineIRBuilder &B,
- LegalizerHelper &Helper) {
+static bool legalizeGLoadStore(MachineInstr &MI, MachineIRBuilder &B,
+ LegalizerHelper &Helper) {
llvm::MachineRegisterInfo &MRI = *B.getMRI();
GISelChangeObserver &Observer = Helper.Observer;
llvm::MachineOperand &ValMO = MI.getOperand(0);
@@ -3479,7 +3479,7 @@ bool PISALegalizerInfo::legalizeCustom(
return legalizeGItofp(MI, B);
case TargetOpcode::G_STORE:
case TargetOpcode::G_LOAD:
- return legalizeGLoad(MI, B, Helper);
+ return legalizeGLoadStore(MI, B, Helper);
case TargetOpcode::G_SEXTLOAD:
case TargetOpcode::G_ZEXTLOAD:
return legalizeGExtload(MI, B);
>From 9f5888734904002f793a4f60879550f9bad55667 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 07:11:38 +0000
Subject: [PATCH 12/14] Replace llvm_unreachable with reportFatalUsageError
---
llvm/lib/Target/PISA/PISALegalizerInfo.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
index 3078800c0a05fd..f68a83471960a4 100644
--- a/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
+++ b/llvm/lib/Target/PISA/PISALegalizerInfo.cpp
@@ -19,6 +19,7 @@
#include "llvm/CodeGen/TargetOpcodes.h"
#include "llvm/IR/IntrinsicsPISA.h"
#include "llvm/IR/PISAIntrinsicUtils.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/PISAAddrSpace.h"
using namespace llvm;
@@ -2164,7 +2165,7 @@ static bool legalizeGFpow(MachineInstr &MI, MachineIRBuilder &B) {
// can only do approximation of pow()
bool AllowApprox = MI.getFlag(MachineInstr::FmAfn);
if (!AllowApprox)
- llvm_unreachable("not implemented (fpow)");
+ reportFatalUsageError("G_FPOW without afn is not supported on PISA");
llvm::Register LogReg = MRI.createGenericVirtualRegister(DstTy);
llvm::Register MulReg = MRI.createGenericVirtualRegister(DstTy);
>From e8c46bb8c17c83b8ed6cd71f6024a6122df13193 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 07:13:46 +0000
Subject: [PATCH 13/14] Replace /// with //
---
llvm/lib/Target/PISA/PISACombine.td | 2 +-
.../Target/PISA/PISAPostLegalizerCombiner.cpp | 2 +-
.../Target/PISA/PISAPreLegalizerCombiner.cpp | 22 +++++++++----------
3 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/llvm/lib/Target/PISA/PISACombine.td b/llvm/lib/Target/PISA/PISACombine.td
index 2b7939e661d887..5e27e050012287 100644
--- a/llvm/lib/Target/PISA/PISACombine.td
+++ b/llvm/lib/Target/PISA/PISACombine.td
@@ -826,7 +826,7 @@ def reduce_predicates : GICombineRule<
// %28:_(s8) = G_TRUNC %2:reg32b(s32)
// %31:_(s32) = G_LSHR %2:reg32b, %23:_(s32)
// %32:_(s8) = G_TRUNC %31:_(s32)
-///%149:_(<4 x s8>) = G_BUILD_VECTOR %17:_(s8), %25:_(s8), %28:_(s8), %32:_(s8)
+// %149:_(<4 x s8>) = G_BUILD_VECTOR %17:_(s8), %25:_(s8), %28:_(s8), %32:_(s8)
// %141:_(s32) = G_BITCAST %149:_(<4 x s8>)
// => %A = G_AND %1, 0xFFFF
// => %B = G_SHL %2, 16
diff --git a/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp b/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
index a3f66f9f42e8f2..44e684baf83805 100644
--- a/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
+++ b/llvm/lib/Target/PISA/PISAPostLegalizerCombiner.cpp
@@ -1064,7 +1064,7 @@ void PISAPostLegalizerCombinerImpl::applyAndSelect(MachineInstr &MI,
// %28:_(s8) = G_TRUNC %2:reg32b(s32)
// %31:_(s32) = G_LSHR %2:reg32b, %23:_(s32)
// %32:_(s8) = G_TRUNC %31:_(s32)
-///%149:_(<4 x s8>) = G_BUILD_VECTOR %17:_(s8), %25:_(s8), %28:_(s8), %32:_(s8)
+// %149:_(<4 x s8>) = G_BUILD_VECTOR %17:_(s8), %25:_(s8), %28:_(s8), %32:_(s8)
// %141:_(s32) = G_BITCAST %149:_(<4 x s8>)
// => %A = G_AND %1, 0xFFFF
// => %B = G_SHL %2, 16
diff --git a/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp b/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
index 292652c0ed21d9..000bb301a5dc88 100644
--- a/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
+++ b/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
@@ -223,7 +223,7 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
llvm::Register ValueReg = LS.getOperand(0).getReg();
llvm::MachineMemOperand &MMO = LS.getMMO();
- /// The remaining size in Bits that still has to be loaded/stored
+ // The remaining size in Bits that still has to be loaded/stored
ssize_t Size = LS.getMemSizeInBits().getValue();
// Support sizes that are not a multiple of 8 by "promoting" them to the next
// multiple of 8. If the size is already a multiple of 8, it is not modified
@@ -242,7 +242,7 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
ValueReg = NewValueReg;
}
- /// The register holding the loaded value at the end
+ // The register holding the loaded value at the end
Register LoadRes;
while (Size > 0) {
uint64_t OpSize = bit_floor(static_cast<size_t>(Size));
@@ -253,7 +253,7 @@ void PISAPreLegalizerCombinerImpl::applyExpandNonPowerOf2LoadStore(
llvm::MachineMemOperand *NewMMO =
MI.getMF()->getMachineMemOperand(&MMO, MMO.getOffset() + Offset, OpTy);
- /// Stores the (potentially modified) pointer register
+ // Stores the (potentially modified) pointer register
llvm::Register AddrReg = PointerReg;
// We might need to change the store offset
if (Offset != 0) {
@@ -389,14 +389,14 @@ bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
llvm::Register ValueReg = StoreInst.getOperand(0).getReg();
- /// This has the integer size at the beginning, and each individual load
- /// decreases the value by its load size. At the end, this must be zero.
- /// NB: We use the size of the store here, promoted to the next multiple
- /// of 8. This size might be modified later if we find a G_*EXT/G_TRUNC.
+ // This has the integer size at the beginning, and each individual load
+ // decreases the value by its load size. At the end, this must be zero.
+ // NB: We use the size of the store here, promoted to the next multiple
+ // of 8. This size might be modified later if we find a G_*EXT/G_TRUNC.
unsigned ValueSize = StoreInst.getMemSize().getValue() * 8;
- /// This stores the size of the last load instruction in Bytes, s.t. we can
- /// verify that the current load is larger than the previous one
+ // This stores the size of the last load instruction in Bytes, s.t. we can
+ // verify that the current load is larger than the previous one
unsigned LastSize = 0;
SizeModificationOp = nullptr;
@@ -411,7 +411,7 @@ bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
NextChainInst->getOpcode() == TargetOpcode::G_ZEXT ||
NextChainInst->getOpcode() == TargetOpcode::G_SEXT ||
NextChainInst->getOpcode() == TargetOpcode::G_SEXT_INREG) {
- /// The "actual" size that was used during loading
+ // The "actual" size that was used during loading
unsigned LoadSize = MRI.getType(NextChainInst->getOperand(1).getReg())
.getScalarSizeInBits();
@@ -662,7 +662,7 @@ void PISAPreLegalizerCombinerImpl::applySimplifyNonPowerOf2LoadStoreChain(
llvm::MachineMemOperand *NewMMO = MI.getMF()->getMachineMemOperand(
&MMO, MMO.getOffset() + Offset, LoadTy);
- /// Stores the (potentially modified) pointer register
+ // Stores the (potentially modified) pointer register
llvm::Register AddrReg = PointerReg;
// Add the offset to the pointer reg if the offset is not zero
>From 3bdb62ea7b7b4b41afc15fc46f044b5ff759d5f7 Mon Sep 17 00:00:00 2001
From: Michal Paszkowski <michal.paszkowski at intel.com>
Date: Tue, 22 Sep 2026 12:20:51 +0000
Subject: [PATCH 14/14] Fix PISAPreLegalizerCombiner
---
.../Target/PISA/PISAPreLegalizerCombiner.cpp | 11 ++++----
.../PISA/GlobalISel/prelegalizer-i1.mir | 27 +++++++++++++++++++
2 files changed, 32 insertions(+), 6 deletions(-)
create mode 100644 llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-i1.mir
diff --git a/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp b/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
index 000bb301a5dc88..7a18d54e545751 100644
--- a/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
+++ b/llvm/lib/Target/PISA/PISAPreLegalizerCombiner.cpp
@@ -521,15 +521,14 @@ bool PISAPreLegalizerCombinerImpl::matchSimplifyNonPowerOf2LoadStoreChain(
: NextPowerOf2(SizeAfterModificationOp)) == LoadSize)
return false;
- // If the G_SEXT source size equals the load size and the source is
- // byte-aligned, applying this transformation would reconstruct the
- // same G_SEXT(G_LOAD) pattern, causing an infinite loop. When the
- // source size is not byte-aligned, the apply inserts in-place
- // shl/ashr to align it first, producing a different pattern.
+ // When the G_SEXT source size equals the load size, this rewrite
+ // cannot eliminate the load or the extension. For non-byte-aligned
+ // loads, generic combines can also fold the inserted shifts back to
+ // G_SEXT(G_LOAD), causing an infinite loop.
unsigned SextSrcSize =
MRI.getType(SizeModificationOp->getOperand(1).getReg())
.getScalarSizeInBits();
- if (LoadSize == SextSrcSize && SextSrcSize % 8 == 0)
+ if (LoadSize == SextSrcSize)
return false;
// TODO: revisit
diff --git a/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-i1.mir b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-i1.mir
new file mode 100644
index 00000000000000..32f150320ba291
--- /dev/null
+++ b/llvm/test/CodeGen/PISA/GlobalISel/prelegalizer-i1.mir
@@ -0,0 +1,27 @@
+# RUN: llc -mtriple=pisa -run-pass=pisa-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck -check-prefix=LEGAL %s
+--- |
+ define pisa_kernel void @test_load_i1() {
+ ; LEGAL-LABEL: {{^}}name: test_load_i1
+ ; LEGAL: [[CONST:%[0-9]+]]:_(p1) = G_CONSTANT i64 0
+ ; LEGAL: [[LOAD:%[0-9]+]]:_(i1) = G_LOAD [[CONST]](p1)
+ ; LEGAL: [[SEXT:%[0-9]+]]:_(i32) = G_SEXT [[LOAD]](i1)
+ ; LEGAL: G_STORE [[SEXT]](i32), [[CONST]](p1)
+
+ %L = load i1, ptr addrspace(1) null, align 1
+ %S = sext i1 %L to i32
+ store i32 %S, ptr addrspace(1) null, align 4
+ ret void
+ }
+...
+---
+name: test_load_i1
+alignment: 1
+tracksRegLiveness: true
+body: |
+ bb.1:
+ %1:_(p1) = G_CONSTANT i64 0
+ %0:_(i1) = G_LOAD %1:_(p1) :: (load (i1) from `ptr addrspace(1) null`, addrspace 1)
+ %2:_(i32) = G_SEXT %0:_(i1)
+ G_STORE %2:_(i32), %1:_(p1) :: (store (i32) into `ptr addrspace(1) null`, addrspace 1)
+ ret
+...
More information about the llvm-branch-commits
mailing list