[llvm] [X86][GISEL] Enable Pre Legalizer Combiner (PR #172204)

via llvm-commits llvm-commits at lists.llvm.org
Sun Dec 14 04:04:54 PST 2025


https://github.com/mahesh-attarde created https://github.com/llvm/llvm-project/pull/172204

This patch enables Pre-legalization Combiner for X86 Target. It includes basic bring up with intent to cover non-regressing support from all_combines.

>From 719c80ea417491375068e60756e8fe7b5a1bd151 Mon Sep 17 00:00:00 2001
From: mattarde <mattarde at intel.com>
Date: Sun, 14 Dec 2025 04:02:40 -0800
Subject: [PATCH] [X86][GISEL] Enable Pre Legalizer Combiner

---
 llvm/lib/Target/X86/CMakeLists.txt            |   3 +
 .../X86/GISel/X86PreLegalizerCombiner.cpp     | 183 ++++++++++++++++++
 llvm/lib/Target/X86/X86.h                     |   2 +
 llvm/lib/Target/X86/X86.td                    |   5 +
 llvm/lib/Target/X86/X86Combine.td             |  20 ++
 llvm/lib/Target/X86/X86TargetMachine.cpp      |   8 +
 .../X86/GlobalISel/stacksave-stackrestore.ll  |   7 +-
 .../switch-bit-test-unreachable-default.ll    |   3 +-
 8 files changed, 225 insertions(+), 6 deletions(-)
 create mode 100644 llvm/lib/Target/X86/GISel/X86PreLegalizerCombiner.cpp
 create mode 100644 llvm/lib/Target/X86/X86Combine.td

diff --git a/llvm/lib/Target/X86/CMakeLists.txt b/llvm/lib/Target/X86/CMakeLists.txt
index 434a6d2c3553f..f2880d6c6ea5e 100644
--- a/llvm/lib/Target/X86/CMakeLists.txt
+++ b/llvm/lib/Target/X86/CMakeLists.txt
@@ -19,6 +19,8 @@ tablegen(LLVM X86GenRegisterBank.inc -gen-register-bank)
 tablegen(LLVM X86GenRegisterInfo.inc -gen-register-info)
 tablegen(LLVM X86GenSubtargetInfo.inc -gen-subtarget)
 tablegen(LLVM X86GenFoldTables.inc -gen-x86-fold-tables -asmwriternum=1)
+tablegen(LLVM X86GenPreLegalizeGICombiner.inc -gen-global-isel-combiner
+              -combiners="X86PreLegalizerCombiner")
 
 add_public_tablegen_target(X86CommonTableGen)
 
@@ -87,6 +89,7 @@ set(sources
   GISel/X86CallLowering.cpp
   GISel/X86InstructionSelector.cpp
   GISel/X86LegalizerInfo.cpp
+  GISel/X86PreLegalizerCombiner.cpp
   GISel/X86RegisterBankInfo.cpp
   )
 
diff --git a/llvm/lib/Target/X86/GISel/X86PreLegalizerCombiner.cpp b/llvm/lib/Target/X86/GISel/X86PreLegalizerCombiner.cpp
new file mode 100644
index 0000000000000..619eda9149baa
--- /dev/null
+++ b/llvm/lib/Target/X86/GISel/X86PreLegalizerCombiner.cpp
@@ -0,0 +1,183 @@
+//===---------------- X86PreLegalizerCombiner.cpp -------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This pass does combining of machine instructions at the generic MI level,
+/// before the legalizer.
+///
+//===----------------------------------------------------------------------===//
+#include "X86.h"
+#include "X86TargetMachine.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/GIMatchTableExecutorImpl.h"
+#include "llvm/CodeGen/GlobalISel/GISelValueTracking.h"
+#include "llvm/CodeGen/GlobalISel/LegalizerInfo.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/MachineFunction.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/IR/Instructions.h"
+
+#define GET_GICOMBINER_DEPS
+#include "X86GenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_DEPS
+
+#define DEBUG_TYPE "x86-prelegalizer-combiner"
+
+using namespace llvm;
+using namespace MIPatternMatch;
+
+namespace {
+
+#define GET_GICOMBINER_TYPES
+#include "X86GenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_TYPES
+
+class X86PreLegalizerCombinerImpl : public Combiner {
+protected:
+  const CombinerHelper Helper;
+  const X86PreLegalizerCombinerImplRuleConfig &RuleConfig;
+  const X86Subtarget &STI;
+
+public:
+  X86PreLegalizerCombinerImpl(
+      MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
+      GISelValueTracking &VT, GISelCSEInfo *CSEInfo,
+      const X86PreLegalizerCombinerImplRuleConfig &RuleConfig,
+      const X86Subtarget &STI, MachineDominatorTree *MDT,
+      const LegalizerInfo *LI);
+
+  static const char *getName() { return "X86PreLegalizerCombiner"; }
+
+  bool tryCombineAll(MachineInstr &I) const override;
+
+  bool tryCombineAllImpl(MachineInstr &I) const;
+
+private:
+#define GET_GICOMBINER_CLASS_MEMBERS
+#include "X86GenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CLASS_MEMBERS
+};
+
+#define GET_GICOMBINER_IMPL
+#include "X86GenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_IMPL
+
+X86PreLegalizerCombinerImpl::X86PreLegalizerCombinerImpl(
+    MachineFunction &MF, CombinerInfo &CInfo, const TargetPassConfig *TPC,
+    GISelValueTracking &VT, GISelCSEInfo *CSEInfo,
+    const X86PreLegalizerCombinerImplRuleConfig &RuleConfig,
+    const X86Subtarget &STI, MachineDominatorTree *MDT, const LegalizerInfo *LI)
+    : Combiner(MF, CInfo, TPC, &VT, CSEInfo),
+      Helper(Observer, B, /*IsPreLegalize*/ true, &VT, MDT, LI),
+      RuleConfig(RuleConfig), STI(STI),
+#define GET_GICOMBINER_CONSTRUCTOR_INITS
+#include "X86GenPreLegalizeGICombiner.inc"
+#undef GET_GICOMBINER_CONSTRUCTOR_INITS
+{
+}
+
+bool X86PreLegalizerCombinerImpl::tryCombineAll(MachineInstr &MI) const {
+  if (tryCombineAllImpl(MI))
+    return true;
+  LLVM_DEBUG(dbgs() << "\nNo table match found.\nTry Custom Combine for "
+                    << MI);
+  return false;
+}
+
+class X86PreLegalizerCombiner : public MachineFunctionPass {
+public:
+  static char ID;
+
+  X86PreLegalizerCombiner();
+
+  StringRef getPassName() const override { return "X86PreLegalizerCombiner"; }
+
+  bool runOnMachineFunction(MachineFunction &MF) override;
+
+  void getAnalysisUsage(AnalysisUsage &AU) const override;
+
+private:
+  X86PreLegalizerCombinerImplRuleConfig RuleConfig;
+};
+} // end anonymous namespace
+
+void X86PreLegalizerCombiner::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);
+}
+
+X86PreLegalizerCombiner::X86PreLegalizerCombiner() : MachineFunctionPass(ID) {
+  if (!RuleConfig.parseCommandLineOption())
+    report_fatal_error("Invalid rule identifier");
+}
+
+bool X86PreLegalizerCombiner::runOnMachineFunction(MachineFunction &MF) {
+  if (MF.getProperties().hasFailedISel())
+    return false;
+  auto &TPC = getAnalysis<TargetPassConfig>();
+
+  // Enable CSE.
+  GISelCSEAnalysisWrapper &Wrapper =
+      getAnalysis<GISelCSEAnalysisWrapperPass>().getCSEWrapper();
+  auto *CSEInfo = &Wrapper.get(TPC.getCSEConfig());
+
+  const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();
+  const auto *LI = ST.getLegalizerInfo();
+
+  const Function &F = MF.getFunction();
+  bool EnableOpt =
+      MF.getTarget().getOptLevel() != CodeGenOptLevel::None && !skipFunction(F);
+  GISelValueTracking *VT =
+      &getAnalysis<GISelValueTrackingAnalysisLegacy>().get(MF);
+  MachineDominatorTree *MDT =
+      &getAnalysis<MachineDominatorTreeWrapperPass>().getDomTree();
+  CombinerInfo CInfo(/*AllowIllegalOps*/ true, /*ShouldLegalizeIllegal*/ false,
+                     /*LegalizerInfo*/ nullptr, EnableOpt, F.hasOptSize(),
+                     F.hasMinSize());
+  // Disable fixed-point iteration to reduce compile-time
+  CInfo.MaxIterations = 1;
+  CInfo.ObserverLvl = CombinerInfo::ObserverLevel::SinglePass;
+  // This is the first Combiner, so the input IR might contain dead
+  // instructions.
+  CInfo.EnableFullDCE = true;
+  X86PreLegalizerCombinerImpl Impl(MF, CInfo, &TPC, *VT, CSEInfo, RuleConfig,
+                                   ST, MDT, LI);
+  return Impl.combineMachineInstrs();
+}
+
+char X86PreLegalizerCombiner::ID = 0;
+INITIALIZE_PASS_BEGIN(X86PreLegalizerCombiner, DEBUG_TYPE,
+                      "Combine X86 machine instrs before legalization", false,
+                      false)
+INITIALIZE_PASS_DEPENDENCY(TargetPassConfig)
+INITIALIZE_PASS_DEPENDENCY(GISelValueTrackingAnalysisLegacy)
+INITIALIZE_PASS_DEPENDENCY(GISelCSEAnalysisWrapperPass)
+INITIALIZE_PASS_END(X86PreLegalizerCombiner, DEBUG_TYPE,
+                    "Combine X86 machine instrs before legalization", false,
+                    false)
+
+namespace llvm {
+FunctionPass *createX86PreLegalizerCombiner() {
+  return new X86PreLegalizerCombiner();
+}
+} // end namespace llvm
diff --git a/llvm/lib/Target/X86/X86.h b/llvm/lib/Target/X86/X86.h
index 97848bec7127e..d7c4a641c643c 100644
--- a/llvm/lib/Target/X86/X86.h
+++ b/llvm/lib/Target/X86/X86.h
@@ -220,6 +220,7 @@ InstructionSelector *createX86InstructionSelector(const X86TargetMachine &TM,
                                                   const X86Subtarget &,
                                                   const X86RegisterBankInfo &);
 
+FunctionPass *createX86PreLegalizerCombiner();
 FunctionPass *createX86LoadValueInjectionLoadHardeningPass();
 FunctionPass *createX86LoadValueInjectionRetHardeningPass();
 FunctionPass *createX86SpeculativeLoadHardeningPass();
@@ -263,6 +264,7 @@ void initializeX86SpeculativeLoadHardeningPassPass(PassRegistry &);
 void initializeX86TileConfigPass(PassRegistry &);
 void initializeX86SuppressAPXForRelocationPassPass(PassRegistry &);
 void initializeX86WinEHUnwindV2Pass(PassRegistry &);
+void initializeX86PreLegalizerCombinerPass(PassRegistry &);
 
 namespace X86AS {
 enum : unsigned {
diff --git a/llvm/lib/Target/X86/X86.td b/llvm/lib/Target/X86/X86.td
index 8f29a64d58194..c4e63c971bdc8 100644
--- a/llvm/lib/Target/X86/X86.td
+++ b/llvm/lib/Target/X86/X86.td
@@ -2084,3 +2084,8 @@ def X86 : Target {
 //===----------------------------------------------------------------------===//
 
 include "X86PfmCounters.td"
+
+//===----------------------------------------------------------------------===//
+// Global Isel Combiner
+//===----------------------------------------------------------------------===//
+include "X86Combine.td"
diff --git a/llvm/lib/Target/X86/X86Combine.td b/llvm/lib/Target/X86/X86Combine.td
new file mode 100644
index 0000000000000..1c099644a6a0f
--- /dev/null
+++ b/llvm/lib/Target/X86/X86Combine.td
@@ -0,0 +1,20 @@
+//===---------------------- X86Combiner.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 "llvm/Target/GlobalISel/Combine.td"
+
+// all_x86combines is based on generic all_combines, currently x86 gisel does not
+// have vector support and few open issue to address which resulted in failure with
+// combines. We will introduce more combines gradually.
+
+def all_x86combines : GICombineGroup<[identity_combines, reassocs, 
+    simplify_add_to_sub]>;
+
+def X86PreLegalizerCombiner : GICombiner<"X86PreLegalizerCombinerImpl", [all_x86combines]> {
+    let CombineAllMethodName = "tryCombineAllImpl";
+}
diff --git a/llvm/lib/Target/X86/X86TargetMachine.cpp b/llvm/lib/Target/X86/X86TargetMachine.cpp
index 713df63479987..8dc558a88a733 100644
--- a/llvm/lib/Target/X86/X86TargetMachine.cpp
+++ b/llvm/lib/Target/X86/X86TargetMachine.cpp
@@ -107,6 +107,7 @@ extern "C" LLVM_C_ABI void LLVMInitializeX86Target() {
   initializeX86DynAllocaExpanderLegacyPass(PR);
   initializeX86SuppressAPXForRelocationPassPass(PR);
   initializeX86WinEHUnwindV2Pass(PR);
+  initializeX86PreLegalizerCombinerPass(PR);
 }
 
 static std::unique_ptr<TargetLoweringObjectFile> createTLOF(const Triple &TT) {
@@ -373,6 +374,7 @@ class X86PassConfig : public TargetPassConfig {
   bool addLegalizeMachineIR() override;
   bool addRegBankSelect() override;
   bool addGlobalInstructionSelect() override;
+  void addPreLegalizeMachineIR() override;
   bool addILPOpts() override;
   bool addPreISel() override;
   void addMachineSSAOptimization() override;
@@ -487,6 +489,12 @@ bool X86PassConfig::addGlobalInstructionSelect() {
   return false;
 }
 
+void X86PassConfig::addPreLegalizeMachineIR() {
+  if (getOptLevel() != CodeGenOptLevel::None) {
+    addPass(createX86PreLegalizerCombiner());
+  }
+}
+
 bool X86PassConfig::addILPOpts() {
   addPass(&EarlyIfConverterLegacyID);
   if (EnableMachineCombinerPass)
diff --git a/llvm/test/CodeGen/X86/GlobalISel/stacksave-stackrestore.ll b/llvm/test/CodeGen/X86/GlobalISel/stacksave-stackrestore.ll
index e86c04ee22dbd..f55706edf1301 100644
--- a/llvm/test/CodeGen/X86/GlobalISel/stacksave-stackrestore.ll
+++ b/llvm/test/CodeGen/X86/GlobalISel/stacksave-stackrestore.ll
@@ -18,10 +18,9 @@ define void @test_scoped_alloca(i64 %n) {
 ; CHECK-NEXT:    .cfi_offset %rbx, -24
 ; CHECK-NEXT:    movq %rsp, %rbx
 ; CHECK-NEXT:    movq %rsp, %rax
-; CHECK-NEXT:    imulq $1, %rdi, %rcx
-; CHECK-NEXT:    addq $15, %rcx
-; CHECK-NEXT:    andq $-16, %rcx
-; CHECK-NEXT:    subq %rcx, %rax
+; CHECK-NEXT:    addq $15, %rdi
+; CHECK-NEXT:    andq $-16, %rdi
+; CHECK-NEXT:    subq %rdi, %rax
 ; CHECK-NEXT:    movq %rax, %rsp
 ; CHECK-NEXT:    movq %rax, %rdi
 ; CHECK-NEXT:    callq use_addr
diff --git a/llvm/test/CodeGen/X86/switch-bit-test-unreachable-default.ll b/llvm/test/CodeGen/X86/switch-bit-test-unreachable-default.ll
index 1a93e38af9f9b..43ca1b1d0bc48 100644
--- a/llvm/test/CodeGen/X86/switch-bit-test-unreachable-default.ll
+++ b/llvm/test/CodeGen/X86/switch-bit-test-unreachable-default.ll
@@ -40,12 +40,11 @@ define i32 @baz(i32 %0) {
 ; CHECK-GISEL:   %0:gr32 = COPY $edi
 ; CHECK-GISEL:   %10:gr32 = MOV32ri 1
 ; CHECK-GISEL:   %11:gr32 = MOV32r0 implicit-def dead $eflags
-; CHECK-GISEL:   %2:gr32 = SUB32ri %0:gr32(tied-def 0), 0, implicit-def dead $eflags
 ; CHECK-GISEL: bb.5 (%ir-block.1):
 ; CHECK-GISEL: ; predecessors: %bb.1
 ; CHECK-GISEL:   successors: %bb.4(0x55555555), %bb.2(0x2aaaaaab); %bb.4(66.67%), %bb.2(33.33%)
 ; CHECK-GISEL:   %3:gr32 = MOV32ri 1
-; CHECK-GISEL:   %13:gr8 = COPY %2.sub_8bit:gr32
+; CHECK-GISEL:   %13:gr8 = COPY %0.sub_8bit:gr32
 ; CHECK-GISEL:   $cl = COPY %13:gr8
 ; CHECK-GISEL:   %4:gr32 = SHL32rCL %3:gr32(tied-def 0), implicit-def dead $eflags, implicit $cl
 ; CHECK-GISEL:   %6:gr32 = AND32ri %4:gr32(tied-def 0), 13056, implicit-def dead $eflags



More information about the llvm-commits mailing list