Skip to content

[GlobalISel][ARM] Support missing case for G_CONSTANT #79022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

spavloff
Copy link
Collaborator

The instruction:

%0:gprb(s32) = G_CONSTANT i32 -1

now cannot be selected on ARM. In DAG selector the similar instruction is selected with the help of mod_imm_not, which is a PatLeaf node. This node is specified with a transformation helper (imm_not_XFORM), that creates a new SDValue. Such rules are not processed by TableGen now and the generated match table does not contain records that could be used to select such instruction.

This change implements selection for this case.

The instruction:

    %0:gprb(s32) = G_CONSTANT i32 -1

now cannot be selected on ARM. In DAG selector the similar instruction
is selected with the help of `mod_imm_not`, which is a PatLeaf node.
This node is specified with a transformation helper (imm_not_XFORM),
that creates a new SDValue. Such rules are not processed by TableGen now
and the generated match table does not contain records that could be
used to select such instruction.

This change implements selection for this case.
@llvmbot
Copy link
Member

llvmbot commented Jan 22, 2024

@llvm/pr-subscribers-llvm-globalisel

@llvm/pr-subscribers-backend-arm

Author: Serge Pavlov (spavloff)

Changes

The instruction:

%0:gprb(s32) = G_CONSTANT i32 -1

now cannot be selected on ARM. In DAG selector the similar instruction is selected with the help of mod_imm_not, which is a PatLeaf node. This node is specified with a transformation helper (imm_not_XFORM), that creates a new SDValue. Such rules are not processed by TableGen now and the generated match table does not contain records that could be used to select such instruction.

This change implements selection for this case.


Full diff: https://github.com/llvm/llvm-project/pull/79022.diff

2 Files Affected:

  • (modified) llvm/lib/Target/ARM/ARMInstructionSelector.cpp (+49-22)
  • (added) llvm/test/CodeGen/ARM/GlobalISel/select-imm.mir (+25)
diff --git a/llvm/lib/Target/ARM/ARMInstructionSelector.cpp b/llvm/lib/Target/ARM/ARMInstructionSelector.cpp
index f391058a70514c..f4c5513d857b58 100644
--- a/llvm/lib/Target/ARM/ARMInstructionSelector.cpp
+++ b/llvm/lib/Target/ARM/ARMInstructionSelector.cpp
@@ -60,6 +60,7 @@ class ARMInstructionSelector : public InstructionSelector {
   bool selectGlobal(MachineInstrBuilder &MIB, MachineRegisterInfo &MRI) const;
   bool selectSelect(MachineInstrBuilder &MIB, MachineRegisterInfo &MRI) const;
   bool selectShift(unsigned ShiftOpc, MachineInstrBuilder &MIB) const;
+  bool selectConstant(MachineInstrBuilder &MIB) const;
 
   // Check if the types match and both operands have the expected size and
   // register bank.
@@ -810,6 +811,31 @@ bool ARMInstructionSelector::selectShift(unsigned ShiftOpc,
   return constrainSelectedInstRegOperands(*MIB, TII, TRI, RBI);
 }
 
+bool ARMInstructionSelector::selectConstant(MachineInstrBuilder &MIB) const {
+  MachineInstr &MI = *MIB;
+  assert(MI.getOpcode() == TargetOpcode::G_CONSTANT && "Expected G_CONSTANT");
+
+  auto &Val = MI.getOperand(1);
+  uint32_t ImmValue;
+  if (Val.isImm())
+    ImmValue = Val.getImm();
+  else if (Val.isCImm())
+    ImmValue = Val.getCImm()->getZExtValue();
+  else
+    return false;
+
+  // Process nodes didn't handled by TableGen-based selector.
+  if (!STI.isThumb()) {
+    if (int Imm12 = ARM_AM::getSOImmVal(~ImmValue); Imm12 != -1) {
+      MI.setDesc(TII.get(ARM::MVNi));
+      Val.ChangeToImmediate(Imm12);
+      MIB.add(predOps(ARMCC::AL)).add(condCodeOp());
+      return constrainSelectedInstRegOperands(MI, TII, TRI, RBI);
+    }
+  }
+  return false;
+}
+
 void ARMInstructionSelector::renderVFPF32Imm(
   MachineInstrBuilder &NewInstBuilder, const MachineInstr &OldInst,
   int OpIdx) const {
@@ -958,33 +984,34 @@ bool ARMInstructionSelector::select(MachineInstr &I) {
     I.setDesc(TII.get(COPY));
     return selectCopy(I, TII, MRI, TRI, RBI);
   }
-  case G_CONSTANT: {
-    if (!MRI.getType(I.getOperand(0).getReg()).isPointer()) {
-      // Non-pointer constants should be handled by TableGen.
-      LLVM_DEBUG(dbgs() << "Unsupported constant type\n");
-      return false;
-    }
-
-    auto &Val = I.getOperand(1);
-    if (Val.isCImm()) {
-      if (!Val.getCImm()->isZero()) {
-        LLVM_DEBUG(dbgs() << "Unsupported pointer constant value\n");
+  case G_CONSTANT:
+    if (!selectConstant(MIB)) {
+      auto &Val = MIB->getOperand(1);
+      if (!MRI.getType(I.getOperand(0).getReg()).isPointer()) {
+        // Non-pointer constants should be handled by TableGen.
+        LLVM_DEBUG(dbgs() << "Unsupported constant type\n");
         return false;
       }
-      Val.ChangeToImmediate(0);
-    } else {
-      assert(Val.isImm() && "Unexpected operand for G_CONSTANT");
-      if (Val.getImm() != 0) {
-        LLVM_DEBUG(dbgs() << "Unsupported pointer constant value\n");
-        return false;
+
+      if (Val.isCImm()) {
+        if (!Val.getCImm()->isZero()) {
+          LLVM_DEBUG(dbgs() << "Unsupported pointer constant value\n");
+          return false;
+        }
+        Val.ChangeToImmediate(0);
+      } else {
+        assert(Val.isImm() && "Unexpected operand for G_CONSTANT");
+        if (Val.getImm() != 0) {
+          LLVM_DEBUG(dbgs() << "Unsupported pointer constant value\n");
+          return false;
+        }
       }
-    }
 
-    assert(!STI.isThumb() && "Unsupported subtarget");
-    I.setDesc(TII.get(ARM::MOVi));
-    MIB.add(predOps(ARMCC::AL)).add(condCodeOp());
+      assert(!STI.isThumb() && "Unsupported subtarget");
+      I.setDesc(TII.get(ARM::MOVi));
+      MIB.add(predOps(ARMCC::AL)).add(condCodeOp());
+    }
     break;
-  }
   case G_FCONSTANT: {
     // Load from constant pool
     unsigned Size = MRI.getType(I.getOperand(0).getReg()).getSizeInBits() / 8;
diff --git a/llvm/test/CodeGen/ARM/GlobalISel/select-imm.mir b/llvm/test/CodeGen/ARM/GlobalISel/select-imm.mir
new file mode 100644
index 00000000000000..59e983bb8c46b9
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/GlobalISel/select-imm.mir
@@ -0,0 +1,25 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4
+# RUN: llc -mtriple arm-- -mattr=+v6 -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
+
+---
+name:            get_const_01
+legalized:       true
+regBankSelected: true
+selected:        false
+tracksRegLiveness: true
+registers:
+  - { id: 0, class: gprb }
+body:             |
+  bb.0:
+    liveins: $r0
+    ; CHECK-LABEL: name: get_const_01
+    ; CHECK: liveins: $r0
+    ; CHECK-NEXT: {{  $}}
+    ; CHECK-NEXT: [[MVNi:%[0-9]+]]:gpr = MVNi 0, 14 /* CC::al */, $noreg, $noreg
+    ; CHECK-NEXT: $r0 = COPY [[MVNi]]
+    ; CHECK-NEXT: MOVPCLR 14 /* CC::al */, $noreg, implicit $r0
+    %0:gprb(s32) = G_CONSTANT i32 -1
+    $r0 = COPY %0(s32)
+    MOVPCLR 14 /* CC::al */, $noreg, implicit $r0
+
+...

@spavloff
Copy link
Collaborator Author

Ping.

@arsenm
Copy link
Contributor

arsenm commented Jan 31, 2024

Instead of switching to manual selection, can you implement the mapping from SDNodeXForm to a custom renderer?

@spavloff
Copy link
Collaborator Author

spavloff commented Feb 3, 2024

Implemented solution based on custom rendere: #80555.

@spavloff spavloff closed this Feb 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants