Index: docs/LangRef.rst =================================================================== --- docs/LangRef.rst +++ docs/LangRef.rst @@ -7155,6 +7155,28 @@ This instrinsic does *not* empty the instruction pipeline. Modifications of the current function are outside the scope of the intrinsic. +'``llvm.is.constant.*``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare i1 @llvm.is.constant.i32(i32) + +Overview: +""""""""" + +The '``llvm.is.constant``' intrinsic checks if the argument value is +known at compile time. 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/LegalizeDAG.cpp =================================================================== --- lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -35,6 +35,7 @@ #include "llvm/Target/TargetMachine.h" using namespace llvm; +#define DEBUG_TYPE "legalize-dag" //===----------------------------------------------------------------------===// /// SelectionDAGLegalize - This takes an arbitrary SelectionDAG as input and /// hacks on it until the target machine can handle it. This involves @@ -1284,6 +1285,13 @@ return; } break; + case ISD::IS_CONSTANT: { + DEBUG(dbgs() << "Lowering llvm.is.constant to FALSE.\n"); + SDValue NewVal = DAG.getConstant(0, Node->getValueType(0)); + ReplaceNode(Node, NewVal.getNode()); + LegalizeOp(NewVal.getNode()); + return; + } default: if (Node->getOpcode() >= ISD::BUILTIN_OP_END) { 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, VT); + } } } 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,29 @@ +; RUN: opt -O3 < %s | lli +; +; Expected outcome: @fun is inlined and @llvm.is.constant is resolved to TRUE +; Without inlining @llvm.is.constant would resolve to FALSE. + +declare i1 @llvm.is.constant.i32(i32 %a) + +; Function Attrs: nounwind uwtable alwaysinline +define private i1 @fun(i32 %x) #0 { + %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 @fun(i32 127) + br i1 %call, label %IfTrue, label %IfFalse +IfTrue: + ret i32 0 +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,37 @@ +; RUN: llc < %s | FileCheck %s +; RUN: llc -debug -o /dev/null 2>&1 < %s | FileCheck %s -check-prefix=DEBUG + +declare i1 @llvm.is.constant.i32(i32 %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 +} + +attributes #0 = { nounwind uwtable }