[llvm] 2426172 - [GlobalISel] Add G_ASSERT_ZEXT

Jessica Paquette via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 28 13:59:39 PST 2021


Author: Jessica Paquette
Date: 2021-01-28T13:58:37-08:00
New Revision: 24261729a49feb4cf0376c6f9326374ab28ec0a5

URL: https://github.com/llvm/llvm-project/commit/24261729a49feb4cf0376c6f9326374ab28ec0a5
DIFF: https://github.com/llvm/llvm-project/commit/24261729a49feb4cf0376c6f9326374ab28ec0a5.diff

LOG: [GlobalISel] Add G_ASSERT_ZEXT

This adds a generic opcode which communicates that a type has already been
zero-extended from a narrower type.

This is intended to be similar to AssertZext in SelectionDAG.

For example,

```
%x_was_extended:_(s64) = G_ASSERT_ZEXT %x, 16
```

Signifies that the top 48 bits of %x are known to be 0.

This is useful in cases like this:

```
define i1 @zeroext_param(i8 zeroext %x) {
  %cmp = icmp ult i8 %x, -20
  ret i1 %cmp
}
```

In AArch64, `%x` must use a 32-bit register, which is then truncated to a 8-bit
value.

If we know that `%x` is already zero-ed out in the relevant high bits, we can
avoid the truncate.

Currently, in GISel, this looks like this:

```
_zeroext_param:
  and w8, w0, #0xff ; We don't actually need this!
  cmp w8, #236
  cset w0, lo
  ret
```

While SDAG does not produce the truncation, since it knows that it's
unnecessary:

```
_zeroext_param:
  cmp w0, #236
  cset w0, lo
  ret
```

This patch

- Adds G_ASSERT_ZEXT
- Adds MIRBuilder support for it
- Adds MachineVerifier support for it
- Documents it

It also puts G_ASSERT_ZEXT into its own class of "hint instruction." (There
should be a G_ASSERT_SEXT in the future, maybe a G_ASSERT_ALIGN as well.)

This allows us to skip over hints in the legalizer etc. These can then later
be selected like COPY instructions or removed.

Differential Revision: https://reviews.llvm.org/D95564

Added: 
    llvm/test/CodeGen/AArch64/GlobalISel/legalize-ignore-hint.mir
    llvm/test/MachineVerifier/test_g_assert_zext.mir
    llvm/test/MachineVerifier/test_g_assert_zext_register_bank_class.mir

Modified: 
    llvm/docs/GlobalISel/GenericOpcode.rst
    llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
    llvm/include/llvm/CodeGen/TargetOpcodes.h
    llvm/include/llvm/Support/TargetOpcodes.def
    llvm/include/llvm/Target/GenericOpcodes.td
    llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
    llvm/lib/CodeGen/MachineVerifier.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/GlobalISel/GenericOpcode.rst b/llvm/docs/GlobalISel/GenericOpcode.rst
index 9b9076b126c2..51adb6316d9d 100644
--- a/llvm/docs/GlobalISel/GenericOpcode.rst
+++ b/llvm/docs/GlobalISel/GenericOpcode.rst
@@ -735,3 +735,37 @@ An alignment value of `0` or `1` mean no specific alignment.
 .. code-block:: none
 
   %8:_(p0) = G_DYN_STACKALLOC %7(s64), 32
+
+Optimization Hints
+------------------
+
+These instructions do not correspond to any target instructions. They act as
+hints for various combines.
+
+G_ASSERT_ZEXT
+^^^^^^^^^^^^^
+
+Signifies that the contents of a register were previously zero-extended from a
+smaller type.
+
+The smaller type is denoted using an immediate operand. For scalars, this is the
+width of the entire smaller type. For vectors, this is the width of the smaller
+element type.
+
+.. code-block:: none
+
+  %x_assert:_(s32) = G_ASSERT_ZEXT %x(s32), 16
+  %y_assert:_(<2 x s32>) = G_ASSERT_ZEXT %y(<2 x s32>), 16
+
+G_ASSERT_ZEXT acts like a restricted form of copy.
+
+The source and destination registers must
+
+- Be virtual
+- Belong to the same register class
+- Belong to the same register bank
+
+It should always be safe to
+
+- Look through the source register
+- Replace the destination register with the source register

diff  --git a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
index 1ab4cd704824..93fd1cce29fd 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
@@ -810,6 +810,12 @@ class MachineIRBuilder {
   /// \return a MachineInstrBuilder for the newly created instruction.
   MachineInstrBuilder buildCopy(const DstOp &Res, const SrcOp &Op);
 
+  /// Build and insert \p Res = G_ASSERT_ZEXT Op, Size
+  ///
+  /// \return a MachineInstrBuilder for the newly created instruction.
+  MachineInstrBuilder buildAssertZExt(const DstOp &Res, const SrcOp &Op,
+                                      unsigned Size);
+
   /// Build and insert `Res = G_LOAD Addr, MMO`.
   ///
   /// Loads the value stored at \p Addr. Puts the result in \p Res.

diff  --git a/llvm/include/llvm/CodeGen/TargetOpcodes.h b/llvm/include/llvm/CodeGen/TargetOpcodes.h
index 080a244f6f69..169caf30bb22 100644
--- a/llvm/include/llvm/CodeGen/TargetOpcodes.h
+++ b/llvm/include/llvm/CodeGen/TargetOpcodes.h
@@ -36,6 +36,14 @@ inline bool isPreISelGenericOpcode(unsigned Opcode) {
 inline bool isTargetSpecificOpcode(unsigned Opcode) {
   return Opcode > TargetOpcode::PRE_ISEL_GENERIC_OPCODE_END;
 }
+
+/// \returns true if \p Opcode is an optimization hint opcode which is not
+/// supposed to appear after ISel.
+inline bool isPreISelGenericOptimizationHint(unsigned Opcode) {
+  return Opcode >= TargetOpcode::PRE_ISEL_GENERIC_OPTIMIZATION_HINT_START &&
+         Opcode <= TargetOpcode::PRE_ISEL_GENERIC_OPTIMIZATION_HINT_END;
+}
+
 } // end namespace llvm
 
 #endif

diff  --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def
index a63d40484089..ca78dbcb33fc 100644
--- a/llvm/include/llvm/Support/TargetOpcodes.def
+++ b/llvm/include/llvm/Support/TargetOpcodes.def
@@ -213,6 +213,14 @@ HANDLE_TARGET_OPCODE(ICALL_BRANCH_FUNNEL)
 /// This is something we might want to relax, but for now, this is convenient
 /// to produce diagnostics.
 
+/// Instructions which should not exist past instruction selection, but do not
+/// generate code. These instructions only act as optimization hints.
+HANDLE_TARGET_OPCODE(G_ASSERT_ZEXT)
+HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPTIMIZATION_HINT_START,
+                            G_ASSERT_ZEXT)
+HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPTIMIZATION_HINT_END,
+                            G_ASSERT_ZEXT)
+
 /// Generic ADD instruction. This is an integer add.
 HANDLE_TARGET_OPCODE(G_ADD)
 HANDLE_TARGET_OPCODE_MARKER(PRE_ISEL_GENERIC_OPCODE_START, G_ADD)

diff  --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td
index 209925969df3..77f7752e8317 100644
--- a/llvm/include/llvm/Target/GenericOpcodes.td
+++ b/llvm/include/llvm/Target/GenericOpcodes.td
@@ -1337,3 +1337,15 @@ def G_MEMSET : GenericInstruction {
   let hasSideEffects = false;
   let mayStore = true;
 }
+
+//------------------------------------------------------------------------------
+// Optimization hints
+//------------------------------------------------------------------------------
+
+// Asserts that an operation has already been zero-extended from a specific
+// type.
+def G_ASSERT_ZEXT : GenericInstruction {
+  let OutOperandList = (outs type0:$dst);
+  let InOperandList = (ins type0:$src, untyped_imm_0:$sz);
+  let hasSideEffects = false;
+}

diff  --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
index 67ef02a4e7b2..3ff331af53c3 100644
--- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
@@ -240,6 +240,12 @@ MachineInstrBuilder MachineIRBuilder::buildCopy(const DstOp &Res,
   return buildInstr(TargetOpcode::COPY, Res, Op);
 }
 
+MachineInstrBuilder MachineIRBuilder::buildAssertZExt(const DstOp &Res,
+                                                      const SrcOp &Op,
+                                                      unsigned Size) {
+  return buildInstr(TargetOpcode::G_ASSERT_ZEXT, Res, Op).addImm(Size);
+}
+
 MachineInstrBuilder MachineIRBuilder::buildConstant(const DstOp &Res,
                                                     const ConstantInt &Val) {
   LLT Ty = Res.getLLTTy(*getMRI());

diff  --git a/llvm/lib/CodeGen/MachineVerifier.cpp b/llvm/lib/CodeGen/MachineVerifier.cpp
index 0f6d9b888f47..2691dd8fd89f 100644
--- a/llvm/lib/CodeGen/MachineVerifier.cpp
+++ b/llvm/lib/CodeGen/MachineVerifier.cpp
@@ -941,6 +941,41 @@ void MachineVerifier::verifyPreISelGenericInstruction(const MachineInstr *MI) {
 
   // Verify properties of various specific instruction types
   switch (MI->getOpcode()) {
+  case TargetOpcode::G_ASSERT_ZEXT: {
+    if (!MI->getOperand(2).isImm()) {
+      report("G_ASSERT_ZEXT expects an immediate operand #2", MI);
+      break;
+    }
+
+    Register Dst = MI->getOperand(0).getReg();
+    Register Src = MI->getOperand(1).getReg();
+    LLT DstTy = MRI->getType(Dst);
+    LLT SrcTy = MRI->getType(Src);
+    verifyVectorElementMatch(DstTy, SrcTy, MI);
+    int64_t Imm = MI->getOperand(2).getImm();
+    if (Imm <= 0) {
+      report("G_ASSERT_ZEXT size must be >= 1", MI);
+      break;
+    }
+
+    if (Imm >= SrcTy.getScalarSizeInBits()) {
+      report("G_ASSERT_ZEXT size must be less than source bit width", MI);
+      break;
+    }
+
+    if (MRI->getRegBankOrNull(Src) != MRI->getRegBankOrNull(Dst)) {
+      report("G_ASSERT_ZEXT source and destination register banks must match",
+             MI);
+      break;
+    }
+
+    if (MRI->getRegClassOrNull(Src) != MRI->getRegClassOrNull(Dst))
+      report("G_ASSERT_ZEXT source and destination register classes must match",
+             MI);
+
+    break;
+  }
+
   case TargetOpcode::G_CONSTANT:
   case TargetOpcode::G_FCONSTANT: {
     LLT DstTy = MRI->getType(MI->getOperand(0).getReg());
@@ -1594,7 +1629,8 @@ void MachineVerifier::visitMachineInstrBefore(const MachineInstr *MI) {
     }
   }
 
-  if (isPreISelGenericOpcode(MCID.getOpcode())) {
+  unsigned Opc = MCID.getOpcode();
+  if (isPreISelGenericOpcode(Opc) || isPreISelGenericOptimizationHint(Opc)) {
     verifyPreISelGenericInstruction(MI);
     return;
   }

diff  --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-ignore-hint.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-ignore-hint.mir
new file mode 100644
index 000000000000..8cf3e70b2935
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-ignore-hint.mir
@@ -0,0 +1,21 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple=aarch64 -run-pass=legalizer -verify-machineinstrs %s -o - | FileCheck %s
+#
+# Verify that hint instructions are ignored by the legalizer.
+
+---
+name:            assert_zext
+tracksRegLiveness: true
+body: |
+  bb.0:
+    liveins: $w0, $w1
+    ; CHECK-LABEL: name: assert_zext
+    ; CHECK: %copy:_(s32) = COPY $w1
+    ; CHECK: %hint:_(s32) = G_ASSERT_ZEXT %copy, 16
+    ; CHECK: $w0 = COPY %hint(s32)
+    ; CHECK: RET_ReallyLR implicit $w0
+    %copy:_(s32) = COPY $w1
+    %hint:_(s32) = G_ASSERT_ZEXT %copy, 16
+    $w0 = COPY %hint
+    RET_ReallyLR implicit $w0
+...

diff  --git a/llvm/test/MachineVerifier/test_g_assert_zext.mir b/llvm/test/MachineVerifier/test_g_assert_zext.mir
new file mode 100644
index 000000000000..f39be8bf935e
--- /dev/null
+++ b/llvm/test/MachineVerifier/test_g_assert_zext.mir
@@ -0,0 +1,44 @@
+# REQUIRES: aarch64-registered-target
+# RUN: not --crash llc -verify-machineinstrs -mtriple aarch64 -run-pass none -o /dev/null %s 2>&1 | FileCheck %s
+
+name:            test
+body: |
+  bb.0:
+   liveins: $x0, $w0
+   %0:_(s64) = COPY $x0
+   %1:_(<4 x s16>) = COPY $x0
+   %2:_(s32) = COPY $w0
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT expects an immediate operand #2 ***
+   ; CHECK: instruction: %assert_zext_1:_(s64) = G_ASSERT_ZEXT
+   %assert_zext_1:_(s64) = G_ASSERT_ZEXT %0, %0
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT expects an immediate operand #2 ***
+   ; CHECK: instruction: %assert_zext_2:_(s64) = G_ASSERT_ZEXT
+   %assert_zext_2:_(s64) = G_ASSERT_ZEXT %0, i8 8
+
+   ; CHECK: *** Bad machine code: Type mismatch in generic instruction ***
+   ; CHECK: instruction: %assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT
+   ; CHECK: *** Bad machine code: operand types must be all-vector or all-scalar ***
+   ; CHECK: instruction: %assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT
+   %assert_zext_3:_(<2 x s32>) = G_ASSERT_ZEXT %0, 8
+
+   ; CHECK: *** Bad machine code: operand types must preserve number of vector elements ***
+   ; CHECK: instruction: %assert_zext_4:_(<2 x s32>) = G_ASSERT_ZEXT
+   %assert_zext_4:_(<2 x s32>) = G_ASSERT_ZEXT %1, 8
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT size must be >= 1 ***
+   ; CHECK: instruction: %assert_zext_5:_(s64) = G_ASSERT_ZEXT
+   %assert_zext_5:_(s64) = G_ASSERT_ZEXT %0, 0
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT size must be less than source bit width ***
+   ; CHECK: instruction: %assert_zext_6:_(s64) = G_ASSERT_ZEXT
+   %assert_zext_6:_(s64) = G_ASSERT_ZEXT %0, 128
+
+   ; CHECK: *** Bad machine code: Type mismatch in generic instruction ***
+   ; CHECK: instruction: %assert_zext_7:_(s64) = G_ASSERT_ZEXT %2:_, 8
+   %assert_zext_7:_(s64) = G_ASSERT_ZEXT %2, 8
+
+   ; CHECK: *** Bad machine code: Generic instruction cannot have physical register ***
+   ; CHECK: instruction: %assert_zext_8:_(s64) = G_ASSERT_ZEXT $x0, 8
+   %assert_zext_8:_(s64) = G_ASSERT_ZEXT $x0, 8

diff  --git a/llvm/test/MachineVerifier/test_g_assert_zext_register_bank_class.mir b/llvm/test/MachineVerifier/test_g_assert_zext_register_bank_class.mir
new file mode 100644
index 000000000000..b4ed7162fff3
--- /dev/null
+++ b/llvm/test/MachineVerifier/test_g_assert_zext_register_bank_class.mir
@@ -0,0 +1,35 @@
+# REQUIRES: aarch64-registered-target
+# RUN: not --crash llc -verify-machineinstrs -mtriple aarch64 -run-pass none -o /dev/null %s 2>&1 | FileCheck %s
+
+name:            test
+legalized:       true
+regBankSelected: true
+body: |
+  bb.0:
+   liveins: $w0, $w1
+   %bank:gpr(s32) = COPY $w0
+   %class:gpr32(s32) = COPY $w1
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register banks must match ***
+   ; CHECK: instruction: %bank_mismatch:fpr(s32) = G_ASSERT_ZEXT %bank:gpr, 16
+   %bank_mismatch:fpr(s32) = G_ASSERT_ZEXT %bank, 16
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register classes must match ***
+   ; CHECK: instruction: %class_mismatch_gpr:gpr32all(s32) = G_ASSERT_ZEXT %class:gpr32, 16
+   %class_mismatch_gpr:gpr32all(s32) = G_ASSERT_ZEXT %class, 16
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register classes must match ***
+   ; CHECK: instruction: %class_mismatch_fpr:fpr32(s32) = G_ASSERT_ZEXT %class:gpr32, 16
+   %class_mismatch_fpr:fpr32(s32) = G_ASSERT_ZEXT %class, 16
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register banks must match ***
+   ; CHECK: instruction: %dst_has_class_src_has_bank:gpr32all(s32) = G_ASSERT_ZEXT %bank:gpr, 16
+   %dst_has_class_src_has_bank:gpr32all(s32) = G_ASSERT_ZEXT %bank, 16
+
+   ; CHECK: *** Bad machine code: G_ASSERT_ZEXT source and destination register banks must match ***
+   ; CHECK: instruction: %dst_has_bank_src_has_class:gpr(s32) = G_ASSERT_ZEXT %class:gpr32, 16
+   %dst_has_bank_src_has_class:gpr(s32) = G_ASSERT_ZEXT %class, 16
+
+   ; CHECK: *** Bad machine code: Generic instruction cannot have physical register ***
+   ; CHECK: instruction: %implicit_physreg:gpr(s32) = G_ASSERT_ZEXT %class:gpr32, 16, implicit-def $w0
+   %implicit_physreg:gpr(s32) = G_ASSERT_ZEXT %class, 16, implicit-def $w0


        


More information about the llvm-commits mailing list