[llvm] [ValueTracking] Fix KnownBits conflict for calls (range vs returned) (PR #84353)

Björn Pettersson via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 7 10:10:58 PST 2024


https://github.com/bjope created https://github.com/llvm/llvm-project/pull/84353

If a function only exits for certain input values we can still derive that an argument is "returned". We can also derive range metadata that describe the possible value range returned by the function. However, it turns out that those two analyses can result in conflicting information.

Example:
  declare i16 @foo(i16 returned)
  ...
  %A = call i16 @foo(i16 4095), !range !{i16 32, i16 33}

To avoid "Bits known to be one AND zero?" assertion failures we know make sure to discard the known bits for this kind of scenario.

>From bdbf290ed97417a45feafe3a73dc32c1885ed4a5 Mon Sep 17 00:00:00 2001
From: Bjorn Pettersson <bjorn.a.pettersson at ericsson.com>
Date: Thu, 7 Mar 2024 19:06:41 +0100
Subject: [PATCH] [ValueTracking] Fix KnownBits conflict for calls (range vs
 returned)

If a function only exits for certain input values we can still derive
that an argument is "returned". We can also derive range metadata
that describe the possible value range returned by the function.
However, it turns out that those two analyses can result in
conflicting information.

Example:
  declare i16 @foo(i16 returned)
  ...
  %A = call i16 @foo(i16 4095), !range !{i16 32, i16 33}

To avoid "Bits known to be one AND zero?" assertion failures we
know make sure to discard the known bits for this kind of scenario.
---
 llvm/lib/Analysis/ValueTracking.cpp           |  6 ++++++
 llvm/unittests/Analysis/ValueTrackingTest.cpp | 14 ++++++++++++++
 2 files changed, 20 insertions(+)

diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 52ae9f034e5d34..6d0e79e11eed43 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -1476,6 +1476,12 @@ static void computeKnownBitsFromOperator(const Operator *I,
       if (RV->getType() == I->getType()) {
         computeKnownBits(RV, Known2, Depth + 1, Q);
         Known = Known.unionWith(Known2);
+        // If the function doesn't return properly for all input values
+        // (e.g. unreachable exits) then there might be conflicts between the
+        // argument value and the range metadata. Simply discard the known bits
+        // in case of conflicts.
+        if (Known.hasConflict())
+          Known.resetAll();
       }
     }
     if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(I)) {
diff --git a/llvm/unittests/Analysis/ValueTrackingTest.cpp b/llvm/unittests/Analysis/ValueTrackingTest.cpp
index 9e0abe7a16df98..6c6897d83a256e 100644
--- a/llvm/unittests/Analysis/ValueTrackingTest.cpp
+++ b/llvm/unittests/Analysis/ValueTrackingTest.cpp
@@ -2359,6 +2359,20 @@ TEST_F(ComputeKnownBitsTest, ComputeKnownBitsFreeze) {
   EXPECT_EQ(Known.One.getZExtValue(), 0u);
 }
 
+TEST_F(ComputeKnownBitsTest, ComputeKnownBitsReturnedRangeConflict) {
+  parseAssembly(
+      "declare i16 @foo(i16 returned)\n"
+      "\n"
+      "define i16 @test() {\n"
+      "  %A = call i16 @foo(i16 4095), !range !{i16 32, i16 33}\n"
+      "  ret i16 %A\n"
+      "}\n");
+  // The call returns 32 according to range metadata, but 4095 according to the
+  // returned arg operand. Given the conflicting information we expect that the
+  // known bits information simply is cleared.
+  expectKnownBits(/*zero*/ 0u, /*one*/ 0u);
+}
+
 TEST_F(ComputeKnownBitsTest, ComputeKnownBitsAddWithRange) {
   parseAssembly("define void @test(ptr %p) {\n"
                 "  %A = load i64, ptr %p, !range !{i64 64, i64 65536}\n"



More information about the llvm-commits mailing list