[llvm] [TableGen] Make `!and` short-circuit when either of the operand is zero (PR #113963)

Min-Yih Hsu via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 30 11:59:25 PDT 2024


https://github.com/mshockwave updated https://github.com/llvm/llvm-project/pull/113963

>From 425ddc6c5d96fdda6aa23d5c95216a5ef2fcfc1c Mon Sep 17 00:00:00 2001
From: Min Hsu <min.hsu at sifive.com>
Date: Mon, 28 Oct 2024 09:46:20 -0700
Subject: [PATCH 1/2] [TableGen] Make `!and` short-circuit when either of the
 operand is zero

By preemptively simplifying the result of `!and`, we can fold some of
the conditional operators, like `!if` or `!cond`, as early as possible.
---
 llvm/lib/TableGen/Record.cpp     | 17 +++++++++++++++++
 llvm/test/TableGen/true-false.td | 14 ++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index 1d71482b020b22..1f01b82685718a 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -1543,6 +1543,23 @@ const Init *BinOpInit::resolveReferences(Resolver &R) const {
   const Init *lhs = LHS->resolveReferences(R);
   const Init *rhs = RHS->resolveReferences(R);
 
+  if (getOpcode() == AND) {
+    // Short-circuit. Regardless whether this is a logical or bitwise
+    // AND.
+    if (lhs != LHS)
+      if (const auto *LHSi = dyn_cast_or_null<IntInit>(
+              lhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
+        if (!LHSi->getValue())
+          return LHSi;
+      }
+    if (rhs != RHS)
+      if (const auto *RHSi = dyn_cast_or_null<IntInit>(
+              rhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
+        if (!RHSi->getValue())
+          return RHSi;
+      }
+  }
+
   if (LHS != lhs || RHS != rhs)
     return (BinOpInit::get(getOpcode(), lhs, rhs, getType()))
         ->Fold(R.getCurrentRecord());
diff --git a/llvm/test/TableGen/true-false.td b/llvm/test/TableGen/true-false.td
index 597ad9f5ecc8e7..a9094884dbdb9f 100644
--- a/llvm/test/TableGen/true-false.td
+++ b/llvm/test/TableGen/true-false.td
@@ -67,6 +67,20 @@ def rec7 {
   bits<3> flags = { true, false, true };
 }
 
+// The `!and` should be short-circuit such that `!tail` on empty list will never
+// be evaluated.
+// CHECK: def rec8
+// CHECK:   list<int> newSeq = [];
+// CHECK:   list<int> newSeq2 = [];
+
+class Foo <list<int> seq = []> {
+  bit containsStr = !ne(!find(NAME, "BAR"), -1);
+  list<int> newSeq  = !if(!and(!not(!empty(seq)), containsStr), !tail(seq), seq);
+  list<int> newSeq2 = !if(!and(containsStr, !not(!empty(seq))), !tail(seq), seq);
+}
+
+def rec8 : Foo<>;
+
 #ifdef ERROR1
 // ERROR1: Record name '1' is not a string
 

>From 8cce6cecbf060c11e710ee2c6f1fcb8e8ba5d2c1 Mon Sep 17 00:00:00 2001
From: Min Hsu <min.hsu at sifive.com>
Date: Wed, 30 Oct 2024 11:58:37 -0700
Subject: [PATCH 2/2] Add short-circuit for `!or` and add documentations

---
 llvm/docs/TableGen/ProgRef.rst   |  6 ++++--
 llvm/lib/TableGen/Record.cpp     | 34 +++++++++++++++++++-------------
 llvm/test/TableGen/true-false.td |  6 +++++-
 3 files changed, 29 insertions(+), 17 deletions(-)

diff --git a/llvm/docs/TableGen/ProgRef.rst b/llvm/docs/TableGen/ProgRef.rst
index 5cf48d6ed29786..08614beb937bde 100644
--- a/llvm/docs/TableGen/ProgRef.rst
+++ b/llvm/docs/TableGen/ProgRef.rst
@@ -1646,7 +1646,8 @@ and non-0 as true.
 ``!and(``\ *a*\ ``,`` *b*\ ``, ...)``
     This operator does a bitwise AND on *a*, *b*, etc., and produces the
     result. A logical AND can be performed if all the arguments are either
-    0 or 1.
+    0 or 1. This operator is short-circuit to 0 when one of the operands
+    is 0.
 
 ``!cast<``\ *type*\ ``>(``\ *a*\ ``)``
     This operator performs a cast on *a* and produces the result.
@@ -1872,7 +1873,8 @@ and non-0 as true.
 ``!or(``\ *a*\ ``,`` *b*\ ``, ...)``
     This operator does a bitwise OR on *a*, *b*, etc., and produces the
     result. A logical OR can be performed if all the arguments are either
-    0 or 1.
+    0 or 1. This operator is short-circuit to -1 (all ones) if one of the
+    operands is -1.
 
 ``!range([``\ *start*\ ``,]`` *end*\ ``[,``\ *step*\ ``])``
     This operator produces half-open range sequence ``[start : end : step)`` as
diff --git a/llvm/lib/TableGen/Record.cpp b/llvm/lib/TableGen/Record.cpp
index 1f01b82685718a..311b1e3adedf86 100644
--- a/llvm/lib/TableGen/Record.cpp
+++ b/llvm/lib/TableGen/Record.cpp
@@ -1543,21 +1543,27 @@ const Init *BinOpInit::resolveReferences(Resolver &R) const {
   const Init *lhs = LHS->resolveReferences(R);
   const Init *rhs = RHS->resolveReferences(R);
 
-  if (getOpcode() == AND) {
+  unsigned Opc = getOpcode();
+  if (Opc == AND || Opc == OR) {
     // Short-circuit. Regardless whether this is a logical or bitwise
-    // AND.
-    if (lhs != LHS)
-      if (const auto *LHSi = dyn_cast_or_null<IntInit>(
-              lhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
-        if (!LHSi->getValue())
-          return LHSi;
-      }
-    if (rhs != RHS)
-      if (const auto *RHSi = dyn_cast_or_null<IntInit>(
-              rhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
-        if (!RHSi->getValue())
-          return RHSi;
-      }
+    // AND/OR.
+    // Ideally we could also short-circuit `!or(true, ...)`, but it's
+    // difficult to do it right without knowing if rest of the operands
+    // are all `bit` or not. Therefore, we're only implementing a relatively
+    // limited version of short-circuit against all ones (`true` is casted
+    // to 1 rather than all ones before we evaluate `!or`).
+    if (const auto *LHSi = dyn_cast_or_null<IntInit>(
+            lhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
+      if ((Opc == AND && !LHSi->getValue()) ||
+          (Opc == OR && LHSi->getValue() == -1))
+        return LHSi;
+    }
+    if (const auto *RHSi = dyn_cast_or_null<IntInit>(
+            rhs->convertInitializerTo(IntRecTy::get(getRecordKeeper())))) {
+      if ((Opc == AND && !RHSi->getValue()) ||
+          (Opc == OR && RHSi->getValue() == -1))
+        return RHSi;
+    }
   }
 
   if (LHS != lhs || RHS != rhs)
diff --git a/llvm/test/TableGen/true-false.td b/llvm/test/TableGen/true-false.td
index a9094884dbdb9f..b86569fceb7c94 100644
--- a/llvm/test/TableGen/true-false.td
+++ b/llvm/test/TableGen/true-false.td
@@ -67,16 +67,20 @@ def rec7 {
   bits<3> flags = { true, false, true };
 }
 
-// The `!and` should be short-circuit such that `!tail` on empty list will never
+// `!and` and `!or` should be short-circuit such that `!tail` on empty list will never
 // be evaluated.
 // CHECK: def rec8
 // CHECK:   list<int> newSeq = [];
 // CHECK:   list<int> newSeq2 = [];
+// CHECK:   list<int> newSeq3 = [];
+// CHECK:   list<int> newSeq4 = [];
 
 class Foo <list<int> seq = []> {
   bit containsStr = !ne(!find(NAME, "BAR"), -1);
   list<int> newSeq  = !if(!and(!not(!empty(seq)), containsStr), !tail(seq), seq);
   list<int> newSeq2 = !if(!and(containsStr, !not(!empty(seq))), !tail(seq), seq);
+  list<int> newSeq3 = !if(!or(containsStr, -1), seq, !tail(seq));
+  list<int> newSeq4 = !if(!or(-1, containsStr), seq, !tail(seq));
 }
 
 def rec8 : Foo<>;



More information about the llvm-commits mailing list