[llvm] [GlobalISel][TableGen] Adds GIReplaceRegWithConstant/GIReplaceRegWithFConstant MIR builtin patterns (PR #219563)

Vikash Gupta via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 12:15:15 PDT 2026


https://github.com/vg0204 updated https://github.com/llvm/llvm-project/pull/219563

>From 1b2c290b8af8e2e8cf9b1d5701a1ff2c10ecd577 Mon Sep 17 00:00:00 2001
From: vg0204 <Vikash.Gupta at amd.com>
Date: Sat, 29 Aug 2026 00:43:41 +0530
Subject: [PATCH] [GlobalISel][TableGen] Adds GIReplaceRegWithConstant and
 GIReplaceRegWithFConstant MIR builtin patterns.

Extends the declarative apply-side vocabulary of MIR pattern
builtins so combines can materialize a constant and replace
the root without hand-written C++.

- GIReplaceRegWithConstant replaces a register defined by the
  match root with a G_CONSTANT of the same type - the declarative
  form of Helper.replaceInstWithConstant.

- GIReplaceRegWithFConstant does the same with a G_FCONSTANT - the
  declarative form of Helper.replaceInstWithFConstant.

Both take the immediate as a TableGen integer literal. For the
fp variant the grammar has no fp literals, so the literal is
interpreted as a floating-point value (0 -> 0.0, -1 -> -1.0);
non-integer values such as 0.5 cannot be expressed today.
---
 llvm/docs/GlobalISel/MIRPatterns.md           | 27 +++++-
 .../CodeGen/GlobalISel/GIMatchTableExecutor.h |  6 ++
 .../GlobalISel/GIMatchTableExecutorImpl.h     | 22 ++++-
 .../include/llvm/Target/GlobalISel/Combine.td | 37 ++++++++
 .../builtins/builtin-pattern-errors.td        | 94 ++++++++++++++++++-
 .../builtins/builtin-pattern-parrsing.td      | 46 ++++++++-
 .../match-table-replacereg-constant.td        | 38 ++++++++
 .../match-table-replacereg-fconstant.td       | 39 ++++++++
 .../Common/GlobalISel/MatchTable/Matchers.cpp |  9 ++
 .../Common/GlobalISel/MatchTable/Matchers.h   | 20 ++++
 .../TableGen/Common/GlobalISel/Patterns.cpp   | 15 ++-
 .../TableGen/Common/GlobalISel/Patterns.h     |  6 +-
 .../TableGen/GlobalISelCombinerEmitter.cpp    | 45 +++++++++
 13 files changed, 394 insertions(+), 10 deletions(-)
 create mode 100644 llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/match-table-replacereg-constant.td
 create mode 100644 llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/match-table-replacereg-fconstant.td

diff --git a/llvm/docs/GlobalISel/MIRPatterns.md b/llvm/docs/GlobalISel/MIRPatterns.md
index 8253edf463562..8cf4357e7eb7a 100644
--- a/llvm/docs/GlobalISel/MIRPatterns.md
+++ b/llvm/docs/GlobalISel/MIRPatterns.md
@@ -435,7 +435,32 @@ def Foo : GICombineRule<
   (apply (GIEraseRoot))>;
 ```
 
-#### Common Pattern #3: Emitting a Constant Value
+#### Common Pattern #3: Replacing the Root with a Constant
+
+If we want to replace all uses of a register defined by the match root with a
+freshly materialized constant of the same type, we can use the
+`GIReplaceRegWithConstant` and `GIReplaceRegWithFConstant` builtins. They are
+the declarative forms of `Helper.replaceInstWithConstant` and
+`Helper.replaceInstWithFConstant`.
+
+```text
+// Replaces all uses of $dst with a G_CONSTANT of $dst's type, then erases the
+// root.
+def Foo : GICombineRule<
+  (defs root:$dst),
+  (match (G_FOO $dst, $src)),
+  (apply (GIReplaceRegWithConstant $dst, 42))>;
+
+// The FConstant variant builds a G_FCONSTANT instead. The immediate is written
+// as an integer literal and interpreted as a floating-point value (the pattern
+// grammar has no fp literals), so 0 materializes 0.0.
+def Bar : GICombineRule<
+  (defs root:$dst),
+  (match (G_BAR $dst, $src)),
+  (apply (GIReplaceRegWithFConstant $dst, 0))>;
+```
+
+#### Common Pattern #4: Emitting a Constant Value
 
 When an immediate operand appears in an 'apply' pattern, the behavior
 depends on whether it's typed or not.
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
index 6e3ccf1923c40..fc48fa9f3c21c 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutor.h
@@ -384,6 +384,12 @@ enum {
   /// - Imm(8) - The immediate to add
   GIR_BuildConstant,
 
+  /// Builds a floating-point constant and stores its result in a TempReg.
+  /// The immediate is interpreted as a floating-point value.
+  /// - TempRegID(ULEB128) - Temp Register to define.
+  /// - Imm(8) - The immediate (interpreted as a double) to materialize
+  GIR_BuildFConstant,
+
   /// Copy an operand to the specified instruction
   /// - NewInsnID(ULEB128) - Instruction ID to modify
   /// - OldInsnID(ULEB128) - Instruction ID to copy from
diff --git a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
index e0720928c6526..619e65bbc3893 100644
--- a/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
+++ b/llvm/include/llvm/CodeGen/GlobalISel/GIMatchTableExecutorImpl.h
@@ -1112,14 +1112,26 @@ bool GIMatchTableExecutor::executeMatchTable(
       break;
     }
 
-    case GIR_BuildConstant: {
+    case GIR_BuildConstant:
+    case GIR_BuildFConstant: {
       uint64_t TempRegID = readULEB();
       uint64_t Imm = readU64();
       initializeBuilder();
-      Builder.buildConstant(State.TempRegisters[TempRegID], Imm);
-      DEBUG_WITH_TYPE(TgtExecutor::getName(),
-                      dbgs() << CurrentIdx << ": GIR_BuildConstant(TempReg["
-                             << TempRegID << "], Imm=" << Imm << ")\n");
+      if (MatcherOpcode == GIR_BuildFConstant) {
+        // The immediate is a signed integer literal interpreted as a
+        // floating-point value. Go through int64_t to preserve the sign;
+        // buildFConstant converts it to the temp reg's fp semantics.
+        Builder.buildFConstant(State.TempRegisters[TempRegID],
+                               static_cast<double>(static_cast<int64_t>(Imm)));
+      } else {
+        Builder.buildConstant(State.TempRegisters[TempRegID], Imm);
+      }
+      DEBUG_WITH_TYPE(
+          TgtExecutor::getName(),
+          dbgs() << CurrentIdx << ": "
+                 << (MatcherOpcode == GIR_BuildFConstant ? "GIR_BuildFConstant"
+                                                         : "GIR_BuildConstant")
+                 << "(TempReg[" << TempRegID << "], Imm=" << Imm << ")\n");
       break;
     }
 
diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td
index e0f665e85c2cc..6b82463d862a1 100644
--- a/llvm/include/llvm/Target/GlobalISel/Combine.td
+++ b/llvm/include/llvm/Target/GlobalISel/Combine.td
@@ -162,6 +162,43 @@ class GIBuiltinInst;
 //   "canReplaceReg" is checked before applying the rule.
 def GIReplaceReg : GIBuiltinInst;
 
+// Replace a register defined by the match root with a G_CONSTANT of the same
+// type. The declarative form of Helper.replaceInstWithConstant.
+//
+// Usage:
+//    (apply (GIReplaceRegWithConstant $reg, 42))
+//
+// Operands:
+// - $reg (out) register defined by the match root
+// - the integer immediate to materialize (sign-extended to $reg's type)
+//
+// Semantics:
+// - Can only appear in an 'apply' pattern.
+// - $reg must be a register defined by the match root.
+// - Builds a G_CONSTANT of $reg's type, replaces all uses of $reg with it,
+//   then erases the root.
+def GIReplaceRegWithConstant : GIBuiltinInst;
+
+// Replace a register defined by the match root with a G_FCONSTANT of the same
+// type. The declarative form of Helper.replaceInstWithFConstant.
+//
+// Usage:
+//    (apply (GIReplaceRegWithFConstant $reg, 0))
+//
+// Operands:
+// - $reg (out) register defined by the match root
+// - the immediate to materialize, given as an integer literal and interpreted
+//   as a floating-point value (the pattern grammar has no fp literals), e.g.
+//   0 -> 0.0, 1 -> 1.0, -1 -> -1.0. Non-integer fp values (e.g. 0.5) cannot be
+//   expressed today.
+//
+// Semantics:
+// - Can only appear in an 'apply' pattern.
+// - $reg must be a register defined by the match root.
+// - Builds a G_FCONSTANT of $reg's type, replaces all uses of $reg with it,
+//   then erases the root.
+def GIReplaceRegWithFConstant : GIBuiltinInst;
+
 // Apply action that erases the match root.
 //
 // Usage:
diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-errors.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-errors.td
index 8e3e27daaa5bf..6512dd0e6e48c 100644
--- a/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-errors.td
+++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-errors.td
@@ -78,6 +78,86 @@ def replacereg_nonroot : GICombineRule<
   (match (COPY $dst, $tmp), (COPY $tmp, $src)),
   (apply (GIReplaceReg $dst, $src), (GIReplaceReg $tmp, $src))>;
 
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'GIReplaceRegWithConstant' cannot be used in a 'match' pattern
+def replaceregconstant_in_match : GICombineRule<
+  (defs root:$dst),
+  (match (GIReplaceRegWithConstant $dst, 0)),
+  (apply (COPY $dst, $src))>;
+
+// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: 'GIReplaceRegWithConstant' expected 2 operands, got 1
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceRegWithConstant ?:$dst)'
+def replaceregconstant_ops : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $src)),
+  (apply (GIReplaceRegWithConstant $dst))>;
+
+// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: expected operand 1 of 'GIReplaceRegWithConstant' to be an immediate
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceRegWithConstant ?:$dst, ?:$src)'
+def replaceregconstant_nonimm : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $src)),
+  (apply (GIReplaceRegWithConstant $dst, $src))>;
+
+// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: out operand of 'GIReplaceRegWithConstant' cannot be an immediate
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceRegWithConstant (i32 0), 0)'
+def replaceregconstant_immreg : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $src)),
+  (apply (GIReplaceRegWithConstant (i32 0), 0))>;
+
+// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: expected operand 1 of 'GIReplaceRegWithConstant' to be an immediate
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceRegWithConstant ?:$dst, (i32 0):$k)'
+def replaceregconstant_namedimm : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $src)),
+  (apply (GIReplaceRegWithConstant $dst, (i32 0):$k))>;
+
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: GIReplaceRegWithConstant cannot replace 'tmp': this builtin can only replace a register defined by the match root
+def replaceregconstant_nonroot : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $tmp), (COPY $tmp, $src)),
+  (apply (GIReplaceReg $dst, $src), (GIReplaceRegWithConstant $tmp, 0))>;
+
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: 'GIReplaceRegWithFConstant' cannot be used in a 'match' pattern
+def replaceregfconstant_in_match : GICombineRule<
+  (defs root:$dst),
+  (match (GIReplaceRegWithFConstant $dst, 0)),
+  (apply (COPY $dst, $src))>;
+
+// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: 'GIReplaceRegWithFConstant' expected 2 operands, got 1
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceRegWithFConstant ?:$dst)'
+def replaceregfconstant_ops : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $src)),
+  (apply (GIReplaceRegWithFConstant $dst))>;
+
+// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: expected operand 1 of 'GIReplaceRegWithFConstant' to be an immediate
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceRegWithFConstant ?:$dst, ?:$src)'
+def replaceregfconstant_nonimm : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $src)),
+  (apply (GIReplaceRegWithFConstant $dst, $src))>;
+
+// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: out operand of 'GIReplaceRegWithFConstant' cannot be an immediate
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceRegWithFConstant (i32 0), 0)'
+def replaceregfconstant_immreg : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $src)),
+  (apply (GIReplaceRegWithFConstant (i32 0), 0))>;
+
+// CHECK: :[[@LINE+2]]:{{[0-9]+}}: error: expected operand 1 of 'GIReplaceRegWithFConstant' to be an immediate
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: Failed to parse pattern: '(GIReplaceRegWithFConstant ?:$dst, (i32 0):$k)'
+def replaceregfconstant_namedimm : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $src)),
+  (apply (GIReplaceRegWithFConstant $dst, (i32 0):$k))>;
+
+// CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: GIReplaceRegWithFConstant cannot replace 'tmp': this builtin can only replace a register defined by the match root
+def replaceregfconstant_nonroot : GICombineRule<
+  (defs root:$dst),
+  (match (COPY $dst, $tmp), (COPY $tmp, $src)),
+  (apply (GIReplaceReg $dst, $src), (GIReplaceRegWithFConstant $tmp, 0))>;
+
 // CHECK: error: Failed to parse one or more rules
 
 def MyCombiner: GICombiner<"GenMyCombiner", [
@@ -90,5 +170,17 @@ def MyCombiner: GICombiner<"GenMyCombiner", [
   eraseroot_notinstmatch,
   replacereg_in_match,
   replacereg_ops,
-  replacereg_nonroot
+  replacereg_nonroot,
+  replaceregconstant_in_match,
+  replaceregconstant_ops,
+  replaceregconstant_nonimm,
+  replaceregconstant_immreg,
+  replaceregconstant_namedimm,
+  replaceregconstant_nonroot,
+  replaceregfconstant_in_match,
+  replaceregfconstant_ops,
+  replaceregfconstant_nonimm,
+  replaceregfconstant_immreg,
+  replaceregfconstant_namedimm,
+  replaceregfconstant_nonroot
 ]>;
diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-parrsing.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-parrsing.td
index 9c839ee48d011..641576db0ed34 100644
--- a/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-parrsing.td
+++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/builtin-pattern-parrsing.td
@@ -49,7 +49,51 @@ def BuiltinTest1 : GICombineRule<
   (apply (GIEraseRoot))
 >;
 
+// CHECK:      (CombineRule name:BuiltinTest2 id:2 root:a
+// CHECK-NEXT:   (MatchPats
+// CHECK-NEXT:     <match_root>__BuiltinTest2_match_0:(CodeGenInstructionPattern G_TRUNC operands:[<def>$a, $b])
+// CHECK-NEXT:   )
+// CHECK-NEXT:   (ApplyPats
+// CHECK-NEXT:     <apply_root>__BuiltinTest2_apply_0:(BuiltinPattern GIReplaceRegWithConstant operands:[<def>$a, 42])
+// CHECK-NEXT:   )
+// CHECK-NEXT:   (OperandTable MatchPats
+// CHECK-NEXT:     a -> __BuiltinTest2_match_0
+// CHECK-NEXT:     b -> <live-in>
+// CHECK-NEXT:   )
+// CHECK-NEXT:   (OperandTable ApplyPats
+// CHECK-NEXT:     a -> __BuiltinTest2_apply_0
+// CHECK-NEXT:   )
+// CHECK-NEXT: )
+def BuiltinTest2 : GICombineRule<
+  (defs root:$a),
+  (match (G_TRUNC $a, $b)),
+  (apply (GIReplaceRegWithConstant $a, 42))
+>;
+
+// CHECK:      (CombineRule name:BuiltinTest3 id:3 root:a
+// CHECK-NEXT:   (MatchPats
+// CHECK-NEXT:     <match_root>__BuiltinTest3_match_0:(CodeGenInstructionPattern G_TRUNC operands:[<def>$a, $b])
+// CHECK-NEXT:   )
+// CHECK-NEXT:   (ApplyPats
+// CHECK-NEXT:     <apply_root>__BuiltinTest3_apply_0:(BuiltinPattern GIReplaceRegWithFConstant operands:[<def>$a, 0])
+// CHECK-NEXT:   )
+// CHECK-NEXT:   (OperandTable MatchPats
+// CHECK-NEXT:     a -> __BuiltinTest3_match_0
+// CHECK-NEXT:     b -> <live-in>
+// CHECK-NEXT:   )
+// CHECK-NEXT:   (OperandTable ApplyPats
+// CHECK-NEXT:     a -> __BuiltinTest3_apply_0
+// CHECK-NEXT:   )
+// CHECK-NEXT: )
+def BuiltinTest3 : GICombineRule<
+  (defs root:$a),
+  (match (G_TRUNC $a, $b)),
+  (apply (GIReplaceRegWithFConstant $a, 0))
+>;
+
 def MyCombiner: GICombiner<"GenMyCombiner", [
   BuiltinTest0,
-  BuiltinTest1
+  BuiltinTest1,
+  BuiltinTest2,
+  BuiltinTest3
 ]>;
diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/match-table-replacereg-constant.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/match-table-replacereg-constant.td
new file mode 100644
index 0000000000000..a92ba8b2490a5
--- /dev/null
+++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/match-table-replacereg-constant.td
@@ -0,0 +1,38 @@
+// RUN: llvm-tblgen -I %p/../../../../include -gen-global-isel-combiner \
+// RUN:     -combiners=MyCombiner %s | \
+// RUN: FileCheck %s
+
+include "llvm/Target/Target.td"
+include "llvm/Target/GlobalISel/Combine.td"
+
+def MyTargetISA : InstrInfo;
+def MyTarget : Target { let InstructionSet = MyTargetISA; }
+
+def ReplaceWithConstant : GICombineRule<
+  (defs root:$dst),
+  (match  (G_FNEG $dst, $src)),
+  (apply  (GIReplaceRegWithConstant $dst, 42))>;
+
+def MyCombiner: GICombiner<"GenMyCombiner", [
+  ReplaceWithConstant
+]>;
+
+//      CHECK: const uint8_t *GenMyCombiner::getMatchTable() const {
+// CHECK-NEXT:   constexpr static uint8_t MatchTable0[] = {
+// CHECK-NEXT:     GIM_Try, /*On fail goto*//*Label 0*/ GIMT_Encode4([[L34:[0-9]+]]), // Rule ID 0 //
+// CHECK-NEXT:       GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule0Enabled),
+// CHECK-NEXT:       GIM_CheckOpcode, /*MI*/0, GIMT_Encode2(TargetOpcode::G_FNEG),
+// CHECK-NEXT:       // MIs[0] dst
+// CHECK-NEXT:       GIM_RecordRegType, /*MI*/0, /*Op*/0, /*TempTypeIdx*/255,
+// CHECK-NEXT:       // MIs[0] src
+// CHECK-NEXT:       // No operand predicates
+// CHECK-NEXT:       GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/255,
+// CHECK-NEXT:       GIR_BuildConstant, /*TempRegID*/0, /*Val*/GIMT_Encode8(42),
+// CHECK-NEXT:       // Combiner Rule #0: ReplaceWithConstant
+// CHECK-NEXT:       GIR_ReplaceRegWithTempReg, /*OldInsnID*/0, /*OldOpIdx*/0, /*TempRegID*/0,
+// CHECK-NEXT:       GIR_EraseRootFromParent_Done,
+// CHECK-NEXT:     // Label 0: @[[L34]]
+// CHECK-NEXT:     GIM_Reject,
+// CHECK-NEXT:     }; // Size: {{[0-9]+}} bytes
+// CHECK-NEXT:   return MatchTable0;
+// CHECK-NEXT: }
diff --git a/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/match-table-replacereg-fconstant.td b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/match-table-replacereg-fconstant.td
new file mode 100644
index 0000000000000..0e7a14360f141
--- /dev/null
+++ b/llvm/test/TableGen/GlobalISelCombinerEmitter/builtins/match-table-replacereg-fconstant.td
@@ -0,0 +1,39 @@
+// RUN: llvm-tblgen -I %p/../../../../include -gen-global-isel-combiner \
+// RUN:     -combiners=MyCombiner %s | \
+// RUN: FileCheck %s
+
+include "llvm/Target/Target.td"
+include "llvm/Target/GlobalISel/Combine.td"
+
+def MyTargetISA : InstrInfo;
+def MyTarget : Target { let InstructionSet = MyTargetISA; }
+
+// The integer literal is interpreted as a floating-point value, so 0 -> 0.0.
+def ReplaceWithFConstant : GICombineRule<
+  (defs root:$dst),
+  (match  (G_FNEG $dst, $src)),
+  (apply  (GIReplaceRegWithFConstant $dst, 0))>;
+
+def MyCombiner: GICombiner<"GenMyCombiner", [
+  ReplaceWithFConstant
+]>;
+
+//      CHECK: const uint8_t *GenMyCombiner::getMatchTable() const {
+// CHECK-NEXT:   constexpr static uint8_t MatchTable0[] = {
+// CHECK-NEXT:     GIM_Try, /*On fail goto*//*Label 0*/ GIMT_Encode4([[L34:[0-9]+]]), // Rule ID 0 //
+// CHECK-NEXT:       GIM_CheckSimplePredicate, GIMT_Encode2(GICXXPred_Simple_IsRule0Enabled),
+// CHECK-NEXT:       GIM_CheckOpcode, /*MI*/0, GIMT_Encode2(TargetOpcode::G_FNEG),
+// CHECK-NEXT:       // MIs[0] dst
+// CHECK-NEXT:       GIM_RecordRegType, /*MI*/0, /*Op*/0, /*TempTypeIdx*/255,
+// CHECK-NEXT:       // MIs[0] src
+// CHECK-NEXT:       // No operand predicates
+// CHECK-NEXT:       GIR_MakeTempReg, /*TempRegID*/0, /*TypeID*/255,
+// CHECK-NEXT:       GIR_BuildFConstant, /*TempRegID*/0, /*Val*/GIMT_Encode8(0),
+// CHECK-NEXT:       // Combiner Rule #0: ReplaceWithFConstant
+// CHECK-NEXT:       GIR_ReplaceRegWithTempReg, /*OldInsnID*/0, /*OldOpIdx*/0, /*TempRegID*/0,
+// CHECK-NEXT:       GIR_EraseRootFromParent_Done,
+// CHECK-NEXT:     // Label 0: @[[L34]]
+// CHECK-NEXT:     GIM_Reject,
+// CHECK-NEXT:     }; // Size: {{[0-9]+}} bytes
+// CHECK-NEXT:   return MatchTable0;
+// CHECK-NEXT: }
diff --git a/llvm/utils/TableGen/Common/GlobalISel/MatchTable/Matchers.cpp b/llvm/utils/TableGen/Common/GlobalISel/MatchTable/Matchers.cpp
index cc1089fc316c7..90aaee43079ff 100644
--- a/llvm/utils/TableGen/Common/GlobalISel/MatchTable/Matchers.cpp
+++ b/llvm/utils/TableGen/Common/GlobalISel/MatchTable/Matchers.cpp
@@ -2076,6 +2076,15 @@ void BuildConstantAction::emitActionOpcodes(MatchTable &Table) const {
         << MatchTable::IntValue(8, Val) << MatchTable::LineBreak;
 }
 
+//===- BuildFConstantAction -----------------------------------------------===//
+
+void BuildFConstantAction::emitActionOpcodes(MatchTable &Table) const {
+  Table << MatchTable::Opcode("GIR_BuildFConstant")
+        << MatchTable::Comment("TempRegID")
+        << MatchTable::ULEB128Value(TempRegID) << MatchTable::Comment("Val")
+        << MatchTable::IntValue(8, Val) << MatchTable::LineBreak;
+}
+
 //===- EraseInstAction ----------------------------------------------------===//
 
 void EraseInstAction::emitActionOpcodes(MatchTable &Table) const {
diff --git a/llvm/utils/TableGen/Common/GlobalISel/MatchTable/Matchers.h b/llvm/utils/TableGen/Common/GlobalISel/MatchTable/Matchers.h
index b7ea01279eea9..8d3a07d0fc582 100644
--- a/llvm/utils/TableGen/Common/GlobalISel/MatchTable/Matchers.h
+++ b/llvm/utils/TableGen/Common/GlobalISel/MatchTable/Matchers.h
@@ -2191,6 +2191,7 @@ class MatchAction {
     AK_DebugComment,
     AK_BuildMI,
     AK_BuildConstantMI,
+    AK_BuildFConstantMI,
     AK_EraseInst,
     AK_ReplaceReg,
     AK_ConstraintOpsToDef,
@@ -2320,6 +2321,25 @@ class BuildConstantAction : public MatchAction {
   void emitActionOpcodes(MatchTable &Table) const override;
 };
 
+/// Generates code to create a floating-point constant that defines a TempReg.
+/// The instruction created is a G_FCONSTANT. The integer value is interpreted
+/// as a floating-point value at runtime (the pattern grammar has no fp
+/// literals).
+class BuildFConstantAction : public MatchAction {
+  unsigned TempRegID;
+  int64_t Val;
+
+public:
+  BuildFConstantAction(unsigned TempRegID, int64_t Val)
+      : MatchAction(AK_BuildFConstantMI), TempRegID(TempRegID), Val(Val) {}
+
+  static bool classof(const MatchAction *A) {
+    return A->getKind() == AK_BuildFConstantMI;
+  }
+
+  void emitActionOpcodes(MatchTable &Table) const override;
+};
+
 class EraseInstAction : public MatchAction {
   unsigned InsnID;
 
diff --git a/llvm/utils/TableGen/Common/GlobalISel/Patterns.cpp b/llvm/utils/TableGen/Common/GlobalISel/Patterns.cpp
index dfd37fd64a652..7616be8863076 100644
--- a/llvm/utils/TableGen/Common/GlobalISel/Patterns.cpp
+++ b/llvm/utils/TableGen/Common/GlobalISel/Patterns.cpp
@@ -873,8 +873,21 @@ bool BuiltinPattern::checkSemantics(ArrayRef<SMLoc> Loc) {
   if (!InstructionPattern::checkSemantics(Loc))
     return false;
 
-  // For now all builtins just take names, no immediates.
+  // The constant-replacement builtins take a trailing integer immediate
+  // operand; every other builtin operand must be a plain name.
+  const bool TakesTrailingImm = getBuiltinKind() == BI_ReplaceRegWithConstant ||
+                                getBuiltinKind() == BI_ReplaceRegWithFConstant;
+
   for (const auto &[Idx, Op] : enumerate(operands())) {
+    if (TakesTrailingImm && Idx + 1 == operands().size()) {
+      if (!Op.hasImmValue() || Op.isNamedImmediate()) {
+        PrintError(Loc, "expected operand " + std::to_string(Idx) + " of '" +
+                            getInstName() + "' to be an immediate");
+        return false;
+      }
+      continue;
+    }
+
     if (!Op.isNamedOperand() || Op.isNamedImmediate()) {
       PrintError(Loc, "expected operand " + std::to_string(Idx) + " of '" +
                           getInstName() + "' to be a name");
diff --git a/llvm/utils/TableGen/Common/GlobalISel/Patterns.h b/llvm/utils/TableGen/Common/GlobalISel/Patterns.h
index 2bf2a22ee9998..832ee22b05c9b 100644
--- a/llvm/utils/TableGen/Common/GlobalISel/Patterns.h
+++ b/llvm/utils/TableGen/Common/GlobalISel/Patterns.h
@@ -691,6 +691,8 @@ class PatFragPattern : public InstructionPattern {
 /// Represents builtin instructions such as "GIReplaceReg" and "GIEraseRoot".
 enum BuiltinKind {
   BI_ReplaceReg,
+  BI_ReplaceRegWithConstant,
+  BI_ReplaceRegWithFConstant,
   BI_EraseRoot,
 };
 
@@ -702,8 +704,10 @@ class BuiltinPattern : public InstructionPattern {
     unsigned NumDefs;
   };
 
-  static constexpr std::array<BuiltinInfo, 2> KnownBuiltins = {{
+  static constexpr std::array<BuiltinInfo, 4> KnownBuiltins = {{
       {"GIReplaceReg", BI_ReplaceReg, 2, 1},
+      {"GIReplaceRegWithConstant", BI_ReplaceRegWithConstant, 2, 1},
+      {"GIReplaceRegWithFConstant", BI_ReplaceRegWithFConstant, 2, 1},
       {"GIEraseRoot", BI_EraseRoot, 0, 0},
   }};
 
diff --git a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
index bb93d0248e55f..6d9b6956fd991 100644
--- a/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
+++ b/llvm/utils/TableGen/GlobalISelCombinerEmitter.cpp
@@ -1371,6 +1371,24 @@ bool CombineRuleBuilder::checkSemantics() {
       }
       break;
     }
+    case BI_ReplaceRegWithConstant:
+    case BI_ReplaceRegWithFConstant: {
+      // These can only replace a register defined by the root instruction.
+      StringRef RegName = BIP->getOperand(0).getOperandName();
+      auto *Def = MatchOpTable.getDef(RegName);
+      if (!Def) {
+        PrintError(Name + " cannot find a matched pattern that defines '" +
+                   RegName + "'");
+        return false;
+      }
+      if (Def != MatchRoot) {
+        PrintError(Name + " cannot replace '" + RegName +
+                   "': this builtin can only replace a register defined by the "
+                   "match root");
+        return false;
+      }
+      break;
+    }
     }
   }
 
@@ -2206,6 +2224,33 @@ bool CombineRuleBuilder::emitBuiltinApplyPattern(
     assert(MatchOpTable.getDef(Old) == MatchRoot);
     return true;
   }
+  case BI_ReplaceRegWithConstant:
+  case BI_ReplaceRegWithFConstant: {
+    StringRef Reg = P.getOperand(0).getOperandName();
+    int64_t Imm = P.getOperand(1).getImmValue();
+
+    auto &RegOM = M.getOperandMatcher(Reg);
+
+    // Build a G_CONSTANT/G_FCONSTANT into a fresh temp reg of the same type as
+    // the replaced register, then replace all uses of the register with it.
+    // buildConstant/buildFConstant derive the value's width/semantics from the
+    // temp reg's (recorded) type, so no explicit type operand is needed. For
+    // the fp variant the integer literal is interpreted as a floating-point
+    // value at runtime (the pattern grammar has no fp literals).
+    unsigned TempRegID = M.allocateTempRegID();
+    auto InsertIt = M.insertAction<MakeTempRegisterAction>(
+        M.actions_begin(), RegOM.getTempTypeIdx(M), TempRegID);
+    if (P.getBuiltinKind() == BI_ReplaceRegWithConstant)
+      M.insertAction<BuildConstantAction>(++InsertIt, TempRegID, Imm);
+    else
+      M.insertAction<BuildFConstantAction>(++InsertIt, TempRegID, Imm);
+
+    M.addAction<ReplaceRegAction>(RegOM.getInsnVarID(), RegOM.getOpIdx(),
+                                  TempRegID);
+    // checkSemantics should have ensured that we can only rewrite the root.
+    assert(MatchOpTable.getDef(Reg) == MatchRoot);
+    return true;
+  }
   }
 
   llvm_unreachable("Unknown BuiltinKind!");



More information about the llvm-commits mailing list