Skip to content

[LLVM][TableGen] Support type casts of nodes with multiple results #109728

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

Merged
merged 1 commit into from
Oct 1, 2024

Conversation

stephenchouca
Copy link
Contributor

@stephenchouca stephenchouca commented Sep 23, 2024

Currently, type casts can only be used to pattern match for intrinsics with a single overloaded return value. For instance:

def int_foo : Intrinsic<[llvm_anyint_ty], []>;
def : Pat<(i32 (int_foo)), ...>;

This patch extends type casts to support matching intrinsics with multiple overloaded return values. As an example, the following defines a pattern that matches only if the overloaded intrinsic call returns an i16 for the first result and an i32 for the second result:

def int_bar : Intrinsic<[llvm_anyint_ty, llvm_anyint_ty], []>;
def : Pat<([i16, i32] (int_bar)), ...>;

@llvmbot llvmbot added tablegen llvm:SelectionDAG SelectionDAGISel as well labels Sep 23, 2024
@llvmbot
Copy link
Member

llvmbot commented Sep 23, 2024

@llvm/pr-subscribers-tablegen

Author: Stephen Chou (stephenchouca)

Changes

Currently, type casts can only be used to pattern match for intrinsics with a single overloaded return value. For example:

def int_foo : Intrinsic&lt;[llvm_anyint_ty], []&gt;;
def : Pat&lt;(i32 (int_foo)), ...&gt;;

This patch extends type casts to support matching intrinsics with multiple overloaded return values. This is enabled with a new ValueTypeList class that can be used to define the target of a type cast. As an example, the following defines a pattern that matches only if the results of the overloaded intrinsic call are both i32:

def int_bar : Intrinsic&lt;[llvm_anyint_ty, llvm_anyint_ty], []&gt;;
def i32_i32 : ValueTypeList&lt;[i32, i32]&gt;;
def : Pat&lt;(i32_i32 (int_bar)), ...&gt;;

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

3 Files Affected:

  • (modified) llvm/include/llvm/Target/TargetSelectionDAG.td (+15)
  • (added) llvm/test/TableGen/dag-isel-valuetypelist.td (+38)
  • (modified) llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp (+31-8)
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index adf8a75f620225..df50cc07d5cb22 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -2070,3 +2070,18 @@ class ComplexPattern<ValueType ty, int numops, string fn,
   list<SDNodeProperty> Properties = props;
   int Complexity = complexity;
 }
+
+//===----------------------------------------------------------------------===//
+// Value type list.
+//
+// This can be used to pattern match for intrinsics with multiple overloaded
+// return values. For example:
+//
+// def int_foo : Irtrinsic<[llvm_anyint_ty, llvm_anyint_ty], []>;
+// def i32_i32 : ValueTypeList<[i32, i32]>;
+// def : Pat<(i32_i32 (int_foo)), ...>;
+//
+
+class ValueTypeList<list<ValueType> vts> {
+  list<ValueType> VTs = vts ;
+}
diff --git a/llvm/test/TableGen/dag-isel-valuetypelist.td b/llvm/test/TableGen/dag-isel-valuetypelist.td
new file mode 100644
index 00000000000000..db0b30695522d9
--- /dev/null
+++ b/llvm/test/TableGen/dag-isel-valuetypelist.td
@@ -0,0 +1,38 @@
+// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include %s | FileCheck %s
+
+include "llvm/Target/Target.td"
+
+def TestTargetInstrInfo : InstrInfo;
+
+def TestTarget : Target {
+  let InstructionSet = TestTargetInstrInfo;
+}
+
+def REG : Register<"REG">;
+def GPR : RegisterClass<"TestTarget", [i16, i32], 32, (add REG)>;
+
+def int_foo : Intrinsic<[llvm_anyint_ty, llvm_anyint_ty], []>;
+
+def INSTR_FOO_I16_I32 : Instruction {
+  let OutOperandList = (outs GPR:$a, GPR:$b);
+  let InOperandList = (ins);
+}
+
+def INSTR_FOO_I32_I16 : Instruction {
+  let OutOperandList = (outs GPR:$a, GPR:$b);
+  let InOperandList = (ins);
+}
+
+def i16_i32 : ValueTypeList<[i16, i32]>;
+def i32_i16 : ValueTypeList<[i32, i16]>;
+
+// CHECK:      OPC_CheckOpcode, TARGET_VAL(ISD::INTRINSIC_W_CHAIN)
+// CHECK: 7*/  OPC_SwitchType {{.*}}, 10, /*MVT::i16*/6
+// CHECK:       OPC_CheckTypeRes, 1, /*MVT::i32*/7
+// CHECK:       OPC_MorphNodeTo2Chain, TARGET_VAL(::INSTR_FOO_I16_I32)
+def : Pat<(i16_i32 (int_foo)), (i16_i32 (INSTR_FOO_I16_I32))>;
+
+// CHECK: 20*/ /*SwitchType*/ {{.*}} /*MVT::i32*/7
+// CHECK:       OPC_CheckTypeRes, 1, /*MVT::i16*/6
+// CHECK:       OPC_MorphNodeTo2Chain, TARGET_VAL(::INSTR_FOO_I32_I16)
+def : Pat<(i32_i16 (int_foo)), (i32_i16 (INSTR_FOO_I32_I16))>;
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
index fd80bc681c70d9..578275c36b69cd 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
@@ -2903,23 +2903,46 @@ TreePatternNodePtr TreePattern::ParseTreePattern(Init *TheInit,
   }
   Record *Operator = OpDef->getDef();
 
+  auto ParseCastOperand = [this](DagInit *Dag, StringRef OpName) {
+    if (Dag->getNumArgs() != 1)
+      error("Type cast only takes one operand!");
+
+    if (!OpName.empty())
+      error("Type cast should not have a name!");
+
+    return ParseTreePattern(Dag->getArg(0), Dag->getArgNameStr(0));
+  };
+
   if (Operator->isSubClassOf("ValueType")) {
     // If the operator is a ValueType, then this must be "type cast" of a leaf
     // node.
-    if (Dag->getNumArgs() != 1)
-      error("Type cast only takes one operand!");
+    TreePatternNodePtr New = ParseCastOperand(Dag, OpName);
 
-    TreePatternNodePtr New =
-        ParseTreePattern(Dag->getArg(0), Dag->getArgNameStr(0));
+    if (New->getNumTypes() != 1)
+      error("ValueType cast can only have one type!");
 
     // Apply the type cast.
-    if (New->getNumTypes() != 1)
-      error("Type cast can only have one type!");
     const CodeGenHwModes &CGH = getDAGPatterns().getTargetInfo().getHwModes();
     New->UpdateNodeType(0, getValueTypeByHwMode(Operator, CGH), *this);
 
-    if (!OpName.empty())
-      error("ValueType cast should not have a name!");
+    return New;
+  }
+
+  if (Operator->isSubClassOf("ValueTypeList")) {
+    // If the operator is a ValueTypeList, then this must be "type cast" of a
+    // leaf node with multiple results.
+    TreePatternNodePtr New = ParseCastOperand(Dag, OpName);
+
+    ListInit *LI = Operator->getValueAsListInit("VTs");
+    if (New->getNumTypes() != LI->size())
+      error("Invalid number of type casts!");
+
+    // Apply the type casts.
+    const CodeGenHwModes &CGH = getDAGPatterns().getTargetInfo().getHwModes();
+    for (unsigned i = 0, e = New->getNumTypes(); i != e; ++i)
+      New->UpdateNodeType(
+          i, getValueTypeByHwMode(LI->getElementAsRecord(i), CGH), *this);
+
     return New;
   }
 

@llvmbot
Copy link
Member

llvmbot commented Sep 23, 2024

@llvm/pr-subscribers-llvm-selectiondag

Author: Stephen Chou (stephenchouca)

Changes

Currently, type casts can only be used to pattern match for intrinsics with a single overloaded return value. For example:

def int_foo : Intrinsic&lt;[llvm_anyint_ty], []&gt;;
def : Pat&lt;(i32 (int_foo)), ...&gt;;

This patch extends type casts to support matching intrinsics with multiple overloaded return values. This is enabled with a new ValueTypeList class that can be used to define the target of a type cast. As an example, the following defines a pattern that matches only if the results of the overloaded intrinsic call are both i32:

def int_bar : Intrinsic&lt;[llvm_anyint_ty, llvm_anyint_ty], []&gt;;
def i32_i32 : ValueTypeList&lt;[i32, i32]&gt;;
def : Pat&lt;(i32_i32 (int_bar)), ...&gt;;

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

3 Files Affected:

  • (modified) llvm/include/llvm/Target/TargetSelectionDAG.td (+15)
  • (added) llvm/test/TableGen/dag-isel-valuetypelist.td (+38)
  • (modified) llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp (+31-8)
diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index adf8a75f620225..df50cc07d5cb22 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -2070,3 +2070,18 @@ class ComplexPattern<ValueType ty, int numops, string fn,
   list<SDNodeProperty> Properties = props;
   int Complexity = complexity;
 }
+
+//===----------------------------------------------------------------------===//
+// Value type list.
+//
+// This can be used to pattern match for intrinsics with multiple overloaded
+// return values. For example:
+//
+// def int_foo : Irtrinsic<[llvm_anyint_ty, llvm_anyint_ty], []>;
+// def i32_i32 : ValueTypeList<[i32, i32]>;
+// def : Pat<(i32_i32 (int_foo)), ...>;
+//
+
+class ValueTypeList<list<ValueType> vts> {
+  list<ValueType> VTs = vts ;
+}
diff --git a/llvm/test/TableGen/dag-isel-valuetypelist.td b/llvm/test/TableGen/dag-isel-valuetypelist.td
new file mode 100644
index 00000000000000..db0b30695522d9
--- /dev/null
+++ b/llvm/test/TableGen/dag-isel-valuetypelist.td
@@ -0,0 +1,38 @@
+// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include %s | FileCheck %s
+
+include "llvm/Target/Target.td"
+
+def TestTargetInstrInfo : InstrInfo;
+
+def TestTarget : Target {
+  let InstructionSet = TestTargetInstrInfo;
+}
+
+def REG : Register<"REG">;
+def GPR : RegisterClass<"TestTarget", [i16, i32], 32, (add REG)>;
+
+def int_foo : Intrinsic<[llvm_anyint_ty, llvm_anyint_ty], []>;
+
+def INSTR_FOO_I16_I32 : Instruction {
+  let OutOperandList = (outs GPR:$a, GPR:$b);
+  let InOperandList = (ins);
+}
+
+def INSTR_FOO_I32_I16 : Instruction {
+  let OutOperandList = (outs GPR:$a, GPR:$b);
+  let InOperandList = (ins);
+}
+
+def i16_i32 : ValueTypeList<[i16, i32]>;
+def i32_i16 : ValueTypeList<[i32, i16]>;
+
+// CHECK:      OPC_CheckOpcode, TARGET_VAL(ISD::INTRINSIC_W_CHAIN)
+// CHECK: 7*/  OPC_SwitchType {{.*}}, 10, /*MVT::i16*/6
+// CHECK:       OPC_CheckTypeRes, 1, /*MVT::i32*/7
+// CHECK:       OPC_MorphNodeTo2Chain, TARGET_VAL(::INSTR_FOO_I16_I32)
+def : Pat<(i16_i32 (int_foo)), (i16_i32 (INSTR_FOO_I16_I32))>;
+
+// CHECK: 20*/ /*SwitchType*/ {{.*}} /*MVT::i32*/7
+// CHECK:       OPC_CheckTypeRes, 1, /*MVT::i16*/6
+// CHECK:       OPC_MorphNodeTo2Chain, TARGET_VAL(::INSTR_FOO_I32_I16)
+def : Pat<(i32_i16 (int_foo)), (i32_i16 (INSTR_FOO_I32_I16))>;
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
index fd80bc681c70d9..578275c36b69cd 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
@@ -2903,23 +2903,46 @@ TreePatternNodePtr TreePattern::ParseTreePattern(Init *TheInit,
   }
   Record *Operator = OpDef->getDef();
 
+  auto ParseCastOperand = [this](DagInit *Dag, StringRef OpName) {
+    if (Dag->getNumArgs() != 1)
+      error("Type cast only takes one operand!");
+
+    if (!OpName.empty())
+      error("Type cast should not have a name!");
+
+    return ParseTreePattern(Dag->getArg(0), Dag->getArgNameStr(0));
+  };
+
   if (Operator->isSubClassOf("ValueType")) {
     // If the operator is a ValueType, then this must be "type cast" of a leaf
     // node.
-    if (Dag->getNumArgs() != 1)
-      error("Type cast only takes one operand!");
+    TreePatternNodePtr New = ParseCastOperand(Dag, OpName);
 
-    TreePatternNodePtr New =
-        ParseTreePattern(Dag->getArg(0), Dag->getArgNameStr(0));
+    if (New->getNumTypes() != 1)
+      error("ValueType cast can only have one type!");
 
     // Apply the type cast.
-    if (New->getNumTypes() != 1)
-      error("Type cast can only have one type!");
     const CodeGenHwModes &CGH = getDAGPatterns().getTargetInfo().getHwModes();
     New->UpdateNodeType(0, getValueTypeByHwMode(Operator, CGH), *this);
 
-    if (!OpName.empty())
-      error("ValueType cast should not have a name!");
+    return New;
+  }
+
+  if (Operator->isSubClassOf("ValueTypeList")) {
+    // If the operator is a ValueTypeList, then this must be "type cast" of a
+    // leaf node with multiple results.
+    TreePatternNodePtr New = ParseCastOperand(Dag, OpName);
+
+    ListInit *LI = Operator->getValueAsListInit("VTs");
+    if (New->getNumTypes() != LI->size())
+      error("Invalid number of type casts!");
+
+    // Apply the type casts.
+    const CodeGenHwModes &CGH = getDAGPatterns().getTargetInfo().getHwModes();
+    for (unsigned i = 0, e = New->getNumTypes(); i != e; ++i)
+      New->UpdateNodeType(
+          i, getValueTypeByHwMode(LI->getElementAsRecord(i), CGH), *this);
+
     return New;
   }
 

@stephenchouca stephenchouca force-pushed the tablegen_multiple_type_casts branch from 65f0c3a to 5a307be Compare September 24, 2024 00:12
@stephenchouca stephenchouca force-pushed the tablegen_multiple_type_casts branch from 5a307be to 8a9513e Compare September 24, 2024 17:31
Copy link

github-actions bot commented Sep 24, 2024

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

@stephenchouca stephenchouca force-pushed the tablegen_multiple_type_casts branch 3 times, most recently from 3b8aa40 to cbf6f65 Compare September 24, 2024 17:41
@stephenchouca stephenchouca force-pushed the tablegen_multiple_type_casts branch from cbf6f65 to ee9c4ce Compare September 24, 2024 20:15
@stephenchouca stephenchouca force-pushed the tablegen_multiple_type_casts branch 2 times, most recently from 4747f1f to 8bc8eff Compare September 24, 2024 21:20
@stephenchouca stephenchouca force-pushed the tablegen_multiple_type_casts branch 2 times, most recently from a1fae64 to d35fb1f Compare September 26, 2024 05:48
@stephenchouca stephenchouca force-pushed the tablegen_multiple_type_casts branch from d35fb1f to d6cc3d9 Compare September 30, 2024 22:59
Currently, type casts can only be used to pattern match for intrinsics with a single overloaded return value. For instance:
```
def int_foo : Intrinsic<[llvm_anyint_ty], []>;
def : Pat<(i32 (int_foo)), ...>;
```

This patch extends type casts to support matching intrinsics with multiple overloaded return values. As an example, the following defines a pattern that matches only if the overloaded intrinsic call returns an `i16` for the first result and an `i32` for the second result:
```
def int_bar : Intrinsic<[llvm_anyint_ty, llvm_anyint_ty], []>;
def : Pat<([i16, i32] (int_bar)), ...>;
```
@stephenchouca stephenchouca force-pushed the tablegen_multiple_type_casts branch from d6cc3d9 to ed2b750 Compare September 30, 2024 23:00
@stephenchouca
Copy link
Contributor Author

It'd be great if someone can merge this pull request for me; I don't have write access. Thanks!

@arsenm arsenm merged commit ec61311 into llvm:main Oct 1, 2024
8 checks passed
@stephenchouca stephenchouca deleted the tablegen_multiple_type_casts branch October 2, 2024 17:03
Sterling-Augustine pushed a commit to Sterling-Augustine/llvm-project that referenced this pull request Oct 3, 2024
…lvm#109728)

Currently, type casts can only be used to pattern match for intrinsics
with a single overloaded return value. For instance:
```
def int_foo : Intrinsic<[llvm_anyint_ty], []>;
def : Pat<(i32 (int_foo)), ...>;
```

This patch extends type casts to support matching intrinsics with
multiple overloaded return values. As an example, the following defines
a pattern that matches only if the overloaded intrinsic call returns an
`i16` for the first result and an `i32` for the second result:
```
def int_bar : Intrinsic<[llvm_anyint_ty, llvm_anyint_ty], []>;
def : Pat<([i16, i32] (int_bar)), ...>;
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
llvm:SelectionDAG SelectionDAGISel as well tablegen
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants