Index: llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp =================================================================== --- llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp +++ llvm/lib/Transforms/Utils/SimplifyLibCalls.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/SimplifyLibCalls.h" +#include "llvm/ADT/APSInt.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/Triple.h" @@ -1183,12 +1184,13 @@ } /// Use exp{,2}(x * y) for pow(exp{,2}(x), y); -/// exp2(x) for pow(2.0, x); exp10(x) for pow(10.0, x). +/// exp2(n * x) for pow(2.0 ** n, x); exp10(x) for pow(10.0, x). Value *LibCallSimplifier::replacePowWithExp(CallInst *Pow, IRBuilder<> &B) { Value *Base = Pow->getArgOperand(0), *Expo = Pow->getArgOperand(1); AttributeList Attrs = Pow->getCalledFunction()->getAttributes(); Module *Mod = Pow->getModule(); Type *Ty = Pow->getType(); + bool Ignored; // Evaluate special cases related to a nested function as the base. @@ -1205,21 +1207,53 @@ // TODO: Handle exp10() when more targets have it available. CallInst *BaseFn = dyn_cast(Base); if (BaseFn && BaseFn->isFast() && Pow->isFast()) { - LibFunc LibFn; - Function *CalleeFn = BaseFn->getCalledFunction(); - if (CalleeFn && TLI->getLibFunc(CalleeFn->getName(), LibFn) && - (LibFn == LibFunc_exp || LibFn == LibFunc_exp2) && TLI->has(LibFn)) { - Value *FMul = B.CreateFMul(BaseFn->getArgOperand(0), Expo, "mul"); - return emitUnaryFloatFnCall(FMul, CalleeFn->getName(), B, Attrs); + Function *CalledFn = BaseFn->getCalledFunction(); + if (CalledFn) { + StringRef NameFn = CalledFn->getName(); + LibFunc Fn; + if (TLI->getLibFunc(NameFn, Fn) && TLI->has(Fn)) { + Value *FMul = B.CreateFMul(BaseFn->getArgOperand(0), Expo, "mul"); + Value *ExpFn; + + switch (Fn) { + default: + return nullptr; + case LibFunc_exp: + case LibFunc_expf: + case LibFunc_expl: + ExpFn = Intrinsic::getDeclaration(Mod, Intrinsic::exp, Ty); + return B.CreateCall(ExpFn, FMul, "exp"); + case LibFunc_exp2: + case LibFunc_exp2f: + case LibFunc_exp2l: + ExpFn = Intrinsic::getDeclaration(Mod, Intrinsic::exp2, Ty); + return B.CreateCall(ExpFn, FMul, "exp2"); + } + } } } // Evaluate special cases related to a constant base. - // pow(2.0, x) -> exp2(x) - if (match(Base, m_SpecificFP(2.0))) { - Value *Exp2 = Intrinsic::getDeclaration(Mod, Intrinsic::exp2, Ty); - return B.CreateCall(Exp2, Expo, "exp2"); + const APFloat *BaseF; + if (!match(Pow->getArgOperand(0), m_APFloat(BaseF))) + return nullptr; + + // pow(2.0 ** n, x) -> exp2(n * x) + APFloat BaseR = APFloat(1.0); + BaseR.convert(BaseF->getSemantics(), APFloat::rmTowardZero, &Ignored); + BaseR = BaseR / *BaseF; + bool isInteger = BaseF->isInteger(), + isReciprocal = BaseR.isInteger(); + const APFloat *NF = isReciprocal ? &BaseR : BaseF; + APSInt NI(64, false); + if ((isInteger || isReciprocal) && + !NF->convertToInteger(NI, APFloat::rmTowardZero, &Ignored) && + NI > 1 && NI.isPowerOf2()) { + double N = NI.logBase2() * (isReciprocal ? -1.0 : 1.0); + Value *ExpoN = B.CreateFMul(Expo, ConstantFP::get(Ty, N)); + Value *Exp2Fn = Intrinsic::getDeclaration(Mod, Intrinsic::exp2, Ty); + return B.CreateCall(Exp2Fn, ExpoN, "exp2"); } // pow(10.0, x) -> exp10(x) Index: llvm/test/Transforms/InstCombine/pow-1.ll =================================================================== --- llvm/test/Transforms/InstCombine/pow-1.ll +++ llvm/test/Transforms/InstCombine/pow-1.ll @@ -64,11 +64,11 @@ ret float %retval } -; TODO: Should result in exp2(-2.0 * x). define double @test_simplify3n(double %x) { ; ANY-LABEL: @test_simplify3n( -; ANY-NEXT: [[RETVAL:%.*]] = call double @pow(double 2.500000e-01, double [[X:%.*]]) -; ANY-NEXT: ret double [[RETVAL]] +; ANY-NEXT: [[TMP1:%.*]] = fmul double [[X:%.*]], -2.000000e+00 +; ANY-NEXT: [[EXP2:%.*]] = call double @llvm.exp2.f64(double [[TMP1]]) +; ANY-NEXT: ret double [[EXP2]] ; %retval = call double @pow(double 0.25, double %x) ret double %retval @@ -83,11 +83,11 @@ ret <2 x float> %retval } -; TODO: Should result in exp2(2.0 * x). define <2 x double> @test_simplify3vn(<2 x double> %x) { ; ANY-LABEL: @test_simplify3vn( -; ANY-NEXT: [[RETVAL:%.*]] = call <2 x double> @llvm.pow.v2f64(<2 x double> , <2 x double> [[X:%.*]]) -; ANY-NEXT: ret <2 x double> [[RETVAL]] +; ANY-NEXT: [[TMP1:%.*]] = fmul <2 x double> [[X:%.*]], +; ANY-NEXT: [[EXP2:%.*]] = call <2 x double> @llvm.exp2.v2f64(<2 x double> [[TMP1]]) +; ANY-NEXT: ret <2 x double> [[EXP2]] ; %retval = call <2 x double> @llvm.pow.v2f64(<2 x double> , <2 x double> %x) ret <2 x double> %retval @@ -102,11 +102,11 @@ ret double %retval } -; TODO: Should result in exp2f(3.0 * x). define float @test_simplify4n(float %x) { ; ANY-LABEL: @test_simplify4n( -; ANY-NEXT: [[RETVAL:%.*]] = call float @powf(float 8.000000e+00, float [[X:%.*]]) -; ANY-NEXT: ret float [[RETVAL]] +; ANY-NEXT: [[TMP1:%.*]] = fmul float [[X:%.*]], 3.000000e+00 +; ANY-NEXT: [[EXP2:%.*]] = call float @llvm.exp2.f32(float [[TMP1]]) +; ANY-NEXT: ret float [[EXP2]] ; %retval = call float @powf(float 8.0, float %x) ret float %retval @@ -121,11 +121,11 @@ ret <2 x double> %retval } -; TODO: Should result in exp2f(-x). define <2 x float> @test_simplify4vn(<2 x float> %x) { ; ANY-LABEL: @test_simplify4vn( -; ANY-NEXT: [[RETVAL:%.*]] = call <2 x float> @llvm.pow.v2f32(<2 x float> , <2 x float> [[X:%.*]]) -; ANY-NEXT: ret <2 x float> [[RETVAL]] +; ANY-NEXT: [[TMP1:%.*]] = fsub <2 x float> , [[X:%.*]] +; ANY-NEXT: [[EXP2:%.*]] = call <2 x float> @llvm.exp2.v2f32(<2 x float> [[TMP1]]) +; ANY-NEXT: ret <2 x float> [[EXP2]] ; %retval = call <2 x float> @llvm.pow.v2f32(<2 x float> , <2 x float> %x) ret <2 x float> %retval Index: llvm/test/Transforms/InstCombine/pow-exp.ll =================================================================== --- llvm/test/Transforms/InstCombine/pow-exp.ll +++ llvm/test/Transforms/InstCombine/pow-exp.ll @@ -1,23 +1,21 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py ; RUN: opt < %s -instcombine -S | FileCheck %s -; TODO: Should result in expf(x * y). define float @powf_expf(float %x, float %y) { ; CHECK-LABEL: @powf_expf( -; CHECK-NEXT: [[CALL:%.*]] = call fast float @expf(float [[X:%.*]]) #1 -; CHECK-NEXT: [[POW:%.*]] = call fast float @llvm.pow.f32(float [[CALL]], float [[Y:%.*]]) -; CHECK-NEXT: ret float [[POW]] +; CHECK-NEXT: [[MUL:%.*]] = fmul fast float [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[EXP:%.*]] = call fast float @llvm.exp.f32(float [[MUL]]) +; CHECK-NEXT: ret float [[EXP]] ; %call = call fast float @expf(float %x) nounwind readnone %pow = call fast float @llvm.pow.f32(float %call, float %y) ret float %pow } -; TODO: Should result in intrinsic call to exp(). define double @pow_exp(double %x, double %y) { ; CHECK-LABEL: @pow_exp( ; CHECK-NEXT: [[MUL:%.*]] = fmul fast double [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[EXP:%.*]] = call fast double @exp(double [[MUL]]) +; CHECK-NEXT: [[EXP:%.*]] = call fast double @llvm.exp.f64(double [[MUL]]) ; CHECK-NEXT: ret double [[EXP]] ; %call = call fast double @exp(double %x) nounwind readnone @@ -25,23 +23,21 @@ ret double %pow } -; TODO: Should result in exp2f(x * y). define float @powf_exp2f(float %x, float %y) { ; CHECK-LABEL: @powf_exp2f( -; CHECK-NEXT: [[CALL:%.*]] = call fast float @exp2f(float [[X:%.*]]) #1 -; CHECK-NEXT: [[POW:%.*]] = call fast float @llvm.pow.f32(float [[CALL]], float [[Y:%.*]]) -; CHECK-NEXT: ret float [[POW]] +; CHECK-NEXT: [[MUL:%.*]] = fmul fast float [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[EXP2:%.*]] = call fast float @llvm.exp2.f32(float [[MUL]]) +; CHECK-NEXT: ret float [[EXP2]] ; %call = call fast float @exp2f(float %x) nounwind readnone %pow = call fast float @llvm.pow.f32(float %call, float %y) ret float %pow } -; TODO: Should result in intrinsic call to exp2(). define double @pow_exp2(double %x, double %y) { ; CHECK-LABEL: @pow_exp2( ; CHECK-NEXT: [[MUL:%.*]] = fmul fast double [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: [[EXP2:%.*]] = call fast double @exp2(double [[MUL]]) +; CHECK-NEXT: [[EXP2:%.*]] = call fast double @llvm.exp2.f64(double [[MUL]]) ; CHECK-NEXT: ret double [[EXP2]] ; %call = call fast double @exp2(double %x) nounwind readnone