Skip to content

Commit 679f925

Browse files
committed
Clang: Add minnum/maxnum builtin functions support
With llvm#112852, we claimed that llvm.minnum and llvm.maxnum should treat +0.0>-0.0, while libc doesn't require fmin(3)/fmax(3) for it. To make llvm.minnum/llvm.maxnum easy to use, we define the builtin functions for them, include __builtin_minnum __builtin_elementwise_minnum __builtin_minnum __builtin_elementwise_minnum __builtin_minnum __builtin_elementwise_minnum __builtin_minnum __builtin_maxnum __builtin_elementwise_maxnum __builtin_maxnum __builtin_elementwise_maxnum __builtin_maxnum __builtin_elementwise_maxnum __builtin_maxnum All of them support _Float16, float, double, long double.
1 parent 1f195af commit 679f925

File tree

5 files changed

+365
-0
lines changed

5 files changed

+365
-0
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ def FmaxF16F128 : Builtin, F16F128MathTemplate {
209209
let Prototype = "T(T, T)";
210210
}
211211

212+
def MinNum : Builtin {
213+
let Spellings = ["__builtin_minnum"];
214+
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, CustomTypeChecking, Constexpr];
215+
let Prototype = "void(...)";
216+
}
217+
218+
def MaxNum : Builtin {
219+
let Spellings = ["__builtin_maxnum"];
220+
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, CustomTypeChecking, Constexpr];
221+
let Prototype = "void(...)";
222+
}
223+
212224
def FminF16F128 : Builtin, F16F128MathTemplate {
213225
let Spellings = ["__builtin_fmin"];
214226
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];
@@ -1304,6 +1316,18 @@ def ElementwiseMin : Builtin {
13041316
let Prototype = "void(...)";
13051317
}
13061318

1319+
def ElementwiseMaxNum : Builtin {
1320+
let Spellings = ["__builtin_elementwise_maxnum"];
1321+
let Attributes = [NoThrow, Const, CustomTypeChecking];
1322+
let Prototype = "void(...)";
1323+
}
1324+
1325+
def ElementwiseMinNum : Builtin {
1326+
let Spellings = ["__builtin_elementwise_minnum"];
1327+
let Attributes = [NoThrow, Const, CustomTypeChecking];
1328+
let Prototype = "void(...)";
1329+
}
1330+
13071331
def ElementwiseMaximum : Builtin {
13081332
let Spellings = ["__builtin_elementwise_maximum"];
13091333
let Attributes = [NoThrow, Const, CustomTypeChecking];

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2597,6 +2597,7 @@ class Sema final : public SemaBase {
25972597
ExprResult AtomicOpsOverloaded(ExprResult TheCallResult,
25982598
AtomicExpr::AtomicOp Op);
25992599

2600+
bool BuiltinMaxNumMinNumMath(CallExpr *TheCall);
26002601
/// \param FPOnly restricts the arguments to floating-point types.
26012602
bool BuiltinElementwiseMath(CallExpr *TheCall,
26022603
EltwiseBuiltinArgTyRestriction ArgTyRestr =

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2629,6 +2629,16 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
26292629
Intrinsic::minnum,
26302630
Intrinsic::experimental_constrained_minnum));
26312631

2632+
case Builtin::BI__builtin_maxnum:
2633+
return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
2634+
*this, E, Intrinsic::maxnum,
2635+
Intrinsic::experimental_constrained_maxnum));
2636+
2637+
case Builtin::BI__builtin_minnum:
2638+
return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
2639+
*this, E, Intrinsic::minnum,
2640+
Intrinsic::experimental_constrained_minnum));
2641+
26322642
case Builtin::BIfmaximum_num:
26332643
case Builtin::BIfmaximum_numf:
26342644
case Builtin::BIfmaximum_numl:
@@ -3818,6 +3828,22 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
38183828
return RValue::get(Result);
38193829
}
38203830

3831+
case Builtin::BI__builtin_elementwise_maxnum: {
3832+
Value *Op0 = EmitScalarExpr(E->getArg(0));
3833+
Value *Op1 = EmitScalarExpr(E->getArg(1));
3834+
Value *Result = Builder.CreateBinaryIntrinsic(llvm::Intrinsic::maxnum, Op0,
3835+
Op1, nullptr, "elt.maxnum");
3836+
return RValue::get(Result);
3837+
}
3838+
3839+
case Builtin::BI__builtin_elementwise_minnum: {
3840+
Value *Op0 = EmitScalarExpr(E->getArg(0));
3841+
Value *Op1 = EmitScalarExpr(E->getArg(1));
3842+
Value *Result = Builder.CreateBinaryIntrinsic(llvm::Intrinsic::minnum, Op0,
3843+
Op1, nullptr, "elt.minnum");
3844+
return RValue::get(Result);
3845+
}
3846+
38213847
case Builtin::BI__builtin_elementwise_maximum: {
38223848
Value *Op0 = EmitScalarExpr(E->getArg(0));
38233849
Value *Op1 = EmitScalarExpr(E->getArg(1));

clang/lib/Sema/SemaChecking.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2760,8 +2760,17 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
27602760
return ExprError();
27612761
break;
27622762

2763+
case Builtin::BI__builtin_minnum:
2764+
case Builtin::BI__builtin_maxnum: {
2765+
if (BuiltinMaxNumMinNumMath(TheCall))
2766+
return ExprError();
2767+
break;
2768+
}
2769+
27632770
// These builtins restrict the element type to floating point
27642771
// types only, and take in two arguments.
2772+
case Builtin::BI__builtin_elementwise_minnum:
2773+
case Builtin::BI__builtin_elementwise_maxnum:
27652774
case Builtin::BI__builtin_elementwise_minimum:
27662775
case Builtin::BI__builtin_elementwise_maximum:
27672776
case Builtin::BI__builtin_elementwise_atan2:
@@ -15278,6 +15287,42 @@ bool Sema::PrepareBuiltinElementwiseMathOneArgCall(
1527815287
return false;
1527915288
}
1528015289

15290+
bool Sema::BuiltinMaxNumMinNumMath(CallExpr *TheCall) {
15291+
if (checkArgCount(TheCall, 2))
15292+
return true;
15293+
15294+
ExprResult OrigArg0 = TheCall->getArg(0);
15295+
ExprResult OrigArg1 = TheCall->getArg(1);
15296+
15297+
// Do standard promotions between the two arguments, returning their common
15298+
// type.
15299+
QualType Res = UsualArithmeticConversions(
15300+
OrigArg0, OrigArg1, TheCall->getExprLoc(), ACK_Comparison);
15301+
if (OrigArg0.isInvalid() || OrigArg1.isInvalid())
15302+
return true;
15303+
15304+
// Make sure any conversions are pushed back into the call; this is
15305+
// type safe since unordered compare builtins are declared as "_Bool
15306+
// foo(...)".
15307+
TheCall->setArg(0, OrigArg0.get());
15308+
TheCall->setArg(1, OrigArg1.get());
15309+
15310+
if (!OrigArg0.get()->isTypeDependent() && OrigArg1.get()->isTypeDependent())
15311+
return true;
15312+
15313+
// If the common type isn't a real floating type, then the arguments were
15314+
// invalid for this operation.
15315+
if (Res.isNull() || !Res->isRealFloatingType())
15316+
return Diag(OrigArg0.get()->getBeginLoc(),
15317+
diag::err_typecheck_call_invalid_ordered_compare)
15318+
<< OrigArg0.get()->getType() << OrigArg1.get()->getType()
15319+
<< SourceRange(OrigArg0.get()->getBeginLoc(),
15320+
OrigArg1.get()->getEndLoc());
15321+
15322+
TheCall->setType(Res);
15323+
return false;
15324+
}
15325+
1528115326
bool Sema::BuiltinElementwiseMath(CallExpr *TheCall,
1528215327
EltwiseBuiltinArgTyRestriction ArgTyRestr) {
1528315328
if (auto Res = BuiltinVectorMath(TheCall, ArgTyRestr); Res.has_value()) {

0 commit comments

Comments
 (0)