Skip to content

[Clang] constexpr builtin floating point classification / comparison functions #94118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 11 additions & 9 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -533,42 +533,42 @@ def BuiltinComplex : Builtin {
def IsGreater : Builtin {
let Spellings = ["__builtin_isgreater"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
CustomTypeChecking, Constexpr];
let Prototype = "int(...)";
}

def IsGreaterEqual : Builtin {
let Spellings = ["__builtin_isgreaterequal"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
CustomTypeChecking, Constexpr];
let Prototype = "int(...)";
}

def IsLess : Builtin {
let Spellings = ["__builtin_isless"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
CustomTypeChecking, Constexpr];
let Prototype = "int(...)";
}

def IsLessEqual : Builtin {
let Spellings = ["__builtin_islessequal"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
CustomTypeChecking, Constexpr];
let Prototype = "int(...)";
}

def IsLessGreater : Builtin {
let Spellings = ["__builtin_islessgreater"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
CustomTypeChecking, Constexpr];
let Prototype = "int(...)";
}

def IsUnordered : Builtin {
let Spellings = ["__builtin_isunordered"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
CustomTypeChecking, Constexpr];
let Prototype = "int(...)";
}

Expand Down Expand Up @@ -646,19 +646,21 @@ def IsFPClass : Builtin {
def Signbit : Builtin {
let Spellings = ["__builtin_signbit"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
CustomTypeChecking];
CustomTypeChecking, Constexpr];
let Prototype = "int(...)";
}

def SignbitF : Builtin {
let Spellings = ["__builtin_signbitf"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
Constexpr];
let Prototype = "int(float)";
}

def SignbitL : Builtin {
let Spellings = ["__builtin_signbitl"];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const];
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const,
Constexpr];
let Prototype = "int(long double)";
}

Expand Down
67 changes: 67 additions & 0 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12650,6 +12650,73 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
Success(Val.isZero() ? 1 : 0, E);
}

case Builtin::BI__builtin_signbit:
case Builtin::BI__builtin_signbitf:
case Builtin::BI__builtin_signbitl: {
APFloat Val(0.0);
return EvaluateFloat(E->getArg(0), Val, Info) &&
Success(Val.isNegative() ? 1 : 0, E);
}

case Builtin::BI__builtin_isgreater:
case Builtin::BI__builtin_isgreaterequal:
case Builtin::BI__builtin_isless:
case Builtin::BI__builtin_islessequal:
case Builtin::BI__builtin_islessgreater:
case Builtin::BI__builtin_isunordered: {
APFloat LHS(0.0);
APFloat RHS(0.0);
if (!EvaluateFloat(E->getArg(0), LHS, Info) ||
!EvaluateFloat(E->getArg(1), RHS, Info))
return false;

APFloat::cmpResult Cmp = LHS.compare(RHS);
bool FunctionResult;
if (BuiltinOp == Builtin::BI__builtin_isunordered ||
Cmp == APFloat::cmpResult::cmpUnordered) {
FunctionResult = BuiltinOp == Builtin::BI__builtin_isunordered &&
Cmp == APFloat::cmpResult::cmpUnordered;
} else {
int CmpStrong;
switch (Cmp) {
case APFloat::cmpResult::cmpEqual:
CmpStrong = 0;
break;
case APFloat::cmpResult::cmpLessThan:
CmpStrong = -1;
break;
case APFloat::cmpResult::cmpGreaterThan:
CmpStrong = 1;
break;
default:
llvm_unreachable("Unchecked cmpResult enum");
}

switch (BuiltinOp) {
case Builtin::BI__builtin_isgreater:
FunctionResult = CmpStrong > 0;
break;
case Builtin::BI__builtin_isgreaterequal:
FunctionResult = CmpStrong >= 0;
break;
case Builtin::BI__builtin_isless:
FunctionResult = CmpStrong < 0;
break;
case Builtin::BI__builtin_islessequal:
FunctionResult = CmpStrong <= 0;
break;
case Builtin::BI__builtin_islessgreater:
FunctionResult = CmpStrong != 0;
break;
default:
llvm_unreachable("Unexpected builtin ID: Should be a floating point "
"comparison function");
}
}

return Success(FunctionResult ? 1 : 0, E);
}

case Builtin::BI__builtin_issignaling: {
APFloat Val(0.0);
return EvaluateFloat(E->getArg(0), Val, Info) &&
Expand Down
87 changes: 87 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,78 @@ static bool interp__builtin_iszero(InterpState &S, CodePtr OpPC,
return true;
}

static bool interp__builtin_signbit(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame, const Function *F,
const CallExpr *Call) {
const Floating &Arg = S.Stk.peek<Floating>();

pushInteger(S, Arg.isNegative(), Call->getType());
return true;
}

static bool interp_floating_comparison(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const Function *F,
const CallExpr *Call) {
const Floating &RHS = S.Stk.peek<Floating>();
const Floating &LHS = S.Stk.peek<Floating>(align(2u * primSize(PT_Float)));
unsigned ID = F->getBuiltinID();
assert(ID == Builtin::BI__builtin_isgreater ||
ID == Builtin::BI__builtin_isgreaterequal ||
ID == Builtin::BI__builtin_isless ||
ID == Builtin::BI__builtin_islessequal ||
ID == Builtin::BI__builtin_islessgreater ||
ID == Builtin::BI__builtin_isunordered);

ComparisonCategoryResult Cmp = LHS.compare(RHS);
bool FunctionResult;
if (ID == Builtin::BI__builtin_isunordered ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if we could share much of this implementation with the one in ExprConstant.cpp instead of duplicating the code. However, I don't insist (eventually I hope the ExprConstant.cpp code will go away).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It turned out much nicer when I switched on the builtin ID first, so there's not much left to share. This could be extracted to a bool ComparisonFunction(unsigned BuiltinID, const APFloat &LHS, const APFloat &RHS), but I don't know where I could put that so that ExprConstant.cpp and InterpBuiltin.cpp could both access it

Cmp == ComparisonCategoryResult::Unordered) {
FunctionResult = ID == Builtin::BI__builtin_isunordered &&
Cmp == ComparisonCategoryResult::Unordered;
} else {
int CmpStrong;
switch (Cmp) {
case ComparisonCategoryResult::Equal:
case ComparisonCategoryResult::Equivalent:
CmpStrong = 0;
break;
case ComparisonCategoryResult::Less:
CmpStrong = -1;
break;
case ComparisonCategoryResult::Greater:
CmpStrong = 1;
break;
default:
llvm_unreachable("Unchecked ComparisonCategoryResult enum");
}

switch (ID) {
case Builtin::BI__builtin_isgreater:
FunctionResult = CmpStrong > 0;
break;
case Builtin::BI__builtin_isgreaterequal:
FunctionResult = CmpStrong >= 0;
break;
case Builtin::BI__builtin_isless:
FunctionResult = CmpStrong < 0;
break;
case Builtin::BI__builtin_islessequal:
FunctionResult = CmpStrong <= 0;
break;
case Builtin::BI__builtin_islessgreater:
FunctionResult = CmpStrong != 0;
break;
default:
llvm_unreachable("Unexpected builtin ID: Should be a floating point "
"comparison function");
}
}

pushInteger(S, FunctionResult, Call->getType());
return true;
}

/// First parameter to __builtin_isfpclass is the floating value, the
/// second one is an integral value.
static bool interp__builtin_isfpclass(InterpState &S, CodePtr OpPC,
Expand Down Expand Up @@ -1214,6 +1286,21 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
if (!interp__builtin_iszero(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_signbit:
case Builtin::BI__builtin_signbitf:
case Builtin::BI__builtin_signbitl:
if (!interp__builtin_signbit(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_isgreater:
case Builtin::BI__builtin_isgreaterequal:
case Builtin::BI__builtin_isless:
case Builtin::BI__builtin_islessequal:
case Builtin::BI__builtin_islessgreater:
case Builtin::BI__builtin_isunordered:
if (!interp_floating_comparison(S, OpPC, Frame, F, Call))
return false;
break;
case Builtin::BI__builtin_isfpclass:
if (!interp__builtin_isfpclass(S, OpPC, Frame, F, Call))
return false;
Expand Down
Loading
Loading