[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