Skip to content

Commit 2e4a8c4

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 136f257 commit 2e4a8c4

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];
@@ -1298,6 +1310,18 @@ def ElementwiseMin : Builtin {
12981310
let Prototype = "void(...)";
12991311
}
13001312

1313+
def ElementwiseMaxNum : Builtin {
1314+
let Spellings = ["__builtin_elementwise_maxnum"];
1315+
let Attributes = [NoThrow, Const, CustomTypeChecking];
1316+
let Prototype = "void(...)";
1317+
}
1318+
1319+
def ElementwiseMinNum : Builtin {
1320+
let Spellings = ["__builtin_elementwise_minnum"];
1321+
let Attributes = [NoThrow, Const, CustomTypeChecking];
1322+
let Prototype = "void(...)";
1323+
}
1324+
13011325
def ElementwiseMaximum : Builtin {
13021326
let Spellings = ["__builtin_elementwise_maximum"];
13031327
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
@@ -2569,6 +2569,7 @@ class Sema final : public SemaBase {
25692569
ExprResult AtomicOpsOverloaded(ExprResult TheCallResult,
25702570
AtomicExpr::AtomicOp Op);
25712571

2572+
bool BuiltinMaxNumMinNumMath(CallExpr *TheCall);
25722573
/// \param FPOnly restricts the arguments to floating-point types.
25732574
bool BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly = false);
25742575
bool PrepareBuiltinReduceMathOneArgCall(CallExpr *TheCall);

clang/lib/CodeGen/CGBuiltin.cpp

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

3223+
case Builtin::BI__builtin_maxnum:
3224+
return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
3225+
*this, E, Intrinsic::maxnum,
3226+
Intrinsic::experimental_constrained_maxnum));
3227+
3228+
case Builtin::BI__builtin_minnum:
3229+
return RValue::get(emitBinaryMaybeConstrainedFPBuiltin(
3230+
*this, E, Intrinsic::minnum,
3231+
Intrinsic::experimental_constrained_minnum));
3232+
32233233
case Builtin::BIfmaximum_num:
32243234
case Builtin::BIfmaximum_numf:
32253235
case Builtin::BIfmaximum_numl:
@@ -4398,6 +4408,22 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
43984408
return RValue::get(Result);
43994409
}
44004410

4411+
case Builtin::BI__builtin_elementwise_maxnum: {
4412+
Value *Op0 = EmitScalarExpr(E->getArg(0));
4413+
Value *Op1 = EmitScalarExpr(E->getArg(1));
4414+
Value *Result = Builder.CreateBinaryIntrinsic(llvm::Intrinsic::maxnum, Op0,
4415+
Op1, nullptr, "elt.maxnum");
4416+
return RValue::get(Result);
4417+
}
4418+
4419+
case Builtin::BI__builtin_elementwise_minnum: {
4420+
Value *Op0 = EmitScalarExpr(E->getArg(0));
4421+
Value *Op1 = EmitScalarExpr(E->getArg(1));
4422+
Value *Result = Builder.CreateBinaryIntrinsic(llvm::Intrinsic::minnum, Op0,
4423+
Op1, nullptr, "elt.minnum");
4424+
return RValue::get(Result);
4425+
}
4426+
44014427
case Builtin::BI__builtin_elementwise_maximum: {
44024428
Value *Op0 = EmitScalarExpr(E->getArg(0));
44034429
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
@@ -2753,8 +2753,17 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
27532753
break;
27542754
}
27552755

2756+
case Builtin::BI__builtin_minnum:
2757+
case Builtin::BI__builtin_maxnum: {
2758+
if (BuiltinMaxNumMinNumMath(TheCall))
2759+
return ExprError();
2760+
break;
2761+
}
2762+
27562763
// These builtins restrict the element type to floating point
27572764
// types only, and take in two arguments.
2765+
case Builtin::BI__builtin_elementwise_minnum:
2766+
case Builtin::BI__builtin_elementwise_maxnum:
27582767
case Builtin::BI__builtin_elementwise_minimum:
27592768
case Builtin::BI__builtin_elementwise_maximum:
27602769
case Builtin::BI__builtin_elementwise_atan2:
@@ -15234,6 +15243,42 @@ bool Sema::PrepareBuiltinElementwiseMathOneArgCall(CallExpr *TheCall) {
1523415243
return false;
1523515244
}
1523615245

15246+
bool Sema::BuiltinMaxNumMinNumMath(CallExpr *TheCall) {
15247+
if (checkArgCount(TheCall, 2))
15248+
return true;
15249+
15250+
ExprResult OrigArg0 = TheCall->getArg(0);
15251+
ExprResult OrigArg1 = TheCall->getArg(1);
15252+
15253+
// Do standard promotions between the two arguments, returning their common
15254+
// type.
15255+
QualType Res = UsualArithmeticConversions(
15256+
OrigArg0, OrigArg1, TheCall->getExprLoc(), ACK_Comparison);
15257+
if (OrigArg0.isInvalid() || OrigArg1.isInvalid())
15258+
return true;
15259+
15260+
// Make sure any conversions are pushed back into the call; this is
15261+
// type safe since unordered compare builtins are declared as "_Bool
15262+
// foo(...)".
15263+
TheCall->setArg(0, OrigArg0.get());
15264+
TheCall->setArg(1, OrigArg1.get());
15265+
15266+
if (!OrigArg0.get()->isTypeDependent() && OrigArg1.get()->isTypeDependent())
15267+
return true;
15268+
15269+
// If the common type isn't a real floating type, then the arguments were
15270+
// invalid for this operation.
15271+
if (Res.isNull() || !Res->isRealFloatingType())
15272+
return Diag(OrigArg0.get()->getBeginLoc(),
15273+
diag::err_typecheck_call_invalid_ordered_compare)
15274+
<< OrigArg0.get()->getType() << OrigArg1.get()->getType()
15275+
<< SourceRange(OrigArg0.get()->getBeginLoc(),
15276+
OrigArg1.get()->getEndLoc());
15277+
15278+
TheCall->setType(Res);
15279+
return false;
15280+
}
15281+
1523715282
bool Sema::BuiltinElementwiseMath(CallExpr *TheCall, bool FPOnly) {
1523815283
if (auto Res = BuiltinVectorMath(TheCall, FPOnly); Res.has_value()) {
1523915284
TheCall->setType(*Res);

0 commit comments

Comments
 (0)