diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp --- a/llvm/lib/Analysis/ConstantFolding.cpp +++ b/llvm/lib/Analysis/ConstantFolding.cpp @@ -1840,6 +1840,8 @@ assert(Operands.size() == 1 && "Wrong number of operands."); if (IntrinsicID == Intrinsic::is_constant) { + if (isa(Operands[0])) + return ConstantInt::getFalse(Ty->getContext()); // We know we have a "Constant" argument. But we want to only // return true for manifest constants, not those that depend on // constants with unknowable values, e.g. GlobalValue or BlockAddress. diff --git a/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp b/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp --- a/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp +++ b/llvm/lib/Transforms/Scalar/LowerConstantIntrinsics.cpp @@ -45,10 +45,18 @@ "Number of 'objectsize' intrinsic calls handled"); static Value *lowerIsConstantIntrinsic(IntrinsicInst *II) { - Value *Op = II->getOperand(0); - - return isa(Op) ? ConstantInt::getTrue(II->getType()) - : ConstantInt::getFalse(II->getType()); + SmallVector WorkList; + WorkList.push_back(II->getOperand(0)); + while (WorkList.size()) { + Value *V = WorkList.back(); + WorkList.pop_back(); + if (auto *CA = dyn_cast(V)) + for (Value *Op : CA->operands()) + WorkList.push_back(Op); + else if (!isa(V) || isa(V)) + return ConstantInt::getFalse(II->getType()); + } + return ConstantInt::getTrue(II->getType()); } static bool replaceConditionalBranchesOnConstant(Instruction *II, diff --git a/llvm/test/Transforms/EarlyCSE/is_constant.ll b/llvm/test/Transforms/EarlyCSE/is_constant.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/EarlyCSE/is_constant.ll @@ -0,0 +1,15 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt -passes=early-cse %s -S -o - | FileCheck %s + +; Test that EarlyCSE doesn't turn this into a return of true. +declare i1 @llvm.is.constant.i32(i32) #0 +define dso_local i32 @foo() { +; CHECK-LABEL: @foo( +; CHECK-NEXT: ret i32 0 +; + + %1 = call i1 @llvm.is.constant.i32(i32 undef) + %2 = zext i1 %1 to i32 + ret i32 %2 +} +attributes #0 = { convergent nofree nosync nounwind readnone willreturn } diff --git a/llvm/test/Transforms/InstCombine/intrinsics.ll b/llvm/test/Transforms/InstCombine/intrinsics.ll --- a/llvm/test/Transforms/InstCombine/intrinsics.ll +++ b/llvm/test/Transforms/InstCombine/intrinsics.ll @@ -19,6 +19,7 @@ declare double @llvm.trunc.f64(double %Val) nounwind readonly declare double @llvm.rint.f64(double %Val) nounwind readonly declare double @llvm.nearbyint.f64(double %Val) nounwind readonly +declare i1 @llvm.is.constant.i32(i32) nounwind readonly define void @powi(double %V, double *%P) { ; CHECK-LABEL: @powi( @@ -59,7 +60,7 @@ ; Make sure we don't add range metadata to i1 cttz. define i1 @cttz_i1(i1 %arg) { ; CHECK-LABEL: @cttz_i1( -; CHECK-NEXT: [[CNT:%.*]] = call i1 @llvm.cttz.i1(i1 [[ARG:%.*]], i1 false) #2 +; CHECK-NEXT: [[CNT:%.*]] = call i1 @llvm.cttz.i1(i1 [[ARG:%.*]], i1 false) #3 ; CHECK-NEXT: ret i1 [[CNT]] ; %cnt = call i1 @llvm.cttz.i1(i1 %arg, i1 false) nounwind readnone @@ -89,7 +90,7 @@ define i32 @cttz_knownbits2(i32 %arg) { ; CHECK-LABEL: @cttz_knownbits2( ; CHECK-NEXT: [[OR:%.*]] = or i32 [[ARG:%.*]], 4 -; CHECK-NEXT: [[CNT:%.*]] = call i32 @llvm.cttz.i32(i32 [[OR]], i1 true) #2, !range ![[$CTTZ_RANGE:[0-9]+]] +; CHECK-NEXT: [[CNT:%.*]] = call i32 @llvm.cttz.i32(i32 [[OR]], i1 true) #3, !range ![[$CTTZ_RANGE:[0-9]+]] ; CHECK-NEXT: ret i32 [[CNT]] ; %or = or i32 %arg, 4 @@ -151,7 +152,7 @@ ; Make sure we don't add range metadata to i1 ctlz. define i1 @ctlz_i1(i1 %arg) { ; CHECK-LABEL: @ctlz_i1( -; CHECK-NEXT: [[CNT:%.*]] = call i1 @llvm.ctlz.i1(i1 [[ARG:%.*]], i1 false) #2 +; CHECK-NEXT: [[CNT:%.*]] = call i1 @llvm.ctlz.i1(i1 [[ARG:%.*]], i1 false) #3 ; CHECK-NEXT: ret i1 [[CNT]] ; %cnt = call i1 @llvm.ctlz.i1(i1 %arg, i1 false) nounwind readnone @@ -181,7 +182,7 @@ define i8 @ctlz_knownbits2(i8 %arg) { ; CHECK-LABEL: @ctlz_knownbits2( ; CHECK-NEXT: [[OR:%.*]] = or i8 [[ARG:%.*]], 32 -; CHECK-NEXT: [[CNT:%.*]] = call i8 @llvm.ctlz.i8(i8 [[OR]], i1 true) #2, !range ![[$CTLZ_RANGE:[0-9]+]] +; CHECK-NEXT: [[CNT:%.*]] = call i8 @llvm.ctlz.i8(i8 [[OR]], i1 true) #3, !range ![[$CTLZ_RANGE:[0-9]+]] ; CHECK-NEXT: ret i8 [[CNT]] ; %or = or i8 %arg, 32 @@ -425,5 +426,13 @@ ret void } +define i32 @is_constant() { +; CHECK-LABEL: @is_constant( +; CHECK-NEXT: ret i32 0 + %1 = call i1 @llvm.is.constant.i32(i32 undef) nounwind readonly + %2 = zext i1 %1 to i32 + ret i32 %2 +} + ; CHECK: [[$CTTZ_RANGE]] = !{i32 0, i32 3} ; CHECK: [[$CTLZ_RANGE]] = !{i8 0, i8 3} diff --git a/llvm/test/Transforms/LowerConstantIntrinsics/constant-intrinsics.ll b/llvm/test/Transforms/LowerConstantIntrinsics/constant-intrinsics.ll --- a/llvm/test/Transforms/LowerConstantIntrinsics/constant-intrinsics.ll +++ b/llvm/test/Transforms/LowerConstantIntrinsics/constant-intrinsics.ll @@ -112,3 +112,26 @@ ret i1 %res6 } + +@real_mode_blob_end = external dso_local global [0 x i8], align 1 +define i1 @global_array() { +; CHECK-LABEL: @global_array( +; CHECK-NEXT: ret i1 false + %1 = call i1 @llvm.is.constant.i64(i64 ptrtoint ([0 x i8]* @real_mode_blob_end to i64)) + ret i1 %1 +} + +define i1 @undef1() { +; CHECK-LABEL: undef1( +; CHECK-NEXT: ret i1 false + %1 = call i1 @llvm.is.constant.i64(i64 undef) + ret i1 %1 +} + +@undef_array = dso_local global [2 x i8] [i8 42, i8 undef] +define i1 @undef2() { +; CHECK-LABEL: undef2( +; CHECK-NEXT: ret i1 false + %1 = call i1 @llvm.is.constant.i64(i64 ptrtoint ([2 x i8]* @undef_array to i64)) + ret i1 %1 +}