/// argument.
bool isArgumentInteresting(Argument *A,
SmallVectorImpl<CallArgBinding> &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;
}
SmallVectorImpl<CallArgBinding> &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<CallInst>(U) && !isa<InvokeInst>(U))
continue;
}
- if (isa<Constant>(V) && (Solver.getLatticeValueFor(V).isConstant() ||
- EnableSpecializationForLiteralConstant))
- Constants.push_back({&CS, cast<Constant>(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<Constant>(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});
}
}
--- /dev/null
+; 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]]