Index: llvm/trunk/docs/LangRef.rst =================================================================== --- llvm/trunk/docs/LangRef.rst +++ llvm/trunk/docs/LangRef.rst @@ -15417,6 +15417,51 @@ This intrinsic actually does nothing, but optimizers must assume that it has externally observable side effects. +'``llvm.is.constant.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use llvm.is.constant with any argument type. + +:: + + declare i1 @llvm.is.constant.i32(i32 %operand) nounwind readnone + declare i1 @llvm.is.constant.f32(float %operand) nounwind readnone + declare i1 @llvm.is.constant.TYPENAME(TYPE %operand) nounwind readnone + +Overview: +""""""""" + +The '``llvm.is.constant``' intrinsic will return true if the argument +is known to be a manifest compile-time constant. It is guaranteed to +fold to either true or false before generating machine code. + +Semantics: +"""""""""" + +This intrinsic generates no code. If its argument is known to be a +manifest compile-time constant value, then the intrinsic will be +converted to a constant true value. Otherwise, it will be converted to +a constant false value. + +In particular, note that if the argument is a constant expression +which refers to a global (the address of which _is_ a constant, but +not manifest during the compile), then the intrinsic evaluates to +false. + +The result also intentionally depends on the result of optimization +passes -- e.g., the result can change depending on whether a +function gets inlined or not. A function's parameters are +obviously not constant. However, a call like +``llvm.is.constant.i32(i32 %param)`` *can* return true after the +function is inlined, if the value passed to the function parameter was +a constant. + +On the other hand, if constant folding is not run, it will never +evaluate to true, even in simple cases. + Stack Map Intrinsics -------------------- Index: llvm/trunk/include/llvm/Analysis/TargetTransformInfoImpl.h =================================================================== --- llvm/trunk/include/llvm/Analysis/TargetTransformInfoImpl.h +++ llvm/trunk/include/llvm/Analysis/TargetTransformInfoImpl.h @@ -160,6 +160,7 @@ case Intrinsic::invariant_end: case Intrinsic::launder_invariant_group: case Intrinsic::strip_invariant_group: + case Intrinsic::is_constant: case Intrinsic::lifetime_start: case Intrinsic::lifetime_end: case Intrinsic::objectsize: Index: llvm/trunk/include/llvm/IR/Intrinsics.td =================================================================== --- llvm/trunk/include/llvm/IR/Intrinsics.td +++ llvm/trunk/include/llvm/IR/Intrinsics.td @@ -900,6 +900,10 @@ def int_clear_cache : Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty], [], "llvm.clear_cache">; +// Intrinsic to detect whether its argument is a constant. +def int_is_constant : Intrinsic<[llvm_i1_ty], [llvm_any_ty], [IntrNoMem], "llvm.is.constant">; + + //===-------------------------- Masked Intrinsics -------------------------===// // def int_masked_store : Intrinsic<[], [llvm_anyvector_ty, Index: llvm/trunk/lib/Analysis/ConstantFolding.cpp =================================================================== --- llvm/trunk/lib/Analysis/ConstantFolding.cpp +++ llvm/trunk/lib/Analysis/ConstantFolding.cpp @@ -1426,6 +1426,7 @@ case Intrinsic::x86_avx512_vcvtsd2usi64: case Intrinsic::x86_avx512_cvttsd2usi: case Intrinsic::x86_avx512_cvttsd2usi64: + case Intrinsic::is_constant: return true; default: return false; @@ -1600,11 +1601,32 @@ return APF.convertToDouble(); } +static bool isManifestConstant(const Constant *c) { + if (isa(c)) { + return true; + } else if (isa(c) || isa(c)) { + for (const Value *subc : c->operand_values()) { + if (!isManifestConstant(cast(subc))) + return false; + } + return true; + } + return false; +} + Constant *ConstantFoldScalarCall(StringRef Name, unsigned IntrinsicID, Type *Ty, ArrayRef Operands, const TargetLibraryInfo *TLI, ImmutableCallSite CS) { if (Operands.size() == 1) { + if (IntrinsicID == Intrinsic::is_constant) { + // 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. + if (isManifestConstant(Operands[0])) + return ConstantInt::getTrue(Ty->getContext()); + return nullptr; + } if (isa(Operands[0])) { // cosine(arg) is between -1 and 1. cosine(invalid arg) is NaN if (IntrinsicID == Intrinsic::cos) Index: llvm/trunk/lib/CodeGen/CodeGenPrepare.cpp =================================================================== --- llvm/trunk/lib/CodeGen/CodeGenPrepare.cpp +++ llvm/trunk/lib/CodeGen/CodeGenPrepare.cpp @@ -321,6 +321,24 @@ } private: + template + void resetIteratorIfInvalidatedWhileCalling(BasicBlock *BB, F f) { + // Substituting can cause recursive simplifications, which can invalidate + // our iterator. Use a WeakTrackingVH to hold onto it in case this + // happens. + Value *CurValue = &*CurInstIterator; + WeakTrackingVH IterHandle(CurValue); + + f(); + + // If the iterator instruction was recursively deleted, start over at the + // start of the block. + if (IterHandle != CurValue) { + CurInstIterator = BB->begin(); + SunkAddrs.clear(); + } + } + bool eliminateFallThrough(Function &F); bool eliminateMostlyEmptyBlocks(Function &F); BasicBlock *findDestBlockOfMergeableEmptyBlock(BasicBlock *BB); @@ -1690,21 +1708,18 @@ // Lower all uses of llvm.objectsize.* ConstantInt *RetVal = lowerObjectSizeCall(II, *DL, TLInfo, /*MustSucceed=*/true); - // Substituting this can cause recursive simplifications, which can - // invalidate our iterator. Use a WeakTrackingVH to hold onto it in case - // this - // happens. - Value *CurValue = &*CurInstIterator; - WeakTrackingVH IterHandle(CurValue); - replaceAndRecursivelySimplify(CI, RetVal, TLInfo, nullptr); - - // If the iterator instruction was recursively deleted, start over at the - // start of the block. - if (IterHandle != CurValue) { - CurInstIterator = BB->begin(); - SunkAddrs.clear(); - } + resetIteratorIfInvalidatedWhileCalling(BB, [&]() { + replaceAndRecursivelySimplify(CI, RetVal, TLInfo, nullptr); + }); + return true; + } + case Intrinsic::is_constant: { + // If is_constant hasn't folded away yet, lower it to false now. + Constant *RetVal = ConstantInt::get(II->getType(), 0); + resetIteratorIfInvalidatedWhileCalling(BB, [&]() { + replaceAndRecursivelySimplify(CI, RetVal, TLInfo, nullptr); + }); return true; } case Intrinsic::aarch64_stlxr: Index: llvm/trunk/lib/CodeGen/GlobalISel/IRTranslator.cpp =================================================================== --- llvm/trunk/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ llvm/trunk/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -947,6 +947,11 @@ MIRBuilder.buildConstant(getOrCreateVReg(CI), Min->isZero() ? -1ULL : 0); return true; } + case Intrinsic::is_constant: + // If this wasn't constant-folded away by now, then it's not a + // constant. + MIRBuilder.buildConstant(getOrCreateVReg(CI), 0); + return true; case Intrinsic::stackguard: getStackGuard(getOrCreateVReg(CI), MIRBuilder); return true; Index: llvm/trunk/lib/CodeGen/SelectionDAG/FastISel.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/FastISel.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/FastISel.cpp @@ -1450,6 +1450,14 @@ updateValueMap(II, ResultReg); return true; } + case Intrinsic::is_constant: { + Constant *ResCI = ConstantInt::get(II->getType(), 0); + unsigned ResultReg = getRegForValue(ResCI); + if (!ResultReg) + return false; + updateValueMap(II, ResultReg); + return true; + } case Intrinsic::launder_invariant_group: case Intrinsic::strip_invariant_group: case Intrinsic::expect: { Index: llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ llvm/trunk/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5897,6 +5897,13 @@ setValue(&I, Res); return nullptr; } + + case Intrinsic::is_constant: + // If this wasn't constant-folded away by now, then it's not a + // constant. + setValue(&I, DAG.getConstant(0, sdl, MVT::i1)); + return nullptr; + case Intrinsic::annotation: case Intrinsic::ptr_annotation: case Intrinsic::launder_invariant_group: Index: llvm/trunk/lib/Transforms/Scalar/SCCP.cpp =================================================================== --- llvm/trunk/lib/Transforms/Scalar/SCCP.cpp +++ llvm/trunk/lib/Transforms/Scalar/SCCP.cpp @@ -1230,6 +1230,8 @@ SmallVector Operands; for (CallSite::arg_iterator AI = CS.arg_begin(), E = CS.arg_end(); AI != E; ++AI) { + if (AI->get()->getType()->isStructTy()) + return markOverdefined(I); // Can't handle struct args. LatticeVal State = getValueState(*AI); if (State.isUnknown()) Index: llvm/trunk/test/CodeGen/Generic/is-constant.ll =================================================================== --- llvm/trunk/test/CodeGen/Generic/is-constant.ll +++ llvm/trunk/test/CodeGen/Generic/is-constant.ll @@ -0,0 +1,114 @@ +; RUN: opt -O2 -S < %s | FileCheck %s +; RUN: llc -o /dev/null 2>&1 < %s +; RUN: llc -O0 -o /dev/null 2>&1 < %s + +;; The llc runs above are just to ensure it doesn't blow up upon +;; seeing an is_constant intrinsic. + +declare i1 @llvm.is.constant.i32(i32 %a) +declare i1 @llvm.is.constant.i64(i64 %a) +declare i1 @llvm.is.constant.i256(i256 %a) +declare i1 @llvm.is.constant.v2i64(<2 x i64> %a) +declare i1 @llvm.is.constant.f32(float %a) +declare i1 @llvm.is.constant.sl_i32i32s({i32, i32} %a) +declare i1 @llvm.is.constant.a2i32([2 x i32] %a) +declare i1 @llvm.is.constant.p0i64(i64* %a) + +;; Basic test that optimization folds away the is.constant when given +;; a constant. +define i1 @test_constant() #0 { +; CHECK-LABEL: @test_constant( +; CHECK-NOT: llvm.is.constant +; CHECK: ret i1 true +%y = call i1 @llvm.is.constant.i32(i32 44) + ret i1 %y +} + +;; And test that the intrinsic sticks around when given a +;; non-constant. +define i1 @test_nonconstant(i32 %x) #0 { +; CHECK-LABEL: @test_nonconstant( +; CHECK: @llvm.is.constant + %y = call i1 @llvm.is.constant.i32(i32 %x) + ret i1 %y +} + +;; Ensure that nested is.constants fold. +define i32 @test_nested() #0 { +; CHECK-LABEL: @test_nested( +; CHECK-NOT: llvm.is.constant +; CHECK: ret i32 13 + %val1 = call i1 @llvm.is.constant.i32(i32 27) + %val2 = zext i1 %val1 to i32 + %val3 = add i32 %val2, 12 + %1 = call i1 @llvm.is.constant.i32(i32 %val3) + %2 = zext i1 %1 to i32 + %3 = add i32 %2, 12 + ret i32 %3 +} + +@G = global [2 x i64] zeroinitializer +define i1 @test_global() #0 { +; CHECK-LABEL: @test_global( +; CHECK: llvm.is.constant + %ret = call i1 @llvm.is.constant.p0i64(i64* getelementptr ([2 x i64], [2 x i64]* @G, i32 0, i32 0)) + ret i1 %ret +} + +define i1 @test_diff() #0 { +; CHECK-LABEL: @test_diff( + %ret = call i1 @llvm.is.constant.i64(i64 sub ( + i64 ptrtoint (i64* getelementptr inbounds ([2 x i64], [2 x i64]* @G, i64 0, i64 1) to i64), + i64 ptrtoint ([2 x i64]* @G to i64))) + ret i1 %ret +} + +define i1 @test_various_types(i256 %int, float %float, <2 x i64> %vec, {i32, i32} %struct, [2 x i32] %arr, i64* %ptr) #0 { +; CHECK-LABEL: @test_various_types( +; CHECK: llvm.is.constant +; CHECK: llvm.is.constant +; CHECK: llvm.is.constant +; CHECK: llvm.is.constant +; CHECK: llvm.is.constant +; CHECK: llvm.is.constant +; CHECK-NOT: llvm.is.constant + %v1 = call i1 @llvm.is.constant.i256(i256 %int) + %v2 = call i1 @llvm.is.constant.f32(float %float) + %v3 = call i1 @llvm.is.constant.v2i64(<2 x i64> %vec) + %v4 = call i1 @llvm.is.constant.sl_i32i32s({i32, i32} %struct) + %v5 = call i1 @llvm.is.constant.a2i32([2 x i32] %arr) + %v6 = call i1 @llvm.is.constant.p0i64(i64* %ptr) + + %c1 = call i1 @llvm.is.constant.i256(i256 -1) + %c2 = call i1 @llvm.is.constant.f32(float 17.0) + %c3 = call i1 @llvm.is.constant.v2i64(<2 x i64> ) + %c4 = call i1 @llvm.is.constant.sl_i32i32s({i32, i32} {i32 -1, i32 32}) + %c5 = call i1 @llvm.is.constant.a2i32([2 x i32] [i32 -1, i32 32]) + %c6 = call i1 @llvm.is.constant.p0i64(i64* inttoptr (i32 42 to i64*)) + + %x1 = add i1 %v1, %c1 + %x2 = add i1 %v2, %c2 + %x3 = add i1 %v3, %c3 + %x4 = add i1 %v4, %c4 + %x5 = add i1 %v5, %c5 + %x6 = add i1 %v6, %c6 + + %res2 = add i1 %x1, %x2 + %res3 = add i1 %res2, %x3 + %res4 = add i1 %res3, %x4 + %res5 = add i1 %res4, %x5 + %res6 = add i1 %res5, %x6 + + ret i1 %res6 +} + +define i1 @test_various_types2() #0 { +; CHECK-LABEL: @test_various_types2( +; CHECK: ret i1 false + %r = call i1 @test_various_types(i256 -1, float 22.0, <2 x i64> , + {i32, i32} {i32 -1, i32 55}, [2 x i32] [i32 -1, i32 55], + i64* inttoptr (i64 42 to i64*)) + ret i1 %r +} + +attributes #0 = { nounwind uwtable } Index: llvm/trunk/test/CodeGen/X86/is-constant.ll =================================================================== --- llvm/trunk/test/CodeGen/X86/is-constant.ll +++ llvm/trunk/test/CodeGen/X86/is-constant.ll @@ -0,0 +1,50 @@ +; RUN: llc -O2 < %s | FileCheck %s --check-prefix=CHECK-O2 --check-prefix=CHECK +; RUN: llc -O0 -fast-isel < %s | FileCheck %s --check-prefix=CHECK-O0 --check-prefix=CHECK +; RUN: llc -O0 -fast-isel=0 < %s | FileCheck %s --check-prefix=CHECK-O0 --check-prefix=CHECK +; RUN: llc -O0 -global-isel < %s | FileCheck %s --check-prefix=CHECK-O0 --check-prefix=CHECK + +;; Ensure that an unfoldable is.constant gets lowered reasonably in +;; optimized codegen, in particular, that the "true" branch is +;; eliminated. +;; +;; This isn't asserting any specific output from non-optimized runs, +;; (e.g., currently the not-taken branch does not get eliminated). But +;; it does ensure that lowering succeeds in all 3 codegen paths. + +target triple = "x86_64-unknown-linux-gnu" + +declare i1 @llvm.is.constant.i32(i32 %a) nounwind readnone +declare i1 @llvm.is.constant.i64(i64 %a) nounwind readnone +declare i64 @llvm.objectsize.i64.p0i8(i8*, i1, i1) nounwind readnone + +declare i32 @subfun_1() +declare i32 @subfun_2() + +define i32 @test_branch(i32 %in) nounwind { +; CHECK-LABEL: test_branch: +; CHECK-O2: %bb.0: +; CHECK-O2-NEXT: jmp subfun_2 + %v = call i1 @llvm.is.constant.i32(i32 %in) + br i1 %v, label %True, label %False + +True: + %call1 = tail call i32 @subfun_1() + ret i32 %call1 + +False: + %call2 = tail call i32 @subfun_2() + ret i32 %call2 +} + +;; llvm.objectsize is another tricky case which gets folded to -1 very +;; late in the game. We'd like to ensure that llvm.is.constant of +;; llvm.objectsize is true. +define i1 @test_objectsize(i8* %obj) nounwind { +; CHECK-LABEL: test_objectsize: +; CHECK-O2: %bb.0: +; CHECK-O2: movb $1, %al +; CHECK-O2-NEXT: retq + %os = call i64 @llvm.objectsize.i64.p0i8(i8* %obj, i1 false, i1 false) + %v = call i1 @llvm.is.constant.i64(i64 %os) + ret i1 %v +} Index: llvm/trunk/test/Transforms/SCCP/ipsccp-basic.ll =================================================================== --- llvm/trunk/test/Transforms/SCCP/ipsccp-basic.ll +++ llvm/trunk/test/Transforms/SCCP/ipsccp-basic.ll @@ -258,3 +258,16 @@ } declare i64 @llvm.ctpop.i64(i64) + +;;======================== test12 +;; Ensure that a struct as an arg to a potentially constant-foldable +;; function does not crash SCCP (for now it'll just ignores it) + +define i1 @test12() { + %c = call i1 @llvm.is.constant.sl_i32i32s({i32, i32} {i32 -1, i32 32}) + ret i1 %c +; CHECK-LABEL: define i1 @test12 +; CHECK: ret i1 %c +} + +declare i1 @llvm.is.constant.sl_i32i32s({i32, i32} %a)