-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
[ValueTracking] Handle nonnull attributes at callsite #124908
Conversation
@llvm/pr-subscribers-llvm-ir @llvm/pr-subscribers-llvm-transforms Author: Yingwei Zheng (dtcxzyw) ChangesAlive2: https://alive2.llvm.org/ce/z/yJfskv Full diff: https://github.com/llvm/llvm-project/pull/124908.diff 10 Files Affected:
diff --git a/llvm/include/llvm/IR/InstrTypes.h b/llvm/include/llvm/IR/InstrTypes.h
index 47ddc7555594c5..6ff90e1d095aac 100644
--- a/llvm/include/llvm/IR/InstrTypes.h
+++ b/llvm/include/llvm/IR/InstrTypes.h
@@ -1591,6 +1591,14 @@ class CallBase : public Instruction {
/// Determine whether the argument or parameter has the given attribute.
bool paramHasAttr(unsigned ArgNo, Attribute::AttrKind Kind) const;
+ /// Return true if this argument has the nonnull attribute on either the
+ /// CallBase instruction or the called function. Also returns true if at least
+ /// one byte is known to be dereferenceable and the pointer is in
+ /// addrspace(0). If \p AllowUndefOrPoison is true, respect the semantics of
+ /// nonnull attribute and return true even if the argument can be undef or
+ /// poison.
+ bool paramHasNonNullAttr(unsigned ArgNo, bool AllowUndefOrPoison) const;
+
/// Get the attribute of a given kind at a position.
Attribute getAttributeAtIndex(unsigned i, Attribute::AttrKind Kind) const {
return getAttributes().getAttributeAtIndex(i, Kind);
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index b63a0a07f7de29..e83fad07dd32f0 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2660,13 +2660,16 @@ static bool isKnownNonNullFromDominatingCondition(const Value *V,
// If the value is used as an argument to a call or invoke, then argument
// attributes may provide an answer about null-ness.
- if (const auto *CB = dyn_cast<CallBase>(U))
- if (auto *CalledFunc = CB->getCalledFunction())
- for (const Argument &Arg : CalledFunc->args())
- if (CB->getArgOperand(Arg.getArgNo()) == V &&
- Arg.hasNonNullAttr(/* AllowUndefOrPoison */ false) &&
- DT->dominates(CB, CtxI))
- return true;
+ if (V->getType()->isPointerTy()) {
+ if (const auto *CB = dyn_cast<CallBase>(U))
+ if (auto *CalledFunc = CB->getCalledFunction())
+ for (const Argument &Arg : CalledFunc->args())
+ if (CB->getArgOperand(Arg.getArgNo()) == V &&
+ CB->paramHasNonNullAttr(Arg.getArgNo(),
+ /*AllowUndefOrPoison=*/false) &&
+ DT->dominates(CB, CtxI))
+ return true;
+ }
// If the value is used as a load/store, then the pointer must be non null.
if (V == getLoadStorePointerOperand(U)) {
diff --git a/llvm/lib/IR/Instructions.cpp b/llvm/lib/IR/Instructions.cpp
index c9f5807765e400..ef4a5aca1e8a2a 100644
--- a/llvm/lib/IR/Instructions.cpp
+++ b/llvm/lib/IR/Instructions.cpp
@@ -432,6 +432,24 @@ bool CallBase::paramHasAttr(unsigned ArgNo, Attribute::AttrKind Kind) const {
}
}
+bool CallBase::paramHasNonNullAttr(unsigned ArgNo,
+ bool AllowUndefOrPoison) const {
+ assert(getArgOperand(ArgNo)->getType()->isPointerTy() &&
+ "Argument must be a pointer");
+ if (paramHasAttr(ArgNo, Attribute::NonNull) &&
+ (AllowUndefOrPoison || paramHasAttr(ArgNo, Attribute::NoUndef)))
+ return true;
+
+ Attribute Attr = getParamAttr(ArgNo, Attribute::Dereferenceable);
+ if (Attr.isValid() && Attr.getDereferenceableBytes() > 0 &&
+ !NullPointerIsDefined(
+ getCaller(),
+ getArgOperand(ArgNo)->getType()->getPointerAddressSpace()))
+ return true;
+
+ return false;
+}
+
bool CallBase::hasFnAttrOnCalledFunction(Attribute::AttrKind Kind) const {
if (auto *F = dyn_cast<Function>(getCalledOperand()))
return F->getAttributes().hasFnAttr(Kind);
diff --git a/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll b/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll
index 79d2653a3a1466..dff7e13b8a2e77 100644
--- a/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll
+++ b/llvm/test/Analysis/ValueTracking/known-nonnull-at.ll
@@ -220,3 +220,70 @@ return:
%retval.0 = phi ptr [ %1, %if.end ], [ null, %entry ]
ret ptr %retval.0
}
+
+define i1 @test_known_nonnull_at_callsite(ptr %src) {
+; CHECK-LABEL: @test_known_nonnull_at_callsite(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: call void @callee(ptr noundef nonnull [[SRC:%.*]])
+; CHECK-NEXT: ret i1 false
+;
+entry:
+ call void @callee(ptr noundef nonnull %src)
+ %nonnull = icmp eq ptr %src, null
+ ret i1 %nonnull
+}
+
+define i1 @test_known_nonnull_mixed(ptr %src) {
+; CHECK-LABEL: @test_known_nonnull_mixed(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: call void @callee2(ptr nonnull [[SRC:%.*]])
+; CHECK-NEXT: ret i1 false
+;
+entry:
+ call void @callee2(ptr nonnull %src)
+ %nonnull = icmp eq ptr %src, null
+ ret i1 %nonnull
+}
+
+define i1 @test_known_nonnull_at_callsite_dereferenceable(ptr %src) {
+; CHECK-LABEL: @test_known_nonnull_at_callsite_dereferenceable(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: call void @callee(ptr dereferenceable(1) [[SRC:%.*]])
+; CHECK-NEXT: ret i1 false
+;
+entry:
+ call void @callee(ptr dereferenceable(1) %src)
+ %nonnull = icmp eq ptr %src, null
+ ret i1 %nonnull
+}
+
+; Negative tests
+
+define i1 @test_known_nonnull_at_callsite_without_noundef(ptr %src) {
+; CHECK-LABEL: @test_known_nonnull_at_callsite_without_noundef(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: call void @callee(ptr nonnull [[SRC:%.*]])
+; CHECK-NEXT: [[NONNULL:%.*]] = icmp eq ptr [[SRC]], null
+; CHECK-NEXT: ret i1 [[NONNULL]]
+;
+entry:
+ call void @callee(ptr nonnull %src)
+ %nonnull = icmp eq ptr %src, null
+ ret i1 %nonnull
+}
+
+define i1 @test_known_nonnull_at_callsite_dereferenceable_null_is_defined(ptr %src) null_pointer_is_valid {
+; CHECK-LABEL: @test_known_nonnull_at_callsite_dereferenceable_null_is_defined(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: call void @callee(ptr dereferenceable(1) [[SRC:%.*]])
+; CHECK-NEXT: [[NONNULL:%.*]] = icmp eq ptr [[SRC]], null
+; CHECK-NEXT: ret i1 [[NONNULL]]
+;
+entry:
+ call void @callee(ptr dereferenceable(1) %src)
+ %nonnull = icmp eq ptr %src, null
+ ret i1 %nonnull
+}
+
+declare void @callee(ptr)
+declare void @callee2(ptr noundef)
diff --git a/llvm/test/Transforms/InstCombine/align-addr.ll b/llvm/test/Transforms/InstCombine/align-addr.ll
index 6ef4d85fe4e412..b77037e592b54b 100644
--- a/llvm/test/Transforms/InstCombine/align-addr.ll
+++ b/llvm/test/Transforms/InstCombine/align-addr.ll
@@ -112,7 +112,7 @@ define void @test3(ptr sret(%struct.s) %a4) {
; Check that the alignment is bumped up the alignment of the sret type.
; CHECK-LABEL: @test3(
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 4 dereferenceable(16) [[A4:%.*]], i8 0, i64 16, i1 false)
-; CHECK-NEXT: call void @use(ptr [[A4]])
+; CHECK-NEXT: call void @use(ptr nonnull [[A4]])
; CHECK-NEXT: ret void
;
call void @llvm.memset.p0.i64(ptr %a4, i8 0, i64 16, i1 false)
diff --git a/llvm/test/Transforms/InstCombine/memset_chk-1.ll b/llvm/test/Transforms/InstCombine/memset_chk-1.ll
index 44b549e400dd85..9020f174fb5b7d 100644
--- a/llvm/test/Transforms/InstCombine/memset_chk-1.ll
+++ b/llvm/test/Transforms/InstCombine/memset_chk-1.ll
@@ -92,7 +92,7 @@ define i32 @test_rauw(ptr %a, ptr %b, ptr %c) {
; CHECK-NEXT: [[CALL49:%.*]] = call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[A:%.*]])
; CHECK-NEXT: [[ADD180:%.*]] = add i64 [[CALL49]], 1
; CHECK-NEXT: [[YO107:%.*]] = call i64 @llvm.objectsize.i64.p0(ptr [[B:%.*]], i1 false, i1 false, i1 false)
-; CHECK-NEXT: [[CALL50:%.*]] = call ptr @__memmove_chk(ptr [[B]], ptr [[A]], i64 [[ADD180]], i64 [[YO107]])
+; CHECK-NEXT: [[CALL50:%.*]] = call ptr @__memmove_chk(ptr [[B]], ptr nonnull [[A]], i64 [[ADD180]], i64 [[YO107]])
; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[B]])
; CHECK-NEXT: [[STRCHR1:%.*]] = getelementptr inbounds i8, ptr [[B]], i64 [[STRLEN]]
; CHECK-NEXT: [[D:%.*]] = load ptr, ptr [[C:%.*]], align 8
@@ -100,7 +100,7 @@ define i32 @test_rauw(ptr %a, ptr %b, ptr %c) {
; CHECK-NEXT: [[SUB183:%.*]] = ptrtoint ptr [[B]] to i64
; CHECK-NEXT: [[SUB184:%.*]] = sub i64 [[SUB182]], [[SUB183]]
; CHECK-NEXT: [[ADD52_I_I:%.*]] = add nsw i64 [[SUB184]], 1
-; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 1 [[STRCHR1]], i8 0, i64 [[ADD52_I_I]], i1 false)
+; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr nonnull align 1 [[STRCHR1]], i8 0, i64 [[ADD52_I_I]], i1 false)
; CHECK-NEXT: ret i32 4
;
entry:
diff --git a/llvm/test/Transforms/InstCombine/sprintf-1.ll b/llvm/test/Transforms/InstCombine/sprintf-1.ll
index 0749015059415c..1d87758340f710 100644
--- a/llvm/test/Transforms/InstCombine/sprintf-1.ll
+++ b/llvm/test/Transforms/InstCombine/sprintf-1.ll
@@ -103,7 +103,7 @@ define i32 @test_simplify7(ptr %dst, ptr %str) {
; NOSTPCPY-LABEL: @test_simplify7(
; NOSTPCPY-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[STR:%.*]])
; NOSTPCPY-NEXT: [[LENINC:%.*]] = add i32 [[STRLEN]], 1
-; NOSTPCPY-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST:%.*]], ptr align 1 [[STR]], i32 [[LENINC]], i1 false)
+; NOSTPCPY-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST:%.*]], ptr nonnull align 1 [[STR]], i32 [[LENINC]], i1 false)
; NOSTPCPY-NEXT: ret i32 [[STRLEN]]
;
%r = call i32 (ptr, ptr, ...) @sprintf(ptr %dst, ptr @percent_s, ptr %str)
@@ -133,7 +133,7 @@ define i32 @test_simplify9(ptr %dst, ptr %str) {
; NOSTPCPY-LABEL: @test_simplify9(
; NOSTPCPY-NEXT: [[STRLEN:%.*]] = call i32 @strlen(ptr noundef nonnull dereferenceable(1) [[STR:%.*]])
; NOSTPCPY-NEXT: [[LENINC:%.*]] = add i32 [[STRLEN]], 1
-; NOSTPCPY-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST:%.*]], ptr align 1 [[STR]], i32 [[LENINC]], i1 false)
+; NOSTPCPY-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[DST:%.*]], ptr nonnull align 1 [[STR]], i32 [[LENINC]], i1 false)
; NOSTPCPY-NEXT: ret i32 [[STRLEN]]
;
%r = call i32 (ptr, ptr, ...) @sprintf(ptr %dst, ptr @percent_s, ptr %str)
diff --git a/llvm/test/Transforms/InstCombine/stpncpy-1.ll b/llvm/test/Transforms/InstCombine/stpncpy-1.ll
index 87f54918b7d25e..6ef9b425ae9d93 100644
--- a/llvm/test/Transforms/InstCombine/stpncpy-1.ll
+++ b/llvm/test/Transforms/InstCombine/stpncpy-1.ll
@@ -70,11 +70,11 @@ define void @fold_stpncpy_overlap(ptr %dst, i64 %n) {
define void @call_stpncpy_overlap(ptr %dst, i64 %n) {
; ANY-LABEL: @call_stpncpy_overlap(
; ANY-NEXT: [[ES_2:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[DST]], i64 2)
-; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_2]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_2]])
; ANY-NEXT: [[ES_3:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST]], ptr noundef nonnull dereferenceable(1) [[DST]], i64 3)
-; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_3]])
-; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr [[DST]], ptr [[DST]], i64 [[N:%.*]])
-; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_N]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_3]])
+; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr nonnull [[DST]], ptr nonnull [[DST]], i64 [[N:%.*]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_N]])
; ANY-NEXT: ret void
;
; Do not transform stpncpy(D, D, 2).
@@ -428,9 +428,9 @@ define void @fold_stpncpy_s(ptr %dst, ptr %src) {
define void @call_stpncpy_s(ptr %dst, ptr %src, i64 %n) {
; ANY-LABEL: @call_stpncpy_s(
; ANY-NEXT: [[ES_2:%.*]] = call ptr @stpncpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[SRC:%.*]], i64 2)
-; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_2]])
-; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr [[DST]], ptr [[SRC]], i64 [[N:%.*]])
-; ANY-NEXT: call void @sink(ptr [[DST]], ptr [[ES_N]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_2]])
+; ANY-NEXT: [[ES_N:%.*]] = call ptr @stpncpy(ptr nonnull [[DST]], ptr nonnull [[SRC]], i64 [[N:%.*]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], ptr [[ES_N]])
; ANY-NEXT: ret void
;
; Do not transform stpncpy(D, S, 2). Both *D and *S must be derefernceable
diff --git a/llvm/test/Transforms/InstCombine/strlcpy-1.ll b/llvm/test/Transforms/InstCombine/strlcpy-1.ll
index fd9d0580426f0a..ad538259ae9625 100644
--- a/llvm/test/Transforms/InstCombine/strlcpy-1.ll
+++ b/llvm/test/Transforms/InstCombine/strlcpy-1.ll
@@ -229,18 +229,18 @@ define void @fold_strlcpy_s_0(ptr %dst, ptr %s, i64 %n) {
define void @call_strlcpy_s0_n(ptr %dst, ptr %s, i64 %n) {
; ANY-LABEL: @call_strlcpy_s0_n(
; ANY-NEXT: [[NS_2:%.*]] = call i64 @strlcpy(ptr noundef nonnull dereferenceable(1) [[DST:%.*]], ptr noundef nonnull dereferenceable(1) [[S:%.*]], i64 2)
-; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS_2]])
-; ANY-NEXT: [[NS_N:%.*]] = call i64 @strlcpy(ptr [[DST]], ptr noundef nonnull dereferenceable(1) [[S]], i64 [[N:%.*]])
-; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS_N]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS_2]])
+; ANY-NEXT: [[NS_N:%.*]] = call i64 @strlcpy(ptr nonnull [[DST]], ptr noundef nonnull dereferenceable(1) [[S]], i64 [[N:%.*]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS_N]])
; ANY-NEXT: [[NZ:%.*]] = or i64 [[N]], 1
; ANY-NEXT: [[NS_NZ:%.*]] = call i64 @strlcpy(ptr noundef nonnull dereferenceable(1) [[DST]], ptr noundef nonnull dereferenceable(1) [[S]], i64 [[NZ]])
-; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS_NZ]])
-; ANY-NEXT: [[NS0_N:%.*]] = call i64 @strlcpy(ptr [[DST]], ptr noundef nonnull dereferenceable(1) getelementptr inbounds nuw (i8, ptr @s4, i64 4), i64 [[N]])
-; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS0_N]])
-; ANY-NEXT: [[NS1_N:%.*]] = call i64 @strlcpy(ptr [[DST]], ptr noundef nonnull dereferenceable(1) getelementptr inbounds nuw (i8, ptr @s4, i64 3), i64 [[N]])
-; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS1_N]])
-; ANY-NEXT: [[NS4_N:%.*]] = call i64 @strlcpy(ptr [[DST]], ptr noundef nonnull dereferenceable(1) @s4, i64 [[N]])
-; ANY-NEXT: call void @sink(ptr [[DST]], i64 [[NS4_N]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS_NZ]])
+; ANY-NEXT: [[NS0_N:%.*]] = call i64 @strlcpy(ptr nonnull [[DST]], ptr noundef nonnull dereferenceable(1) getelementptr inbounds nuw (i8, ptr @s4, i64 4), i64 [[N]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS0_N]])
+; ANY-NEXT: [[NS1_N:%.*]] = call i64 @strlcpy(ptr nonnull [[DST]], ptr noundef nonnull dereferenceable(1) getelementptr inbounds nuw (i8, ptr @s4, i64 3), i64 [[N]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS1_N]])
+; ANY-NEXT: [[NS4_N:%.*]] = call i64 @strlcpy(ptr nonnull [[DST]], ptr noundef nonnull dereferenceable(1) @s4, i64 [[N]])
+; ANY-NEXT: call void @sink(ptr nonnull [[DST]], i64 [[NS4_N]])
; ANY-NEXT: ret void
;
%ns_2 = call i64 @strlcpy(ptr %dst, ptr %s, i64 2)
diff --git a/llvm/test/Transforms/InstCombine/strstr-1.ll b/llvm/test/Transforms/InstCombine/strstr-1.ll
index 68de7614aad2ba..083ee47bb47d1f 100644
--- a/llvm/test/Transforms/InstCombine/strstr-1.ll
+++ b/llvm/test/Transforms/InstCombine/strstr-1.ll
@@ -58,7 +58,7 @@ define ptr @test_simplify4(ptr %str) {
define i1 @test_simplify5(ptr %str, ptr %pat) {
; CHECK-LABEL: @test_simplify5(
; CHECK-NEXT: [[STRLEN:%.*]] = call i64 @strlen(ptr noundef nonnull dereferenceable(1) [[PAT:%.*]])
-; CHECK-NEXT: [[STRNCMP:%.*]] = call i32 @strncmp(ptr [[STR:%.*]], ptr [[PAT]], i64 [[STRLEN]])
+; CHECK-NEXT: [[STRNCMP:%.*]] = call i32 @strncmp(ptr [[STR:%.*]], ptr nonnull [[PAT]], i64 [[STRLEN]])
; CHECK-NEXT: [[CMP1:%.*]] = icmp eq i32 [[STRNCMP]], 0
; CHECK-NEXT: ret i1 [[CMP1]]
;
|
You can test this locally with the following command:git diff -U0 --pickaxe-regex -S '([^a-zA-Z0-9#_-]undef[^a-zA-Z0-9_-]|UndefValue::get)' 98d6dd39887361ff2161401614da0b7854234419 c18530d365188617806e9efae399fe1a66e758a5 llvm/include/llvm/IR/InstrTypes.h llvm/lib/Analysis/ValueTracking.cpp llvm/lib/IR/Instructions.cpp llvm/test/Analysis/ValueTracking/known-nonnull-at.ll llvm/test/Transforms/InstCombine/align-addr.ll llvm/test/Transforms/InstCombine/memset_chk-1.ll llvm/test/Transforms/InstCombine/sprintf-1.ll llvm/test/Transforms/InstCombine/stpncpy-1.ll llvm/test/Transforms/InstCombine/strlcpy-1.ll llvm/test/Transforms/InstCombine/strncpy-4.ll llvm/test/Transforms/InstCombine/strstr-1.ll llvm/test/Transforms/InstSimplify/known-non-zero-opaque-ptrs.ll The following files introduce new uses of undef:
Undef is now deprecated and should only be used in the rare cases where no replacement is possible. For example, a load of uninitialized memory yields In tests, avoid using For example, this is considered a bad practice: define void @fn() {
...
br i1 undef, ...
} Please use the following instead: define void @fn(i1 %cond) {
...
br i1 %cond, ...
} Please refer to the Undefined Behavior Manual for more information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
As a possible followup, you might consider adding support for nonnull+noundef to LVI. |
LLVM Buildbot has detected a new failure on builder Full details are available at: https://lab.llvm.org/buildbot/#/builders/73/builds/12667 Here is the relevant piece of the build log for the reference
|
This patch is the followup of #124908.
This patch is the followup of llvm/llvm-project#124908.
Alive2: https://alive2.llvm.org/ce/z/yJfskv
Closes #124540.