cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
394 lines
14 KiB
C++
394 lines
14 KiB
C++
//===- ConstantFPRange.cpp - ConstantFPRange implementation ---------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/IR/ConstantFPRange.h"
|
|
#include "llvm/ADT/APFloat.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <cassert>
|
|
|
|
using namespace llvm;
|
|
|
|
void ConstantFPRange::makeEmpty() {
|
|
auto &Sem = Lower.getSemantics();
|
|
Lower = APFloat::getInf(Sem, /*Negative=*/false);
|
|
Upper = APFloat::getInf(Sem, /*Negative=*/true);
|
|
MayBeQNaN = false;
|
|
MayBeSNaN = false;
|
|
}
|
|
|
|
void ConstantFPRange::makeFull() {
|
|
auto &Sem = Lower.getSemantics();
|
|
Lower = APFloat::getInf(Sem, /*Negative=*/true);
|
|
Upper = APFloat::getInf(Sem, /*Negative=*/false);
|
|
MayBeQNaN = true;
|
|
MayBeSNaN = true;
|
|
}
|
|
|
|
bool ConstantFPRange::isNaNOnly() const {
|
|
return Lower.isPosInfinity() && Upper.isNegInfinity();
|
|
}
|
|
|
|
ConstantFPRange::ConstantFPRange(const fltSemantics &Sem, bool IsFullSet)
|
|
: Lower(Sem, APFloat::uninitialized), Upper(Sem, APFloat::uninitialized) {
|
|
Lower = APFloat::getInf(Sem, /*Negative=*/IsFullSet);
|
|
Upper = APFloat::getInf(Sem, /*Negative=*/!IsFullSet);
|
|
MayBeQNaN = IsFullSet;
|
|
MayBeSNaN = IsFullSet;
|
|
}
|
|
|
|
ConstantFPRange::ConstantFPRange(const APFloat &Value)
|
|
: Lower(Value.getSemantics(), APFloat::uninitialized),
|
|
Upper(Value.getSemantics(), APFloat::uninitialized) {
|
|
if (Value.isNaN()) {
|
|
makeEmpty();
|
|
bool IsSNaN = Value.isSignaling();
|
|
MayBeQNaN = !IsSNaN;
|
|
MayBeSNaN = IsSNaN;
|
|
} else {
|
|
Lower = Upper = Value;
|
|
MayBeQNaN = MayBeSNaN = false;
|
|
}
|
|
}
|
|
|
|
// We treat that -0 is less than 0 here.
|
|
static APFloat::cmpResult strictCompare(const APFloat &LHS,
|
|
const APFloat &RHS) {
|
|
assert(!LHS.isNaN() && !RHS.isNaN() && "Unordered compare");
|
|
if (LHS.isZero() && RHS.isZero()) {
|
|
if (LHS.isNegative() == RHS.isNegative())
|
|
return APFloat::cmpEqual;
|
|
return LHS.isNegative() ? APFloat::cmpLessThan : APFloat::cmpGreaterThan;
|
|
}
|
|
return LHS.compare(RHS);
|
|
}
|
|
|
|
static bool isNonCanonicalEmptySet(const APFloat &Lower, const APFloat &Upper) {
|
|
return strictCompare(Lower, Upper) == APFloat::cmpGreaterThan &&
|
|
!(Lower.isInfinity() && Upper.isInfinity());
|
|
}
|
|
|
|
static void canonicalizeRange(APFloat &Lower, APFloat &Upper) {
|
|
if (isNonCanonicalEmptySet(Lower, Upper)) {
|
|
Lower = APFloat::getInf(Lower.getSemantics(), /*Negative=*/false);
|
|
Upper = APFloat::getInf(Upper.getSemantics(), /*Negative=*/true);
|
|
}
|
|
}
|
|
|
|
ConstantFPRange::ConstantFPRange(APFloat LowerVal, APFloat UpperVal,
|
|
bool MayBeQNaNVal, bool MayBeSNaNVal)
|
|
: Lower(std::move(LowerVal)), Upper(std::move(UpperVal)),
|
|
MayBeQNaN(MayBeQNaNVal), MayBeSNaN(MayBeSNaNVal) {
|
|
assert(&Lower.getSemantics() == &Upper.getSemantics() &&
|
|
"Should only use the same semantics");
|
|
assert(!isNonCanonicalEmptySet(Lower, Upper) && "Non-canonical form");
|
|
}
|
|
|
|
ConstantFPRange ConstantFPRange::getFinite(const fltSemantics &Sem) {
|
|
return ConstantFPRange(APFloat::getLargest(Sem, /*Negative=*/true),
|
|
APFloat::getLargest(Sem, /*Negative=*/false),
|
|
/*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
|
|
}
|
|
|
|
ConstantFPRange ConstantFPRange::getNaNOnly(const fltSemantics &Sem,
|
|
bool MayBeQNaN, bool MayBeSNaN) {
|
|
return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/false),
|
|
APFloat::getInf(Sem, /*Negative=*/true), MayBeQNaN,
|
|
MayBeSNaN);
|
|
}
|
|
|
|
ConstantFPRange ConstantFPRange::getNonNaN(const fltSemantics &Sem) {
|
|
return ConstantFPRange(APFloat::getInf(Sem, /*Negative=*/true),
|
|
APFloat::getInf(Sem, /*Negative=*/false),
|
|
/*MayBeQNaN=*/false, /*MayBeSNaN=*/false);
|
|
}
|
|
|
|
/// Return true for ULT/UGT/OLT/OGT
|
|
static bool fcmpPredExcludesEqual(FCmpInst::Predicate Pred) {
|
|
return !(Pred & FCmpInst::FCMP_OEQ);
|
|
}
|
|
|
|
/// Return [-inf, V) or [-inf, V]
|
|
static ConstantFPRange makeLessThan(APFloat V, FCmpInst::Predicate Pred) {
|
|
const fltSemantics &Sem = V.getSemantics();
|
|
if (fcmpPredExcludesEqual(Pred)) {
|
|
if (V.isNegInfinity())
|
|
return ConstantFPRange::getEmpty(Sem);
|
|
V.next(/*nextDown=*/true);
|
|
}
|
|
return ConstantFPRange::getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
|
|
std::move(V));
|
|
}
|
|
|
|
/// Return (V, +inf] or [V, +inf]
|
|
static ConstantFPRange makeGreaterThan(APFloat V, FCmpInst::Predicate Pred) {
|
|
const fltSemantics &Sem = V.getSemantics();
|
|
if (fcmpPredExcludesEqual(Pred)) {
|
|
if (V.isPosInfinity())
|
|
return ConstantFPRange::getEmpty(Sem);
|
|
V.next(/*nextDown=*/false);
|
|
}
|
|
return ConstantFPRange::getNonNaN(std::move(V),
|
|
APFloat::getInf(Sem, /*Negative=*/false));
|
|
}
|
|
|
|
/// Make sure that +0/-0 are both included in the range.
|
|
static ConstantFPRange extendZeroIfEqual(const ConstantFPRange &CR,
|
|
FCmpInst::Predicate Pred) {
|
|
if (fcmpPredExcludesEqual(Pred))
|
|
return CR;
|
|
|
|
APFloat Lower = CR.getLower();
|
|
APFloat Upper = CR.getUpper();
|
|
if (Lower.isPosZero())
|
|
Lower = APFloat::getZero(Lower.getSemantics(), /*Negative=*/true);
|
|
if (Upper.isNegZero())
|
|
Upper = APFloat::getZero(Upper.getSemantics(), /*Negative=*/false);
|
|
return ConstantFPRange(std::move(Lower), std::move(Upper), CR.containsQNaN(),
|
|
CR.containsSNaN());
|
|
}
|
|
|
|
static ConstantFPRange setNaNField(const ConstantFPRange &CR,
|
|
FCmpInst::Predicate Pred) {
|
|
bool ContainsNaN = FCmpInst::isUnordered(Pred);
|
|
return ConstantFPRange(CR.getLower(), CR.getUpper(),
|
|
/*MayBeQNaN=*/ContainsNaN, /*MayBeSNaN=*/ContainsNaN);
|
|
}
|
|
|
|
ConstantFPRange
|
|
ConstantFPRange::makeAllowedFCmpRegion(FCmpInst::Predicate Pred,
|
|
const ConstantFPRange &Other) {
|
|
if (Other.isEmptySet())
|
|
return Other;
|
|
if (Other.containsNaN() && FCmpInst::isUnordered(Pred))
|
|
return getFull(Other.getSemantics());
|
|
if (Other.isNaNOnly() && FCmpInst::isOrdered(Pred))
|
|
return getEmpty(Other.getSemantics());
|
|
|
|
switch (Pred) {
|
|
case FCmpInst::FCMP_TRUE:
|
|
return getFull(Other.getSemantics());
|
|
case FCmpInst::FCMP_FALSE:
|
|
return getEmpty(Other.getSemantics());
|
|
case FCmpInst::FCMP_ORD:
|
|
return getNonNaN(Other.getSemantics());
|
|
case FCmpInst::FCMP_UNO:
|
|
return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true,
|
|
/*MayBeSNaN=*/true);
|
|
case FCmpInst::FCMP_OEQ:
|
|
case FCmpInst::FCMP_UEQ:
|
|
return setNaNField(extendZeroIfEqual(Other, Pred), Pred);
|
|
case FCmpInst::FCMP_ONE:
|
|
case FCmpInst::FCMP_UNE:
|
|
if (const APFloat *SingleElement =
|
|
Other.getSingleElement(/*ExcludesNaN=*/true)) {
|
|
const fltSemantics &Sem = SingleElement->getSemantics();
|
|
if (SingleElement->isPosInfinity())
|
|
return setNaNField(
|
|
getNonNaN(APFloat::getInf(Sem, /*Negative=*/true),
|
|
APFloat::getLargest(Sem, /*Negative=*/false)),
|
|
Pred);
|
|
if (SingleElement->isNegInfinity())
|
|
return setNaNField(
|
|
getNonNaN(APFloat::getLargest(Sem, /*Negative=*/true),
|
|
APFloat::getInf(Sem, /*Negative=*/false)),
|
|
Pred);
|
|
}
|
|
return Pred == FCmpInst::FCMP_ONE ? getNonNaN(Other.getSemantics())
|
|
: getFull(Other.getSemantics());
|
|
case FCmpInst::FCMP_OLT:
|
|
case FCmpInst::FCMP_OLE:
|
|
case FCmpInst::FCMP_ULT:
|
|
case FCmpInst::FCMP_ULE:
|
|
return setNaNField(
|
|
extendZeroIfEqual(makeLessThan(Other.getUpper(), Pred), Pred), Pred);
|
|
case FCmpInst::FCMP_OGT:
|
|
case FCmpInst::FCMP_OGE:
|
|
case FCmpInst::FCMP_UGT:
|
|
case FCmpInst::FCMP_UGE:
|
|
return setNaNField(
|
|
extendZeroIfEqual(makeGreaterThan(Other.getLower(), Pred), Pred), Pred);
|
|
default:
|
|
llvm_unreachable("Unexpected predicate");
|
|
}
|
|
}
|
|
|
|
ConstantFPRange
|
|
ConstantFPRange::makeSatisfyingFCmpRegion(FCmpInst::Predicate Pred,
|
|
const ConstantFPRange &Other) {
|
|
if (Other.isEmptySet())
|
|
return getFull(Other.getSemantics());
|
|
if (Other.containsNaN() && FCmpInst::isOrdered(Pred))
|
|
return getEmpty(Other.getSemantics());
|
|
if (Other.isNaNOnly() && FCmpInst::isUnordered(Pred))
|
|
return getFull(Other.getSemantics());
|
|
|
|
switch (Pred) {
|
|
case FCmpInst::FCMP_TRUE:
|
|
return getFull(Other.getSemantics());
|
|
case FCmpInst::FCMP_FALSE:
|
|
return getEmpty(Other.getSemantics());
|
|
case FCmpInst::FCMP_ORD:
|
|
return getNonNaN(Other.getSemantics());
|
|
case FCmpInst::FCMP_UNO:
|
|
return getNaNOnly(Other.getSemantics(), /*MayBeQNaN=*/true,
|
|
/*MayBeSNaN=*/true);
|
|
case FCmpInst::FCMP_OEQ:
|
|
case FCmpInst::FCMP_UEQ:
|
|
return setNaNField(Other.isSingleElement(/*ExcludesNaN=*/true) ||
|
|
((Other.classify() & ~fcNan) == fcZero)
|
|
? extendZeroIfEqual(Other, Pred)
|
|
: getEmpty(Other.getSemantics()),
|
|
Pred);
|
|
case FCmpInst::FCMP_ONE:
|
|
case FCmpInst::FCMP_UNE:
|
|
return getEmpty(Other.getSemantics());
|
|
case FCmpInst::FCMP_OLT:
|
|
case FCmpInst::FCMP_OLE:
|
|
case FCmpInst::FCMP_ULT:
|
|
case FCmpInst::FCMP_ULE:
|
|
return setNaNField(
|
|
extendZeroIfEqual(makeLessThan(Other.getLower(), Pred), Pred), Pred);
|
|
case FCmpInst::FCMP_OGT:
|
|
case FCmpInst::FCMP_OGE:
|
|
case FCmpInst::FCMP_UGT:
|
|
case FCmpInst::FCMP_UGE:
|
|
return setNaNField(
|
|
extendZeroIfEqual(makeGreaterThan(Other.getUpper(), Pred), Pred), Pred);
|
|
default:
|
|
llvm_unreachable("Unexpected predicate");
|
|
}
|
|
}
|
|
|
|
std::optional<ConstantFPRange>
|
|
ConstantFPRange::makeExactFCmpRegion(FCmpInst::Predicate Pred,
|
|
const APFloat &Other) {
|
|
if ((Pred == FCmpInst::FCMP_UNE || Pred == FCmpInst::FCMP_ONE) &&
|
|
!Other.isNaN())
|
|
return std::nullopt;
|
|
return makeSatisfyingFCmpRegion(Pred, ConstantFPRange(Other));
|
|
}
|
|
|
|
bool ConstantFPRange::fcmp(FCmpInst::Predicate Pred,
|
|
const ConstantFPRange &Other) const {
|
|
return makeSatisfyingFCmpRegion(Pred, Other).contains(*this);
|
|
}
|
|
|
|
bool ConstantFPRange::isFullSet() const {
|
|
return Lower.isNegInfinity() && Upper.isPosInfinity() && MayBeQNaN &&
|
|
MayBeSNaN;
|
|
}
|
|
|
|
bool ConstantFPRange::isEmptySet() const {
|
|
return Lower.isPosInfinity() && Upper.isNegInfinity() && !MayBeQNaN &&
|
|
!MayBeSNaN;
|
|
}
|
|
|
|
bool ConstantFPRange::contains(const APFloat &Val) const {
|
|
assert(&getSemantics() == &Val.getSemantics() &&
|
|
"Should only use the same semantics");
|
|
|
|
if (Val.isNaN())
|
|
return Val.isSignaling() ? MayBeSNaN : MayBeQNaN;
|
|
return strictCompare(Lower, Val) != APFloat::cmpGreaterThan &&
|
|
strictCompare(Val, Upper) != APFloat::cmpGreaterThan;
|
|
}
|
|
|
|
bool ConstantFPRange::contains(const ConstantFPRange &CR) const {
|
|
assert(&getSemantics() == &CR.getSemantics() &&
|
|
"Should only use the same semantics");
|
|
|
|
if (CR.MayBeQNaN && !MayBeQNaN)
|
|
return false;
|
|
|
|
if (CR.MayBeSNaN && !MayBeSNaN)
|
|
return false;
|
|
|
|
return strictCompare(Lower, CR.Lower) != APFloat::cmpGreaterThan &&
|
|
strictCompare(CR.Upper, Upper) != APFloat::cmpGreaterThan;
|
|
}
|
|
|
|
const APFloat *ConstantFPRange::getSingleElement(bool ExcludesNaN) const {
|
|
if (!ExcludesNaN && (MayBeSNaN || MayBeQNaN))
|
|
return nullptr;
|
|
return Lower.bitwiseIsEqual(Upper) ? &Lower : nullptr;
|
|
}
|
|
|
|
std::optional<bool> ConstantFPRange::getSignBit() const {
|
|
if (!MayBeSNaN && !MayBeQNaN && Lower.isNegative() == Upper.isNegative())
|
|
return Lower.isNegative();
|
|
return std::nullopt;
|
|
}
|
|
|
|
bool ConstantFPRange::operator==(const ConstantFPRange &CR) const {
|
|
if (MayBeSNaN != CR.MayBeSNaN || MayBeQNaN != CR.MayBeQNaN)
|
|
return false;
|
|
return Lower.bitwiseIsEqual(CR.Lower) && Upper.bitwiseIsEqual(CR.Upper);
|
|
}
|
|
|
|
FPClassTest ConstantFPRange::classify() const {
|
|
uint32_t Mask = fcNone;
|
|
if (MayBeSNaN)
|
|
Mask |= fcSNan;
|
|
if (MayBeQNaN)
|
|
Mask |= fcQNan;
|
|
if (!isNaNOnly()) {
|
|
FPClassTest LowerMask = Lower.classify();
|
|
FPClassTest UpperMask = Upper.classify();
|
|
assert(LowerMask <= UpperMask && "Range is nan-only.");
|
|
// Set all bits from log2(LowerMask) to log2(UpperMask).
|
|
Mask |= (UpperMask << 1) - LowerMask;
|
|
}
|
|
return static_cast<FPClassTest>(Mask);
|
|
}
|
|
|
|
void ConstantFPRange::print(raw_ostream &OS) const {
|
|
if (isFullSet())
|
|
OS << "full-set";
|
|
else if (isEmptySet())
|
|
OS << "empty-set";
|
|
else {
|
|
bool NaNOnly = isNaNOnly();
|
|
if (!NaNOnly)
|
|
OS << '[' << Lower << ", " << Upper << ']';
|
|
|
|
if (MayBeSNaN || MayBeQNaN) {
|
|
if (!NaNOnly)
|
|
OS << " with ";
|
|
if (MayBeSNaN && MayBeQNaN)
|
|
OS << "NaN";
|
|
else if (MayBeSNaN)
|
|
OS << "SNaN";
|
|
else if (MayBeQNaN)
|
|
OS << "QNaN";
|
|
}
|
|
}
|
|
}
|
|
|
|
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
|
|
LLVM_DUMP_METHOD void ConstantFPRange::dump() const { print(dbgs()); }
|
|
#endif
|
|
|
|
ConstantFPRange
|
|
ConstantFPRange::intersectWith(const ConstantFPRange &CR) const {
|
|
assert(&getSemantics() == &CR.getSemantics() &&
|
|
"Should only use the same semantics");
|
|
APFloat NewLower = maxnum(Lower, CR.Lower);
|
|
APFloat NewUpper = minnum(Upper, CR.Upper);
|
|
canonicalizeRange(NewLower, NewUpper);
|
|
return ConstantFPRange(std::move(NewLower), std::move(NewUpper),
|
|
MayBeQNaN & CR.MayBeQNaN, MayBeSNaN & CR.MayBeSNaN);
|
|
}
|
|
|
|
ConstantFPRange ConstantFPRange::unionWith(const ConstantFPRange &CR) const {
|
|
assert(&getSemantics() == &CR.getSemantics() &&
|
|
"Should only use the same semantics");
|
|
return ConstantFPRange(minnum(Lower, CR.Lower), maxnum(Upper, CR.Upper),
|
|
MayBeQNaN | CR.MayBeQNaN, MayBeSNaN | CR.MayBeSNaN);
|
|
}
|