Skip to content
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

[X86][GlobalISel] Enable G_BRJT #81811

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

RemiSEGARD
Copy link
Contributor

@RemiSEGARD RemiSEGARD commented Feb 15, 2024

  • Legalizer for G_BRJT and G_JUMP_TABLE
  • Instruction Selection for G_BRJT
  • The G_JUMP_TABLE becomes dead at instruction selection. I am unsure that it needs to be used or what to do with it.
    Fixes unable to legalize instruction: G_BRJT #47127

@RemiSEGARD RemiSEGARD marked this pull request as ready for review February 15, 2024 01:15
@llvmbot
Copy link
Member

llvmbot commented Feb 15, 2024

@llvm/pr-subscribers-llvm-globalisel

Author: Rémi SEGARD (RemiSEGARD)

Changes
  • Legalizer for G_BRJT and G_JUMP_TABLE
  • Instruction Selection for G_BRJT
  • The G_JUMP_TABLE becomes dead at instruction selection. I am unsure that it needs to be used or what to do with it.

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

4 Files Affected:

  • (modified) llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp (+42)
  • (modified) llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp (+9)
  • (added) llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir (+84)
  • (added) llvm/test/CodeGen/X86/GlobalISel/select-brjt.mir (+126)
diff --git a/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp b/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp
index 26932ba2c8e242..9cec11e7fcce6d 100644
--- a/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp
+++ b/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp
@@ -107,6 +107,8 @@ class X86InstructionSelector : public InstructionSelector {
                      MachineFunction &MF) const;
   bool selectCondBranch(MachineInstr &I, MachineRegisterInfo &MRI,
                         MachineFunction &MF) const;
+  bool selectBrJT(MachineInstr &I, MachineRegisterInfo &MRI,
+                  MachineFunction &MF) const;
   bool selectTurnIntoCOPY(MachineInstr &I, MachineRegisterInfo &MRI,
                           const unsigned DstReg,
                           const TargetRegisterClass *DstRC,
@@ -421,6 +423,8 @@ bool X86InstructionSelector::select(MachineInstr &I) {
     return selectInsert(I, MRI, MF);
   case TargetOpcode::G_BRCOND:
     return selectCondBranch(I, MRI, MF);
+  case TargetOpcode::G_BRJT:
+    return selectBrJT(I, MRI, MF);
   case TargetOpcode::G_IMPLICIT_DEF:
   case TargetOpcode::G_PHI:
     return selectImplicitDefOrPHI(I, MRI);
@@ -1472,6 +1476,44 @@ bool X86InstructionSelector::selectCondBranch(MachineInstr &I,
   return true;
 }
 
+bool X86InstructionSelector::selectBrJT(MachineInstr &I,
+                                        MachineRegisterInfo &MRI,
+                                        MachineFunction &MF) const {
+  assert((I.getOpcode() == TargetOpcode::G_BRJT) && "unexpected instruction");
+
+  auto Dst = I.getOperand(0).getReg();
+  auto DstTy = MRI.getType(I.getOperand(0).getReg());
+  auto JTI = I.getOperand(1).getIndex();
+  auto Idx = I.getOperand(2).getReg();
+  auto NewDst = MRI.createGenericVirtualRegister(MRI.getType(Dst));
+  auto *MMO = MF.getMachineMemOperand(MachinePointerInfo().getJumpTable(MF),
+                                      MachineMemOperand::MOLoad, DstTy,
+                                      Align(DstTy.getSizeInBytes()));
+
+  auto MovOp = STI.is64Bit() ? X86::MOV64rm : X86::MOV32rm;
+  auto JmpOp = STI.is64Bit() ? X86::JMP64r : X86::JMP32r;
+
+  // TODO: Maybe there is a way to use X86AddressMode
+  auto Mov = BuildMI(I.getParent(), I.getDebugLoc(), TII.get(MovOp))
+                 .addDef(NewDst)
+                 .addReg(0)
+                 .addImm(8)
+                 .addReg(Idx)
+                 .addJumpTableIndex(JTI)
+                 .addReg(0)
+                 .addMemOperand(MMO);
+
+  auto Jmp =
+      BuildMI(I.getParent(), I.getDebugLoc(), TII.get(JmpOp)).addReg(NewDst);
+
+  I.removeFromParent();
+
+  if (!constrainSelectedInstRegOperands(*Mov, TII, TRI, RBI) &&
+      !constrainSelectedInstRegOperands(*Jmp, TII, TRI, RBI))
+    return false;
+  return true;
+}
+
 bool X86InstructionSelector::materializeFP(MachineInstr &I,
                                            MachineRegisterInfo &MRI,
                                            MachineFunction &MF) const {
diff --git a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
index 27381dff338e2d..a659d9d850f87e 100644
--- a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
+++ b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
@@ -326,6 +326,15 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI,
 
   getActionDefinitionsBuilder(G_BRCOND).legalFor({s1});
 
+  getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0});
+  getActionDefinitionsBuilder(G_BRJT)
+      .legalIf([=](const LegalityQuery &Query) -> bool {
+        return typePairInSet(0, 1, {{p0, s32}})(Query) ||
+               (Is64Bit && typePairInSet(0, 1, {{p0, s64}})(Query));
+      })
+      .widenScalarOrEltToNextPow2(2, 32)
+      .clampScalar(1, s32, sMaxScalar);
+
   // pointer handling
   const std::initializer_list<LLT> PtrTypes32 = {s1, s8, s16, s32};
   const std::initializer_list<LLT> PtrTypes64 = {s1, s8, s16, s32, s64};
diff --git a/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir b/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir
new file mode 100644
index 00000000000000..b4975cd613ebca
--- /dev/null
+++ b/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir
@@ -0,0 +1,84 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4
+# RUN: llc -mtriple=x86_64-linux-gnu -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=ALL
+# RUN: llc -mtriple=i386-linux-gnu   -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=ALL
+
+---
+name:            test
+alignment:       16
+legalized:       false
+jumpTable:
+  kind:            block-address
+  entries:
+    - id:              0
+      blocks:          [ '%bb.2', '%bb.3', ]
+body:             |
+  ; ALL-LABEL: name: test
+  ; ALL: bb.0.entry:
+  ; ALL-NEXT:   successors: %bb.1(0x40000000), %bb.4(0x40000000)
+  ; ALL-NEXT:   liveins: $edi
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $edi
+  ; ALL-NEXT:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+  ; ALL-NEXT:   [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
+  ; ALL-NEXT:   [[SUB:%[0-9]+]]:_(s32) = G_SUB [[COPY]], [[C1]]
+  ; ALL-NEXT:   [[ICMP:%[0-9]+]]:_(s8) = G_ICMP intpred(ugt), [[SUB]](s32), [[C]]
+  ; ALL-NEXT:   [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ICMP]](s8)
+  ; ALL-NEXT:   G_BRCOND [[TRUNC]](s1), %bb.4
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.1.entry:
+  ; ALL-NEXT:   successors: %bb.2(0x40000000), %bb.3(0x40000000)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT:   [[JUMP_TABLE:%[0-9]+]]:_(p0) = G_JUMP_TABLE %jump-table.0
+  ; ALL-NEXT:   G_BRJT [[JUMP_TABLE]](p0), %jump-table.0, [[SUB]](s32)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.2:
+  ; ALL-NEXT:   successors: %bb.5(0x80000000)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT:   G_BR %bb.5
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.3:
+  ; ALL-NEXT:   successors: %bb.5(0x80000000)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT:   G_BR %bb.5
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.4:
+  ; ALL-NEXT:   successors: %bb.5(0x80000000)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.5:
+  ; ALL-NEXT:   $eax = COPY [[COPY]](s32)
+  ; ALL-NEXT:   RET 0, implicit $eax
+  bb.0.entry:
+    successors: %bb.1(0x40000000), %bb.4(0x40000000)
+    liveins: $edi
+
+    %0:_(s32) = COPY $edi
+    %4:_(s32) = G_CONSTANT i32 4
+    %1:_(s32) = G_CONSTANT i32 0
+    %2:_(s32) = G_SUB %0, %1
+    %5:_(s1) = G_ICMP intpred(ugt), %2(s32), %4
+    G_BRCOND %5(s1), %bb.4
+
+  bb.1.entry:
+    successors: %bb.2(0x1999999a), %bb.3(0x1999999a)
+
+    %6:_(p0) = G_JUMP_TABLE %jump-table.0
+    G_BRJT %6(p0), %jump-table.0, %2(s32)
+
+  bb.2:
+    successors: %bb.5(0x80000000)
+
+    G_BR %bb.5
+
+  bb.3:
+    successors: %bb.5(0x80000000)
+
+    G_BR %bb.5
+
+  bb.4:
+    successors: %bb.5(0x80000000)
+
+  bb.5:
+    $eax = COPY %0(s32)
+    RET 0, implicit $eax
+
+...
diff --git a/llvm/test/CodeGen/X86/GlobalISel/select-brjt.mir b/llvm/test/CodeGen/X86/GlobalISel/select-brjt.mir
new file mode 100644
index 00000000000000..def4e345a274bc
--- /dev/null
+++ b/llvm/test/CodeGen/X86/GlobalISel/select-brjt.mir
@@ -0,0 +1,126 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4
+# RUN: llc -mtriple=x86_64-linux-gnu -run-pass=instruction-select %s -o - | FileCheck %s --check-prefix=X64
+# RUN: llc -mtriple=i386-linux-gnu   -run-pass=instruction-select %s -o - | FileCheck %s --check-prefix=X32
+
+---
+name:            test
+alignment:       16
+exposesReturnsTwice: false
+legalized:       true
+regBankSelected: true
+selected:        false
+jumpTable:
+  kind:            block-address
+  entries:
+    - id:              0
+      blocks:          [ '%bb.2', '%bb.3' ]
+body:             |
+  ; X64-LABEL: name: test
+  ; X64: bb.0.entry:
+  ; X64-NEXT:   successors: %bb.1(0x40000000), %bb.4(0x40000000)
+  ; X64-NEXT:   liveins: $edi
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; X64-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 4
+  ; X64-NEXT:   [[SUB32ri:%[0-9]+]]:gr32 = SUB32ri [[COPY]], 0, implicit-def dead $eflags
+  ; X64-NEXT:   [[COPY1:%[0-9]+]]:gr64_nosp = COPY [[SUB32ri]]
+  ; X64-NEXT:   [[COPY2:%[0-9]+]]:gr32 = COPY [[COPY1]]
+  ; X64-NEXT:   CMP32rr [[COPY2]], [[MOV32ri]], implicit-def $eflags
+  ; X64-NEXT:   [[SETCCr:%[0-9]+]]:gr8 = SETCCr 7, implicit $eflags
+  ; X64-NEXT:   TEST8ri [[SETCCr]], 1, implicit-def $eflags
+  ; X64-NEXT:   JCC_1 %bb.4, 5, implicit $eflags
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.1.entry:
+  ; X64-NEXT:   successors: %bb.2(0x40000000), %bb.3(0x40000000)
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT:   [[MOV64rm:%[0-9]+]]:gr64 = MOV64rm $noreg, 8, [[COPY1]], %jump-table.0, $noreg :: (load (p0) from jump-table)
+  ; X64-NEXT:   JMP64r [[MOV64rm]]
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.2:
+  ; X64-NEXT:   successors: %bb.5(0x80000000)
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT:   JMP_1 %bb.5
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.3:
+  ; X64-NEXT:   successors: %bb.5(0x80000000)
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT:   JMP_1 %bb.5
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.4:
+  ; X64-NEXT:   successors: %bb.5(0x80000000)
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.5:
+  ; X64-NEXT:   $eax = COPY [[COPY]]
+  ; X64-NEXT:   RET 0, implicit $eax
+  ;
+  ; X32-LABEL: name: test
+  ; X32: bb.0.entry:
+  ; X32-NEXT:   successors: %bb.1(0x40000000), %bb.4(0x40000000)
+  ; X32-NEXT:   liveins: $edi
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; X32-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 4
+  ; X32-NEXT:   [[SUB32ri:%[0-9]+]]:gr32_nosp = SUB32ri [[COPY]], 0, implicit-def dead $eflags
+  ; X32-NEXT:   CMP32rr [[SUB32ri]], [[MOV32ri]], implicit-def $eflags
+  ; X32-NEXT:   [[SETCCr:%[0-9]+]]:gr8 = SETCCr 7, implicit $eflags
+  ; X32-NEXT:   TEST8ri [[SETCCr]], 1, implicit-def $eflags
+  ; X32-NEXT:   JCC_1 %bb.4, 5, implicit $eflags
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.1.entry:
+  ; X32-NEXT:   successors: %bb.2(0x40000000), %bb.3(0x40000000)
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT:   [[MOV32rm:%[0-9]+]]:gr32 = MOV32rm $noreg, 8, [[SUB32ri]], %jump-table.0, $noreg :: (load (p0) from jump-table)
+  ; X32-NEXT:   JMP32r [[MOV32rm]]
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.2:
+  ; X32-NEXT:   successors: %bb.5(0x80000000)
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT:   JMP_1 %bb.5
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.3:
+  ; X32-NEXT:   successors: %bb.5(0x80000000)
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT:   JMP_1 %bb.5
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.4:
+  ; X32-NEXT:   successors: %bb.5(0x80000000)
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.5:
+  ; X32-NEXT:   $eax = COPY [[COPY]]
+  ; X32-NEXT:   RET 0, implicit $eax
+  bb.0.entry:
+    successors: %bb.1(0x40000000), %bb.4(0x40000000)
+    liveins: $edi
+
+    %0:gpr(s32) = COPY $edi
+    %4:gpr(s32) = G_CONSTANT i32 4
+    %1:gpr(s32) = G_CONSTANT i32 0
+    %2:gpr(s32) = G_SUB %0, %1
+    %8:gpr(s8) = G_ICMP intpred(ugt), %2(s32), %4
+    %5:gpr(s1) = G_TRUNC %8(s8)
+    G_BRCOND %5(s1), %bb.4
+
+  bb.1.entry:
+    successors: %bb.2(0x40000000), %bb.3(0x40000000)
+
+    %6:gpr(p0) = G_JUMP_TABLE %jump-table.0
+    G_BRJT %6(p0), %jump-table.0, %2(s32)
+
+  bb.2:
+    successors: %bb.5(0x80000000)
+
+    G_BR %bb.5
+
+  bb.3:
+    successors: %bb.5(0x80000000)
+
+    G_BR %bb.5
+
+  bb.4:
+    successors: %bb.5(0x80000000)
+
+  bb.5:
+    $eax = COPY %0(s32)
+    RET 0, implicit $eax
+
+...

@llvmbot
Copy link
Member

llvmbot commented Feb 15, 2024

@llvm/pr-subscribers-backend-x86

Author: Rémi SEGARD (RemiSEGARD)

Changes
  • Legalizer for G_BRJT and G_JUMP_TABLE
  • Instruction Selection for G_BRJT
  • The G_JUMP_TABLE becomes dead at instruction selection. I am unsure that it needs to be used or what to do with it.

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

4 Files Affected:

  • (modified) llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp (+42)
  • (modified) llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp (+9)
  • (added) llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir (+84)
  • (added) llvm/test/CodeGen/X86/GlobalISel/select-brjt.mir (+126)
diff --git a/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp b/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp
index 26932ba2c8e242..9cec11e7fcce6d 100644
--- a/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp
+++ b/llvm/lib/Target/X86/GISel/X86InstructionSelector.cpp
@@ -107,6 +107,8 @@ class X86InstructionSelector : public InstructionSelector {
                      MachineFunction &MF) const;
   bool selectCondBranch(MachineInstr &I, MachineRegisterInfo &MRI,
                         MachineFunction &MF) const;
+  bool selectBrJT(MachineInstr &I, MachineRegisterInfo &MRI,
+                  MachineFunction &MF) const;
   bool selectTurnIntoCOPY(MachineInstr &I, MachineRegisterInfo &MRI,
                           const unsigned DstReg,
                           const TargetRegisterClass *DstRC,
@@ -421,6 +423,8 @@ bool X86InstructionSelector::select(MachineInstr &I) {
     return selectInsert(I, MRI, MF);
   case TargetOpcode::G_BRCOND:
     return selectCondBranch(I, MRI, MF);
+  case TargetOpcode::G_BRJT:
+    return selectBrJT(I, MRI, MF);
   case TargetOpcode::G_IMPLICIT_DEF:
   case TargetOpcode::G_PHI:
     return selectImplicitDefOrPHI(I, MRI);
@@ -1472,6 +1476,44 @@ bool X86InstructionSelector::selectCondBranch(MachineInstr &I,
   return true;
 }
 
+bool X86InstructionSelector::selectBrJT(MachineInstr &I,
+                                        MachineRegisterInfo &MRI,
+                                        MachineFunction &MF) const {
+  assert((I.getOpcode() == TargetOpcode::G_BRJT) && "unexpected instruction");
+
+  auto Dst = I.getOperand(0).getReg();
+  auto DstTy = MRI.getType(I.getOperand(0).getReg());
+  auto JTI = I.getOperand(1).getIndex();
+  auto Idx = I.getOperand(2).getReg();
+  auto NewDst = MRI.createGenericVirtualRegister(MRI.getType(Dst));
+  auto *MMO = MF.getMachineMemOperand(MachinePointerInfo().getJumpTable(MF),
+                                      MachineMemOperand::MOLoad, DstTy,
+                                      Align(DstTy.getSizeInBytes()));
+
+  auto MovOp = STI.is64Bit() ? X86::MOV64rm : X86::MOV32rm;
+  auto JmpOp = STI.is64Bit() ? X86::JMP64r : X86::JMP32r;
+
+  // TODO: Maybe there is a way to use X86AddressMode
+  auto Mov = BuildMI(I.getParent(), I.getDebugLoc(), TII.get(MovOp))
+                 .addDef(NewDst)
+                 .addReg(0)
+                 .addImm(8)
+                 .addReg(Idx)
+                 .addJumpTableIndex(JTI)
+                 .addReg(0)
+                 .addMemOperand(MMO);
+
+  auto Jmp =
+      BuildMI(I.getParent(), I.getDebugLoc(), TII.get(JmpOp)).addReg(NewDst);
+
+  I.removeFromParent();
+
+  if (!constrainSelectedInstRegOperands(*Mov, TII, TRI, RBI) &&
+      !constrainSelectedInstRegOperands(*Jmp, TII, TRI, RBI))
+    return false;
+  return true;
+}
+
 bool X86InstructionSelector::materializeFP(MachineInstr &I,
                                            MachineRegisterInfo &MRI,
                                            MachineFunction &MF) const {
diff --git a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
index 27381dff338e2d..a659d9d850f87e 100644
--- a/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
+++ b/llvm/lib/Target/X86/GISel/X86LegalizerInfo.cpp
@@ -326,6 +326,15 @@ X86LegalizerInfo::X86LegalizerInfo(const X86Subtarget &STI,
 
   getActionDefinitionsBuilder(G_BRCOND).legalFor({s1});
 
+  getActionDefinitionsBuilder(G_JUMP_TABLE).legalFor({p0});
+  getActionDefinitionsBuilder(G_BRJT)
+      .legalIf([=](const LegalityQuery &Query) -> bool {
+        return typePairInSet(0, 1, {{p0, s32}})(Query) ||
+               (Is64Bit && typePairInSet(0, 1, {{p0, s64}})(Query));
+      })
+      .widenScalarOrEltToNextPow2(2, 32)
+      .clampScalar(1, s32, sMaxScalar);
+
   // pointer handling
   const std::initializer_list<LLT> PtrTypes32 = {s1, s8, s16, s32};
   const std::initializer_list<LLT> PtrTypes64 = {s1, s8, s16, s32, s64};
diff --git a/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir b/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir
new file mode 100644
index 00000000000000..b4975cd613ebca
--- /dev/null
+++ b/llvm/test/CodeGen/X86/GlobalISel/legalize-brjt.mir
@@ -0,0 +1,84 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4
+# RUN: llc -mtriple=x86_64-linux-gnu -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=ALL
+# RUN: llc -mtriple=i386-linux-gnu   -run-pass=legalizer %s -o - | FileCheck %s --check-prefix=ALL
+
+---
+name:            test
+alignment:       16
+legalized:       false
+jumpTable:
+  kind:            block-address
+  entries:
+    - id:              0
+      blocks:          [ '%bb.2', '%bb.3', ]
+body:             |
+  ; ALL-LABEL: name: test
+  ; ALL: bb.0.entry:
+  ; ALL-NEXT:   successors: %bb.1(0x40000000), %bb.4(0x40000000)
+  ; ALL-NEXT:   liveins: $edi
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT:   [[COPY:%[0-9]+]]:_(s32) = COPY $edi
+  ; ALL-NEXT:   [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 4
+  ; ALL-NEXT:   [[C1:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
+  ; ALL-NEXT:   [[SUB:%[0-9]+]]:_(s32) = G_SUB [[COPY]], [[C1]]
+  ; ALL-NEXT:   [[ICMP:%[0-9]+]]:_(s8) = G_ICMP intpred(ugt), [[SUB]](s32), [[C]]
+  ; ALL-NEXT:   [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[ICMP]](s8)
+  ; ALL-NEXT:   G_BRCOND [[TRUNC]](s1), %bb.4
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.1.entry:
+  ; ALL-NEXT:   successors: %bb.2(0x40000000), %bb.3(0x40000000)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT:   [[JUMP_TABLE:%[0-9]+]]:_(p0) = G_JUMP_TABLE %jump-table.0
+  ; ALL-NEXT:   G_BRJT [[JUMP_TABLE]](p0), %jump-table.0, [[SUB]](s32)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.2:
+  ; ALL-NEXT:   successors: %bb.5(0x80000000)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT:   G_BR %bb.5
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.3:
+  ; ALL-NEXT:   successors: %bb.5(0x80000000)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT:   G_BR %bb.5
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.4:
+  ; ALL-NEXT:   successors: %bb.5(0x80000000)
+  ; ALL-NEXT: {{  $}}
+  ; ALL-NEXT: bb.5:
+  ; ALL-NEXT:   $eax = COPY [[COPY]](s32)
+  ; ALL-NEXT:   RET 0, implicit $eax
+  bb.0.entry:
+    successors: %bb.1(0x40000000), %bb.4(0x40000000)
+    liveins: $edi
+
+    %0:_(s32) = COPY $edi
+    %4:_(s32) = G_CONSTANT i32 4
+    %1:_(s32) = G_CONSTANT i32 0
+    %2:_(s32) = G_SUB %0, %1
+    %5:_(s1) = G_ICMP intpred(ugt), %2(s32), %4
+    G_BRCOND %5(s1), %bb.4
+
+  bb.1.entry:
+    successors: %bb.2(0x1999999a), %bb.3(0x1999999a)
+
+    %6:_(p0) = G_JUMP_TABLE %jump-table.0
+    G_BRJT %6(p0), %jump-table.0, %2(s32)
+
+  bb.2:
+    successors: %bb.5(0x80000000)
+
+    G_BR %bb.5
+
+  bb.3:
+    successors: %bb.5(0x80000000)
+
+    G_BR %bb.5
+
+  bb.4:
+    successors: %bb.5(0x80000000)
+
+  bb.5:
+    $eax = COPY %0(s32)
+    RET 0, implicit $eax
+
+...
diff --git a/llvm/test/CodeGen/X86/GlobalISel/select-brjt.mir b/llvm/test/CodeGen/X86/GlobalISel/select-brjt.mir
new file mode 100644
index 00000000000000..def4e345a274bc
--- /dev/null
+++ b/llvm/test/CodeGen/X86/GlobalISel/select-brjt.mir
@@ -0,0 +1,126 @@
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 4
+# RUN: llc -mtriple=x86_64-linux-gnu -run-pass=instruction-select %s -o - | FileCheck %s --check-prefix=X64
+# RUN: llc -mtriple=i386-linux-gnu   -run-pass=instruction-select %s -o - | FileCheck %s --check-prefix=X32
+
+---
+name:            test
+alignment:       16
+exposesReturnsTwice: false
+legalized:       true
+regBankSelected: true
+selected:        false
+jumpTable:
+  kind:            block-address
+  entries:
+    - id:              0
+      blocks:          [ '%bb.2', '%bb.3' ]
+body:             |
+  ; X64-LABEL: name: test
+  ; X64: bb.0.entry:
+  ; X64-NEXT:   successors: %bb.1(0x40000000), %bb.4(0x40000000)
+  ; X64-NEXT:   liveins: $edi
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; X64-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 4
+  ; X64-NEXT:   [[SUB32ri:%[0-9]+]]:gr32 = SUB32ri [[COPY]], 0, implicit-def dead $eflags
+  ; X64-NEXT:   [[COPY1:%[0-9]+]]:gr64_nosp = COPY [[SUB32ri]]
+  ; X64-NEXT:   [[COPY2:%[0-9]+]]:gr32 = COPY [[COPY1]]
+  ; X64-NEXT:   CMP32rr [[COPY2]], [[MOV32ri]], implicit-def $eflags
+  ; X64-NEXT:   [[SETCCr:%[0-9]+]]:gr8 = SETCCr 7, implicit $eflags
+  ; X64-NEXT:   TEST8ri [[SETCCr]], 1, implicit-def $eflags
+  ; X64-NEXT:   JCC_1 %bb.4, 5, implicit $eflags
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.1.entry:
+  ; X64-NEXT:   successors: %bb.2(0x40000000), %bb.3(0x40000000)
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT:   [[MOV64rm:%[0-9]+]]:gr64 = MOV64rm $noreg, 8, [[COPY1]], %jump-table.0, $noreg :: (load (p0) from jump-table)
+  ; X64-NEXT:   JMP64r [[MOV64rm]]
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.2:
+  ; X64-NEXT:   successors: %bb.5(0x80000000)
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT:   JMP_1 %bb.5
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.3:
+  ; X64-NEXT:   successors: %bb.5(0x80000000)
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT:   JMP_1 %bb.5
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.4:
+  ; X64-NEXT:   successors: %bb.5(0x80000000)
+  ; X64-NEXT: {{  $}}
+  ; X64-NEXT: bb.5:
+  ; X64-NEXT:   $eax = COPY [[COPY]]
+  ; X64-NEXT:   RET 0, implicit $eax
+  ;
+  ; X32-LABEL: name: test
+  ; X32: bb.0.entry:
+  ; X32-NEXT:   successors: %bb.1(0x40000000), %bb.4(0x40000000)
+  ; X32-NEXT:   liveins: $edi
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT:   [[COPY:%[0-9]+]]:gr32 = COPY $edi
+  ; X32-NEXT:   [[MOV32ri:%[0-9]+]]:gr32 = MOV32ri 4
+  ; X32-NEXT:   [[SUB32ri:%[0-9]+]]:gr32_nosp = SUB32ri [[COPY]], 0, implicit-def dead $eflags
+  ; X32-NEXT:   CMP32rr [[SUB32ri]], [[MOV32ri]], implicit-def $eflags
+  ; X32-NEXT:   [[SETCCr:%[0-9]+]]:gr8 = SETCCr 7, implicit $eflags
+  ; X32-NEXT:   TEST8ri [[SETCCr]], 1, implicit-def $eflags
+  ; X32-NEXT:   JCC_1 %bb.4, 5, implicit $eflags
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.1.entry:
+  ; X32-NEXT:   successors: %bb.2(0x40000000), %bb.3(0x40000000)
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT:   [[MOV32rm:%[0-9]+]]:gr32 = MOV32rm $noreg, 8, [[SUB32ri]], %jump-table.0, $noreg :: (load (p0) from jump-table)
+  ; X32-NEXT:   JMP32r [[MOV32rm]]
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.2:
+  ; X32-NEXT:   successors: %bb.5(0x80000000)
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT:   JMP_1 %bb.5
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.3:
+  ; X32-NEXT:   successors: %bb.5(0x80000000)
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT:   JMP_1 %bb.5
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.4:
+  ; X32-NEXT:   successors: %bb.5(0x80000000)
+  ; X32-NEXT: {{  $}}
+  ; X32-NEXT: bb.5:
+  ; X32-NEXT:   $eax = COPY [[COPY]]
+  ; X32-NEXT:   RET 0, implicit $eax
+  bb.0.entry:
+    successors: %bb.1(0x40000000), %bb.4(0x40000000)
+    liveins: $edi
+
+    %0:gpr(s32) = COPY $edi
+    %4:gpr(s32) = G_CONSTANT i32 4
+    %1:gpr(s32) = G_CONSTANT i32 0
+    %2:gpr(s32) = G_SUB %0, %1
+    %8:gpr(s8) = G_ICMP intpred(ugt), %2(s32), %4
+    %5:gpr(s1) = G_TRUNC %8(s8)
+    G_BRCOND %5(s1), %bb.4
+
+  bb.1.entry:
+    successors: %bb.2(0x40000000), %bb.3(0x40000000)
+
+    %6:gpr(p0) = G_JUMP_TABLE %jump-table.0
+    G_BRJT %6(p0), %jump-table.0, %2(s32)
+
+  bb.2:
+    successors: %bb.5(0x80000000)
+
+    G_BR %bb.5
+
+  bb.3:
+    successors: %bb.5(0x80000000)
+
+    G_BR %bb.5
+
+  bb.4:
+    successors: %bb.5(0x80000000)
+
+  bb.5:
+    $eax = COPY %0(s32)
+    RET 0, implicit $eax
+
+...

@RKSimon RKSimon requested review from RKSimon and e-kud February 15, 2024 09:47
@e-kud
Copy link
Contributor

e-kud commented Feb 15, 2024

@RemiSEGARD Thanks! Let me check some tests where I've seen fallback due to G_BRJT.
Tagging @turinevgeny as he may have some related work.
Also I think we should mention in the description that it closes #47127

@RemiSEGARD RemiSEGARD force-pushed the x86-gisel-select-brjt branch from 3863c66 to 2f12cf0 Compare February 15, 2024 16:28
@e-kud
Copy link
Contributor

e-kud commented Feb 15, 2024

@RemiSEGARD The selected instruction doesn't work in pie mode as we use absolute address of .rodata. With -no-pie it works fine. So, we need to consider TLI.isJumpTableRelative during selection.

$ cat test.c
int square(int num, int x) {
        switch (num) {
                case 1:
                        return x * x;
                case 2:
                        return x + x;
                case 3:
                        return 0;
                case 4:
                        return 1;
        }
        return -1;
}
int main(int argc, char **argv) {
    return square(argc, argc);
}
$ clang -mllvm -global-isel=1 test.c -pie
/usr/bin/ld: /tmp/test-5b004b.o: relocation R_X86_64_32S against `.rodata' can not be used when making a PIE object; recompile with -fPIE
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I see that SelectionDAG expands br_jt into address calculation and brind. We can do the same by transforming G_BRJT into G_BRINDIRECT. Then we can add GINodeEquiv<G_BRINDIRECT, brind> and we should be able to match existing patterns from SelectionDAG avoiding manual selection. What do you think?

@topperc
Copy link
Collaborator

topperc commented Feb 15, 2024

I have a patch for RISC-V to legalize BR_JT to address calculation and BR_INDIRECT. #73711 some of that could be made generic. I just haven't had time to work on GISel recently.

@RemiSEGARD
Copy link
Contributor Author

@e-kud @topperc Thanks for the feedback! I like the idea of lowering G_BRJT to simpler instructions during the legalizer. I'm currently very unfamiliar with SelectionDag so i was just trying to replicate the instructions it was selecting.
I will look into your suggestions and see what I can do!

@RemiSEGARD RemiSEGARD force-pushed the x86-gisel-select-brjt branch from 2f12cf0 to 3d621ee Compare February 17, 2024 20:46
Copy link

github-actions bot commented Feb 17, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@RemiSEGARD RemiSEGARD force-pushed the x86-gisel-select-brjt branch from 3d621ee to 22d9a0f Compare February 17, 2024 20:51
Copy link
Contributor

@e-kud e-kud left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RemiSEGARD could you please add an end-to-end test e.g. test/Target/X86/isel-brjt.ll for all selectors similarly to other isel-*.ll tests.

@RemiSEGARD
Copy link
Contributor Author

RemiSEGARD commented Mar 5, 2024

@e-kud Sorry for the late reply, I haven't had much time recently. I applied your suggestions.

I also noticed that when compiling your example with -m32 the legalization of G_BRJT fails because MJTI->getEntryKind() returns EK_Custom32 which seems to be using a global offset table in SelectionDAG. I haven't had time to look into how to handle that for now. If you have some tips on where to look it would be much appreciated!

@e-kud
Copy link
Contributor

e-kud commented Mar 14, 2024

@e-kud Sorry for the late reply, I haven't had much time recently. I applied your suggestions.

I also noticed that when compiling your example with -m32 the legalization of G_BRJT fails because MJTI->getEntryKind() returns EK_Custom32 which seems to be using a global offset table in SelectionDAG. I haven't had time to look into how to handle that for now. If you have some tips on where to look it would be much appreciated!

In case of -m32 we need to load the addresses using GOT. You need to create an add similar to https://godbolt.org/z/M954fjj6d, using GlobalBaseReg and MO_GOTOFF flag. Notice, that load of base GOT address is generated by CGBR pass that is hidden from print-after-all.


MachineMemOperand *MMO = MF.getMachineMemOperand(
MachinePointerInfo::getJumpTable(MF), MachineMemOperand::MOLoad,
PtrTy.getSizeInBytes(), Align(EntrySize));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
PtrTy.getSizeInBytes(), Align(EntrySize));
EntrySize, Align(EntrySize));

PtrTy.getSizeInBytes(), Align(EntrySize));

auto ShiftAmt =
MIB.buildConstant(IdxTy, Log2_32(MJTI->getEntrySize(MF.getDataLayout())));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
MIB.buildConstant(IdxTy, Log2_32(MJTI->getEntrySize(MF.getDataLayout())));
MIB.buildConstant(IdxTy, Log2_32(EntrySize));

[[fallthrough]];
case MachineJumpTableInfo::EK_LabelDifference32:
auto Load = MIB.buildLoadInstr(
TargetOpcode::G_LOAD, LLT::scalar(PtrTy.getSizeInBits()), Target, *MMO);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TargetOpcode::G_LOAD, LLT::scalar(PtrTy.getSizeInBits()), Target, *MMO);
TargetOpcode::G_LOAD, LLT::scalar(EntrySize * 8), Target, *MMO);

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.

unable to legalize instruction: G_BRJT
4 participants