diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:01:22 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2017-04-16 16:01:22 +0000 |
commit | 71d5a2540a98c81f5bcaeb48805e0e2881f530ef (patch) | |
tree | 5343938942df402b49ec7300a1c25a2d4ccd5821 /lib/Support | |
parent | 31bbf64f3a4974a2d6c8b3b27ad2f519caf74057 (diff) | |
download | src-71d5a2540a98c81f5bcaeb48805e0e2881f530ef.tar.gz src-71d5a2540a98c81f5bcaeb48805e0e2881f530ef.zip |
Notes
Diffstat (limited to 'lib/Support')
46 files changed, 3718 insertions, 1461 deletions
diff --git a/lib/Support/APFloat.cpp b/lib/Support/APFloat.cpp index 4cfbbf8645e0..9778628911cd 100644 --- a/lib/Support/APFloat.cpp +++ b/lib/Support/APFloat.cpp @@ -26,8 +26,21 @@ #include <cstring> #include <limits.h> +#define APFLOAT_DISPATCH_ON_SEMANTICS(METHOD_CALL) \ + do { \ + if (usesLayout<IEEEFloat>(getSemantics())) \ + return U.IEEE.METHOD_CALL; \ + if (usesLayout<DoubleAPFloat>(getSemantics())) \ + return U.Double.METHOD_CALL; \ + llvm_unreachable("Unexpected semantics"); \ + } while (false) + using namespace llvm; +// TODO: Remove these and use APInt qualified types directly. +typedef APInt::WordType integerPart; +const unsigned int integerPartWidth = APInt::APINT_BITS_PER_WORD; + /// A macro used to combine two fcCategory enums into one key which can be used /// in a switch statement to classify how the interaction of two APFloat's /// categories affects an operation. @@ -66,33 +79,43 @@ namespace llvm { static const fltSemantics semX87DoubleExtended = {16383, -16382, 64, 80}; static const fltSemantics semBogus = {0, 0, 0, 0}; - /* The PowerPC format consists of two doubles. It does not map cleanly - onto the usual format above. It is approximated using twice the - mantissa bits. Note that for exponents near the double minimum, - we no longer can represent the full 106 mantissa bits, so those - will be treated as denormal numbers. - - FIXME: While this approximation is equivalent to what GCC uses for - compile-time arithmetic on PPC double-double numbers, it is not able - to represent all possible values held by a PPC double-double number, - for example: (long double) 1.0 + (long double) 0x1p-106 - Should this be replaced by a full emulation of PPC double-double? + /* The IBM double-double semantics. Such a number consists of a pair of IEEE + 64-bit doubles (Hi, Lo), where |Hi| > |Lo|, and if normal, + (double)(Hi + Lo) == Hi. The numeric value it's modeling is Hi + Lo. + Therefore it has two 53-bit mantissa parts that aren't necessarily adjacent + to each other, and two 11-bit exponents. Note: we need to make the value different from semBogus as otherwise an unsafe optimization may collapse both values to a single address, and we heavily rely on them having distinct addresses. */ static const fltSemantics semPPCDoubleDouble = {-1, 0, 0, 0}; - /* There are temporary semantics for the real PPCDoubleDouble implementation. - Currently, APFloat of PPCDoubleDouble holds one PPCDoubleDoubleImpl as the - high part of double double, and one IEEEdouble as the low part, so that - the old operations operate on PPCDoubleDoubleImpl, while the newly added - operations also populate the IEEEdouble. + /* These are legacy semantics for the fallback, inaccrurate implementation of + IBM double-double, if the accurate semPPCDoubleDouble doesn't handle the + operation. It's equivalent to having an IEEE number with consecutive 106 + bits of mantissa and 11 bits of exponent. + + It's not equivalent to IBM double-double. For example, a legit IBM + double-double, 1 + epsilon: + + 1 + epsilon = 1 + (1 >> 1076) - TODO: Once all functions support DoubleAPFloat mode, we'll change all - PPCDoubleDoubleImpl to IEEEdouble and remove PPCDoubleDoubleImpl. */ - static const fltSemantics semPPCDoubleDoubleImpl = {1023, -1022 + 53, 53 + 53, - 128}; + is not representable by a consecutive 106 bits of mantissa. + + Currently, these semantics are used in the following way: + + semPPCDoubleDouble -> (IEEEdouble, IEEEdouble) -> + (64-bit APInt, 64-bit APInt) -> (128-bit APInt) -> + semPPCDoubleDoubleLegacy -> IEEE operations + + We use bitcastToAPInt() to get the bit representation (in APInt) of the + underlying IEEEdouble, then use the APInt constructor to construct the + legacy IEEE float. + + TODO: Implement all operations in semPPCDoubleDouble, and delete these + semantics. */ + static const fltSemantics semPPCDoubleDoubleLegacy = {1023, -1022 + 53, + 53 + 53, 128}; const fltSemantics &APFloatBase::IEEEhalf() { return semIEEEhalf; @@ -742,7 +765,7 @@ IEEEFloat &IEEEFloat::operator=(IEEEFloat &&rhs) { bool IEEEFloat::isDenormal() const { return isFiniteNonZero() && (exponent == semantics->minExponent) && - (APInt::tcExtractBit(significandParts(), + (APInt::tcExtractBit(significandParts(), semantics->precision - 1) == 0); } @@ -862,11 +885,6 @@ IEEEFloat::IEEEFloat(IEEEFloat &&rhs) : semantics(&semBogus) { IEEEFloat::~IEEEFloat() { freeSignificand(); } -// Profile - This method 'profiles' an APFloat for use with FoldingSet. -void IEEEFloat::Profile(FoldingSetNodeID &ID) const { - ID.Add(bitcastToAPInt()); -} - unsigned int IEEEFloat::partCount() const { return partCountForBits(semantics->precision + 1); } @@ -966,14 +984,14 @@ lostFraction IEEEFloat::multiplySignificand(const IEEEFloat &rhs, // rhs = b23 . b22 ... b0 * 2^e2 // the result of multiplication is: // *this = c48 c47 c46 . c45 ... c0 * 2^(e1+e2) - // Note that there are three significant bits at the left-hand side of the + // Note that there are three significant bits at the left-hand side of the // radix point: two for the multiplication, and an overflow bit for the // addition (that will always be zero at this point). Move the radix point // toward left by two bits, and adjust exponent accordingly. exponent += 2; if (addend && addend->isNonZero()) { - // The intermediate result of the multiplication has "2 * precision" + // The intermediate result of the multiplication has "2 * precision" // signicant bit; adjust the addend to be consistent with mul result. // Significand savedSignificand = significand; @@ -1025,7 +1043,7 @@ lostFraction IEEEFloat::multiplySignificand(const IEEEFloat &rhs, } // Convert the result having "2 * precision" significant-bits back to the one - // having "precision" significant-bits. First, move the radix point from + // having "precision" significant-bits. First, move the radix point from // poision "2*precision - 1" to "precision - 1". The exponent need to be // adjusted by "2*precision - 1" - "precision - 1" = "precision". exponent -= precision + 1; @@ -1611,16 +1629,6 @@ void IEEEFloat::changeSign() { sign = !sign; } -void IEEEFloat::clearSign() { - /* So is this one. */ - sign = 0; -} - -void IEEEFloat::copySign(const IEEEFloat &rhs) { - /* And this one. */ - sign = rhs.sign; -} - /* Normalized addition or subtraction. */ IEEEFloat::opStatus IEEEFloat::addOrSubtract(const IEEEFloat &rhs, roundingMode rounding_mode, @@ -1712,9 +1720,10 @@ IEEEFloat::opStatus IEEEFloat::remainder(const IEEEFloat &rhs) { int parts = partCount(); integerPart *x = new integerPart[parts]; bool ignored; - fs = V.convertToInteger(x, parts * integerPartWidth, true, - rmNearestTiesToEven, &ignored); - if (fs==opInvalidOp) { + fs = V.convertToInteger(makeMutableArrayRef(x, parts), + parts * integerPartWidth, true, rmNearestTiesToEven, + &ignored); + if (fs == opInvalidOp) { delete[] x; return fs; } @@ -1735,43 +1744,20 @@ IEEEFloat::opStatus IEEEFloat::remainder(const IEEEFloat &rhs) { return fs; } -/* Normalized llvm frem (C fmod). - This is not currently correct in all cases. */ +/* Normalized llvm frem (C fmod). */ IEEEFloat::opStatus IEEEFloat::mod(const IEEEFloat &rhs) { opStatus fs; fs = modSpecials(rhs); - if (isFiniteNonZero() && rhs.isFiniteNonZero()) { - IEEEFloat V = *this; - unsigned int origSign = sign; - - fs = V.divide(rhs, rmNearestTiesToEven); - if (fs == opDivByZero) - return fs; - - int parts = partCount(); - integerPart *x = new integerPart[parts]; - bool ignored; - fs = V.convertToInteger(x, parts * integerPartWidth, true, - rmTowardZero, &ignored); - if (fs==opInvalidOp) { - delete[] x; - return fs; - } - - fs = V.convertFromZeroExtendedInteger(x, parts * integerPartWidth, true, - rmNearestTiesToEven); - assert(fs==opOK); // should always work - - fs = V.multiply(rhs, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // should not overflow or underflow - + while (isFiniteNonZero() && rhs.isFiniteNonZero() && + compareAbsoluteValue(rhs) != cmpLessThan) { + IEEEFloat V = scalbn(rhs, ilogb(*this) - ilogb(rhs), rmNearestTiesToEven); + if (compareAbsoluteValue(V) == cmpLessThan) + V = scalbn(V, -1, rmNearestTiesToEven); + V.sign = sign; + fs = subtract(V, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // likewise - - if (isZero()) - sign = origSign; // IEEE754 requires this - delete[] x; + assert(fs==opOK); } return fs; } @@ -1840,7 +1826,7 @@ IEEEFloat::opStatus IEEEFloat::roundToIntegral(roundingMode rounding_mode) { IEEEFloat MagicConstant(*semantics); fs = MagicConstant.convertFromAPInt(IntegerConstant, false, rmNearestTiesToEven); - MagicConstant.copySign(*this); + MagicConstant.sign = sign; if (fs != opOK) return fs; @@ -2047,7 +2033,7 @@ IEEEFloat::opStatus IEEEFloat::convert(const fltSemantics &toSemantics, Note that for conversions to integer type the C standard requires round-to-zero to always be used. */ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( - integerPart *parts, unsigned int width, bool isSigned, + MutableArrayRef<integerPart> parts, unsigned int width, bool isSigned, roundingMode rounding_mode, bool *isExact) const { lostFraction lost_fraction; const integerPart *src; @@ -2060,9 +2046,10 @@ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( return opInvalidOp; dstPartsCount = partCountForBits(width); + assert(dstPartsCount <= parts.size() && "Integer too big"); if (category == fcZero) { - APInt::tcSet(parts, 0, dstPartsCount); + APInt::tcSet(parts.data(), 0, dstPartsCount); // Negative zero can't be represented as an int. *isExact = !sign; return opOK; @@ -2074,7 +2061,7 @@ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( the destination. */ if (exponent < 0) { /* Our absolute value is less than one; truncate everything. */ - APInt::tcSet(parts, 0, dstPartsCount); + APInt::tcSet(parts.data(), 0, dstPartsCount); /* For exponent -1 the integer bit represents .5, look at that. For smaller exponents leftmost truncated bit is 0. */ truncatedBits = semantics->precision -1U - exponent; @@ -2090,11 +2077,13 @@ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( if (bits < semantics->precision) { /* We truncate (semantics->precision - bits) bits. */ truncatedBits = semantics->precision - bits; - APInt::tcExtract(parts, dstPartsCount, src, bits, truncatedBits); + APInt::tcExtract(parts.data(), dstPartsCount, src, bits, truncatedBits); } else { /* We want at least as many bits as are available. */ - APInt::tcExtract(parts, dstPartsCount, src, semantics->precision, 0); - APInt::tcShiftLeft(parts, dstPartsCount, bits - semantics->precision); + APInt::tcExtract(parts.data(), dstPartsCount, src, semantics->precision, + 0); + APInt::tcShiftLeft(parts.data(), dstPartsCount, + bits - semantics->precision); truncatedBits = 0; } } @@ -2106,7 +2095,7 @@ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( truncatedBits); if (lost_fraction != lfExactlyZero && roundAwayFromZero(rounding_mode, lost_fraction, truncatedBits)) { - if (APInt::tcIncrement(parts, dstPartsCount)) + if (APInt::tcIncrement(parts.data(), dstPartsCount)) return opInvalidOp; /* Overflow. */ } } else { @@ -2114,7 +2103,7 @@ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( } /* Step 3: check if we fit in the destination. */ - unsigned int omsb = APInt::tcMSB(parts, dstPartsCount) + 1; + unsigned int omsb = APInt::tcMSB(parts.data(), dstPartsCount) + 1; if (sign) { if (!isSigned) { @@ -2125,7 +2114,8 @@ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( /* It takes omsb bits to represent the unsigned integer value. We lose a bit for the sign, but care is needed as the maximally negative integer is a special case. */ - if (omsb == width && APInt::tcLSB(parts, dstPartsCount) + 1 != omsb) + if (omsb == width && + APInt::tcLSB(parts.data(), dstPartsCount) + 1 != omsb) return opInvalidOp; /* This case can happen because of rounding. */ @@ -2133,7 +2123,7 @@ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( return opInvalidOp; } - APInt::tcNegate (parts, dstPartsCount); + APInt::tcNegate (parts.data(), dstPartsCount); } else { if (omsb >= width + !isSigned) return opInvalidOp; @@ -2155,11 +2145,10 @@ IEEEFloat::opStatus IEEEFloat::convertToSignExtendedInteger( the original value. This is almost equivalent to result==opOK, except for negative zeroes. */ -IEEEFloat::opStatus IEEEFloat::convertToInteger(integerPart *parts, - unsigned int width, - bool isSigned, - roundingMode rounding_mode, - bool *isExact) const { +IEEEFloat::opStatus +IEEEFloat::convertToInteger(MutableArrayRef<integerPart> parts, + unsigned int width, bool isSigned, + roundingMode rounding_mode, bool *isExact) const { opStatus fs; fs = convertToSignExtendedInteger(parts, width, isSigned, rounding_mode, @@ -2169,6 +2158,7 @@ IEEEFloat::opStatus IEEEFloat::convertToInteger(integerPart *parts, unsigned int bits, dstPartsCount; dstPartsCount = partCountForBits(width); + assert(dstPartsCount <= parts.size() && "Integer too big"); if (category == fcNaN) bits = 0; @@ -2177,30 +2167,14 @@ IEEEFloat::opStatus IEEEFloat::convertToInteger(integerPart *parts, else bits = width - isSigned; - APInt::tcSetLeastSignificantBits(parts, dstPartsCount, bits); + APInt::tcSetLeastSignificantBits(parts.data(), dstPartsCount, bits); if (sign && isSigned) - APInt::tcShiftLeft(parts, dstPartsCount, width - 1); + APInt::tcShiftLeft(parts.data(), dstPartsCount, width - 1); } return fs; } -/* Same as convertToInteger(integerPart*, ...), except the result is returned in - an APSInt, whose initial bit-width and signed-ness are used to determine the - precision of the conversion. - */ -IEEEFloat::opStatus IEEEFloat::convertToInteger(APSInt &result, - roundingMode rounding_mode, - bool *isExact) const { - unsigned bitWidth = result.getBitWidth(); - SmallVector<uint64_t, 4> parts(result.getNumWords()); - opStatus status = convertToInteger( - parts.data(), bitWidth, result.isSigned(), rounding_mode, isExact); - // Keeps the original signed-ness. - result = APInt(bitWidth, parts); - return status; -} - /* Convert an unsigned integer SRC to a floating point number, rounding according to ROUNDING_MODE. The sign of the floating point number is not modified. */ @@ -2484,7 +2458,7 @@ IEEEFloat::convertFromDecimalString(StringRef str, roundingMode rounding_mode) { // Test if we have a zero number allowing for strings with no null terminators // and zero decimals with non-zero exponents. - // + // // We computed firstSigDigit by ignoring all zeros and dots. Thus if // D->firstSigDigit equals str.end(), every digit must be a zero and there can // be at most one dot. On the other hand, if we have a zero with a non-zero @@ -2852,7 +2826,7 @@ APInt IEEEFloat::convertF80LongDoubleAPFloatToAPInt() const { } APInt IEEEFloat::convertPPCDoubleDoubleAPFloatToAPInt() const { - assert(semantics == (const llvm::fltSemantics *)&semPPCDoubleDoubleImpl); + assert(semantics == (const llvm::fltSemantics *)&semPPCDoubleDoubleLegacy); assert(partCount()==2); uint64_t words[2]; @@ -3033,7 +3007,7 @@ APInt IEEEFloat::bitcastToAPInt() const { if (semantics == (const llvm::fltSemantics*)&semIEEEquad) return convertQuadrupleAPFloatToAPInt(); - if (semantics == (const llvm::fltSemantics *)&semPPCDoubleDoubleImpl) + if (semantics == (const llvm::fltSemantics *)&semPPCDoubleDoubleLegacy) return convertPPCDoubleDoubleAPFloatToAPInt(); assert(semantics == (const llvm::fltSemantics*)&semX87DoubleExtended && @@ -3103,14 +3077,14 @@ void IEEEFloat::initFromPPCDoubleDoubleAPInt(const APInt &api) { // Get the first double and convert to our format. initFromDoubleAPInt(APInt(64, i1)); - fs = convert(semPPCDoubleDoubleImpl, rmNearestTiesToEven, &losesInfo); + fs = convert(semPPCDoubleDoubleLegacy, rmNearestTiesToEven, &losesInfo); assert(fs == opOK && !losesInfo); (void)fs; // Unless we have a special case, add in second double. if (isFiniteNonZero()) { IEEEFloat v(semIEEEdouble, APInt(64, i2)); - fs = v.convert(semPPCDoubleDoubleImpl, rmNearestTiesToEven, &losesInfo); + fs = v.convert(semPPCDoubleDoubleLegacy, rmNearestTiesToEven, &losesInfo); assert(fs == opOK && !losesInfo); (void)fs; @@ -3264,7 +3238,7 @@ void IEEEFloat::initFromAPInt(const fltSemantics *Sem, const APInt &api) { return initFromF80LongDoubleAPInt(api); if (Sem == &semIEEEquad) return initFromQuadrupleAPInt(api); - if (Sem == &semPPCDoubleDoubleImpl) + if (Sem == &semPPCDoubleDoubleLegacy) return initFromPPCDoubleDoubleAPInt(api); llvm_unreachable(nullptr); @@ -3620,7 +3594,7 @@ void IEEEFloat::toString(SmallVectorImpl<char> &Str, unsigned FormatPrecision, Str.push_back(buffer[NDigits-I-1]); } -bool IEEEFloat::getExactInverse(IEEEFloat *inv) const { +bool IEEEFloat::getExactInverse(APFloat *inv) const { // Special floats and denormals have no exact inverse. if (!isFiniteNonZero()) return false; @@ -3644,7 +3618,7 @@ bool IEEEFloat::getExactInverse(IEEEFloat *inv) const { reciprocal.significandLSB() == reciprocal.semantics->precision - 1); if (inv) - *inv = reciprocal; + *inv = APFloat(reciprocal, *semantics); return true; } @@ -3856,28 +3830,29 @@ IEEEFloat frexp(const IEEEFloat &Val, int &Exp, IEEEFloat::roundingMode RM) { } DoubleAPFloat::DoubleAPFloat(const fltSemantics &S) - : Semantics(&S), Floats(new APFloat[2]{APFloat(semPPCDoubleDoubleImpl), - APFloat(semIEEEdouble)}) { + : Semantics(&S), + Floats(new APFloat[2]{APFloat(semIEEEdouble), APFloat(semIEEEdouble)}) { assert(Semantics == &semPPCDoubleDouble); } DoubleAPFloat::DoubleAPFloat(const fltSemantics &S, uninitializedTag) : Semantics(&S), - Floats(new APFloat[2]{APFloat(semPPCDoubleDoubleImpl, uninitialized), + Floats(new APFloat[2]{APFloat(semIEEEdouble, uninitialized), APFloat(semIEEEdouble, uninitialized)}) { assert(Semantics == &semPPCDoubleDouble); } DoubleAPFloat::DoubleAPFloat(const fltSemantics &S, integerPart I) - : Semantics(&S), Floats(new APFloat[2]{APFloat(semPPCDoubleDoubleImpl, I), + : Semantics(&S), Floats(new APFloat[2]{APFloat(semIEEEdouble, I), APFloat(semIEEEdouble)}) { assert(Semantics == &semPPCDoubleDouble); } DoubleAPFloat::DoubleAPFloat(const fltSemantics &S, const APInt &I) - : Semantics(&S), Floats(new APFloat[2]{ - APFloat(semPPCDoubleDoubleImpl, I), - APFloat(semIEEEdouble, APInt(64, I.getRawData()[1]))}) { + : Semantics(&S), + Floats(new APFloat[2]{ + APFloat(semIEEEdouble, APInt(64, I.getRawData()[0])), + APFloat(semIEEEdouble, APInt(64, I.getRawData()[1]))}) { assert(Semantics == &semPPCDoubleDouble); } @@ -3886,9 +3861,7 @@ DoubleAPFloat::DoubleAPFloat(const fltSemantics &S, APFloat &&First, : Semantics(&S), Floats(new APFloat[2]{std::move(First), std::move(Second)}) { assert(Semantics == &semPPCDoubleDouble); - // TODO Check for First == &IEEEdouble once the transition is done. - assert(&Floats[0].getSemantics() == &semPPCDoubleDoubleImpl || - &Floats[0].getSemantics() == &semIEEEdouble); + assert(&Floats[0].getSemantics() == &semIEEEdouble); assert(&Floats[1].getSemantics() == &semIEEEdouble); } @@ -3917,6 +3890,7 @@ DoubleAPFloat &DoubleAPFloat::operator=(const DoubleAPFloat &RHS) { return *this; } +// Implement addition, subtraction, multiplication and division based on: // "Software for Doubled-Precision Floating-Point Computations", // by Seppo Linnainmaa, ACM TOMS vol 7 no 3, September 1981, pages 272-283. APFloat::opStatus DoubleAPFloat::addImpl(const APFloat &a, const APFloat &aa, @@ -3928,7 +3902,7 @@ APFloat::opStatus DoubleAPFloat::addImpl(const APFloat &a, const APFloat &aa, if (!z.isFinite()) { if (!z.isInfinity()) { Floats[0] = std::move(z); - Floats[1].makeZero(false); + Floats[1].makeZero(/* Neg = */ false); return (opStatus)Status; } Status = opOK; @@ -3946,7 +3920,7 @@ APFloat::opStatus DoubleAPFloat::addImpl(const APFloat &a, const APFloat &aa, } if (!z.isFinite()) { Floats[0] = std::move(z); - Floats[1].makeZero(false); + Floats[1].makeZero(/* Neg = */ false); return (opStatus)Status; } Floats[0] = z; @@ -3982,13 +3956,13 @@ APFloat::opStatus DoubleAPFloat::addImpl(const APFloat &a, const APFloat &aa, Status |= zz.add(cc, RM); if (zz.isZero() && !zz.isNegative()) { Floats[0] = std::move(z); - Floats[1].makeZero(false); + Floats[1].makeZero(/* Neg = */ false); return opOK; } Floats[0] = z; Status |= Floats[0].add(zz, RM); if (!Floats[0].isFinite()) { - Floats[1].makeZero(false); + Floats[1].makeZero(/* Neg = */ false); return (opStatus)Status; } Floats[1] = std::move(z); @@ -4033,25 +4007,15 @@ APFloat::opStatus DoubleAPFloat::addWithSpecial(const DoubleAPFloat &LHS, } assert(LHS.getCategory() == fcNormal && RHS.getCategory() == fcNormal); - // These conversions will go away once PPCDoubleDoubleImpl goes away. - // (PPCDoubleDoubleImpl, IEEEDouble) -> (IEEEDouble, IEEEDouble) - APFloat A(semIEEEdouble, - APInt(64, LHS.Floats[0].bitcastToAPInt().getRawData()[0])), - AA(LHS.Floats[1]), - C(semIEEEdouble, APInt(64, RHS.Floats[0].bitcastToAPInt().getRawData()[0])), + APFloat A(LHS.Floats[0]), AA(LHS.Floats[1]), C(RHS.Floats[0]), CC(RHS.Floats[1]); + assert(&A.getSemantics() == &semIEEEdouble); assert(&AA.getSemantics() == &semIEEEdouble); + assert(&C.getSemantics() == &semIEEEdouble); assert(&CC.getSemantics() == &semIEEEdouble); - Out.Floats[0] = APFloat(semIEEEdouble); + assert(&Out.Floats[0].getSemantics() == &semIEEEdouble); assert(&Out.Floats[1].getSemantics() == &semIEEEdouble); - - auto Ret = Out.addImpl(A, AA, C, CC, RM); - - // (IEEEDouble, IEEEDouble) -> (PPCDoubleDoubleImpl, IEEEDouble) - uint64_t Buffer[] = {Out.Floats[0].bitcastToAPInt().getRawData()[0], - Out.Floats[1].bitcastToAPInt().getRawData()[0]}; - Out.Floats[0] = APFloat(semPPCDoubleDoubleImpl, APInt(128, 2, Buffer)); - return Ret; + return Out.addImpl(A, AA, C, CC, RM); } APFloat::opStatus DoubleAPFloat::add(const DoubleAPFloat &RHS, @@ -4067,6 +4031,140 @@ APFloat::opStatus DoubleAPFloat::subtract(const DoubleAPFloat &RHS, return Ret; } +APFloat::opStatus DoubleAPFloat::multiply(const DoubleAPFloat &RHS, + APFloat::roundingMode RM) { + const auto &LHS = *this; + auto &Out = *this; + /* Interesting observation: For special categories, finding the lowest + common ancestor of the following layered graph gives the correct + return category: + + NaN + / \ + Zero Inf + \ / + Normal + + e.g. NaN * NaN = NaN + Zero * Inf = NaN + Normal * Zero = Zero + Normal * Inf = Inf + */ + if (LHS.getCategory() == fcNaN) { + Out = LHS; + return opOK; + } + if (RHS.getCategory() == fcNaN) { + Out = RHS; + return opOK; + } + if ((LHS.getCategory() == fcZero && RHS.getCategory() == fcInfinity) || + (LHS.getCategory() == fcInfinity && RHS.getCategory() == fcZero)) { + Out.makeNaN(false, false, nullptr); + return opOK; + } + if (LHS.getCategory() == fcZero || LHS.getCategory() == fcInfinity) { + Out = LHS; + return opOK; + } + if (RHS.getCategory() == fcZero || RHS.getCategory() == fcInfinity) { + Out = RHS; + return opOK; + } + assert(LHS.getCategory() == fcNormal && RHS.getCategory() == fcNormal && + "Special cases not handled exhaustively"); + + int Status = opOK; + APFloat A = Floats[0], B = Floats[1], C = RHS.Floats[0], D = RHS.Floats[1]; + // t = a * c + APFloat T = A; + Status |= T.multiply(C, RM); + if (!T.isFiniteNonZero()) { + Floats[0] = T; + Floats[1].makeZero(/* Neg = */ false); + return (opStatus)Status; + } + + // tau = fmsub(a, c, t), that is -fmadd(-a, c, t). + APFloat Tau = A; + T.changeSign(); + Status |= Tau.fusedMultiplyAdd(C, T, RM); + T.changeSign(); + { + // v = a * d + APFloat V = A; + Status |= V.multiply(D, RM); + // w = b * c + APFloat W = B; + Status |= W.multiply(C, RM); + Status |= V.add(W, RM); + // tau += v + w + Status |= Tau.add(V, RM); + } + // u = t + tau + APFloat U = T; + Status |= U.add(Tau, RM); + + Floats[0] = U; + if (!U.isFinite()) { + Floats[1].makeZero(/* Neg = */ false); + } else { + // Floats[1] = (t - u) + tau + Status |= T.subtract(U, RM); + Status |= T.add(Tau, RM); + Floats[1] = T; + } + return (opStatus)Status; +} + +APFloat::opStatus DoubleAPFloat::divide(const DoubleAPFloat &RHS, + APFloat::roundingMode RM) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt()); + auto Ret = + Tmp.divide(APFloat(semPPCDoubleDoubleLegacy, RHS.bitcastToAPInt()), RM); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +APFloat::opStatus DoubleAPFloat::remainder(const DoubleAPFloat &RHS) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt()); + auto Ret = + Tmp.remainder(APFloat(semPPCDoubleDoubleLegacy, RHS.bitcastToAPInt())); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +APFloat::opStatus DoubleAPFloat::mod(const DoubleAPFloat &RHS) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt()); + auto Ret = Tmp.mod(APFloat(semPPCDoubleDoubleLegacy, RHS.bitcastToAPInt())); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +APFloat::opStatus +DoubleAPFloat::fusedMultiplyAdd(const DoubleAPFloat &Multiplicand, + const DoubleAPFloat &Addend, + APFloat::roundingMode RM) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt()); + auto Ret = Tmp.fusedMultiplyAdd( + APFloat(semPPCDoubleDoubleLegacy, Multiplicand.bitcastToAPInt()), + APFloat(semPPCDoubleDoubleLegacy, Addend.bitcastToAPInt()), RM); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +APFloat::opStatus DoubleAPFloat::roundToIntegral(APFloat::roundingMode RM) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt()); + auto Ret = Tmp.roundToIntegral(RM); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + void DoubleAPFloat::changeSign() { Floats[0].changeSign(); Floats[1].changeSign(); @@ -4101,12 +4199,200 @@ bool DoubleAPFloat::isNegative() const { return Floats[0].isNegative(); } void DoubleAPFloat::makeInf(bool Neg) { Floats[0].makeInf(Neg); - Floats[1].makeZero(false); + Floats[1].makeZero(/* Neg = */ false); +} + +void DoubleAPFloat::makeZero(bool Neg) { + Floats[0].makeZero(Neg); + Floats[1].makeZero(/* Neg = */ false); +} + +void DoubleAPFloat::makeLargest(bool Neg) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + Floats[0] = APFloat(semIEEEdouble, APInt(64, 0x7fefffffffffffffull)); + Floats[1] = APFloat(semIEEEdouble, APInt(64, 0x7c8ffffffffffffeull)); + if (Neg) + changeSign(); +} + +void DoubleAPFloat::makeSmallest(bool Neg) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + Floats[0].makeSmallest(Neg); + Floats[1].makeZero(/* Neg = */ false); +} + +void DoubleAPFloat::makeSmallestNormalized(bool Neg) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + Floats[0] = APFloat(semIEEEdouble, APInt(64, 0x0360000000000000ull)); + if (Neg) + Floats[0].changeSign(); + Floats[1].makeZero(/* Neg = */ false); } void DoubleAPFloat::makeNaN(bool SNaN, bool Neg, const APInt *fill) { Floats[0].makeNaN(SNaN, Neg, fill); - Floats[1].makeZero(false); + Floats[1].makeZero(/* Neg = */ false); +} + +APFloat::cmpResult DoubleAPFloat::compare(const DoubleAPFloat &RHS) const { + auto Result = Floats[0].compare(RHS.Floats[0]); + // |Float[0]| > |Float[1]| + if (Result == APFloat::cmpEqual) + return Floats[1].compare(RHS.Floats[1]); + return Result; +} + +bool DoubleAPFloat::bitwiseIsEqual(const DoubleAPFloat &RHS) const { + return Floats[0].bitwiseIsEqual(RHS.Floats[0]) && + Floats[1].bitwiseIsEqual(RHS.Floats[1]); +} + +hash_code hash_value(const DoubleAPFloat &Arg) { + if (Arg.Floats) + return hash_combine(hash_value(Arg.Floats[0]), hash_value(Arg.Floats[1])); + return hash_combine(Arg.Semantics); +} + +APInt DoubleAPFloat::bitcastToAPInt() const { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + uint64_t Data[] = { + Floats[0].bitcastToAPInt().getRawData()[0], + Floats[1].bitcastToAPInt().getRawData()[0], + }; + return APInt(128, 2, Data); +} + +APFloat::opStatus DoubleAPFloat::convertFromString(StringRef S, + roundingMode RM) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy); + auto Ret = Tmp.convertFromString(S, RM); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +APFloat::opStatus DoubleAPFloat::next(bool nextDown) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt()); + auto Ret = Tmp.next(nextDown); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +APFloat::opStatus +DoubleAPFloat::convertToInteger(MutableArrayRef<integerPart> Input, + unsigned int Width, bool IsSigned, + roundingMode RM, bool *IsExact) const { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + return APFloat(semPPCDoubleDoubleLegacy, bitcastToAPInt()) + .convertToInteger(Input, Width, IsSigned, RM, IsExact); +} + +APFloat::opStatus DoubleAPFloat::convertFromAPInt(const APInt &Input, + bool IsSigned, + roundingMode RM) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy); + auto Ret = Tmp.convertFromAPInt(Input, IsSigned, RM); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +APFloat::opStatus +DoubleAPFloat::convertFromSignExtendedInteger(const integerPart *Input, + unsigned int InputSize, + bool IsSigned, roundingMode RM) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy); + auto Ret = Tmp.convertFromSignExtendedInteger(Input, InputSize, IsSigned, RM); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +APFloat::opStatus +DoubleAPFloat::convertFromZeroExtendedInteger(const integerPart *Input, + unsigned int InputSize, + bool IsSigned, roundingMode RM) { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy); + auto Ret = Tmp.convertFromZeroExtendedInteger(Input, InputSize, IsSigned, RM); + *this = DoubleAPFloat(semPPCDoubleDouble, Tmp.bitcastToAPInt()); + return Ret; +} + +unsigned int DoubleAPFloat::convertToHexString(char *DST, + unsigned int HexDigits, + bool UpperCase, + roundingMode RM) const { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + return APFloat(semPPCDoubleDoubleLegacy, bitcastToAPInt()) + .convertToHexString(DST, HexDigits, UpperCase, RM); +} + +bool DoubleAPFloat::isDenormal() const { + return getCategory() == fcNormal && + (Floats[0].isDenormal() || Floats[1].isDenormal() || + // (double)(Hi + Lo) == Hi defines a normal number. + Floats[0].compare(Floats[0] + Floats[1]) != cmpEqual); +} + +bool DoubleAPFloat::isSmallest() const { + if (getCategory() != fcNormal) + return false; + DoubleAPFloat Tmp(*this); + Tmp.makeSmallest(this->isNegative()); + return Tmp.compare(*this) == cmpEqual; +} + +bool DoubleAPFloat::isLargest() const { + if (getCategory() != fcNormal) + return false; + DoubleAPFloat Tmp(*this); + Tmp.makeLargest(this->isNegative()); + return Tmp.compare(*this) == cmpEqual; +} + +bool DoubleAPFloat::isInteger() const { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy); + (void)Tmp.add(Floats[0], rmNearestTiesToEven); + (void)Tmp.add(Floats[1], rmNearestTiesToEven); + return Tmp.isInteger(); +} + +void DoubleAPFloat::toString(SmallVectorImpl<char> &Str, + unsigned FormatPrecision, + unsigned FormatMaxPadding) const { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat(semPPCDoubleDoubleLegacy, bitcastToAPInt()) + .toString(Str, FormatPrecision, FormatMaxPadding); +} + +bool DoubleAPFloat::getExactInverse(APFloat *inv) const { + assert(Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat Tmp(semPPCDoubleDoubleLegacy, bitcastToAPInt()); + if (!inv) + return Tmp.getExactInverse(nullptr); + APFloat Inv(semPPCDoubleDoubleLegacy); + auto Ret = Tmp.getExactInverse(&Inv); + *inv = APFloat(semPPCDoubleDouble, Inv.bitcastToAPInt()); + return Ret; +} + +DoubleAPFloat scalbn(DoubleAPFloat Arg, int Exp, APFloat::roundingMode RM) { + assert(Arg.Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + return DoubleAPFloat(semPPCDoubleDouble, scalbn(Arg.Floats[0], Exp, RM), + scalbn(Arg.Floats[1], Exp, RM)); +} + +DoubleAPFloat frexp(const DoubleAPFloat &Arg, int &Exp, + APFloat::roundingMode RM) { + assert(Arg.Semantics == &semPPCDoubleDouble && "Unexpected Semantics"); + APFloat First = frexp(Arg.Floats[0], Exp, RM); + APFloat Second = Arg.Floats[1]; + if (Arg.getCategory() == APFloat::fcNormal) + Second = scalbn(Second, -Exp, RM); + return DoubleAPFloat(semPPCDoubleDouble, std::move(First), std::move(Second)); } } // End detail namespace @@ -4126,10 +4412,16 @@ APFloat::Storage::Storage(IEEEFloat F, const fltSemantics &Semantics) { } APFloat::opStatus APFloat::convertFromString(StringRef Str, roundingMode RM) { - return getIEEE().convertFromString(Str, RM); + APFLOAT_DISPATCH_ON_SEMANTICS(convertFromString(Str, RM)); } -hash_code hash_value(const APFloat &Arg) { return hash_value(Arg.getIEEE()); } +hash_code hash_value(const APFloat &Arg) { + if (APFloat::usesLayout<detail::IEEEFloat>(Arg.getSemantics())) + return hash_value(Arg.U.IEEE); + if (APFloat::usesLayout<detail::DoubleAPFloat>(Arg.getSemantics())) + return hash_value(Arg.U.Double); + llvm_unreachable("Unexpected semantics"); +} APFloat::APFloat(const fltSemantics &Semantics, StringRef S) : APFloat(Semantics) { @@ -4146,10 +4438,8 @@ APFloat::opStatus APFloat::convert(const fltSemantics &ToSemantics, if (usesLayout<IEEEFloat>(getSemantics()) && usesLayout<DoubleAPFloat>(ToSemantics)) { assert(&ToSemantics == &semPPCDoubleDouble); - auto Ret = U.IEEE.convert(semPPCDoubleDoubleImpl, RM, losesInfo); - *this = APFloat(DoubleAPFloat(semPPCDoubleDouble, std::move(*this), - APFloat(semIEEEdouble)), - ToSemantics); + auto Ret = U.IEEE.convert(semPPCDoubleDoubleLegacy, RM, losesInfo); + *this = APFloat(ToSemantics, U.IEEE.bitcastToAPInt()); return Ret; } if (usesLayout<DoubleAPFloat>(getSemantics()) && @@ -4189,6 +4479,30 @@ void APFloat::print(raw_ostream &OS) const { OS << Buffer << "\n"; } -void APFloat::dump() const { print(dbgs()); } +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void APFloat::dump() const { print(dbgs()); } +#endif + +void APFloat::Profile(FoldingSetNodeID &NID) const { + NID.Add(bitcastToAPInt()); +} + +/* Same as convertToInteger(integerPart*, ...), except the result is returned in + an APSInt, whose initial bit-width and signed-ness are used to determine the + precision of the conversion. + */ +APFloat::opStatus APFloat::convertToInteger(APSInt &result, + roundingMode rounding_mode, + bool *isExact) const { + unsigned bitWidth = result.getBitWidth(); + SmallVector<uint64_t, 4> parts(result.getNumWords()); + opStatus status = convertToInteger(parts, bitWidth, result.isSigned(), + rounding_mode, isExact); + // Keeps the original signed-ness. + result = APInt(bitWidth, parts); + return status; +} } // End llvm namespace + +#undef APFLOAT_DISPATCH_ON_SEMANTICS diff --git a/lib/Support/APInt.cpp b/lib/Support/APInt.cpp index fb8b45166a41..0c7da1dad0d2 100644 --- a/lib/Support/APInt.cpp +++ b/lib/Support/APInt.cpp @@ -63,7 +63,7 @@ inline static unsigned getDigit(char cdigit, uint8_t radix) { r = cdigit - 'a'; if (r <= radix - 11U) return r + 10; - + radix = 10; } @@ -76,14 +76,17 @@ inline static unsigned getDigit(char cdigit, uint8_t radix) { void APInt::initSlowCase(uint64_t val, bool isSigned) { + VAL = 0; pVal = getClearedMemory(getNumWords()); pVal[0] = val; if (isSigned && int64_t(val) < 0) for (unsigned i = 1; i < getNumWords(); ++i) pVal[i] = -1ULL; + clearUnusedBits(); } void APInt::initSlowCase(const APInt& that) { + VAL = 0; pVal = getMemory(getNumWords()); memcpy(pVal, that.pVal, getNumWords() * APINT_WORD_SIZE); } @@ -95,6 +98,7 @@ void APInt::initFromArray(ArrayRef<uint64_t> bigVal) { VAL = bigVal[0]; else { // Get memory, cleared to 0 + VAL = 0; pVal = getClearedMemory(getNumWords()); // Calculate the number of words to copy unsigned words = std::min<unsigned>(bigVal.size(), getNumWords()); @@ -106,17 +110,17 @@ void APInt::initFromArray(ArrayRef<uint64_t> bigVal) { } APInt::APInt(unsigned numBits, ArrayRef<uint64_t> bigVal) - : BitWidth(numBits), VAL(0) { + : BitWidth(numBits) { initFromArray(bigVal); } APInt::APInt(unsigned numBits, unsigned numWords, const uint64_t bigVal[]) - : BitWidth(numBits), VAL(0) { + : BitWidth(numBits) { initFromArray(makeArrayRef(bigVal, numWords)); } APInt::APInt(unsigned numbits, StringRef Str, uint8_t radix) - : BitWidth(numbits), VAL(0) { + : VAL(0), BitWidth(numbits) { assert(BitWidth && "Bitwidth too small"); fromString(numbits, Str, radix); } @@ -153,16 +157,6 @@ APInt& APInt::AssignSlowCase(const APInt& RHS) { return clearUnusedBits(); } -APInt& APInt::operator=(uint64_t RHS) { - if (isSingleWord()) - VAL = RHS; - else { - pVal[0] = RHS; - memset(pVal+1, 0, (getNumWords() - 1) * APINT_WORD_SIZE); - } - return clearUnusedBits(); -} - /// This method 'profiles' an APInt for use with FoldingSet. void APInt::Profile(FoldingSetNodeID& ID) const { ID.AddInteger(BitWidth); @@ -177,76 +171,24 @@ void APInt::Profile(FoldingSetNodeID& ID) const { ID.AddInteger(pVal[i]); } -/// This function adds a single "digit" integer, y, to the multiple -/// "digit" integer array, x[]. x[] is modified to reflect the addition and -/// 1 is returned if there is a carry out, otherwise 0 is returned. -/// @returns the carry of the addition. -static bool add_1(uint64_t dest[], uint64_t x[], unsigned len, uint64_t y) { - for (unsigned i = 0; i < len; ++i) { - dest[i] = y + x[i]; - if (dest[i] < y) - y = 1; // Carry one to next digit. - else { - y = 0; // No need to carry so exit early - break; - } - } - return y; -} - /// @brief Prefix increment operator. Increments the APInt by one. APInt& APInt::operator++() { if (isSingleWord()) ++VAL; else - add_1(pVal, pVal, getNumWords(), 1); + tcIncrement(pVal, getNumWords()); return clearUnusedBits(); } -/// This function subtracts a single "digit" (64-bit word), y, from -/// the multi-digit integer array, x[], propagating the borrowed 1 value until -/// no further borrowing is needed or it runs out of "digits" in x. The result -/// is 1 if "borrowing" exhausted the digits in x, or 0 if x was not exhausted. -/// In other words, if y > x then this function returns 1, otherwise 0. -/// @returns the borrow out of the subtraction -static bool sub_1(uint64_t x[], unsigned len, uint64_t y) { - for (unsigned i = 0; i < len; ++i) { - uint64_t X = x[i]; - x[i] -= y; - if (y > X) - y = 1; // We have to "borrow 1" from next "digit" - else { - y = 0; // No need to borrow - break; // Remaining digits are unchanged so exit early - } - } - return bool(y); -} - /// @brief Prefix decrement operator. Decrements the APInt by one. APInt& APInt::operator--() { if (isSingleWord()) --VAL; else - sub_1(pVal, getNumWords(), 1); + tcDecrement(pVal, getNumWords()); return clearUnusedBits(); } -/// This function adds the integer array x to the integer array Y and -/// places the result in dest. -/// @returns the carry out from the addition -/// @brief General addition of 64-bit integer arrays -static bool add(uint64_t *dest, const uint64_t *x, const uint64_t *y, - unsigned len) { - bool carry = false; - for (unsigned i = 0; i< len; ++i) { - uint64_t limit = std::min(x[i],y[i]); // must come first in case dest == x - dest[i] = x[i] + y[i] + carry; - carry = dest[i] < limit || (carry && dest[i] == limit); - } - return carry; -} - /// Adds the RHS APint to this APInt. /// @returns this, after addition of RHS. /// @brief Addition assignment operator. @@ -254,9 +196,8 @@ APInt& APInt::operator+=(const APInt& RHS) { assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); if (isSingleWord()) VAL += RHS.VAL; - else { - add(pVal, pVal, RHS.pVal, getNumWords()); - } + else + tcAdd(pVal, RHS.pVal, 0, getNumWords()); return clearUnusedBits(); } @@ -264,24 +205,10 @@ APInt& APInt::operator+=(uint64_t RHS) { if (isSingleWord()) VAL += RHS; else - add_1(pVal, pVal, getNumWords(), RHS); + tcAddPart(pVal, RHS, getNumWords()); return clearUnusedBits(); } -/// Subtracts the integer array y from the integer array x -/// @returns returns the borrow out. -/// @brief Generalized subtraction of 64-bit integer arrays. -static bool sub(uint64_t *dest, const uint64_t *x, const uint64_t *y, - unsigned len) { - bool borrow = false; - for (unsigned i = 0; i < len; ++i) { - uint64_t x_tmp = borrow ? x[i] - 1 : x[i]; - borrow = y[i] > x_tmp || (borrow && x[i] == 0); - dest[i] = x_tmp - y[i]; - } - return borrow; -} - /// Subtracts the RHS APInt from this APInt /// @returns this, after subtraction /// @brief Subtraction assignment operator. @@ -290,7 +217,7 @@ APInt& APInt::operator-=(const APInt& RHS) { if (isSingleWord()) VAL -= RHS.VAL; else - sub(pVal, pVal, RHS.pVal, getNumWords()); + tcSubtract(pVal, RHS.pVal, 0, getNumWords()); return clearUnusedBits(); } @@ -298,7 +225,7 @@ APInt& APInt::operator-=(uint64_t RHS) { if (isSingleWord()) VAL -= RHS; else - sub_1(pVal, getNumWords(), RHS); + tcSubtractPart(pVal, RHS, getNumWords()); return clearUnusedBits(); } @@ -339,7 +266,7 @@ static uint64_t mul_1(uint64_t dest[], uint64_t x[], unsigned len, uint64_t y) { /// Multiplies integer array x by integer array y and stores the result into /// the integer array dest. Note that dest's size must be >= xlen + ylen. -/// @brief Generalized multiplicate of integer arrays. +/// @brief Generalized multiplication of integer arrays. static void mul(uint64_t dest[], uint64_t x[], unsigned xlen, uint64_t y[], unsigned ylen) { dest[xlen] = mul_1(dest, x, xlen, y[0]); @@ -412,69 +339,19 @@ APInt& APInt::operator*=(const APInt& RHS) { return *this; } -APInt& APInt::operator&=(const APInt& RHS) { - assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); - if (isSingleWord()) { - VAL &= RHS.VAL; - return *this; - } - unsigned numWords = getNumWords(); - for (unsigned i = 0; i < numWords; ++i) - pVal[i] &= RHS.pVal[i]; +APInt& APInt::AndAssignSlowCase(const APInt& RHS) { + tcAnd(pVal, RHS.pVal, getNumWords()); return *this; } -APInt& APInt::operator|=(const APInt& RHS) { - assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); - if (isSingleWord()) { - VAL |= RHS.VAL; - return *this; - } - unsigned numWords = getNumWords(); - for (unsigned i = 0; i < numWords; ++i) - pVal[i] |= RHS.pVal[i]; +APInt& APInt::OrAssignSlowCase(const APInt& RHS) { + tcOr(pVal, RHS.pVal, getNumWords()); return *this; } -APInt& APInt::operator^=(const APInt& RHS) { - assert(BitWidth == RHS.BitWidth && "Bit widths must be the same"); - if (isSingleWord()) { - VAL ^= RHS.VAL; - this->clearUnusedBits(); - return *this; - } - unsigned numWords = getNumWords(); - for (unsigned i = 0; i < numWords; ++i) - pVal[i] ^= RHS.pVal[i]; - return clearUnusedBits(); -} - -APInt APInt::AndSlowCase(const APInt& RHS) const { - unsigned numWords = getNumWords(); - uint64_t* val = getMemory(numWords); - for (unsigned i = 0; i < numWords; ++i) - val[i] = pVal[i] & RHS.pVal[i]; - return APInt(val, getBitWidth()); -} - -APInt APInt::OrSlowCase(const APInt& RHS) const { - unsigned numWords = getNumWords(); - uint64_t *val = getMemory(numWords); - for (unsigned i = 0; i < numWords; ++i) - val[i] = pVal[i] | RHS.pVal[i]; - return APInt(val, getBitWidth()); -} - -APInt APInt::XorSlowCase(const APInt& RHS) const { - unsigned numWords = getNumWords(); - uint64_t *val = getMemory(numWords); - for (unsigned i = 0; i < numWords; ++i) - val[i] = pVal[i] ^ RHS.pVal[i]; - - APInt Result(val, getBitWidth()); - // 0^0==1 so clear the high bits in case they got set. - Result.clearUnusedBits(); - return Result; +APInt& APInt::XorAssignSlowCase(const APInt& RHS) { + tcXor(pVal, RHS.pVal, getNumWords()); + return *this; } APInt APInt::operator*(const APInt& RHS) const { @@ -511,11 +388,11 @@ bool APInt::ult(const APInt& RHS) const { if (n1 < n2) return true; - // If magnitude of RHS is greather than LHS, return false. + // If magnitude of RHS is greater than LHS, return false. if (n2 < n1) return false; - // If they bot fit in a word, just compare the low order word + // If they both fit in a word, just compare the low order word if (n1 <= APINT_BITS_PER_WORD && n2 <= APINT_BITS_PER_WORD) return pVal[0] < RHS.pVal[0]; @@ -545,7 +422,7 @@ bool APInt::slt(const APInt& RHS) const { if (lhsNeg != rhsNeg) return lhsNeg; - // Otherwise we can just use an unsigned comparision, because even negative + // Otherwise we can just use an unsigned comparison, because even negative // numbers compare correctly this way if both have the same signed-ness. return ult(RHS); } @@ -557,6 +434,33 @@ void APInt::setBit(unsigned bitPosition) { pVal[whichWord(bitPosition)] |= maskBit(bitPosition); } +void APInt::setBitsSlowCase(unsigned loBit, unsigned hiBit) { + unsigned loWord = whichWord(loBit); + unsigned hiWord = whichWord(hiBit); + + // Create an initial mask for the low word with zeros below loBit. + uint64_t loMask = UINT64_MAX << whichBit(loBit); + + // If hiBit is not aligned, we need a high mask. + unsigned hiShiftAmt = whichBit(hiBit); + if (hiShiftAmt != 0) { + // Create a high mask with zeros above hiBit. + uint64_t hiMask = UINT64_MAX >> (APINT_BITS_PER_WORD - hiShiftAmt); + // If loWord and hiWord are equal, then we combine the masks. Otherwise, + // set the bits in hiWord. + if (hiWord == loWord) + loMask &= hiMask; + else + pVal[hiWord] |= hiMask; + } + // Apply the mask to the low word. + pVal[loWord] |= loMask; + + // Fill any words between loWord and hiWord with all ones. + for (unsigned word = loWord + 1; word < hiWord; ++word) + pVal[word] = UINT64_MAX; +} + /// Set the given bit to 0 whose position is given as "bitPosition". /// @brief Set a given bit to 0. void APInt::clearBit(unsigned bitPosition) { @@ -567,6 +471,10 @@ void APInt::clearBit(unsigned bitPosition) { } /// @brief Toggle every bit to its opposite value. +void APInt::flipAllBitsSlowCase() { + tcComplement(pVal, getNumWords()); + clearUnusedBits(); +} /// Toggle a given bit to its opposite value whose position is given /// as "bitPosition". @@ -577,9 +485,104 @@ void APInt::flipBit(unsigned bitPosition) { else setBit(bitPosition); } +void APInt::insertBits(const APInt &subBits, unsigned bitPosition) { + unsigned subBitWidth = subBits.getBitWidth(); + assert(0 < subBitWidth && (subBitWidth + bitPosition) <= BitWidth && + "Illegal bit insertion"); + + // Insertion is a direct copy. + if (subBitWidth == BitWidth) { + *this = subBits; + return; + } + + // Single word result can be done as a direct bitmask. + if (isSingleWord()) { + uint64_t mask = UINT64_MAX >> (APINT_BITS_PER_WORD - subBitWidth); + VAL &= ~(mask << bitPosition); + VAL |= (subBits.VAL << bitPosition); + return; + } + + unsigned loBit = whichBit(bitPosition); + unsigned loWord = whichWord(bitPosition); + unsigned hi1Word = whichWord(bitPosition + subBitWidth - 1); + + // Insertion within a single word can be done as a direct bitmask. + if (loWord == hi1Word) { + uint64_t mask = UINT64_MAX >> (APINT_BITS_PER_WORD - subBitWidth); + pVal[loWord] &= ~(mask << loBit); + pVal[loWord] |= (subBits.VAL << loBit); + return; + } + + // Insert on word boundaries. + if (loBit == 0) { + // Direct copy whole words. + unsigned numWholeSubWords = subBitWidth / APINT_BITS_PER_WORD; + memcpy(pVal + loWord, subBits.getRawData(), + numWholeSubWords * APINT_WORD_SIZE); + + // Mask+insert remaining bits. + unsigned remainingBits = subBitWidth % APINT_BITS_PER_WORD; + if (remainingBits != 0) { + uint64_t mask = UINT64_MAX >> (APINT_BITS_PER_WORD - remainingBits); + pVal[hi1Word] &= ~mask; + pVal[hi1Word] |= subBits.getWord(subBitWidth - 1); + } + return; + } + + // General case - set/clear individual bits in dst based on src. + // TODO - there is scope for optimization here, but at the moment this code + // path is barely used so prefer readability over performance. + for (unsigned i = 0; i != subBitWidth; ++i) { + if (subBits[i]) + setBit(bitPosition + i); + else + clearBit(bitPosition + i); + } +} + +APInt APInt::extractBits(unsigned numBits, unsigned bitPosition) const { + assert(numBits > 0 && "Can't extract zero bits"); + assert(bitPosition < BitWidth && (numBits + bitPosition) <= BitWidth && + "Illegal bit extraction"); + + if (isSingleWord()) + return APInt(numBits, VAL >> bitPosition); + + unsigned loBit = whichBit(bitPosition); + unsigned loWord = whichWord(bitPosition); + unsigned hiWord = whichWord(bitPosition + numBits - 1); + + // Single word result extracting bits from a single word source. + if (loWord == hiWord) + return APInt(numBits, pVal[loWord] >> loBit); + + // Extracting bits that start on a source word boundary can be done + // as a fast memory copy. + if (loBit == 0) + return APInt(numBits, makeArrayRef(pVal + loWord, 1 + hiWord - loWord)); + + // General case - shift + copy source words directly into place. + APInt Result(numBits, 0); + unsigned NumSrcWords = getNumWords(); + unsigned NumDstWords = Result.getNumWords(); + + for (unsigned word = 0; word < NumDstWords; ++word) { + uint64_t w0 = pVal[loWord + word]; + uint64_t w1 = + (loWord + word + 1) < NumSrcWords ? pVal[loWord + word + 1] : 0; + Result.pVal[word] = (w0 >> loBit) | (w1 << (APINT_BITS_PER_WORD - loBit)); + } + + return Result.clearUnusedBits(); +} + unsigned APInt::getBitsNeeded(StringRef str, uint8_t radix) { assert(!str.empty() && "Invalid string length"); - assert((radix == 10 || radix == 8 || radix == 16 || radix == 2 || + assert((radix == 10 || radix == 8 || radix == 16 || radix == 2 || radix == 36) && "Radix should be 2, 8, 10, 16, or 36!"); @@ -604,7 +607,7 @@ unsigned APInt::getBitsNeeded(StringRef str, uint8_t radix) { return slen * 4 + isNegative; // FIXME: base 36 - + // This is grossly inefficient but accurate. We could probably do something // with a computation of roughly slen*64/20 and then adjust by the value of // the first few digits. But, I'm not sure how accurate that could be. @@ -613,7 +616,7 @@ unsigned APInt::getBitsNeeded(StringRef str, uint8_t radix) { // be too large. This avoids the assertion in the constructor. This // calculation doesn't work appropriately for the numbers 0-9, so just use 4 // bits in that case. - unsigned sufficient + unsigned sufficient = radix == 10? (slen == 1 ? 4 : slen * 64/18) : (slen == 1 ? 7 : slen * 16/3); @@ -647,19 +650,20 @@ bool APInt::isSplat(unsigned SplatSizeInBits) const { /// This function returns the high "numBits" bits of this APInt. APInt APInt::getHiBits(unsigned numBits) const { - return APIntOps::lshr(*this, BitWidth - numBits); + return this->lshr(BitWidth - numBits); } /// This function returns the low "numBits" bits of this APInt. APInt APInt::getLoBits(unsigned numBits) const { - return APIntOps::lshr(APIntOps::shl(*this, BitWidth - numBits), - BitWidth - numBits); + APInt Result(getLowBitsSet(BitWidth, numBits)); + Result &= *this; + return Result; } unsigned APInt::countLeadingZerosSlowCase() const { unsigned Count = 0; for (int i = getNumWords()-1; i >= 0; --i) { - integerPart V = pVal[i]; + uint64_t V = pVal[i]; if (V == 0) Count += APINT_BITS_PER_WORD; else { @@ -729,18 +733,6 @@ unsigned APInt::countPopulationSlowCase() const { return Count; } -/// Perform a logical right-shift from Src to Dst, which must be equal or -/// non-overlapping, of Words words, by Shift, which must be less than 64. -static void lshrNear(uint64_t *Dst, uint64_t *Src, unsigned Words, - unsigned Shift) { - uint64_t Carry = 0; - for (int I = Words - 1; I >= 0; --I) { - uint64_t Tmp = Src[I]; - Dst[I] = (Tmp >> Shift) | Carry; - Carry = Tmp << (64 - Shift); - } -} - APInt APInt::byteSwap() const { assert(BitWidth >= 16 && BitWidth % 16 == 0 && "Cannot byteswap!"); if (BitWidth == 16) @@ -761,8 +753,7 @@ APInt APInt::byteSwap() const { for (unsigned I = 0, N = getNumWords(); I != N; ++I) Result.pVal[I] = ByteSwap_64(pVal[N - I - 1]); if (Result.BitWidth != BitWidth) { - lshrNear(Result.pVal, Result.pVal, getNumWords(), - Result.BitWidth - BitWidth); + Result.lshrInPlace(Result.BitWidth - BitWidth); Result.BitWidth = BitWidth; } return Result; @@ -798,14 +789,46 @@ APInt APInt::reverseBits() const { return Reversed; } -APInt llvm::APIntOps::GreatestCommonDivisor(const APInt& API1, - const APInt& API2) { - APInt A = API1, B = API2; - while (!!B) { - APInt T = B; - B = APIntOps::urem(A, B); - A = T; +APInt llvm::APIntOps::GreatestCommonDivisor(APInt A, APInt B) { + // Fast-path a common case. + if (A == B) return A; + + // Corner cases: if either operand is zero, the other is the gcd. + if (!A) return B; + if (!B) return A; + + // Count common powers of 2 and remove all other powers of 2. + unsigned Pow2; + { + unsigned Pow2_A = A.countTrailingZeros(); + unsigned Pow2_B = B.countTrailingZeros(); + if (Pow2_A > Pow2_B) { + A.lshrInPlace(Pow2_A - Pow2_B); + Pow2 = Pow2_B; + } else if (Pow2_B > Pow2_A) { + B.lshrInPlace(Pow2_B - Pow2_A); + Pow2 = Pow2_A; + } else { + Pow2 = Pow2_A; + } + } + + // Both operands are odd multiples of 2^Pow_2: + // + // gcd(a, b) = gcd(|a - b| / 2^i, min(a, b)) + // + // This is a modified version of Stein's algorithm, taking advantage of + // efficient countTrailingZeros(). + while (A != B) { + if (A.ugt(B)) { + A -= B; + A.lshrInPlace(A.countTrailingZeros() - Pow2); + } else { + B -= A; + B.lshrInPlace(B.countTrailingZeros() - Pow2); + } } + return A; } @@ -1117,68 +1140,59 @@ APInt APInt::lshr(const APInt &shiftAmt) const { return lshr((unsigned)shiftAmt.getLimitedValue(BitWidth)); } +/// Perform a logical right-shift from Src to Dst of Words words, by Shift, +/// which must be less than 64. If the source and destination ranges overlap, +/// we require that Src >= Dst (put another way, we require that the overall +/// operation is a right shift on the combined range). +static void lshrWords(APInt::WordType *Dst, APInt::WordType *Src, + unsigned Words, unsigned Shift) { + assert(Shift < APInt::APINT_BITS_PER_WORD); + + if (!Words) + return; + + if (Shift == 0) { + std::memmove(Dst, Src, Words * APInt::APINT_WORD_SIZE); + return; + } + + uint64_t Low = Src[0]; + for (unsigned I = 1; I != Words; ++I) { + uint64_t High = Src[I]; + Dst[I - 1] = + (Low >> Shift) | (High << (APInt::APINT_BITS_PER_WORD - Shift)); + Low = High; + } + Dst[Words - 1] = Low >> Shift; +} + /// Logical right-shift this APInt by shiftAmt. /// @brief Logical right-shift function. -APInt APInt::lshr(unsigned shiftAmt) const { +void APInt::lshrInPlace(unsigned shiftAmt) { if (isSingleWord()) { if (shiftAmt >= BitWidth) - return APInt(BitWidth, 0); + VAL = 0; else - return APInt(BitWidth, this->VAL >> shiftAmt); - } - - // If all the bits were shifted out, the result is 0. This avoids issues - // with shifting by the size of the integer type, which produces undefined - // results. We define these "undefined results" to always be 0. - if (shiftAmt >= BitWidth) - return APInt(BitWidth, 0); - - // If none of the bits are shifted out, the result is *this. This avoids - // issues with shifting by the size of the integer type, which produces - // undefined results in the code below. This is also an optimization. - if (shiftAmt == 0) - return *this; - - // Create some space for the result. - uint64_t * val = new uint64_t[getNumWords()]; - - // If we are shifting less than a word, compute the shift with a simple carry - if (shiftAmt < APINT_BITS_PER_WORD) { - lshrNear(val, pVal, getNumWords(), shiftAmt); - APInt Result(val, BitWidth); - Result.clearUnusedBits(); - return Result; + VAL >>= shiftAmt; + return; } - // Compute some values needed by the remaining shift algorithms - unsigned wordShift = shiftAmt % APINT_BITS_PER_WORD; - unsigned offset = shiftAmt / APINT_BITS_PER_WORD; + // Don't bother performing a no-op shift. + if (!shiftAmt) + return; - // If we are shifting whole words, just move whole words - if (wordShift == 0) { - for (unsigned i = 0; i < getNumWords() - offset; ++i) - val[i] = pVal[i+offset]; - for (unsigned i = getNumWords()-offset; i < getNumWords(); i++) - val[i] = 0; - APInt Result(val, BitWidth); - Result.clearUnusedBits(); - return Result; - } + // Find number of complete words being shifted out and zeroed. + const unsigned Words = getNumWords(); + const unsigned ShiftFullWords = + std::min(shiftAmt / APINT_BITS_PER_WORD, Words); - // Shift the low order words - unsigned breakWord = getNumWords() - offset -1; - for (unsigned i = 0; i < breakWord; ++i) - val[i] = (pVal[i+offset] >> wordShift) | - (pVal[i+offset+1] << (APINT_BITS_PER_WORD - wordShift)); - // Shift the break word. - val[breakWord] = pVal[breakWord+offset] >> wordShift; + // Fill in first Words - ShiftFullWords by shifting. + lshrWords(pVal, pVal + ShiftFullWords, Words - ShiftFullWords, + shiftAmt % APINT_BITS_PER_WORD); - // Remaining words are 0 - for (unsigned i = breakWord+1; i < getNumWords(); ++i) - val[i] = 0; - APInt Result(val, BitWidth); - Result.clearUnusedBits(); - return Result; + // The remaining high words are all zero. + for (unsigned I = Words - ShiftFullWords; I != Words; ++I) + pVal[I] = 0; } /// Left-shift this APInt by shiftAmt. @@ -1244,8 +1258,21 @@ APInt APInt::shlSlowCase(unsigned shiftAmt) const { return Result; } +// Calculate the rotate amount modulo the bit width. +static unsigned rotateModulo(unsigned BitWidth, const APInt &rotateAmt) { + unsigned rotBitWidth = rotateAmt.getBitWidth(); + APInt rot = rotateAmt; + if (rotBitWidth < BitWidth) { + // Extend the rotate APInt, so that the urem doesn't divide by 0. + // e.g. APInt(1, 32) would give APInt(1, 0). + rot = rotateAmt.zext(BitWidth); + } + rot = rot.urem(APInt(rot.getBitWidth(), BitWidth)); + return rot.getLimitedValue(BitWidth); +} + APInt APInt::rotl(const APInt &rotateAmt) const { - return rotl((unsigned)rotateAmt.getLimitedValue(BitWidth)); + return rotl(rotateModulo(BitWidth, rotateAmt)); } APInt APInt::rotl(unsigned rotateAmt) const { @@ -1256,7 +1283,7 @@ APInt APInt::rotl(unsigned rotateAmt) const { } APInt APInt::rotr(const APInt &rotateAmt) const { - return rotr((unsigned)rotateAmt.getLimitedValue(BitWidth)); + return rotr(rotateModulo(BitWidth, rotateAmt)); } APInt APInt::rotr(unsigned rotateAmt) const { @@ -1618,7 +1645,7 @@ static void KnuthDiv(unsigned *u, unsigned *v, unsigned *q, unsigned* r, if (r) { // The value d is expressed by the "shift" value above since we avoided // multiplication by d by using a shift left. So, all we have to do is - // shift right here. In order to mak + // shift right here. if (shift) { unsigned carry = 0; DEBUG(dbgs() << "KnuthDiv: remainder:"); @@ -2014,7 +2041,7 @@ APInt APInt::sdiv_ov(const APInt &RHS, bool &Overflow) const { APInt APInt::smul_ov(const APInt &RHS, bool &Overflow) const { APInt Res = *this * RHS; - + if (*this != 0 && RHS != 0) Overflow = Res.sdiv(RHS) != *this || Res.sdiv(*this) != RHS; else @@ -2041,7 +2068,7 @@ APInt APInt::sshl_ov(const APInt &ShAmt, bool &Overflow) const { Overflow = ShAmt.uge(countLeadingZeros()); else Overflow = ShAmt.uge(countLeadingOnes()); - + return *this << ShAmt; } @@ -2061,7 +2088,7 @@ APInt APInt::ushl_ov(const APInt &ShAmt, bool &Overflow) const { void APInt::fromString(unsigned numbits, StringRef str, uint8_t radix) { // Check our assumptions here assert(!str.empty() && "Invalid string length"); - assert((radix == 10 || radix == 8 || radix == 16 || radix == 2 || + assert((radix == 10 || radix == 8 || radix == 16 || radix == 2 || radix == 36) && "Radix should be 2, 8, 10, 16, or 36!"); @@ -2086,9 +2113,8 @@ void APInt::fromString(unsigned numbits, StringRef str, uint8_t radix) { // Figure out if we can shift instead of multiply unsigned shift = (radix == 16 ? 4 : radix == 8 ? 3 : radix == 2 ? 1 : 0); - // Set up an APInt for the digit to add outside the loop so we don't + // Set up an APInt for the radix multiplier outside the loop so we don't // constantly construct/destruct it. - APInt apdigit(getBitWidth(), 0); APInt apradix(getBitWidth(), radix); // Enter digit traversal loop @@ -2105,11 +2131,7 @@ void APInt::fromString(unsigned numbits, StringRef str, uint8_t radix) { } // Add in the digit we just interpreted - if (apdigit.isSingleWord()) - apdigit.VAL = digit; - else - apdigit.pVal[0] = digit; - *this += apdigit; + *this += digit; } // If its negative, put it in two's complement form if (isNeg) { @@ -2120,7 +2142,7 @@ void APInt::fromString(unsigned numbits, StringRef str, uint8_t radix) { void APInt::toString(SmallVectorImpl<char> &Str, unsigned Radix, bool Signed, bool formatAsCLiteral) const { - assert((Radix == 10 || Radix == 8 || Radix == 16 || Radix == 2 || + assert((Radix == 10 || Radix == 8 || Radix == 16 || Radix == 2 || Radix == 36) && "Radix should be 2, 8, 10, 16, or 36!"); @@ -2208,7 +2230,7 @@ void APInt::toString(SmallVectorImpl<char> &Str, unsigned Radix, // For the 2, 8 and 16 bit cases, we can just shift instead of divide // because the number of bits per digit (1, 3 and 4 respectively) divides - // equaly. We just shift until the value is zero. + // equally. We just shift until the value is zero. if (Radix == 2 || Radix == 8 || Radix == 16) { // Just shift tmp right for each digit width until it becomes zero unsigned ShiftAmt = (Radix == 16 ? 4 : (Radix == 8 ? 3 : 1)); @@ -2245,14 +2267,15 @@ std::string APInt::toString(unsigned Radix = 10, bool Signed = true) const { return S.str(); } - +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void APInt::dump() const { SmallString<40> S, U; this->toStringUnsigned(U); this->toStringSigned(S); dbgs() << "APInt(" << BitWidth << "b, " - << U << "u " << S << "s)"; + << U << "u " << S << "s)\n"; } +#endif void APInt::print(raw_ostream &OS, bool isSigned) const { SmallString<40> S; @@ -2265,83 +2288,60 @@ void APInt::print(raw_ostream &OS, bool isSigned) const { // Assumed by lowHalf, highHalf, partMSB and partLSB. A fairly safe // and unrestricting assumption. -static_assert(integerPartWidth % 2 == 0, "Part width must be divisible by 2!"); +static_assert(APInt::APINT_BITS_PER_WORD % 2 == 0, + "Part width must be divisible by 2!"); /* Some handy functions local to this file. */ -namespace { - /* Returns the integer part with the least significant BITS set. - BITS cannot be zero. */ - static inline integerPart - lowBitMask(unsigned int bits) - { - assert(bits != 0 && bits <= integerPartWidth); +/* Returns the integer part with the least significant BITS set. + BITS cannot be zero. */ +static inline APInt::WordType lowBitMask(unsigned bits) { + assert(bits != 0 && bits <= APInt::APINT_BITS_PER_WORD); - return ~(integerPart) 0 >> (integerPartWidth - bits); - } + return ~(APInt::WordType) 0 >> (APInt::APINT_BITS_PER_WORD - bits); +} - /* Returns the value of the lower half of PART. */ - static inline integerPart - lowHalf(integerPart part) - { - return part & lowBitMask(integerPartWidth / 2); - } +/* Returns the value of the lower half of PART. */ +static inline APInt::WordType lowHalf(APInt::WordType part) { + return part & lowBitMask(APInt::APINT_BITS_PER_WORD / 2); +} - /* Returns the value of the upper half of PART. */ - static inline integerPart - highHalf(integerPart part) - { - return part >> (integerPartWidth / 2); - } +/* Returns the value of the upper half of PART. */ +static inline APInt::WordType highHalf(APInt::WordType part) { + return part >> (APInt::APINT_BITS_PER_WORD / 2); +} - /* Returns the bit number of the most significant set bit of a part. - If the input number has no bits set -1U is returned. */ - static unsigned int - partMSB(integerPart value) - { - return findLastSet(value, ZB_Max); - } +/* Returns the bit number of the most significant set bit of a part. + If the input number has no bits set -1U is returned. */ +static unsigned partMSB(APInt::WordType value) { + return findLastSet(value, ZB_Max); +} - /* Returns the bit number of the least significant set bit of a - part. If the input number has no bits set -1U is returned. */ - static unsigned int - partLSB(integerPart value) - { - return findFirstSet(value, ZB_Max); - } +/* Returns the bit number of the least significant set bit of a + part. If the input number has no bits set -1U is returned. */ +static unsigned partLSB(APInt::WordType value) { + return findFirstSet(value, ZB_Max); } /* Sets the least significant part of a bignum to the input value, and zeroes out higher parts. */ -void -APInt::tcSet(integerPart *dst, integerPart part, unsigned int parts) -{ - unsigned int i; - +void APInt::tcSet(WordType *dst, WordType part, unsigned parts) { assert(parts > 0); dst[0] = part; - for (i = 1; i < parts; i++) + for (unsigned i = 1; i < parts; i++) dst[i] = 0; } /* Assign one bignum to another. */ -void -APInt::tcAssign(integerPart *dst, const integerPart *src, unsigned int parts) -{ - unsigned int i; - - for (i = 0; i < parts; i++) +void APInt::tcAssign(WordType *dst, const WordType *src, unsigned parts) { + for (unsigned i = 0; i < parts; i++) dst[i] = src[i]; } /* Returns true if a bignum is zero, false otherwise. */ -bool -APInt::tcIsZero(const integerPart *src, unsigned int parts) -{ - unsigned int i; - - for (i = 0; i < parts; i++) +bool APInt::tcIsZero(const WordType *src, unsigned parts) { + for (unsigned i = 0; i < parts; i++) if (src[i]) return false; @@ -2349,41 +2349,29 @@ APInt::tcIsZero(const integerPart *src, unsigned int parts) } /* Extract the given bit of a bignum; returns 0 or 1. */ -int -APInt::tcExtractBit(const integerPart *parts, unsigned int bit) -{ - return (parts[bit / integerPartWidth] & - ((integerPart) 1 << bit % integerPartWidth)) != 0; +int APInt::tcExtractBit(const WordType *parts, unsigned bit) { + return (parts[whichWord(bit)] & maskBit(bit)) != 0; } /* Set the given bit of a bignum. */ -void -APInt::tcSetBit(integerPart *parts, unsigned int bit) -{ - parts[bit / integerPartWidth] |= (integerPart) 1 << (bit % integerPartWidth); +void APInt::tcSetBit(WordType *parts, unsigned bit) { + parts[whichWord(bit)] |= maskBit(bit); } /* Clears the given bit of a bignum. */ -void -APInt::tcClearBit(integerPart *parts, unsigned int bit) -{ - parts[bit / integerPartWidth] &= - ~((integerPart) 1 << (bit % integerPartWidth)); +void APInt::tcClearBit(WordType *parts, unsigned bit) { + parts[whichWord(bit)] &= ~maskBit(bit); } /* Returns the bit number of the least significant set bit of a number. If the input number has no bits set -1U is returned. */ -unsigned int -APInt::tcLSB(const integerPart *parts, unsigned int n) -{ - unsigned int i, lsb; - - for (i = 0; i < n; i++) { - if (parts[i] != 0) { - lsb = partLSB(parts[i]); +unsigned APInt::tcLSB(const WordType *parts, unsigned n) { + for (unsigned i = 0; i < n; i++) { + if (parts[i] != 0) { + unsigned lsb = partLSB(parts[i]); - return lsb + i * integerPartWidth; - } + return lsb + i * APINT_BITS_PER_WORD; + } } return -1U; @@ -2391,18 +2379,14 @@ APInt::tcLSB(const integerPart *parts, unsigned int n) /* Returns the bit number of the most significant set bit of a number. If the input number has no bits set -1U is returned. */ -unsigned int -APInt::tcMSB(const integerPart *parts, unsigned int n) -{ - unsigned int msb; - +unsigned APInt::tcMSB(const WordType *parts, unsigned n) { do { --n; if (parts[n] != 0) { - msb = partMSB(parts[n]); + unsigned msb = partMSB(parts[n]); - return msb + n * integerPartWidth; + return msb + n * APINT_BITS_PER_WORD; } } while (n); @@ -2414,31 +2398,28 @@ APInt::tcMSB(const integerPart *parts, unsigned int n) the least significant bit of DST. All high bits above srcBITS in DST are zero-filled. */ void -APInt::tcExtract(integerPart *dst, unsigned int dstCount,const integerPart *src, - unsigned int srcBits, unsigned int srcLSB) -{ - unsigned int firstSrcPart, dstParts, shift, n; - - dstParts = (srcBits + integerPartWidth - 1) / integerPartWidth; +APInt::tcExtract(WordType *dst, unsigned dstCount, const WordType *src, + unsigned srcBits, unsigned srcLSB) { + unsigned dstParts = (srcBits + APINT_BITS_PER_WORD - 1) / APINT_BITS_PER_WORD; assert(dstParts <= dstCount); - firstSrcPart = srcLSB / integerPartWidth; + unsigned firstSrcPart = srcLSB / APINT_BITS_PER_WORD; tcAssign (dst, src + firstSrcPart, dstParts); - shift = srcLSB % integerPartWidth; + unsigned shift = srcLSB % APINT_BITS_PER_WORD; tcShiftRight (dst, dstParts, shift); - /* We now have (dstParts * integerPartWidth - shift) bits from SRC + /* We now have (dstParts * APINT_BITS_PER_WORD - shift) bits from SRC in DST. If this is less that srcBits, append the rest, else clear the high bits. */ - n = dstParts * integerPartWidth - shift; + unsigned n = dstParts * APINT_BITS_PER_WORD - shift; if (n < srcBits) { - integerPart mask = lowBitMask (srcBits - n); + WordType mask = lowBitMask (srcBits - n); dst[dstParts - 1] |= ((src[firstSrcPart + dstParts] & mask) - << n % integerPartWidth); + << n % APINT_BITS_PER_WORD); } else if (n > srcBits) { - if (srcBits % integerPartWidth) - dst[dstParts - 1] &= lowBitMask (srcBits % integerPartWidth); + if (srcBits % APINT_BITS_PER_WORD) + dst[dstParts - 1] &= lowBitMask (srcBits % APINT_BITS_PER_WORD); } /* Clear high parts. */ @@ -2447,18 +2428,12 @@ APInt::tcExtract(integerPart *dst, unsigned int dstCount,const integerPart *src, } /* DST += RHS + C where C is zero or one. Returns the carry flag. */ -integerPart -APInt::tcAdd(integerPart *dst, const integerPart *rhs, - integerPart c, unsigned int parts) -{ - unsigned int i; - +APInt::WordType APInt::tcAdd(WordType *dst, const WordType *rhs, + WordType c, unsigned parts) { assert(c <= 1); - for (i = 0; i < parts; i++) { - integerPart l; - - l = dst[i]; + for (unsigned i = 0; i < parts; i++) { + WordType l = dst[i]; if (c) { dst[i] += rhs[i] + 1; c = (dst[i] <= l); @@ -2471,19 +2446,29 @@ APInt::tcAdd(integerPart *dst, const integerPart *rhs, return c; } -/* DST -= RHS + C where C is zero or one. Returns the carry flag. */ -integerPart -APInt::tcSubtract(integerPart *dst, const integerPart *rhs, - integerPart c, unsigned int parts) -{ - unsigned int i; +/// This function adds a single "word" integer, src, to the multiple +/// "word" integer array, dst[]. dst[] is modified to reflect the addition and +/// 1 is returned if there is a carry out, otherwise 0 is returned. +/// @returns the carry of the addition. +APInt::WordType APInt::tcAddPart(WordType *dst, WordType src, + unsigned parts) { + for (unsigned i = 0; i < parts; ++i) { + dst[i] += src; + if (dst[i] >= src) + return 0; // No need to carry so exit early. + src = 1; // Carry one to next digit. + } - assert(c <= 1); + return 1; +} - for (i = 0; i < parts; i++) { - integerPart l; +/* DST -= RHS + C where C is zero or one. Returns the carry flag. */ +APInt::WordType APInt::tcSubtract(WordType *dst, const WordType *rhs, + WordType c, unsigned parts) { + assert(c <= 1); - l = dst[i]; + for (unsigned i = 0; i < parts; i++) { + WordType l = dst[i]; if (c) { dst[i] -= rhs[i] + 1; c = (dst[i] >= l); @@ -2496,10 +2481,28 @@ APInt::tcSubtract(integerPart *dst, const integerPart *rhs, return c; } +/// This function subtracts a single "word" (64-bit word), src, from +/// the multi-word integer array, dst[], propagating the borrowed 1 value until +/// no further borrowing is needed or it runs out of "words" in dst. The result +/// is 1 if "borrowing" exhausted the digits in dst, or 0 if dst was not +/// exhausted. In other words, if src > dst then this function returns 1, +/// otherwise 0. +/// @returns the borrow out of the subtraction +APInt::WordType APInt::tcSubtractPart(WordType *dst, WordType src, + unsigned parts) { + for (unsigned i = 0; i < parts; ++i) { + WordType Dst = dst[i]; + dst[i] -= src; + if (src <= Dst) + return 0; // No need to borrow so exit early. + src = 1; // We have to "borrow 1" from next "word" + } + + return 1; +} + /* Negate a bignum in-place. */ -void -APInt::tcNegate(integerPart *dst, unsigned int parts) -{ +void APInt::tcNegate(WordType *dst, unsigned parts) { tcComplement(dst, parts); tcIncrement(dst, parts); } @@ -2515,23 +2518,20 @@ APInt::tcNegate(integerPart *dst, unsigned int parts) DSTPARTS parts of the result, and if all of the omitted higher parts were zero return zero, otherwise overflow occurred and return one. */ -int -APInt::tcMultiplyPart(integerPart *dst, const integerPart *src, - integerPart multiplier, integerPart carry, - unsigned int srcParts, unsigned int dstParts, - bool add) -{ - unsigned int i, n; - +int APInt::tcMultiplyPart(WordType *dst, const WordType *src, + WordType multiplier, WordType carry, + unsigned srcParts, unsigned dstParts, + bool add) { /* Otherwise our writes of DST kill our later reads of SRC. */ assert(dst <= src || dst >= src + srcParts); assert(dstParts <= srcParts + 1); /* N loops; minimum of dstParts and srcParts. */ - n = dstParts < srcParts ? dstParts: srcParts; + unsigned n = dstParts < srcParts ? dstParts: srcParts; + unsigned i; for (i = 0; i < n; i++) { - integerPart low, mid, high, srcPart; + WordType low, mid, high, srcPart; /* [ LOW, HIGH ] = MULTIPLIER * SRC[i] + DST[i] + CARRY. @@ -2543,7 +2543,7 @@ APInt::tcMultiplyPart(integerPart *dst, const integerPart *src, srcPart = src[i]; - if (multiplier == 0 || srcPart == 0) { + if (multiplier == 0 || srcPart == 0) { low = carry; high = 0; } else { @@ -2552,14 +2552,14 @@ APInt::tcMultiplyPart(integerPart *dst, const integerPart *src, mid = lowHalf(srcPart) * highHalf(multiplier); high += highHalf(mid); - mid <<= integerPartWidth / 2; + mid <<= APINT_BITS_PER_WORD / 2; if (low + mid < low) high++; low += mid; mid = highHalf(srcPart) * lowHalf(multiplier); high += highHalf(mid); - mid <<= integerPartWidth / 2; + mid <<= APINT_BITS_PER_WORD / 2; if (low + mid < low) high++; low += mid; @@ -2608,19 +2608,14 @@ APInt::tcMultiplyPart(integerPart *dst, const integerPart *src, is filled with the least significant parts of the result. Returns one if overflow occurred, otherwise zero. DST must be disjoint from both operands. */ -int -APInt::tcMultiply(integerPart *dst, const integerPart *lhs, - const integerPart *rhs, unsigned int parts) -{ - unsigned int i; - int overflow; - +int APInt::tcMultiply(WordType *dst, const WordType *lhs, + const WordType *rhs, unsigned parts) { assert(dst != lhs && dst != rhs); - overflow = 0; + int overflow = 0; tcSet(dst, 0, parts); - for (i = 0; i < parts; i++) + for (unsigned i = 0; i < parts; i++) overflow |= tcMultiplyPart(&dst[i], lhs, rhs[i], 0, parts, parts - i, true); @@ -2631,25 +2626,21 @@ APInt::tcMultiply(integerPart *dst, const integerPart *lhs, operands. No overflow occurs. DST must be disjoint from both operands. Returns the number of parts required to hold the result. */ -unsigned int -APInt::tcFullMultiply(integerPart *dst, const integerPart *lhs, - const integerPart *rhs, unsigned int lhsParts, - unsigned int rhsParts) -{ +unsigned APInt::tcFullMultiply(WordType *dst, const WordType *lhs, + const WordType *rhs, unsigned lhsParts, + unsigned rhsParts) { /* Put the narrower number on the LHS for less loops below. */ if (lhsParts > rhsParts) { return tcFullMultiply (dst, rhs, lhs, rhsParts, lhsParts); } else { - unsigned int n; - assert(dst != lhs && dst != rhs); tcSet(dst, 0, rhsParts); - for (n = 0; n < lhsParts; n++) - tcMultiplyPart(&dst[n], rhs, lhs[n], 0, rhsParts, rhsParts + 1, true); + for (unsigned i = 0; i < lhsParts; i++) + tcMultiplyPart(&dst[i], rhs, lhs[i], 0, rhsParts, rhsParts + 1, true); - n = lhsParts + rhsParts; + unsigned n = lhsParts + rhsParts; return n - (dst[n - 1] == 0); } @@ -2665,23 +2656,18 @@ APInt::tcFullMultiply(integerPart *dst, const integerPart *lhs, use by the routine; its contents need not be initialized and are destroyed. LHS, REMAINDER and SCRATCH must be distinct. */ -int -APInt::tcDivide(integerPart *lhs, const integerPart *rhs, - integerPart *remainder, integerPart *srhs, - unsigned int parts) -{ - unsigned int n, shiftCount; - integerPart mask; - +int APInt::tcDivide(WordType *lhs, const WordType *rhs, + WordType *remainder, WordType *srhs, + unsigned parts) { assert(lhs != remainder && lhs != srhs && remainder != srhs); - shiftCount = tcMSB(rhs, parts) + 1; + unsigned shiftCount = tcMSB(rhs, parts) + 1; if (shiftCount == 0) return true; - shiftCount = parts * integerPartWidth - shiftCount; - n = shiftCount / integerPartWidth; - mask = (integerPart) 1 << (shiftCount % integerPartWidth); + shiftCount = parts * APINT_BITS_PER_WORD - shiftCount; + unsigned n = shiftCount / APINT_BITS_PER_WORD; + WordType mask = (WordType) 1 << (shiftCount % APINT_BITS_PER_WORD); tcAssign(srhs, rhs, parts); tcShiftLeft(srhs, parts, shiftCount); @@ -2704,7 +2690,7 @@ APInt::tcDivide(integerPart *lhs, const integerPart *rhs, shiftCount--; tcShiftRight(srhs, parts, 1); if ((mask >>= 1) == 0) { - mask = (integerPart) 1 << (integerPartWidth - 1); + mask = (WordType) 1 << (APINT_BITS_PER_WORD - 1); n--; } } @@ -2714,18 +2700,14 @@ APInt::tcDivide(integerPart *lhs, const integerPart *rhs, /* Shift a bignum left COUNT bits in-place. Shifted in bits are zero. There are no restrictions on COUNT. */ -void -APInt::tcShiftLeft(integerPart *dst, unsigned int parts, unsigned int count) -{ +void APInt::tcShiftLeft(WordType *dst, unsigned parts, unsigned count) { if (count) { - unsigned int jump, shift; - /* Jump is the inter-part jump; shift is is intra-part shift. */ - jump = count / integerPartWidth; - shift = count % integerPartWidth; + unsigned jump = count / APINT_BITS_PER_WORD; + unsigned shift = count % APINT_BITS_PER_WORD; while (parts > jump) { - integerPart part; + WordType part; parts--; @@ -2735,7 +2717,7 @@ APInt::tcShiftLeft(integerPart *dst, unsigned int parts, unsigned int count) if (shift) { part <<= shift; if (parts >= jump + 1) - part |= dst[parts - jump - 1] >> (integerPartWidth - shift); + part |= dst[parts - jump - 1] >> (APINT_BITS_PER_WORD - shift); } dst[parts] = part; @@ -2748,20 +2730,16 @@ APInt::tcShiftLeft(integerPart *dst, unsigned int parts, unsigned int count) /* Shift a bignum right COUNT bits in-place. Shifted in bits are zero. There are no restrictions on COUNT. */ -void -APInt::tcShiftRight(integerPart *dst, unsigned int parts, unsigned int count) -{ +void APInt::tcShiftRight(WordType *dst, unsigned parts, unsigned count) { if (count) { - unsigned int i, jump, shift; - /* Jump is the inter-part jump; shift is is intra-part shift. */ - jump = count / integerPartWidth; - shift = count % integerPartWidth; + unsigned jump = count / APINT_BITS_PER_WORD; + unsigned shift = count % APINT_BITS_PER_WORD; /* Perform the shift. This leaves the most significant COUNT bits of the result at zero. */ - for (i = 0; i < parts; i++) { - integerPart part; + for (unsigned i = 0; i < parts; i++) { + WordType part; if (i + jump >= parts) { part = 0; @@ -2770,7 +2748,7 @@ APInt::tcShiftRight(integerPart *dst, unsigned int parts, unsigned int count) if (shift) { part >>= shift; if (i + jump + 1 < parts) - part |= dst[i + jump + 1] << (integerPartWidth - shift); + part |= dst[i + jump + 1] << (APINT_BITS_PER_WORD - shift); } } @@ -2780,107 +2758,55 @@ APInt::tcShiftRight(integerPart *dst, unsigned int parts, unsigned int count) } /* Bitwise and of two bignums. */ -void -APInt::tcAnd(integerPart *dst, const integerPart *rhs, unsigned int parts) -{ - unsigned int i; - - for (i = 0; i < parts; i++) +void APInt::tcAnd(WordType *dst, const WordType *rhs, unsigned parts) { + for (unsigned i = 0; i < parts; i++) dst[i] &= rhs[i]; } /* Bitwise inclusive or of two bignums. */ -void -APInt::tcOr(integerPart *dst, const integerPart *rhs, unsigned int parts) -{ - unsigned int i; - - for (i = 0; i < parts; i++) +void APInt::tcOr(WordType *dst, const WordType *rhs, unsigned parts) { + for (unsigned i = 0; i < parts; i++) dst[i] |= rhs[i]; } /* Bitwise exclusive or of two bignums. */ -void -APInt::tcXor(integerPart *dst, const integerPart *rhs, unsigned int parts) -{ - unsigned int i; - - for (i = 0; i < parts; i++) +void APInt::tcXor(WordType *dst, const WordType *rhs, unsigned parts) { + for (unsigned i = 0; i < parts; i++) dst[i] ^= rhs[i]; } /* Complement a bignum in-place. */ -void -APInt::tcComplement(integerPart *dst, unsigned int parts) -{ - unsigned int i; - - for (i = 0; i < parts; i++) +void APInt::tcComplement(WordType *dst, unsigned parts) { + for (unsigned i = 0; i < parts; i++) dst[i] = ~dst[i]; } /* Comparison (unsigned) of two bignums. */ -int -APInt::tcCompare(const integerPart *lhs, const integerPart *rhs, - unsigned int parts) -{ +int APInt::tcCompare(const WordType *lhs, const WordType *rhs, + unsigned parts) { while (parts) { - parts--; - if (lhs[parts] == rhs[parts]) - continue; + parts--; + if (lhs[parts] == rhs[parts]) + continue; - if (lhs[parts] > rhs[parts]) - return 1; - else - return -1; - } + return (lhs[parts] > rhs[parts]) ? 1 : -1; + } return 0; } -/* Increment a bignum in-place, return the carry flag. */ -integerPart -APInt::tcIncrement(integerPart *dst, unsigned int parts) -{ - unsigned int i; - - for (i = 0; i < parts; i++) - if (++dst[i] != 0) - break; - - return i == parts; -} - -/* Decrement a bignum in-place, return the borrow flag. */ -integerPart -APInt::tcDecrement(integerPart *dst, unsigned int parts) { - for (unsigned int i = 0; i < parts; i++) { - // If the current word is non-zero, then the decrement has no effect on the - // higher-order words of the integer and no borrow can occur. Exit early. - if (dst[i]--) - return 0; - } - // If every word was zero, then there is a borrow. - return 1; -} - - /* Set the least significant BITS bits of a bignum, clear the rest. */ -void -APInt::tcSetLeastSignificantBits(integerPart *dst, unsigned int parts, - unsigned int bits) -{ - unsigned int i; - - i = 0; - while (bits > integerPartWidth) { - dst[i++] = ~(integerPart) 0; - bits -= integerPartWidth; +void APInt::tcSetLeastSignificantBits(WordType *dst, unsigned parts, + unsigned bits) { + unsigned i = 0; + while (bits > APINT_BITS_PER_WORD) { + dst[i++] = ~(WordType) 0; + bits -= APINT_BITS_PER_WORD; } if (bits) - dst[i++] = ~(integerPart) 0 >> (integerPartWidth - bits); + dst[i++] = ~(WordType) 0 >> (APINT_BITS_PER_WORD - bits); while (i < parts) dst[i++] = 0; diff --git a/lib/Support/ARMAttributeParser.cpp b/lib/Support/ARMAttributeParser.cpp new file mode 100644 index 000000000000..63e800a5b78b --- /dev/null +++ b/lib/Support/ARMAttributeParser.cpp @@ -0,0 +1,708 @@ +//===--- ARMAttributeParser.cpp - ARM Attribute Information Printer -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ARMAttributeParser.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::ARMBuildAttrs; + + +static const EnumEntry<unsigned> TagNames[] = { + { "Tag_File", ARMBuildAttrs::File }, + { "Tag_Section", ARMBuildAttrs::Section }, + { "Tag_Symbol", ARMBuildAttrs::Symbol }, +}; + +namespace llvm { +#define ATTRIBUTE_HANDLER(Attr_) \ + { ARMBuildAttrs::Attr_, &ARMAttributeParser::Attr_ } + +const ARMAttributeParser::DisplayHandler +ARMAttributeParser::DisplayRoutines[] = { + { ARMBuildAttrs::CPU_raw_name, &ARMAttributeParser::StringAttribute, }, + { ARMBuildAttrs::CPU_name, &ARMAttributeParser::StringAttribute }, + ATTRIBUTE_HANDLER(CPU_arch), + ATTRIBUTE_HANDLER(CPU_arch_profile), + ATTRIBUTE_HANDLER(ARM_ISA_use), + ATTRIBUTE_HANDLER(THUMB_ISA_use), + ATTRIBUTE_HANDLER(FP_arch), + ATTRIBUTE_HANDLER(WMMX_arch), + ATTRIBUTE_HANDLER(Advanced_SIMD_arch), + ATTRIBUTE_HANDLER(PCS_config), + ATTRIBUTE_HANDLER(ABI_PCS_R9_use), + ATTRIBUTE_HANDLER(ABI_PCS_RW_data), + ATTRIBUTE_HANDLER(ABI_PCS_RO_data), + ATTRIBUTE_HANDLER(ABI_PCS_GOT_use), + ATTRIBUTE_HANDLER(ABI_PCS_wchar_t), + ATTRIBUTE_HANDLER(ABI_FP_rounding), + ATTRIBUTE_HANDLER(ABI_FP_denormal), + ATTRIBUTE_HANDLER(ABI_FP_exceptions), + ATTRIBUTE_HANDLER(ABI_FP_user_exceptions), + ATTRIBUTE_HANDLER(ABI_FP_number_model), + ATTRIBUTE_HANDLER(ABI_align_needed), + ATTRIBUTE_HANDLER(ABI_align_preserved), + ATTRIBUTE_HANDLER(ABI_enum_size), + ATTRIBUTE_HANDLER(ABI_HardFP_use), + ATTRIBUTE_HANDLER(ABI_VFP_args), + ATTRIBUTE_HANDLER(ABI_WMMX_args), + ATTRIBUTE_HANDLER(ABI_optimization_goals), + ATTRIBUTE_HANDLER(ABI_FP_optimization_goals), + ATTRIBUTE_HANDLER(compatibility), + ATTRIBUTE_HANDLER(CPU_unaligned_access), + ATTRIBUTE_HANDLER(FP_HP_extension), + ATTRIBUTE_HANDLER(ABI_FP_16bit_format), + ATTRIBUTE_HANDLER(MPextension_use), + ATTRIBUTE_HANDLER(DIV_use), + ATTRIBUTE_HANDLER(DSP_extension), + ATTRIBUTE_HANDLER(T2EE_use), + ATTRIBUTE_HANDLER(Virtualization_use), + ATTRIBUTE_HANDLER(nodefaults) +}; + +#undef ATTRIBUTE_HANDLER + +uint64_t ARMAttributeParser::ParseInteger(const uint8_t *Data, + uint32_t &Offset) { + unsigned Length; + uint64_t Value = decodeULEB128(Data + Offset, &Length); + Offset = Offset + Length; + return Value; +} + +StringRef ARMAttributeParser::ParseString(const uint8_t *Data, + uint32_t &Offset) { + const char *String = reinterpret_cast<const char*>(Data + Offset); + size_t Length = std::strlen(String); + Offset = Offset + Length + 1; + return StringRef(String, Length); +} + +void ARMAttributeParser::IntegerAttribute(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + + uint64_t Value = ParseInteger(Data, Offset); + Attributes.insert(std::make_pair(Tag, Value)); + + if (SW) + SW->printNumber(ARMBuildAttrs::AttrTypeAsString(Tag), Value); +} + +void ARMAttributeParser::StringAttribute(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + StringRef TagName = ARMBuildAttrs::AttrTypeAsString(Tag, /*TagPrefix*/false); + StringRef ValueDesc = ParseString(Data, Offset); + + if (SW) { + DictScope AS(*SW, "Attribute"); + SW->printNumber("Tag", Tag); + if (!TagName.empty()) + SW->printString("TagName", TagName); + SW->printString("Value", ValueDesc); + } +} + +void ARMAttributeParser::PrintAttribute(unsigned Tag, unsigned Value, + StringRef ValueDesc) { + Attributes.insert(std::make_pair(Tag, Value)); + + if (SW) { + StringRef TagName = ARMBuildAttrs::AttrTypeAsString(Tag, + /*TagPrefix*/false); + DictScope AS(*SW, "Attribute"); + SW->printNumber("Tag", Tag); + SW->printNumber("Value", Value); + if (!TagName.empty()) + SW->printString("TagName", TagName); + if (!ValueDesc.empty()) + SW->printString("Description", ValueDesc); + } +} + +void ARMAttributeParser::CPU_arch(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Pre-v4", "ARM v4", "ARM v4T", "ARM v5T", "ARM v5TE", "ARM v5TEJ", "ARM v6", + "ARM v6KZ", "ARM v6T2", "ARM v6K", "ARM v7", "ARM v6-M", "ARM v6S-M", + "ARM v7E-M", "ARM v8" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::CPU_arch_profile(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + uint64_t Encoded = ParseInteger(Data, Offset); + + StringRef Profile; + switch (Encoded) { + default: Profile = "Unknown"; break; + case 'A': Profile = "Application"; break; + case 'R': Profile = "Real-time"; break; + case 'M': Profile = "Microcontroller"; break; + case 'S': Profile = "Classic"; break; + case 0: Profile = "None"; break; + } + + PrintAttribute(Tag, Encoded, Profile); +} + +void ARMAttributeParser::ARM_ISA_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "Permitted" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::THUMB_ISA_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "Thumb-1", "Thumb-2" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::FP_arch(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Permitted", "VFPv1", "VFPv2", "VFPv3", "VFPv3-D16", "VFPv4", + "VFPv4-D16", "ARMv8-a FP", "ARMv8-a FP-D16" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::WMMX_arch(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "WMMXv1", "WMMXv2" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::Advanced_SIMD_arch(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Permitted", "NEONv1", "NEONv2+FMA", "ARMv8-a NEON", "ARMv8.1-a NEON" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::PCS_config(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "None", "Bare Platform", "Linux Application", "Linux DSO", "Palm OS 2004", + "Reserved (Palm OS)", "Symbian OS 2004", "Reserved (Symbian OS)" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_PCS_R9_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "v6", "Static Base", "TLS", "Unused" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_PCS_RW_data(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Absolute", "PC-relative", "SB-relative", "Not Permitted" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_PCS_RO_data(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Absolute", "PC-relative", "Not Permitted" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_PCS_GOT_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Permitted", "Direct", "GOT-Indirect" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_PCS_wchar_t(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Permitted", "Unknown", "2-byte", "Unknown", "4-byte" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_FP_rounding(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "IEEE-754", "Runtime" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_FP_denormal(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Unsupported", "IEEE-754", "Sign Only" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_FP_exceptions(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "IEEE-754" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_FP_user_exceptions(AttrType Tag, + const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "IEEE-754" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_FP_number_model(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Permitted", "Finite Only", "RTABI", "IEEE-754" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_align_needed(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Permitted", "8-byte alignment", "4-byte alignment", "Reserved" + }; + + uint64_t Value = ParseInteger(Data, Offset); + + std::string Description; + if (Value < array_lengthof(Strings)) + Description = std::string(Strings[Value]); + else if (Value <= 12) + Description = std::string("8-byte alignment, ") + utostr(1ULL << Value) + + std::string("-byte extended alignment"); + else + Description = "Invalid"; + + PrintAttribute(Tag, Value, Description); +} + +void ARMAttributeParser::ABI_align_preserved(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Required", "8-byte data alignment", "8-byte data and code alignment", + "Reserved" + }; + + uint64_t Value = ParseInteger(Data, Offset); + + std::string Description; + if (Value < array_lengthof(Strings)) + Description = std::string(Strings[Value]); + else if (Value <= 12) + Description = std::string("8-byte stack alignment, ") + + utostr(1ULL << Value) + std::string("-byte data alignment"); + else + Description = "Invalid"; + + PrintAttribute(Tag, Value, Description); +} + +void ARMAttributeParser::ABI_enum_size(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Permitted", "Packed", "Int32", "External Int32" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_HardFP_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Tag_FP_arch", "Single-Precision", "Reserved", "Tag_FP_arch (deprecated)" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_VFP_args(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "AAPCS", "AAPCS VFP", "Custom", "Not Permitted" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_WMMX_args(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "AAPCS", "iWMMX", "Custom" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_optimization_goals(AttrType Tag, + const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", "Debugging", + "Best Debugging" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_FP_optimization_goals(AttrType Tag, + const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", "Accuracy", + "Best Accuracy" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::compatibility(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + uint64_t Integer = ParseInteger(Data, Offset); + StringRef String = ParseString(Data, Offset); + + if (SW) { + DictScope AS(*SW, "Attribute"); + SW->printNumber("Tag", Tag); + SW->startLine() << "Value: " << Integer << ", " << String << '\n'; + SW->printString("TagName", AttrTypeAsString(Tag, /*TagPrefix*/false)); + switch (Integer) { + case 0: + SW->printString("Description", StringRef("No Specific Requirements")); + break; + case 1: + SW->printString("Description", StringRef("AEABI Conformant")); + break; + default: + SW->printString("Description", StringRef("AEABI Non-Conformant")); + break; + } + } +} + +void ARMAttributeParser::CPU_unaligned_access(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "v6-style" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::FP_HP_extension(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "If Available", "Permitted" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::ABI_FP_16bit_format(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "IEEE-754", "VFPv3" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::MPextension_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "Permitted" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::DIV_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "If Available", "Not Permitted", "Permitted" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::DSP_extension(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "Permitted" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::T2EE_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { "Not Permitted", "Permitted" }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::Virtualization_use(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + static const char *const Strings[] = { + "Not Permitted", "TrustZone", "Virtualization Extensions", + "TrustZone + Virtualization Extensions" + }; + + uint64_t Value = ParseInteger(Data, Offset); + StringRef ValueDesc = + (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; + PrintAttribute(Tag, Value, ValueDesc); +} + +void ARMAttributeParser::nodefaults(AttrType Tag, const uint8_t *Data, + uint32_t &Offset) { + uint64_t Value = ParseInteger(Data, Offset); + PrintAttribute(Tag, Value, "Unspecified Tags UNDEFINED"); +} + +void ARMAttributeParser::ParseIndexList(const uint8_t *Data, uint32_t &Offset, + SmallVectorImpl<uint8_t> &IndexList) { + for (;;) { + unsigned Length; + uint64_t Value = decodeULEB128(Data + Offset, &Length); + Offset = Offset + Length; + if (Value == 0) + break; + IndexList.push_back(Value); + } +} + +void ARMAttributeParser::ParseAttributeList(const uint8_t *Data, + uint32_t &Offset, uint32_t Length) { + while (Offset < Length) { + unsigned Length; + uint64_t Tag = decodeULEB128(Data + Offset, &Length); + Offset += Length; + + bool Handled = false; + for (unsigned AHI = 0, AHE = array_lengthof(DisplayRoutines); + AHI != AHE && !Handled; ++AHI) { + if (DisplayRoutines[AHI].Attribute == Tag) { + (this->*DisplayRoutines[AHI].Routine)(ARMBuildAttrs::AttrType(Tag), + Data, Offset); + Handled = true; + break; + } + } + if (!Handled) { + if (Tag < 32) { + errs() << "unhandled AEABI Tag " << Tag + << " (" << ARMBuildAttrs::AttrTypeAsString(Tag) << ")\n"; + continue; + } + + if (Tag % 2 == 0) + IntegerAttribute(ARMBuildAttrs::AttrType(Tag), Data, Offset); + else + StringAttribute(ARMBuildAttrs::AttrType(Tag), Data, Offset); + } + } +} + +void ARMAttributeParser::ParseSubsection(const uint8_t *Data, uint32_t Length) { + uint32_t Offset = sizeof(uint32_t); /* SectionLength */ + + const char *VendorName = reinterpret_cast<const char*>(Data + Offset); + size_t VendorNameLength = std::strlen(VendorName); + Offset = Offset + VendorNameLength + 1; + + if (SW) { + SW->printNumber("SectionLength", Length); + SW->printString("Vendor", StringRef(VendorName, VendorNameLength)); + } + + if (StringRef(VendorName, VendorNameLength).lower() != "aeabi") { + return; + } + + while (Offset < Length) { + /// Tag_File | Tag_Section | Tag_Symbol uleb128:byte-size + uint8_t Tag = Data[Offset]; + Offset = Offset + sizeof(Tag); + + uint32_t Size = + *reinterpret_cast<const support::ulittle32_t*>(Data + Offset); + Offset = Offset + sizeof(Size); + + if (SW) { + SW->printEnum("Tag", Tag, makeArrayRef(TagNames)); + SW->printNumber("Size", Size); + } + + if (Size > Length) { + errs() << "subsection length greater than section length\n"; + return; + } + + StringRef ScopeName, IndexName; + SmallVector<uint8_t, 8> Indicies; + switch (Tag) { + case ARMBuildAttrs::File: + ScopeName = "FileAttributes"; + break; + case ARMBuildAttrs::Section: + ScopeName = "SectionAttributes"; + IndexName = "Sections"; + ParseIndexList(Data, Offset, Indicies); + break; + case ARMBuildAttrs::Symbol: + ScopeName = "SymbolAttributes"; + IndexName = "Symbols"; + ParseIndexList(Data, Offset, Indicies); + break; + default: + errs() << "unrecognised tag: 0x" << utohexstr(Tag) << '\n'; + return; + } + + if (SW) { + DictScope ASS(*SW, ScopeName); + if (!Indicies.empty()) + SW->printList(IndexName, Indicies); + ParseAttributeList(Data, Offset, Length); + } else { + ParseAttributeList(Data, Offset, Length); + } + } +} + +void ARMAttributeParser::Parse(ArrayRef<uint8_t> Section, bool isLittle) { + size_t Offset = 1; + unsigned SectionNumber = 0; + + while (Offset < Section.size()) { + uint32_t SectionLength = isLittle ? + support::endian::read32le(Section.data() + Offset) : + support::endian::read32be(Section.data() + Offset); + + if (SW) { + SW->startLine() << "Section " << ++SectionNumber << " {\n"; + SW->indent(); + } + + ParseSubsection(Section.data() + Offset, SectionLength); + Offset = Offset + SectionLength; + + if (SW) { + SW->unindent(); + SW->startLine() << "}\n"; + } + } +} +} + diff --git a/lib/Support/BinaryStreamError.cpp b/lib/Support/BinaryStreamError.cpp new file mode 100644 index 000000000000..60f5e21f041a --- /dev/null +++ b/lib/Support/BinaryStreamError.cpp @@ -0,0 +1,56 @@ +//===- BinaryStreamError.cpp - Error extensions for streams -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/BinaryStreamError.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +char BinaryStreamError::ID = 0; + +BinaryStreamError::BinaryStreamError(stream_error_code C) + : BinaryStreamError(C, "") {} + +BinaryStreamError::BinaryStreamError(StringRef Context) + : BinaryStreamError(stream_error_code::unspecified, Context) {} + +BinaryStreamError::BinaryStreamError(stream_error_code C, StringRef Context) + : Code(C) { + ErrMsg = "Stream Error: "; + switch (C) { + case stream_error_code::unspecified: + ErrMsg += "An unspecified error has occurred."; + break; + case stream_error_code::stream_too_short: + ErrMsg += "The stream is too short to perform the requested operation."; + break; + case stream_error_code::invalid_array_size: + ErrMsg += "The buffer size is not a multiple of the array element size."; + break; + case stream_error_code::invalid_offset: + ErrMsg += "The specified offset is invalid for the current stream."; + break; + case stream_error_code::filesystem_error: + ErrMsg += "An I/O error occurred on the file system."; + break; + } + + if (!Context.empty()) { + ErrMsg += " "; + ErrMsg += Context; + } +} + +void BinaryStreamError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } + +StringRef BinaryStreamError::getErrorMessage() const { return ErrMsg; } + +std::error_code BinaryStreamError::convertToErrorCode() const { + return inconvertibleErrorCode(); +} diff --git a/lib/Support/BinaryStreamReader.cpp b/lib/Support/BinaryStreamReader.cpp new file mode 100644 index 000000000000..c7a2e0ddb179 --- /dev/null +++ b/lib/Support/BinaryStreamReader.cpp @@ -0,0 +1,95 @@ +//===- BinaryStreamReader.cpp - Reads objects from a binary stream --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/BinaryStreamReader.h" + +#include "llvm/Support/BinaryStreamError.h" +#include "llvm/Support/BinaryStreamRef.h" + +using namespace llvm; + +BinaryStreamReader::BinaryStreamReader(BinaryStreamRef S) + : Stream(S), Offset(0) {} + +Error BinaryStreamReader::readLongestContiguousChunk( + ArrayRef<uint8_t> &Buffer) { + if (auto EC = Stream.readLongestContiguousChunk(Offset, Buffer)) + return EC; + Offset += Buffer.size(); + return Error::success(); +} + +Error BinaryStreamReader::readBytes(ArrayRef<uint8_t> &Buffer, uint32_t Size) { + if (auto EC = Stream.readBytes(Offset, Size, Buffer)) + return EC; + Offset += Size; + return Error::success(); +} + +Error BinaryStreamReader::readCString(StringRef &Dest) { + // TODO: This could be made more efficient by using readLongestContiguousChunk + // and searching for null terminators in the resulting buffer. + + uint32_t Length = 0; + // First compute the length of the string by reading 1 byte at a time. + uint32_t OriginalOffset = getOffset(); + const char *C; + while (true) { + if (auto EC = readObject(C)) + return EC; + if (*C == '\0') + break; + ++Length; + } + // Now go back and request a reference for that many bytes. + uint32_t NewOffset = getOffset(); + setOffset(OriginalOffset); + + if (auto EC = readFixedString(Dest, Length)) + return EC; + + // Now set the offset back to where it was after we calculated the length. + setOffset(NewOffset); + return Error::success(); +} + +Error BinaryStreamReader::readFixedString(StringRef &Dest, uint32_t Length) { + ArrayRef<uint8_t> Bytes; + if (auto EC = readBytes(Bytes, Length)) + return EC; + Dest = StringRef(reinterpret_cast<const char *>(Bytes.begin()), Bytes.size()); + return Error::success(); +} + +Error BinaryStreamReader::readStreamRef(BinaryStreamRef &Ref) { + return readStreamRef(Ref, bytesRemaining()); +} + +Error BinaryStreamReader::readStreamRef(BinaryStreamRef &Ref, uint32_t Length) { + if (bytesRemaining() < Length) + return make_error<BinaryStreamError>(stream_error_code::stream_too_short); + Ref = Stream.slice(Offset, Length); + Offset += Length; + return Error::success(); +} + +Error BinaryStreamReader::skip(uint32_t Amount) { + if (Amount > bytesRemaining()) + return make_error<BinaryStreamError>(stream_error_code::stream_too_short); + Offset += Amount; + return Error::success(); +} + +uint8_t BinaryStreamReader::peek() const { + ArrayRef<uint8_t> Buffer; + auto EC = Stream.readBytes(Offset, 1, Buffer); + assert(!EC && "Cannot peek an empty buffer!"); + llvm::consumeError(std::move(EC)); + return Buffer[0]; +} diff --git a/lib/Support/BinaryStreamWriter.cpp b/lib/Support/BinaryStreamWriter.cpp new file mode 100644 index 000000000000..d60b75642d0f --- /dev/null +++ b/lib/Support/BinaryStreamWriter.cpp @@ -0,0 +1,68 @@ +//===- BinaryStreamWriter.cpp - Writes objects to a BinaryStream ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/BinaryStreamWriter.h" + +#include "llvm/Support/BinaryStreamError.h" +#include "llvm/Support/BinaryStreamReader.h" +#include "llvm/Support/BinaryStreamRef.h" + +using namespace llvm; + +BinaryStreamWriter::BinaryStreamWriter(WritableBinaryStreamRef S) + : Stream(S), Offset(0) {} + +Error BinaryStreamWriter::writeBytes(ArrayRef<uint8_t> Buffer) { + if (auto EC = Stream.writeBytes(Offset, Buffer)) + return EC; + Offset += Buffer.size(); + return Error::success(); +} + +Error BinaryStreamWriter::writeCString(StringRef Str) { + if (auto EC = writeFixedString(Str)) + return EC; + if (auto EC = writeObject('\0')) + return EC; + + return Error::success(); +} + +Error BinaryStreamWriter::writeFixedString(StringRef Str) { + return writeBytes(ArrayRef<uint8_t>(Str.bytes_begin(), Str.bytes_end())); +} + +Error BinaryStreamWriter::writeStreamRef(BinaryStreamRef Ref) { + return writeStreamRef(Ref, Ref.getLength()); +} + +Error BinaryStreamWriter::writeStreamRef(BinaryStreamRef Ref, uint32_t Length) { + BinaryStreamReader SrcReader(Ref.slice(0, Length)); + // This is a bit tricky. If we just call readBytes, we are requiring that it + // return us the entire stream as a contiguous buffer. There is no guarantee + // this can be satisfied by returning a reference straight from the buffer, as + // an implementation may not store all data in a single contiguous buffer. So + // we iterate over each contiguous chunk, writing each one in succession. + while (SrcReader.bytesRemaining() > 0) { + ArrayRef<uint8_t> Chunk; + if (auto EC = SrcReader.readLongestContiguousChunk(Chunk)) + return EC; + if (auto EC = writeBytes(Chunk)) + return EC; + } + return Error::success(); +} + +Error BinaryStreamWriter::padToAlignment(uint32_t Align) { + uint32_t NewOffset = alignTo(Offset, Align); + if (NewOffset > getLength()) + return make_error<BinaryStreamError>(stream_error_code::stream_too_short); + Offset = NewOffset; + return Error::success(); +} diff --git a/lib/Support/BranchProbability.cpp b/lib/Support/BranchProbability.cpp index 1c41659cf8df..44ad110d456a 100644 --- a/lib/Support/BranchProbability.cpp +++ b/lib/Support/BranchProbability.cpp @@ -32,7 +32,9 @@ raw_ostream &BranchProbability::print(raw_ostream &OS) const { Percent); } +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void BranchProbability::dump() const { print(dbgs()) << '\n'; } +#endif BranchProbability::BranchProbability(uint32_t Numerator, uint32_t Denominator) { assert(Denominator > 0 && "Denominator cannot be 0!"); diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index 15418ad2fd06..491614b4bf63 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -9,6 +9,9 @@ elseif( CMAKE_HOST_UNIX ) if( HAVE_LIBDL ) set(system_libs ${system_libs} ${CMAKE_DL_LIBS}) endif() + if( HAVE_BACKTRACE ) + set(system_libs ${system_libs} ${Backtrace_LIBRARIES}) + endif() if(LLVM_ENABLE_TERMINFO) if(HAVE_TERMINFO) set(system_libs ${system_libs} ${TERMINFO_LIBS}) @@ -17,7 +20,7 @@ elseif( CMAKE_HOST_UNIX ) if( LLVM_ENABLE_THREADS AND HAVE_LIBATOMIC ) set(system_libs ${system_libs} atomic) endif() - set(system_libs ${system_libs} ${PTHREAD_LIB}) + set(system_libs ${system_libs} ${LLVM_PTHREAD_LIB}) if ( LLVM_ENABLE_ZLIB AND HAVE_LIBZ ) set(system_libs ${system_libs} z) endif() @@ -31,8 +34,12 @@ add_llvm_library(LLVMSupport APInt.cpp APSInt.cpp ARMBuildAttrs.cpp + ARMAttributeParser.cpp ARMWinEH.cpp Allocator.cpp + BinaryStreamError.cpp + BinaryStreamReader.cpp + BinaryStreamWriter.cpp BlockFrequency.cpp BranchProbability.cpp CachePruning.cpp @@ -46,6 +53,7 @@ add_llvm_library(LLVMSupport CrashRecoveryContext.cpp DataExtractor.cpp Debug.cpp + DebugCounter.cpp DeltaAlgorithm.cpp DAGDeltaAlgorithm.cpp Dwarf.cpp @@ -66,6 +74,7 @@ add_llvm_library(LLVMSupport LineIterator.cpp Locale.cpp LockFileManager.cpp + LowLevelType.cpp ManagedStatic.cpp MathExtras.cpp MemoryBuffer.cpp @@ -134,7 +143,7 @@ add_llvm_library(LLVMSupport Windows ${LLVM_MAIN_INCLUDE_DIR}/llvm/ADT ${LLVM_MAIN_INCLUDE_DIR}/llvm/Support - + ${Backtrace_INCLUDE_DIRS} LINK_LIBS ${system_libs} ) diff --git a/lib/Support/CachePruning.cpp b/lib/Support/CachePruning.cpp index 3831625962ca..aca123639565 100644 --- a/lib/Support/CachePruning.cpp +++ b/lib/Support/CachePruning.cpp @@ -15,6 +15,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -33,8 +34,75 @@ static void writeTimestampFile(StringRef TimestampFile) { raw_fd_ostream Out(TimestampFile.str(), EC, sys::fs::F_None); } +static Expected<std::chrono::seconds> parseDuration(StringRef Duration) { + if (Duration.empty()) + return make_error<StringError>("Duration must not be empty", + inconvertibleErrorCode()); + + StringRef NumStr = Duration.slice(0, Duration.size()-1); + uint64_t Num; + if (NumStr.getAsInteger(0, Num)) + return make_error<StringError>("'" + NumStr + "' not an integer", + inconvertibleErrorCode()); + + switch (Duration.back()) { + case 's': + return std::chrono::seconds(Num); + case 'm': + return std::chrono::minutes(Num); + case 'h': + return std::chrono::hours(Num); + default: + return make_error<StringError>("'" + Duration + + "' must end with one of 's', 'm' or 'h'", + inconvertibleErrorCode()); + } +} + +Expected<CachePruningPolicy> +llvm::parseCachePruningPolicy(StringRef PolicyStr) { + CachePruningPolicy Policy; + std::pair<StringRef, StringRef> P = {"", PolicyStr}; + while (!P.second.empty()) { + P = P.second.split(':'); + + StringRef Key, Value; + std::tie(Key, Value) = P.first.split('='); + if (Key == "prune_interval") { + auto DurationOrErr = parseDuration(Value); + if (!DurationOrErr) + return DurationOrErr.takeError(); + Policy.Interval = *DurationOrErr; + } else if (Key == "prune_after") { + auto DurationOrErr = parseDuration(Value); + if (!DurationOrErr) + return DurationOrErr.takeError(); + Policy.Expiration = *DurationOrErr; + } else if (Key == "cache_size") { + if (Value.back() != '%') + return make_error<StringError>("'" + Value + "' must be a percentage", + inconvertibleErrorCode()); + StringRef SizeStr = Value.slice(0, Value.size() - 1); + uint64_t Size; + if (SizeStr.getAsInteger(0, Size)) + return make_error<StringError>("'" + SizeStr + "' not an integer", + inconvertibleErrorCode()); + if (Size > 100) + return make_error<StringError>("'" + SizeStr + + "' must be between 0 and 100", + inconvertibleErrorCode()); + Policy.PercentageOfAvailableSpace = Size; + } else { + return make_error<StringError>("Unknown key: '" + Key + "'", + inconvertibleErrorCode()); + } + } + + return Policy; +} + /// Prune the cache of files that haven't been accessed in a long time. -bool CachePruning::prune() { +bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) { using namespace std::chrono; if (Path.empty()) @@ -47,7 +115,11 @@ bool CachePruning::prune() { if (!isPathDir) return false; - if (Expiration == seconds(0) && PercentageOfAvailableSpace == 0) { + Policy.PercentageOfAvailableSpace = + std::min(Policy.PercentageOfAvailableSpace, 100u); + + if (Policy.Expiration == seconds(0) && + Policy.PercentageOfAvailableSpace == 0) { DEBUG(dbgs() << "No pruning settings set, exit early\n"); // Nothing will be pruned, early exit return false; @@ -67,12 +139,12 @@ bool CachePruning::prune() { return false; } } else { - if (Interval == seconds(0)) { + if (Policy.Interval == seconds(0)) { // Check whether the time stamp is older than our pruning interval. // If not, do nothing. const auto TimeStampModTime = FileStatus.getLastModificationTime(); auto TimeStampAge = CurrentTime - TimeStampModTime; - if (TimeStampAge <= Interval) { + if (TimeStampAge <= Policy.Interval) { DEBUG(dbgs() << "Timestamp file too recent (" << duration_cast<seconds>(TimeStampAge).count() << "s old), do not prune.\n"); @@ -85,7 +157,7 @@ bool CachePruning::prune() { writeTimestampFile(TimestampFile); } - bool ShouldComputeSize = (PercentageOfAvailableSpace > 0); + bool ShouldComputeSize = (Policy.PercentageOfAvailableSpace > 0); // Keep track of space std::set<std::pair<uint64_t, std::string>> FileSizes; @@ -108,8 +180,11 @@ bool CachePruning::prune() { // Walk all of the files within this directory. for (sys::fs::directory_iterator File(CachePathNative, EC), FileEnd; File != FileEnd && !EC; File.increment(EC)) { - // Do not touch the timestamp. - if (File->path() == TimestampFile) + // Ignore any files not beginning with the string "llvmcache-". This + // includes the timestamp file as well as any files created by the user. + // This acts as a safeguard against data loss if the user specifies the + // wrong directory as their cache directory. + if (!sys::path::filename(File->path()).startswith("llvmcache-")) continue; // Look at this file. If we can't stat it, there's nothing interesting @@ -122,7 +197,7 @@ bool CachePruning::prune() { // If the file hasn't been used recently enough, delete it const auto FileAccessTime = FileStatus.getLastAccessedTime(); auto FileAge = CurrentTime - FileAccessTime; - if (FileAge > Expiration) { + if (FileAge > Policy.Expiration) { DEBUG(dbgs() << "Remove " << File->path() << " (" << duration_cast<seconds>(FileAge).count() << "s old)\n"); sys::fs::remove(File->path()); @@ -143,9 +218,11 @@ bool CachePruning::prune() { auto AvailableSpace = TotalSize + SpaceInfo.free; auto FileAndSize = FileSizes.rbegin(); DEBUG(dbgs() << "Occupancy: " << ((100 * TotalSize) / AvailableSpace) - << "% target is: " << PercentageOfAvailableSpace << "\n"); + << "% target is: " << Policy.PercentageOfAvailableSpace + << "\n"); // Remove the oldest accessed files first, till we get below the threshold - while (((100 * TotalSize) / AvailableSpace) > PercentageOfAvailableSpace && + while (((100 * TotalSize) / AvailableSpace) > + Policy.PercentageOfAvailableSpace && FileAndSize != FileSizes.rend()) { // Remove the file. sys::fs::remove(FileAndSize->second); diff --git a/lib/Support/Chrono.cpp b/lib/Support/Chrono.cpp index cdadbd879979..daccaf1fc103 100644 --- a/lib/Support/Chrono.cpp +++ b/lib/Support/Chrono.cpp @@ -16,6 +16,13 @@ namespace llvm { using namespace sys; +const char llvm::detail::unit<std::ratio<3600>>::value[] = "h"; +const char llvm::detail::unit<std::ratio<60>>::value[] = "m"; +const char llvm::detail::unit<std::ratio<1>>::value[] = "s"; +const char llvm::detail::unit<std::milli>::value[] = "ms"; +const char llvm::detail::unit<std::micro>::value[] = "us"; +const char llvm::detail::unit<std::nano>::value[] = "ns"; + static inline struct tm getStructTM(TimePoint<> TP) { struct tm Storage; std::time_t OurTime = toTimeT(TP); diff --git a/lib/Support/CommandLine.cpp b/lib/Support/CommandLine.cpp index 3889902eea54..f4a9108b8544 100644 --- a/lib/Support/CommandLine.cpp +++ b/lib/Support/CommandLine.cpp @@ -123,7 +123,7 @@ public: void ResetAllOptionOccurrences(); bool ParseCommandLineOptions(int argc, const char *const *argv, - StringRef Overview, bool IgnoreErrors); + StringRef Overview, raw_ostream *Errs = nullptr); void addLiteralOption(Option &Opt, SubCommand *SC, StringRef Name) { if (Opt.hasArgStr()) @@ -1013,9 +1013,9 @@ void cl::ParseEnvironmentOptions(const char *progName, const char *envVar, } bool cl::ParseCommandLineOptions(int argc, const char *const *argv, - StringRef Overview, bool IgnoreErrors) { + StringRef Overview, raw_ostream *Errs) { return GlobalParser->ParseCommandLineOptions(argc, argv, Overview, - IgnoreErrors); + Errs); } void CommandLineParser::ResetAllOptionOccurrences() { @@ -1030,7 +1030,7 @@ void CommandLineParser::ResetAllOptionOccurrences() { bool CommandLineParser::ParseCommandLineOptions(int argc, const char *const *argv, StringRef Overview, - bool IgnoreErrors) { + raw_ostream *Errs) { assert(hasOptions() && "No options specified!"); // Expand response files. @@ -1045,6 +1045,9 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, ProgramName = sys::path::filename(StringRef(argv[0])); ProgramOverview = Overview; + bool IgnoreErrors = Errs; + if (!Errs) + Errs = &errs(); bool ErrorParsing = false; // Check out the positional arguments to collect information about them. @@ -1097,15 +1100,14 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, // not specified after an option that eats all extra arguments, or this // one will never get any! // - if (!IgnoreErrors) { + if (!IgnoreErrors) Opt->error("error - option can never match, because " "another positional argument will match an " "unbounded number of values, and this option" " does not require a value!"); - errs() << ProgramName << ": CommandLine Error: Option '" - << Opt->ArgStr << "' is all messed up!\n"; - errs() << PositionalOpts.size(); - } + *Errs << ProgramName << ": CommandLine Error: Option '" << Opt->ArgStr + << "' is all messed up!\n"; + *Errs << PositionalOpts.size(); ErrorParsing = true; } UnboundedFound |= EatsUnboundedNumberOfValues(Opt); @@ -1200,15 +1202,13 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, if (!Handler) { if (SinkOpts.empty()) { - if (!IgnoreErrors) { - errs() << ProgramName << ": Unknown command line argument '" - << argv[i] << "'. Try: '" << argv[0] << " -help'\n"; - - if (NearestHandler) { - // If we know a near match, report it as well. - errs() << ProgramName << ": Did you mean '-" << NearestHandlerString - << "'?\n"; - } + *Errs << ProgramName << ": Unknown command line argument '" << argv[i] + << "'. Try: '" << argv[0] << " -help'\n"; + + if (NearestHandler) { + // If we know a near match, report it as well. + *Errs << ProgramName << ": Did you mean '-" << NearestHandlerString + << "'?\n"; } ErrorParsing = true; @@ -1231,22 +1231,18 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, // Check and handle positional arguments now... if (NumPositionalRequired > PositionalVals.size()) { - if (!IgnoreErrors) { - errs() << ProgramName + *Errs << ProgramName << ": Not enough positional command line arguments specified!\n" << "Must specify at least " << NumPositionalRequired << " positional argument" << (NumPositionalRequired > 1 ? "s" : "") << ": See: " << argv[0] << " - help\n"; - } ErrorParsing = true; } else if (!HasUnlimitedPositionals && PositionalVals.size() > PositionalOpts.size()) { - if (!IgnoreErrors) { - errs() << ProgramName << ": Too many positional arguments specified!\n" - << "Can specify at most " << PositionalOpts.size() - << " positional arguments: See: " << argv[0] << " -help\n"; - } + *Errs << ProgramName << ": Too many positional arguments specified!\n" + << "Can specify at most " << PositionalOpts.size() + << " positional arguments: See: " << argv[0] << " -help\n"; ErrorParsing = true; } else if (!ConsumeAfterOpt) { @@ -1404,8 +1400,8 @@ static StringRef getValueStr(const Option &O, StringRef DefaultMsg) { // Return the width of the option tag for printing... size_t alias::getOptionWidth() const { return ArgStr.size() + 6; } -static void printHelpStr(StringRef HelpStr, size_t Indent, - size_t FirstLineIndentedBy) { +void Option::printHelpStr(StringRef HelpStr, size_t Indent, + size_t FirstLineIndentedBy) { std::pair<StringRef, StringRef> Split = HelpStr.split('\n'); outs().indent(Indent - FirstLineIndentedBy) << " - " << Split.first << "\n"; while (!Split.second.empty()) { @@ -1448,7 +1444,7 @@ void basic_parser_impl::printOptionInfo(const Option &O, if (!ValName.empty()) outs() << "=<" << getValueStr(O, ValName) << '>'; - printHelpStr(O.HelpStr, GlobalWidth, getOptionWidth(O)); + Option::printHelpStr(O.HelpStr, GlobalWidth, getOptionWidth(O)); } void basic_parser_impl::printOptionName(const Option &O, @@ -1587,7 +1583,7 @@ void generic_parser_base::printOptionInfo(const Option &O, size_t GlobalWidth) const { if (O.hasArgStr()) { outs() << " -" << O.ArgStr; - printHelpStr(O.HelpStr, GlobalWidth, O.ArgStr.size() + 6); + Option::printHelpStr(O.HelpStr, GlobalWidth, O.ArgStr.size() + 6); for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { size_t NumSpaces = GlobalWidth - getOption(i).size() - 8; @@ -1600,7 +1596,7 @@ void generic_parser_base::printOptionInfo(const Option &O, for (unsigned i = 0, e = getNumOptions(); i != e; ++i) { auto Option = getOption(i); outs() << " -" << Option; - printHelpStr(getDescription(i), GlobalWidth, Option.size() + 8); + Option::printHelpStr(getDescription(i), GlobalWidth, Option.size() + 8); } } } @@ -1856,10 +1852,11 @@ public: // Helper function for printOptions(). // It shall return a negative value if A's name should be lexicographically - // ordered before B's name. It returns a value greater equal zero otherwise. + // ordered before B's name. It returns a value greater than zero if B's name + // should be ordered before A's name, and it returns 0 otherwise. static int OptionCategoryCompare(OptionCategory *const *A, OptionCategory *const *B) { - return (*A)->getName() == (*B)->getName(); + return (*A)->getName().compare((*B)->getName()); } // Make sure we inherit our base class's operator=() @@ -2182,5 +2179,6 @@ void cl::ResetAllOptionOccurrences() { void LLVMParseCommandLineOptions(int argc, const char *const *argv, const char *Overview) { - llvm::cl::ParseCommandLineOptions(argc, argv, StringRef(Overview), true); + llvm::cl::ParseCommandLineOptions(argc, argv, StringRef(Overview), + &llvm::nulls()); } diff --git a/lib/Support/Compression.cpp b/lib/Support/Compression.cpp index 5d556462e89c..c279d10f6c61 100644 --- a/lib/Support/Compression.cpp +++ b/lib/Support/Compression.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Config/config.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #if LLVM_ENABLE_ZLIB == 1 && HAVE_ZLIB_H #include <zlib.h> @@ -24,6 +25,10 @@ using namespace llvm; #if LLVM_ENABLE_ZLIB == 1 && HAVE_LIBZ +static Error createError(StringRef Err) { + return make_error<StringError>(Err, inconvertibleErrorCode()); +} + static int encodeZlibCompressionLevel(zlib::CompressionLevel Level) { switch (Level) { case zlib::NoCompression: return 0; @@ -34,53 +39,59 @@ static int encodeZlibCompressionLevel(zlib::CompressionLevel Level) { llvm_unreachable("Invalid zlib::CompressionLevel!"); } -static zlib::Status encodeZlibReturnValue(int ReturnValue) { - switch (ReturnValue) { - case Z_OK: return zlib::StatusOK; - case Z_MEM_ERROR: return zlib::StatusOutOfMemory; - case Z_BUF_ERROR: return zlib::StatusBufferTooShort; - case Z_STREAM_ERROR: return zlib::StatusInvalidArg; - case Z_DATA_ERROR: return zlib::StatusInvalidData; - default: llvm_unreachable("unknown zlib return status!"); +static StringRef convertZlibCodeToString(int Code) { + switch (Code) { + case Z_MEM_ERROR: + return "zlib error: Z_MEM_ERROR"; + case Z_BUF_ERROR: + return "zlib error: Z_BUF_ERROR"; + case Z_STREAM_ERROR: + return "zlib error: Z_STREAM_ERROR"; + case Z_DATA_ERROR: + return "zlib error: Z_DATA_ERROR"; + case Z_OK: + default: + llvm_unreachable("unknown or unexpected zlib status code"); } } bool zlib::isAvailable() { return true; } -zlib::Status zlib::compress(StringRef InputBuffer, - SmallVectorImpl<char> &CompressedBuffer, - CompressionLevel Level) { + +Error zlib::compress(StringRef InputBuffer, + SmallVectorImpl<char> &CompressedBuffer, + CompressionLevel Level) { unsigned long CompressedSize = ::compressBound(InputBuffer.size()); CompressedBuffer.resize(CompressedSize); int CLevel = encodeZlibCompressionLevel(Level); - Status Res = encodeZlibReturnValue(::compress2( - (Bytef *)CompressedBuffer.data(), &CompressedSize, - (const Bytef *)InputBuffer.data(), InputBuffer.size(), CLevel)); + int Res = ::compress2((Bytef *)CompressedBuffer.data(), &CompressedSize, + (const Bytef *)InputBuffer.data(), InputBuffer.size(), + CLevel); // Tell MemorySanitizer that zlib output buffer is fully initialized. // This avoids a false report when running LLVM with uninstrumented ZLib. __msan_unpoison(CompressedBuffer.data(), CompressedSize); CompressedBuffer.resize(CompressedSize); - return Res; + return Res ? createError(convertZlibCodeToString(Res)) : Error::success(); } -zlib::Status zlib::uncompress(StringRef InputBuffer, char *UncompressedBuffer, - size_t &UncompressedSize) { - Status Res = encodeZlibReturnValue( +Error zlib::uncompress(StringRef InputBuffer, char *UncompressedBuffer, + size_t &UncompressedSize) { + int Res = ::uncompress((Bytef *)UncompressedBuffer, (uLongf *)&UncompressedSize, - (const Bytef *)InputBuffer.data(), InputBuffer.size())); + (const Bytef *)InputBuffer.data(), InputBuffer.size()); // Tell MemorySanitizer that zlib output buffer is fully initialized. // This avoids a false report when running LLVM with uninstrumented ZLib. __msan_unpoison(UncompressedBuffer, UncompressedSize); - return Res; + return Res ? createError(convertZlibCodeToString(Res)) : Error::success(); } -zlib::Status zlib::uncompress(StringRef InputBuffer, - SmallVectorImpl<char> &UncompressedBuffer, - size_t UncompressedSize) { +Error zlib::uncompress(StringRef InputBuffer, + SmallVectorImpl<char> &UncompressedBuffer, + size_t UncompressedSize) { UncompressedBuffer.resize(UncompressedSize); - Status Res = + Error E = uncompress(InputBuffer, UncompressedBuffer.data(), UncompressedSize); UncompressedBuffer.resize(UncompressedSize); - return Res; + return E; } uint32_t zlib::crc32(StringRef Buffer) { @@ -89,19 +100,19 @@ uint32_t zlib::crc32(StringRef Buffer) { #else bool zlib::isAvailable() { return false; } -zlib::Status zlib::compress(StringRef InputBuffer, - SmallVectorImpl<char> &CompressedBuffer, - CompressionLevel Level) { - return zlib::StatusUnsupported; +Error zlib::compress(StringRef InputBuffer, + SmallVectorImpl<char> &CompressedBuffer, + CompressionLevel Level) { + llvm_unreachable("zlib::compress is unavailable"); } -zlib::Status zlib::uncompress(StringRef InputBuffer, char *UncompressedBuffer, - size_t &UncompressedSize) { - return zlib::StatusUnsupported; +Error zlib::uncompress(StringRef InputBuffer, char *UncompressedBuffer, + size_t &UncompressedSize) { + llvm_unreachable("zlib::uncompress is unavailable"); } -zlib::Status zlib::uncompress(StringRef InputBuffer, - SmallVectorImpl<char> &UncompressedBuffer, - size_t UncompressedSize) { - return zlib::StatusUnsupported; +Error zlib::uncompress(StringRef InputBuffer, + SmallVectorImpl<char> &UncompressedBuffer, + size_t UncompressedSize) { + llvm_unreachable("zlib::uncompress is unavailable"); } uint32_t zlib::crc32(StringRef Buffer) { llvm_unreachable("zlib::crc32 is unavailable"); diff --git a/lib/Support/DebugCounter.cpp b/lib/Support/DebugCounter.cpp new file mode 100644 index 000000000000..29dae8a20f00 --- /dev/null +++ b/lib/Support/DebugCounter.cpp @@ -0,0 +1,108 @@ +#include "llvm/Support/DebugCounter.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Options.h" + +using namespace llvm; + +// This class overrides the default list implementation of printing so we +// can pretty print the list of debug counter options. This type of +// dynamic option is pretty rare (basically this and pass lists). +class DebugCounterList : public cl::list<std::string, DebugCounter> { +private: + using Base = cl::list<std::string, DebugCounter>; + +public: + template <class... Mods> + explicit DebugCounterList(Mods &&... Ms) : Base(std::forward<Mods>(Ms)...) {} + +private: + void printOptionInfo(size_t GlobalWidth) const override { + // This is a variant of from generic_parser_base::printOptionInfo. Sadly, + // it's not easy to make it more usable. We could get it to print these as + // options if we were a cl::opt and registered them, but lists don't have + // options, nor does the parser for std::string. The other mechanisms for + // options are global and would pollute the global namespace with our + // counters. Rather than go that route, we have just overridden the + // printing, which only a few things call anyway. + outs() << " -" << ArgStr; + // All of the other options in CommandLine.cpp use ArgStr.size() + 6 for + // width, so we do the same. + Option::printHelpStr(HelpStr, GlobalWidth, ArgStr.size() + 6); + const auto &CounterInstance = DebugCounter::instance(); + for (auto Name : CounterInstance) { + const auto Info = + CounterInstance.getCounterInfo(CounterInstance.getCounterId(Name)); + size_t NumSpaces = GlobalWidth - Info.first.size() - 8; + outs() << " =" << Info.first; + outs().indent(NumSpaces) << " - " << Info.second << '\n'; + } + } +}; + +// Create our command line option. +static DebugCounterList DebugCounterOption( + "debug-counter", + cl::desc("Comma separated list of debug counter skip and count"), + cl::CommaSeparated, cl::ZeroOrMore, cl::location(DebugCounter::instance())); + +static ManagedStatic<DebugCounter> DC; + +DebugCounter &DebugCounter::instance() { return *DC; } + +// This is called by the command line parser when it sees a value for the +// debug-counter option defined above. +void DebugCounter::push_back(const std::string &Val) { + if (Val.empty()) + return; + // The strings should come in as counter=value + auto CounterPair = StringRef(Val).split('='); + if (CounterPair.second.empty()) { + errs() << "DebugCounter Error: " << Val << " does not have an = in it\n"; + return; + } + // Now we have counter=value. + // First, process value. + long CounterVal; + if (CounterPair.second.getAsInteger(0, CounterVal)) { + errs() << "DebugCounter Error: " << CounterPair.second + << " is not a number\n"; + return; + } + // Now we need to see if this is the skip or the count, remove the suffix, and + // add it to the counter values. + if (CounterPair.first.endswith("-skip")) { + auto CounterName = CounterPair.first.drop_back(5); + unsigned CounterID = RegisteredCounters.idFor(CounterName); + if (!CounterID) { + errs() << "DebugCounter Error: " << CounterName + << " is not a registered counter\n"; + return; + } + + auto Res = Counters.insert({CounterID, {0, -1}}); + Res.first->second.first = CounterVal; + } else if (CounterPair.first.endswith("-count")) { + auto CounterName = CounterPair.first.drop_back(6); + unsigned CounterID = RegisteredCounters.idFor(CounterName); + if (!CounterID) { + errs() << "DebugCounter Error: " << CounterName + << " is not a registered counter\n"; + return; + } + + auto Res = Counters.insert({CounterID, {0, -1}}); + Res.first->second.second = CounterVal; + } else { + errs() << "DebugCounter Error: " << CounterPair.first + << " does not end with -skip or -count\n"; + } +} + +void DebugCounter::print(raw_ostream &OS) { + OS << "Counters and values:\n"; + for (const auto &KV : Counters) + OS << left_justify(RegisteredCounters[KV.first], 32) << ": {" + << KV.second.first << "," << KV.second.second << "}\n"; +} diff --git a/lib/Support/Dwarf.cpp b/lib/Support/Dwarf.cpp index 8950e8c919a4..f13da62e4a87 100644 --- a/lib/Support/Dwarf.cpp +++ b/lib/Support/Dwarf.cpp @@ -304,6 +304,17 @@ StringRef llvm::dwarf::ApplePropertyString(unsigned Prop) { } } +StringRef llvm::dwarf::UnitTypeString(unsigned UT) { + switch (UT) { + default: + return StringRef(); +#define HANDLE_DW_UT(ID, NAME) \ + case DW_UT_##NAME: \ + return "DW_UT_" #NAME; +#include "llvm/Support/Dwarf.def" + } +} + StringRef llvm::dwarf::AtomTypeString(unsigned AT) { switch (AT) { case dwarf::DW_ATOM_null: diff --git a/lib/Support/DynamicLibrary.cpp b/lib/Support/DynamicLibrary.cpp index ced21e46afe8..92ce6185306a 100644 --- a/lib/Support/DynamicLibrary.cpp +++ b/lib/Support/DynamicLibrary.cpp @@ -9,8 +9,6 @@ // // This file implements the operating system DynamicLibrary concept. // -// FIXME: This file leaks ExplicitSymbols and OpenedHandles! -// //===----------------------------------------------------------------------===// #include "llvm/Support/DynamicLibrary.h" @@ -51,7 +49,7 @@ using namespace llvm::sys; //=== independent code. //===----------------------------------------------------------------------===// -static DenseSet<void *> *OpenedHandles = nullptr; +static llvm::ManagedStatic<DenseSet<void *> > OpenedHandles; DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *filename, std::string *errMsg) { @@ -70,9 +68,6 @@ DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *filename, handle = RTLD_DEFAULT; #endif - if (!OpenedHandles) - OpenedHandles = new DenseSet<void *>(); - // If we've already loaded this library, dlclose() the handle in order to // keep the internal refcount at +1. if (!OpenedHandles->insert(handle).second) @@ -81,6 +76,18 @@ DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *filename, return DynamicLibrary(handle); } +DynamicLibrary DynamicLibrary::addPermanentLibrary(void *handle, + std::string *errMsg) { + SmartScopedLock<true> lock(*SymbolsMutex); + // If we've already loaded this library, tell the caller. + if (!OpenedHandles->insert(handle).second) { + if (errMsg) *errMsg = "Library already loaded"; + return DynamicLibrary(); + } + + return DynamicLibrary(handle); +} + void *DynamicLibrary::getAddressOfSymbol(const char *symbolName) { if (!isValid()) return nullptr; @@ -121,7 +128,7 @@ void* DynamicLibrary::SearchForAddressOfSymbol(const char *symbolName) { #if defined(HAVE_DLFCN_H) && defined(HAVE_DLOPEN) // Now search the libraries. - if (OpenedHandles) { + if (OpenedHandles.isConstructed()) { for (DenseSet<void *>::iterator I = OpenedHandles->begin(), E = OpenedHandles->end(); I != E; ++I) { //lt_ptr ptr = lt_dlsym(*I, symbolName); diff --git a/lib/Support/FileOutputBuffer.cpp b/lib/Support/FileOutputBuffer.cpp index 57e5a8d7871c..731740d012d9 100644 --- a/lib/Support/FileOutputBuffer.cpp +++ b/lib/Support/FileOutputBuffer.cpp @@ -57,6 +57,8 @@ FileOutputBuffer::create(StringRef FilePath, size_t Size, unsigned Flags) { // FIXME: In posix, you use the access() call to check this. } break; + case sys::fs::file_type::directory_file: + return errc::is_a_directory; default: if (EC) return EC; diff --git a/lib/Support/Host.cpp b/lib/Support/Host.cpp index d1b40412a6fc..970ecfd7df90 100644 --- a/lib/Support/Host.cpp +++ b/lib/Support/Host.cpp @@ -52,25 +52,218 @@ using namespace llvm; -#if defined(__linux__) -static ssize_t LLVM_ATTRIBUTE_UNUSED readCpuInfo(void *Buf, size_t Size) { - // Note: We cannot mmap /proc/cpuinfo here and then process the resulting - // memory buffer because the 'file' has 0 size (it can be read from only - // as a stream). - - int FD; - std::error_code EC = sys::fs::openFileForRead("/proc/cpuinfo", FD); - if (EC) { - DEBUG(dbgs() << "Unable to open /proc/cpuinfo: " << EC.message() << "\n"); - return -1; +static std::unique_ptr<llvm::MemoryBuffer> + LLVM_ATTRIBUTE_UNUSED getProcCpuinfoContent() { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text = + llvm::MemoryBuffer::getFileAsStream("/proc/cpuinfo"); + if (std::error_code EC = Text.getError()) { + llvm::errs() << "Can't read " + << "/proc/cpuinfo: " << EC.message() << "\n"; + return nullptr; } - int Ret = read(FD, Buf, Size); - int CloseStatus = close(FD); - if (CloseStatus) - return -1; - return Ret; + return std::move(*Text); +} + +StringRef sys::detail::getHostCPUNameForPowerPC( + const StringRef &ProcCpuinfoContent) { + // Access to the Processor Version Register (PVR) on PowerPC is privileged, + // and so we must use an operating-system interface to determine the current + // processor type. On Linux, this is exposed through the /proc/cpuinfo file. + const char *generic = "generic"; + + // The cpu line is second (after the 'processor: 0' line), so if this + // buffer is too small then something has changed (or is wrong). + StringRef::const_iterator CPUInfoStart = ProcCpuinfoContent.begin(); + StringRef::const_iterator CPUInfoEnd = ProcCpuinfoContent.end(); + + StringRef::const_iterator CIP = CPUInfoStart; + + StringRef::const_iterator CPUStart = 0; + size_t CPULen = 0; + + // We need to find the first line which starts with cpu, spaces, and a colon. + // After the colon, there may be some additional spaces and then the cpu type. + while (CIP < CPUInfoEnd && CPUStart == 0) { + if (CIP < CPUInfoEnd && *CIP == '\n') + ++CIP; + + if (CIP < CPUInfoEnd && *CIP == 'c') { + ++CIP; + if (CIP < CPUInfoEnd && *CIP == 'p') { + ++CIP; + if (CIP < CPUInfoEnd && *CIP == 'u') { + ++CIP; + while (CIP < CPUInfoEnd && (*CIP == ' ' || *CIP == '\t')) + ++CIP; + + if (CIP < CPUInfoEnd && *CIP == ':') { + ++CIP; + while (CIP < CPUInfoEnd && (*CIP == ' ' || *CIP == '\t')) + ++CIP; + + if (CIP < CPUInfoEnd) { + CPUStart = CIP; + while (CIP < CPUInfoEnd && (*CIP != ' ' && *CIP != '\t' && + *CIP != ',' && *CIP != '\n')) + ++CIP; + CPULen = CIP - CPUStart; + } + } + } + } + } + + if (CPUStart == 0) + while (CIP < CPUInfoEnd && *CIP != '\n') + ++CIP; + } + + if (CPUStart == 0) + return generic; + + return StringSwitch<const char *>(StringRef(CPUStart, CPULen)) + .Case("604e", "604e") + .Case("604", "604") + .Case("7400", "7400") + .Case("7410", "7400") + .Case("7447", "7400") + .Case("7455", "7450") + .Case("G4", "g4") + .Case("POWER4", "970") + .Case("PPC970FX", "970") + .Case("PPC970MP", "970") + .Case("G5", "g5") + .Case("POWER5", "g5") + .Case("A2", "a2") + .Case("POWER6", "pwr6") + .Case("POWER7", "pwr7") + .Case("POWER8", "pwr8") + .Case("POWER8E", "pwr8") + .Case("POWER8NVL", "pwr8") + .Case("POWER9", "pwr9") + .Default(generic); +} + +StringRef sys::detail::getHostCPUNameForARM( + const StringRef &ProcCpuinfoContent) { + // The cpuid register on arm is not accessible from user space. On Linux, + // it is exposed through the /proc/cpuinfo file. + + // Read 32 lines from /proc/cpuinfo, which should contain the CPU part line + // in all cases. + SmallVector<StringRef, 32> Lines; + ProcCpuinfoContent.split(Lines, "\n"); + + // Look for the CPU implementer line. + StringRef Implementer; + StringRef Hardware; + for (unsigned I = 0, E = Lines.size(); I != E; ++I) { + if (Lines[I].startswith("CPU implementer")) + Implementer = Lines[I].substr(15).ltrim("\t :"); + if (Lines[I].startswith("Hardware")) + Hardware = Lines[I].substr(8).ltrim("\t :"); + } + + if (Implementer == "0x41") { // ARM Ltd. + // MSM8992/8994 may give cpu part for the core that the kernel is running on, + // which is undeterministic and wrong. Always return cortex-a53 for these SoC. + if (Hardware.endswith("MSM8994") || Hardware.endswith("MSM8996")) + return "cortex-a53"; + + + // Look for the CPU part line. + for (unsigned I = 0, E = Lines.size(); I != E; ++I) + if (Lines[I].startswith("CPU part")) + // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The + // values correspond to the "Part number" in the CP15/c0 register. The + // contents are specified in the various processor manuals. + return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :")) + .Case("0x926", "arm926ej-s") + .Case("0xb02", "mpcore") + .Case("0xb36", "arm1136j-s") + .Case("0xb56", "arm1156t2-s") + .Case("0xb76", "arm1176jz-s") + .Case("0xc08", "cortex-a8") + .Case("0xc09", "cortex-a9") + .Case("0xc0f", "cortex-a15") + .Case("0xc20", "cortex-m0") + .Case("0xc23", "cortex-m3") + .Case("0xc24", "cortex-m4") + .Case("0xd04", "cortex-a35") + .Case("0xd03", "cortex-a53") + .Case("0xd07", "cortex-a57") + .Case("0xd08", "cortex-a72") + .Case("0xd09", "cortex-a73") + .Default("generic"); + } + + if (Implementer == "0x51") // Qualcomm Technologies, Inc. + // Look for the CPU part line. + for (unsigned I = 0, E = Lines.size(); I != E; ++I) + if (Lines[I].startswith("CPU part")) + // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The + // values correspond to the "Part number" in the CP15/c0 register. The + // contents are specified in the various processor manuals. + return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :")) + .Case("0x06f", "krait") // APQ8064 + .Case("0x201", "kryo") + .Case("0x205", "kryo") + .Default("generic"); + + return "generic"; +} + +StringRef sys::detail::getHostCPUNameForS390x( + const StringRef &ProcCpuinfoContent) { + // STIDP is a privileged operation, so use /proc/cpuinfo instead. + + // The "processor 0:" line comes after a fair amount of other information, + // including a cache breakdown, but this should be plenty. + SmallVector<StringRef, 32> Lines; + ProcCpuinfoContent.split(Lines, "\n"); + + // Look for the CPU features. + SmallVector<StringRef, 32> CPUFeatures; + for (unsigned I = 0, E = Lines.size(); I != E; ++I) + if (Lines[I].startswith("features")) { + size_t Pos = Lines[I].find(":"); + if (Pos != StringRef::npos) { + Lines[I].drop_front(Pos + 1).split(CPUFeatures, ' '); + break; + } + } + + // We need to check for the presence of vector support independently of + // the machine type, since we may only use the vector register set when + // supported by the kernel (and hypervisor). + bool HaveVectorSupport = false; + for (unsigned I = 0, E = CPUFeatures.size(); I != E; ++I) { + if (CPUFeatures[I] == "vx") + HaveVectorSupport = true; + } + + // Now check the processor machine type. + for (unsigned I = 0, E = Lines.size(); I != E; ++I) { + if (Lines[I].startswith("processor ")) { + size_t Pos = Lines[I].find("machine = "); + if (Pos != StringRef::npos) { + Pos += sizeof("machine = ") - 1; + unsigned int Id; + if (!Lines[I].drop_front(Pos).getAsInteger(10, Id)) { + if (Id >= 2964 && HaveVectorSupport) + return "z13"; + if (Id >= 2827) + return "zEC12"; + if (Id >= 2817) + return "z196"; + } + } + break; + } + } + + return "generic"; } -#endif #if defined(__i386__) || defined(_M_IX86) || \ defined(__x86_64__) || defined(_M_X64) @@ -1020,201 +1213,21 @@ StringRef sys::getHostCPUName() { } #elif defined(__linux__) && (defined(__ppc__) || defined(__powerpc__)) StringRef sys::getHostCPUName() { - // Access to the Processor Version Register (PVR) on PowerPC is privileged, - // and so we must use an operating-system interface to determine the current - // processor type. On Linux, this is exposed through the /proc/cpuinfo file. - const char *generic = "generic"; - - // The cpu line is second (after the 'processor: 0' line), so if this - // buffer is too small then something has changed (or is wrong). - char buffer[1024]; - ssize_t CPUInfoSize = readCpuInfo(buffer, sizeof(buffer)); - if (CPUInfoSize == -1) - return generic; - - const char *CPUInfoStart = buffer; - const char *CPUInfoEnd = buffer + CPUInfoSize; - - const char *CIP = CPUInfoStart; - - const char *CPUStart = 0; - size_t CPULen = 0; - - // We need to find the first line which starts with cpu, spaces, and a colon. - // After the colon, there may be some additional spaces and then the cpu type. - while (CIP < CPUInfoEnd && CPUStart == 0) { - if (CIP < CPUInfoEnd && *CIP == '\n') - ++CIP; - - if (CIP < CPUInfoEnd && *CIP == 'c') { - ++CIP; - if (CIP < CPUInfoEnd && *CIP == 'p') { - ++CIP; - if (CIP < CPUInfoEnd && *CIP == 'u') { - ++CIP; - while (CIP < CPUInfoEnd && (*CIP == ' ' || *CIP == '\t')) - ++CIP; - - if (CIP < CPUInfoEnd && *CIP == ':') { - ++CIP; - while (CIP < CPUInfoEnd && (*CIP == ' ' || *CIP == '\t')) - ++CIP; - - if (CIP < CPUInfoEnd) { - CPUStart = CIP; - while (CIP < CPUInfoEnd && (*CIP != ' ' && *CIP != '\t' && - *CIP != ',' && *CIP != '\n')) - ++CIP; - CPULen = CIP - CPUStart; - } - } - } - } - } - - if (CPUStart == 0) - while (CIP < CPUInfoEnd && *CIP != '\n') - ++CIP; - } - - if (CPUStart == 0) - return generic; - - return StringSwitch<const char *>(StringRef(CPUStart, CPULen)) - .Case("604e", "604e") - .Case("604", "604") - .Case("7400", "7400") - .Case("7410", "7400") - .Case("7447", "7400") - .Case("7455", "7450") - .Case("G4", "g4") - .Case("POWER4", "970") - .Case("PPC970FX", "970") - .Case("PPC970MP", "970") - .Case("G5", "g5") - .Case("POWER5", "g5") - .Case("A2", "a2") - .Case("POWER6", "pwr6") - .Case("POWER7", "pwr7") - .Case("POWER8", "pwr8") - .Case("POWER8E", "pwr8") - .Case("POWER8NVL", "pwr8") - .Case("POWER9", "pwr9") - .Default(generic); + std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent(); + const StringRef& Content = P ? P->getBuffer() : ""; + return detail::getHostCPUNameForPowerPC(Content); } -#elif defined(__linux__) && defined(__arm__) +#elif defined(__linux__) && (defined(__arm__) || defined(__aarch64__)) StringRef sys::getHostCPUName() { - // The cpuid register on arm is not accessible from user space. On Linux, - // it is exposed through the /proc/cpuinfo file. - - // Read 1024 bytes from /proc/cpuinfo, which should contain the CPU part line - // in all cases. - char buffer[1024]; - ssize_t CPUInfoSize = readCpuInfo(buffer, sizeof(buffer)); - if (CPUInfoSize == -1) - return "generic"; - - StringRef Str(buffer, CPUInfoSize); - - SmallVector<StringRef, 32> Lines; - Str.split(Lines, "\n"); - - // Look for the CPU implementer line. - StringRef Implementer; - for (unsigned I = 0, E = Lines.size(); I != E; ++I) - if (Lines[I].startswith("CPU implementer")) - Implementer = Lines[I].substr(15).ltrim("\t :"); - - if (Implementer == "0x41") // ARM Ltd. - // Look for the CPU part line. - for (unsigned I = 0, E = Lines.size(); I != E; ++I) - if (Lines[I].startswith("CPU part")) - // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The - // values correspond to the "Part number" in the CP15/c0 register. The - // contents are specified in the various processor manuals. - return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :")) - .Case("0x926", "arm926ej-s") - .Case("0xb02", "mpcore") - .Case("0xb36", "arm1136j-s") - .Case("0xb56", "arm1156t2-s") - .Case("0xb76", "arm1176jz-s") - .Case("0xc08", "cortex-a8") - .Case("0xc09", "cortex-a9") - .Case("0xc0f", "cortex-a15") - .Case("0xc20", "cortex-m0") - .Case("0xc23", "cortex-m3") - .Case("0xc24", "cortex-m4") - .Default("generic"); - - if (Implementer == "0x51") // Qualcomm Technologies, Inc. - // Look for the CPU part line. - for (unsigned I = 0, E = Lines.size(); I != E; ++I) - if (Lines[I].startswith("CPU part")) - // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The - // values correspond to the "Part number" in the CP15/c0 register. The - // contents are specified in the various processor manuals. - return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :")) - .Case("0x06f", "krait") // APQ8064 - .Default("generic"); - - return "generic"; + std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent(); + const StringRef& Content = P ? P->getBuffer() : ""; + return detail::getHostCPUNameForARM(Content); } #elif defined(__linux__) && defined(__s390x__) StringRef sys::getHostCPUName() { - // STIDP is a privileged operation, so use /proc/cpuinfo instead. - - // The "processor 0:" line comes after a fair amount of other information, - // including a cache breakdown, but this should be plenty. - char buffer[2048]; - ssize_t CPUInfoSize = readCpuInfo(buffer, sizeof(buffer)); - if (CPUInfoSize == -1) - return "generic"; - - StringRef Str(buffer, CPUInfoSize); - SmallVector<StringRef, 32> Lines; - Str.split(Lines, "\n"); - - // Look for the CPU features. - SmallVector<StringRef, 32> CPUFeatures; - for (unsigned I = 0, E = Lines.size(); I != E; ++I) - if (Lines[I].startswith("features")) { - size_t Pos = Lines[I].find(":"); - if (Pos != StringRef::npos) { - Lines[I].drop_front(Pos + 1).split(CPUFeatures, ' '); - break; - } - } - - // We need to check for the presence of vector support independently of - // the machine type, since we may only use the vector register set when - // supported by the kernel (and hypervisor). - bool HaveVectorSupport = false; - for (unsigned I = 0, E = CPUFeatures.size(); I != E; ++I) { - if (CPUFeatures[I] == "vx") - HaveVectorSupport = true; - } - - // Now check the processor machine type. - for (unsigned I = 0, E = Lines.size(); I != E; ++I) { - if (Lines[I].startswith("processor ")) { - size_t Pos = Lines[I].find("machine = "); - if (Pos != StringRef::npos) { - Pos += sizeof("machine = ") - 1; - unsigned int Id; - if (!Lines[I].drop_front(Pos).getAsInteger(10, Id)) { - if (Id >= 2964 && HaveVectorSupport) - return "z13"; - if (Id >= 2827) - return "zEC12"; - if (Id >= 2817) - return "z196"; - } - } - break; - } - } - - return "generic"; + std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent(); + const StringRef& Content = P ? P->getBuffer() : ""; + return detail::getHostCPUNameForS390x(Content); } #else StringRef sys::getHostCPUName() { return "generic"; } @@ -1232,6 +1245,7 @@ static int computeHostNumPhysicalCores() { if (std::error_code EC = Text.getError()) { llvm::errs() << "Can't read " << "/proc/cpuinfo: " << EC.message() << "\n"; + return -1; } SmallVector<StringRef, 8> strs; (*Text)->getBuffer().split(strs, "\n", /*MaxSplit=*/-1, @@ -1353,6 +1367,10 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) { Features["tbm"] = HasExtLeaf1 && ((ECX >> 21) & 1); Features["mwaitx"] = HasExtLeaf1 && ((ECX >> 29) & 1); + bool HasExtLeaf8 = MaxExtLevel >= 0x80000008 && + !getX86CpuIDAndInfoEx(0x80000008,0x0, &EAX, &EBX, &ECX, &EDX); + Features["clzero"] = HasExtLeaf8 && ((EBX >> 0) & 1); + bool HasLeaf7 = MaxLevel >= 7 && !getX86CpuIDAndInfoEx(0x7, 0x0, &EAX, &EBX, &ECX, &EDX); @@ -1362,14 +1380,10 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) { Features["fsgsbase"] = HasLeaf7 && ((EBX >> 0) & 1); Features["sgx"] = HasLeaf7 && ((EBX >> 2) & 1); Features["bmi"] = HasLeaf7 && ((EBX >> 3) & 1); - Features["hle"] = HasLeaf7 && ((EBX >> 4) & 1); Features["bmi2"] = HasLeaf7 && ((EBX >> 8) & 1); - Features["invpcid"] = HasLeaf7 && ((EBX >> 10) & 1); Features["rtm"] = HasLeaf7 && ((EBX >> 11) & 1); Features["rdseed"] = HasLeaf7 && ((EBX >> 18) & 1); Features["adx"] = HasLeaf7 && ((EBX >> 19) & 1); - Features["smap"] = HasLeaf7 && ((EBX >> 20) & 1); - Features["pcommit"] = HasLeaf7 && ((EBX >> 22) & 1); Features["clflushopt"] = HasLeaf7 && ((EBX >> 23) & 1); Features["clwb"] = HasLeaf7 && ((EBX >> 24) & 1); Features["sha"] = HasLeaf7 && ((EBX >> 29) & 1); @@ -1401,17 +1415,12 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) { } #elif defined(__linux__) && (defined(__arm__) || defined(__aarch64__)) bool sys::getHostCPUFeatures(StringMap<bool> &Features) { - // Read 1024 bytes from /proc/cpuinfo, which should contain the Features line - // in all cases. - char buffer[1024]; - ssize_t CPUInfoSize = readCpuInfo(buffer, sizeof(buffer)); - if (CPUInfoSize == -1) + std::unique_ptr<llvm::MemoryBuffer> P = getProcCpuinfoContent(); + if (!P) return false; - StringRef Str(buffer, CPUInfoSize); - SmallVector<StringRef, 32> Lines; - Str.split(Lines, "\n"); + P->getBuffer().split(Lines, "\n"); SmallVector<StringRef, 32> CPUFeatures; diff --git a/lib/Support/LockFileManager.cpp b/lib/Support/LockFileManager.cpp index 444aaa37c8c8..8be9879fbc24 100644 --- a/lib/Support/LockFileManager.cpp +++ b/lib/Support/LockFileManager.cpp @@ -304,9 +304,9 @@ LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { Interval.tv_sec = 0; Interval.tv_nsec = 1000000; #endif - // Don't wait more than five minutes per iteration. Total timeout for the file - // to appear is ~8.5 mins. - const unsigned MaxSeconds = 5*60; + // Don't wait more than 40s per iteration. Total timeout for the file + // to appear is ~1.5 minutes. + const unsigned MaxSeconds = 40; do { // Sleep for the designated interval, to allow the owning process time to // finish up and remove the lock file. diff --git a/lib/Support/LowLevelType.cpp b/lib/Support/LowLevelType.cpp new file mode 100644 index 000000000000..4290d69cd197 --- /dev/null +++ b/lib/Support/LowLevelType.cpp @@ -0,0 +1,47 @@ +//===-- llvm/Support/LowLevelType.cpp -------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file This file implements the more header-heavy bits of the LLT class to +/// avoid polluting users' namespaces. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/LowLevelTypeImpl.h" +#include "llvm/Support/raw_ostream.h" +using namespace llvm; + +LLT::LLT(MVT VT) { + if (VT.isVector()) { + SizeInBits = VT.getVectorElementType().getSizeInBits(); + ElementsOrAddrSpace = VT.getVectorNumElements(); + Kind = ElementsOrAddrSpace == 1 ? Scalar : Vector; + } else if (VT.isValid()) { + // Aggregates are no different from real scalars as far as GlobalISel is + // concerned. + Kind = Scalar; + SizeInBits = VT.getSizeInBits(); + ElementsOrAddrSpace = 1; + assert(SizeInBits != 0 && "invalid zero-sized type"); + } else { + Kind = Invalid; + SizeInBits = ElementsOrAddrSpace = 0; + } +} + +void LLT::print(raw_ostream &OS) const { + if (isVector()) + OS << "<" << ElementsOrAddrSpace << " x s" << SizeInBits << ">"; + else if (isPointer()) + OS << "p" << getAddressSpace(); + else if (isValid()) { + assert(isScalar() && "unexpected type"); + OS << "s" << getScalarSizeInBits(); + } else + llvm_unreachable("trying to print an invalid type"); +} diff --git a/lib/Support/MD5.cpp b/lib/Support/MD5.cpp index 942571eab0f3..bdbf1d677938 100644 --- a/lib/Support/MD5.cpp +++ b/lib/Support/MD5.cpp @@ -38,9 +38,13 @@ */ #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" #include "llvm/Support/MD5.h" #include "llvm/Support/raw_ostream.h" +#include <array> +#include <cstdint> #include <cstring> // The basic MD5 functions. @@ -68,7 +72,7 @@ ((MD5_u32plus) ptr[(n) * 4 + 3] << 24)) #define GET(n) (block[(n)]) -namespace llvm { +using namespace llvm; /// \brief This processes one or more 64-byte data blocks, but does NOT update ///the bit counters. There are no alignment requirements. @@ -179,9 +183,7 @@ const uint8_t *MD5::body(ArrayRef<uint8_t> Data) { return ptr; } -MD5::MD5() - : a(0x67452301), b(0xefcdab89), c(0x98badcfe), d(0x10325476), hi(0), lo(0) { -} +MD5::MD5() = default; /// Incrementally add the bytes in \p Data to the hash. void MD5::update(ArrayRef<uint8_t> Data) { @@ -259,10 +261,16 @@ void MD5::final(MD5Result &Result) { support::endian::write32le(&Result[12], d); } -void MD5::stringifyResult(MD5Result &Result, SmallString<32> &Str) { +SmallString<32> MD5::MD5Result::digest() const { + SmallString<32> Str; raw_svector_ostream Res(Str); for (int i = 0; i < 16; ++i) - Res << format("%.2x", Result[i]); + Res << format("%.2x", Bytes[i]); + return Str; +} + +void MD5::stringifyResult(MD5Result &Result, SmallString<32> &Str) { + Str = Result.digest(); } std::array<uint8_t, 16> MD5::hash(ArrayRef<uint8_t> Data) { @@ -271,8 +279,5 @@ std::array<uint8_t, 16> MD5::hash(ArrayRef<uint8_t> Data) { MD5::MD5Result Res; Hash.final(Res); - std::array<uint8_t, 16> Arr; - memcpy(Arr.data(), Res, sizeof(Res)); - return Arr; -} + return Res; } diff --git a/lib/Support/ManagedStatic.cpp b/lib/Support/ManagedStatic.cpp index 7dd31315f90d..fb7cd070c42d 100644 --- a/lib/Support/ManagedStatic.cpp +++ b/lib/Support/ManagedStatic.cpp @@ -21,7 +21,7 @@ using namespace llvm; static const ManagedStaticBase *StaticList = nullptr; static sys::Mutex *ManagedStaticMutex = nullptr; -LLVM_DEFINE_ONCE_FLAG(mutex_init_flag); +static llvm::once_flag mutex_init_flag; static void initializeMutex() { ManagedStaticMutex = new sys::Mutex(); diff --git a/lib/Support/MemoryBuffer.cpp b/lib/Support/MemoryBuffer.cpp index a3a18c9283ce..227e792d83dc 100644 --- a/lib/Support/MemoryBuffer.cpp +++ b/lib/Support/MemoryBuffer.cpp @@ -103,7 +103,7 @@ public: static ErrorOr<std::unique_ptr<MemoryBuffer>> getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize, - uint64_t Offset, bool RequiresNullTerminator, bool IsVolatileSize); + uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile); std::unique_ptr<MemoryBuffer> MemoryBuffer::getMemBuffer(StringRef InputData, StringRef BufferName, @@ -178,8 +178,8 @@ MemoryBuffer::getFileOrSTDIN(const Twine &Filename, int64_t FileSize, ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize, - uint64_t Offset) { - return getFileAux(FilePath, -1, MapSize, Offset, false, false); + uint64_t Offset, bool IsVolatile) { + return getFileAux(FilePath, -1, MapSize, Offset, false, IsVolatile); } @@ -254,19 +254,19 @@ getMemoryBufferForStream(int FD, const Twine &BufferName) { ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getFile(const Twine &Filename, int64_t FileSize, - bool RequiresNullTerminator, bool IsVolatileSize) { + bool RequiresNullTerminator, bool IsVolatile) { return getFileAux(Filename, FileSize, FileSize, 0, - RequiresNullTerminator, IsVolatileSize); + RequiresNullTerminator, IsVolatile); } static ErrorOr<std::unique_ptr<MemoryBuffer>> getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, - bool IsVolatileSize); + bool IsVolatile); static ErrorOr<std::unique_ptr<MemoryBuffer>> getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize, - uint64_t Offset, bool RequiresNullTerminator, bool IsVolatileSize) { + uint64_t Offset, bool RequiresNullTerminator, bool IsVolatile) { int FD; std::error_code EC = sys::fs::openFileForRead(Filename, FD); if (EC) @@ -274,7 +274,7 @@ getFileAux(const Twine &Filename, int64_t FileSize, uint64_t MapSize, ErrorOr<std::unique_ptr<MemoryBuffer>> Ret = getOpenFileImpl(FD, Filename, FileSize, MapSize, Offset, - RequiresNullTerminator, IsVolatileSize); + RequiresNullTerminator, IsVolatile); close(FD); return Ret; } @@ -285,11 +285,11 @@ static bool shouldUseMmap(int FD, off_t Offset, bool RequiresNullTerminator, int PageSize, - bool IsVolatileSize) { + bool IsVolatile) { // mmap may leave the buffer without null terminator if the file size changed // by the time the last page is mapped in, so avoid it if the file size is // likely to change. - if (IsVolatileSize) + if (IsVolatile) return false; // We don't use mmap for small files because this can severely fragment our @@ -300,7 +300,6 @@ static bool shouldUseMmap(int FD, if (!RequiresNullTerminator) return true; - // If we don't know the file size, use fstat to find out. fstat on an open // file descriptor is cheaper than stat on a random path. // FIXME: this chunk of code is duplicated, but it avoids a fstat when @@ -338,7 +337,7 @@ static bool shouldUseMmap(int FD, static ErrorOr<std::unique_ptr<MemoryBuffer>> getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize, uint64_t MapSize, int64_t Offset, bool RequiresNullTerminator, - bool IsVolatileSize) { + bool IsVolatile) { static int PageSize = sys::Process::getPageSize(); // Default is to map the full file. @@ -365,7 +364,7 @@ getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize, } if (shouldUseMmap(FD, FileSize, MapSize, Offset, RequiresNullTerminator, - PageSize, IsVolatileSize)) { + PageSize, IsVolatile)) { std::error_code EC; std::unique_ptr<MemoryBuffer> Result( new (NamedBufferAlloc(Filename)) @@ -415,17 +414,16 @@ getOpenFileImpl(int FD, const Twine &Filename, uint64_t FileSize, ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getOpenFile(int FD, const Twine &Filename, uint64_t FileSize, - bool RequiresNullTerminator, bool IsVolatileSize) { + bool RequiresNullTerminator, bool IsVolatile) { return getOpenFileImpl(FD, Filename, FileSize, FileSize, 0, - RequiresNullTerminator, IsVolatileSize); + RequiresNullTerminator, IsVolatile); } ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getOpenFileSlice(int FD, const Twine &Filename, uint64_t MapSize, - int64_t Offset) { + int64_t Offset, bool IsVolatile) { assert(MapSize != uint64_t(-1)); - return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false, - /*IsVolatileSize*/ false); + return getOpenFileImpl(FD, Filename, -1, MapSize, Offset, false, IsVolatile); } ErrorOr<std::unique_ptr<MemoryBuffer>> MemoryBuffer::getSTDIN() { diff --git a/lib/Support/Path.cpp b/lib/Support/Path.cpp index 4bb035eeccca..9fd6652ce4b8 100644 --- a/lib/Support/Path.cpp +++ b/lib/Support/Path.cpp @@ -11,13 +11,14 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Support/Path.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/Support/COFF.h" -#include "llvm/Support/MachO.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/Path.h" +#include "llvm/Support/MachO.h" #include "llvm/Support/Process.h" #include <cctype> #include <cstring> @@ -34,16 +35,29 @@ using namespace llvm::support::endian; namespace { using llvm::StringRef; using llvm::sys::path::is_separator; + using llvm::sys::path::Style; + inline Style real_style(Style style) { #ifdef LLVM_ON_WIN32 - const char *separators = "\\/"; - const char preferred_separator = '\\'; + return (style == Style::posix) ? Style::posix : Style::windows; #else - const char separators = '/'; - const char preferred_separator = '/'; + return (style == Style::windows) ? Style::windows : Style::posix; #endif + } - StringRef find_first_component(StringRef path) { + inline const char *separators(Style style) { + if (real_style(style) == Style::windows) + return "\\/"; + return "/"; + } + + inline char preferred_separator(Style style) { + if (real_style(style) == Style::windows) + return '\\'; + return '/'; + } + + StringRef find_first_component(StringRef path, Style style) { // Look for this first component in the following order. // * empty (in this case we return an empty string) // * either C: or {//,\\}net. @@ -53,96 +67,85 @@ namespace { if (path.empty()) return path; -#ifdef LLVM_ON_WIN32 - // C: - if (path.size() >= 2 && std::isalpha(static_cast<unsigned char>(path[0])) && - path[1] == ':') - return path.substr(0, 2); -#endif + if (real_style(style) == Style::windows) { + // C: + if (path.size() >= 2 && + std::isalpha(static_cast<unsigned char>(path[0])) && path[1] == ':') + return path.substr(0, 2); + } // //net - if ((path.size() > 2) && - is_separator(path[0]) && - path[0] == path[1] && - !is_separator(path[2])) { + if ((path.size() > 2) && is_separator(path[0], style) && + path[0] == path[1] && !is_separator(path[2], style)) { // Find the next directory separator. - size_t end = path.find_first_of(separators, 2); + size_t end = path.find_first_of(separators(style), 2); return path.substr(0, end); } // {/,\} - if (is_separator(path[0])) + if (is_separator(path[0], style)) return path.substr(0, 1); // * {file,directory}name - size_t end = path.find_first_of(separators); + size_t end = path.find_first_of(separators(style)); return path.substr(0, end); } - size_t filename_pos(StringRef str) { - if (str.size() == 2 && - is_separator(str[0]) && - str[0] == str[1]) + size_t filename_pos(StringRef str, Style style) { + if (str.size() == 2 && is_separator(str[0], style) && str[0] == str[1]) return 0; - if (str.size() > 0 && is_separator(str[str.size() - 1])) + if (str.size() > 0 && is_separator(str[str.size() - 1], style)) return str.size() - 1; - size_t pos = str.find_last_of(separators, str.size() - 1); + size_t pos = str.find_last_of(separators(style), str.size() - 1); -#ifdef LLVM_ON_WIN32 - if (pos == StringRef::npos) - pos = str.find_last_of(':', str.size() - 2); -#endif + if (real_style(style) == Style::windows) { + if (pos == StringRef::npos) + pos = str.find_last_of(':', str.size() - 2); + } - if (pos == StringRef::npos || - (pos == 1 && is_separator(str[0]))) + if (pos == StringRef::npos || (pos == 1 && is_separator(str[0], style))) return 0; return pos + 1; } - size_t root_dir_start(StringRef str) { + size_t root_dir_start(StringRef str, Style style) { // case "c:/" -#ifdef LLVM_ON_WIN32 - if (str.size() > 2 && - str[1] == ':' && - is_separator(str[2])) - return 2; -#endif + if (real_style(style) == Style::windows) { + if (str.size() > 2 && str[1] == ':' && is_separator(str[2], style)) + return 2; + } // case "//" - if (str.size() == 2 && - is_separator(str[0]) && - str[0] == str[1]) + if (str.size() == 2 && is_separator(str[0], style) && str[0] == str[1]) return StringRef::npos; // case "//net" - if (str.size() > 3 && - is_separator(str[0]) && - str[0] == str[1] && - !is_separator(str[2])) { - return str.find_first_of(separators, 2); + if (str.size() > 3 && is_separator(str[0], style) && str[0] == str[1] && + !is_separator(str[2], style)) { + return str.find_first_of(separators(style), 2); } // case "/" - if (str.size() > 0 && is_separator(str[0])) + if (str.size() > 0 && is_separator(str[0], style)) return 0; return StringRef::npos; } - size_t parent_path_end(StringRef path) { - size_t end_pos = filename_pos(path); + size_t parent_path_end(StringRef path, Style style) { + size_t end_pos = filename_pos(path, style); - bool filename_was_sep = path.size() > 0 && is_separator(path[end_pos]); + bool filename_was_sep = + path.size() > 0 && is_separator(path[end_pos], style); // Skip separators except for root dir. - size_t root_dir_pos = root_dir_start(path.substr(0, end_pos)); + size_t root_dir_pos = root_dir_start(path.substr(0, end_pos), style); - while(end_pos > 0 && - (end_pos - 1) != root_dir_pos && - is_separator(path[end_pos - 1])) + while (end_pos > 0 && (end_pos - 1) != root_dir_pos && + is_separator(path[end_pos - 1], style)) --end_pos; if (end_pos == 1 && root_dir_pos == 0 && filename_was_sep) @@ -230,11 +233,12 @@ namespace llvm { namespace sys { namespace path { -const_iterator begin(StringRef path) { +const_iterator begin(StringRef path, Style style) { const_iterator i; i.Path = path; - i.Component = find_first_component(path); + i.Component = find_first_component(path, style); i.Position = 0; + i.S = style; return i; } @@ -259,27 +263,21 @@ const_iterator &const_iterator::operator++() { // Both POSIX and Windows treat paths that begin with exactly two separators // specially. - bool was_net = Component.size() > 2 && - is_separator(Component[0]) && - Component[1] == Component[0] && - !is_separator(Component[2]); + bool was_net = Component.size() > 2 && is_separator(Component[0], S) && + Component[1] == Component[0] && !is_separator(Component[2], S); // Handle separators. - if (is_separator(Path[Position])) { + if (is_separator(Path[Position], S)) { // Root dir. - if (was_net -#ifdef LLVM_ON_WIN32 + if (was_net || // c:/ - || Component.endswith(":") -#endif - ) { + (real_style(S) == Style::windows && Component.endswith(":"))) { Component = Path.substr(Position, 1); return *this; } // Skip extra separators. - while (Position != Path.size() && - is_separator(Path[Position])) { + while (Position != Path.size() && is_separator(Path[Position], S)) { ++Position; } @@ -292,7 +290,7 @@ const_iterator &const_iterator::operator++() { } // Find next component. - size_t end_pos = Path.find_first_of(separators, Position); + size_t end_pos = Path.find_first_of(separators(S), Position); Component = Path.slice(Position, end_pos); return *this; @@ -306,10 +304,11 @@ ptrdiff_t const_iterator::operator-(const const_iterator &RHS) const { return Position - RHS.Position; } -reverse_iterator rbegin(StringRef Path) { +reverse_iterator rbegin(StringRef Path, Style style) { reverse_iterator I; I.Path = Path; I.Position = Path.size(); + I.S = style; return ++I; } @@ -324,10 +323,9 @@ reverse_iterator rend(StringRef Path) { reverse_iterator &reverse_iterator::operator++() { // If we're at the end and the previous char was a '/', return '.' unless // we are the root path. - size_t root_dir_pos = root_dir_start(Path); - if (Position == Path.size() && - Path.size() > root_dir_pos + 1 && - is_separator(Path[Position - 1])) { + size_t root_dir_pos = root_dir_start(Path, S); + if (Position == Path.size() && Path.size() > root_dir_pos + 1 && + is_separator(Path[Position - 1], S)) { --Position; Component = "."; return *this; @@ -336,13 +334,12 @@ reverse_iterator &reverse_iterator::operator++() { // Skip separators unless it's the root directory. size_t end_pos = Position; - while(end_pos > 0 && - (end_pos - 1) != root_dir_pos && - is_separator(Path[end_pos - 1])) + while (end_pos > 0 && (end_pos - 1) != root_dir_pos && + is_separator(Path[end_pos - 1], S)) --end_pos; // Find next separator. - size_t start_pos = filename_pos(Path.substr(0, end_pos)); + size_t start_pos = filename_pos(Path.substr(0, end_pos), S); Component = Path.slice(start_pos, end_pos); Position = start_pos; return *this; @@ -357,21 +354,15 @@ ptrdiff_t reverse_iterator::operator-(const reverse_iterator &RHS) const { return Position - RHS.Position; } -StringRef root_path(StringRef path) { - const_iterator b = begin(path), - pos = b, - e = end(path); +StringRef root_path(StringRef path, Style style) { + const_iterator b = begin(path, style), pos = b, e = end(path); if (b != e) { - bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0]; - bool has_drive = -#ifdef LLVM_ON_WIN32 - b->endswith(":"); -#else - false; -#endif + bool has_net = + b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; + bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if (has_net || has_drive) { - if ((++pos != e) && is_separator((*pos)[0])) { + if ((++pos != e) && is_separator((*pos)[0], style)) { // {C:/,//net/}, so get the first two components. return path.substr(0, b->size() + pos->size()); } else { @@ -381,7 +372,7 @@ StringRef root_path(StringRef path) { } // POSIX style root directory. - if (is_separator((*b)[0])) { + if (is_separator((*b)[0], style)) { return *b; } } @@ -389,17 +380,12 @@ StringRef root_path(StringRef path) { return StringRef(); } -StringRef root_name(StringRef path) { - const_iterator b = begin(path), - e = end(path); +StringRef root_name(StringRef path, Style style) { + const_iterator b = begin(path, style), e = end(path); if (b != e) { - bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0]; - bool has_drive = -#ifdef LLVM_ON_WIN32 - b->endswith(":"); -#else - false; -#endif + bool has_net = + b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; + bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if (has_net || has_drive) { // just {C:,//net}, return the first component. @@ -411,27 +397,21 @@ StringRef root_name(StringRef path) { return StringRef(); } -StringRef root_directory(StringRef path) { - const_iterator b = begin(path), - pos = b, - e = end(path); +StringRef root_directory(StringRef path, Style style) { + const_iterator b = begin(path, style), pos = b, e = end(path); if (b != e) { - bool has_net = b->size() > 2 && is_separator((*b)[0]) && (*b)[1] == (*b)[0]; - bool has_drive = -#ifdef LLVM_ON_WIN32 - b->endswith(":"); -#else - false; -#endif + bool has_net = + b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; + bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); if ((has_net || has_drive) && // {C:,//net}, skip to the next component. - (++pos != e) && is_separator((*pos)[0])) { + (++pos != e) && is_separator((*pos)[0], style)) { return *pos; } // POSIX style root directory. - if (!has_net && is_separator((*b)[0])) { + if (!has_net && is_separator((*b)[0], style)) { return *b; } } @@ -440,15 +420,13 @@ StringRef root_directory(StringRef path) { return StringRef(); } -StringRef relative_path(StringRef path) { - StringRef root = root_path(path); +StringRef relative_path(StringRef path, Style style) { + StringRef root = root_path(path, style); return path.substr(root.size()); } -void append(SmallVectorImpl<char> &path, const Twine &a, - const Twine &b, - const Twine &c, - const Twine &d) { +void append(SmallVectorImpl<char> &path, Style style, const Twine &a, + const Twine &b, const Twine &c, const Twine &d) { SmallString<32> a_storage; SmallString<32> b_storage; SmallString<32> c_storage; @@ -461,13 +439,15 @@ void append(SmallVectorImpl<char> &path, const Twine &a, if (!d.isTriviallyEmpty()) components.push_back(d.toStringRef(d_storage)); for (auto &component : components) { - bool path_has_sep = !path.empty() && is_separator(path[path.size() - 1]); - bool component_has_sep = !component.empty() && is_separator(component[0]); - bool is_root_name = has_root_name(component); + bool path_has_sep = + !path.empty() && is_separator(path[path.size() - 1], style); + bool component_has_sep = + !component.empty() && is_separator(component[0], style); + bool is_root_name = has_root_name(component, style); if (path_has_sep) { // Strip separators from beginning of component. - size_t loc = component.find_first_not_of(separators); + size_t loc = component.find_first_not_of(separators(style)); StringRef c = component.substr(loc); // Append it. @@ -477,41 +457,47 @@ void append(SmallVectorImpl<char> &path, const Twine &a, if (!component_has_sep && !(path.empty() || is_root_name)) { // Add a separator. - path.push_back(preferred_separator); + path.push_back(preferred_separator(style)); } path.append(component.begin(), component.end()); } } -void append(SmallVectorImpl<char> &path, - const_iterator begin, const_iterator end) { +void append(SmallVectorImpl<char> &path, const Twine &a, const Twine &b, + const Twine &c, const Twine &d) { + append(path, Style::native, a, b, c, d); +} + +void append(SmallVectorImpl<char> &path, const_iterator begin, + const_iterator end, Style style) { for (; begin != end; ++begin) - path::append(path, *begin); + path::append(path, style, *begin); } -StringRef parent_path(StringRef path) { - size_t end_pos = parent_path_end(path); +StringRef parent_path(StringRef path, Style style) { + size_t end_pos = parent_path_end(path, style); if (end_pos == StringRef::npos) return StringRef(); else return path.substr(0, end_pos); } -void remove_filename(SmallVectorImpl<char> &path) { - size_t end_pos = parent_path_end(StringRef(path.begin(), path.size())); +void remove_filename(SmallVectorImpl<char> &path, Style style) { + size_t end_pos = parent_path_end(StringRef(path.begin(), path.size()), style); if (end_pos != StringRef::npos) path.set_size(end_pos); } -void replace_extension(SmallVectorImpl<char> &path, const Twine &extension) { +void replace_extension(SmallVectorImpl<char> &path, const Twine &extension, + Style style) { StringRef p(path.begin(), path.size()); SmallString<32> ext_storage; StringRef ext = extension.toStringRef(ext_storage); // Erase existing extension. size_t pos = p.find_last_of('.'); - if (pos != StringRef::npos && pos >= filename_pos(p)) + if (pos != StringRef::npos && pos >= filename_pos(p, style)) path.set_size(pos); // Append '.' if needed. @@ -523,8 +509,8 @@ void replace_extension(SmallVectorImpl<char> &path, const Twine &extension) { } void replace_path_prefix(SmallVectorImpl<char> &Path, - const StringRef &OldPrefix, - const StringRef &NewPrefix) { + const StringRef &OldPrefix, const StringRef &NewPrefix, + Style style) { if (OldPrefix.empty() && NewPrefix.empty()) return; @@ -540,53 +526,58 @@ void replace_path_prefix(SmallVectorImpl<char> &Path, StringRef RelPath = OrigPath.substr(OldPrefix.size()); SmallString<256> NewPath; - path::append(NewPath, NewPrefix); - path::append(NewPath, RelPath); + path::append(NewPath, style, NewPrefix); + path::append(NewPath, style, RelPath); Path.swap(NewPath); } -void native(const Twine &path, SmallVectorImpl<char> &result) { +void native(const Twine &path, SmallVectorImpl<char> &result, Style style) { assert((!path.isSingleStringRef() || path.getSingleStringRef().data() != result.data()) && "path and result are not allowed to overlap!"); // Clear result. result.clear(); path.toVector(result); - native(result); + native(result, style); } -void native(SmallVectorImpl<char> &Path) { -#ifdef LLVM_ON_WIN32 - std::replace(Path.begin(), Path.end(), '/', '\\'); -#else - for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) { - if (*PI == '\\') { - auto PN = PI + 1; - if (PN < PE && *PN == '\\') - ++PI; // increment once, the for loop will move over the escaped slash - else - *PI = '/'; +void native(SmallVectorImpl<char> &Path, Style style) { + if (Path.empty()) + return; + if (real_style(style) == Style::windows) { + std::replace(Path.begin(), Path.end(), '/', '\\'); + if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) { + SmallString<128> PathHome; + home_directory(PathHome); + PathHome.append(Path.begin() + 1, Path.end()); + Path = PathHome; + } + } else { + for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) { + if (*PI == '\\') { + auto PN = PI + 1; + if (PN < PE && *PN == '\\') + ++PI; // increment once, the for loop will move over the escaped slash + else + *PI = '/'; + } } } -#endif } -std::string convert_to_slash(StringRef path) { -#ifdef LLVM_ON_WIN32 +std::string convert_to_slash(StringRef path, Style style) { + if (real_style(style) != Style::windows) + return path; + std::string s = path.str(); std::replace(s.begin(), s.end(), '\\', '/'); return s; -#else - return path; -#endif } -StringRef filename(StringRef path) { - return *rbegin(path); -} +StringRef filename(StringRef path, Style style) { return *rbegin(path, style); } -StringRef stem(StringRef path) { - StringRef fname = filename(path); +StringRef stem(StringRef path, Style style) { + StringRef fname = filename(path, style); size_t pos = fname.find_last_of('.'); if (pos == StringRef::npos) return fname; @@ -598,8 +589,8 @@ StringRef stem(StringRef path) { return fname.substr(0, pos); } -StringRef extension(StringRef path) { - StringRef fname = filename(path); +StringRef extension(StringRef path, Style style) { + StringRef fname = filename(path, style); size_t pos = fname.find_last_of('.'); if (pos == StringRef::npos) return StringRef(); @@ -611,110 +602,109 @@ StringRef extension(StringRef path) { return fname.substr(pos); } -bool is_separator(char value) { - switch(value) { -#ifdef LLVM_ON_WIN32 - case '\\': // fall through -#endif - case '/': return true; - default: return false; - } +bool is_separator(char value, Style style) { + if (value == '/') + return true; + if (real_style(style) == Style::windows) + return value == '\\'; + return false; } -static const char preferred_separator_string[] = { preferred_separator, '\0' }; - -StringRef get_separator() { - return preferred_separator_string; +StringRef get_separator(Style style) { + if (real_style(style) == Style::windows) + return "\\"; + return "/"; } -bool has_root_name(const Twine &path) { +bool has_root_name(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !root_name(p).empty(); + return !root_name(p, style).empty(); } -bool has_root_directory(const Twine &path) { +bool has_root_directory(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !root_directory(p).empty(); + return !root_directory(p, style).empty(); } -bool has_root_path(const Twine &path) { +bool has_root_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !root_path(p).empty(); + return !root_path(p, style).empty(); } -bool has_relative_path(const Twine &path) { +bool has_relative_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !relative_path(p).empty(); + return !relative_path(p, style).empty(); } -bool has_filename(const Twine &path) { +bool has_filename(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !filename(p).empty(); + return !filename(p, style).empty(); } -bool has_parent_path(const Twine &path) { +bool has_parent_path(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !parent_path(p).empty(); + return !parent_path(p, style).empty(); } -bool has_stem(const Twine &path) { +bool has_stem(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !stem(p).empty(); + return !stem(p, style).empty(); } -bool has_extension(const Twine &path) { +bool has_extension(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - return !extension(p).empty(); + return !extension(p, style).empty(); } -bool is_absolute(const Twine &path) { +bool is_absolute(const Twine &path, Style style) { SmallString<128> path_storage; StringRef p = path.toStringRef(path_storage); - bool rootDir = has_root_directory(p), -#ifdef LLVM_ON_WIN32 - rootName = has_root_name(p); -#else - rootName = true; -#endif + bool rootDir = has_root_directory(p, style); + bool rootName = + (real_style(style) != Style::windows) || has_root_name(p, style); return rootDir && rootName; } -bool is_relative(const Twine &path) { return !is_absolute(path); } +bool is_relative(const Twine &path, Style style) { + return !is_absolute(path, style); +} -StringRef remove_leading_dotslash(StringRef Path) { +StringRef remove_leading_dotslash(StringRef Path, Style style) { // Remove leading "./" (or ".//" or "././" etc.) - while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1])) { + while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1], style)) { Path = Path.substr(2); - while (Path.size() > 0 && is_separator(Path[0])) + while (Path.size() > 0 && is_separator(Path[0], style)) Path = Path.substr(1); } return Path; } -static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot) { +static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot, + Style style) { SmallVector<StringRef, 16> components; // Skip the root path, then look for traversal in the components. - StringRef rel = path::relative_path(path); - for (StringRef C : llvm::make_range(path::begin(rel), path::end(rel))) { + StringRef rel = path::relative_path(path, style); + for (StringRef C : + llvm::make_range(path::begin(rel, style), path::end(rel))) { if (C == ".") continue; // Leading ".." will remain in the path unless it's at the root. @@ -723,22 +713,23 @@ static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot) { components.pop_back(); continue; } - if (path::is_absolute(path)) + if (path::is_absolute(path, style)) continue; } components.push_back(C); } - SmallString<256> buffer = path::root_path(path); + SmallString<256> buffer = path::root_path(path, style); for (StringRef C : components) - path::append(buffer, C); + path::append(buffer, style, C); return buffer; } -bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot) { +bool remove_dots(SmallVectorImpl<char> &path, bool remove_dot_dot, + Style style) { StringRef p(path.data(), path.size()); - SmallString<256> result = remove_dots(p, remove_dot_dot); + SmallString<256> result = remove_dots(p, remove_dot_dot, style); if (result == path) return false; @@ -776,7 +767,7 @@ createTemporaryFile(const Twine &Model, int &ResultFD, llvm::SmallVectorImpl<char> &ResultPath, FSEntity Type) { SmallString<128> Storage; StringRef P = Model.toNullTerminatedStringRef(Storage); - assert(P.find_first_of(separators) == StringRef::npos && + assert(P.find_first_of(separators(Style::native)) == StringRef::npos && "Model must be a simple filename."); // Use P.begin() so that createUniqueEntity doesn't need to recreate Storage. return createUniqueEntity(P.begin(), ResultFD, ResultPath, @@ -818,12 +809,9 @@ static std::error_code make_absolute(const Twine ¤t_directory, bool use_current_directory) { StringRef p(path.data(), path.size()); - bool rootDirectory = path::has_root_directory(p), -#ifdef LLVM_ON_WIN32 - rootName = path::has_root_name(p); -#else - rootName = true; -#endif + bool rootDirectory = path::has_root_directory(p); + bool rootName = + (real_style(Style::native) != Style::windows) || path::has_root_name(p); // Already absolute. if (rootName && rootDirectory) @@ -937,6 +925,36 @@ std::error_code copy_file(const Twine &From, const Twine &To) { return std::error_code(); } +ErrorOr<MD5::MD5Result> md5_contents(int FD) { + MD5 Hash; + + constexpr size_t BufSize = 4096; + std::vector<uint8_t> Buf(BufSize); + int BytesRead = 0; + for (;;) { + BytesRead = read(FD, Buf.data(), BufSize); + if (BytesRead <= 0) + break; + Hash.update(makeArrayRef(Buf.data(), BytesRead)); + } + + if (BytesRead < 0) + return std::error_code(errno, std::generic_category()); + MD5::MD5Result Result; + Hash.final(Result); + return Result; +} + +ErrorOr<MD5::MD5Result> md5_contents(const Twine &Path) { + int FD; + if (auto EC = openFileForRead(Path, FD)) + return EC; + + auto Result = md5_contents(FD); + close(FD); + return Result; +} + bool exists(file_status status) { return status_known(status) && status.type() != file_type::file_not_found; } @@ -945,6 +963,13 @@ bool status_known(file_status s) { return s.type() != file_type::status_error; } +file_type get_file_type(const Twine &Path, bool Follow) { + file_status st; + if (status(Path, st, Follow)) + return file_type::status_error; + return st.type(); +} + bool is_directory(file_status status) { return status.type() == file_type::directory_file; } @@ -969,6 +994,18 @@ std::error_code is_regular_file(const Twine &path, bool &result) { return std::error_code(); } +bool is_symlink_file(file_status status) { + return status.type() == file_type::symlink_file; +} + +std::error_code is_symlink_file(const Twine &path, bool &result) { + file_status st; + if (std::error_code ec = status(path, st, false)) + return ec; + result = is_symlink_file(st); + return std::error_code(); +} + bool is_other(file_status status) { return exists(status) && !is_regular_file(status) && @@ -1162,7 +1199,15 @@ std::error_code identify_magic(const Twine &Path, file_magic &Result) { } std::error_code directory_entry::status(file_status &result) const { - return fs::status(Path, result); + return fs::status(Path, result, FollowSymlinks); +} + +ErrorOr<perms> getPermissions(const Twine &Path) { + file_status Status; + if (std::error_code EC = status(Path, Status)) + return EC; + + return Status.permissions(); } } // end namespace fs diff --git a/lib/Support/RWMutex.cpp b/lib/Support/RWMutex.cpp index 3b6309cef21a..6c9781c4e2d6 100644 --- a/lib/Support/RWMutex.cpp +++ b/lib/Support/RWMutex.cpp @@ -13,7 +13,6 @@ #include "llvm/Config/config.h" #include "llvm/Support/RWMutex.h" -#include <cstring> //===----------------------------------------------------------------------===// //=== WARNING: Implementation here must contain only TRULY operating system @@ -22,29 +21,31 @@ #if !defined(LLVM_ENABLE_THREADS) || LLVM_ENABLE_THREADS == 0 // Define all methods as no-ops if threading is explicitly disabled -namespace llvm { + +using namespace llvm; using namespace sys; -RWMutexImpl::RWMutexImpl() { } -RWMutexImpl::~RWMutexImpl() { } + +RWMutexImpl::RWMutexImpl() = default; +RWMutexImpl::~RWMutexImpl() = default; + bool RWMutexImpl::reader_acquire() { return true; } bool RWMutexImpl::reader_release() { return true; } bool RWMutexImpl::writer_acquire() { return true; } bool RWMutexImpl::writer_release() { return true; } -} + #else #if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_RWLOCK_INIT) #include <cassert> +#include <cstdlib> #include <pthread.h> -#include <stdlib.h> -namespace llvm { +using namespace llvm; using namespace sys; // Construct a RWMutex using pthread calls RWMutexImpl::RWMutexImpl() - : data_(nullptr) { // Declare the pthread_rwlock data structures pthread_rwlock_t* rwlock = @@ -113,8 +114,6 @@ RWMutexImpl::writer_release() return errorcode == 0; } -} - #elif defined(LLVM_ON_UNIX) #include "Unix/RWMutex.inc" #elif defined( LLVM_ON_WIN32) diff --git a/lib/Support/Signals.cpp b/lib/Support/Signals.cpp index e5e38f59c040..57f36bf175b3 100644 --- a/lib/Support/Signals.cpp +++ b/lib/Support/Signals.cpp @@ -29,7 +29,6 @@ #include <vector> namespace llvm { -using namespace sys; //===----------------------------------------------------------------------===// //=== WARNING: Implementation here must contain only TRULY operating system diff --git a/lib/Support/SourceMgr.cpp b/lib/Support/SourceMgr.cpp index 4cb9b2ff2cda..ca2391c10ff1 100644 --- a/lib/Support/SourceMgr.cpp +++ b/lib/Support/SourceMgr.cpp @@ -13,30 +13,43 @@ // //===----------------------------------------------------------------------===// -#include "llvm/Support/SourceMgr.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorOr.h" #include "llvm/Support/Locale.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/SourceMgr.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <memory> +#include <string> +#include <utility> + using namespace llvm; static const size_t TabStop = 8; namespace { + struct LineNoCacheTy { const char *LastQuery; unsigned LastQueryBufferID; unsigned LineNoOfQuery; }; -} + +} // end anonymous namespace static LineNoCacheTy *getCache(void *Ptr) { return (LineNoCacheTy*)Ptr; } - SourceMgr::~SourceMgr() { // Delete the line # cache if allocated. if (LineNoCacheTy *Cache = getCache(LineNoCache)) @@ -132,12 +145,10 @@ void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const { << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n"; } - SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg, ArrayRef<SMRange> Ranges, ArrayRef<SMFixIt> FixIts) const { - // First thing to do: find the current buffer containing the specified // location to pull out the source line. SmallVector<std::pair<unsigned, unsigned>, 4> ColRanges; @@ -223,7 +234,7 @@ void SourceMgr::PrintMessage(raw_ostream &OS, SMLoc Loc, void SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind, const Twine &Msg, ArrayRef<SMRange> Ranges, ArrayRef<SMFixIt> FixIts, bool ShowColors) const { - PrintMessage(llvm::errs(), Loc, Kind, Msg, Ranges, FixIts, ShowColors); + PrintMessage(errs(), Loc, Kind, Msg, Ranges, FixIts, ShowColors); } //===----------------------------------------------------------------------===// @@ -233,7 +244,7 @@ void SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind, SMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN, int Line, int Col, SourceMgr::DiagKind Kind, StringRef Msg, StringRef LineStr, - ArrayRef<std::pair<unsigned,unsigned> > Ranges, + ArrayRef<std::pair<unsigned,unsigned>> Ranges, ArrayRef<SMFixIt> Hints) : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind), Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()), @@ -286,7 +297,7 @@ static void buildFixItLine(std::string &CaretLine, std::string &FixItLine, // FIXME: This assertion is intended to catch unintended use of multibyte // characters in fixits. If we decide to do this, we'll have to track // separate byte widths for the source and fixit lines. - assert((size_t)llvm::sys::locale::columnWidth(I->getText()) == + assert((size_t)sys::locale::columnWidth(I->getText()) == I->getText().size()); // This relies on one byte per column in our fixit hints. diff --git a/lib/Support/StringRef.cpp b/lib/Support/StringRef.cpp index d81250e48dde..9b7cc1c1d182 100644 --- a/lib/Support/StringRef.cpp +++ b/lib/Support/StringRef.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/APFloat.h" #include "llvm/ADT/APInt.h" #include "llvm/ADT/Hashing.h" #include "llvm/ADT/edit_distance.h" @@ -595,6 +596,18 @@ bool StringRef::getAsInteger(unsigned Radix, APInt &Result) const { return false; } +bool StringRef::getAsDouble(double &Result, bool AllowInexact) const { + APFloat F(0.0); + APFloat::opStatus Status = + F.convertFromString(*this, APFloat::rmNearestTiesToEven); + if (Status != APFloat::opOK) { + if (!AllowInexact || Status != APFloat::opInexact) + return true; + } + + Result = F.convertToDouble(); + return false; +} // Implementation of StringRef hashing. hash_code llvm::hash_value(StringRef S) { diff --git a/lib/Support/TargetParser.cpp b/lib/Support/TargetParser.cpp index 42fab671a251..639d2ece263a 100644 --- a/lib/Support/TargetParser.cpp +++ b/lib/Support/TargetParser.cpp @@ -448,6 +448,8 @@ bool llvm::AArch64::getExtensionFeatures(unsigned Extensions, Features.push_back("+spe"); if (Extensions & AArch64::AEK_RAS) Features.push_back("+ras"); + if (Extensions & AArch64::AEK_LSE) + Features.push_back("+lse"); return true; } @@ -725,6 +727,7 @@ unsigned llvm::ARM::parseArchProfile(StringRef Arch) { case ARM::AK_ARMV8R: return ARM::PK_R; case ARM::AK_ARMV7A: + case ARM::AK_ARMV7VE: case ARM::AK_ARMV7K: case ARM::AK_ARMV8A: case ARM::AK_ARMV8_1A: @@ -761,6 +764,7 @@ unsigned llvm::ARM::parseArchVersion(StringRef Arch) { case ARM::AK_ARMV6M: return 6; case ARM::AK_ARMV7A: + case ARM::AK_ARMV7VE: case ARM::AK_ARMV7R: case ARM::AK_ARMV7M: case ARM::AK_ARMV7S: diff --git a/lib/Support/Threading.cpp b/lib/Support/Threading.cpp index 760f9e2c388b..6a10b988d464 100644 --- a/lib/Support/Threading.cpp +++ b/lib/Support/Threading.cpp @@ -14,14 +14,20 @@ #include "llvm/Support/Threading.h" #include "llvm/Config/config.h" -#include "llvm/Support/Atomic.h" #include "llvm/Support/Host.h" -#include "llvm/Support/Mutex.h" -#include "llvm/Support/thread.h" + #include <cassert> +#include <errno.h> +#include <stdlib.h> +#include <string.h> using namespace llvm; +//===----------------------------------------------------------------------===// +//=== WARNING: Implementation here must contain only TRULY operating system +//=== independent code. +//===----------------------------------------------------------------------===// + bool llvm::llvm_is_multithreaded() { #if LLVM_ENABLE_THREADS != 0 return true; @@ -30,100 +36,47 @@ bool llvm::llvm_is_multithreaded() { #endif } -#if LLVM_ENABLE_THREADS != 0 && defined(HAVE_PTHREAD_H) -#include <pthread.h> - -struct ThreadInfo { - void (*UserFn)(void *); - void *UserData; -}; -static void *ExecuteOnThread_Dispatch(void *Arg) { - ThreadInfo *TI = reinterpret_cast<ThreadInfo*>(Arg); - TI->UserFn(TI->UserData); - return nullptr; -} - -void llvm::llvm_execute_on_thread(void (*Fn)(void*), void *UserData, +#if LLVM_ENABLE_THREADS == 0 || \ + (!defined(LLVM_ON_WIN32) && !defined(HAVE_PTHREAD_H)) +// Support for non-Win32, non-pthread implementation. +void llvm::llvm_execute_on_thread(void (*Fn)(void *), void *UserData, unsigned RequestedStackSize) { - ThreadInfo Info = { Fn, UserData }; - pthread_attr_t Attr; - pthread_t Thread; - - // Construct the attributes object. - if (::pthread_attr_init(&Attr) != 0) - return; - - // Set the requested stack size, if given. - if (RequestedStackSize != 0) { - if (::pthread_attr_setstacksize(&Attr, RequestedStackSize) != 0) - goto error; - } - - // Construct and execute the thread. - if (::pthread_create(&Thread, &Attr, ExecuteOnThread_Dispatch, &Info) != 0) - goto error; - - // Wait for the thread and clean up. - ::pthread_join(Thread, nullptr); - - error: - ::pthread_attr_destroy(&Attr); + (void)RequestedStackSize; + Fn(UserData); } -#elif LLVM_ENABLE_THREADS!=0 && defined(LLVM_ON_WIN32) -#include "Windows/WindowsSupport.h" -#include <process.h> -// Windows will at times define MemoryFence. -#ifdef MemoryFence -#undef MemoryFence -#endif +unsigned llvm::heavyweight_hardware_concurrency() { return 1; } -struct ThreadInfo { - void (*func)(void*); - void *param; -}; +uint64_t llvm::get_threadid() { return 0; } -static unsigned __stdcall ThreadCallback(void *param) { - struct ThreadInfo *info = reinterpret_cast<struct ThreadInfo *>(param); - info->func(info->param); +uint32_t llvm::get_max_thread_name_length() { return 0; } - return 0; -} +void llvm::set_thread_name(const Twine &Name) {} -void llvm::llvm_execute_on_thread(void (*Fn)(void*), void *UserData, - unsigned RequestedStackSize) { - struct ThreadInfo param = { Fn, UserData }; - - HANDLE hThread = (HANDLE)::_beginthreadex(NULL, - RequestedStackSize, ThreadCallback, - ¶m, 0, NULL); +void llvm::get_thread_name(SmallVectorImpl<char> &Name) { Name.clear(); } - if (hThread) { - // We actually don't care whether the wait succeeds or fails, in - // the same way we don't care whether the pthread_join call succeeds - // or fails. There's not much we could do if this were to fail. But - // on success, this call will wait until the thread finishes executing - // before returning. - (void)::WaitForSingleObject(hThread, INFINITE); - ::CloseHandle(hThread); - } -} #else -// Support for non-Win32, non-pthread implementation. -void llvm::llvm_execute_on_thread(void (*Fn)(void*), void *UserData, - unsigned RequestedStackSize) { - (void) RequestedStackSize; - Fn(UserData); -} - -#endif +#include <thread> unsigned llvm::heavyweight_hardware_concurrency() { -#if !LLVM_ENABLE_THREADS - return 1; -#endif + // Since we can't get here unless LLVM_ENABLE_THREADS == 1, it is safe to use + // `std::thread` directly instead of `llvm::thread` (and indeed, doing so + // allows us to not define `thread` in the llvm namespace, which conflicts + // with some platforms such as FreeBSD whose headers also define a struct + // called `thread` in the global namespace which can cause ambiguity due to + // ADL. int NumPhysical = sys::getHostNumPhysicalCores(); if (NumPhysical == -1) - return thread::hardware_concurrency(); + return std::thread::hardware_concurrency(); return NumPhysical; } + +// Include the platform-specific parts of this class. +#ifdef LLVM_ON_UNIX +#include "Unix/Threading.inc" +#endif +#ifdef LLVM_ON_WIN32 +#include "Windows/Threading.inc" +#endif + +#endif diff --git a/lib/Support/Timer.cpp b/lib/Support/Timer.cpp index fbd73d0b6b3b..8d68c6ae9682 100644 --- a/lib/Support/Timer.cpp +++ b/lib/Support/Timer.cpp @@ -72,22 +72,9 @@ std::unique_ptr<raw_fd_ostream> llvm::CreateInfoOutputFile() { return llvm::make_unique<raw_fd_ostream>(2, false); // stderr. } - -static TimerGroup *DefaultTimerGroup = nullptr; static TimerGroup *getDefaultTimerGroup() { - TimerGroup *tmp = DefaultTimerGroup; - sys::MemoryFence(); - if (tmp) return tmp; - - sys::SmartScopedLock<true> Lock(*TimerLock); - tmp = DefaultTimerGroup; - if (!tmp) { - tmp = new TimerGroup("misc", "Miscellaneous Ungrouped Timers"); - sys::MemoryFence(); - DefaultTimerGroup = tmp; - } - - return tmp; + static TimerGroup DefaultTimerGroup("misc", "Miscellaneous Ungrouped Timers"); + return &DefaultTimerGroup; } //===----------------------------------------------------------------------===// @@ -309,7 +296,7 @@ void TimerGroup::PrintQueuedTimers(raw_ostream &OS) { // If this is not an collection of ungrouped times, print the total time. // Ungrouped timers don't really make sense to add up. We still print the // TOTAL line to make the percentages make sense. - if (this != DefaultTimerGroup) + if (this != getDefaultTimerGroup()) OS << format(" Total Execution Time: %5.4f seconds (%5.4f wall clock)\n", Total.getProcessTime(), Total.getWallTime()); OS << '\n'; diff --git a/lib/Support/Triple.cpp b/lib/Support/Triple.cpp index 6783b40a125d..64d5977e2ebd 100644 --- a/lib/Support/Triple.cpp +++ b/lib/Support/Triple.cpp @@ -510,6 +510,7 @@ static Triple::ObjectFormatType parseFormat(StringRef EnvironmentName) { .EndsWith("coff", Triple::COFF) .EndsWith("elf", Triple::ELF) .EndsWith("macho", Triple::MachO) + .EndsWith("wasm", Triple::Wasm) .Default(Triple::UnknownObjectFormat); } @@ -550,6 +551,8 @@ static Triple::SubArchType parseSubArch(StringRef SubArchName) { case ARM::AK_ARMV7A: case ARM::AK_ARMV7R: return Triple::ARMSubArch_v7; + case ARM::AK_ARMV7VE: + return Triple::ARMSubArch_v7ve; case ARM::AK_ARMV7K: return Triple::ARMSubArch_v7k; case ARM::AK_ARMV7M: @@ -581,6 +584,7 @@ static StringRef getObjectFormatTypeName(Triple::ObjectFormatType Kind) { case Triple::COFF: return "coff"; case Triple::ELF: return "elf"; case Triple::MachO: return "macho"; + case Triple::Wasm: return "wasm"; } llvm_unreachable("unknown object format type"); } @@ -1511,6 +1515,7 @@ StringRef Triple::getARMCPUForArch(StringRef MArch) const { return "strongarm"; } case llvm::Triple::NaCl: + case llvm::Triple::OpenBSD: return "cortex-a8"; default: switch (getEnvironment()) { diff --git a/lib/Support/Twine.cpp b/lib/Support/Twine.cpp index 465c6e6b8c4c..d17cd4e66439 100644 --- a/lib/Support/Twine.cpp +++ b/lib/Support/Twine.cpp @@ -173,10 +173,12 @@ void Twine::printRepr(raw_ostream &OS) const { OS << ")"; } +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) LLVM_DUMP_METHOD void Twine::dump() const { print(dbgs()); } -void Twine::dumpRepr() const { +LLVM_DUMP_METHOD void Twine::dumpRepr() const { printRepr(dbgs()); } +#endif diff --git a/lib/Support/Unix/Path.inc b/lib/Support/Unix/Path.inc index e0b11aaff007..93f8982196b3 100644 --- a/lib/Support/Unix/Path.inc +++ b/lib/Support/Unix/Path.inc @@ -48,6 +48,8 @@ # endif #endif +#include <pwd.h> + #ifdef __APPLE__ #include <mach-o/dyld.h> #include <sys/attr.h> @@ -65,23 +67,41 @@ #endif #include <sys/types.h> -#if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__ANDROID__) +#if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && \ + !defined(__linux__) #include <sys/statvfs.h> #define STATVFS statvfs +#define FSTATVFS fstatvfs #define STATVFS_F_FRSIZE(vfs) vfs.f_frsize #else -#ifdef __OpenBSD__ +#if defined(__OpenBSD__) || defined(__FreeBSD__) #include <sys/param.h> #include <sys/mount.h> -#elif defined(__ANDROID__) +#elif defined(__linux__) +#if defined(HAVE_LINUX_MAGIC_H) +#include <linux/magic.h> +#else +#if defined(HAVE_LINUX_NFS_FS_H) +#include <linux/nfs_fs.h> +#endif +#if defined(HAVE_LINUX_SMB_H) +#include <linux/smb.h> +#endif +#endif #include <sys/vfs.h> #else #include <sys/mount.h> #endif #define STATVFS statfs +#define FSTATVFS fstatfs #define STATVFS_F_FRSIZE(vfs) static_cast<uint64_t>(vfs.f_bsize) #endif +#if defined(__NetBSD__) +#define STATVFS_F_FLAG(vfs) (vfs).f_flag +#else +#define STATVFS_F_FLAG(vfs) (vfs).f_flags +#endif using namespace llvm; @@ -180,7 +200,7 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { if (getprogpath(exe_path, argv0)) return exe_path; } -#elif defined(HAVE_DLFCN_H) +#elif defined(HAVE_DLFCN_H) && defined(HAVE_DLADDR) // Use dladdr to get executable path if available. Dl_info DLInfo; int err = dladdr(MainAddr, &DLInfo); @@ -210,6 +230,10 @@ UniqueID file_status::getUniqueID() const { return UniqueID(fs_st_dev, fs_st_ino); } +uint32_t file_status::getLinkCount() const { + return fs_st_nlinks; +} + ErrorOr<space_info> disk_space(const Twine &Path) { struct STATVFS Vfs; if (::STATVFS(Path.str().c_str(), &Vfs)) @@ -257,6 +281,16 @@ std::error_code current_path(SmallVectorImpl<char> &result) { return std::error_code(); } +std::error_code set_current_path(const Twine &path) { + SmallString<128> path_storage; + StringRef p = path.toNullTerminatedStringRef(path_storage); + + if (::chdir(p.begin()) == -1) + return std::error_code(errno, std::generic_category()); + + return std::error_code(); +} + std::error_code create_directory(const Twine &path, bool IgnoreExisting, perms Perms) { SmallString<128> path_storage; @@ -325,6 +359,51 @@ std::error_code remove(const Twine &path, bool IgnoreNonExisting) { return std::error_code(); } +static bool is_local_impl(struct STATVFS &Vfs) { +#if defined(__linux__) +#ifndef NFS_SUPER_MAGIC +#define NFS_SUPER_MAGIC 0x6969 +#endif +#ifndef SMB_SUPER_MAGIC +#define SMB_SUPER_MAGIC 0x517B +#endif +#ifndef CIFS_MAGIC_NUMBER +#define CIFS_MAGIC_NUMBER 0xFF534D42 +#endif + switch ((uint32_t)Vfs.f_type) { + case NFS_SUPER_MAGIC: + case SMB_SUPER_MAGIC: + case CIFS_MAGIC_NUMBER: + return false; + default: + return true; + } +#elif defined(__CYGWIN__) + // Cygwin doesn't expose this information; would need to use Win32 API. + return false; +#else + return !!(STATVFS_F_FLAG(Vfs) & MNT_LOCAL); +#endif +} + +std::error_code is_local(const Twine &Path, bool &Result) { + struct STATVFS Vfs; + if (::STATVFS(Path.str().c_str(), &Vfs)) + return std::error_code(errno, std::generic_category()); + + Result = is_local_impl(Vfs); + return std::error_code(); +} + +std::error_code is_local(int FD, bool &Result) { + struct STATVFS Vfs; + if (::FSTATVFS(FD, &Vfs)) + return std::error_code(errno, std::generic_category()); + + Result = is_local_impl(Vfs); + return std::error_code(); +} + std::error_code rename(const Twine &from, const Twine &to) { // Get arguments. SmallString<128> from_storage; @@ -405,6 +484,46 @@ std::error_code equivalent(const Twine &A, const Twine &B, bool &result) { return std::error_code(); } +static void expandTildeExpr(SmallVectorImpl<char> &Path) { + StringRef PathStr(Path.begin(), Path.size()); + if (PathStr.empty() || !PathStr.startswith("~")) + return; + + PathStr = PathStr.drop_front(); + StringRef Expr = + PathStr.take_until([](char c) { return path::is_separator(c); }); + StringRef Remainder = PathStr.substr(Expr.size() + 1); + SmallString<128> Storage; + if (Expr.empty()) { + // This is just ~/..., resolve it to the current user's home dir. + if (!path::home_directory(Storage)) { + // For some reason we couldn't get the home directory. Just exit. + return; + } + + // Overwrite the first character and insert the rest. + Path[0] = Storage[0]; + Path.insert(Path.begin() + 1, Storage.begin() + 1, Storage.end()); + return; + } + + // This is a string of the form ~username/, look up this user's entry in the + // password database. + struct passwd *Entry = nullptr; + std::string User = Expr.str(); + Entry = ::getpwnam(User.c_str()); + + if (!Entry) { + // Unable to look up the entry, just return back the original path. + return; + } + + Storage = Remainder; + Path.clear(); + Path.append(Entry->pw_dir, Entry->pw_dir + strlen(Entry->pw_dir)); + llvm::sys::path::append(Path, Storage); +} + static std::error_code fillStatus(int StatRet, const struct stat &Status, file_status &Result) { if (StatRet != 0) { @@ -430,22 +549,23 @@ static std::error_code fillStatus(int StatRet, const struct stat &Status, Type = file_type::fifo_file; else if (S_ISSOCK(Status.st_mode)) Type = file_type::socket_file; + else if (S_ISLNK(Status.st_mode)) + Type = file_type::symlink_file; - perms Perms = static_cast<perms>(Status.st_mode); - Result = - file_status(Type, Perms, Status.st_dev, Status.st_ino, Status.st_atime, - Status.st_mtime, Status.st_uid, Status.st_gid, - Status.st_size); + perms Perms = static_cast<perms>(Status.st_mode) & all_perms; + Result = file_status(Type, Perms, Status.st_dev, Status.st_nlink, + Status.st_ino, Status.st_atime, Status.st_mtime, + Status.st_uid, Status.st_gid, Status.st_size); return std::error_code(); } -std::error_code status(const Twine &Path, file_status &Result) { +std::error_code status(const Twine &Path, file_status &Result, bool Follow) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); struct stat Status; - int StatRet = ::stat(P.begin(), &Status); + int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status); return fillStatus(StatRet, Status, Result); } @@ -455,6 +575,15 @@ std::error_code status(int FD, file_status &Result) { return fillStatus(StatRet, Status, Result); } +std::error_code setPermissions(const Twine &Path, perms Permissions) { + SmallString<128> PathStorage; + StringRef P = Path.toNullTerminatedStringRef(PathStorage); + + if (::chmod(P.begin(), Permissions)) + return std::error_code(errno, std::generic_category()); + return std::error_code(); +} + std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { #if defined(HAVE_FUTIMENS) timespec Times[2]; @@ -481,6 +610,26 @@ std::error_code mapped_file_region::init(int FD, uint64_t Offset, int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE; int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE); +#if defined(__APPLE__) + //---------------------------------------------------------------------- + // Newer versions of MacOSX have a flag that will allow us to read from + // binaries whose code signature is invalid without crashing by using + // the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media + // is mapped we can avoid crashing and return zeroes to any pages we try + // to read if the media becomes unavailable by using the + // MAP_RESILIENT_MEDIA flag. These flags are only usable when mapping + // with PROT_READ, so take care not to specify them otherwise. + //---------------------------------------------------------------------- + if (Mode == readonly) { +#if defined(MAP_RESILIENT_CODESIGN) + flags |= MAP_RESILIENT_CODESIGN; +#endif +#if defined(MAP_RESILIENT_MEDIA) + flags |= MAP_RESILIENT_MEDIA; +#endif + } +#endif // #if defined (__APPLE__) + Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset); if (Mapping == MAP_FAILED) return std::error_code(errno, std::generic_category()); @@ -526,7 +675,8 @@ int mapped_file_region::alignment() { } std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path){ + StringRef path, + bool follow_symlinks) { SmallString<128> path_null(path); DIR *directory = ::opendir(path_null.c_str()); if (!directory) @@ -535,7 +685,7 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, it.IterationHandle = reinterpret_cast<intptr_t>(directory); // Add something for replace_filename to replace. path::append(path_null, "."); - it.CurrentEntry = directory_entry(path_null.str()); + it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks); return directory_iterator_increment(it); } @@ -577,10 +727,19 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, SmallVectorImpl<char> *RealPath) { SmallString<128> Storage; StringRef P = Name.toNullTerminatedStringRef(Storage); - while ((ResultFD = open(P.begin(), O_RDONLY)) < 0) { + int OpenFlags = O_RDONLY; +#ifdef O_CLOEXEC + OpenFlags |= O_CLOEXEC; +#endif + while ((ResultFD = open(P.begin(), OpenFlags)) < 0) { if (errno != EINTR) return std::error_code(errno, std::generic_category()); } +#ifndef O_CLOEXEC + int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); + (void)r; + assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed"); +#endif // Attempt to get the real name of the file, if the user asked if(!RealPath) return std::error_code(); @@ -616,6 +775,10 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, int OpenFlags = O_CREAT; +#ifdef O_CLOEXEC + OpenFlags |= O_CLOEXEC; +#endif + if (Flags & F_RW) OpenFlags |= O_RDWR; else @@ -635,6 +798,11 @@ std::error_code openFileForWrite(const Twine &Name, int &ResultFD, if (errno != EINTR) return std::error_code(errno, std::generic_category()); } +#ifndef O_CLOEXEC + int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); + (void)r; + assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed"); +#endif return std::error_code(); } @@ -685,18 +853,85 @@ std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) { return std::error_code(); } +template <typename T> +static std::error_code remove_directories_impl(const T &Entry, + bool IgnoreErrors) { + std::error_code EC; + directory_iterator Begin(Entry, EC, false); + directory_iterator End; + while (Begin != End) { + auto &Item = *Begin; + file_status st; + EC = Item.status(st); + if (EC && !IgnoreErrors) + return EC; + + if (is_directory(st)) { + EC = remove_directories_impl(Item, IgnoreErrors); + if (EC && !IgnoreErrors) + return EC; + } + + EC = fs::remove(Item.path(), true); + if (EC && !IgnoreErrors) + return EC; + + Begin.increment(EC); + if (EC && !IgnoreErrors) + return EC; + } + return std::error_code(); +} + +std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { + auto EC = remove_directories_impl(path, IgnoreErrors); + if (EC && !IgnoreErrors) + return EC; + EC = fs::remove(path, true); + if (EC && !IgnoreErrors) + return EC; + return std::error_code(); +} + +std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest, + bool expand_tilde) { + dest.clear(); + if (path.isTriviallyEmpty()) + return std::error_code(); + + if (expand_tilde) { + SmallString<128> Storage; + path.toVector(Storage); + expandTildeExpr(Storage); + return real_path(Storage, dest, false); + } + + int fd; + std::error_code EC = openFileForRead(path, fd, &dest); + + if (EC) + return EC; + ::close(fd); + return std::error_code(); +} + } // end namespace fs namespace path { bool home_directory(SmallVectorImpl<char> &result) { - if (char *RequestedDir = getenv("HOME")) { - result.clear(); - result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); - return true; + char *RequestedDir = getenv("HOME"); + if (!RequestedDir) { + struct passwd *pw = getpwuid(getuid()); + if (pw && pw->pw_dir) + RequestedDir = pw->pw_dir; } + if (!RequestedDir) + return false; - return false; + result.clear(); + result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + return true; } static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) { diff --git a/lib/Support/Unix/Signals.inc b/lib/Support/Unix/Signals.inc index 9752b70644c6..88ad21e9806e 100644 --- a/lib/Support/Unix/Signals.inc +++ b/lib/Support/Unix/Signals.inc @@ -25,8 +25,8 @@ #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <string> -#if HAVE_EXECINFO_H -# include <execinfo.h> // For backtrace(). +#ifdef HAVE_BACKTRACE +# include BACKTRACE_HEADER // For backtrace(). #endif #if HAVE_SIGNAL_H #include <signal.h> @@ -59,7 +59,7 @@ using namespace llvm; static RETSIGTYPE SignalHandler(int Sig); // defined below. -static ManagedStatic<SmartMutex<true> > SignalsMutex; +static ManagedStatic<sys::SmartMutex<true> > SignalsMutex; /// InterruptFunction - The function to call if ctrl-c is pressed. static void (*InterruptFunction)() = nullptr; @@ -149,11 +149,7 @@ static void CreateSigAltStack() {} #endif static void RegisterHandlers() { - // We need to dereference the signals mutex during handler registration so - // that we force its construction. This is to prevent the first use being - // during handling an actual signal because you can't safely call new in a - // signal handler. - *SignalsMutex; + sys::SmartScopedLock<true> Guard(*SignalsMutex); // If the handlers are already registered, we're done. if (NumRegisteredSignals != 0) return; @@ -223,7 +219,7 @@ static RETSIGTYPE SignalHandler(int Sig) { sigprocmask(SIG_UNBLOCK, &SigMask, nullptr); { - unique_lock<SmartMutex<true>> Guard(*SignalsMutex); + unique_lock<sys::SmartMutex<true>> Guard(*SignalsMutex); RemoveFilesToRemove(); if (std::find(std::begin(IntSigs), std::end(IntSigs), Sig) @@ -412,7 +408,7 @@ void llvm::sys::PrintStackTrace(raw_ostream &OS) { if (printSymbolizedStackTrace(Argv0, StackTrace, depth, OS)) return; -#if HAVE_DLFCN_H && __GNUG__ && !defined(__CYGWIN__) +#if HAVE_DLFCN_H && HAVE_DLADDR int width = 0; for (int i = 0; i < depth; ++i) { Dl_info dlinfo; @@ -462,7 +458,7 @@ void llvm::sys::PrintStackTrace(raw_ostream &OS) { } static void PrintStackTraceSignalHandler(void *) { - PrintStackTrace(llvm::errs()); + sys::PrintStackTrace(llvm::errs()); } void llvm::sys::DisableSystemDialogsOnCrash() {} diff --git a/lib/Support/Unix/Threading.inc b/lib/Support/Unix/Threading.inc new file mode 100644 index 000000000000..407b194e1b6a --- /dev/null +++ b/lib/Support/Unix/Threading.inc @@ -0,0 +1,215 @@ +//===- Unix/Threading.inc - Unix Threading Implementation ----- -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides the Unix specific implementation of Threading functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" + +#if defined(__APPLE__) +#include <mach/mach_init.h> +#include <mach/mach_port.h> +#endif + +#include <pthread.h> + +#if defined(__FreeBSD__) +#include <pthread_np.h> // For pthread_getthreadid_np() +#endif + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) +#include <sys/sysctl.h> +#include <sys/user.h> +#include <errno.h> +#include <unistd.h> +#endif + +#if defined(__NetBSD__) +#include <lwp.h> // For _lwp_self() +#endif + +#if defined(__linux__) +#include <unistd.h> // For syscall() +#include <sys/syscall.h> // For syscall codes +#endif + +namespace { + struct ThreadInfo { + void(*UserFn)(void *); + void *UserData; + }; +} + +static void *ExecuteOnThread_Dispatch(void *Arg) { + ThreadInfo *TI = reinterpret_cast<ThreadInfo*>(Arg); + TI->UserFn(TI->UserData); + return nullptr; +} + +void llvm::llvm_execute_on_thread(void(*Fn)(void*), void *UserData, + unsigned RequestedStackSize) { + ThreadInfo Info = { Fn, UserData }; + pthread_attr_t Attr; + pthread_t Thread; + + // Construct the attributes object. + if (::pthread_attr_init(&Attr) != 0) + return; + + // Set the requested stack size, if given. + if (RequestedStackSize != 0) { + if (::pthread_attr_setstacksize(&Attr, RequestedStackSize) != 0) + goto error; + } + + // Construct and execute the thread. + if (::pthread_create(&Thread, &Attr, ExecuteOnThread_Dispatch, &Info) != 0) + goto error; + + // Wait for the thread and clean up. + ::pthread_join(Thread, nullptr); + +error: + ::pthread_attr_destroy(&Attr); +} + + +uint64_t llvm::get_threadid() { +#if defined(__APPLE__) + // Calling "mach_thread_self()" bumps the reference count on the thread + // port, so we need to deallocate it. mach_task_self() doesn't bump the ref + // count. + thread_port_t Self = mach_thread_self(); + mach_port_deallocate(mach_task_self(), Self); + return Self; +#elif defined(__FreeBSD__) + return uint64_t(pthread_getthreadid_np()); +#elif defined(__NetBSD__) + return uint64_t(_lwp_self()); +#elif defined(__ANDROID__) + return uint64_t(gettid()); +#elif defined(__linux__) + return uint64_t(syscall(SYS_gettid)); +#elif defined(LLVM_ON_WIN32) + return uint64_t(::GetCurrentThreadId()); +#else + return uint64_t(pthread_self()); +#endif +} + + +static constexpr uint32_t get_max_thread_name_length_impl() { +#if defined(__NetBSD__) + return PTHREAD_MAX_NAMELEN_NP; +#elif defined(__APPLE__) + return 64; +#elif defined(__linux__) +#if HAVE_PTHREAD_SETNAME_NP + return 16; +#else + return 0; +#endif +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + return 16; +#else + return 0; +#endif +} + +uint32_t llvm::get_max_thread_name_length() { + return get_max_thread_name_length_impl(); +} + +void llvm::set_thread_name(const Twine &Name) { + // Make sure the input is null terminated. + SmallString<64> Storage; + StringRef NameStr = Name.toNullTerminatedStringRef(Storage); + + // Truncate from the beginning, not the end, if the specified name is too + // long. For one, this ensures that the resulting string is still null + // terminated, but additionally the end of a long thread name will usually + // be more unique than the beginning, since a common pattern is for similar + // threads to share a common prefix. + if (get_max_thread_name_length() > 0) + NameStr = NameStr.take_back(get_max_thread_name_length()); + (void)NameStr; +#if defined(__linux__) +#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__) +#if HAVE_PTHREAD_SETNAME_NP + ::pthread_setname_np(::pthread_self(), NameStr.data()); +#endif +#endif +#elif defined(__FreeBSD__) + ::pthread_set_name_np(::pthread_self(), NameStr.data()); +#elif defined(__NetBSD__) + ::pthread_setname_np(::pthread_self(), "%s", + const_cast<char *>(NameStr.data())); +#elif defined(__APPLE__) + ::pthread_setname_np(NameStr.data()); +#endif +} + +void llvm::get_thread_name(SmallVectorImpl<char> &Name) { + Name.clear(); + +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + int pid = ::getpid(); + uint64_t tid = get_threadid(); + + struct kinfo_proc *kp = nullptr, *nkp; + size_t len = 0; + int error; + int ctl[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID | KERN_PROC_INC_THREAD, + (int)pid }; + + while (1) { + error = sysctl(ctl, 4, kp, &len, nullptr, 0); + if (kp == nullptr || (error != 0 && errno == ENOMEM)) { + // Add extra space in case threads are added before next call. + len += sizeof(*kp) + len / 10; + nkp = (struct kinfo_proc *)realloc(kp, len); + if (nkp == nullptr) { + free(kp); + return; + } + kp = nkp; + continue; + } + if (error != 0) + len = 0; + break; + } + + for (size_t i = 0; i < len / sizeof(*kp); i++) { + if (kp[i].ki_tid == (lwpid_t)tid) { + Name.append(kp[i].ki_tdname, kp[i].ki_tdname + strlen(kp[i].ki_tdname)); + break; + } + } + free(kp); + return; +#elif defined(__NetBSD__) + constexpr uint32_t len = get_max_thread_name_length_impl(); + char buf[len]; + ::pthread_getname_np(::pthread_self(), buf, len); + + Name.append(buf, buf + strlen(buf)); +#elif defined(__linux__) +#if (defined(__GLIBC__) && defined(_GNU_SOURCE)) || defined(__ANDROID__) +#if HAVE_PTHREAD_GETNAME_NP + constexpr uint32_t len = get_max_thread_name_length_impl(); + char Buffer[len]; + if (0 == ::pthread_getname_np(::pthread_self(), Buffer, len)) + Name.append(Buffer, Buffer + strlen(Buffer)); +#endif +#endif +#endif +} diff --git a/lib/Support/Windows/DynamicLibrary.inc b/lib/Support/Windows/DynamicLibrary.inc index 050689483deb..709499deeafa 100644 --- a/lib/Support/Windows/DynamicLibrary.inc +++ b/lib/Support/Windows/DynamicLibrary.inc @@ -24,7 +24,6 @@ #endif namespace llvm { -using namespace sys; //===----------------------------------------------------------------------===// //=== WARNING: Implementation here must contain only Win32 specific code @@ -33,7 +32,7 @@ using namespace sys; typedef BOOL (WINAPI *fpEnumerateLoadedModules)(HANDLE,PENUMLOADED_MODULES_CALLBACK64,PVOID); static fpEnumerateLoadedModules fEnumerateLoadedModules; -static DenseSet<HMODULE> *OpenedHandles; +static llvm::ManagedStatic<DenseSet<HMODULE> > OpenedHandles; static bool loadDebugHelp(void) { HMODULE hLib = ::LoadLibraryW(L"Dbghelp.dll"); @@ -51,15 +50,13 @@ ELM_Callback(PCSTR ModuleName, DWORD64 ModuleBase, return TRUE; } -DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *filename, - std::string *errMsg) { +sys::DynamicLibrary +sys::DynamicLibrary::getPermanentLibrary(const char *filename, + std::string *errMsg) { SmartScopedLock<true> lock(*SymbolsMutex); if (!filename) { // When no file is specified, enumerate all DLLs and EXEs in the process. - if (OpenedHandles == 0) - OpenedHandles = new DenseSet<HMODULE>(); - if (!fEnumerateLoadedModules) { if (!loadDebugHelp()) { assert(false && "These APIs should always be available"); @@ -79,7 +76,7 @@ DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *filename, MakeErrMsg(errMsg, std::string(filename) + ": Can't convert to UTF-16"); return DynamicLibrary(); } - + HMODULE a_handle = LoadLibraryW(filenameUnicode.data()); if (a_handle == 0) { @@ -87,9 +84,6 @@ DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *filename, return DynamicLibrary(); } - if (OpenedHandles == 0) - OpenedHandles = new DenseSet<HMODULE>(); - // If we've already loaded this library, FreeLibrary() the handle in order to // keep the internal refcount at +1. if (!OpenedHandles->insert(a_handle).second) @@ -98,6 +92,18 @@ DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *filename, return DynamicLibrary(a_handle); } +sys::DynamicLibrary +sys::DynamicLibrary::addPermanentLibrary(void *handle, std::string *errMsg) { + SmartScopedLock<true> lock(*SymbolsMutex); + // If we've already loaded this library, tell the caller. + if (!OpenedHandles->insert((HMODULE)handle).second) { + MakeErrMsg(errMsg, "Library already loaded"); + return DynamicLibrary(); + } + + return DynamicLibrary(handle); +} + // Stack probing routines are in the support library (e.g. libgcc), but we don't // have dynamic linking on windows. Provide a hook. #define EXPLICIT_SYMBOL(SYM) \ @@ -123,7 +129,7 @@ DynamicLibrary DynamicLibrary::getPermanentLibrary(const char *filename, #undef INLINE_DEF_SYMBOL1 #undef INLINE_DEF_SYMBOL2 -void* DynamicLibrary::SearchForAddressOfSymbol(const char* symbolName) { +void *sys::DynamicLibrary::SearchForAddressOfSymbol(const char *symbolName) { SmartScopedLock<true> Lock(*SymbolsMutex); // First check symbols added via AddSymbol(). @@ -135,7 +141,7 @@ void* DynamicLibrary::SearchForAddressOfSymbol(const char* symbolName) { } // Now search the libraries. - if (OpenedHandles) { + if (OpenedHandles.isConstructed()) { for (DenseSet<HMODULE>::iterator I = OpenedHandles->begin(), E = OpenedHandles->end(); I != E; ++I) { FARPROC ptr = GetProcAddress((HMODULE)*I, symbolName); @@ -171,7 +177,7 @@ void* DynamicLibrary::SearchForAddressOfSymbol(const char* symbolName) { return 0; } -void *DynamicLibrary::getAddressOfSymbol(const char *symbolName) { +void *sys::DynamicLibrary::getAddressOfSymbol(const char *symbolName) { if (!isValid()) return NULL; if (Data == &OpenedHandles) diff --git a/lib/Support/Windows/Mutex.inc b/lib/Support/Windows/Mutex.inc index ab79d079122f..0af145ec9a4e 100644 --- a/lib/Support/Windows/Mutex.inc +++ b/lib/Support/Windows/Mutex.inc @@ -20,15 +20,14 @@ #include "llvm/Support/Mutex.h" namespace llvm { -using namespace sys; -MutexImpl::MutexImpl(bool /*recursive*/) +sys::MutexImpl::MutexImpl(bool /*recursive*/) { data_ = new CRITICAL_SECTION; InitializeCriticalSection((LPCRITICAL_SECTION)data_); } -MutexImpl::~MutexImpl() +sys::MutexImpl::~MutexImpl() { DeleteCriticalSection((LPCRITICAL_SECTION)data_); delete (LPCRITICAL_SECTION)data_; @@ -36,21 +35,21 @@ MutexImpl::~MutexImpl() } bool -MutexImpl::acquire() +sys::MutexImpl::acquire() { EnterCriticalSection((LPCRITICAL_SECTION)data_); return true; } bool -MutexImpl::release() +sys::MutexImpl::release() { LeaveCriticalSection((LPCRITICAL_SECTION)data_); return true; } bool -MutexImpl::tryacquire() +sys::MutexImpl::tryacquire() { return TryEnterCriticalSection((LPCRITICAL_SECTION)data_); } diff --git a/lib/Support/Windows/Path.inc b/lib/Support/Windows/Path.inc index 27b250b428a5..b00d3905f658 100644 --- a/lib/Support/Windows/Path.inc +++ b/lib/Support/Windows/Path.inc @@ -26,6 +26,7 @@ // These two headers must be included last, and make sure shlobj is required // after Windows.h to make sure it picks up our definition of _WIN32_WINNT #include "WindowsSupport.h" +#include <shellapi.h> #include <shlobj.h> #undef max @@ -178,6 +179,10 @@ TimePoint<> file_status::getLastModificationTime() const { return toTimePoint(Time); } +uint32_t file_status::getLinkCount() const { + return NumLinks; +} + std::error_code current_path(SmallVectorImpl<char> &result) { SmallVector<wchar_t, MAX_PATH> cur_path; DWORD len = MAX_PATH; @@ -200,6 +205,18 @@ std::error_code current_path(SmallVectorImpl<char> &result) { return UTF16ToUTF8(cur_path.begin(), cur_path.size(), result); } +std::error_code set_current_path(const Twine &path) { + // Convert to utf-16. + SmallVector<wchar_t, 128> wide_path; + if (std::error_code ec = widenPath(path, wide_path)) + return ec; + + if (!::SetCurrentDirectoryW(wide_path.begin())) + return mapWindowsError(::GetLastError()); + + return std::error_code(); +} + std::error_code create_directory(const Twine &path, bool IgnoreExisting, perms Perms) { SmallVector<wchar_t, 128> path_utf16; @@ -265,6 +282,80 @@ std::error_code remove(const Twine &path, bool IgnoreNonExisting) { return std::error_code(); } +static std::error_code is_local_internal(SmallVectorImpl<wchar_t> &Path, + bool &Result) { + SmallVector<wchar_t, 128> VolumePath; + size_t Len = 128; + while (true) { + VolumePath.resize(Len); + BOOL Success = + ::GetVolumePathNameW(Path.data(), VolumePath.data(), VolumePath.size()); + + if (Success) + break; + + DWORD Err = ::GetLastError(); + if (Err != ERROR_INSUFFICIENT_BUFFER) + return mapWindowsError(Err); + + Len *= 2; + } + // If the output buffer has exactly enough space for the path name, but not + // the null terminator, it will leave the output unterminated. Push a null + // terminator onto the end to ensure that this never happens. + VolumePath.push_back(L'\0'); + VolumePath.set_size(wcslen(VolumePath.data())); + const wchar_t *P = VolumePath.data(); + + UINT Type = ::GetDriveTypeW(P); + switch (Type) { + case DRIVE_FIXED: + Result = true; + return std::error_code(); + case DRIVE_REMOTE: + case DRIVE_CDROM: + case DRIVE_RAMDISK: + case DRIVE_REMOVABLE: + Result = false; + return std::error_code(); + default: + return make_error_code(errc::no_such_file_or_directory); + } + llvm_unreachable("Unreachable!"); +} + +std::error_code is_local(const Twine &path, bool &result) { + if (!llvm::sys::fs::exists(path) || !llvm::sys::path::has_root_path(path)) + return make_error_code(errc::no_such_file_or_directory); + + SmallString<128> Storage; + StringRef P = path.toStringRef(Storage); + + // Convert to utf-16. + SmallVector<wchar_t, 128> WidePath; + if (std::error_code ec = widenPath(P, WidePath)) + return ec; + return is_local_internal(WidePath, result); +} + +std::error_code is_local(int FD, bool &Result) { + SmallVector<wchar_t, 128> FinalPath; + HANDLE Handle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); + + size_t Len = 128; + do { + FinalPath.reserve(Len); + Len = ::GetFinalPathNameByHandleW(Handle, FinalPath.data(), + FinalPath.capacity() - 1, VOLUME_NAME_NT); + if (Len == 0) + return mapWindowsError(::GetLastError()); + } while (Len > FinalPath.capacity()); + + FinalPath.set_size(Len); + + return is_local_internal(FinalPath, Result); +} + std::error_code rename(const Twine &from, const Twine &to) { // Convert to utf-16. SmallVector<wchar_t, 128> wide_from; @@ -443,13 +534,16 @@ static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { file_type Type = (Info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? file_type::directory_file : file_type::regular_file; - Result = - file_status(Type, Info.ftLastAccessTime.dwHighDateTime, - Info.ftLastAccessTime.dwLowDateTime, - Info.ftLastWriteTime.dwHighDateTime, - Info.ftLastWriteTime.dwLowDateTime, - Info.dwVolumeSerialNumber, Info.nFileSizeHigh, - Info.nFileSizeLow, Info.nFileIndexHigh, Info.nFileIndexLow); + perms Permissions = (Info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + ? (all_read | all_exe) + : all_all; + Result = file_status( + Type, Permissions, Info.nNumberOfLinks, + Info.ftLastAccessTime.dwHighDateTime, + Info.ftLastAccessTime.dwLowDateTime, + Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime, + Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow, + Info.nFileIndexHigh, Info.nFileIndexLow); return std::error_code(); } @@ -465,7 +559,7 @@ handle_status_error: return mapWindowsError(LastError); } -std::error_code status(const Twine &path, file_status &result) { +std::error_code status(const Twine &path, file_status &result, bool Follow) { SmallString<128> path_storage; SmallVector<wchar_t, 128> path_utf16; @@ -482,28 +576,19 @@ std::error_code status(const Twine &path, file_status &result) { if (attr == INVALID_FILE_ATTRIBUTES) return getStatus(INVALID_HANDLE_VALUE, result); + DWORD Flags = FILE_FLAG_BACKUP_SEMANTICS; // Handle reparse points. - if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { - ScopedFileHandle h( - ::CreateFileW(path_utf16.begin(), - 0, // Attributes only. - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - 0)); - if (!h) - return getStatus(INVALID_HANDLE_VALUE, result); - } + if (!Follow && (attr & FILE_ATTRIBUTE_REPARSE_POINT)) + Flags |= FILE_FLAG_OPEN_REPARSE_POINT; ScopedFileHandle h( ::CreateFileW(path_utf16.begin(), 0, // Attributes only. FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); - if (!h) - return getStatus(INVALID_HANDLE_VALUE, result); + NULL, OPEN_EXISTING, Flags, 0)); + if (!h) + return getStatus(INVALID_HANDLE_VALUE, result); - return getStatus(h, result); + return getStatus(h, result); } std::error_code status(int FD, file_status &Result) { @@ -511,6 +596,37 @@ std::error_code status(int FD, file_status &Result) { return getStatus(FileHandle, Result); } +std::error_code setPermissions(const Twine &Path, perms Permissions) { + SmallVector<wchar_t, 128> PathUTF16; + if (std::error_code EC = widenPath(Path, PathUTF16)) + return EC; + + DWORD Attributes = ::GetFileAttributesW(PathUTF16.begin()); + if (Attributes == INVALID_FILE_ATTRIBUTES) + return mapWindowsError(GetLastError()); + + // There are many Windows file attributes that are not to do with the file + // permissions (e.g. FILE_ATTRIBUTE_HIDDEN). We need to be careful to preserve + // them. + if (Permissions & all_write) { + Attributes &= ~FILE_ATTRIBUTE_READONLY; + if (Attributes == 0) + // FILE_ATTRIBUTE_NORMAL indicates no other attributes are set. + Attributes |= FILE_ATTRIBUTE_NORMAL; + } + else { + Attributes |= FILE_ATTRIBUTE_READONLY; + // FILE_ATTRIBUTE_NORMAL is not compatible with any other attributes, so + // remove it, if it is present. + Attributes &= ~FILE_ATTRIBUTE_NORMAL; + } + + if (!::SetFileAttributesW(PathUTF16.begin(), Attributes)) + return mapWindowsError(GetLastError()); + + return std::error_code(); +} + std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { FILETIME FT = toFILETIME(Time); HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); @@ -616,7 +732,8 @@ int mapped_file_region::alignment() { } std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path){ + StringRef path, + bool follow_symlinks) { SmallVector<wchar_t, 128> path_utf16; if (std::error_code ec = widenPath(path, path_utf16)) @@ -661,7 +778,7 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, it.IterationHandle = intptr_t(FindHandle.take()); SmallString<128> directory_entry_path(path); path::append(directory_entry_path, directory_entry_name_utf8); - it.CurrentEntry = directory_entry(directory_entry_path); + it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks); return std::error_code(); } @@ -701,6 +818,52 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { return std::error_code(); } +static std::error_code realPathFromHandle(HANDLE H, + SmallVectorImpl<char> &RealPath) { + RealPath.clear(); + llvm::SmallVector<wchar_t, MAX_PATH> Buffer; + DWORD CountChars = ::GetFinalPathNameByHandleW( + H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); + if (CountChars > Buffer.capacity()) { + // The buffer wasn't big enough, try again. In this case the return value + // *does* indicate the size of the null terminator. + Buffer.reserve(CountChars); + CountChars = ::GetFinalPathNameByHandleW( + H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); + } + if (CountChars == 0) + return mapWindowsError(GetLastError()); + + const wchar_t *Data = Buffer.data(); + if (CountChars >= 4) { + if (0 == ::memcmp(Data, L"\\\\?\\", 8)) { + CountChars -= 4; + Data += 4; + } + } + + // Convert the result from UTF-16 to UTF-8. + return UTF16ToUTF8(Data, CountChars, RealPath); +} + +static std::error_code directoryRealPath(const Twine &Name, + SmallVectorImpl<char> &RealPath) { + SmallVector<wchar_t, 128> PathUTF16; + + if (std::error_code EC = widenPath(Name, PathUTF16)) + return EC; + + HANDLE H = + ::CreateFileW(PathUTF16.begin(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (H == INVALID_HANDLE_VALUE) + return mapWindowsError(GetLastError()); + std::error_code EC = realPathFromHandle(H, RealPath); + ::CloseHandle(H); + return EC; +} + std::error_code openFileForRead(const Twine &Name, int &ResultFD, SmallVectorImpl<char> *RealPath) { SmallVector<wchar_t, 128> PathUTF16; @@ -732,20 +895,8 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, } // Fetch the real name of the file, if the user asked - if (RealPath) { - RealPath->clear(); - wchar_t RealPathUTF16[MAX_PATH]; - DWORD CountChars = - ::GetFinalPathNameByHandleW(H, RealPathUTF16, MAX_PATH, - FILE_NAME_NORMALIZED); - if (CountChars > 0 && CountChars < MAX_PATH) { - // Convert the result from UTF-16 to UTF-8. - SmallString<MAX_PATH> RealPathUTF8; - if (!UTF16ToUTF8(RealPathUTF16, CountChars, RealPathUTF8)) - RealPath->append(RealPathUTF8.data(), - RealPathUTF8.data() + strlen(RealPathUTF8.data())); - } - } + if (RealPath) + realPathFromHandle(H, *RealPath); ResultFD = FD; return std::error_code(); @@ -843,6 +994,81 @@ std::error_code getPathFromOpenFD(int FD, SmallVectorImpl<char> &ResultPath) { return windows::UTF16ToUTF8(TempPath.data(), CharCount, ResultPath); } + +std::error_code remove_directories(const Twine &path, bool IgnoreErrors) { + // Convert to utf-16. + SmallVector<wchar_t, 128> Path16; + std::error_code EC = widenPath(path, Path16); + if (EC && !IgnoreErrors) + return EC; + + // SHFileOperation() accepts a list of paths, and so must be double null- + // terminated to indicate the end of the list. The buffer is already null + // terminated, but since that null character is not considered part of the + // vector's size, pushing another one will just consume that byte. So we + // need to push 2 null terminators. + Path16.push_back(0); + Path16.push_back(0); + + SHFILEOPSTRUCTW shfos = {}; + shfos.wFunc = FO_DELETE; + shfos.pFrom = Path16.data(); + shfos.fFlags = FOF_NO_UI; + + int result = ::SHFileOperationW(&shfos); + if (result != 0 && !IgnoreErrors) + return mapWindowsError(result); + return std::error_code(); +} + +static void expandTildeExpr(SmallVectorImpl<char> &Path) { + // Path does not begin with a tilde expression. + if (Path.empty() || Path[0] != '~') + return; + + StringRef PathStr(Path.begin(), Path.size()); + PathStr = PathStr.drop_front(); + StringRef Expr = PathStr.take_until([](char c) { return path::is_separator(c); }); + + if (!Expr.empty()) { + // This is probably a ~username/ expression. Don't support this on Windows. + return; + } + + SmallString<128> HomeDir; + if (!path::home_directory(HomeDir)) { + // For some reason we couldn't get the home directory. Just exit. + return; + } + + // Overwrite the first character and insert the rest. + Path[0] = HomeDir[0]; + Path.insert(Path.begin() + 1, HomeDir.begin() + 1, HomeDir.end()); +} + +std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest, + bool expand_tilde) { + dest.clear(); + if (path.isTriviallyEmpty()) + return std::error_code(); + + if (expand_tilde) { + SmallString<128> Storage; + path.toVector(Storage); + expandTildeExpr(Storage); + return real_path(Storage, dest, false); + } + + if (is_directory(path)) + return directoryRealPath(path, dest); + + int fd; + if (std::error_code EC = llvm::sys::fs::openFileForRead(path, fd, &dest)) + return EC; + ::close(fd); + return std::error_code(); +} + } // end namespace fs namespace path { diff --git a/lib/Support/Windows/Process.inc b/lib/Support/Windows/Process.inc index 8d646b3217a0..18aef610d54a 100644 --- a/lib/Support/Windows/Process.inc +++ b/lib/Support/Windows/Process.inc @@ -47,7 +47,6 @@ #endif using namespace llvm; -using namespace sys; // This function retrieves the page size using GetNativeSystemInfo() and is // present solely so it can be called once to initialize the self_process member diff --git a/lib/Support/Windows/Program.inc b/lib/Support/Windows/Program.inc index 78fc538bd9bf..721167da5b15 100644 --- a/lib/Support/Windows/Program.inc +++ b/lib/Support/Windows/Program.inc @@ -29,7 +29,6 @@ //===----------------------------------------------------------------------===// namespace llvm { -using namespace sys; ProcessInfo::ProcessInfo() : ProcessHandle(0), Pid(0), ReturnCode(0) {} diff --git a/lib/Support/Windows/RWMutex.inc b/lib/Support/Windows/RWMutex.inc index 2d1d25f67b8a..ac60c2fc05be 100644 --- a/lib/Support/Windows/RWMutex.inc +++ b/lib/Support/Windows/RWMutex.inc @@ -19,7 +19,6 @@ #include "WindowsSupport.h" namespace llvm { -using namespace sys; // Windows has slim read-writer lock support on Vista and higher, so we // will attempt to load the APIs. If they exist, we will use them, and @@ -73,7 +72,7 @@ static bool loadSRW() { return sHasSRW; } -RWMutexImpl::RWMutexImpl() { +sys::RWMutexImpl::RWMutexImpl() { if (loadSRW()) { data_ = calloc(1, sizeof(SRWLOCK)); fpInitializeSRWLock(static_cast<PSRWLOCK>(data_)); @@ -83,14 +82,14 @@ RWMutexImpl::RWMutexImpl() { } } -RWMutexImpl::~RWMutexImpl() { +sys::RWMutexImpl::~RWMutexImpl() { if (!sHasSRW) DeleteCriticalSection(static_cast<LPCRITICAL_SECTION>(data_)); // Nothing to do in the case of slim reader/writers except free the memory. free(data_); } -bool RWMutexImpl::reader_acquire() { +bool sys::RWMutexImpl::reader_acquire() { if (sHasSRW) { fpAcquireSRWLockShared(static_cast<PSRWLOCK>(data_)); } else { @@ -99,7 +98,7 @@ bool RWMutexImpl::reader_acquire() { return true; } -bool RWMutexImpl::reader_release() { +bool sys::RWMutexImpl::reader_release() { if (sHasSRW) { fpReleaseSRWLockShared(static_cast<PSRWLOCK>(data_)); } else { @@ -108,7 +107,7 @@ bool RWMutexImpl::reader_release() { return true; } -bool RWMutexImpl::writer_acquire() { +bool sys::RWMutexImpl::writer_acquire() { if (sHasSRW) { fpAcquireSRWLockExclusive(static_cast<PSRWLOCK>(data_)); } else { @@ -117,7 +116,7 @@ bool RWMutexImpl::writer_acquire() { return true; } -bool RWMutexImpl::writer_release() { +bool sys::RWMutexImpl::writer_release() { if (sHasSRW) { fpReleaseSRWLockExclusive(static_cast<PSRWLOCK>(data_)); } else { diff --git a/lib/Support/Windows/Signals.inc b/lib/Support/Windows/Signals.inc index f739421eece4..1ef51888baf3 100644 --- a/lib/Support/Windows/Signals.inc +++ b/lib/Support/Windows/Signals.inc @@ -776,7 +776,7 @@ static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { // the nasty sorts of crashes that aren't 100% reproducible from a set of // inputs (or in the event that the user is unable or unwilling to provide a // reproducible case). - if (!llvm::Process::AreCoreFilesPrevented()) { + if (!llvm::sys::Process::AreCoreFilesPrevented()) { MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo; ExceptionInfo.ThreadId = ::GetCurrentThreadId(); ExceptionInfo.ExceptionPointers = ep; diff --git a/lib/Support/Windows/ThreadLocal.inc b/lib/Support/Windows/ThreadLocal.inc index b9cb8ff9836e..8be1c3ecfbb9 100644 --- a/lib/Support/Windows/ThreadLocal.inc +++ b/lib/Support/Windows/ThreadLocal.inc @@ -20,33 +20,32 @@ #include "llvm/Support/ThreadLocal.h" namespace llvm { -using namespace sys; -ThreadLocalImpl::ThreadLocalImpl() : data() { +sys::ThreadLocalImpl::ThreadLocalImpl() : data() { static_assert(sizeof(DWORD) <= sizeof(data), "size too big"); DWORD* tls = reinterpret_cast<DWORD*>(&data); *tls = TlsAlloc(); assert(*tls != TLS_OUT_OF_INDEXES); } -ThreadLocalImpl::~ThreadLocalImpl() { +sys::ThreadLocalImpl::~ThreadLocalImpl() { DWORD* tls = reinterpret_cast<DWORD*>(&data); TlsFree(*tls); } -void *ThreadLocalImpl::getInstance() { +void *sys::ThreadLocalImpl::getInstance() { DWORD* tls = reinterpret_cast<DWORD*>(&data); return TlsGetValue(*tls); } -void ThreadLocalImpl::setInstance(const void* d){ +void sys::ThreadLocalImpl::setInstance(const void* d){ DWORD* tls = reinterpret_cast<DWORD*>(&data); int errorcode = TlsSetValue(*tls, const_cast<void*>(d)); assert(errorcode != 0); (void)errorcode; } -void ThreadLocalImpl::removeInstance() { +void sys::ThreadLocalImpl::removeInstance() { setInstance(0); } diff --git a/lib/Support/Windows/Threading.inc b/lib/Support/Windows/Threading.inc new file mode 100644 index 000000000000..decb48887af2 --- /dev/null +++ b/lib/Support/Windows/Threading.inc @@ -0,0 +1,109 @@ +//===- Windows/Threading.inc - Win32 Threading Implementation - -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file provides the Win32 specific implementation of Threading functions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" + +#include "Windows/WindowsSupport.h" +#include <process.h> + +// Windows will at times define MemoryFence. +#ifdef MemoryFence +#undef MemoryFence +#endif + +namespace { + struct ThreadInfo { + void(*func)(void*); + void *param; + }; +} + +static unsigned __stdcall ThreadCallback(void *param) { + struct ThreadInfo *info = reinterpret_cast<struct ThreadInfo *>(param); + info->func(info->param); + + return 0; +} + +void llvm::llvm_execute_on_thread(void(*Fn)(void*), void *UserData, + unsigned RequestedStackSize) { + struct ThreadInfo param = { Fn, UserData }; + + HANDLE hThread = (HANDLE)::_beginthreadex(NULL, + RequestedStackSize, ThreadCallback, + ¶m, 0, NULL); + + if (hThread) { + // We actually don't care whether the wait succeeds or fails, in + // the same way we don't care whether the pthread_join call succeeds + // or fails. There's not much we could do if this were to fail. But + // on success, this call will wait until the thread finishes executing + // before returning. + (void)::WaitForSingleObject(hThread, INFINITE); + ::CloseHandle(hThread); + } +} + +uint64_t llvm::get_threadid() { + return uint64_t(::GetCurrentThreadId()); +} + +uint32_t llvm::get_max_thread_name_length() { return 0; } + +#if defined(_MSC_VER) +static void SetThreadName(DWORD Id, LPCSTR Name) { + constexpr DWORD MS_VC_EXCEPTION = 0x406D1388; + +#pragma pack(push, 8) + struct THREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to thread name + DWORD dwThreadId; // Thread ID (-1 == current thread) + DWORD dwFlags; // Reserved. Do not use. + }; +#pragma pack(pop) + + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = Name; + info.dwThreadId = Id; + info.dwFlags = 0; + + __try { + ::RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), + (ULONG_PTR *)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + } +} +#endif + +void llvm::set_thread_name(const Twine &Name) { +#if defined(_MSC_VER) + // Make sure the input is null terminated. + SmallString<64> Storage; + StringRef NameStr = Name.toNullTerminatedStringRef(Storage); + SetThreadName(::GetCurrentThreadId(), NameStr.data()); +#endif +} + +void llvm::get_thread_name(SmallVectorImpl<char> &Name) { + // "Name" is not an inherent property of a thread on Windows. In fact, when + // you "set" the name, you are only firing a one-time message to a debugger + // which it interprets as a program setting its threads' name. We may be + // able to get fancy by creating a TLS entry when someone calls + // set_thread_name so that subsequent calls to get_thread_name return this + // value. + Name.clear(); +} diff --git a/lib/Support/YAMLTraits.cpp b/lib/Support/YAMLTraits.cpp index 9849b3aa1ce9..c410b1d56086 100644 --- a/lib/Support/YAMLTraits.cpp +++ b/lib/Support/YAMLTraits.cpp @@ -398,17 +398,10 @@ bool Input::canElideEmptySequence() { //===----------------------------------------------------------------------===// Output::Output(raw_ostream &yout, void *context, int WrapColumn) - : IO(context), - Out(yout), - WrapColumn(WrapColumn), - Column(0), - ColumnAtFlowStart(0), - ColumnAtMapFlowStart(0), - NeedBitValueComma(false), - NeedFlowSequenceComma(false), - EnumerationMatchFound(false), - NeedsNewLine(false) { -} + : IO(context), Out(yout), WrapColumn(WrapColumn), Column(0), + ColumnAtFlowStart(0), ColumnAtMapFlowStart(0), NeedBitValueComma(false), + NeedFlowSequenceComma(false), EnumerationMatchFound(false), + NeedsNewLine(false), WriteDefaultValues(false) {} Output::~Output() { } @@ -462,7 +455,7 @@ std::vector<StringRef> Output::keys() { bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault, bool &UseDefault, void *&) { UseDefault = false; - if (Required || !SameAsDefault) { + if (Required || !SameAsDefault || WriteDefaultValues) { auto State = StateStack.back(); if (State == inFlowMapFirstKey || State == inFlowMapOtherKey) { flowKey(Key); diff --git a/lib/Support/raw_ostream.cpp b/lib/Support/raw_ostream.cpp index d073802db932..1abc8ed8683d 100644 --- a/lib/Support/raw_ostream.cpp +++ b/lib/Support/raw_ostream.cpp @@ -465,8 +465,7 @@ void format_object_base::home() { static int getFD(StringRef Filename, std::error_code &EC, sys::fs::OpenFlags Flags) { // Handle "-" as stdout. Note that when we do this, we consider ourself - // the owner of stdout. This means that we can do things like close the - // file descriptor when we're done and set the "binary" flag globally. + // the owner of stdout and may set the "binary" flag globally based on Flags. if (Filename == "-") { EC = std::error_code(); // If user requested binary then put stdout into binary mode if @@ -497,6 +496,13 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered) ShouldClose = false; return; } + // We do not want to close STDOUT as there may have been several uses of it + // such as the case: llc %s -o=- -pass-remarks-output=- -filetype=asm + // which cause multiple closes of STDOUT_FILENO and/or use-after-close of it. + // Using dup() in getFD doesn't work as we end up with original STDOUT_FILENO + // open anyhow. + if (FD <= STDERR_FILENO) + ShouldClose = false; // Get the starting position. off_t loc = ::lseek(FD, 0, SEEK_CUR); |