Index: llvm/lib/Transforms/IPO/FunctionSpecialization.cpp =================================================================== --- llvm/lib/Transforms/IPO/FunctionSpecialization.cpp +++ llvm/lib/Transforms/IPO/FunctionSpecialization.cpp @@ -666,17 +666,35 @@ /// argument. bool isArgumentInteresting(Argument *A, SmallVectorImpl &Constants) { + + // No point in specialization if the argument is unused. + if (A->user_empty()) + return false; + // For now, don't attempt to specialize functions based on the values of // composite types. - if (!A->getType()->isSingleValueType() || A->user_empty()) + Type *ArgTy = A->getType() ; + if (!ArgTy->isSingleValueType()) + return false; + + // Specialization of integer and floating point types needs to be explicitly enabled. + if (!EnableSpecializationForLiteralConstant && + (ArgTy->isIntegerTy() || ArgTy->isFloatingPointTy())) return false; - // If the argument isn't overdefined, there's nothing to do. It should - // already be constant. - if (!Solver.getLatticeValueFor(A).isOverdefined()) { + // SCCP solver does not record an argument that will be constructed on + // stack. + if (A->hasByValAttr() && !A->getParent()->onlyReadsMemory()) + return false; + + // Check the lattice value and decide if we should attemt to specialize, + // based on this argument. No point in specialization, if the lattice value + // is already a constant. + const ValueLatticeElement &LV = Solver.getLatticeValueFor(A); + if (LV.isUnknownOrUndef() || LV.isConstant() || + (LV.isConstantRange() && LV.getConstantRange().isSingleElement())) { LLVM_DEBUG(dbgs() << "FnSpecialization: Nothing to do, argument " - << A->getNameOrAsOperand() - << " is already constant?\n"); + << A->getNameOrAsOperand() << " is already constant\n"); return false; } @@ -709,11 +727,6 @@ SmallVectorImpl &Constants) { Function *F = A->getParent(); - // SCCP solver does not record an argument that will be constructed on - // stack. - if (A->hasByValAttr() && !F->onlyReadsMemory()) - return; - // Iterate over all the call sites of the argument's parent function. for (User *U : F->users()) { if (!isa(U) && !isa(U)) @@ -744,9 +757,23 @@ continue; } - if (isa(V) && (Solver.getLatticeValueFor(V).isConstant() || - EnableSpecializationForLiteralConstant)) - Constants.push_back({&CS, cast(V)}); + // Select for possible specialisation arguments which are constants or + // are deduced to be constants or constant ranges with a single element. + Constant *C = dyn_cast(V); + if (!C) { + const ValueLatticeElement &LV = Solver.getLatticeValueFor(V); + if (LV.isConstant()) + C = LV.getConstant(); + else if (LV.isConstantRange() && + LV.getConstantRange().isSingleElement()) { + assert(V->getType()->isIntegerTy() && "Non-integral constant range"); + C = Constant::getIntegerValue( + V->getType(), *LV.getConstantRange().getSingleElement()); + } else + continue; + } + + Constants.push_back({&CS, C}); } } Index: llvm/test/Transforms/FunctionSpecialization/literal-const.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/FunctionSpecialization/literal-const.ll @@ -0,0 +1,92 @@ +; RUN: opt -S --passes=function-specialization \ +; RUN: -force-function-specialization < %s | FileCheck %s -check-prefix CHECK-NOLIT +; RUN: opt -S --passes=function-specialization \ +; RUN: -function-specialization-for-literal-constant \ +; RUN: -force-function-specialization < %s | FileCheck %s -check-prefix CHECK-LIT + +define i32 @f0(i32 noundef %x) { +entry: + %call = tail call i32 @neg(i32 noundef %x, i1 noundef zeroext false) + ret i32 %call +} + +define i32 @f1(i32 noundef %x) { +entry: + %call = tail call i32 @neg(i32 noundef %x, i1 noundef zeroext true) + ret i32 %call +} + +define i32 @g0(i32 noundef %x) { +entry: + %call = tail call i32 @add(i32 noundef %x, i32 noundef 1) + ret i32 %call +} + +define i32 @g1(i32 noundef %x) { +entry: + %call = tail call i32 @add(i32 noundef %x, i32 noundef 2) + ret i32 %call +} + +define float @h0(float noundef %x) { +entry: + %call = tail call float @addf(float noundef %x, float noundef 1.000000e+00) + ret float %call +} + +define float @h1(float noundef %x) { +entry: + %call = tail call float @addf(float noundef %x, float noundef 2.000000e+00) + ret float %call +} + +define internal i32 @neg(i32 noundef %x, i1 noundef zeroext %b) { +entry: + %sub = sub nsw i32 0, %x + %cond = select i1 %b, i32 %sub, i32 %x + ret i32 %cond +} + +define internal i32 @add(i32 noundef %x, i32 noundef %y) { +entry: + %add = add nsw i32 %y, %x + ret i32 %add +} + +define internal float @addf(float noundef %x, float noundef %y) { +entry: + %add = fadd float %x, %y + ret float %add +} + + +; Check no functions were specialised. +; CHECK-NOLIT-NOT: @neg. +; CHECK-NOLIT-NOT: @add. +; CHECK-NOLIT-NOT: @addf. + +; CHECK-LIT-LABEL: define i32 @f0 +; CHECK-LIT: call i32 @neg.[[#A:]] + +; CHECK-LIT-LABEL: define i32 @f1 +; CHECK-LIT: call i32 @neg.[[#B:]] + +; CHECK-LIT-LABEL: define i32 @g0 +; CHECK-LIT: call i32 @add.[[#C:]] + +; CHECK-LIT-LABEL: define i32 @g1 +; CHECK-LIT: call i32 @add.[[#D:]] + +; CHECK-LIT-LABEL: define float @h0 +; CHECK-LIT: call float @addf.[[#E:]] + +; CHECK-LIT-LABEL: define float @h1 +; CHECK-LIT: call float @addf.[[#F:]] + +; Check all of `neg`, `add`, and `addf` were specialised. +; CHECK-LIT-DAG: @neg.[[#A]] +; CHECK-LIT-DAG: @neg.[[#B]] +; CHECK-LIT-DAG: @add.[[#C]] +; CHECK-LIT-DAG: @add.[[#D]] +; CHECK-LIT-DAG: @addf.[[#E]] +; CHECK-LIT-DAG: @addf.[[#F]]