Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -7155,6 +7155,36 @@ This instrinsic does *not* empty the instruction pipeline. Modifications of the current function are outside the scope of the intrinsic. +'``llvm.is.constant.*``' Intrinsics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +This is an overloaded intrinsic. You can use llvm.is.constant on any integer bit width. + +:: + + declare i1 @llvm.is.constant.i1(i1) + declare i1 @llvm.is.constant.i16(i16) + declare i1 @llvm.is.constant.i32(i32) + declare i1 @llvm.is.constant.i64(i64) + declare i1 @llvm.is.constant.i256(i256) + +Overview: +""""""""" + +The '``llvm.is.constant``' intrinsic checks if the argument value is +known at compile time, to the best of compiler knowledge. +The result of this intrinsic might depend on the optimisation level +i.e. function inlining. + +Semantics: +"""""""""" + +This function returns true if the argument value is known at compile time, +false otherwise. + Standard C Library Intrinsics ----------------------------- Index: include/llvm/CodeGen/ISDOpcodes.h =================================================================== --- include/llvm/CodeGen/ISDOpcodes.h +++ include/llvm/CodeGen/ISDOpcodes.h @@ -647,6 +647,9 @@ /// is the chain and the second operand is the alloca pointer. LIFETIME_START, LIFETIME_END, + /// This corresponds to llvm.is.constant intrinsics. + IS_CONSTANT, + /// BUILTIN_OP_END - This must be the last enum value in this list. /// The target-specific pre-isel opcode values start here. BUILTIN_OP_END Index: include/llvm/IR/Intrinsics.td =================================================================== --- include/llvm/IR/Intrinsics.td +++ include/llvm/IR/Intrinsics.td @@ -526,6 +526,9 @@ def int_clear_cache : Intrinsic<[], [llvm_ptr_ty, llvm_ptr_ty], [], "llvm.clear_cache">; +// llvm.is.constant intrinsics +def int_is_constant : Intrinsic<[llvm_i1_ty], [llvm_anyint_ty], [IntrNoMem], "llvm.is.constant">; + //===----------------------------------------------------------------------===// // Target-specific intrinsics //===----------------------------------------------------------------------===// Index: include/llvm/Target/TargetSelectionDAG.td =================================================================== --- include/llvm/Target/TargetSelectionDAG.td +++ include/llvm/Target/TargetSelectionDAG.td @@ -496,6 +496,8 @@ def assertsext : SDNode<"ISD::AssertSext", SDT_assertext>; def assertzext : SDNode<"ISD::AssertZext", SDT_assertext>; +def is_constant : SDNode<"ISD::IS_CONSTANT", SDTIntUnaryOp>; + //===----------------------------------------------------------------------===// // Selection DAG Condition Codes Index: lib/Analysis/ConstantFolding.cpp =================================================================== --- lib/Analysis/ConstantFolding.cpp +++ lib/Analysis/ConstantFolding.cpp @@ -1269,6 +1269,7 @@ case Intrinsic::x86_sse2_cvtsd2si64: case Intrinsic::x86_sse2_cvttsd2si: case Intrinsic::x86_sse2_cvttsd2si64: + case Intrinsic::is_constant: return true; default: return false; Index: lib/CodeGen/SelectionDAG/SelectionDAG.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -51,6 +51,8 @@ using namespace llvm; +#define DEBUG_TYPE "selection-dag" + /// makeVTList - Return an instance of the SDVTList struct initialized with the /// specified members. static SDVTList makeVTList(const EVT *VTs, unsigned NumVTs) { @@ -2629,6 +2631,10 @@ case ISD::CTTZ_ZERO_UNDEF: return getConstant(Val.countTrailingZeros(), VT, C->isTargetOpcode(), C->isOpaque()); + case ISD::IS_CONSTANT: { + DEBUG(dbgs() << "Lowering llvm.is.constant to TRUE.\n"); + return getConstant(1, MVT::i1); + } } } @@ -2693,6 +2699,10 @@ unsigned OpOpcode = Operand.getNode()->getOpcode(); switch (Opcode) { + case ISD::IS_CONSTANT: + DEBUG(dbgs() << "Lowering llvm.is.constant to FALSE.\n"); + return getConstant(0, MVT::i1); + case ISD::TokenFactor: case ISD::MERGE_VALUES: case ISD::CONCAT_VECTORS: Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5306,6 +5306,12 @@ setValue(&I, getValue(I.getArgOperand(0))); return nullptr; } + case Intrinsic::is_constant: { + SDValue Arg = getValue(I.getArgOperand(0)); + EVT Ty = Arg.getValueType(); + setValue(&I, DAG.getNode(ISD::IS_CONSTANT, sdl, Ty, Arg)); + return nullptr; + } case Intrinsic::debugtrap: case Intrinsic::trap: { Index: lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -236,6 +236,8 @@ case ISD::FP16_TO_FP32: return "fp16_to_fp32"; case ISD::FP32_TO_FP16: return "fp32_to_fp16"; + case ISD::IS_CONSTANT: return "IsConstant"; + case ISD::CONVERT_RNDSAT: { switch (cast(this)->getCvtCode()) { default: llvm_unreachable("Unknown cvt code!"); Index: test/CodeGen/Generic/inline-is-constant.ll =================================================================== --- /dev/null +++ test/CodeGen/Generic/inline-is-constant.ll @@ -0,0 +1,38 @@ +; RUN: opt -O3 < %s | llc | FileCheck -check-prefix=INLINE %s +; Expected outcome: @inlined_fun is inlined and @llvm.is.constant is resolved to TRUE +; +; +; RUN: llc -debug < %s 2>&1 | FileCheck -check-prefix=NOINLINE %s +; Without inlining @llvm.is.constant should resolve to FALSE. + +declare i1 @llvm.is.constant.i32(i32 %a) + +; NOINLINE: Lowering llvm.is.constant to FALSE +; NOINLINE: @inlined_fun + +; INLINE-NOT: @inlined_fun +; Function Attrs: nounwind uwtable alwaysinline +define private i1 @inlined_fun(i32 %x) #0 { +; INLINE-NOT: @llvm.is.constant + %x.addr = alloca i32, align 4 + store i32 %x, i32* %x.addr, align 4 + %value = load i32* %x.addr, align 4 + %const = call i1 @llvm.is.constant.i32(i32 %value) + ret i1 %const +} + +; Function Attrs: nounwind uwtable +define i32 @main() #1 { + %call = call i1 @inlined_fun(i32 127) + br i1 %call, label %IfTrue, label %IfFalse +IfTrue: + ret i32 0 +; IfFalse branch should be optimized away +; INLINE-NOT: %IfFalse +IfFalse: + ret i32 1 +} + +attributes #0 = { nounwind uwtable alwaysinline } + +attributes #1 = { nounwind uwtable } Index: test/CodeGen/Generic/is-constant.ll =================================================================== --- /dev/null +++ test/CodeGen/Generic/is-constant.ll @@ -0,0 +1,76 @@ +; RUN: llc < %s | FileCheck %s +; RUN: llc -debug -o /dev/null 2>&1 < %s | FileCheck %s -check-prefix=DEBUG + +declare i1 @llvm.is.constant.i1(i1 %a) +declare i1 @llvm.is.constant.i16(i16 %a) +declare i1 @llvm.is.constant.i32(i32 %a) +declare i1 @llvm.is.constant.i64(i64 %a) +declare i1 @llvm.is.constant.i256(i256 %a) + +define i1 @fun1() #0 { +; CHECK: fun1: +; CHECK-NOT: llvm.is.constant +; DEBUG: Lowering llvm.is.constant to TRUE + %y = call i1 @llvm.is.constant.i32(i32 44) + ret i1 %y +} + +define i1 @fun2(i32 %x) #0 { +; CHECK: fun2: +; CHECK-NOT: llvm.is.constant +; DEBUG: Lowering llvm.is.constant to FALSE + %y = call i1 @llvm.is.constant.i32(i32 %x) + ret i1 %y +} + + +define i32 @fun3() #0 { +; CHECK: fun3: +; CHECK-NOT: llvm.is.constant +; DEBUG: Lowering llvm.is.constant to TRUE +; DEBUG: Lowering llvm.is.constant to TRUE + %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 +} + +define i1 @fun4(i1 %a.1, i16 %a.16, i64 %a.64, i256 %a.256) #0 { +; CHECK: fun4: +; CHECK-NOT: llvm.is.constant +; DEBUG: Lowering llvm.is.constant to FALSE + %r.1.1 = call i1 @llvm.is.constant.i1(i1 %a.1) +; DEBUG: Lowering llvm.is.constant to TRUE + %r.1.2 = call i1 @llvm.is.constant.i1(i1 -1) + +; DEBUG: Lowering llvm.is.constant to FALSE + %r.16.1 = call i1 @llvm.is.constant.i16(i16 %a.16) +; DEBUG: Lowering llvm.is.constant to TRUE + %r.16.2 = call i1 @llvm.is.constant.i16(i16 -1) + +; DEBUG: Lowering llvm.is.constant to FALSE + %r.64.1 = call i1 @llvm.is.constant.i64(i64 %a.64) +; DEBUG: Lowering llvm.is.constant to TRUE + %r.64.2 = call i1 @llvm.is.constant.i64(i64 -1) + +; DEBUG: Lowering llvm.is.constant to FALSE + %r.256.1 = call i1 @llvm.is.constant.i256(i256 %a.256) +; DEBUG: Lowering llvm.is.constant to TRUE + %r.256.2 = call i1 @llvm.is.constant.i256(i256 -1) + + %x.1 = add i1 %r.1.1, %r.1.2 + %x.16 = add i1 %r.16.1, %r.16.2 + %x.64 = add i1 %r.64.1, %r.64.2 + %x.256 = add i1 %r.256.1, %r.256.2 + + %y.1 = add i1 %x.1, %x.16 + %y.2 = add i1 %y.1, %x.64 + %y.3 = add i1 %y.2, %x.256 + + ret i1 %y.3 +} + +attributes #0 = { nounwind uwtable }