Skip to content

Commit

Permalink
[CodeGen] convert math libcalls/builtins to equivalent LLVM intrinsics
Browse files Browse the repository at this point in the history
There are 20 LLVM math intrinsics that correspond to mathlib calls according to the LangRef:
http://llvm.org/docs/LangRef.html#standard-c-library-intrinsics

We were only converting 3 mathlib calls (sqrt, fma, pow) and 12 builtin calls (ceil, copysign, 
fabs, floor, fma, fmax, fmin, nearbyint, pow, rint, round, trunc) to their intrinsic-equivalents.

This patch pulls the transforms together and handles all 20 cases. The switch is guarded by a 
check for const-ness to make sure we're not doing the transform if errno could possibly be set by
the libcall or builtin.

Differential Revision: https://reviews.llvm.org/D40044

llvm-svn: 319593
  • Loading branch information
rotateright committed Dec 1, 2017
1 parent e7487e4 commit 3e287b4
Show file tree
Hide file tree
Showing 6 changed files with 294 additions and 215 deletions.
266 changes: 173 additions & 93 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,179 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
Result.Val.getFloat()));
}

// Math builtins have the same semantics as their math library twins.
// There are LLVM math intrinsics corresponding to math library functions
// except the intrinsic will never set errno while the math library might.
// Thus, we can transform math library and builtin calls to their
// semantically-equivalent LLVM intrinsic counterparts if the call is marked
// 'const' (it is known to never set errno).
if (FD->hasAttr<ConstAttr>()) {
switch (BuiltinID) {
case Builtin::BIceil:
case Builtin::BIceilf:
case Builtin::BIceill:
case Builtin::BI__builtin_ceil:
case Builtin::BI__builtin_ceilf:
case Builtin::BI__builtin_ceill:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::ceil));

case Builtin::BIcopysign:
case Builtin::BIcopysignf:
case Builtin::BIcopysignl:
case Builtin::BI__builtin_copysign:
case Builtin::BI__builtin_copysignf:
case Builtin::BI__builtin_copysignl:
return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::copysign));

case Builtin::BIcos:
case Builtin::BIcosf:
case Builtin::BIcosl:
case Builtin::BI__builtin_cos:
case Builtin::BI__builtin_cosf:
case Builtin::BI__builtin_cosl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::cos));

case Builtin::BIexp:
case Builtin::BIexpf:
case Builtin::BIexpl:
case Builtin::BI__builtin_exp:
case Builtin::BI__builtin_expf:
case Builtin::BI__builtin_expl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::exp));

case Builtin::BIexp2:
case Builtin::BIexp2f:
case Builtin::BIexp2l:
case Builtin::BI__builtin_exp2:
case Builtin::BI__builtin_exp2f:
case Builtin::BI__builtin_exp2l:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::exp2));

case Builtin::BIfabs:
case Builtin::BIfabsf:
case Builtin::BIfabsl:
case Builtin::BI__builtin_fabs:
case Builtin::BI__builtin_fabsf:
case Builtin::BI__builtin_fabsl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::fabs));

case Builtin::BIfloor:
case Builtin::BIfloorf:
case Builtin::BIfloorl:
case Builtin::BI__builtin_floor:
case Builtin::BI__builtin_floorf:
case Builtin::BI__builtin_floorl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::floor));

case Builtin::BIfma:
case Builtin::BIfmaf:
case Builtin::BIfmal:
case Builtin::BI__builtin_fma:
case Builtin::BI__builtin_fmaf:
case Builtin::BI__builtin_fmal:
return RValue::get(emitTernaryBuiltin(*this, E, Intrinsic::fma));

case Builtin::BIfmax:
case Builtin::BIfmaxf:
case Builtin::BIfmaxl:
case Builtin::BI__builtin_fmax:
case Builtin::BI__builtin_fmaxf:
case Builtin::BI__builtin_fmaxl:
return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::maxnum));

case Builtin::BIfmin:
case Builtin::BIfminf:
case Builtin::BIfminl:
case Builtin::BI__builtin_fmin:
case Builtin::BI__builtin_fminf:
case Builtin::BI__builtin_fminl:
return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::minnum));

case Builtin::BIlog:
case Builtin::BIlogf:
case Builtin::BIlogl:
case Builtin::BI__builtin_log:
case Builtin::BI__builtin_logf:
case Builtin::BI__builtin_logl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log));

case Builtin::BIlog10:
case Builtin::BIlog10f:
case Builtin::BIlog10l:
case Builtin::BI__builtin_log10:
case Builtin::BI__builtin_log10f:
case Builtin::BI__builtin_log10l:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log10));

case Builtin::BIlog2:
case Builtin::BIlog2f:
case Builtin::BIlog2l:
case Builtin::BI__builtin_log2:
case Builtin::BI__builtin_log2f:
case Builtin::BI__builtin_log2l:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::log2));

case Builtin::BInearbyint:
case Builtin::BInearbyintf:
case Builtin::BInearbyintl:
case Builtin::BI__builtin_nearbyint:
case Builtin::BI__builtin_nearbyintf:
case Builtin::BI__builtin_nearbyintl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::nearbyint));

case Builtin::BIpow:
case Builtin::BIpowf:
case Builtin::BIpowl:
case Builtin::BI__builtin_pow:
case Builtin::BI__builtin_powf:
case Builtin::BI__builtin_powl:
return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::pow));

case Builtin::BIrint:
case Builtin::BIrintf:
case Builtin::BIrintl:
case Builtin::BI__builtin_rint:
case Builtin::BI__builtin_rintf:
case Builtin::BI__builtin_rintl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::rint));

case Builtin::BIround:
case Builtin::BIroundf:
case Builtin::BIroundl:
case Builtin::BI__builtin_round:
case Builtin::BI__builtin_roundf:
case Builtin::BI__builtin_roundl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::round));

case Builtin::BIsin:
case Builtin::BIsinf:
case Builtin::BIsinl:
case Builtin::BI__builtin_sin:
case Builtin::BI__builtin_sinf:
case Builtin::BI__builtin_sinl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::sin));

case Builtin::BIsqrt:
case Builtin::BIsqrtf:
case Builtin::BIsqrtl:
case Builtin::BI__builtin_sqrt:
case Builtin::BI__builtin_sqrtf:
case Builtin::BI__builtin_sqrtl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::sqrt));

case Builtin::BItrunc:
case Builtin::BItruncf:
case Builtin::BItruncl:
case Builtin::BI__builtin_trunc:
case Builtin::BI__builtin_truncf:
case Builtin::BI__builtin_truncl:
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::trunc));

default:
break;
}
}

switch (BuiltinID) {
default: break; // Handle intrinsics and libm functions below.
case Builtin::BI__builtin___CFStringMakeConstantString:
Expand Down Expand Up @@ -894,11 +1067,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,

return RValue::get(Result);
}
case Builtin::BI__builtin_fabs:
case Builtin::BI__builtin_fabsf:
case Builtin::BI__builtin_fabsl: {
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::fabs));
}
case Builtin::BI__builtin_fmod:
case Builtin::BI__builtin_fmodf:
case Builtin::BI__builtin_fmodl: {
Expand All @@ -907,51 +1075,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
Value *Result = Builder.CreateFRem(Arg1, Arg2, "fmod");
return RValue::get(Result);
}
case Builtin::BI__builtin_copysign:
case Builtin::BI__builtin_copysignf:
case Builtin::BI__builtin_copysignl: {
return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::copysign));
}
case Builtin::BI__builtin_ceil:
case Builtin::BI__builtin_ceilf:
case Builtin::BI__builtin_ceill: {
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::ceil));
}
case Builtin::BI__builtin_floor:
case Builtin::BI__builtin_floorf:
case Builtin::BI__builtin_floorl: {
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::floor));
}
case Builtin::BI__builtin_trunc:
case Builtin::BI__builtin_truncf:
case Builtin::BI__builtin_truncl: {
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::trunc));
}
case Builtin::BI__builtin_rint:
case Builtin::BI__builtin_rintf:
case Builtin::BI__builtin_rintl: {
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::rint));
}
case Builtin::BI__builtin_nearbyint:
case Builtin::BI__builtin_nearbyintf:
case Builtin::BI__builtin_nearbyintl: {
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::nearbyint));
}
case Builtin::BI__builtin_round:
case Builtin::BI__builtin_roundf:
case Builtin::BI__builtin_roundl: {
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::round));
}
case Builtin::BI__builtin_fmin:
case Builtin::BI__builtin_fminf:
case Builtin::BI__builtin_fminl: {
return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::minnum));
}
case Builtin::BI__builtin_fmax:
case Builtin::BI__builtin_fmaxf:
case Builtin::BI__builtin_fmaxl: {
return RValue::get(emitBinaryBuiltin(*this, E, Intrinsic::maxnum));
}
case Builtin::BI__builtin_conj:
case Builtin::BI__builtin_conjf:
case Builtin::BI__builtin_conjl: {
Expand Down Expand Up @@ -2073,49 +2196,6 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD,
return RValue::get(nullptr);
}

case Builtin::BIsqrt:
case Builtin::BIsqrtf:
case Builtin::BIsqrtl:
// Builtins have the same semantics as library functions. The LLVM intrinsic
// has the same semantics as the library function except it does not set
// errno. Thus, we can transform either sqrt or __builtin_sqrt to @llvm.sqrt
// if the call is 'const' (the call must not set errno).
//
// FIXME: The builtin cases are not here because they are marked 'const' in
// Builtins.def. So that means they are wrongly defined to have different
// semantics than the library functions. If we included them here, we would
// turn them into LLVM intrinsics regardless of whether -fmath-errno was on.
if (FD->hasAttr<ConstAttr>())
return RValue::get(emitUnaryBuiltin(*this, E, Intrinsic::sqrt));
break;

case Builtin::BI__builtin_pow:
case Builtin::BI__builtin_powf:
case Builtin::BI__builtin_powl:
case Builtin::BIpow:
case Builtin::BIpowf:
case Builtin::BIpowl: {
// Transform a call to pow* into a @llvm.pow.* intrinsic call.
if (!FD->hasAttr<ConstAttr>())
break;
Value *Base = EmitScalarExpr(E->getArg(0));
Value *Exponent = EmitScalarExpr(E->getArg(1));
llvm::Type *ArgType = Base->getType();
Value *F = CGM.getIntrinsic(Intrinsic::pow, ArgType);
return RValue::get(Builder.CreateCall(F, {Base, Exponent}));
}

case Builtin::BIfma:
case Builtin::BIfmaf:
case Builtin::BIfmal:
case Builtin::BI__builtin_fma:
case Builtin::BI__builtin_fmaf:
case Builtin::BI__builtin_fmal:
// A constant libcall or builtin is equivalent to the LLVM intrinsic.
if (FD->hasAttr<ConstAttr>())
return RValue::get(emitTernaryBuiltin(*this, E, Intrinsic::fma));
break;

case Builtin::BI__builtin_signbit:
case Builtin::BI__builtin_signbitf:
case Builtin::BI__builtin_signbitl: {
Expand Down
6 changes: 2 additions & 4 deletions clang/test/CodeGen/builtin-sqrt.c
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
// RUN: %clang_cc1 -fmath-errno -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s --check-prefix=HAS_ERRNO
// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s --check-prefix=NO_ERRNO

// FIXME: If the builtin does not set errno, it should be converted to an LLVM intrinsic.

float foo(float X) {
// HAS_ERRNO: call float @sqrtf(float
// NO_ERRNO: call float @sqrtf(float
// NO_ERRNO: call float @llvm.sqrt.f32(float
return __builtin_sqrtf(X);
}

// HAS_ERRNO: declare float @sqrtf(float) [[ATTR:#[0-9]+]]
// HAS_ERRNO-NOT: attributes [[ATTR]] = {{{.*}} readnone

// NO_ERRNO: declare float @sqrtf(float) [[ATTR:#[0-9]+]]
// NO_ERRNO: declare float @llvm.sqrt.f32(float) [[ATTR:#[0-9]+]]
// NO_ERRNO: attributes [[ATTR]] = { nounwind readnone {{.*}}}

6 changes: 3 additions & 3 deletions clang/test/CodeGen/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -331,13 +331,13 @@ void test_float_builtin_ops(float F, double D, long double LD) {
// CHECK: call x86_fp80 @llvm.floor.f80

resf = __builtin_sqrtf(F);
// CHECK: call float @sqrtf(
// CHECK: call float @llvm.sqrt.f32(

resd = __builtin_sqrt(D);
// CHECK: call double @sqrt(
// CHECK: call double @llvm.sqrt.f64(

resld = __builtin_sqrtl(LD);
// CHECK: call x86_fp80 @sqrtl(
// CHECK: call x86_fp80 @llvm.sqrt.f80

resf = __builtin_truncf(F);
// CHECK: call float @llvm.trunc.f32
Expand Down
12 changes: 6 additions & 6 deletions clang/test/CodeGen/libcalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,19 +105,19 @@ void test_builtins(double d, float f, long double ld) {
double exp_ = exp(d);
long double expl_ = expl(ld);
float expf_ = expf(f);
// CHECK-NO: declare double @exp(double) [[NUW_RN]]
// CHECK-NO: declare x86_fp80 @expl(x86_fp80) [[NUW_RN]]
// CHECK-NO: declare float @expf(float) [[NUW_RN]]
// CHECK-NO: declare double @llvm.exp.f64(double) [[NUW_RNI]]
// CHECK-NO: declare x86_fp80 @llvm.exp.f80(x86_fp80) [[NUW_RNI]]
// CHECK-NO: declare float @llvm.exp.f32(float) [[NUW_RNI]]
// CHECK-YES-NOT: declare double @exp(double) [[NUW_RN]]
// CHECK-YES-NOT: declare x86_fp80 @expl(x86_fp80) [[NUW_RN]]
// CHECK-YES-NOT: declare float @expf(float) [[NUW_RN]]

double log_ = log(d);
long double logl_ = logl(ld);
float logf_ = logf(f);
// CHECK-NO: declare double @log(double) [[NUW_RN]]
// CHECK-NO: declare x86_fp80 @logl(x86_fp80) [[NUW_RN]]
// CHECK-NO: declare float @logf(float) [[NUW_RN]]
// CHECK-NO: declare double @llvm.log.f64(double) [[NUW_RNI]]
// CHECK-NO: declare x86_fp80 @llvm.log.f80(x86_fp80) [[NUW_RNI]]
// CHECK-NO: declare float @llvm.log.f32(float) [[NUW_RNI]]
// CHECK-YES-NOT: declare double @log(double) [[NUW_RN]]
// CHECK-YES-NOT: declare x86_fp80 @logl(x86_fp80) [[NUW_RN]]
// CHECK-YES-NOT: declare float @logf(float) [[NUW_RN]]
Expand Down
Loading

0 comments on commit 3e287b4

Please sign in to comment.