diff options
Diffstat (limited to 'llvm/lib/Support')
99 files changed, 5830 insertions, 2633 deletions
diff --git a/llvm/lib/Support/AArch64TargetParser.cpp b/llvm/lib/Support/AArch64TargetParser.cpp index b5cd4af0eb3de..a6de44605675a 100644 --- a/llvm/lib/Support/AArch64TargetParser.cpp +++ b/llvm/lib/Support/AArch64TargetParser.cpp @@ -12,8 +12,8 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/AArch64TargetParser.h" -#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" #include <cctype> using namespace llvm; @@ -116,6 +116,8 @@ bool AArch64::getArchFeatures(AArch64::ArchKind AK, Features.push_back("+v8.4a"); if (AK == ArchKind::ARMV8_5A) Features.push_back("+v8.5a"); + if (AK == AArch64::ArchKind::ARMV8_6A) + Features.push_back("+v8.6a"); return AK != ArchKind::INVALID; } @@ -191,7 +193,7 @@ AArch64::ArchKind AArch64::parseArch(StringRef Arch) { return ArchKind::INVALID; StringRef Syn = ARM::getArchSynonym(Arch); - for (const auto A : AArch64ARCHNames) { + for (const auto &A : AArch64ARCHNames) { if (A.getName().endswith(Syn)) return A.ID; } @@ -199,7 +201,7 @@ AArch64::ArchKind AArch64::parseArch(StringRef Arch) { } AArch64::ArchExtKind AArch64::parseArchExt(StringRef ArchExt) { - for (const auto A : AArch64ARCHExtNames) { + for (const auto &A : AArch64ARCHExtNames) { if (ArchExt == A.getName()) return static_cast<ArchExtKind>(A.ID); } @@ -207,7 +209,7 @@ AArch64::ArchExtKind AArch64::parseArchExt(StringRef ArchExt) { } AArch64::ArchKind AArch64::parseCPUArch(StringRef CPU) { - for (const auto C : AArch64CPUNames) { + for (const auto &C : AArch64CPUNames) { if (CPU == C.getName()) return C.ArchID; } diff --git a/llvm/lib/Support/AMDGPUMetadata.cpp b/llvm/lib/Support/AMDGPUMetadata.cpp index 4ea197a973894..bfa1fe86cd3ed 100644 --- a/llvm/lib/Support/AMDGPUMetadata.cpp +++ b/llvm/lib/Support/AMDGPUMetadata.cpp @@ -111,7 +111,11 @@ struct MappingTraits<Kernel::Arg::Metadata> { YIO.mapRequired(Kernel::Arg::Key::Size, MD.mSize); YIO.mapRequired(Kernel::Arg::Key::Align, MD.mAlign); YIO.mapRequired(Kernel::Arg::Key::ValueKind, MD.mValueKind); - YIO.mapRequired(Kernel::Arg::Key::ValueType, MD.mValueType); + + // Removed. Accepted for parsing compatibility, but not emitted. + Optional<ValueType> Unused; + YIO.mapOptional(Kernel::Arg::Key::ValueType, Unused); + YIO.mapOptional(Kernel::Arg::Key::PointeeAlign, MD.mPointeeAlign, uint32_t(0)); YIO.mapOptional(Kernel::Arg::Key::AddrSpaceQual, MD.mAddrSpaceQual, diff --git a/llvm/lib/Support/APFloat.cpp b/llvm/lib/Support/APFloat.cpp index 050c37baefb87..569cac790af99 100644 --- a/llvm/lib/Support/APFloat.cpp +++ b/llvm/lib/Support/APFloat.cpp @@ -69,6 +69,7 @@ namespace llvm { }; static const fltSemantics semIEEEhalf = {15, -14, 11, 16}; + static const fltSemantics semBFloat = {127, -126, 8, 16}; static const fltSemantics semIEEEsingle = {127, -126, 24, 32}; static const fltSemantics semIEEEdouble = {1023, -1022, 53, 64}; static const fltSemantics semIEEEquad = {16383, -16382, 113, 128}; @@ -117,6 +118,8 @@ namespace llvm { switch (S) { case S_IEEEhalf: return IEEEhalf(); + case S_BFloat: + return BFloat(); case S_IEEEsingle: return IEEEsingle(); case S_IEEEdouble: @@ -135,6 +138,8 @@ namespace llvm { APFloatBase::SemanticsToEnum(const llvm::fltSemantics &Sem) { if (&Sem == &llvm::APFloat::IEEEhalf()) return S_IEEEhalf; + else if (&Sem == &llvm::APFloat::BFloat()) + return S_BFloat; else if (&Sem == &llvm::APFloat::IEEEsingle()) return S_IEEEsingle; else if (&Sem == &llvm::APFloat::IEEEdouble()) @@ -152,6 +157,9 @@ namespace llvm { const fltSemantics &APFloatBase::IEEEhalf() { return semIEEEhalf; } + const fltSemantics &APFloatBase::BFloat() { + return semBFloat; + } const fltSemantics &APFloatBase::IEEEsingle() { return semIEEEsingle; } @@ -171,6 +179,12 @@ namespace llvm { return semPPCDoubleDouble; } + constexpr RoundingMode APFloatBase::rmNearestTiesToEven; + constexpr RoundingMode APFloatBase::rmTowardPositive; + constexpr RoundingMode APFloatBase::rmTowardNegative; + constexpr RoundingMode APFloatBase::rmTowardZero; + constexpr RoundingMode APFloatBase::rmNearestTiesToAway; + /* A tight upper bound on number of parts required to hold the value pow(5, power) is @@ -1323,6 +1337,9 @@ bool IEEEFloat::roundAwayFromZero(roundingMode rounding_mode, case rmTowardNegative: return sign; + + default: + break; } llvm_unreachable("Invalid rounding mode found"); } @@ -1439,25 +1456,26 @@ IEEEFloat::opStatus IEEEFloat::addOrSubtractSpecials(const IEEEFloat &rhs, default: llvm_unreachable(nullptr); + case PackCategoriesIntoKey(fcZero, fcNaN): + case PackCategoriesIntoKey(fcNormal, fcNaN): + case PackCategoriesIntoKey(fcInfinity, fcNaN): + assign(rhs); + LLVM_FALLTHROUGH; case PackCategoriesIntoKey(fcNaN, fcZero): case PackCategoriesIntoKey(fcNaN, fcNormal): case PackCategoriesIntoKey(fcNaN, fcInfinity): case PackCategoriesIntoKey(fcNaN, fcNaN): + if (isSignaling()) { + makeQuiet(); + return opInvalidOp; + } + return rhs.isSignaling() ? opInvalidOp : opOK; + case PackCategoriesIntoKey(fcNormal, fcZero): case PackCategoriesIntoKey(fcInfinity, fcNormal): case PackCategoriesIntoKey(fcInfinity, fcZero): return opOK; - case PackCategoriesIntoKey(fcZero, fcNaN): - case PackCategoriesIntoKey(fcNormal, fcNaN): - case PackCategoriesIntoKey(fcInfinity, fcNaN): - // We need to be sure to flip the sign here for subtraction because we - // don't have a separate negate operation so -NaN becomes 0 - NaN here. - sign = rhs.sign ^ subtract; - category = fcNaN; - copySignificand(rhs); - return opOK; - case PackCategoriesIntoKey(fcNormal, fcInfinity): case PackCategoriesIntoKey(fcZero, fcInfinity): category = fcInfinity; @@ -1562,20 +1580,22 @@ IEEEFloat::opStatus IEEEFloat::multiplySpecials(const IEEEFloat &rhs) { default: llvm_unreachable(nullptr); - case PackCategoriesIntoKey(fcNaN, fcZero): - case PackCategoriesIntoKey(fcNaN, fcNormal): - case PackCategoriesIntoKey(fcNaN, fcInfinity): - case PackCategoriesIntoKey(fcNaN, fcNaN): - sign = false; - return opOK; - case PackCategoriesIntoKey(fcZero, fcNaN): case PackCategoriesIntoKey(fcNormal, fcNaN): case PackCategoriesIntoKey(fcInfinity, fcNaN): + assign(rhs); sign = false; - category = fcNaN; - copySignificand(rhs); - return opOK; + LLVM_FALLTHROUGH; + case PackCategoriesIntoKey(fcNaN, fcZero): + case PackCategoriesIntoKey(fcNaN, fcNormal): + case PackCategoriesIntoKey(fcNaN, fcInfinity): + case PackCategoriesIntoKey(fcNaN, fcNaN): + sign ^= rhs.sign; // restore the original sign + if (isSignaling()) { + makeQuiet(); + return opInvalidOp; + } + return rhs.isSignaling() ? opInvalidOp : opOK; case PackCategoriesIntoKey(fcNormal, fcInfinity): case PackCategoriesIntoKey(fcInfinity, fcNormal): @@ -1607,15 +1627,20 @@ IEEEFloat::opStatus IEEEFloat::divideSpecials(const IEEEFloat &rhs) { case PackCategoriesIntoKey(fcZero, fcNaN): case PackCategoriesIntoKey(fcNormal, fcNaN): case PackCategoriesIntoKey(fcInfinity, fcNaN): - category = fcNaN; - copySignificand(rhs); + assign(rhs); + sign = false; LLVM_FALLTHROUGH; case PackCategoriesIntoKey(fcNaN, fcZero): case PackCategoriesIntoKey(fcNaN, fcNormal): case PackCategoriesIntoKey(fcNaN, fcInfinity): case PackCategoriesIntoKey(fcNaN, fcNaN): - sign = false; - LLVM_FALLTHROUGH; + sign ^= rhs.sign; // restore the original sign + if (isSignaling()) { + makeQuiet(); + return opInvalidOp; + } + return rhs.isSignaling() ? opInvalidOp : opOK; + case PackCategoriesIntoKey(fcInfinity, fcZero): case PackCategoriesIntoKey(fcInfinity, fcNormal): case PackCategoriesIntoKey(fcZero, fcInfinity): @@ -1645,21 +1670,62 @@ IEEEFloat::opStatus IEEEFloat::modSpecials(const IEEEFloat &rhs) { default: llvm_unreachable(nullptr); + case PackCategoriesIntoKey(fcZero, fcNaN): + case PackCategoriesIntoKey(fcNormal, fcNaN): + case PackCategoriesIntoKey(fcInfinity, fcNaN): + assign(rhs); + LLVM_FALLTHROUGH; case PackCategoriesIntoKey(fcNaN, fcZero): case PackCategoriesIntoKey(fcNaN, fcNormal): case PackCategoriesIntoKey(fcNaN, fcInfinity): case PackCategoriesIntoKey(fcNaN, fcNaN): + if (isSignaling()) { + makeQuiet(); + return opInvalidOp; + } + return rhs.isSignaling() ? opInvalidOp : opOK; + case PackCategoriesIntoKey(fcZero, fcInfinity): case PackCategoriesIntoKey(fcZero, fcNormal): case PackCategoriesIntoKey(fcNormal, fcInfinity): return opOK; + case PackCategoriesIntoKey(fcNormal, fcZero): + case PackCategoriesIntoKey(fcInfinity, fcZero): + case PackCategoriesIntoKey(fcInfinity, fcNormal): + case PackCategoriesIntoKey(fcInfinity, fcInfinity): + case PackCategoriesIntoKey(fcZero, fcZero): + makeNaN(); + return opInvalidOp; + + case PackCategoriesIntoKey(fcNormal, fcNormal): + return opOK; + } +} + +IEEEFloat::opStatus IEEEFloat::remainderSpecials(const IEEEFloat &rhs) { + switch (PackCategoriesIntoKey(category, rhs.category)) { + default: + llvm_unreachable(nullptr); + case PackCategoriesIntoKey(fcZero, fcNaN): case PackCategoriesIntoKey(fcNormal, fcNaN): case PackCategoriesIntoKey(fcInfinity, fcNaN): - sign = false; - category = fcNaN; - copySignificand(rhs); + assign(rhs); + LLVM_FALLTHROUGH; + case PackCategoriesIntoKey(fcNaN, fcZero): + case PackCategoriesIntoKey(fcNaN, fcNormal): + case PackCategoriesIntoKey(fcNaN, fcInfinity): + case PackCategoriesIntoKey(fcNaN, fcNaN): + if (isSignaling()) { + makeQuiet(); + return opInvalidOp; + } + return rhs.isSignaling() ? opInvalidOp : opOK; + + case PackCategoriesIntoKey(fcZero, fcInfinity): + case PackCategoriesIntoKey(fcZero, fcNormal): + case PackCategoriesIntoKey(fcNormal, fcInfinity): return opOK; case PackCategoriesIntoKey(fcNormal, fcZero): @@ -1671,7 +1737,7 @@ IEEEFloat::opStatus IEEEFloat::modSpecials(const IEEEFloat &rhs) { return opInvalidOp; case PackCategoriesIntoKey(fcNormal, fcNormal): - return opOK; + return opDivByZero; // fake status, indicating this is not a special case } } @@ -1759,40 +1825,108 @@ IEEEFloat::opStatus IEEEFloat::divide(const IEEEFloat &rhs, return fs; } -/* Normalized remainder. This is not currently correct in all cases. */ +/* Normalized remainder. */ IEEEFloat::opStatus IEEEFloat::remainder(const IEEEFloat &rhs) { opStatus fs; - IEEEFloat V = *this; unsigned int origSign = sign; - fs = V.divide(rhs, rmNearestTiesToEven); - if (fs == opDivByZero) + // First handle the special cases. + fs = remainderSpecials(rhs); + if (fs != opDivByZero) return fs; - int parts = partCount(); - integerPart *x = new integerPart[parts]; - bool ignored; - fs = V.convertToInteger(makeMutableArrayRef(x, parts), - parts * integerPartWidth, true, rmNearestTiesToEven, - &ignored); - if (fs == opInvalidOp) { - delete[] x; - return fs; + fs = opOK; + + // Make sure the current value is less than twice the denom. If the addition + // did not succeed (an overflow has happened), which means that the finite + // value we currently posses must be less than twice the denom (as we are + // using the same semantics). + IEEEFloat P2 = rhs; + if (P2.add(rhs, rmNearestTiesToEven) == opOK) { + fs = mod(P2); + assert(fs == opOK); } - fs = V.convertFromZeroExtendedInteger(x, parts * integerPartWidth, true, - rmNearestTiesToEven); - assert(fs==opOK); // should always work + // Lets work with absolute numbers. + IEEEFloat P = rhs; + P.sign = false; + sign = false; - fs = V.multiply(rhs, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // should not overflow or underflow + // + // To calculate the remainder we use the following scheme. + // + // The remainder is defained as follows: + // + // remainder = numer - rquot * denom = x - r * p + // + // Where r is the result of: x/p, rounded toward the nearest integral value + // (with halfway cases rounded toward the even number). + // + // Currently, (after x mod 2p): + // r is the number of 2p's present inside x, which is inherently, an even + // number of p's. + // + // We may split the remaining calculation into 4 options: + // - if x < 0.5p then we round to the nearest number with is 0, and are done. + // - if x == 0.5p then we round to the nearest even number which is 0, and we + // are done as well. + // - if 0.5p < x < p then we round to nearest number which is 1, and we have + // to subtract 1p at least once. + // - if x >= p then we must subtract p at least once, as x must be a + // remainder. + // + // By now, we were done, or we added 1 to r, which in turn, now an odd number. + // + // We can now split the remaining calculation to the following 3 options: + // - if x < 0.5p then we round to the nearest number with is 0, and are done. + // - if x == 0.5p then we round to the nearest even number. As r is odd, we + // must round up to the next even number. so we must subtract p once more. + // - if x > 0.5p (and inherently x < p) then we must round r up to the next + // integral, and subtract p once more. + // - fs = subtract(V, rmNearestTiesToEven); - assert(fs==opOK || fs==opInexact); // likewise + // Extend the semantics to prevent an overflow/underflow or inexact result. + bool losesInfo; + fltSemantics extendedSemantics = *semantics; + extendedSemantics.maxExponent++; + extendedSemantics.minExponent--; + extendedSemantics.precision += 2; + + IEEEFloat VEx = *this; + fs = VEx.convert(extendedSemantics, rmNearestTiesToEven, &losesInfo); + assert(fs == opOK && !losesInfo); + IEEEFloat PEx = P; + fs = PEx.convert(extendedSemantics, rmNearestTiesToEven, &losesInfo); + assert(fs == opOK && !losesInfo); + + // It is simpler to work with 2x instead of 0.5p, and we do not need to lose + // any fraction. + fs = VEx.add(VEx, rmNearestTiesToEven); + assert(fs == opOK); + + if (VEx.compare(PEx) == cmpGreaterThan) { + fs = subtract(P, rmNearestTiesToEven); + assert(fs == opOK); + + // Make VEx = this.add(this), but because we have different semantics, we do + // not want to `convert` again, so we just subtract PEx twice (which equals + // to the desired value). + fs = VEx.subtract(PEx, rmNearestTiesToEven); + assert(fs == opOK); + fs = VEx.subtract(PEx, rmNearestTiesToEven); + assert(fs == opOK); + + cmpResult result = VEx.compare(PEx); + if (result == cmpGreaterThan || result == cmpEqual) { + fs = subtract(P, rmNearestTiesToEven); + assert(fs == opOK); + } + } if (isZero()) sign = origSign; // IEEE754 requires this - delete[] x; + else + sign ^= origSign; return fs; } @@ -1860,14 +1994,59 @@ IEEEFloat::opStatus IEEEFloat::fusedMultiplyAdd(const IEEEFloat &multiplicand, return fs; } -/* Rounding-mode corrrect round to integral value. */ +/* Rounding-mode correct round to integral value. */ IEEEFloat::opStatus IEEEFloat::roundToIntegral(roundingMode rounding_mode) { opStatus fs; + if (isInfinity()) + // [IEEE Std 754-2008 6.1]: + // The behavior of infinity in floating-point arithmetic is derived from the + // limiting cases of real arithmetic with operands of arbitrarily + // large magnitude, when such a limit exists. + // ... + // Operations on infinite operands are usually exact and therefore signal no + // exceptions ... + return opOK; + + if (isNaN()) { + if (isSignaling()) { + // [IEEE Std 754-2008 6.2]: + // Under default exception handling, any operation signaling an invalid + // operation exception and for which a floating-point result is to be + // delivered shall deliver a quiet NaN. + makeQuiet(); + // [IEEE Std 754-2008 6.2]: + // Signaling NaNs shall be reserved operands that, under default exception + // handling, signal the invalid operation exception(see 7.2) for every + // general-computational and signaling-computational operation except for + // the conversions described in 5.12. + return opInvalidOp; + } else { + // [IEEE Std 754-2008 6.2]: + // For an operation with quiet NaN inputs, other than maximum and minimum + // operations, if a floating-point result is to be delivered the result + // shall be a quiet NaN which should be one of the input NaNs. + // ... + // Every general-computational and quiet-computational operation involving + // one or more input NaNs, none of them signaling, shall signal no + // exception, except fusedMultiplyAdd might signal the invalid operation + // exception(see 7.2). + return opOK; + } + } + + if (isZero()) { + // [IEEE Std 754-2008 6.3]: + // ... the sign of the result of conversions, the quantize operation, the + // roundToIntegral operations, and the roundToIntegralExact(see 5.3.1) is + // the sign of the first or only operand. + return opOK; + } + // If the exponent is large enough, we know that this value is already // integral, and the arithmetic below would potentially cause it to saturate // to +/-Inf. Bail out early instead. - if (isFiniteNonZero() && exponent+1 >= (int)semanticsPrecision(*semantics)) + if (exponent+1 >= (int)semanticsPrecision(*semantics)) return opOK; // The algorithm here is quite simple: we add 2^(p-1), where p is the @@ -1881,19 +2060,18 @@ IEEEFloat::opStatus IEEEFloat::roundToIntegral(roundingMode rounding_mode) { IEEEFloat MagicConstant(*semantics); fs = MagicConstant.convertFromAPInt(IntegerConstant, false, rmNearestTiesToEven); + assert(fs == opOK); MagicConstant.sign = sign; - if (fs != opOK) - return fs; - - // Preserve the input sign so that we can handle 0.0/-0.0 cases correctly. + // Preserve the input sign so that we can handle the case of zero result + // correctly. bool inputSign = isNegative(); fs = add(MagicConstant, rounding_mode); - if (fs != opOK && fs != opInexact) - return fs; - fs = subtract(MagicConstant, rounding_mode); + // Current value and 'MagicConstant' are both integers, so the result of the + // subtraction is always exact according to Sterbenz' lemma. + subtract(MagicConstant, rounding_mode); // Restore the input sign. if (inputSign != isNegative()) @@ -2621,24 +2799,70 @@ IEEEFloat::convertFromDecimalString(StringRef str, roundingMode rounding_mode) { } bool IEEEFloat::convertFromStringSpecials(StringRef str) { + const size_t MIN_NAME_SIZE = 3; + + if (str.size() < MIN_NAME_SIZE) + return false; + if (str.equals("inf") || str.equals("INFINITY") || str.equals("+Inf")) { makeInf(false); return true; } - if (str.equals("-inf") || str.equals("-INFINITY") || str.equals("-Inf")) { - makeInf(true); - return true; + bool IsNegative = str.front() == '-'; + if (IsNegative) { + str = str.drop_front(); + if (str.size() < MIN_NAME_SIZE) + return false; + + if (str.equals("inf") || str.equals("INFINITY") || str.equals("Inf")) { + makeInf(true); + return true; + } } - if (str.equals("nan") || str.equals("NaN")) { - makeNaN(false, false); - return true; + // If we have a 's' (or 'S') prefix, then this is a Signaling NaN. + bool IsSignaling = str.front() == 's' || str.front() == 'S'; + if (IsSignaling) { + str = str.drop_front(); + if (str.size() < MIN_NAME_SIZE) + return false; } - if (str.equals("-nan") || str.equals("-NaN")) { - makeNaN(false, true); - return true; + if (str.startswith("nan") || str.startswith("NaN")) { + str = str.drop_front(3); + + // A NaN without payload. + if (str.empty()) { + makeNaN(IsSignaling, IsNegative); + return true; + } + + // Allow the payload to be inside parentheses. + if (str.front() == '(') { + // Parentheses should be balanced (and not empty). + if (str.size() <= 2 || str.back() != ')') + return false; + + str = str.slice(1, str.size() - 1); + } + + // Determine the payload number's radix. + unsigned Radix = 10; + if (str[0] == '0') { + if (str.size() > 1 && tolower(str[1]) == 'x') { + str = str.drop_front(2); + Radix = 16; + } else + Radix = 8; + } + + // Parse the payload and make the NaN. + APInt Payload; + if (!str.getAsInteger(Radix, Payload)) { + makeNaN(IsSignaling, IsNegative, &Payload); + return true; + } } return false; @@ -3039,6 +3263,33 @@ APInt IEEEFloat::convertFloatAPFloatToAPInt() const { (mysignificand & 0x7fffff))); } +APInt IEEEFloat::convertBFloatAPFloatToAPInt() const { + assert(semantics == (const llvm::fltSemantics *)&semBFloat); + assert(partCount() == 1); + + uint32_t myexponent, mysignificand; + + if (isFiniteNonZero()) { + myexponent = exponent + 127; // bias + mysignificand = (uint32_t)*significandParts(); + if (myexponent == 1 && !(mysignificand & 0x80)) + myexponent = 0; // denormal + } else if (category == fcZero) { + myexponent = 0; + mysignificand = 0; + } else if (category == fcInfinity) { + myexponent = 0xff; + mysignificand = 0; + } else { + assert(category == fcNaN && "Unknown category!"); + myexponent = 0xff; + mysignificand = (uint32_t)*significandParts(); + } + + return APInt(16, (((sign & 1) << 15) | ((myexponent & 0xff) << 7) | + (mysignificand & 0x7f))); +} + APInt IEEEFloat::convertHalfAPFloatToAPInt() const { assert(semantics == (const llvm::fltSemantics*)&semIEEEhalf); assert(partCount()==1); @@ -3074,6 +3325,9 @@ APInt IEEEFloat::bitcastToAPInt() const { if (semantics == (const llvm::fltSemantics*)&semIEEEhalf) return convertHalfAPFloatToAPInt(); + if (semantics == (const llvm::fltSemantics *)&semBFloat) + return convertBFloatAPFloatToAPInt(); + if (semantics == (const llvm::fltSemantics*)&semIEEEsingle) return convertFloatAPFloatToAPInt(); @@ -3270,6 +3524,37 @@ void IEEEFloat::initFromFloatAPInt(const APInt &api) { } } +void IEEEFloat::initFromBFloatAPInt(const APInt &api) { + assert(api.getBitWidth() == 16); + uint32_t i = (uint32_t)*api.getRawData(); + uint32_t myexponent = (i >> 7) & 0xff; + uint32_t mysignificand = i & 0x7f; + + initialize(&semBFloat); + assert(partCount() == 1); + + sign = i >> 15; + if (myexponent == 0 && mysignificand == 0) { + // exponent, significand meaningless + category = fcZero; + } else if (myexponent == 0xff && mysignificand == 0) { + // exponent, significand meaningless + category = fcInfinity; + } else if (myexponent == 0xff && mysignificand != 0) { + // sign, exponent, significand meaningless + category = fcNaN; + *significandParts() = mysignificand; + } else { + category = fcNormal; + exponent = myexponent - 127; // bias + *significandParts() = mysignificand; + if (myexponent == 0) // denormal + exponent = -126; + else + *significandParts() |= 0x80; // integer bit + } +} + void IEEEFloat::initFromHalfAPInt(const APInt &api) { assert(api.getBitWidth()==16); uint32_t i = (uint32_t)*api.getRawData(); @@ -3308,6 +3593,8 @@ void IEEEFloat::initFromHalfAPInt(const APInt &api) { void IEEEFloat::initFromAPInt(const fltSemantics *Sem, const APInt &api) { if (Sem == &semIEEEhalf) return initFromHalfAPInt(api); + if (Sem == &semBFloat) + return initFromBFloatAPInt(api); if (Sem == &semIEEEsingle) return initFromFloatAPInt(api); if (Sem == &semIEEEdouble) @@ -4425,7 +4712,7 @@ 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); + Floats[0] != Floats[0] + Floats[1]); } bool DoubleAPFloat::isSmallest() const { @@ -4547,26 +4834,9 @@ APFloat::opStatus APFloat::convert(const fltSemantics &ToSemantics, llvm_unreachable("Unexpected semantics"); } -APFloat APFloat::getAllOnesValue(unsigned BitWidth, bool isIEEE) { - if (isIEEE) { - switch (BitWidth) { - case 16: - return APFloat(semIEEEhalf, APInt::getAllOnesValue(BitWidth)); - case 32: - return APFloat(semIEEEsingle, APInt::getAllOnesValue(BitWidth)); - case 64: - return APFloat(semIEEEdouble, APInt::getAllOnesValue(BitWidth)); - case 80: - return APFloat(semX87DoubleExtended, APInt::getAllOnesValue(BitWidth)); - case 128: - return APFloat(semIEEEquad, APInt::getAllOnesValue(BitWidth)); - default: - llvm_unreachable("Unknown floating bit width"); - } - } else { - assert(BitWidth == 128); - return APFloat(semPPCDoubleDouble, APInt::getAllOnesValue(BitWidth)); - } +APFloat APFloat::getAllOnesValue(const fltSemantics &Semantics, + unsigned BitWidth) { + return APFloat(Semantics, APInt::getAllOnesValue(BitWidth)); } void APFloat::print(raw_ostream &OS) const { diff --git a/llvm/lib/Support/APInt.cpp b/llvm/lib/Support/APInt.cpp index 9b9cd70078b3d..9a6f93feaa29f 100644 --- a/llvm/lib/Support/APInt.cpp +++ b/llvm/lib/Support/APInt.cpp @@ -548,9 +548,11 @@ unsigned APInt::getBitsNeeded(StringRef str, uint8_t radix) { hash_code llvm::hash_value(const APInt &Arg) { if (Arg.isSingleWord()) - return hash_combine(Arg.U.VAL); + return hash_combine(Arg.BitWidth, Arg.U.VAL); - return hash_combine_range(Arg.U.pVal, Arg.U.pVal + Arg.getNumWords()); + return hash_combine( + Arg.BitWidth, + hash_combine_range(Arg.U.pVal, Arg.U.pVal + Arg.getNumWords())); } bool APInt::isSplat(unsigned SplatSizeInBits) const { @@ -670,20 +672,16 @@ bool APInt::isSubsetOfSlowCase(const APInt &RHS) const { } APInt APInt::byteSwap() const { - assert(BitWidth >= 16 && BitWidth % 16 == 0 && "Cannot byteswap!"); + assert(BitWidth >= 16 && BitWidth % 8 == 0 && "Cannot byteswap!"); if (BitWidth == 16) return APInt(BitWidth, ByteSwap_16(uint16_t(U.VAL))); if (BitWidth == 32) return APInt(BitWidth, ByteSwap_32(unsigned(U.VAL))); - if (BitWidth == 48) { - unsigned Tmp1 = unsigned(U.VAL >> 16); - Tmp1 = ByteSwap_32(Tmp1); - uint16_t Tmp2 = uint16_t(U.VAL); - Tmp2 = ByteSwap_16(Tmp2); - return APInt(BitWidth, (uint64_t(Tmp2) << 32) | Tmp1); + if (BitWidth <= 64) { + uint64_t Tmp1 = ByteSwap_64(U.VAL); + Tmp1 >>= (64 - BitWidth); + return APInt(BitWidth, Tmp1); } - if (BitWidth == 64) - return APInt(BitWidth, ByteSwap_64(U.VAL)); APInt Result(getNumWords() * APINT_BITS_PER_WORD, 0); for (unsigned I = 0, N = getNumWords(); I != N; ++I) @@ -2283,7 +2281,7 @@ void APInt::toString(SmallVectorImpl<char> &Str, unsigned Radix, std::string APInt::toString(unsigned Radix = 10, bool Signed = true) const { SmallString<40> S; toString(S, Radix, Signed, /* formatAsCLiteral = */false); - return S.str(); + return std::string(S.str()); } #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) @@ -3088,7 +3086,8 @@ void llvm::StoreIntToMemory(const APInt &IntVal, uint8_t *Dst, /// LoadIntFromMemory - Loads the integer stored in the LoadBytes bytes starting /// from Src into IntVal, which is assumed to be wide enough and to hold zero. -void llvm::LoadIntFromMemory(APInt &IntVal, uint8_t *Src, unsigned LoadBytes) { +void llvm::LoadIntFromMemory(APInt &IntVal, const uint8_t *Src, + unsigned LoadBytes) { assert((IntVal.getBitWidth()+7)/8 >= LoadBytes && "Integer too small!"); uint8_t *Dst = reinterpret_cast<uint8_t *>( const_cast<uint64_t *>(IntVal.getRawData())); diff --git a/llvm/lib/Support/APSInt.cpp b/llvm/lib/Support/APSInt.cpp index 7c48880f96eac..b65b6824eaf8e 100644 --- a/llvm/lib/Support/APSInt.cpp +++ b/llvm/lib/Support/APSInt.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/APSInt.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/StringRef.h" +#include <cassert> using namespace llvm; @@ -25,14 +26,14 @@ APSInt::APSInt(StringRef Str) { APInt Tmp(NumBits, Str, /*radix=*/10); if (Str[0] == '-') { unsigned MinBits = Tmp.getMinSignedBits(); - if (MinBits > 0 && MinBits < NumBits) - Tmp = Tmp.trunc(MinBits); + if (MinBits < NumBits) + Tmp = Tmp.trunc(std::max<unsigned>(1, MinBits)); *this = APSInt(Tmp, /*isUnsigned=*/false); return; } unsigned ActiveBits = Tmp.getActiveBits(); - if (ActiveBits > 0 && ActiveBits < NumBits) - Tmp = Tmp.trunc(ActiveBits); + if (ActiveBits < NumBits) + Tmp = Tmp.trunc(std::max<unsigned>(1, ActiveBits)); *this = APSInt(Tmp, /*isUnsigned=*/true); } diff --git a/llvm/lib/Support/ARMAttributeParser.cpp b/llvm/lib/Support/ARMAttributeParser.cpp index 8a89f4c45fb95..17ad38d226143 100644 --- a/llvm/lib/Support/ARMAttributeParser.cpp +++ b/llvm/lib/Support/ARMAttributeParser.cpp @@ -1,4 +1,4 @@ -//===--- ARMAttributeParser.cpp - ARM Attribute Information Printer -------===// +//===- ARMAttributeParser.cpp - ARM Attribute Information Printer ---------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -9,719 +9,365 @@ #include "llvm/Support/ARMAttributeParser.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Errc.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(MVE_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) +#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(MVE_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 DecodeLength; - uint64_t Value = decodeULEB128(Data + Offset, &DecodeLength); - Offset += DecodeLength; - 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); +Error ARMAttributeParser::stringAttribute(AttrType tag) { + StringRef tagName = + ELFAttrs::attrTypeAsString(tag, tagToStringMap, /*TagPrefix=*/false); + StringRef desc = de.getCStrRef(cursor); + + if (sw) { + DictScope scope(*sw, "Attribute"); + sw->printNumber("Tag", tag); + if (!tagName.empty()) + sw->printString("TagName", tagName); + sw->printString("Value", desc); } + return Error::success(); } -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[] = { +Error ARMAttributeParser::CPU_arch(AttrType tag) { + static const char *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", nullptr, "ARM v8-M Baseline", "ARM v8-M Mainline", nullptr, nullptr, nullptr, "ARM v8.1-M Mainline" }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); + return parseStringAttribute("CPU_arch", tag, makeArrayRef(strings)); } -void ARMAttributeParser::CPU_arch_profile(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - uint64_t Encoded = ParseInteger(Data, Offset); +Error ARMAttributeParser::CPU_arch_profile(AttrType tag) { + uint64_t value = de.getULEB128(cursor); - 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; + StringRef profile; + switch (value) { + 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); + printAttribute(tag, value, profile); + return Error::success(); } -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); +Error ARMAttributeParser::ARM_ISA_use(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Permitted"}; + return parseStringAttribute("ARM_ISA_use", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::THUMB_ISA_use(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Thumb-1", "Thumb-2"}; + return parseStringAttribute("THUMB_ISA_use", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::FP_arch(AttrType tag) { + static const char *strings[] = { + "Not Permitted", "VFPv1", "VFPv2", "VFPv3", "VFPv3-D16", + "VFPv4", "VFPv4-D16", "ARMv8-a FP", "ARMv8-a FP-D16"}; + return parseStringAttribute("FP_arch", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::WMMX_arch(AttrType tag) { + static const char *strings[] = {"Not Permitted", "WMMXv1", "WMMXv2"}; + return parseStringAttribute("WMMX_arch", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::Advanced_SIMD_arch(AttrType tag) { + static const char *strings[] = {"Not Permitted", "NEONv1", "NEONv2+FMA", + "ARMv8-a NEON", "ARMv8.1-a NEON"}; + return parseStringAttribute("Advanced_SIMD_arch", tag, makeArrayRef(strings)); } -void ARMAttributeParser::MVE_arch(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { - "Not Permitted", "MVE integer", "MVE integer and float" - }; - - uint64_t Value = ParseInteger(Data, Offset); - StringRef ValueDesc = - (Value < array_lengthof(Strings)) ? Strings[Value] : nullptr; - PrintAttribute(Tag, Value, ValueDesc); +Error ARMAttributeParser::MVE_arch(AttrType tag) { + static const char *strings[] = {"Not Permitted", "MVE integer", + "MVE integer and float"}; + return parseStringAttribute("MVE_arch", tag, makeArrayRef(strings)); } -void ARMAttributeParser::PCS_config(AttrType Tag, const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { +Error ARMAttributeParser::PCS_config(AttrType tag) { + static const char *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); + "Reserved (Palm OS)", "Symbian OS 2004", "Reserved (Symbian OS)"}; + return parseStringAttribute("PCS_config", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_PCS_R9_use(AttrType tag) { + static const char *strings[] = {"v6", "Static Base", "TLS", "Unused"}; + return parseStringAttribute("ABI_PCS_R9_use", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_PCS_RW_data(AttrType tag) { + static const char *strings[] = {"Absolute", "PC-relative", "SB-relative", + "Not Permitted"}; + return parseStringAttribute("ABI_PCS_RW_data", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_PCS_RO_data(AttrType tag) { + static const char *strings[] = {"Absolute", "PC-relative", "Not Permitted"}; + return parseStringAttribute("ABI_PCS_RO_data", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_PCS_GOT_use(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Direct", "GOT-Indirect"}; + return parseStringAttribute("ABI_PCS_GOT_use", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_PCS_wchar_t(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Unknown", "2-byte", + "Unknown", "4-byte"}; + return parseStringAttribute("ABI_PCS_wchar_t", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_FP_rounding(AttrType tag) { + static const char *strings[] = {"IEEE-754", "Runtime"}; + return parseStringAttribute("ABI_FP_rounding", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_FP_denormal(AttrType tag) { + static const char *strings[] = {"Unsupported", "IEEE-754", "Sign Only"}; + return parseStringAttribute("ABI_FP_denormal", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_FP_exceptions(AttrType tag) { + static const char *strings[] = {"Not Permitted", "IEEE-754"}; + return parseStringAttribute("ABI_FP_exceptions", tag, makeArrayRef(strings)); } - -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); +Error ARMAttributeParser::ABI_FP_user_exceptions(AttrType tag) { + static const char *strings[] = {"Not Permitted", "IEEE-754"}; + return parseStringAttribute("ABI_FP_user_exceptions", tag, + makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_FP_number_model(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Finite Only", "RTABI", + "IEEE-754"}; + return parseStringAttribute("ABI_FP_number_model", tag, + makeArrayRef(strings)); } -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" - }; +Error ARMAttributeParser::ABI_align_needed(AttrType tag) { + static const char *strings[] = {"Not Permitted", "8-byte alignment", + "4-byte alignment", "Reserved"}; - uint64_t Value = ParseInteger(Data, Offset); + uint64_t value = de.getULEB128(cursor); - 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"); + std::string description; + if (value < array_lengthof(strings)) + description = strings[value]; + else if (value <= 12) + description = "8-byte alignment, " + utostr(1ULL << value) + + "-byte extended alignment"; else - Description = "Invalid"; + description = "Invalid"; - PrintAttribute(Tag, Value, Description); + printAttribute(tag, value, description); + return Error::success(); } -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" - }; +Error ARMAttributeParser::ABI_align_preserved(AttrType tag) { + static const char *strings[] = {"Not Required", "8-byte data alignment", + "8-byte data and code alignment", "Reserved"}; - uint64_t Value = ParseInteger(Data, Offset); + uint64_t value = de.getULEB128(cursor); - 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"); + 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"; + description = "Invalid"; - PrintAttribute(Tag, Value, Description); + printAttribute(tag, value, description); + return Error::success(); } -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); +Error ARMAttributeParser::ABI_enum_size(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Packed", "Int32", + "External Int32"}; + return parseStringAttribute("ABI_enum_size", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_HardFP_use(AttrType tag) { + static const char *strings[] = {"Tag_FP_arch", "Single-Precision", "Reserved", + "Tag_FP_arch (deprecated)"}; + return parseStringAttribute("ABI_HardFP_use", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_VFP_args(AttrType tag) { + static const char *strings[] = {"AAPCS", "AAPCS VFP", "Custom", + "Not Permitted"}; + return parseStringAttribute("ABI_VFP_args", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_WMMX_args(AttrType tag) { + static const char *strings[] = {"AAPCS", "iWMMX", "Custom"}; + return parseStringAttribute("ABI_WMMX_args", tag, makeArrayRef(strings)); } -void ARMAttributeParser::ABI_optimization_goals(AttrType Tag, - const uint8_t *Data, - uint32_t &Offset) { - static const char *const Strings[] = { +Error ARMAttributeParser::ABI_optimization_goals(AttrType tag) { + static const char *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) { + return parseStringAttribute("ABI_optimization_goals", tag, + makeArrayRef(strings)); +} + +Error ARMAttributeParser::ABI_FP_optimization_goals(AttrType tag) { + static const char *strings[] = { + "None", "Speed", "Aggressive Speed", "Size", "Aggressive Size", + "Accuracy", "Best Accuracy"}; + return parseStringAttribute("ABI_FP_optimization_goals", tag, + makeArrayRef(strings)); +} + +Error ARMAttributeParser::compatibility(AttrType tag) { + uint64_t integer = de.getULEB128(cursor); + StringRef string = de.getCStrRef(cursor); + + if (sw) { + DictScope scope(*sw, "Attribute"); + sw->printNumber("Tag", tag); + sw->startLine() << "Value: " << integer << ", " << string << '\n'; + sw->printString("TagName", + ELFAttrs::attrTypeAsString(tag, tagToStringMap, + /*hasTagPrefix=*/false)); + switch (integer) { case 0: - SW->printString("Description", StringRef("No Specific Requirements")); + sw->printString("Description", StringRef("No Specific Requirements")); break; case 1: - SW->printString("Description", StringRef("AEABI Conformant")); + sw->printString("Description", StringRef("AEABI Conformant")); break; default: - SW->printString("Description", StringRef("AEABI Non-Conformant")); + sw->printString("Description", StringRef("AEABI Non-Conformant")); break; } } + return Error::success(); } -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); +Error ARMAttributeParser::CPU_unaligned_access(AttrType tag) { + static const char *strings[] = {"Not Permitted", "v6-style"}; + return parseStringAttribute("CPU_unaligned_access", tag, + makeArrayRef(strings)); } -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); +Error ARMAttributeParser::FP_HP_extension(AttrType tag) { + static const char *strings[] = {"If Available", "Permitted"}; + return parseStringAttribute("FP_HP_extension", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::ABI_FP_16bit_format(AttrType tag) { + static const char *strings[] = {"Not Permitted", "IEEE-754", "VFPv3"}; + return parseStringAttribute("ABI_FP_16bit_format", tag, + makeArrayRef(strings)); } -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); +Error ARMAttributeParser::MPextension_use(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Permitted"}; + return parseStringAttribute("MPextension_use", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::DIV_use(AttrType tag) { + static const char *strings[] = {"If Available", "Not Permitted", "Permitted"}; + return parseStringAttribute("DIV_use", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::DSP_extension(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Permitted"}; + return parseStringAttribute("DSP_extension", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::T2EE_use(AttrType tag) { + static const char *strings[] = {"Not Permitted", "Permitted"}; + return parseStringAttribute("T2EE_use", tag, makeArrayRef(strings)); } -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); +Error ARMAttributeParser::Virtualization_use(AttrType tag) { + static const char *strings[] = {"Not Permitted", "TrustZone", + "Virtualization Extensions", + "TrustZone + Virtualization Extensions"}; + return parseStringAttribute("Virtualization_use", tag, makeArrayRef(strings)); } -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 DecodeLength; - uint64_t Value = decodeULEB128(Data + Offset, &DecodeLength); - Offset += DecodeLength; - if (Value == 0) - break; - IndexList.push_back(Value); - } +Error ARMAttributeParser::nodefaults(AttrType tag) { + uint64_t value = de.getULEB128(cursor); + printAttribute(tag, value, "Unspecified Tags UNDEFINED"); + return Error::success(); } -void ARMAttributeParser::ParseAttributeList(const uint8_t *Data, - uint32_t &Offset, uint32_t Length) { - while (Offset < Length) { - unsigned DecodeLength; - uint64_t Tag = decodeULEB128(Data + Offset, &DecodeLength); - Offset += DecodeLength; - - bool Handled = false; - for (unsigned AHI = 0, AHE = array_lengthof(DisplayRoutines); - AHI != AHE && !Handled; ++AHI) { - if (uint64_t(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"; +Error ARMAttributeParser::handler(uint64_t tag, bool &handled) { + handled = false; + for (unsigned AHI = 0, AHE = array_lengthof(displayRoutines); AHI != AHE; + ++AHI) { + if (uint64_t(displayRoutines[AHI].attribute) == tag) { + if (Error e = + (this->*displayRoutines[AHI].routine)(static_cast<AttrType>(tag))) + return e; + handled = true; 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" << Twine::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) { - uint64_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(); - } - if (SectionLength == 0 || (SectionLength + Offset) > Section.size()) { - errs() << "invalid subsection length " << SectionLength << " at offset " - << Offset << "\n"; - return; - } - - ParseSubsection(Section.data() + Offset, SectionLength); - Offset = Offset + SectionLength; - - if (SW) { - SW->unindent(); - SW->startLine() << "}\n"; - } - } -} + return Error::success(); } diff --git a/llvm/lib/Support/ARMBuildAttrs.cpp b/llvm/lib/Support/ARMBuildAttrs.cpp index d0c4fb792cb8c..5aaf0a4e7c62e 100644 --- a/llvm/lib/Support/ARMBuildAttrs.cpp +++ b/llvm/lib/Support/ARMBuildAttrs.cpp @@ -6,97 +6,63 @@ // //===----------------------------------------------------------------------===// -#include "llvm/ADT/StringRef.h" #include "llvm/Support/ARMBuildAttributes.h" using namespace llvm; -namespace { -const struct { - ARMBuildAttrs::AttrType Attr; - StringRef TagName; -} ARMAttributeTags[] = { - { ARMBuildAttrs::File, "Tag_File" }, - { ARMBuildAttrs::Section, "Tag_Section" }, - { ARMBuildAttrs::Symbol, "Tag_Symbol" }, - { ARMBuildAttrs::CPU_raw_name, "Tag_CPU_raw_name" }, - { ARMBuildAttrs::CPU_name, "Tag_CPU_name" }, - { ARMBuildAttrs::CPU_arch, "Tag_CPU_arch" }, - { ARMBuildAttrs::CPU_arch_profile, "Tag_CPU_arch_profile" }, - { ARMBuildAttrs::ARM_ISA_use, "Tag_ARM_ISA_use" }, - { ARMBuildAttrs::THUMB_ISA_use, "Tag_THUMB_ISA_use" }, - { ARMBuildAttrs::FP_arch, "Tag_FP_arch" }, - { ARMBuildAttrs::WMMX_arch, "Tag_WMMX_arch" }, - { ARMBuildAttrs::Advanced_SIMD_arch, "Tag_Advanced_SIMD_arch" }, - { ARMBuildAttrs::MVE_arch, "Tag_MVE_arch" }, - { ARMBuildAttrs::PCS_config, "Tag_PCS_config" }, - { ARMBuildAttrs::ABI_PCS_R9_use, "Tag_ABI_PCS_R9_use" }, - { ARMBuildAttrs::ABI_PCS_RW_data, "Tag_ABI_PCS_RW_data" }, - { ARMBuildAttrs::ABI_PCS_RO_data, "Tag_ABI_PCS_RO_data" }, - { ARMBuildAttrs::ABI_PCS_GOT_use, "Tag_ABI_PCS_GOT_use" }, - { ARMBuildAttrs::ABI_PCS_wchar_t, "Tag_ABI_PCS_wchar_t" }, - { ARMBuildAttrs::ABI_FP_rounding, "Tag_ABI_FP_rounding" }, - { ARMBuildAttrs::ABI_FP_denormal, "Tag_ABI_FP_denormal" }, - { ARMBuildAttrs::ABI_FP_exceptions, "Tag_ABI_FP_exceptions" }, - { ARMBuildAttrs::ABI_FP_user_exceptions, "Tag_ABI_FP_user_exceptions" }, - { ARMBuildAttrs::ABI_FP_number_model, "Tag_ABI_FP_number_model" }, - { ARMBuildAttrs::ABI_align_needed, "Tag_ABI_align_needed" }, - { ARMBuildAttrs::ABI_align_preserved, "Tag_ABI_align_preserved" }, - { ARMBuildAttrs::ABI_enum_size, "Tag_ABI_enum_size" }, - { ARMBuildAttrs::ABI_HardFP_use, "Tag_ABI_HardFP_use" }, - { ARMBuildAttrs::ABI_VFP_args, "Tag_ABI_VFP_args" }, - { ARMBuildAttrs::ABI_WMMX_args, "Tag_ABI_WMMX_args" }, - { ARMBuildAttrs::ABI_optimization_goals, "Tag_ABI_optimization_goals" }, - { ARMBuildAttrs::ABI_FP_optimization_goals, "Tag_ABI_FP_optimization_goals" }, - { ARMBuildAttrs::compatibility, "Tag_compatibility" }, - { ARMBuildAttrs::CPU_unaligned_access, "Tag_CPU_unaligned_access" }, - { ARMBuildAttrs::FP_HP_extension, "Tag_FP_HP_extension" }, - { ARMBuildAttrs::ABI_FP_16bit_format, "Tag_ABI_FP_16bit_format" }, - { ARMBuildAttrs::MPextension_use, "Tag_MPextension_use" }, - { ARMBuildAttrs::DIV_use, "Tag_DIV_use" }, - { ARMBuildAttrs::DSP_extension, "Tag_DSP_extension" }, - { ARMBuildAttrs::nodefaults, "Tag_nodefaults" }, - { ARMBuildAttrs::also_compatible_with, "Tag_also_compatible_with" }, - { ARMBuildAttrs::T2EE_use, "Tag_T2EE_use" }, - { ARMBuildAttrs::conformance, "Tag_conformance" }, - { ARMBuildAttrs::Virtualization_use, "Tag_Virtualization_use" }, +static const TagNameItem tagData[] = { + {ARMBuildAttrs::File, "Tag_File"}, + {ARMBuildAttrs::Section, "Tag_Section"}, + {ARMBuildAttrs::Symbol, "Tag_Symbol"}, + {ARMBuildAttrs::CPU_raw_name, "Tag_CPU_raw_name"}, + {ARMBuildAttrs::CPU_name, "Tag_CPU_name"}, + {ARMBuildAttrs::CPU_arch, "Tag_CPU_arch"}, + {ARMBuildAttrs::CPU_arch_profile, "Tag_CPU_arch_profile"}, + {ARMBuildAttrs::ARM_ISA_use, "Tag_ARM_ISA_use"}, + {ARMBuildAttrs::THUMB_ISA_use, "Tag_THUMB_ISA_use"}, + {ARMBuildAttrs::FP_arch, "Tag_FP_arch"}, + {ARMBuildAttrs::WMMX_arch, "Tag_WMMX_arch"}, + {ARMBuildAttrs::Advanced_SIMD_arch, "Tag_Advanced_SIMD_arch"}, + {ARMBuildAttrs::MVE_arch, "Tag_MVE_arch"}, + {ARMBuildAttrs::PCS_config, "Tag_PCS_config"}, + {ARMBuildAttrs::ABI_PCS_R9_use, "Tag_ABI_PCS_R9_use"}, + {ARMBuildAttrs::ABI_PCS_RW_data, "Tag_ABI_PCS_RW_data"}, + {ARMBuildAttrs::ABI_PCS_RO_data, "Tag_ABI_PCS_RO_data"}, + {ARMBuildAttrs::ABI_PCS_GOT_use, "Tag_ABI_PCS_GOT_use"}, + {ARMBuildAttrs::ABI_PCS_wchar_t, "Tag_ABI_PCS_wchar_t"}, + {ARMBuildAttrs::ABI_FP_rounding, "Tag_ABI_FP_rounding"}, + {ARMBuildAttrs::ABI_FP_denormal, "Tag_ABI_FP_denormal"}, + {ARMBuildAttrs::ABI_FP_exceptions, "Tag_ABI_FP_exceptions"}, + {ARMBuildAttrs::ABI_FP_user_exceptions, "Tag_ABI_FP_user_exceptions"}, + {ARMBuildAttrs::ABI_FP_number_model, "Tag_ABI_FP_number_model"}, + {ARMBuildAttrs::ABI_align_needed, "Tag_ABI_align_needed"}, + {ARMBuildAttrs::ABI_align_preserved, "Tag_ABI_align_preserved"}, + {ARMBuildAttrs::ABI_enum_size, "Tag_ABI_enum_size"}, + {ARMBuildAttrs::ABI_HardFP_use, "Tag_ABI_HardFP_use"}, + {ARMBuildAttrs::ABI_VFP_args, "Tag_ABI_VFP_args"}, + {ARMBuildAttrs::ABI_WMMX_args, "Tag_ABI_WMMX_args"}, + {ARMBuildAttrs::ABI_optimization_goals, "Tag_ABI_optimization_goals"}, + {ARMBuildAttrs::ABI_FP_optimization_goals, "Tag_ABI_FP_optimization_goals"}, + {ARMBuildAttrs::compatibility, "Tag_compatibility"}, + {ARMBuildAttrs::CPU_unaligned_access, "Tag_CPU_unaligned_access"}, + {ARMBuildAttrs::FP_HP_extension, "Tag_FP_HP_extension"}, + {ARMBuildAttrs::ABI_FP_16bit_format, "Tag_ABI_FP_16bit_format"}, + {ARMBuildAttrs::MPextension_use, "Tag_MPextension_use"}, + {ARMBuildAttrs::DIV_use, "Tag_DIV_use"}, + {ARMBuildAttrs::DSP_extension, "Tag_DSP_extension"}, + {ARMBuildAttrs::nodefaults, "Tag_nodefaults"}, + {ARMBuildAttrs::also_compatible_with, "Tag_also_compatible_with"}, + {ARMBuildAttrs::T2EE_use, "Tag_T2EE_use"}, + {ARMBuildAttrs::conformance, "Tag_conformance"}, + {ARMBuildAttrs::Virtualization_use, "Tag_Virtualization_use"}, - // Legacy Names - { ARMBuildAttrs::FP_arch, "Tag_VFP_arch" }, - { ARMBuildAttrs::FP_HP_extension, "Tag_VFP_HP_extension" }, - { ARMBuildAttrs::ABI_align_needed, "Tag_ABI_align8_needed" }, - { ARMBuildAttrs::ABI_align_preserved, "Tag_ABI_align8_preserved" }, + // Legacy Names + {ARMBuildAttrs::FP_arch, "Tag_VFP_arch"}, + {ARMBuildAttrs::FP_HP_extension, "Tag_VFP_HP_extension"}, + {ARMBuildAttrs::ABI_align_needed, "Tag_ABI_align8_needed"}, + {ARMBuildAttrs::ABI_align_preserved, "Tag_ABI_align8_preserved"}, }; -} - -namespace llvm { -namespace ARMBuildAttrs { -StringRef AttrTypeAsString(unsigned Attr, bool HasTagPrefix) { - return AttrTypeAsString(static_cast<AttrType>(Attr), HasTagPrefix); -} - -StringRef AttrTypeAsString(AttrType Attr, bool HasTagPrefix) { - for (unsigned TI = 0, TE = sizeof(ARMAttributeTags) / sizeof(*ARMAttributeTags); - TI != TE; ++TI) - if (ARMAttributeTags[TI].Attr == Attr) { - auto TagName = ARMAttributeTags[TI].TagName; - return HasTagPrefix ? TagName : TagName.drop_front(4); - } - return ""; -} - -int AttrTypeFromString(StringRef Tag) { - bool HasTagPrefix = Tag.startswith("Tag_"); - for (unsigned TI = 0, - TE = sizeof(ARMAttributeTags) / sizeof(*ARMAttributeTags); - TI != TE; ++TI) { - auto TagName = ARMAttributeTags[TI].TagName; - if (TagName.drop_front(HasTagPrefix ? 0 : 4) == Tag) { - return ARMAttributeTags[TI].Attr; - } - } - return -1; -} -} -} +const TagNameMap llvm::ARMBuildAttrs::ARMAttributeTags(tagData, + sizeof(tagData) / + sizeof(TagNameItem)); diff --git a/llvm/lib/Support/ARMTargetParser.cpp b/llvm/lib/Support/ARMTargetParser.cpp index f2c22fd93c8b8..56a91f7dc7876 100644 --- a/llvm/lib/Support/ARMTargetParser.cpp +++ b/llvm/lib/Support/ARMTargetParser.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/ARMTargetParser.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" #include <cctype> using namespace llvm; @@ -27,7 +28,7 @@ static StringRef getHWDivSynonym(StringRef HWDiv) { ARM::ArchKind ARM::parseArch(StringRef Arch) { Arch = getCanonicalArchName(Arch); StringRef Syn = getArchSynonym(Arch); - for (const auto A : ARCHNames) { + for (const auto &A : ARCHNames) { if (A.getName().endswith(Syn)) return A.ID; } @@ -74,6 +75,7 @@ unsigned ARM::parseArchVersion(StringRef Arch) { case ArchKind::ARMV8_3A: case ArchKind::ARMV8_4A: case ArchKind::ARMV8_5A: + case ArchKind::ARMV8_6A: case ArchKind::ARMV8R: case ArchKind::ARMV8MBaseline: case ArchKind::ARMV8MMainline: @@ -108,6 +110,7 @@ ARM::ProfileKind ARM::parseArchProfile(StringRef Arch) { case ArchKind::ARMV8_3A: case ArchKind::ARMV8_4A: case ArchKind::ARMV8_5A: + case ArchKind::ARMV8_6A: return ProfileKind::A; case ArchKind::ARMV2: case ArchKind::ARMV2A: @@ -150,6 +153,7 @@ StringRef ARM::getArchSynonym(StringRef Arch) { .Case("v8.3a", "v8.3-a") .Case("v8.4a", "v8.4-a") .Case("v8.5a", "v8.5-a") + .Case("v8.6a", "v8.6-a") .Case("v8r", "v8-r") .Case("v8m.base", "v8-m.base") .Case("v8m.main", "v8-m.main") @@ -367,11 +371,11 @@ unsigned ARM::getDefaultFPU(StringRef CPU, ARM::ArchKind AK) { .Default(ARM::FK_INVALID); } -unsigned ARM::getDefaultExtensions(StringRef CPU, ARM::ArchKind AK) { +uint64_t ARM::getDefaultExtensions(StringRef CPU, ARM::ArchKind AK) { if (CPU == "generic") return ARM::ARCHNames[static_cast<unsigned>(AK)].ArchBaseExtensions; - return StringSwitch<unsigned>(CPU) + return StringSwitch<uint64_t>(CPU) #define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ .Case(NAME, \ ARCHNames[static_cast<unsigned>(ArchKind::ID)].ArchBaseExtensions | \ @@ -380,7 +384,7 @@ unsigned ARM::getDefaultExtensions(StringRef CPU, ARM::ArchKind AK) { .Default(ARM::AEK_INVALID); } -bool ARM::getHWDivFeatures(unsigned HWDivKind, +bool ARM::getHWDivFeatures(uint64_t HWDivKind, std::vector<StringRef> &Features) { if (HWDivKind == AEK_INVALID) @@ -399,7 +403,7 @@ bool ARM::getHWDivFeatures(unsigned HWDivKind, return true; } -bool ARM::getExtensionFeatures(unsigned Extensions, +bool ARM::getExtensionFeatures(uint64_t Extensions, std::vector<StringRef> &Features) { if (Extensions == AEK_INVALID) @@ -431,7 +435,7 @@ unsigned ARM::getArchAttr(ARM::ArchKind AK) { return ARCHNames[static_cast<unsigned>(AK)].ArchAttr; } -StringRef ARM::getArchExtName(unsigned ArchExtKind) { +StringRef ARM::getArchExtName(uint64_t ArchExtKind) { for (const auto AE : ARCHExtNames) { if (ArchExtKind == AE.ID) return AE.getName(); @@ -486,29 +490,25 @@ static unsigned findDoublePrecisionFPU(unsigned InputFPUKind) { return ARM::FK_INVALID; } -static unsigned getAEKID(StringRef ArchExtName) { - for (const auto AE : ARM::ARCHExtNames) - if (AE.getName() == ArchExtName) - return AE.ID; - return ARM::AEK_INVALID; -} - bool ARM::appendArchExtFeatures( StringRef CPU, ARM::ArchKind AK, StringRef ArchExt, std::vector<StringRef> &Features) { size_t StartingNumFeatures = Features.size(); const bool Negated = stripNegationPrefix(ArchExt); - unsigned ID = getAEKID(ArchExt); + uint64_t ID = parseArchExt(ArchExt); if (ID == AEK_INVALID) return false; for (const auto AE : ARCHExtNames) { - if (Negated && (AE.ID & ID) == ID && AE.NegFeature) - Features.push_back(AE.NegFeature); - else if (AE.ID == ID && AE.Feature) - Features.push_back(AE.Feature); + if (Negated) { + if ((AE.ID & ID) == ID && AE.NegFeature) + Features.push_back(AE.NegFeature); + } else { + if ((AE.ID & ID) == AE.ID && AE.Feature) + Features.push_back(AE.Feature); + } } if (CPU == "") @@ -532,7 +532,7 @@ bool ARM::appendArchExtFeatures( return StartingNumFeatures != Features.size(); } -StringRef ARM::getHWDivName(unsigned HWDivKind) { +StringRef ARM::getHWDivName(uint64_t HWDivKind) { for (const auto D : HWDivNames) { if (HWDivKind == D.ID) return D.getName(); @@ -555,7 +555,7 @@ StringRef ARM::getDefaultCPU(StringRef Arch) { return "generic"; } -unsigned ARM::parseHWDiv(StringRef HWDiv) { +uint64_t ARM::parseHWDiv(StringRef HWDiv) { StringRef Syn = getHWDivSynonym(HWDiv); for (const auto D : HWDivNames) { if (Syn == D.getName()) @@ -564,7 +564,7 @@ unsigned ARM::parseHWDiv(StringRef HWDiv) { return AEK_INVALID; } -unsigned ARM::parseArchExt(StringRef ArchExt) { +uint64_t ARM::parseArchExt(StringRef ArchExt) { for (const auto A : ARCHExtNames) { if (ArchExt == A.getName()) return A.ID; diff --git a/llvm/lib/Support/BranchProbability.cpp b/llvm/lib/Support/BranchProbability.cpp index 195e2d58d8e19..60d5478a90529 100644 --- a/llvm/lib/Support/BranchProbability.cpp +++ b/llvm/lib/Support/BranchProbability.cpp @@ -19,7 +19,7 @@ using namespace llvm; -const uint32_t BranchProbability::D; +constexpr uint32_t BranchProbability::D; raw_ostream &BranchProbability::print(raw_ostream &OS) const { if (isUnknown()) diff --git a/llvm/lib/Support/CRC.cpp b/llvm/lib/Support/CRC.cpp index a3dba1a3aa10a..7ff09debe3b7c 100644 --- a/llvm/lib/Support/CRC.cpp +++ b/llvm/lib/Support/CRC.cpp @@ -25,7 +25,7 @@ using namespace llvm; -#if !LLVM_ENABLE_ZLIB +#if LLVM_ENABLE_ZLIB == 0 || !HAVE_ZLIB_H static const uint32_t CRCTable[256] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, @@ -85,7 +85,15 @@ uint32_t llvm::crc32(uint32_t CRC, ArrayRef<uint8_t> Data) { #include <zlib.h> uint32_t llvm::crc32(uint32_t CRC, ArrayRef<uint8_t> Data) { - return ::crc32(CRC, (const Bytef *)Data.data(), Data.size()); + // Zlib's crc32() only takes a 32-bit length, so we have to iterate for larger + // sizes. One could use crc32_z() instead, but that's a recent (2017) addition + // and may not be available on all systems. + do { + ArrayRef<uint8_t> Slice = Data.take_front(UINT32_MAX); + CRC = ::crc32(CRC, (const Bytef *)Slice.data(), (uInt)Slice.size()); + Data = Data.drop_front(Slice.size()); + } while (Data.size() > 0); + return CRC; } #endif diff --git a/llvm/lib/Support/CachePruning.cpp b/llvm/lib/Support/CachePruning.cpp index 7a2f6c53435ad..7663644db558c 100644 --- a/llvm/lib/Support/CachePruning.cpp +++ b/llvm/lib/Support/CachePruning.cpp @@ -11,7 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/CachePruning.h" - +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" diff --git a/llvm/lib/Support/CodeGenCoverage.cpp b/llvm/lib/Support/CodeGenCoverage.cpp index 2db4193ce3825..93f386b6e23dc 100644 --- a/llvm/lib/Support/CodeGenCoverage.cpp +++ b/llvm/lib/Support/CodeGenCoverage.cpp @@ -11,20 +11,14 @@ #include "llvm/Support/CodeGenCoverage.h" -#include "llvm/Config/llvm-config.h" #include "llvm/Support/Endian.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Mutex.h" +#include "llvm/Support/Process.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/ToolOutputFile.h" -#if LLVM_ON_UNIX -#include <unistd.h> -#elif defined(_WIN32) -#include <windows.h> -#endif - using namespace llvm; static sys::SmartMutex<true> OutputMutex; @@ -89,14 +83,7 @@ bool CodeGenCoverage::emit(StringRef CoveragePrefix, // We can handle locking within a process easily enough but we don't want to // manage it between multiple processes. Use the process ID to ensure no // more than one process is ever writing to the same file at the same time. - std::string Pid = -#if LLVM_ON_UNIX - llvm::to_string(::getpid()); -#elif defined(_WIN32) - llvm::to_string(::GetCurrentProcessId()); -#else - ""; -#endif + std::string Pid = llvm::to_string(sys::Process::getProcessId()); std::string CoverageFilename = (CoveragePrefix + Pid).str(); diff --git a/llvm/lib/Support/CommandLine.cpp b/llvm/lib/Support/CommandLine.cpp index cb73380ba3830..12ef0d511b147 100644 --- a/llvm/lib/Support/CommandLine.cpp +++ b/llvm/lib/Support/CommandLine.cpp @@ -592,6 +592,10 @@ static Option *LookupNearestOption(StringRef Arg, ie = OptionsMap.end(); it != ie; ++it) { Option *O = it->second; + // Do not suggest really hidden options (not shown in any help). + if (O->getOptionHiddenFlag() == ReallyHidden) + continue; + SmallVector<StringRef, 16> OptionNames; O->getExtraOptionNames(OptionNames); if (O->hasArgStr()) @@ -606,7 +610,7 @@ static Option *LookupNearestOption(StringRef Arg, Best = O; BestDistance = Distance; if (RHS.empty() || !PermitValue) - NearestString = Name; + NearestString = std::string(Name); else NearestString = (Twine(Name) + "=" + RHS).str(); } @@ -919,91 +923,118 @@ static size_t parseBackslash(StringRef Src, size_t I, SmallString<128> &Token) { return I - 1; } -void cl::TokenizeWindowsCommandLine(StringRef Src, StringSaver &Saver, - SmallVectorImpl<const char *> &NewArgv, - bool MarkEOLs) { +// Windows treats whitespace, double quotes, and backslashes specially. +static bool isWindowsSpecialChar(char C) { + return isWhitespaceOrNull(C) || C == '\\' || C == '\"'; +} + +// Windows tokenization implementation. The implementation is designed to be +// inlined and specialized for the two user entry points. +static inline void +tokenizeWindowsCommandLineImpl(StringRef Src, StringSaver &Saver, + function_ref<void(StringRef)> AddToken, + bool AlwaysCopy, function_ref<void()> MarkEOL) { SmallString<128> Token; - // This is a small state machine to consume characters until it reaches the - // end of the source string. + // Try to do as much work inside the state machine as possible. enum { INIT, UNQUOTED, QUOTED } State = INIT; - for (size_t I = 0, E = Src.size(); I != E; ++I) { - char C = Src[I]; - - // INIT state indicates that the current input index is at the start of - // the string or between tokens. - if (State == INIT) { - if (isWhitespaceOrNull(C)) { - // Mark the end of lines in response files - if (MarkEOLs && C == '\n') - NewArgv.push_back(nullptr); - continue; + for (size_t I = 0, E = Src.size(); I < E; ++I) { + switch (State) { + case INIT: { + assert(Token.empty() && "token should be empty in initial state"); + // Eat whitespace before a token. + while (I < E && isWhitespaceOrNull(Src[I])) { + if (Src[I] == '\n') + MarkEOL(); + ++I; } - if (C == '"') { + // Stop if this was trailing whitespace. + if (I >= E) + break; + size_t Start = I; + while (I < E && !isWindowsSpecialChar(Src[I])) + ++I; + StringRef NormalChars = Src.slice(Start, I); + if (I >= E || isWhitespaceOrNull(Src[I])) { + if (I < E && Src[I] == '\n') + MarkEOL(); + // No special characters: slice out the substring and start the next + // token. Copy the string if the caller asks us to. + AddToken(AlwaysCopy ? Saver.save(NormalChars) : NormalChars); + } else if (Src[I] == '\"') { + Token += NormalChars; State = QUOTED; - continue; - } - if (C == '\\') { + } else if (Src[I] == '\\') { + Token += NormalChars; I = parseBackslash(Src, I, Token); State = UNQUOTED; - continue; + } else { + llvm_unreachable("unexpected special character"); } - Token.push_back(C); - State = UNQUOTED; - continue; + break; } - // UNQUOTED state means that it's reading a token not quoted by double - // quotes. - if (State == UNQUOTED) { - // Whitespace means the end of the token. - if (isWhitespaceOrNull(C)) { - NewArgv.push_back(Saver.save(StringRef(Token)).data()); + case UNQUOTED: + if (isWhitespaceOrNull(Src[I])) { + // Whitespace means the end of the token. If we are in this state, the + // token must have contained a special character, so we must copy the + // token. + AddToken(Saver.save(Token.str())); Token.clear(); + if (Src[I] == '\n') + MarkEOL(); State = INIT; - // Mark the end of lines in response files - if (MarkEOLs && C == '\n') - NewArgv.push_back(nullptr); - continue; - } - if (C == '"') { + } else if (Src[I] == '\"') { State = QUOTED; - continue; - } - if (C == '\\') { + } else if (Src[I] == '\\') { I = parseBackslash(Src, I, Token); - continue; + } else { + Token.push_back(Src[I]); } - Token.push_back(C); - continue; - } + break; - // QUOTED state means that it's reading a token quoted by double quotes. - if (State == QUOTED) { - if (C == '"') { + case QUOTED: + if (Src[I] == '\"') { if (I < (E - 1) && Src[I + 1] == '"') { // Consecutive double-quotes inside a quoted string implies one // double-quote. Token.push_back('"'); - I = I + 1; - continue; + ++I; + } else { + // Otherwise, end the quoted portion and return to the unquoted state. + State = UNQUOTED; } - State = UNQUOTED; - continue; - } - if (C == '\\') { + } else if (Src[I] == '\\') { I = parseBackslash(Src, I, Token); - continue; + } else { + Token.push_back(Src[I]); } - Token.push_back(C); + break; } } - // Append the last token after hitting EOF with no whitespace. - if (!Token.empty()) - NewArgv.push_back(Saver.save(StringRef(Token)).data()); - // Mark the end of response files - if (MarkEOLs) - NewArgv.push_back(nullptr); + + if (State == UNQUOTED) + AddToken(Saver.save(Token.str())); +} + +void cl::TokenizeWindowsCommandLine(StringRef Src, StringSaver &Saver, + SmallVectorImpl<const char *> &NewArgv, + bool MarkEOLs) { + auto AddToken = [&](StringRef Tok) { NewArgv.push_back(Tok.data()); }; + auto OnEOL = [&]() { + if (MarkEOLs) + NewArgv.push_back(nullptr); + }; + tokenizeWindowsCommandLineImpl(Src, Saver, AddToken, + /*AlwaysCopy=*/true, OnEOL); +} + +void cl::TokenizeWindowsCommandLineNoCopy(StringRef Src, StringSaver &Saver, + SmallVectorImpl<StringRef> &NewArgv) { + auto AddToken = [&](StringRef Tok) { NewArgv.push_back(Tok); }; + auto OnEOL = []() {}; + tokenizeWindowsCommandLineImpl(Src, Saver, AddToken, /*AlwaysCopy=*/false, + OnEOL); } void cl::tokenizeConfigFile(StringRef Source, StringSaver &Saver, @@ -1324,7 +1355,7 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, argc = static_cast<int>(newArgv.size()); // Copy the program name into ProgName, making sure not to overflow it. - ProgramName = sys::path::filename(StringRef(argv[0])); + ProgramName = std::string(sys::path::filename(StringRef(argv[0]))); ProgramOverview = Overview; bool IgnoreErrors = Errs; @@ -1581,9 +1612,9 @@ bool CommandLineParser::ParseCommandLineOptions(int argc, } else { assert(ConsumeAfterOpt && NumPositionalRequired <= PositionalVals.size()); unsigned ValNo = 0; - for (size_t j = 1, e = PositionalOpts.size(); j != e; ++j) - if (RequiresValue(PositionalOpts[j])) { - ErrorParsing |= ProvidePositionalOption(PositionalOpts[j], + for (size_t J = 0, E = PositionalOpts.size(); J != E; ++J) + if (RequiresValue(PositionalOpts[J])) { + ErrorParsing |= ProvidePositionalOption(PositionalOpts[J], PositionalVals[ValNo].first, PositionalVals[ValNo].second); ValNo++; @@ -1751,9 +1782,10 @@ void basic_parser_impl::printOptionInfo(const Option &O, if (!ValName.empty()) { if (O.getMiscFlags() & PositionalEatsArgs) { outs() << " <" << getValueStr(O, ValName) << ">..."; - } else { + } else if (O.getValueExpectedFlag() == ValueOptional) + outs() << "[=<" << getValueStr(O, ValName) << ">]"; + else outs() << "=<" << getValueStr(O, ValName) << '>'; - } } Option::printHelpStr(O.HelpStr, GlobalWidth, getOptionWidth(O)); @@ -2482,7 +2514,7 @@ public: OS << " with assertions"; #endif #if LLVM_VERSION_PRINTER_SHOW_HOST_TARGET_INFO - std::string CPU = sys::getHostCPUName(); + std::string CPU = std::string(sys::getHostCPUName()); if (CPU == "generic") CPU = "(unknown)"; OS << ".\n" @@ -2505,7 +2537,7 @@ public: // information. if (ExtraVersionPrinters != nullptr) { outs() << '\n'; - for (auto I : *ExtraVersionPrinters) + for (const auto &I : *ExtraVersionPrinters) I(outs()); } diff --git a/llvm/lib/Support/Compression.cpp b/llvm/lib/Support/Compression.cpp index 4165a2740cd03..27d92f0e0aec2 100644 --- a/llvm/lib/Support/Compression.cpp +++ b/llvm/lib/Support/Compression.cpp @@ -17,13 +17,13 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" -#if LLVM_ENABLE_ZLIB +#if LLVM_ENABLE_ZLIB == 1 && HAVE_ZLIB_H #include <zlib.h> #endif using namespace llvm; -#if LLVM_ENABLE_ZLIB +#if LLVM_ENABLE_ZLIB == 1 && HAVE_LIBZ static Error createError(StringRef Err) { return make_error<StringError>(Err, inconvertibleErrorCode()); } @@ -74,10 +74,10 @@ Error zlib::uncompress(StringRef InputBuffer, char *UncompressedBuffer, Error zlib::uncompress(StringRef InputBuffer, SmallVectorImpl<char> &UncompressedBuffer, size_t UncompressedSize) { - UncompressedBuffer.resize(UncompressedSize); + UncompressedBuffer.reserve(UncompressedSize); Error E = uncompress(InputBuffer, UncompressedBuffer.data(), UncompressedSize); - UncompressedBuffer.resize(UncompressedSize); + UncompressedBuffer.set_size(UncompressedSize); return E; } diff --git a/llvm/lib/Support/ConvertUTFWrapper.cpp b/llvm/lib/Support/ConvertUTFWrapper.cpp index eb4ead6b46b45..6ec567882ea6b 100644 --- a/llvm/lib/Support/ConvertUTFWrapper.cpp +++ b/llvm/lib/Support/ConvertUTFWrapper.cpp @@ -102,7 +102,7 @@ bool convertUTF16ToUTF8String(ArrayRef<char> SrcBytes, std::string &Out) { if (Src[0] == UNI_UTF16_BYTE_ORDER_MARK_SWAPPED) { ByteSwapped.insert(ByteSwapped.end(), Src, SrcEnd); for (unsigned I = 0, E = ByteSwapped.size(); I != E; ++I) - ByteSwapped[I] = llvm::sys::SwapByteOrder_16(ByteSwapped[I]); + ByteSwapped[I] = llvm::ByteSwap_16(ByteSwapped[I]); Src = &ByteSwapped[0]; SrcEnd = &ByteSwapped[ByteSwapped.size() - 1] + 1; } diff --git a/llvm/lib/Support/CrashRecoveryContext.cpp b/llvm/lib/Support/CrashRecoveryContext.cpp index b9031f52375c3..ec7d7d641dce5 100644 --- a/llvm/lib/Support/CrashRecoveryContext.cpp +++ b/llvm/lib/Support/CrashRecoveryContext.cpp @@ -14,9 +14,6 @@ #include "llvm/Support/ThreadLocal.h" #include <mutex> #include <setjmp.h> -#ifdef _WIN32 -#include <excpt.h> // for GetExceptionInformation -#endif #if LLVM_ON_UNIX #include <sysexits.h> // EX_IOERR #endif @@ -41,11 +38,11 @@ struct CrashRecoveryContextImpl { ::jmp_buf JumpBuffer; volatile unsigned Failed : 1; unsigned SwitchedThread : 1; + unsigned ValidJumpBuffer : 1; public: - CrashRecoveryContextImpl(CrashRecoveryContext *CRC) : CRC(CRC), - Failed(false), - SwitchedThread(false) { + CrashRecoveryContextImpl(CrashRecoveryContext *CRC) noexcept + : CRC(CRC), Failed(false), SwitchedThread(false), ValidJumpBuffer(false) { Next = CurrentContext->get(); CurrentContext->set(this); } @@ -80,10 +77,13 @@ public: CRC->RetCode = RetCode; // Jump back to the RunSafely we were called under. - longjmp(JumpBuffer, 1); + if (ValidJumpBuffer) + longjmp(JumpBuffer, 1); + + // Otherwise let the caller decide of the outcome of the crash. Currently + // this occurs when using SEH on Windows with MSVC or clang-cl. } }; - } static ManagedStatic<std::mutex> gCrashRecoveryContextMutex; @@ -175,6 +175,9 @@ CrashRecoveryContext::unregisterCleanup(CrashRecoveryContextCleanup *cleanup) { } #if defined(_MSC_VER) + +#include <windows.h> // for GetExceptionInformation + // If _MSC_VER is defined, we must have SEH. Use it if it's available. It's way // better than VEH. Vectored exception handling catches all exceptions happening // on the thread with installed exception handlers, so it can interfere with @@ -188,30 +191,45 @@ static void uninstallExceptionOrSignalHandlers() {} // We need this function because the call to GetExceptionInformation() can only // occur inside the __except evaluation block -static int ExceptionFilter(bool DumpStackAndCleanup, - _EXCEPTION_POINTERS *Except) { - if (DumpStackAndCleanup) - sys::CleanupOnSignal((uintptr_t)Except); - return EXCEPTION_EXECUTE_HANDLER; -} +static int ExceptionFilter(_EXCEPTION_POINTERS *Except) { + // Lookup the current thread local recovery object. + const CrashRecoveryContextImpl *CRCI = CurrentContext->get(); -static bool InvokeFunctionCall(function_ref<void()> Fn, - bool DumpStackAndCleanup, int &RetCode) { - __try { - Fn(); - } __except (ExceptionFilter(DumpStackAndCleanup, GetExceptionInformation())) { - RetCode = GetExceptionCode(); - return false; + if (!CRCI) { + // Something has gone horribly wrong, so let's just tell everyone + // to keep searching + CrashRecoveryContext::Disable(); + return EXCEPTION_CONTINUE_SEARCH; } - return true; + + int RetCode = (int)Except->ExceptionRecord->ExceptionCode; + if ((RetCode & 0xF0000000) == 0xE0000000) + RetCode &= ~0xF0000000; // this crash was generated by sys::Process::Exit + + // Handle the crash + const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash( + RetCode, reinterpret_cast<uintptr_t>(Except)); + + return EXCEPTION_EXECUTE_HANDLER; } +#if defined(__clang__) && defined(_M_IX86) +// Work around PR44697. +__attribute__((optnone)) +#endif bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { if (!gCrashRecoveryEnabled) { Fn(); return true; } - return InvokeFunctionCall(Fn, DumpStackAndCleanupOnFailure, RetCode); + assert(!Impl && "Crash recovery context already initialized!"); + Impl = new CrashRecoveryContextImpl(this); + __try { + Fn(); + } __except (ExceptionFilter(GetExceptionInformation())) { + return false; + } + return true; } #else // !_MSC_VER @@ -236,7 +254,7 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { // XP, so if support for older versions of Windows is required, // it will have to be added. -#include "Windows/WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) { @@ -264,10 +282,13 @@ static LONG CALLBACK ExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) // TODO: We can capture the stack backtrace here and store it on the // implementation if we so choose. + int RetCode = (int)ExceptionInfo->ExceptionRecord->ExceptionCode; + if ((RetCode & 0xF0000000) == 0xE0000000) + RetCode &= ~0xF0000000; // this crash was generated by sys::Process::Exit + // Handle the crash const_cast<CrashRecoveryContextImpl *>(CRCI)->HandleCrash( - (int)ExceptionInfo->ExceptionRecord->ExceptionCode, - reinterpret_cast<uintptr_t>(ExceptionInfo)); + RetCode, reinterpret_cast<uintptr_t>(ExceptionInfo)); // Note that we don't actually get here because HandleCrash calls // longjmp, which means the HandleCrash function never returns. @@ -388,6 +409,7 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { CrashRecoveryContextImpl *CRCI = new CrashRecoveryContextImpl(this); Impl = CRCI; + CRCI->ValidJumpBuffer = true; if (setjmp(CRCI->JumpBuffer) != 0) { return false; } @@ -399,12 +421,19 @@ bool CrashRecoveryContext::RunSafely(function_ref<void()> Fn) { #endif // !_MSC_VER -void CrashRecoveryContext::HandleCrash() { - CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *) Impl; +LLVM_ATTRIBUTE_NORETURN +void CrashRecoveryContext::HandleExit(int RetCode) { +#if defined(_WIN32) + // SEH and VEH + ::RaiseException(0xE0000000 | RetCode, 0, 0, NULL); +#else + // On Unix we don't need to raise an exception, we go directly to + // HandleCrash(), then longjmp will unwind the stack for us. + CrashRecoveryContextImpl *CRCI = (CrashRecoveryContextImpl *)Impl; assert(CRCI && "Crash recovery context never initialized!"); - // As per convention, -2 indicates a crash or timeout as opposed to failure to - // execute (see llvm/include/llvm/Support/Program.h) - CRCI->HandleCrash(-2, 0); + CRCI->HandleCrash(RetCode, 0 /*no sig num*/); +#endif + llvm_unreachable("Most likely setjmp wasn't called!"); } // FIXME: Portability. diff --git a/llvm/lib/Support/DataExtractor.cpp b/llvm/lib/Support/DataExtractor.cpp index a98297cdb35f2..133d674275e8c 100644 --- a/llvm/lib/Support/DataExtractor.cpp +++ b/llvm/lib/Support/DataExtractor.cpp @@ -15,29 +15,40 @@ using namespace llvm; -static void unexpectedEndReached(Error *E) { - if (E) - *E = createStringError(errc::illegal_byte_sequence, - "unexpected end of data"); +bool DataExtractor::prepareRead(uint64_t Offset, uint64_t Size, + Error *E) const { + if (isValidOffsetForDataOfSize(Offset, Size)) + return true; + if (E) { + if (Offset <= Data.size()) + *E = createStringError( + errc::illegal_byte_sequence, + "unexpected end of data at offset 0x%zx while reading [0x%" PRIx64 + ", 0x%" PRIx64 ")", + Data.size(), Offset, Offset + Size); + else + *E = createStringError(errc::invalid_argument, + "offset 0x%" PRIx64 + " is beyond the end of data at 0x%zx", + Offset, Data.size()); + } + return false; } static bool isError(Error *E) { return E && *E; } template <typename T> -static T getU(uint64_t *offset_ptr, const DataExtractor *de, - bool isLittleEndian, const char *Data, llvm::Error *Err) { +T DataExtractor::getU(uint64_t *offset_ptr, Error *Err) const { ErrorAsOutParameter ErrAsOut(Err); T val = 0; if (isError(Err)) return val; uint64_t offset = *offset_ptr; - if (!de->isValidOffsetForDataOfSize(offset, sizeof(T))) { - unexpectedEndReached(Err); + if (!prepareRead(offset, sizeof(T), Err)) return val; - } - std::memcpy(&val, &Data[offset], sizeof(val)); - if (sys::IsLittleEndianHost != isLittleEndian) + std::memcpy(&val, &Data.data()[offset], sizeof(val)); + if (sys::IsLittleEndianHost != IsLittleEndian) sys::swapByteOrder(val); // Advance the offset @@ -46,22 +57,19 @@ static T getU(uint64_t *offset_ptr, const DataExtractor *de, } template <typename T> -static T *getUs(uint64_t *offset_ptr, T *dst, uint32_t count, - const DataExtractor *de, bool isLittleEndian, const char *Data, - llvm::Error *Err) { +T *DataExtractor::getUs(uint64_t *offset_ptr, T *dst, uint32_t count, + Error *Err) const { ErrorAsOutParameter ErrAsOut(Err); if (isError(Err)) return nullptr; uint64_t offset = *offset_ptr; - if (!de->isValidOffsetForDataOfSize(offset, sizeof(*dst) * count)) { - unexpectedEndReached(Err); + if (!prepareRead(offset, sizeof(*dst) * count, Err)) return nullptr; - } for (T *value_ptr = dst, *end = dst + count; value_ptr != end; ++value_ptr, offset += sizeof(*dst)) - *value_ptr = getU<T>(offset_ptr, de, isLittleEndian, Data, Err); + *value_ptr = getU<T>(offset_ptr, Err); // Advance the offset *offset_ptr = offset; // Return a non-NULL pointer to the converted data as an indicator of @@ -70,55 +78,49 @@ static T *getUs(uint64_t *offset_ptr, T *dst, uint32_t count, } uint8_t DataExtractor::getU8(uint64_t *offset_ptr, llvm::Error *Err) const { - return getU<uint8_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err); + return getU<uint8_t>(offset_ptr, Err); } -uint8_t * -DataExtractor::getU8(uint64_t *offset_ptr, uint8_t *dst, uint32_t count) const { - return getUs<uint8_t>(offset_ptr, dst, count, this, IsLittleEndian, - Data.data(), nullptr); +uint8_t *DataExtractor::getU8(uint64_t *offset_ptr, uint8_t *dst, + uint32_t count) const { + return getUs<uint8_t>(offset_ptr, dst, count, nullptr); } uint8_t *DataExtractor::getU8(Cursor &C, uint8_t *Dst, uint32_t Count) const { - return getUs<uint8_t>(&C.Offset, Dst, Count, this, IsLittleEndian, - Data.data(), &C.Err); + return getUs<uint8_t>(&C.Offset, Dst, Count, &C.Err); } uint16_t DataExtractor::getU16(uint64_t *offset_ptr, llvm::Error *Err) const { - return getU<uint16_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err); + return getU<uint16_t>(offset_ptr, Err); } uint16_t *DataExtractor::getU16(uint64_t *offset_ptr, uint16_t *dst, uint32_t count) const { - return getUs<uint16_t>(offset_ptr, dst, count, this, IsLittleEndian, - Data.data(), nullptr); + return getUs<uint16_t>(offset_ptr, dst, count, nullptr); } -uint32_t DataExtractor::getU24(uint64_t *offset_ptr) const { - uint24_t ExtractedVal = - getU<uint24_t>(offset_ptr, this, IsLittleEndian, Data.data(), nullptr); +uint32_t DataExtractor::getU24(uint64_t *OffsetPtr, Error *Err) const { + uint24_t ExtractedVal = getU<uint24_t>(OffsetPtr, Err); // The 3 bytes are in the correct byte order for the host. return ExtractedVal.getAsUint32(sys::IsLittleEndianHost); } uint32_t DataExtractor::getU32(uint64_t *offset_ptr, llvm::Error *Err) const { - return getU<uint32_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err); + return getU<uint32_t>(offset_ptr, Err); } uint32_t *DataExtractor::getU32(uint64_t *offset_ptr, uint32_t *dst, uint32_t count) const { - return getUs<uint32_t>(offset_ptr, dst, count, this, IsLittleEndian, - Data.data(), nullptr); + return getUs<uint32_t>(offset_ptr, dst, count, nullptr); } uint64_t DataExtractor::getU64(uint64_t *offset_ptr, llvm::Error *Err) const { - return getU<uint64_t>(offset_ptr, this, IsLittleEndian, Data.data(), Err); + return getU<uint64_t>(offset_ptr, Err); } uint64_t *DataExtractor::getU64(uint64_t *offset_ptr, uint64_t *dst, uint32_t count) const { - return getUs<uint64_t>(offset_ptr, dst, count, this, IsLittleEndian, - Data.data(), nullptr); + return getUs<uint64_t>(offset_ptr, dst, count, nullptr); } uint64_t DataExtractor::getUnsigned(uint64_t *offset_ptr, uint32_t byte_size, @@ -151,59 +153,77 @@ DataExtractor::getSigned(uint64_t *offset_ptr, uint32_t byte_size) const { llvm_unreachable("getSigned unhandled case!"); } -const char *DataExtractor::getCStr(uint64_t *offset_ptr) const { - uint64_t offset = *offset_ptr; - StringRef::size_type pos = Data.find('\0', offset); - if (pos != StringRef::npos) { - *offset_ptr = pos + 1; - return Data.data() + offset; - } - return nullptr; -} +StringRef DataExtractor::getCStrRef(uint64_t *OffsetPtr, Error *Err) const { + ErrorAsOutParameter ErrAsOut(Err); + if (isError(Err)) + return StringRef(); -StringRef DataExtractor::getCStrRef(uint64_t *offset_ptr) const { - uint64_t Start = *offset_ptr; + uint64_t Start = *OffsetPtr; StringRef::size_type Pos = Data.find('\0', Start); if (Pos != StringRef::npos) { - *offset_ptr = Pos + 1; + *OffsetPtr = Pos + 1; return StringRef(Data.data() + Start, Pos - Start); } + if (Err) + *Err = createStringError(errc::illegal_byte_sequence, + "no null terminated string at offset 0x%" PRIx64, + Start); return StringRef(); } -uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr, - llvm::Error *Err) const { - assert(*offset_ptr <= Data.size()); +StringRef DataExtractor::getFixedLengthString(uint64_t *OffsetPtr, + uint64_t Length, + StringRef TrimChars) const { + StringRef Bytes(getBytes(OffsetPtr, Length)); + return Bytes.trim(TrimChars); +} + +StringRef DataExtractor::getBytes(uint64_t *OffsetPtr, uint64_t Length, + Error *Err) const { ErrorAsOutParameter ErrAsOut(Err); if (isError(Err)) - return 0; + return StringRef(); + + if (!prepareRead(*OffsetPtr, Length, Err)) + return StringRef(); + + StringRef Result = Data.substr(*OffsetPtr, Length); + *OffsetPtr += Length; + return Result; +} + +template <typename T> +static T getLEB128(StringRef Data, uint64_t *OffsetPtr, Error *Err, + T (&Decoder)(const uint8_t *p, unsigned *n, + const uint8_t *end, const char **error)) { + ArrayRef<uint8_t> Bytes = arrayRefFromStringRef(Data); + assert(*OffsetPtr <= Bytes.size()); + ErrorAsOutParameter ErrAsOut(Err); + if (isError(Err)) + return T(); const char *error; unsigned bytes_read; - uint64_t result = decodeULEB128( - reinterpret_cast<const uint8_t *>(Data.data() + *offset_ptr), &bytes_read, - reinterpret_cast<const uint8_t *>(Data.data() + Data.size()), &error); + T result = + Decoder(Bytes.data() + *OffsetPtr, &bytes_read, Bytes.end(), &error); if (error) { if (Err) - *Err = createStringError(errc::illegal_byte_sequence, error); - return 0; + *Err = createStringError(errc::illegal_byte_sequence, + "unable to decode LEB128 at offset 0x%8.8" PRIx64 + ": %s", + *OffsetPtr, error); + return T(); } - *offset_ptr += bytes_read; + *OffsetPtr += bytes_read; return result; } -int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr) const { - assert(*offset_ptr <= Data.size()); +uint64_t DataExtractor::getULEB128(uint64_t *offset_ptr, Error *Err) const { + return getLEB128(Data, offset_ptr, Err, decodeULEB128); +} - const char *error; - unsigned bytes_read; - int64_t result = decodeSLEB128( - reinterpret_cast<const uint8_t *>(Data.data() + *offset_ptr), &bytes_read, - reinterpret_cast<const uint8_t *>(Data.data() + Data.size()), &error); - if (error) - return 0; - *offset_ptr += bytes_read; - return result; +int64_t DataExtractor::getSLEB128(uint64_t *offset_ptr, Error *Err) const { + return getLEB128(Data, offset_ptr, Err, decodeSLEB128); } void DataExtractor::skip(Cursor &C, uint64_t Length) const { @@ -211,8 +231,6 @@ void DataExtractor::skip(Cursor &C, uint64_t Length) const { if (isError(&C.Err)) return; - if (isValidOffsetForDataOfSize(C.Offset, Length)) + if (prepareRead(C.Offset, Length, &C.Err)) C.Offset += Length; - else - unexpectedEndReached(&C.Err); } diff --git a/llvm/lib/Support/Debug.cpp b/llvm/lib/Support/Debug.cpp index 737cd576ed80f..73b25d55237b0 100644 --- a/llvm/lib/Support/Debug.cpp +++ b/llvm/lib/Support/Debug.cpp @@ -105,7 +105,7 @@ struct DebugOnlyOpt { SmallVector<StringRef,8> dbgTypes; StringRef(Val).split(dbgTypes, ',', -1, false); for (auto dbgType : dbgTypes) - CurrentDebugType->push_back(dbgType); + CurrentDebugType->push_back(std::string(dbgType)); } }; diff --git a/llvm/lib/Support/DebugCounter.cpp b/llvm/lib/Support/DebugCounter.cpp index 1e3ec300964ca..8c579f395282e 100644 --- a/llvm/lib/Support/DebugCounter.cpp +++ b/llvm/lib/Support/DebugCounter.cpp @@ -31,7 +31,7 @@ private: // width, so we do the same. Option::printHelpStr(HelpStr, GlobalWidth, ArgStr.size() + 6); const auto &CounterInstance = DebugCounter::instance(); - for (auto Name : CounterInstance) { + for (const auto &Name : CounterInstance) { const auto Info = CounterInstance.getCounterInfo(CounterInstance.getCounterId(Name)); size_t NumSpaces = GlobalWidth - Info.first.size() - 8; @@ -85,7 +85,7 @@ void DebugCounter::push_back(const std::string &Val) { // add it to the counter values. if (CounterPair.first.endswith("-skip")) { auto CounterName = CounterPair.first.drop_back(5); - unsigned CounterID = getCounterId(CounterName); + unsigned CounterID = getCounterId(std::string(CounterName)); if (!CounterID) { errs() << "DebugCounter Error: " << CounterName << " is not a registered counter\n"; @@ -98,7 +98,7 @@ void DebugCounter::push_back(const std::string &Val) { Counter.IsSet = true; } else if (CounterPair.first.endswith("-count")) { auto CounterName = CounterPair.first.drop_back(6); - unsigned CounterID = getCounterId(CounterName); + unsigned CounterID = getCounterId(std::string(CounterName)); if (!CounterID) { errs() << "DebugCounter Error: " << CounterName << " is not a registered counter\n"; @@ -123,7 +123,7 @@ void DebugCounter::print(raw_ostream &OS) const { auto &Us = instance(); OS << "Counters and values:\n"; for (auto &CounterName : CounterNames) { - unsigned CounterID = getCounterId(CounterName); + unsigned CounterID = getCounterId(std::string(CounterName)); OS << left_justify(RegisteredCounters[CounterID], 32) << ": {" << Us.Counters[CounterID].Count << "," << Us.Counters[CounterID].Skip << "," << Us.Counters[CounterID].StopAfter << "}\n"; diff --git a/llvm/lib/Support/ELFAttributeParser.cpp b/llvm/lib/Support/ELFAttributeParser.cpp new file mode 100644 index 0000000000000..df955cdf5d30a --- /dev/null +++ b/llvm/lib/Support/ELFAttributeParser.cpp @@ -0,0 +1,233 @@ +//===--- ELFAttributeParser.cpp - ELF Attribute Parser --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ELFAttributeParser.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace llvm; +using namespace llvm::ELFAttrs; + +static const EnumEntry<unsigned> tagNames[] = { + {"Tag_File", ELFAttrs::File}, + {"Tag_Section", ELFAttrs::Section}, + {"Tag_Symbol", ELFAttrs::Symbol}, +}; + +Error ELFAttributeParser::parseStringAttribute(const char *name, unsigned tag, + ArrayRef<const char *> strings) { + uint64_t value = de.getULEB128(cursor); + if (value >= strings.size()) { + printAttribute(tag, value, ""); + return createStringError(errc::invalid_argument, + "unknown " + Twine(name) + + " value: " + Twine(value)); + } + printAttribute(tag, value, strings[value]); + return Error::success(); +} + +Error ELFAttributeParser::integerAttribute(unsigned tag) { + StringRef tagName = + ELFAttrs::attrTypeAsString(tag, tagToStringMap, /*hasTagPrefix=*/false); + uint64_t value = de.getULEB128(cursor); + attributes.insert(std::make_pair(tag, value)); + + if (sw) { + DictScope scope(*sw, "Attribute"); + sw->printNumber("Tag", tag); + if (!tagName.empty()) + sw->printString("TagName", tagName); + sw->printNumber("Value", value); + } + return Error::success(); +} + +Error ELFAttributeParser::stringAttribute(unsigned tag) { + StringRef tagName = + ELFAttrs::attrTypeAsString(tag, tagToStringMap, /*hasTagPrefix=*/false); + StringRef desc = de.getCStrRef(cursor); + attributesStr.insert(std::make_pair(tag, desc)); + + if (sw) { + DictScope scope(*sw, "Attribute"); + sw->printNumber("Tag", tag); + if (!tagName.empty()) + sw->printString("TagName", tagName); + sw->printString("Value", desc); + } + return Error::success(); +} + +void ELFAttributeParser::printAttribute(unsigned tag, unsigned value, + StringRef valueDesc) { + attributes.insert(std::make_pair(tag, value)); + + if (sw) { + StringRef tagName = ELFAttrs::attrTypeAsString(tag, tagToStringMap, + /*hasTagPrefix=*/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 ELFAttributeParser::parseIndexList(SmallVectorImpl<uint8_t> &indexList) { + for (;;) { + uint64_t value = de.getULEB128(cursor); + if (!cursor || !value) + break; + indexList.push_back(value); + } +} + +Error ELFAttributeParser::parseAttributeList(uint32_t length) { + uint64_t pos; + uint64_t end = cursor.tell() + length; + while ((pos = cursor.tell()) < end) { + uint64_t tag = de.getULEB128(cursor); + bool handled; + if (Error e = handler(tag, handled)) + return e; + + if (!handled) { + if (tag < 32) { + return createStringError(errc::invalid_argument, + "invalid tag 0x" + Twine::utohexstr(tag) + + " at offset 0x" + Twine::utohexstr(pos)); + } + + if (tag % 2 == 0) { + if (Error e = integerAttribute(tag)) + return e; + } else { + if (Error e = stringAttribute(tag)) + return e; + } + } + } + return Error::success(); +} + +Error ELFAttributeParser::parseSubsection(uint32_t length) { + uint64_t end = cursor.tell() - sizeof(length) + length; + StringRef vendorName = de.getCStrRef(cursor); + if (sw) { + sw->printNumber("SectionLength", length); + sw->printString("Vendor", vendorName); + } + + // Ignore unrecognized vendor-name. + if (vendorName.lower() != vendor) + return createStringError(errc::invalid_argument, + "unrecognized vendor-name: " + vendorName); + + while (cursor.tell() < end) { + /// Tag_File | Tag_Section | Tag_Symbol uleb128:byte-size + uint8_t tag = de.getU8(cursor); + uint32_t size = de.getU32(cursor); + if (!cursor) + return cursor.takeError(); + + if (sw) { + sw->printEnum("Tag", tag, makeArrayRef(tagNames)); + sw->printNumber("Size", size); + } + if (size < 5) + return createStringError(errc::invalid_argument, + "invalid attribute size " + Twine(size) + + " at offset 0x" + + Twine::utohexstr(cursor.tell() - 5)); + + StringRef scopeName, indexName; + SmallVector<uint8_t, 8> indicies; + switch (tag) { + case ELFAttrs::File: + scopeName = "FileAttributes"; + break; + case ELFAttrs::Section: + scopeName = "SectionAttributes"; + indexName = "Sections"; + parseIndexList(indicies); + break; + case ELFAttrs::Symbol: + scopeName = "SymbolAttributes"; + indexName = "Symbols"; + parseIndexList(indicies); + break; + default: + return createStringError(errc::invalid_argument, + "unrecognized tag 0x" + Twine::utohexstr(tag) + + " at offset 0x" + + Twine::utohexstr(cursor.tell() - 5)); + } + + if (sw) { + DictScope scope(*sw, scopeName); + if (!indicies.empty()) + sw->printList(indexName, indicies); + if (Error e = parseAttributeList(size - 5)) + return e; + } else if (Error e = parseAttributeList(size - 5)) + return e; + } + return Error::success(); +} + +Error ELFAttributeParser::parse(ArrayRef<uint8_t> section, + support::endianness endian) { + unsigned sectionNumber = 0; + de = DataExtractor(section, endian == support::little, 0); + + // For early returns, we have more specific errors, consume the Error in + // cursor. + struct ClearCursorError { + DataExtractor::Cursor &cursor; + ~ClearCursorError() { consumeError(cursor.takeError()); } + } clear{cursor}; + + // Unrecognized format-version. + uint8_t formatVersion = de.getU8(cursor); + if (formatVersion != 'A') + return createStringError(errc::invalid_argument, + "unrecognized format-version: 0x" + + utohexstr(formatVersion)); + + while (!de.eof(cursor)) { + uint32_t sectionLength = de.getU32(cursor); + if (!cursor) + return cursor.takeError(); + + if (sw) { + sw->startLine() << "Section " << ++sectionNumber << " {\n"; + sw->indent(); + } + + if (sectionLength < 4 || cursor.tell() - 4 + sectionLength > section.size()) + return createStringError(errc::invalid_argument, + "invalid section length " + + Twine(sectionLength) + " at offset 0x" + + utohexstr(cursor.tell() - 4)); + + if (Error e = parseSubsection(sectionLength)) + return e; + if (sw) { + sw->unindent(); + sw->startLine() << "}\n"; + } + } + + return cursor.takeError(); +} diff --git a/llvm/lib/Support/ELFAttributes.cpp b/llvm/lib/Support/ELFAttributes.cpp new file mode 100644 index 0000000000000..5be38825d6c6f --- /dev/null +++ b/llvm/lib/Support/ELFAttributes.cpp @@ -0,0 +1,34 @@ +//===-- ELFAttributes.cpp - ELF Attributes --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ELFAttributes.h" +#include "llvm/ADT/StringRef.h" + +using namespace llvm; + +StringRef ELFAttrs::attrTypeAsString(unsigned attr, TagNameMap tagNameMap, + bool hasTagPrefix) { + auto tagNameIt = find_if( + tagNameMap, [attr](const TagNameItem item) { return item.attr == attr; }); + if (tagNameIt == tagNameMap.end()) + return ""; + StringRef tagName = tagNameIt->tagName; + return hasTagPrefix ? tagName : tagName.drop_front(4); +} + +Optional<unsigned> ELFAttrs::attrTypeFromString(StringRef tag, + TagNameMap tagNameMap) { + bool hasTagPrefix = tag.startswith("Tag_"); + auto tagNameIt = + find_if(tagNameMap, [tag, hasTagPrefix](const TagNameItem item) { + return item.tagName.drop_front(hasTagPrefix ? 0 : 4) == tag; + }); + if (tagNameIt == tagNameMap.end()) + return None; + return tagNameIt->attr; +} diff --git a/llvm/lib/Support/ErrorHandling.cpp b/llvm/lib/Support/ErrorHandling.cpp index 0f13f7a536f1d..f70a6921a41a4 100644 --- a/llvm/lib/Support/ErrorHandling.cpp +++ b/llvm/lib/Support/ErrorHandling.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/Errc.h" #include "llvm/Support/Error.h" +#include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include "llvm/Support/Threading.h" #include "llvm/Support/WindowsError.h" @@ -122,7 +123,7 @@ void llvm::report_fatal_error(const Twine &Reason, bool GenCrashDiag) { // files registered with RemoveFileOnSignal. sys::RunInterruptHandlers(); - exit(1); + abort(); } void llvm::install_bad_alloc_error_handler(fatal_error_handler_t handler, diff --git a/llvm/lib/Support/ExtensibleRTTI.cpp b/llvm/lib/Support/ExtensibleRTTI.cpp new file mode 100644 index 0000000000000..1c98d1bb8febc --- /dev/null +++ b/llvm/lib/Support/ExtensibleRTTI.cpp @@ -0,0 +1,13 @@ +//===----- lib/Support/ExtensibleRTTI.cpp - ExtensibleRTTI utilities ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ExtensibleRTTI.h" + +void llvm::RTTIRoot::anchor() {} +char llvm::RTTIRoot::ID = 0; diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp index 2261ecc236c25..d0e79c675bcbd 100644 --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -17,6 +17,7 @@ #include "FileCheckImpl.h" #include "llvm/ADT/StringSet.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/CheckedArithmetic.h" #include "llvm/Support/FormatVariadic.h" #include <cstdint> #include <list> @@ -25,17 +26,301 @@ using namespace llvm; -Expected<uint64_t> NumericVariableUse::eval() const { - Optional<uint64_t> Value = Variable->getValue(); +StringRef ExpressionFormat::toString() const { + switch (Value) { + case Kind::NoFormat: + return StringRef("<none>"); + case Kind::Unsigned: + return StringRef("%u"); + case Kind::Signed: + return StringRef("%d"); + case Kind::HexUpper: + return StringRef("%X"); + case Kind::HexLower: + return StringRef("%x"); + } + llvm_unreachable("unknown expression format"); +} + +Expected<StringRef> ExpressionFormat::getWildcardRegex() const { + switch (Value) { + case Kind::Unsigned: + return StringRef("[0-9]+"); + case Kind::Signed: + return StringRef("-?[0-9]+"); + case Kind::HexUpper: + return StringRef("[0-9A-F]+"); + case Kind::HexLower: + return StringRef("[0-9a-f]+"); + default: + return createStringError(std::errc::invalid_argument, + "trying to match value with invalid format"); + } +} + +Expected<std::string> +ExpressionFormat::getMatchingString(ExpressionValue IntegerValue) const { + if (Value == Kind::Signed) { + Expected<int64_t> SignedValue = IntegerValue.getSignedValue(); + if (!SignedValue) + return SignedValue.takeError(); + return itostr(*SignedValue); + } + + Expected<uint64_t> UnsignedValue = IntegerValue.getUnsignedValue(); + if (!UnsignedValue) + return UnsignedValue.takeError(); + switch (Value) { + case Kind::Unsigned: + return utostr(*UnsignedValue); + case Kind::HexUpper: + return utohexstr(*UnsignedValue, /*LowerCase=*/false); + case Kind::HexLower: + return utohexstr(*UnsignedValue, /*LowerCase=*/true); + default: + return createStringError(std::errc::invalid_argument, + "trying to match value with invalid format"); + } +} + +Expected<ExpressionValue> +ExpressionFormat::valueFromStringRepr(StringRef StrVal, + const SourceMgr &SM) const { + bool ValueIsSigned = Value == Kind::Signed; + StringRef OverflowErrorStr = "unable to represent numeric value"; + if (ValueIsSigned) { + int64_t SignedValue; + + if (StrVal.getAsInteger(10, SignedValue)) + return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); + + return ExpressionValue(SignedValue); + } + + bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower; + uint64_t UnsignedValue; + if (StrVal.getAsInteger(Hex ? 16 : 10, UnsignedValue)) + return ErrorDiagnostic::get(SM, StrVal, OverflowErrorStr); + + return ExpressionValue(UnsignedValue); +} + +static int64_t getAsSigned(uint64_t UnsignedValue) { + // Use memcpy to reinterpret the bitpattern in Value since casting to + // signed is implementation-defined if the unsigned value is too big to be + // represented in the signed type and using an union violates type aliasing + // rules. + int64_t SignedValue; + memcpy(&SignedValue, &UnsignedValue, sizeof(SignedValue)); + return SignedValue; +} + +Expected<int64_t> ExpressionValue::getSignedValue() const { + if (Negative) + return getAsSigned(Value); + + if (Value > (uint64_t)std::numeric_limits<int64_t>::max()) + return make_error<OverflowError>(); + + // Value is in the representable range of int64_t so we can use cast. + return static_cast<int64_t>(Value); +} + +Expected<uint64_t> ExpressionValue::getUnsignedValue() const { + if (Negative) + return make_error<OverflowError>(); + + return Value; +} + +ExpressionValue ExpressionValue::getAbsolute() const { + if (!Negative) + return *this; + + int64_t SignedValue = getAsSigned(Value); + int64_t MaxInt64 = std::numeric_limits<int64_t>::max(); + // Absolute value can be represented as int64_t. + if (SignedValue >= -MaxInt64) + return ExpressionValue(-getAsSigned(Value)); + + // -X == -(max int64_t + Rem), negate each component independently. + SignedValue += MaxInt64; + uint64_t RemainingValueAbsolute = -SignedValue; + return ExpressionValue(MaxInt64 + RemainingValueAbsolute); +} + +Expected<ExpressionValue> llvm::operator+(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + if (LeftOperand.isNegative() && RightOperand.isNegative()) { + int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); + int64_t RightValue = cantFail(RightOperand.getSignedValue()); + Optional<int64_t> Result = checkedAdd<int64_t>(LeftValue, RightValue); + if (!Result) + return make_error<OverflowError>(); + + return ExpressionValue(*Result); + } + + // (-A) + B == B - A. + if (LeftOperand.isNegative()) + return RightOperand - LeftOperand.getAbsolute(); + + // A + (-B) == A - B. + if (RightOperand.isNegative()) + return LeftOperand - RightOperand.getAbsolute(); + + // Both values are positive at this point. + uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + Optional<uint64_t> Result = + checkedAddUnsigned<uint64_t>(LeftValue, RightValue); + if (!Result) + return make_error<OverflowError>(); + + return ExpressionValue(*Result); +} + +Expected<ExpressionValue> llvm::operator-(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + // Result will be negative and thus might underflow. + if (LeftOperand.isNegative() && !RightOperand.isNegative()) { + int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + // Result <= -1 - (max int64_t) which overflows on 1- and 2-complement. + if (RightValue > (uint64_t)std::numeric_limits<int64_t>::max()) + return make_error<OverflowError>(); + Optional<int64_t> Result = + checkedSub(LeftValue, static_cast<int64_t>(RightValue)); + if (!Result) + return make_error<OverflowError>(); + + return ExpressionValue(*Result); + } + + // (-A) - (-B) == B - A. + if (LeftOperand.isNegative()) + return RightOperand.getAbsolute() - LeftOperand.getAbsolute(); + + // A - (-B) == A + B. + if (RightOperand.isNegative()) + return LeftOperand + RightOperand.getAbsolute(); + + // Both values are positive at this point. + uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + if (LeftValue >= RightValue) + return ExpressionValue(LeftValue - RightValue); + else { + uint64_t AbsoluteDifference = RightValue - LeftValue; + uint64_t MaxInt64 = std::numeric_limits<int64_t>::max(); + // Value might underflow. + if (AbsoluteDifference > MaxInt64) { + AbsoluteDifference -= MaxInt64; + int64_t Result = -MaxInt64; + int64_t MinInt64 = std::numeric_limits<int64_t>::min(); + // Underflow, tested by: + // abs(Result + (max int64_t)) > abs((min int64_t) + (max int64_t)) + if (AbsoluteDifference > static_cast<uint64_t>(-(MinInt64 - Result))) + return make_error<OverflowError>(); + Result -= static_cast<int64_t>(AbsoluteDifference); + return ExpressionValue(Result); + } + + return ExpressionValue(-static_cast<int64_t>(AbsoluteDifference)); + } +} + +Expected<ExpressionValue> llvm::operator*(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + // -A * -B == A * B + if (LeftOperand.isNegative() && RightOperand.isNegative()) + return LeftOperand.getAbsolute() * RightOperand.getAbsolute(); + + // A * -B == -B * A + if (RightOperand.isNegative()) + return RightOperand * LeftOperand; + + assert(!RightOperand.isNegative() && "Unexpected negative operand!"); + + // Result will be negative and can underflow. + if (LeftOperand.isNegative()) { + auto Result = LeftOperand.getAbsolute() * RightOperand.getAbsolute(); + if (!Result) + return Result; + + return ExpressionValue(0) - *Result; + } + + // Result will be positive and can overflow. + uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + Optional<uint64_t> Result = + checkedMulUnsigned<uint64_t>(LeftValue, RightValue); + if (!Result) + return make_error<OverflowError>(); + + return ExpressionValue(*Result); +} + +Expected<ExpressionValue> llvm::operator/(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + // -A / -B == A / B + if (LeftOperand.isNegative() && RightOperand.isNegative()) + return LeftOperand.getAbsolute() / RightOperand.getAbsolute(); + + // Check for divide by zero. + if (RightOperand == ExpressionValue(0)) + return make_error<OverflowError>(); + + // Result will be negative and can underflow. + if (LeftOperand.isNegative() || RightOperand.isNegative()) + return ExpressionValue(0) - + cantFail(LeftOperand.getAbsolute() / RightOperand.getAbsolute()); + + uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + return ExpressionValue(LeftValue / RightValue); +} + +Expected<ExpressionValue> llvm::max(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + if (LeftOperand.isNegative() && RightOperand.isNegative()) { + int64_t LeftValue = cantFail(LeftOperand.getSignedValue()); + int64_t RightValue = cantFail(RightOperand.getSignedValue()); + return ExpressionValue(std::max(LeftValue, RightValue)); + } + + if (!LeftOperand.isNegative() && !RightOperand.isNegative()) { + uint64_t LeftValue = cantFail(LeftOperand.getUnsignedValue()); + uint64_t RightValue = cantFail(RightOperand.getUnsignedValue()); + return ExpressionValue(std::max(LeftValue, RightValue)); + } + + if (LeftOperand.isNegative()) + return RightOperand; + + return LeftOperand; +} + +Expected<ExpressionValue> llvm::min(const ExpressionValue &LeftOperand, + const ExpressionValue &RightOperand) { + if (cantFail(max(LeftOperand, RightOperand)) == LeftOperand) + return RightOperand; + + return LeftOperand; +} + +Expected<ExpressionValue> NumericVariableUse::eval() const { + Optional<ExpressionValue> Value = Variable->getValue(); if (Value) return *Value; - return make_error<UndefVarError>(Name); + return make_error<UndefVarError>(getExpressionStr()); } -Expected<uint64_t> BinaryOperation::eval() const { - Expected<uint64_t> LeftOp = LeftOperand->eval(); - Expected<uint64_t> RightOp = RightOperand->eval(); +Expected<ExpressionValue> BinaryOperation::eval() const { + Expected<ExpressionValue> LeftOp = LeftOperand->eval(); + Expected<ExpressionValue> RightOp = RightOperand->eval(); // Bubble up any error (e.g. undefined variables) in the recursive // evaluation. @@ -51,11 +336,42 @@ Expected<uint64_t> BinaryOperation::eval() const { return EvalBinop(*LeftOp, *RightOp); } +Expected<ExpressionFormat> +BinaryOperation::getImplicitFormat(const SourceMgr &SM) const { + Expected<ExpressionFormat> LeftFormat = LeftOperand->getImplicitFormat(SM); + Expected<ExpressionFormat> RightFormat = RightOperand->getImplicitFormat(SM); + if (!LeftFormat || !RightFormat) { + Error Err = Error::success(); + if (!LeftFormat) + Err = joinErrors(std::move(Err), LeftFormat.takeError()); + if (!RightFormat) + Err = joinErrors(std::move(Err), RightFormat.takeError()); + return std::move(Err); + } + + if (*LeftFormat != ExpressionFormat::Kind::NoFormat && + *RightFormat != ExpressionFormat::Kind::NoFormat && + *LeftFormat != *RightFormat) + return ErrorDiagnostic::get( + SM, getExpressionStr(), + "implicit format conflict between '" + LeftOperand->getExpressionStr() + + "' (" + LeftFormat->toString() + ") and '" + + RightOperand->getExpressionStr() + "' (" + RightFormat->toString() + + "), need an explicit format specifier"); + + return *LeftFormat != ExpressionFormat::Kind::NoFormat ? *LeftFormat + : *RightFormat; +} + Expected<std::string> NumericSubstitution::getResult() const { - Expected<uint64_t> EvaluatedValue = ExpressionASTPointer->eval(); + assert(ExpressionPointer->getAST() != nullptr && + "Substituting empty expression"); + Expected<ExpressionValue> EvaluatedValue = + ExpressionPointer->getAST()->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); - return utostr(*EvaluatedValue); + ExpressionFormat Format = ExpressionPointer->getFormat(); + return Format.getMatchingString(*EvaluatedValue); } Expected<std::string> StringSubstitution::getResult() const { @@ -66,30 +382,27 @@ Expected<std::string> StringSubstitution::getResult() const { return Regex::escape(*VarVal); } -bool Pattern::isValidVarNameStart(char C) { return C == '_' || isalpha(C); } +bool Pattern::isValidVarNameStart(char C) { return C == '_' || isAlpha(C); } Expected<Pattern::VariableProperties> Pattern::parseVariable(StringRef &Str, const SourceMgr &SM) { if (Str.empty()) return ErrorDiagnostic::get(SM, Str, "empty variable name"); - bool ParsedOneChar = false; - unsigned I = 0; + size_t I = 0; bool IsPseudo = Str[0] == '@'; // Global vars start with '$'. if (Str[0] == '$' || IsPseudo) ++I; - for (unsigned E = Str.size(); I != E; ++I) { - if (!ParsedOneChar && !isValidVarNameStart(Str[I])) - return ErrorDiagnostic::get(SM, Str, "invalid variable name"); + if (!isValidVarNameStart(Str[I++])) + return ErrorDiagnostic::get(SM, Str, "invalid variable name"); + for (size_t E = Str.size(); I != E; ++I) // Variable names are composed of alphanumeric characters and underscores. - if (Str[I] != '_' && !isalnum(Str[I])) + if (Str[I] != '_' && !isAlnum(Str[I])) break; - ParsedOneChar = true; - } StringRef Name = Str.take_front(I); Str = Str.substr(I); @@ -107,13 +420,15 @@ static char popFront(StringRef &S) { return C; } +char OverflowError::ID = 0; char UndefVarError::ID = 0; char ErrorDiagnostic::ID = 0; char NotFoundError::ID = 0; Expected<NumericVariable *> Pattern::parseNumericVariableDefinition( StringRef &Expr, FileCheckPatternContext *Context, - Optional<size_t> LineNumber, const SourceMgr &SM) { + Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat, + const SourceMgr &SM) { Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM); if (!ParseVarResult) return ParseVarResult.takeError(); @@ -137,10 +452,14 @@ Expected<NumericVariable *> Pattern::parseNumericVariableDefinition( NumericVariable *DefinedNumericVariable; auto VarTableIter = Context->GlobalNumericVariableTable.find(Name); - if (VarTableIter != Context->GlobalNumericVariableTable.end()) + if (VarTableIter != Context->GlobalNumericVariableTable.end()) { DefinedNumericVariable = VarTableIter->second; - else - DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber); + if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat) + return ErrorDiagnostic::get( + SM, Expr, "format different from previous variable definition"); + } else + DefinedNumericVariable = + Context->makeNumericVariable(Name, ImplicitFormat, LineNumber); return DefinedNumericVariable; } @@ -165,7 +484,8 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse( if (VarTableIter != Context->GlobalNumericVariableTable.end()) NumericVariable = VarTableIter->second; else { - NumericVariable = Context->makeNumericVariable(Name); + NumericVariable = Context->makeNumericVariable( + Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned)); Context->GlobalNumericVariableTable[Name] = NumericVariable; } @@ -180,16 +500,36 @@ Expected<std::unique_ptr<NumericVariableUse>> Pattern::parseNumericVariableUse( } Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand( - StringRef &Expr, AllowedOperand AO, Optional<size_t> LineNumber, - FileCheckPatternContext *Context, const SourceMgr &SM) { + StringRef &Expr, AllowedOperand AO, bool MaybeInvalidConstraint, + Optional<size_t> LineNumber, FileCheckPatternContext *Context, + const SourceMgr &SM) { + if (Expr.startswith("(")) { + if (AO != AllowedOperand::Any) + return ErrorDiagnostic::get( + SM, Expr, "parenthesized expression not permitted here"); + return parseParenExpr(Expr, LineNumber, Context, SM); + } + if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) { // Try to parse as a numeric variable use. Expected<Pattern::VariableProperties> ParseVarResult = parseVariable(Expr, SM); - if (ParseVarResult) + if (ParseVarResult) { + // Try to parse a function call. + if (Expr.ltrim(SpaceChars).startswith("(")) { + if (AO != AllowedOperand::Any) + return ErrorDiagnostic::get(SM, ParseVarResult->Name, + "unexpected function call"); + + return parseCallExpr(Expr, ParseVarResult->Name, LineNumber, Context, + SM); + } + return parseNumericVariableUse(ParseVarResult->Name, ParseVarResult->IsPseudo, LineNumber, Context, SM); + } + if (AO == AllowedOperand::LineVar) return ParseVarResult.takeError(); // Ignore the error and retry parsing as a literal. @@ -197,41 +537,79 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand( } // Otherwise, parse it as a literal. - uint64_t LiteralValue; - if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue)) - return std::make_unique<ExpressionLiteral>(LiteralValue); - - return ErrorDiagnostic::get(SM, Expr, - "invalid operand format '" + Expr + "'"); + int64_t SignedLiteralValue; + uint64_t UnsignedLiteralValue; + StringRef SaveExpr = Expr; + // Accept both signed and unsigned literal, default to signed literal. + if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, + UnsignedLiteralValue)) + return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()), + UnsignedLiteralValue); + Expr = SaveExpr; + if (AO == AllowedOperand::Any && !Expr.consumeInteger(0, SignedLiteralValue)) + return std::make_unique<ExpressionLiteral>(SaveExpr.drop_back(Expr.size()), + SignedLiteralValue); + + return ErrorDiagnostic::get( + SM, Expr, + Twine("invalid ") + + (MaybeInvalidConstraint ? "matching constraint or " : "") + + "operand format"); } -static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp + RightOp; -} +Expected<std::unique_ptr<ExpressionAST>> +Pattern::parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM) { + Expr = Expr.ltrim(SpaceChars); + assert(Expr.startswith("(")); + + // Parse right operand. + Expr.consume_front("("); + Expr = Expr.ltrim(SpaceChars); + if (Expr.empty()) + return ErrorDiagnostic::get(SM, Expr, "missing operand in expression"); -static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp - RightOp; + // Note: parseNumericOperand handles nested opening parentheses. + Expected<std::unique_ptr<ExpressionAST>> SubExprResult = parseNumericOperand( + Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber, + Context, SM); + Expr = Expr.ltrim(SpaceChars); + while (SubExprResult && !Expr.empty() && !Expr.startswith(")")) { + StringRef OrigExpr = Expr; + SubExprResult = parseBinop(OrigExpr, Expr, std::move(*SubExprResult), false, + LineNumber, Context, SM); + Expr = Expr.ltrim(SpaceChars); + } + if (!SubExprResult) + return SubExprResult; + + if (!Expr.consume_front(")")) { + return ErrorDiagnostic::get(SM, Expr, + "missing ')' at end of nested expression"); + } + return SubExprResult; } Expected<std::unique_ptr<ExpressionAST>> -Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp, +Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, + std::unique_ptr<ExpressionAST> LeftOp, bool IsLegacyLineExpr, Optional<size_t> LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { - Expr = Expr.ltrim(SpaceChars); - if (Expr.empty()) + RemainingExpr = RemainingExpr.ltrim(SpaceChars); + if (RemainingExpr.empty()) return std::move(LeftOp); // Check if this is a supported operation and select a function to perform // it. - SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); - char Operator = popFront(Expr); + SMLoc OpLoc = SMLoc::getFromPointer(RemainingExpr.data()); + char Operator = popFront(RemainingExpr); binop_eval_t EvalBinop; switch (Operator) { case '+': - EvalBinop = add; + EvalBinop = operator+; break; case '-': - EvalBinop = sub; + EvalBinop = operator-; break; default: return ErrorDiagnostic::get( @@ -239,29 +617,145 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp, } // Parse right operand. - Expr = Expr.ltrim(SpaceChars); - if (Expr.empty()) - return ErrorDiagnostic::get(SM, Expr, "missing operand in expression"); + RemainingExpr = RemainingExpr.ltrim(SpaceChars); + if (RemainingExpr.empty()) + return ErrorDiagnostic::get(SM, RemainingExpr, + "missing operand in expression"); // The second operand in a legacy @LINE expression is always a literal. AllowedOperand AO = - IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any; + IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any; Expected<std::unique_ptr<ExpressionAST>> RightOpResult = - parseNumericOperand(Expr, AO, LineNumber, Context, SM); + parseNumericOperand(RemainingExpr, AO, /*MaybeInvalidConstraint=*/false, + LineNumber, Context, SM); if (!RightOpResult) return RightOpResult; - Expr = Expr.ltrim(SpaceChars); - return std::make_unique<BinaryOperation>(EvalBinop, std::move(LeftOp), + Expr = Expr.drop_back(RemainingExpr.size()); + return std::make_unique<BinaryOperation>(Expr, EvalBinop, std::move(LeftOp), std::move(*RightOpResult)); } -Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock( +Expected<std::unique_ptr<ExpressionAST>> +Pattern::parseCallExpr(StringRef &Expr, StringRef FuncName, + Optional<size_t> LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM) { + Expr = Expr.ltrim(SpaceChars); + assert(Expr.startswith("(")); + + auto OptFunc = StringSwitch<Optional<binop_eval_t>>(FuncName) + .Case("add", operator+) + .Case("div", operator/) + .Case("max", max) + .Case("min", min) + .Case("mul", operator*) + .Case("sub", operator-) + .Default(None); + + if (!OptFunc) + return ErrorDiagnostic::get( + SM, FuncName, Twine("call to undefined function '") + FuncName + "'"); + + Expr.consume_front("("); + Expr = Expr.ltrim(SpaceChars); + + // Parse call arguments, which are comma separated. + SmallVector<std::unique_ptr<ExpressionAST>, 4> Args; + while (!Expr.empty() && !Expr.startswith(")")) { + if (Expr.startswith(",")) + return ErrorDiagnostic::get(SM, Expr, "missing argument"); + + // Parse the argument, which is an arbitary expression. + StringRef OuterBinOpExpr = Expr; + Expected<std::unique_ptr<ExpressionAST>> Arg = parseNumericOperand( + Expr, AllowedOperand::Any, /*MaybeInvalidConstraint=*/false, LineNumber, + Context, SM); + while (Arg && !Expr.empty()) { + Expr = Expr.ltrim(SpaceChars); + // Have we reached an argument terminator? + if (Expr.startswith(",") || Expr.startswith(")")) + break; + + // Arg = Arg <op> <expr> + Arg = parseBinop(OuterBinOpExpr, Expr, std::move(*Arg), false, LineNumber, + Context, SM); + } + + // Prefer an expression error over a generic invalid argument message. + if (!Arg) + return Arg.takeError(); + Args.push_back(std::move(*Arg)); + + // Have we parsed all available arguments? + Expr = Expr.ltrim(SpaceChars); + if (!Expr.consume_front(",")) + break; + + Expr = Expr.ltrim(SpaceChars); + if (Expr.startswith(")")) + return ErrorDiagnostic::get(SM, Expr, "missing argument"); + } + + if (!Expr.consume_front(")")) + return ErrorDiagnostic::get(SM, Expr, + "missing ')' at end of call expression"); + + const unsigned NumArgs = Args.size(); + if (NumArgs == 2) + return std::make_unique<BinaryOperation>(Expr, *OptFunc, std::move(Args[0]), + std::move(Args[1])); + + // TODO: Support more than binop_eval_t. + return ErrorDiagnostic::get(SM, FuncName, + Twine("function '") + FuncName + + Twine("' takes 2 arguments but ") + + Twine(NumArgs) + " given"); +} + +Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock( StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable, bool IsLegacyLineExpr, Optional<size_t> LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr; StringRef DefExpr = StringRef(); DefinedNumericVariable = None; + ExpressionFormat ExplicitFormat = ExpressionFormat(); + + // Parse format specifier (NOTE: ',' is also an argument seperator). + size_t FormatSpecEnd = Expr.find(','); + size_t FunctionStart = Expr.find('('); + if (FormatSpecEnd != StringRef::npos && FormatSpecEnd < FunctionStart) { + Expr = Expr.ltrim(SpaceChars); + if (!Expr.consume_front("%")) + return ErrorDiagnostic::get( + SM, Expr, "invalid matching format specification in expression"); + + // Check for unknown matching format specifier and set matching format in + // class instance representing this expression. + SMLoc fmtloc = SMLoc::getFromPointer(Expr.data()); + switch (popFront(Expr)) { + case 'u': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned); + break; + case 'd': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Signed); + break; + case 'x': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower); + break; + case 'X': + ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper); + break; + default: + return ErrorDiagnostic::get(SM, fmtloc, + "invalid format specifier in expression"); + } + + Expr = Expr.ltrim(SpaceChars); + if (!Expr.consume_front(",")) + return ErrorDiagnostic::get( + SM, Expr, "invalid matching format specification in expression"); + } + // Save variable definition expression if any. size_t DefEnd = Expr.find(':'); if (DefEnd != StringRef::npos) { @@ -269,18 +763,30 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock( Expr = Expr.substr(DefEnd + 1); } + // Parse matching constraint. + Expr = Expr.ltrim(SpaceChars); + bool HasParsedValidConstraint = false; + if (Expr.consume_front("==")) + HasParsedValidConstraint = true; + // Parse the expression itself. Expr = Expr.ltrim(SpaceChars); - if (!Expr.empty()) { + if (Expr.empty()) { + if (HasParsedValidConstraint) + return ErrorDiagnostic::get( + SM, Expr, "empty numeric expression should not have a constraint"); + } else { + Expr = Expr.rtrim(SpaceChars); + StringRef OuterBinOpExpr = Expr; // The first operand in a legacy @LINE expression is always the @LINE // pseudo variable. AllowedOperand AO = IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; - Expected<std::unique_ptr<ExpressionAST>> ParseResult = - parseNumericOperand(Expr, AO, LineNumber, Context, SM); + Expected<std::unique_ptr<ExpressionAST>> ParseResult = parseNumericOperand( + Expr, AO, !HasParsedValidConstraint, LineNumber, Context, SM); while (ParseResult && !Expr.empty()) { - ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, - LineNumber, Context, SM); + ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult), + IsLegacyLineExpr, LineNumber, Context, SM); // Legacy @LINE expressions only allow 2 operands. if (ParseResult && IsLegacyLineExpr && !Expr.empty()) return ErrorDiagnostic::get( @@ -288,22 +794,42 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock( "unexpected characters at end of expression '" + Expr + "'"); } if (!ParseResult) - return ParseResult; + return ParseResult.takeError(); ExpressionASTPointer = std::move(*ParseResult); } + // Select format of the expression, i.e. (i) its explicit format, if any, + // otherwise (ii) its implicit format, if any, otherwise (iii) the default + // format (unsigned). Error out in case of conflicting implicit format + // without explicit format. + ExpressionFormat Format; + if (ExplicitFormat) + Format = ExplicitFormat; + else if (ExpressionASTPointer) { + Expected<ExpressionFormat> ImplicitFormat = + ExpressionASTPointer->getImplicitFormat(SM); + if (!ImplicitFormat) + return ImplicitFormat.takeError(); + Format = *ImplicitFormat; + } + if (!Format) + Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned); + + std::unique_ptr<Expression> ExpressionPointer = + std::make_unique<Expression>(std::move(ExpressionASTPointer), Format); + // Parse the numeric variable definition. if (DefEnd != StringRef::npos) { DefExpr = DefExpr.ltrim(SpaceChars); - Expected<NumericVariable *> ParseResult = - parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM); + Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition( + DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM); if (!ParseResult) return ParseResult.takeError(); DefinedNumericVariable = *ParseResult; } - return std::move(ExpressionASTPointer); + return std::move(ExpressionPointer); } bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -476,10 +1002,10 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix, } // Parse numeric substitution block. - std::unique_ptr<ExpressionAST> ExpressionASTPointer; + std::unique_ptr<Expression> ExpressionPointer; Optional<NumericVariable *> DefinedNumericVariable; if (IsNumBlock) { - Expected<std::unique_ptr<ExpressionAST>> ParseResult = + Expected<std::unique_ptr<Expression>> ParseResult = parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, IsLegacyLineExpr, LineNumber, Context, SM); @@ -487,16 +1013,18 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix, logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - ExpressionASTPointer = std::move(*ParseResult); - SubstNeeded = ExpressionASTPointer != nullptr; + ExpressionPointer = std::move(*ParseResult); + SubstNeeded = ExpressionPointer->getAST() != nullptr; if (DefinedNumericVariable) { IsDefinition = true; DefName = (*DefinedNumericVariable)->getName(); } if (SubstNeeded) SubstStr = MatchStr; - else - MatchRegexp = "[0-9]+"; + else { + ExpressionFormat Format = ExpressionPointer->getFormat(); + MatchRegexp = cantFail(Format.getWildcardRegex()); + } } // Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]]. @@ -554,8 +1082,7 @@ bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix, Substitution *Substitution = IsNumBlock ? Context->makeNumericSubstitution( - SubstStr, std::move(ExpressionASTPointer), - SubstInsertIdx) + SubstStr, std::move(ExpressionPointer), SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } @@ -626,7 +1153,7 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen, if (!Substitutions.empty()) { TmpStr = RegExStr; if (LineNumber) - Context->LineVariable->setValue(*LineNumber); + Context->LineVariable->setValue(ExpressionValue(*LineNumber)); size_t InsertOffset = 0; // Substitute all string variables and expressions whose values are only @@ -635,8 +1162,18 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen, for (const auto &Substitution : Substitutions) { // Substitute and check for failure (e.g. use of undefined variable). Expected<std::string> Value = Substitution->getResult(); - if (!Value) - return Value.takeError(); + if (!Value) { + // Convert to an ErrorDiagnostic to get location information. This is + // done here rather than PrintNoMatch since now we know which + // substitution block caused the overflow. + Error Err = + handleErrors(Value.takeError(), [&](const OverflowError &E) { + return ErrorDiagnostic::get(SM, Substitution->getFromString(), + "unable to substitute variable or " + "numeric expression: overflow error"); + }); + return std::move(Err); + } // Plop it into the regex at the adjusted offset. TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset, @@ -676,11 +1213,12 @@ Expected<size_t> Pattern::match(StringRef Buffer, size_t &MatchLen, NumericVariableMatch.DefinedNumericVariable; StringRef MatchedValue = MatchInfo[CaptureParenGroup]; - uint64_t Val; - if (MatchedValue.getAsInteger(10, Val)) - return ErrorDiagnostic::get(SM, MatchedValue, - "Unable to represent numeric value"); - DefinedNumericVariable->setValue(Val); + ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat(); + Expected<ExpressionValue> Value = + Format.valueFromStringRepr(MatchedValue, SM); + if (!Value) + return Value.takeError(); + DefinedNumericVariable->setValue(*Value); } // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after @@ -721,17 +1259,20 @@ void Pattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer, // variables it uses. if (!MatchedValue) { bool UndefSeen = false; - handleAllErrors(MatchedValue.takeError(), [](const NotFoundError &E) {}, - // Handled in PrintNoMatch(). - [](const ErrorDiagnostic &E) {}, - [&](const UndefVarError &E) { - if (!UndefSeen) { - OS << "uses undefined variable(s):"; - UndefSeen = true; - } - OS << " "; - E.log(OS); - }); + handleAllErrors( + MatchedValue.takeError(), [](const NotFoundError &E) {}, + // Handled in PrintNoMatch(). + [](const ErrorDiagnostic &E) {}, + // Handled in match(). + [](const OverflowError &E) {}, + [&](const UndefVarError &E) { + if (!UndefSeen) { + OS << "uses undefined variable(s):"; + UndefSeen = true; + } + OS << " "; + E.log(OS); + }); } else { // Substitution succeeded. Print substituted value. OS << "with \""; @@ -837,10 +1378,10 @@ FileCheckPatternContext::makeStringSubstitution(StringRef VarName, } Substitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef ExpressionStr, - std::unique_ptr<ExpressionAST> ExpressionASTPointer, size_t InsertIdx) { + StringRef ExpressionStr, std::unique_ptr<Expression> Expression, + size_t InsertIdx) { Substitutions.push_back(std::make_unique<NumericSubstitution>( - this, ExpressionStr, std::move(ExpressionASTPointer), InsertIdx)); + this, ExpressionStr, std::move(Expression), InsertIdx)); return Substitutions.back().get(); } @@ -915,20 +1456,17 @@ FileCheckDiag::FileCheckDiag(const SourceMgr &SM, const Check::FileCheckType &CheckTy, SMLoc CheckLoc, MatchType MatchTy, SMRange InputRange) - : CheckTy(CheckTy), MatchTy(MatchTy) { + : CheckTy(CheckTy), CheckLoc(CheckLoc), MatchTy(MatchTy) { auto Start = SM.getLineAndColumn(InputRange.Start); auto End = SM.getLineAndColumn(InputRange.End); InputStartLine = Start.first; InputStartCol = Start.second; InputEndLine = End.first; InputEndCol = End.second; - Start = SM.getLineAndColumn(CheckLoc); - CheckLine = Start.first; - CheckCol = Start.second; } static bool IsPartOfWord(char c) { - return (isalnum(c) || c == '-' || c == '_'); + return (isAlnum(c) || c == '-' || c == '_'); } Check::FileCheckType &Check::FileCheckType::setCount(int C) { @@ -946,7 +1484,7 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const { case Check::CheckPlain: if (Count > 1) return Prefix.str() + "-COUNT"; - return Prefix; + return std::string(Prefix); case Check::CheckNext: return Prefix.str() + "-NEXT"; case Check::CheckSame: @@ -959,6 +1497,8 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const { return Prefix.str() + "-LABEL"; case Check::CheckEmpty: return Prefix.str() + "-EMPTY"; + case Check::CheckComment: + return std::string(Prefix); case Check::CheckEOF: return "implicit EOF"; case Check::CheckBadNot: @@ -970,13 +1510,24 @@ std::string Check::FileCheckType::getDescription(StringRef Prefix) const { } static std::pair<Check::FileCheckType, StringRef> -FindCheckType(StringRef Buffer, StringRef Prefix) { +FindCheckType(const FileCheckRequest &Req, StringRef Buffer, StringRef Prefix) { if (Buffer.size() <= Prefix.size()) return {Check::CheckNone, StringRef()}; char NextChar = Buffer[Prefix.size()]; StringRef Rest = Buffer.drop_front(Prefix.size() + 1); + + // Check for comment. + if (Req.CommentPrefixes.end() != std::find(Req.CommentPrefixes.begin(), + Req.CommentPrefixes.end(), + Prefix)) { + if (NextChar == ':') + return {Check::CheckComment, Rest}; + // Ignore a comment prefix if it has a suffix like "-NOT". + return {Check::CheckNone, StringRef()}; + } + // Verify that the : is present after the prefix. if (NextChar == ':') return {Check::CheckPlain, Rest}; @@ -1055,8 +1606,9 @@ static size_t SkipWord(StringRef Str, size_t Loc) { /// If no valid prefix is found, the state of Buffer, LineNumber, and CheckTy /// is unspecified. static std::pair<StringRef, StringRef> -FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer, - unsigned &LineNumber, Check::FileCheckType &CheckTy) { +FindFirstMatchingPrefix(const FileCheckRequest &Req, Regex &PrefixRE, + StringRef &Buffer, unsigned &LineNumber, + Check::FileCheckType &CheckTy) { SmallVector<StringRef, 2> Matches; while (!Buffer.empty()) { @@ -1084,7 +1636,7 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer, if (Skipped.empty() || !IsPartOfWord(Skipped.back())) { // Now extract the type. StringRef AfterSuffix; - std::tie(CheckTy, AfterSuffix) = FindCheckType(Buffer, Prefix); + std::tie(CheckTy, AfterSuffix) = FindCheckType(Req, Buffer, Prefix); // If we've found a valid check type for this prefix, we're done. if (CheckTy != Check::CheckNone) @@ -1104,7 +1656,8 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer, void FileCheckPatternContext::createLineVariable() { assert(!LineVariable && "@LINE pseudo numeric variable already created"); StringRef LineName = "@LINE"; - LineVariable = makeNumericVariable(LineName); + LineVariable = makeNumericVariable( + LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned)); GlobalNumericVariableTable[LineName] = LineVariable; } @@ -1114,8 +1667,12 @@ FileCheck::FileCheck(FileCheckRequest Req) FileCheck::~FileCheck() = default; -bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, - Regex &PrefixRE) { +bool FileCheck::readCheckFile( + SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, + std::pair<unsigned, unsigned> *ImpPatBufferIDRange) { + if (ImpPatBufferIDRange) + ImpPatBufferIDRange->first = ImpPatBufferIDRange->second = 0; + Error DefineError = PatternContext->defineCmdlineVariables(Req.GlobalDefines, SM); if (DefineError) { @@ -1126,17 +1683,27 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, PatternContext->createLineVariable(); std::vector<Pattern> ImplicitNegativeChecks; - for (const auto &PatternString : Req.ImplicitCheckNot) { + for (StringRef PatternString : Req.ImplicitCheckNot) { // Create a buffer with fake command line content in order to display the // command line option responsible for the specific implicit CHECK-NOT. std::string Prefix = "-implicit-check-not='"; std::string Suffix = "'"; std::unique_ptr<MemoryBuffer> CmdLine = MemoryBuffer::getMemBufferCopy( - Prefix + PatternString + Suffix, "command line"); + (Prefix + PatternString + Suffix).str(), "command line"); StringRef PatternInBuffer = CmdLine->getBuffer().substr(Prefix.size(), PatternString.size()); - SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + unsigned BufferID = SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + if (ImpPatBufferIDRange) { + if (ImpPatBufferIDRange->first == ImpPatBufferIDRange->second) { + ImpPatBufferIDRange->first = BufferID; + ImpPatBufferIDRange->second = BufferID + 1; + } else { + assert(BufferID == ImpPatBufferIDRange->second && + "expected consecutive source buffer IDs"); + ++ImpPatBufferIDRange->second; + } + } ImplicitNegativeChecks.push_back( Pattern(Check::CheckNot, PatternContext.get())); @@ -1150,6 +1717,7 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, // found. unsigned LineNumber = 1; + bool FoundUsedCheckPrefix = false; while (1) { Check::FileCheckType CheckTy; @@ -1157,9 +1725,12 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, StringRef UsedPrefix; StringRef AfterSuffix; std::tie(UsedPrefix, AfterSuffix) = - FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy); + FindFirstMatchingPrefix(Req, PrefixRE, Buffer, LineNumber, CheckTy); if (UsedPrefix.empty()) break; + if (CheckTy != Check::CheckComment) + FoundUsedCheckPrefix = true; + assert(UsedPrefix.data() == Buffer.data() && "Failed to move Buffer's start forward, or pointed prefix outside " "of the buffer!"); @@ -1201,9 +1772,17 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, // Remember the location of the start of the pattern, for diagnostics. SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); + // Extract the pattern from the buffer. + StringRef PatternBuffer = Buffer.substr(0, EOL); + Buffer = Buffer.substr(EOL); + + // If this is a comment, we're done. + if (CheckTy == Check::CheckComment) + continue; + // Parse the pattern. Pattern P(CheckTy, PatternContext.get(), LineNumber); - if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req)) + if (P.parsePattern(PatternBuffer, UsedPrefix, SM, Req)) return true; // Verify that CHECK-LABEL lines do not define or use variables @@ -1215,8 +1794,6 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, return true; } - Buffer = Buffer.substr(EOL); - // Verify that CHECK-NEXT/SAME/EMPTY lines have at least one CHECK line before them. if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame || CheckTy == Check::CheckEmpty) && @@ -1243,31 +1820,30 @@ bool FileCheck::readCheckFile(SourceMgr &SM, StringRef Buffer, DagNotMatches = ImplicitNegativeChecks; } - // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first - // prefix as a filler for the error message. - if (!DagNotMatches.empty()) { - CheckStrings->emplace_back( - Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1), - *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); - std::swap(DagNotMatches, CheckStrings->back().DagNotStrings); - } - - if (CheckStrings->empty()) { + // When there are no used prefixes we report an error except in the case that + // no prefix is specified explicitly but -implicit-check-not is specified. + if (!FoundUsedCheckPrefix && + (ImplicitNegativeChecks.empty() || !Req.IsDefaultCheckPrefix)) { errs() << "error: no check strings found with prefix" << (Req.CheckPrefixes.size() > 1 ? "es " : " "); - auto I = Req.CheckPrefixes.begin(); - auto E = Req.CheckPrefixes.end(); - if (I != E) { - errs() << "\'" << *I << ":'"; - ++I; + for (size_t I = 0, E = Req.CheckPrefixes.size(); I != E; ++I) { + if (I != 0) + errs() << ", "; + errs() << "\'" << Req.CheckPrefixes[I] << ":'"; } - for (; I != E; ++I) - errs() << ", \'" << *I << ":'"; - errs() << '\n'; return true; } + // Add an EOF pattern for any trailing --implicit-check-not/CHECK-DAG/-NOTs, + // and use the first prefix as a filler for the error message. + if (!DagNotMatches.empty()) { + CheckStrings->emplace_back( + Pattern(Check::CheckEOF, PatternContext.get(), LineNumber + 1), + *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); + std::swap(DagNotMatches, CheckStrings->back().DagNotStrings); + } + return false; } @@ -1706,43 +2282,74 @@ size_t FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, return StartPos; } -// A check prefix must contain only alphanumeric, hyphens and underscores. -static bool ValidateCheckPrefix(StringRef CheckPrefix) { - static const Regex Validator("^[a-zA-Z0-9_-]*$"); - return Validator.match(CheckPrefix); -} - -bool FileCheck::ValidateCheckPrefixes() { - StringSet<> PrefixSet; - - for (StringRef Prefix : Req.CheckPrefixes) { - // Reject empty prefixes. - if (Prefix == "") +static bool ValidatePrefixes(StringRef Kind, StringSet<> &UniquePrefixes, + ArrayRef<StringRef> SuppliedPrefixes) { + for (StringRef Prefix : SuppliedPrefixes) { + if (Prefix.empty()) { + errs() << "error: supplied " << Kind << " prefix must not be the empty " + << "string\n"; return false; - - if (!PrefixSet.insert(Prefix).second) + } + static const Regex Validator("^[a-zA-Z0-9_-]*$"); + if (!Validator.match(Prefix)) { + errs() << "error: supplied " << Kind << " prefix must start with a " + << "letter and contain only alphanumeric characters, hyphens, and " + << "underscores: '" << Prefix << "'\n"; return false; - - if (!ValidateCheckPrefix(Prefix)) + } + if (!UniquePrefixes.insert(Prefix).second) { + errs() << "error: supplied " << Kind << " prefix must be unique among " + << "check and comment prefixes: '" << Prefix << "'\n"; return false; + } } + return true; +} + +static const char *DefaultCheckPrefixes[] = {"CHECK"}; +static const char *DefaultCommentPrefixes[] = {"COM", "RUN"}; +bool FileCheck::ValidateCheckPrefixes() { + StringSet<> UniquePrefixes; + // Add default prefixes to catch user-supplied duplicates of them below. + if (Req.CheckPrefixes.empty()) { + for (const char *Prefix : DefaultCheckPrefixes) + UniquePrefixes.insert(Prefix); + } + if (Req.CommentPrefixes.empty()) { + for (const char *Prefix : DefaultCommentPrefixes) + UniquePrefixes.insert(Prefix); + } + // Do not validate the default prefixes, or diagnostics about duplicates might + // incorrectly indicate that they were supplied by the user. + if (!ValidatePrefixes("check", UniquePrefixes, Req.CheckPrefixes)) + return false; + if (!ValidatePrefixes("comment", UniquePrefixes, Req.CommentPrefixes)) + return false; return true; } Regex FileCheck::buildCheckPrefixRegex() { - // I don't think there's a way to specify an initial value for cl::list, - // so if nothing was specified, add the default - if (Req.CheckPrefixes.empty()) - Req.CheckPrefixes.push_back("CHECK"); + if (Req.CheckPrefixes.empty()) { + for (const char *Prefix : DefaultCheckPrefixes) + Req.CheckPrefixes.push_back(Prefix); + Req.IsDefaultCheckPrefix = true; + } + if (Req.CommentPrefixes.empty()) { + for (const char *Prefix : DefaultCommentPrefixes) + Req.CommentPrefixes.push_back(Prefix); + } - // We already validated the contents of CheckPrefixes so just concatenate - // them as alternatives. + // We already validated the contents of CheckPrefixes and CommentPrefixes so + // just concatenate them as alternatives. SmallString<32> PrefixRegexStr; - for (StringRef Prefix : Req.CheckPrefixes) { - if (Prefix != Req.CheckPrefixes.front()) + for (size_t I = 0, E = Req.CheckPrefixes.size(); I != E; ++I) { + if (I != 0) PrefixRegexStr.push_back('|'); - + PrefixRegexStr.append(Req.CheckPrefixes[I]); + } + for (StringRef Prefix : Req.CommentPrefixes) { + PrefixRegexStr.push_back('|'); PrefixRegexStr.append(Prefix); } @@ -1750,7 +2357,7 @@ Regex FileCheck::buildCheckPrefixRegex() { } Error FileCheckPatternContext::defineCmdlineVariables( - std::vector<std::string> &CmdlineDefines, SourceMgr &SM) { + ArrayRef<StringRef> CmdlineDefines, SourceMgr &SM) { assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() && "Overriding defined variable with command-line variable definitions"); @@ -1777,7 +2384,7 @@ Error FileCheckPatternContext::defineCmdlineVariables( // format as in the input file to be able to reuse // parseNumericSubstitutionBlock. CmdlineDefsDiag += (DefPrefix + CmdlineDef + " (parsed as: [[").str(); - std::string SubstitutionStr = CmdlineDef; + std::string SubstitutionStr = std::string(CmdlineDef); SubstitutionStr[EqIdx] = ':'; CmdlineDefsIndices.push_back( std::make_pair(CmdlineDefsDiag.size(), SubstitutionStr.size())); @@ -1815,20 +2422,19 @@ Error FileCheckPatternContext::defineCmdlineVariables( // to create the necessary class instance. StringRef CmdlineDefExpr = CmdlineDef.substr(1); Optional<NumericVariable *> DefinedNumericVariable; - Expected<std::unique_ptr<ExpressionAST>> ExpressionASTResult = + Expected<std::unique_ptr<Expression>> ExpressionResult = Pattern::parseNumericSubstitutionBlock( CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM); - if (!ExpressionASTResult) { - Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError()); + if (!ExpressionResult) { + Errs = joinErrors(std::move(Errs), ExpressionResult.takeError()); continue; } - std::unique_ptr<ExpressionAST> ExpressionASTPointer = - std::move(*ExpressionASTResult); + std::unique_ptr<Expression> Expression = std::move(*ExpressionResult); // Now evaluate the expression whose value this variable should be set // to, since the expression of a command-line variable definition should // only use variables defined earlier on the command-line. If not, this // is an error and we report it. - Expected<uint64_t> Value = ExpressionASTPointer->eval(); + Expected<ExpressionValue> Value = Expression->getAST()->eval(); if (!Value) { Errs = joinErrors(std::move(Errs), Value.takeError()); continue; diff --git a/llvm/lib/Support/FileCheckImpl.h b/llvm/lib/Support/FileCheckImpl.h index dc07d22aefd83..6ca67ec2964c2 100644 --- a/llvm/lib/Support/FileCheckImpl.h +++ b/llvm/lib/Support/FileCheckImpl.h @@ -15,6 +15,7 @@ #ifndef LLVM_LIB_SUPPORT_FILECHECKIMPL_H #define LLVM_LIB_SUPPORT_FILECHECKIMPL_H +#include "llvm/Support/FileCheck.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -30,28 +31,175 @@ namespace llvm { // Numeric substitution handling code. //===----------------------------------------------------------------------===// +class ExpressionValue; + +/// Type representing the format an expression value should be textualized into +/// for matching. Used to represent both explicit format specifiers as well as +/// implicit format from using numeric variables. +struct ExpressionFormat { + enum class Kind { + /// Denote absence of format. Used for implicit format of literals and + /// empty expressions. + NoFormat, + /// Value is an unsigned integer and should be printed as a decimal number. + Unsigned, + /// Value is a signed integer and should be printed as a decimal number. + Signed, + /// Value should be printed as an uppercase hex number. + HexUpper, + /// Value should be printed as a lowercase hex number. + HexLower + }; + +private: + Kind Value; + +public: + /// Evaluates a format to true if it can be used in a match. + explicit operator bool() const { return Value != Kind::NoFormat; } + + /// Define format equality: formats are equal if neither is NoFormat and + /// their kinds are the same. + bool operator==(const ExpressionFormat &Other) const { + return Value != Kind::NoFormat && Value == Other.Value; + } + + bool operator!=(const ExpressionFormat &Other) const { + return !(*this == Other); + } + + bool operator==(Kind OtherValue) const { return Value == OtherValue; } + + bool operator!=(Kind OtherValue) const { return !(*this == OtherValue); } + + /// \returns the format specifier corresponding to this format as a string. + StringRef toString() const; + + ExpressionFormat() : Value(Kind::NoFormat){}; + explicit ExpressionFormat(Kind Value) : Value(Value){}; + + /// \returns a wildcard regular expression StringRef that matches any value + /// in the format represented by this instance, or an error if the format is + /// NoFormat. + Expected<StringRef> getWildcardRegex() const; + + /// \returns the string representation of \p Value in the format represented + /// by this instance, or an error if conversion to this format failed or the + /// format is NoFormat. + Expected<std::string> getMatchingString(ExpressionValue Value) const; + + /// \returns the value corresponding to string representation \p StrVal + /// according to the matching format represented by this instance or an error + /// with diagnostic against \p SM if \p StrVal does not correspond to a valid + /// and representable value. + Expected<ExpressionValue> valueFromStringRepr(StringRef StrVal, + const SourceMgr &SM) const; +}; + +/// Class to represent an overflow error that might result when manipulating a +/// value. +class OverflowError : public ErrorInfo<OverflowError> { +public: + static char ID; + + std::error_code convertToErrorCode() const override { + return std::make_error_code(std::errc::value_too_large); + } + + void log(raw_ostream &OS) const override { OS << "overflow error"; } +}; + +/// Class representing a numeric value. +class ExpressionValue { +private: + uint64_t Value; + bool Negative; + +public: + template <class T> + explicit ExpressionValue(T Val) : Value(Val), Negative(Val < 0) {} + + bool operator==(const ExpressionValue &Other) const { + return Value == Other.Value && isNegative() == Other.isNegative(); + } + + bool operator!=(const ExpressionValue &Other) const { + return !(*this == Other); + } + + /// Returns true if value is signed and negative, false otherwise. + bool isNegative() const { + assert((Value != 0 || !Negative) && "Unexpected negative zero!"); + return Negative; + } + + /// \returns the value as a signed integer or an error if the value is out of + /// range. + Expected<int64_t> getSignedValue() const; + + /// \returns the value as an unsigned integer or an error if the value is out + /// of range. + Expected<uint64_t> getUnsignedValue() const; + + /// \returns an unsigned ExpressionValue instance whose value is the absolute + /// value to this object's value. + ExpressionValue getAbsolute() const; +}; + +/// Performs operation and \returns its result or an error in case of failure, +/// such as if an overflow occurs. +Expected<ExpressionValue> operator+(const ExpressionValue &Lhs, + const ExpressionValue &Rhs); +Expected<ExpressionValue> operator-(const ExpressionValue &Lhs, + const ExpressionValue &Rhs); +Expected<ExpressionValue> operator*(const ExpressionValue &Lhs, + const ExpressionValue &Rhs); +Expected<ExpressionValue> operator/(const ExpressionValue &Lhs, + const ExpressionValue &Rhs); +Expected<ExpressionValue> max(const ExpressionValue &Lhs, + const ExpressionValue &Rhs); +Expected<ExpressionValue> min(const ExpressionValue &Lhs, + const ExpressionValue &Rhs); + /// Base class representing the AST of a given expression. class ExpressionAST { +private: + StringRef ExpressionStr; + public: + ExpressionAST(StringRef ExpressionStr) : ExpressionStr(ExpressionStr) {} + virtual ~ExpressionAST() = default; + StringRef getExpressionStr() const { return ExpressionStr; } + /// Evaluates and \returns the value of the expression represented by this /// AST or an error if evaluation fails. - virtual Expected<uint64_t> eval() const = 0; + virtual Expected<ExpressionValue> eval() const = 0; + + /// \returns either the implicit format of this AST, a diagnostic against + /// \p SM if implicit formats of the AST's components conflict, or NoFormat + /// if the AST has no implicit format (e.g. AST is made up of a single + /// literal). + virtual Expected<ExpressionFormat> + getImplicitFormat(const SourceMgr &SM) const { + return ExpressionFormat(); + } }; /// Class representing an unsigned literal in the AST of an expression. class ExpressionLiteral : public ExpressionAST { private: /// Actual value of the literal. - uint64_t Value; + ExpressionValue Value; public: - /// Constructs a literal with the specified value. - ExpressionLiteral(uint64_t Val) : Value(Val) {} + template <class T> + explicit ExpressionLiteral(StringRef ExpressionStr, T Val) + : ExpressionAST(ExpressionStr), Value(Val) {} /// \returns the literal's value. - Expected<uint64_t> eval() const override { return Value; } + Expected<ExpressionValue> eval() const override { return Value; } }; /// Class to represent an undefined variable error, which quotes that @@ -78,14 +226,40 @@ public: } }; +/// Class representing an expression and its matching format. +class Expression { +private: + /// Pointer to AST of the expression. + std::unique_ptr<ExpressionAST> AST; + + /// Format to use (e.g. hex upper case letters) when matching the value. + ExpressionFormat Format; + +public: + /// Generic constructor for an expression represented by the given \p AST and + /// whose matching format is \p Format. + Expression(std::unique_ptr<ExpressionAST> AST, ExpressionFormat Format) + : AST(std::move(AST)), Format(Format) {} + + /// \returns pointer to AST of the expression. Pointer is guaranteed to be + /// valid as long as this object is. + ExpressionAST *getAST() const { return AST.get(); } + + ExpressionFormat getFormat() const { return Format; } +}; + /// Class representing a numeric variable and its associated current value. class NumericVariable { private: /// Name of the numeric variable. StringRef Name; + /// Format to use for expressions using this variable without an explicit + /// format. + ExpressionFormat ImplicitFormat; + /// Value of numeric variable, if defined, or None otherwise. - Optional<uint64_t> Value; + Optional<ExpressionValue> Value; /// Line number where this variable is defined, or None if defined before /// input is parsed. Used to determine whether a variable is defined on the @@ -93,20 +267,25 @@ private: Optional<size_t> DefLineNumber; public: - /// Constructor for a variable \p Name defined at line \p DefLineNumber or - /// defined before input is parsed if \p DefLineNumber is None. - explicit NumericVariable(StringRef Name, + /// Constructor for a variable \p Name with implicit format \p ImplicitFormat + /// defined at line \p DefLineNumber or defined before input is parsed if + /// \p DefLineNumber is None. + explicit NumericVariable(StringRef Name, ExpressionFormat ImplicitFormat, Optional<size_t> DefLineNumber = None) - : Name(Name), DefLineNumber(DefLineNumber) {} + : Name(Name), ImplicitFormat(ImplicitFormat), + DefLineNumber(DefLineNumber) {} /// \returns name of this numeric variable. StringRef getName() const { return Name; } + /// \returns implicit format of this numeric variable. + ExpressionFormat getImplicitFormat() const { return ImplicitFormat; } + /// \returns this variable's value. - Optional<uint64_t> getValue() const { return Value; } + Optional<ExpressionValue> getValue() const { return Value; } /// Sets value of this numeric variable to \p NewValue. - void setValue(uint64_t NewValue) { Value = NewValue; } + void setValue(ExpressionValue NewValue) { Value = NewValue; } /// Clears value of this numeric variable, regardless of whether it is /// currently defined or not. @@ -121,22 +300,25 @@ public: /// expression. class NumericVariableUse : public ExpressionAST { private: - /// Name of the numeric variable. - StringRef Name; - /// Pointer to the class instance for the variable this use is about. NumericVariable *Variable; public: NumericVariableUse(StringRef Name, NumericVariable *Variable) - : Name(Name), Variable(Variable) {} - + : ExpressionAST(Name), Variable(Variable) {} /// \returns the value of the variable referenced by this instance. - Expected<uint64_t> eval() const override; + Expected<ExpressionValue> eval() const override; + + /// \returns implicit format of this numeric variable. + Expected<ExpressionFormat> + getImplicitFormat(const SourceMgr &SM) const override { + return Variable->getImplicitFormat(); + } }; /// Type of functions evaluating a given binary operation. -using binop_eval_t = uint64_t (*)(uint64_t, uint64_t); +using binop_eval_t = Expected<ExpressionValue> (*)(const ExpressionValue &, + const ExpressionValue &); /// Class representing a single binary operation in the AST of an expression. class BinaryOperation : public ExpressionAST { @@ -151,9 +333,10 @@ private: binop_eval_t EvalBinop; public: - BinaryOperation(binop_eval_t EvalBinop, std::unique_ptr<ExpressionAST> LeftOp, + BinaryOperation(StringRef ExpressionStr, binop_eval_t EvalBinop, + std::unique_ptr<ExpressionAST> LeftOp, std::unique_ptr<ExpressionAST> RightOp) - : EvalBinop(EvalBinop) { + : ExpressionAST(ExpressionStr), EvalBinop(EvalBinop) { LeftOperand = std::move(LeftOp); RightOperand = std::move(RightOp); } @@ -162,7 +345,14 @@ public: /// using EvalBinop on the result of recursively evaluating the operands. /// \returns the expression value or an error if an undefined numeric /// variable is used in one of the operands. - Expected<uint64_t> eval() const override; + Expected<ExpressionValue> eval() const override; + + /// \returns the implicit format of this AST, if any, a diagnostic against + /// \p SM if the implicit formats of the AST's components conflict, or no + /// format if the AST has no implicit format (e.g. AST is made of a single + /// literal). + Expected<ExpressionFormat> + getImplicitFormat(const SourceMgr &SM) const override; }; class FileCheckPatternContext; @@ -218,14 +408,14 @@ class NumericSubstitution : public Substitution { private: /// Pointer to the class representing the expression whose value is to be /// substituted. - std::unique_ptr<ExpressionAST> ExpressionASTPointer; + std::unique_ptr<Expression> ExpressionPointer; public: - NumericSubstitution(FileCheckPatternContext *Context, StringRef Expr, - std::unique_ptr<ExpressionAST> ExprAST, size_t InsertIdx) - : Substitution(Context, Expr, InsertIdx) { - ExpressionASTPointer = std::move(ExprAST); - } + NumericSubstitution(FileCheckPatternContext *Context, StringRef ExpressionStr, + std::unique_ptr<Expression> ExpressionPointer, + size_t InsertIdx) + : Substitution(Context, ExpressionStr, InsertIdx), + ExpressionPointer(std::move(ExpressionPointer)) {} /// \returns a string containing the result of evaluating the expression in /// this substitution, or an error if evaluation failed. @@ -236,8 +426,6 @@ public: // Pattern handling code. //===----------------------------------------------------------------------===// -struct FileCheckDiag; - /// Class holding the Pattern global state, shared by all patterns: tables /// holding values of variables and whether they are defined or not at any /// given time in the matching process. @@ -270,6 +458,10 @@ private: /// automatically free them once they are guaranteed to no longer be used. std::vector<std::unique_ptr<NumericVariable>> NumericVariables; + /// Vector holding pointers to all parsed expressions. Used to automatically + /// free the expressions once they are guaranteed to no longer be used. + std::vector<std::unique_ptr<Expression>> Expressions; + /// Vector holding pointers to all substitutions. Used to automatically free /// them once they are guaranteed to no longer be used. std::vector<std::unique_ptr<Substitution>> Substitutions; @@ -283,7 +475,7 @@ public: /// command line, passed as a vector of [#]VAR=VAL strings in /// \p CmdlineDefines. \returns an error list containing diagnostics against /// \p SM for all definition parsing failures, if any, or Success otherwise. - Error defineCmdlineVariables(std::vector<std::string> &CmdlineDefines, + Error defineCmdlineVariables(ArrayRef<StringRef> CmdlineDefines, SourceMgr &SM); /// Create @LINE pseudo variable. Value is set when pattern are being @@ -307,10 +499,9 @@ private: /// Makes a new numeric substitution and registers it for destruction when /// the context is destroyed. - Substitution * - makeNumericSubstitution(StringRef ExpressionStr, - std::unique_ptr<ExpressionAST> ExpressionAST, - size_t InsertIdx); + Substitution *makeNumericSubstitution(StringRef ExpressionStr, + std::unique_ptr<Expression> Expression, + size_t InsertIdx); }; /// Class to represent an error holding a diagnostic with location information @@ -388,12 +579,12 @@ class Pattern { std::map<StringRef, unsigned> VariableDefs; /// Structure representing the definition of a numeric variable in a pattern. - /// It holds the pointer to the class representing the numeric variable whose - /// value is being defined and the number of the parenthesis group in - /// RegExStr to capture that value. + /// It holds the pointer to the class instance holding the value and matching + /// format of the numeric variable whose value is being defined and the + /// number of the parenthesis group in RegExStr to capture that value. struct NumericVariableMatch { - /// Pointer to class representing the numeric variable whose value is being - /// defined. + /// Pointer to class instance holding the value and matching format of the + /// numeric variable being defined. NumericVariable *DefinedNumericVariable; /// Number of the parenthesis group in RegExStr that captures the value of @@ -457,12 +648,12 @@ public: /// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE /// expression and \p Context points to the class instance holding the live /// string and numeric variables. \returns a pointer to the class instance - /// representing the AST of the expression whose value must be substitued, or - /// an error holding a diagnostic against \p SM if parsing fails. If - /// substitution was successful, sets \p DefinedNumericVariable to point to - /// the class representing the numeric variable defined in this numeric - /// substitution block, or None if this block does not define any variable. - static Expected<std::unique_ptr<ExpressionAST>> parseNumericSubstitutionBlock( + /// representing the expression whose value must be substitued, or an error + /// holding a diagnostic against \p SM if parsing fails. If substitution was + /// successful, sets \p DefinedNumericVariable to point to the class + /// representing the numeric variable defined in this numeric substitution + /// block, or None if this block does not define any variable. + static Expected<std::unique_ptr<Expression>> parseNumericSubstitutionBlock( StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable, bool IsLegacyLineExpr, Optional<size_t> LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); @@ -526,7 +717,8 @@ private: /// should defining such a variable be invalid. static Expected<NumericVariable *> parseNumericVariableDefinition( StringRef &Expr, FileCheckPatternContext *Context, - Optional<size_t> LineNumber, const SourceMgr &SM); + Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat, + const SourceMgr &SM); /// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use /// at line \p LineNumber, or before input is parsed if \p LineNumber is /// None. Parameter \p Context points to the class instance holding the live @@ -536,29 +728,56 @@ private: static Expected<std::unique_ptr<NumericVariableUse>> parseNumericVariableUse( StringRef Name, bool IsPseudo, Optional<size_t> LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); - enum class AllowedOperand { LineVar, Literal, Any }; + enum class AllowedOperand { LineVar, LegacyLiteral, Any }; /// Parses \p Expr for use of a numeric operand at line \p LineNumber, or - /// before input is parsed if \p LineNumber is None. Accepts both literal - /// values and numeric variables, depending on the value of \p AO. Parameter - /// \p Context points to the class instance holding the live string and - /// numeric variables. \returns the class representing that operand in the - /// AST of the expression or an error holding a diagnostic against \p SM - /// otherwise. + /// before input is parsed if \p LineNumber is None. Accepts literal values, + /// numeric variables and function calls, depending on the value of \p AO. + /// \p MaybeInvalidConstraint indicates whether the text being parsed could + /// be an invalid constraint. \p Context points to the class instance holding + /// the live string and numeric variables. \returns the class representing + /// that operand in the AST of the expression or an error holding a + /// diagnostic against \p SM otherwise. If \p Expr starts with a "(" this + /// function will attempt to parse a parenthesized expression. static Expected<std::unique_ptr<ExpressionAST>> - parseNumericOperand(StringRef &Expr, AllowedOperand AO, + parseNumericOperand(StringRef &Expr, AllowedOperand AO, bool ConstraintParsed, Optional<size_t> LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); - /// Parses \p Expr for a binary operation at line \p LineNumber, or before - /// input is parsed if \p LineNumber is None. The left operand of this binary - /// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether - /// we are parsing a legacy @LINE expression. Parameter \p Context points to - /// the class instance holding the live string and numeric variables. - /// \returns the class representing the binary operation in the AST of the - /// expression, or an error holding a diagnostic against \p SM otherwise. + /// Parses and updates \p RemainingExpr for a binary operation at line + /// \p LineNumber, or before input is parsed if \p LineNumber is None. The + /// left operand of this binary operation is given in \p LeftOp and \p Expr + /// holds the string for the full expression, including the left operand. + /// Parameter \p IsLegacyLineExpr indicates whether we are parsing a legacy + /// @LINE expression. Parameter \p Context points to the class instance + /// holding the live string and numeric variables. \returns the class + /// representing the binary operation in the AST of the expression, or an + /// error holding a diagnostic against \p SM otherwise. + static Expected<std::unique_ptr<ExpressionAST>> + parseBinop(StringRef Expr, StringRef &RemainingExpr, + std::unique_ptr<ExpressionAST> LeftOp, bool IsLegacyLineExpr, + Optional<size_t> LineNumber, FileCheckPatternContext *Context, + const SourceMgr &SM); + + /// Parses a parenthesized expression inside \p Expr at line \p LineNumber, or + /// before input is parsed if \p LineNumber is None. \p Expr must start with + /// a '('. Accepts both literal values and numeric variables. Parameter \p + /// Context points to the class instance holding the live string and numeric + /// variables. \returns the class representing that operand in the AST of the + /// expression or an error holding a diagnostic against \p SM otherwise. + static Expected<std::unique_ptr<ExpressionAST>> + parseParenExpr(StringRef &Expr, Optional<size_t> LineNumber, + FileCheckPatternContext *Context, const SourceMgr &SM); + + /// Parses \p Expr for an argument list belonging to a call to function \p + /// FuncName at line \p LineNumber, or before input is parsed if \p LineNumber + /// is None. Parameter \p FuncLoc is the source location used for diagnostics. + /// Parameter \p Context points to the class instance holding the live string + /// and numeric variables. \returns the class representing that call in the + /// AST of the expression or an error holding a diagnostic against \p SM + /// otherwise. static Expected<std::unique_ptr<ExpressionAST>> - parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp, - bool IsLegacyLineExpr, Optional<size_t> LineNumber, - FileCheckPatternContext *Context, const SourceMgr &SM); + parseCallExpr(StringRef &Expr, StringRef FuncName, + Optional<size_t> LineNumber, FileCheckPatternContext *Context, + const SourceMgr &SM); }; //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Support/FileCollector.cpp b/llvm/lib/Support/FileCollector.cpp index 47fca64137223..59755556a5a3a 100644 --- a/llvm/lib/Support/FileCollector.cpp +++ b/llvm/lib/Support/FileCollector.cpp @@ -8,6 +8,7 @@ #include "llvm/Support/FileCollector.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Twine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Process.h" @@ -34,7 +35,6 @@ static bool isCaseSensitivePath(StringRef Path) { FileCollector::FileCollector(std::string Root, std::string OverlayRoot) : Root(std::move(Root)), OverlayRoot(std::move(OverlayRoot)) { - sys::fs::create_directories(this->Root, true); } bool FileCollector::getRealPath(StringRef SrcPath, @@ -51,7 +51,7 @@ bool FileCollector::getRealPath(StringRef SrcPath, auto EC = sys::fs::real_path(Directory, RealPath); if (EC) return false; - SymlinkMap[Directory] = RealPath.str(); + SymlinkMap[Directory] = std::string(RealPath.str()); } else { RealPath = DirWithSymlink->second; } @@ -61,13 +61,19 @@ bool FileCollector::getRealPath(StringRef SrcPath, return true; } -void FileCollector::addFile(const Twine &file) { +void FileCollector::addFile(const Twine &File) { std::lock_guard<std::mutex> lock(Mutex); - std::string FileStr = file.str(); + std::string FileStr = File.str(); if (markAsSeen(FileStr)) addFileImpl(FileStr); } +void FileCollector::addDirectory(const Twine &Dir) { + assert(sys::fs::is_directory(Dir)); + std::error_code EC; + addDirectoryImpl(Dir, vfs::getRealFileSystem(), EC); +} + void FileCollector::addFileImpl(StringRef SrcPath) { // We need an absolute src path to append to the root. SmallString<256> AbsoluteSrc = SrcPath; @@ -101,6 +107,27 @@ void FileCollector::addFileImpl(StringRef SrcPath) { addFileToMapping(VirtualPath, DstPath); } +llvm::vfs::directory_iterator +FileCollector::addDirectoryImpl(const llvm::Twine &Dir, + IntrusiveRefCntPtr<vfs::FileSystem> FS, + std::error_code &EC) { + auto It = FS->dir_begin(Dir, EC); + if (EC) + return It; + addFile(Dir); + for (; !EC && It != llvm::vfs::directory_iterator(); It.increment(EC)) { + if (It->type() == sys::fs::file_type::regular_file || + It->type() == sys::fs::file_type::directory_file || + It->type() == sys::fs::file_type::symlink_file) { + addFile(It->path()); + } + } + if (EC) + return It; + // Return a new iterator. + return FS->dir_begin(Dir, EC); +} + /// Set the access and modification time for the given file from the given /// status object. static std::error_code @@ -123,6 +150,13 @@ copyAccessAndModificationTime(StringRef Filename, } std::error_code FileCollector::copyFiles(bool StopOnError) { + auto Err = sys::fs::create_directories(Root, /*IgnoreExisting=*/true); + if (Err) { + return Err; + } + + std::lock_guard<std::mutex> lock(Mutex); + for (auto &entry : VFSWriter.getMappings()) { // Create directory tree. if (std::error_code EC = @@ -171,7 +205,7 @@ std::error_code FileCollector::copyFiles(bool StopOnError) { return {}; } -std::error_code FileCollector::writeMapping(StringRef mapping_file) { +std::error_code FileCollector::writeMapping(StringRef MappingFile) { std::lock_guard<std::mutex> lock(Mutex); VFSWriter.setOverlayDir(OverlayRoot); @@ -179,7 +213,7 @@ std::error_code FileCollector::writeMapping(StringRef mapping_file) { VFSWriter.setUseExternalNames(false); std::error_code EC; - raw_fd_ostream os(mapping_file, EC, sys::fs::OF_Text); + raw_fd_ostream os(MappingFile, EC, sys::fs::OF_Text); if (EC) return EC; @@ -188,7 +222,7 @@ std::error_code FileCollector::writeMapping(StringRef mapping_file) { return {}; } -namespace { +namespace llvm { class FileCollectorFileSystem : public vfs::FileSystem { public: @@ -213,22 +247,7 @@ public: llvm::vfs::directory_iterator dir_begin(const llvm::Twine &Dir, std::error_code &EC) override { - auto It = FS->dir_begin(Dir, EC); - if (EC) - return It; - // Collect everything that's listed in case the user needs it. - Collector->addFile(Dir); - for (; !EC && It != llvm::vfs::directory_iterator(); It.increment(EC)) { - if (It->type() == sys::fs::file_type::regular_file || - It->type() == sys::fs::file_type::directory_file || - It->type() == sys::fs::file_type::symlink_file) { - Collector->addFile(It->path()); - } - } - if (EC) - return It; - // Return a new iterator. - return FS->dir_begin(Dir, EC); + return Collector->addDirectoryImpl(Dir, FS, EC); } std::error_code getRealPath(const Twine &Path, @@ -259,7 +278,7 @@ private: std::shared_ptr<FileCollector> Collector; }; -} // end anonymous namespace +} // namespace llvm IntrusiveRefCntPtr<vfs::FileSystem> FileCollector::createCollectorVFS(IntrusiveRefCntPtr<vfs::FileSystem> BaseFS, diff --git a/llvm/lib/Support/FileOutputBuffer.cpp b/llvm/lib/Support/FileOutputBuffer.cpp index 0a5306f684d4e..3342682270dcd 100644 --- a/llvm/lib/Support/FileOutputBuffer.cpp +++ b/llvm/lib/Support/FileOutputBuffer.cpp @@ -12,8 +12,8 @@ #include "llvm/Support/FileOutputBuffer.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" #include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" #include <system_error> @@ -172,6 +172,10 @@ FileOutputBuffer::create(StringRef Path, size_t Size, unsigned Flags) { if (Flags & F_executable) Mode |= fs::all_exe; + // If Size is zero, don't use mmap which will fail with EINVAL. + if (Size == 0) + return createInMemoryBuffer(Path, Size, Mode); + fs::file_status Stat; fs::status(Path, Stat); diff --git a/llvm/lib/Support/FileUtilities.cpp b/llvm/lib/Support/FileUtilities.cpp index d11fbb54dc0d8..e4a86bb69de4d 100644 --- a/llvm/lib/Support/FileUtilities.cpp +++ b/llvm/lib/Support/FileUtilities.cpp @@ -14,6 +14,7 @@ #include "llvm/Support/FileUtilities.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/MemoryBuffer.h" @@ -92,9 +93,9 @@ static bool CompareNumbers(const char *&F1P, const char *&F2P, // If one of the positions is at a space and the other isn't, chomp up 'til // the end of the space. - while (isspace(static_cast<unsigned char>(*F1P)) && F1P != F1End) + while (isSpace(static_cast<unsigned char>(*F1P)) && F1P != F1End) ++F1P; - while (isspace(static_cast<unsigned char>(*F2P)) && F2P != F2End) + while (isSpace(static_cast<unsigned char>(*F2P)) && F2P != F2End) ++F2P; // If we stop on numbers, compare their difference. @@ -318,9 +319,8 @@ llvm::Error llvm::writeFileAtomically( atomic_write_error::output_stream_error); } - if (const std::error_code Error = - sys::fs::rename(/*from=*/GeneratedUniqPath.c_str(), - /*to=*/FinalPath.str().c_str())) { + if (sys::fs::rename(/*from=*/GeneratedUniqPath.c_str(), + /*to=*/FinalPath.str().c_str())) { return llvm::make_error<AtomicFileWriteError>( atomic_write_error::failed_to_rename_temp_file); } diff --git a/llvm/lib/Support/FoldingSet.cpp b/llvm/lib/Support/FoldingSet.cpp index ce6f196e1060f..e3d7168305af5 100644 --- a/llvm/lib/Support/FoldingSet.cpp +++ b/llvm/lib/Support/FoldingSet.cpp @@ -13,6 +13,7 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" @@ -85,6 +86,10 @@ void FoldingSetNodeID::AddInteger(unsigned long long I) { void FoldingSetNodeID::AddString(StringRef String) { unsigned Size = String.size(); + + unsigned NumInserts = 1 + divideCeil(Size, 4); + Bits.reserve(Bits.size() + NumInserts); + Bits.push_back(Size); if (!Size) return; @@ -223,8 +228,6 @@ static void **AllocateBuckets(unsigned NumBuckets) { //===----------------------------------------------------------------------===// // FoldingSetBase Implementation -void FoldingSetBase::anchor() {} - FoldingSetBase::FoldingSetBase(unsigned Log2InitSize) { assert(5 < Log2InitSize && Log2InitSize < 32 && "Initial hash table size out of range"); @@ -266,8 +269,10 @@ void FoldingSetBase::clear() { NumNodes = 0; } -void FoldingSetBase::GrowBucketCount(unsigned NewBucketCount) { - assert((NewBucketCount > NumBuckets) && "Can't shrink a folding set with GrowBucketCount"); +void FoldingSetBase::GrowBucketCount(unsigned NewBucketCount, + const FoldingSetInfo &Info) { + assert((NewBucketCount > NumBuckets) && + "Can't shrink a folding set with GrowBucketCount"); assert(isPowerOf2_32(NewBucketCount) && "Bad bucket count!"); void **OldBuckets = Buckets; unsigned OldNumBuckets = NumBuckets; @@ -290,8 +295,9 @@ void FoldingSetBase::GrowBucketCount(unsigned NewBucketCount) { // Insert the node into the new bucket, after recomputing the hash. InsertNode(NodeInBucket, - GetBucketFor(ComputeNodeHash(NodeInBucket, TempID), - Buckets, NumBuckets)); + GetBucketFor(Info.ComputeNodeHash(this, NodeInBucket, TempID), + Buckets, NumBuckets), + Info); TempID.clear(); } } @@ -301,25 +307,24 @@ void FoldingSetBase::GrowBucketCount(unsigned NewBucketCount) { /// GrowHashTable - Double the size of the hash table and rehash everything. /// -void FoldingSetBase::GrowHashTable() { - GrowBucketCount(NumBuckets * 2); +void FoldingSetBase::GrowHashTable(const FoldingSetInfo &Info) { + GrowBucketCount(NumBuckets * 2, Info); } -void FoldingSetBase::reserve(unsigned EltCount) { +void FoldingSetBase::reserve(unsigned EltCount, const FoldingSetInfo &Info) { // This will give us somewhere between EltCount / 2 and // EltCount buckets. This puts us in the load factor // range of 1.0 - 2.0. if(EltCount < capacity()) return; - GrowBucketCount(PowerOf2Floor(EltCount)); + GrowBucketCount(PowerOf2Floor(EltCount), Info); } /// FindNodeOrInsertPos - Look up the node specified by ID. If it exists, /// return it. If not, return the insertion token that will make insertion /// faster. -FoldingSetBase::Node * -FoldingSetBase::FindNodeOrInsertPos(const FoldingSetNodeID &ID, - void *&InsertPos) { +FoldingSetBase::Node *FoldingSetBase::FindNodeOrInsertPos( + const FoldingSetNodeID &ID, void *&InsertPos, const FoldingSetInfo &Info) { unsigned IDHash = ID.ComputeHash(); void **Bucket = GetBucketFor(IDHash, Buckets, NumBuckets); void *Probe = *Bucket; @@ -328,7 +333,7 @@ FoldingSetBase::FindNodeOrInsertPos(const FoldingSetNodeID &ID, FoldingSetNodeID TempID; while (Node *NodeInBucket = GetNextPtr(Probe)) { - if (NodeEquals(NodeInBucket, ID, IDHash, TempID)) + if (Info.NodeEquals(this, NodeInBucket, ID, IDHash, TempID)) return NodeInBucket; TempID.clear(); @@ -343,13 +348,15 @@ FoldingSetBase::FindNodeOrInsertPos(const FoldingSetNodeID &ID, /// InsertNode - Insert the specified node into the folding set, knowing that it /// is not already in the map. InsertPos must be obtained from /// FindNodeOrInsertPos. -void FoldingSetBase::InsertNode(Node *N, void *InsertPos) { +void FoldingSetBase::InsertNode(Node *N, void *InsertPos, + const FoldingSetInfo &Info) { assert(!N->getNextInBucket()); // Do we need to grow the hashtable? if (NumNodes+1 > capacity()) { - GrowHashTable(); + GrowHashTable(Info); FoldingSetNodeID TempID; - InsertPos = GetBucketFor(ComputeNodeHash(N, TempID), Buckets, NumBuckets); + InsertPos = GetBucketFor(Info.ComputeNodeHash(this, N, TempID), Buckets, + NumBuckets); } ++NumNodes; @@ -413,13 +420,15 @@ bool FoldingSetBase::RemoveNode(Node *N) { /// GetOrInsertNode - If there is an existing simple Node exactly /// equal to the specified node, return it. Otherwise, insert 'N' and it /// instead. -FoldingSetBase::Node *FoldingSetBase::GetOrInsertNode(FoldingSetBase::Node *N) { +FoldingSetBase::Node * +FoldingSetBase::GetOrInsertNode(FoldingSetBase::Node *N, + const FoldingSetInfo &Info) { FoldingSetNodeID ID; - GetNodeProfile(N, ID); + Info.GetNodeProfile(this, N, ID); void *IP; - if (Node *E = FindNodeOrInsertPos(ID, IP)) + if (Node *E = FindNodeOrInsertPos(ID, IP, Info)) return E; - InsertNode(N, IP); + InsertNode(N, IP, Info); return N; } diff --git a/llvm/lib/Support/FormatVariadic.cpp b/llvm/lib/Support/FormatVariadic.cpp index f9e89f69b528c..632e879e540dd 100644 --- a/llvm/lib/Support/FormatVariadic.cpp +++ b/llvm/lib/Support/FormatVariadic.cpp @@ -6,6 +6,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/FormatVariadic.h" +#include <cassert> using namespace llvm; @@ -140,9 +141,9 @@ formatv_object_base::splitLiteralAndReplacement(StringRef Fmt) { return std::make_pair(ReplacementItem{Fmt}, StringRef()); } -std::vector<ReplacementItem> +SmallVector<ReplacementItem, 2> formatv_object_base::parseFormatString(StringRef Fmt) { - std::vector<ReplacementItem> Replacements; + SmallVector<ReplacementItem, 2> Replacements; ReplacementItem I; while (!Fmt.empty()) { std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt); diff --git a/llvm/lib/Support/FormattedStream.cpp b/llvm/lib/Support/FormattedStream.cpp index 4eb747038bb9e..5716afc187e48 100644 --- a/llvm/lib/Support/FormattedStream.cpp +++ b/llvm/lib/Support/FormattedStream.cpp @@ -11,7 +11,9 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/FormattedStream.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Debug.h" +#include "llvm/Support/Unicode.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -19,16 +21,22 @@ using namespace llvm; /// UpdatePosition - Examine the given char sequence and figure out which /// column we end up in after output, and how many line breaks are contained. -/// -static void UpdatePosition(std::pair<unsigned, unsigned> &Position, const char *Ptr, size_t Size) { +/// This assumes that the input string is well-formed UTF-8, and takes into +/// account Unicode characters which render as multiple columns wide. +void formatted_raw_ostream::UpdatePosition(const char *Ptr, size_t Size) { unsigned &Column = Position.first; unsigned &Line = Position.second; - // Keep track of the current column and line by scanning the string for - // special characters - for (const char *End = Ptr + Size; Ptr != End; ++Ptr) { - ++Column; - switch (*Ptr) { + auto ProcessUTF8CodePoint = [&Line, &Column](StringRef CP) { + int Width = sys::unicode::columnWidthUTF8(CP); + if (Width != sys::unicode::ErrorNonPrintableCharacter) + Column += Width; + + // The only special whitespace characters we care about are single-byte. + if (CP.size() > 1) + return; + + switch (CP[0]) { case '\n': Line += 1; LLVM_FALLTHROUGH; @@ -40,6 +48,46 @@ static void UpdatePosition(std::pair<unsigned, unsigned> &Position, const char * Column += (8 - (Column & 0x7)) & 0x7; break; } + }; + + // If we have a partial UTF-8 sequence from the previous buffer, check that + // first. + if (PartialUTF8Char.size()) { + size_t BytesFromBuffer = + getNumBytesForUTF8(PartialUTF8Char[0]) - PartialUTF8Char.size(); + if (Size < BytesFromBuffer) { + // If we still don't have enough bytes for a complete code point, just + // append what we have. + PartialUTF8Char.append(StringRef(Ptr, Size)); + return; + } else { + // The first few bytes from the buffer will complete the code point. + // Concatenate them and process their effect on the line and column + // numbers. + PartialUTF8Char.append(StringRef(Ptr, BytesFromBuffer)); + ProcessUTF8CodePoint(PartialUTF8Char); + PartialUTF8Char.clear(); + Ptr += BytesFromBuffer; + Size -= BytesFromBuffer; + } + } + + // Now scan the rest of the buffer. + unsigned NumBytes; + for (const char *End = Ptr + Size; Ptr < End; Ptr += NumBytes) { + NumBytes = getNumBytesForUTF8(*Ptr); + + // The buffer might end part way through a UTF-8 code unit sequence for a + // Unicode scalar value if it got flushed. If this happens, we can't know + // the display width until we see the rest of the code point. Stash the + // bytes we do have, so that we can reconstruct the whole code point later, + // even if the buffer is being flushed. + if ((unsigned)(End - Ptr) < NumBytes) { + PartialUTF8Char = StringRef(Ptr, End - Ptr); + return; + } + + ProcessUTF8CodePoint(StringRef(Ptr, NumBytes)); } } @@ -52,9 +100,9 @@ void formatted_raw_ostream::ComputePosition(const char *Ptr, size_t Size) { if (Ptr <= Scanned && Scanned <= Ptr + Size) // Scan all characters added since our last scan to determine the new // column. - UpdatePosition(Position, Scanned, Size - (Scanned - Ptr)); + UpdatePosition(Scanned, Size - (Scanned - Ptr)); else - UpdatePosition(Position, Ptr, Size); + UpdatePosition(Ptr, Size); // Update the scanning pointer. Scanned = Ptr + Size; diff --git a/llvm/lib/Support/GraphWriter.cpp b/llvm/lib/Support/GraphWriter.cpp index c689a81925d4c..d8aae92603231 100644 --- a/llvm/lib/Support/GraphWriter.cpp +++ b/llvm/lib/Support/GraphWriter.cpp @@ -76,17 +76,42 @@ StringRef llvm::DOT::getColorString(unsigned ColorNumber) { return Colors[ColorNumber % NumColors]; } +static std::string replaceIllegalFilenameChars(std::string Filename, + const char ReplacementChar) { +#ifdef _WIN32 + std::string IllegalChars = "\\/:?\"<>|"; +#else + std::string IllegalChars = "/"; +#endif + + for (char IllegalChar : IllegalChars) { + std::replace(Filename.begin(), Filename.end(), IllegalChar, + ReplacementChar); + } + + return Filename; +} + std::string llvm::createGraphFilename(const Twine &Name, int &FD) { FD = -1; SmallString<128> Filename; - std::error_code EC = sys::fs::createTemporaryFile(Name, "dot", FD, Filename); + + // Windows can't always handle long paths, so limit the length of the name. + std::string N = Name.str(); + N = N.substr(0, std::min<std::size_t>(N.size(), 140)); + + // Replace illegal characters in graph Filename with '_' if needed + std::string CleansedName = replaceIllegalFilenameChars(N, '_'); + + std::error_code EC = + sys::fs::createTemporaryFile(CleansedName, "dot", FD, Filename); if (EC) { errs() << "Error: " << EC.message() << "\n"; return ""; } errs() << "Writing '" << Filename << "'... "; - return Filename.str(); + return std::string(Filename.str()); } // Execute the graph viewer. Return true if there were errors. @@ -147,7 +172,7 @@ static const char *getProgramName(GraphProgram::Name program) { bool llvm::DisplayGraph(StringRef FilenameRef, bool wait, GraphProgram::Name program) { - std::string Filename = FilenameRef; + std::string Filename = std::string(FilenameRef); std::string ErrMsg; std::string ViewerPath; GraphSession S; diff --git a/llvm/lib/Support/Host.cpp b/llvm/lib/Support/Host.cpp index ef38c1c09413a..658c1ee74cfec 100644 --- a/llvm/lib/Support/Host.cpp +++ b/llvm/lib/Support/Host.cpp @@ -11,9 +11,9 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Host.h" -#include "llvm/Support/TargetParser.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" @@ -21,6 +21,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/X86TargetParser.h" #include "llvm/Support/raw_ostream.h" #include <assert.h> #include <string.h> @@ -28,6 +29,7 @@ // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/Host.inc" +#include <sched.h> #endif #ifdef _WIN32 #include "Windows/Host.inc" @@ -140,6 +142,7 @@ StringRef sys::detail::getHostCPUNameForPowerPC(StringRef ProcCpuinfoContent) { .Case("POWER8E", "pwr8") .Case("POWER8NVL", "pwr8") .Case("POWER9", "pwr9") + .Case("POWER10", "pwr10") // FIXME: If we get a simulator or machine with the capabilities of // mcpu=future, we should revisit this and add the name reported by the // simulator/machine. @@ -178,6 +181,8 @@ StringRef sys::detail::getHostCPUNameForARM(StringRef ProcCpuinfoContent) { // 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. + // This corresponds to the Main ID Register in Technical Reference Manuals. + // and is used in programs like sys-utils return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :")) .Case("0x926", "arm926ej-s") .Case("0xb02", "mpcore") @@ -190,6 +195,8 @@ StringRef sys::detail::getHostCPUNameForARM(StringRef ProcCpuinfoContent) { .Case("0xc20", "cortex-m0") .Case("0xc23", "cortex-m3") .Case("0xc24", "cortex-m4") + .Case("0xd22", "cortex-m55") + .Case("0xd02", "cortex-a34") .Case("0xd04", "cortex-a35") .Case("0xd03", "cortex-a53") .Case("0xd07", "cortex-a57") @@ -197,6 +204,10 @@ StringRef sys::detail::getHostCPUNameForARM(StringRef ProcCpuinfoContent) { .Case("0xd09", "cortex-a73") .Case("0xd0a", "cortex-a75") .Case("0xd0b", "cortex-a76") + .Case("0xd0d", "cortex-a77") + .Case("0xd41", "cortex-a78") + .Case("0xd44", "cortex-x1") + .Case("0xd0c", "neoverse-n1") .Default("generic"); } @@ -215,6 +226,26 @@ StringRef sys::detail::getHostCPUNameForARM(StringRef ProcCpuinfoContent) { } } + if (Implementer == "0x46") { // Fujitsu Ltd. + for (unsigned I = 0, E = Lines.size(); I != E; ++I) { + if (Lines[I].startswith("CPU part")) { + return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :")) + .Case("0x001", "a64fx") + .Default("generic"); + } + } + } + + if (Implementer == "0x4e") { // NVIDIA Corporation + for (unsigned I = 0, E = Lines.size(); I != E; ++I) { + if (Lines[I].startswith("CPU part")) { + return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :")) + .Case("0x004", "carmel") + .Default("generic"); + } + } + } + if (Implementer == "0x48") // HiSilicon Technologies, Inc. // Look for the CPU part line. for (unsigned I = 0, E = Lines.size(); I != E; ++I) @@ -552,58 +583,32 @@ static void detectX86FamilyModel(unsigned EAX, unsigned *Family, } } -static void +static StringRef getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, - unsigned Brand_id, unsigned Features, - unsigned Features2, unsigned Features3, + const unsigned *Features, unsigned *Type, unsigned *Subtype) { - if (Brand_id != 0) - return; + auto testFeature = [&](unsigned F) { + return (Features[F / 32] & (1U << (F % 32))) != 0; + }; + + StringRef CPU; + switch (Family) { case 3: - *Type = X86::INTEL_i386; + CPU = "i386"; break; case 4: - *Type = X86::INTEL_i486; + CPU = "i486"; break; case 5: - if (Features & (1 << X86::FEATURE_MMX)) { - *Type = X86::INTEL_PENTIUM_MMX; + if (testFeature(X86::FEATURE_MMX)) { + CPU = "pentium-mmx"; break; } - *Type = X86::INTEL_PENTIUM; + CPU = "pentium"; break; case 6: switch (Model) { - case 0x01: // Pentium Pro processor - *Type = X86::INTEL_PENTIUM_PRO; - break; - case 0x03: // Intel Pentium II OverDrive processor, Pentium II processor, - // model 03 - case 0x05: // Pentium II processor, model 05, Pentium II Xeon processor, - // model 05, and Intel Celeron processor, model 05 - case 0x06: // Celeron processor, model 06 - *Type = X86::INTEL_PENTIUM_II; - break; - case 0x07: // Pentium III processor, model 07, and Pentium III Xeon - // processor, model 07 - case 0x08: // Pentium III processor, model 08, Pentium III Xeon processor, - // model 08, and Celeron processor, model 08 - case 0x0a: // Pentium III Xeon processor, model 0Ah - case 0x0b: // Pentium III processor, model 0Bh - *Type = X86::INTEL_PENTIUM_III; - break; - case 0x09: // Intel Pentium M processor, Intel Celeron M processor model 09. - case 0x0d: // Intel Pentium M processor, Intel Celeron M processor, model - // 0Dh. All processors are manufactured using the 90 nm process. - case 0x15: // Intel EP80579 Integrated Processor and Intel EP80579 - // Integrated Processor with Intel QuickAssist Technology - *Type = X86::INTEL_PENTIUM_M; - break; - case 0x0e: // Intel Core Duo processor, Intel Core Solo processor, model - // 0Eh. All processors are manufactured using the 65 nm process. - *Type = X86::INTEL_CORE_DUO; - break; // yonah case 0x0f: // Intel Core 2 Duo processor, Intel Core 2 Duo mobile // processor, Intel Core 2 Quad processor, Intel Core 2 Quad // mobile processor, Intel Core 2 Extreme processor, Intel @@ -611,8 +616,8 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, // 0Fh. All processors are manufactured using the 65 nm process. case 0x16: // Intel Celeron processor model 16h. All processors are // manufactured using the 65 nm process - *Type = X86::INTEL_CORE2; // "core2" - *Subtype = X86::INTEL_CORE2_65; + CPU = "core2"; + *Type = X86::INTEL_CORE2; break; case 0x17: // Intel Core 2 Extreme processor, Intel Xeon processor, model // 17h. All processors are manufactured using the 45 nm process. @@ -620,34 +625,38 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, // 45nm: Penryn , Wolfdale, Yorkfield (XE) case 0x1d: // Intel Xeon processor MP. All processors are manufactured using // the 45 nm process. - *Type = X86::INTEL_CORE2; // "penryn" - *Subtype = X86::INTEL_CORE2_45; + CPU = "penryn"; + *Type = X86::INTEL_CORE2; break; case 0x1a: // Intel Core i7 processor and Intel Xeon processor. All // processors are manufactured using the 45 nm process. case 0x1e: // Intel(R) Core(TM) i7 CPU 870 @ 2.93GHz. // As found in a Summer 2010 model iMac. case 0x1f: - case 0x2e: // Nehalem EX - *Type = X86::INTEL_COREI7; // "nehalem" + case 0x2e: // Nehalem EX + CPU = "nehalem"; + *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_NEHALEM; break; case 0x25: // Intel Core i7, laptop version. case 0x2c: // Intel Core i7 processor and Intel Xeon processor. All // processors are manufactured using the 32 nm process. case 0x2f: // Westmere EX - *Type = X86::INTEL_COREI7; // "westmere" + CPU = "westmere"; + *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_WESTMERE; break; case 0x2a: // Intel Core i7 processor. All processors are manufactured // using the 32 nm process. case 0x2d: - *Type = X86::INTEL_COREI7; //"sandybridge" + CPU = "sandybridge"; + *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_SANDYBRIDGE; break; case 0x3a: - case 0x3e: // Ivy Bridge EP - *Type = X86::INTEL_COREI7; // "ivybridge" + case 0x3e: // Ivy Bridge EP + CPU = "ivybridge"; + *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_IVYBRIDGE; break; @@ -656,7 +665,8 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, case 0x3f: case 0x45: case 0x46: - *Type = X86::INTEL_COREI7; // "haswell" + CPU = "haswell"; + *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_HASWELL; break; @@ -665,7 +675,8 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, case 0x47: case 0x4f: case 0x56: - *Type = X86::INTEL_COREI7; // "broadwell" + CPU = "broadwell"; + *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_BROADWELL; break; @@ -674,39 +685,49 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, case 0x5e: // Skylake desktop case 0x8e: // Kaby Lake mobile case 0x9e: // Kaby Lake desktop - *Type = X86::INTEL_COREI7; // "skylake" + case 0xa5: // Comet Lake-H/S + case 0xa6: // Comet Lake-U + CPU = "skylake"; + *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_SKYLAKE; break; // Skylake Xeon: case 0x55: *Type = X86::INTEL_COREI7; - if (Features2 & (1 << (X86::FEATURE_AVX512BF16 - 32))) - *Subtype = X86::INTEL_COREI7_COOPERLAKE; // "cooperlake" - else if (Features2 & (1 << (X86::FEATURE_AVX512VNNI - 32))) - *Subtype = X86::INTEL_COREI7_CASCADELAKE; // "cascadelake" - else - *Subtype = X86::INTEL_COREI7_SKYLAKE_AVX512; // "skylake-avx512" + if (testFeature(X86::FEATURE_AVX512BF16)) { + CPU = "cooperlake"; + *Subtype = X86::INTEL_COREI7_COOPERLAKE; + } else if (testFeature(X86::FEATURE_AVX512VNNI)) { + CPU = "cascadelake"; + *Subtype = X86::INTEL_COREI7_CASCADELAKE; + } else { + CPU = "skylake-avx512"; + *Subtype = X86::INTEL_COREI7_SKYLAKE_AVX512; + } break; // Cannonlake: case 0x66: + CPU = "cannonlake"; *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_CANNONLAKE; // "cannonlake" + *Subtype = X86::INTEL_COREI7_CANNONLAKE; break; // Icelake: case 0x7d: case 0x7e: + CPU = "icelake-client"; *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_ICELAKE_CLIENT; // "icelake-client" + *Subtype = X86::INTEL_COREI7_ICELAKE_CLIENT; break; // Icelake Xeon: case 0x6a: case 0x6c: + CPU = "icelake-server"; *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_ICELAKE_SERVER; // "icelake-server" + *Subtype = X86::INTEL_COREI7_ICELAKE_SERVER; break; case 0x1c: // Most 45 nm Intel Atom processors @@ -714,8 +735,9 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, case 0x27: // 32 nm Atom Medfield case 0x35: // 32 nm Atom Midview case 0x36: // 32 nm Atom Midview + CPU = "bonnell"; *Type = X86::INTEL_BONNELL; - break; // "bonnell" + break; // Atom Silvermont codes from the Intel software optimization guide. case 0x37: @@ -724,14 +746,17 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, case 0x5a: case 0x5d: case 0x4c: // really airmont + CPU = "silvermont"; *Type = X86::INTEL_SILVERMONT; - break; // "silvermont" + break; // Goldmont: case 0x5c: // Apollo Lake case 0x5f: // Denverton + CPU = "goldmont"; *Type = X86::INTEL_GOLDMONT; - break; // "goldmont" + break; case 0x7a: + CPU = "goldmont-plus"; *Type = X86::INTEL_GOLDMONT_PLUS; break; case 0x86: @@ -739,189 +764,140 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, break; case 0x57: - *Type = X86::INTEL_KNL; // knl + CPU = "tremont"; + *Type = X86::INTEL_KNL; break; case 0x85: - *Type = X86::INTEL_KNM; // knm + CPU = "knm"; + *Type = X86::INTEL_KNM; break; default: // Unknown family 6 CPU, try to guess. - // TODO detect tigerlake host - if (Features3 & (1 << (X86::FEATURE_AVX512VP2INTERSECT - 64))) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_TIGERLAKE; - break; - } - - if (Features & (1 << X86::FEATURE_AVX512VBMI2)) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_ICELAKE_CLIENT; - break; - } - - if (Features & (1 << X86::FEATURE_AVX512VBMI)) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_CANNONLAKE; - break; - } - - if (Features2 & (1 << (X86::FEATURE_AVX512BF16 - 32))) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_COOPERLAKE; - break; - } - - if (Features2 & (1 << (X86::FEATURE_AVX512VNNI - 32))) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_CASCADELAKE; - break; + // Don't both with Type/Subtype here, they aren't used by the caller. + // They're used above to keep the code in sync with compiler-rt. + // TODO detect tigerlake host from model + if (testFeature(X86::FEATURE_AVX512VP2INTERSECT)) { + CPU = "tigerlake"; + } else if (testFeature(X86::FEATURE_AVX512VBMI2)) { + CPU = "icelake-client"; + } else if (testFeature(X86::FEATURE_AVX512VBMI)) { + CPU = "cannonlake"; + } else if (testFeature(X86::FEATURE_AVX512BF16)) { + CPU = "cooperlake"; + } else if (testFeature(X86::FEATURE_AVX512VNNI)) { + CPU = "cascadelake"; + } else if (testFeature(X86::FEATURE_AVX512VL)) { + CPU = "skylake-avx512"; + } else if (testFeature(X86::FEATURE_AVX512ER)) { + CPU = "knl"; + } else if (testFeature(X86::FEATURE_CLFLUSHOPT)) { + if (testFeature(X86::FEATURE_SHA)) + CPU = "goldmont"; + else + CPU = "skylake"; + } else if (testFeature(X86::FEATURE_ADX)) { + CPU = "broadwell"; + } else if (testFeature(X86::FEATURE_AVX2)) { + CPU = "haswell"; + } else if (testFeature(X86::FEATURE_AVX)) { + CPU = "sandybridge"; + } else if (testFeature(X86::FEATURE_SSE4_2)) { + if (testFeature(X86::FEATURE_MOVBE)) + CPU = "silvermont"; + else + CPU = "nehalem"; + } else if (testFeature(X86::FEATURE_SSE4_1)) { + CPU = "penryn"; + } else if (testFeature(X86::FEATURE_SSSE3)) { + if (testFeature(X86::FEATURE_MOVBE)) + CPU = "bonnell"; + else + CPU = "core2"; + } else if (testFeature(X86::FEATURE_64BIT)) { + CPU = "core2"; + } else if (testFeature(X86::FEATURE_SSE3)) { + CPU = "yonah"; + } else if (testFeature(X86::FEATURE_SSE2)) { + CPU = "pentium-m"; + } else if (testFeature(X86::FEATURE_SSE)) { + CPU = "pentium3"; + } else if (testFeature(X86::FEATURE_MMX)) { + CPU = "pentium2"; + } else { + CPU = "pentiumpro"; } - - if (Features & (1 << X86::FEATURE_AVX512VL)) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_SKYLAKE_AVX512; - break; - } - - if (Features & (1 << X86::FEATURE_AVX512ER)) { - *Type = X86::INTEL_KNL; // knl - break; - } - - if (Features3 & (1 << (X86::FEATURE_CLFLUSHOPT - 64))) { - if (Features3 & (1 << (X86::FEATURE_SHA - 64))) { - *Type = X86::INTEL_GOLDMONT; - } else { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_SKYLAKE; - } - break; - } - if (Features3 & (1 << (X86::FEATURE_ADX - 64))) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_BROADWELL; - break; - } - if (Features & (1 << X86::FEATURE_AVX2)) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_HASWELL; - break; - } - if (Features & (1 << X86::FEATURE_AVX)) { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_SANDYBRIDGE; - break; - } - if (Features & (1 << X86::FEATURE_SSE4_2)) { - if (Features3 & (1 << (X86::FEATURE_MOVBE - 64))) { - *Type = X86::INTEL_SILVERMONT; - } else { - *Type = X86::INTEL_COREI7; - *Subtype = X86::INTEL_COREI7_NEHALEM; - } - break; - } - if (Features & (1 << X86::FEATURE_SSE4_1)) { - *Type = X86::INTEL_CORE2; // "penryn" - *Subtype = X86::INTEL_CORE2_45; - break; - } - if (Features & (1 << X86::FEATURE_SSSE3)) { - if (Features3 & (1 << (X86::FEATURE_MOVBE - 64))) { - *Type = X86::INTEL_BONNELL; // "bonnell" - } else { - *Type = X86::INTEL_CORE2; // "core2" - *Subtype = X86::INTEL_CORE2_65; - } - break; - } - if (Features3 & (1 << (X86::FEATURE_EM64T - 64))) { - *Type = X86::INTEL_CORE2; // "core2" - *Subtype = X86::INTEL_CORE2_65; - break; - } - if (Features & (1 << X86::FEATURE_SSE3)) { - *Type = X86::INTEL_CORE_DUO; - break; - } - if (Features & (1 << X86::FEATURE_SSE2)) { - *Type = X86::INTEL_PENTIUM_M; - break; - } - if (Features & (1 << X86::FEATURE_SSE)) { - *Type = X86::INTEL_PENTIUM_III; - break; - } - if (Features & (1 << X86::FEATURE_MMX)) { - *Type = X86::INTEL_PENTIUM_II; - break; - } - *Type = X86::INTEL_PENTIUM_PRO; break; } break; case 15: { - if (Features3 & (1 << (X86::FEATURE_EM64T - 64))) { - *Type = X86::INTEL_NOCONA; + if (testFeature(X86::FEATURE_64BIT)) { + CPU = "nocona"; break; } - if (Features & (1 << X86::FEATURE_SSE3)) { - *Type = X86::INTEL_PRESCOTT; + if (testFeature(X86::FEATURE_SSE3)) { + CPU = "prescott"; break; } - *Type = X86::INTEL_PENTIUM_IV; + CPU = "pentium4"; break; } default: - break; /*"generic"*/ + break; // Unknown. } + + return CPU; } -static void getAMDProcessorTypeAndSubtype(unsigned Family, unsigned Model, - unsigned Features, unsigned *Type, - unsigned *Subtype) { - // FIXME: this poorly matches the generated SubtargetFeatureKV table. There - // appears to be no way to generate the wide variety of AMD-specific targets - // from the information returned from CPUID. +static StringRef +getAMDProcessorTypeAndSubtype(unsigned Family, unsigned Model, + const unsigned *Features, + unsigned *Type, unsigned *Subtype) { + auto testFeature = [&](unsigned F) { + return (Features[F / 32] & (1U << (F % 32))) != 0; + }; + + StringRef CPU; + switch (Family) { case 4: - *Type = X86::AMD_i486; + CPU = "i486"; break; case 5: - *Type = X86::AMDPENTIUM; + CPU = "pentium"; switch (Model) { case 6: case 7: - *Subtype = X86::AMDPENTIUM_K6; - break; // "k6" + CPU = "k6"; + break; case 8: - *Subtype = X86::AMDPENTIUM_K62; - break; // "k6-2" + CPU = "k6-2"; + break; case 9: case 13: - *Subtype = X86::AMDPENTIUM_K63; - break; // "k6-3" + CPU = "k6-3"; + break; case 10: - *Subtype = X86::AMDPENTIUM_GEODE; - break; // "geode" + CPU = "geode"; + break; } break; case 6: - if (Features & (1 << X86::FEATURE_SSE)) { - *Type = X86::AMD_ATHLON_XP; - break; // "athlon-xp" + if (testFeature(X86::FEATURE_SSE)) { + CPU = "athlon-xp"; + break; } - *Type = X86::AMD_ATHLON; - break; // "athlon" + CPU = "athlon"; + break; case 15: - if (Features & (1 << X86::FEATURE_SSE3)) { - *Type = X86::AMD_K8SSE3; - break; // "k8-sse3" + if (testFeature(X86::FEATURE_SSE3)) { + CPU = "k8-sse3"; + break; } - *Type = X86::AMD_K8; - break; // "k8" + CPU = "k8"; + break; case 16: + CPU = "amdfam10"; *Type = X86::AMDFAM10H; // "amdfam10" switch (Model) { case 2: @@ -936,63 +912,62 @@ static void getAMDProcessorTypeAndSubtype(unsigned Family, unsigned Model, } break; case 20: + CPU = "btver1"; *Type = X86::AMD_BTVER1; - break; // "btver1"; + break; case 21: + CPU = "bdver1"; *Type = X86::AMDFAM15H; if (Model >= 0x60 && Model <= 0x7f) { + CPU = "bdver4"; *Subtype = X86::AMDFAM15H_BDVER4; - break; // "bdver4"; 60h-7Fh: Excavator + break; // 60h-7Fh: Excavator } if (Model >= 0x30 && Model <= 0x3f) { + CPU = "bdver3"; *Subtype = X86::AMDFAM15H_BDVER3; - break; // "bdver3"; 30h-3Fh: Steamroller + break; // 30h-3Fh: Steamroller } if ((Model >= 0x10 && Model <= 0x1f) || Model == 0x02) { + CPU = "bdver2"; *Subtype = X86::AMDFAM15H_BDVER2; - break; // "bdver2"; 02h, 10h-1Fh: Piledriver + break; // 02h, 10h-1Fh: Piledriver } if (Model <= 0x0f) { *Subtype = X86::AMDFAM15H_BDVER1; - break; // "bdver1"; 00h-0Fh: Bulldozer + break; // 00h-0Fh: Bulldozer } break; case 22: + CPU = "btver2"; *Type = X86::AMD_BTVER2; - break; // "btver2" + break; case 23: + CPU = "znver1"; *Type = X86::AMDFAM17H; if ((Model >= 0x30 && Model <= 0x3f) || Model == 0x71) { + CPU = "znver2"; *Subtype = X86::AMDFAM17H_ZNVER2; - break; // "znver2"; 30h-3fh, 71h: Zen2 + break; // 30h-3fh, 71h: Zen2 } if (Model <= 0x0f) { *Subtype = X86::AMDFAM17H_ZNVER1; - break; // "znver1"; 00h-0Fh: Zen1 + break; // 00h-0Fh: Zen1 } break; default: - break; // "generic" + break; // Unknown AMD CPU. } + + return CPU; } static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf, - unsigned *FeaturesOut, unsigned *Features2Out, - unsigned *Features3Out) { - unsigned Features = 0; - unsigned Features2 = 0; - unsigned Features3 = 0; + unsigned *Features) { unsigned EAX, EBX; auto setFeature = [&](unsigned F) { - if (F < 32) - Features |= 1U << (F & 0x1f); - else if (F < 64) - Features2 |= 1U << ((F - 32) & 0x1f); - else if (F < 96) - Features3 |= 1U << ((F - 64) & 0x1f); - else - llvm_unreachable("Unexpected FeatureBit"); + Features[F / 32] |= 1U << (F % 32); }; if ((EDX >> 15) & 1) @@ -1115,56 +1090,42 @@ static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf, setFeature(X86::FEATURE_FMA4); if (HasExtLeaf1 && ((EDX >> 29) & 1)) - setFeature(X86::FEATURE_EM64T); - - *FeaturesOut = Features; - *Features2Out = Features2; - *Features3Out = Features3; + setFeature(X86::FEATURE_64BIT); } StringRef sys::getHostCPUName() { unsigned EAX = 0, EBX = 0, ECX = 0, EDX = 0; unsigned MaxLeaf, Vendor; -#if defined(__GNUC__) || defined(__clang__) - //FIXME: include cpuid.h from clang or copy __get_cpuid_max here - // and simplify it to not invoke __cpuid (like cpu_model.c in - // compiler-rt/lib/builtins/cpu_model.c? - // Opting for the second option. - if(!isCpuIdSupported()) + if (!isCpuIdSupported()) return "generic"; -#endif + if (getX86CpuIDAndInfo(0, &MaxLeaf, &Vendor, &ECX, &EDX) || MaxLeaf < 1) return "generic"; getX86CpuIDAndInfo(0x1, &EAX, &EBX, &ECX, &EDX); - unsigned Brand_id = EBX & 0xff; unsigned Family = 0, Model = 0; - unsigned Features = 0, Features2 = 0, Features3 = 0; + unsigned Features[(X86::CPU_FEATURE_MAX + 31) / 32] = {0}; detectX86FamilyModel(EAX, &Family, &Model); - getAvailableFeatures(ECX, EDX, MaxLeaf, &Features, &Features2, &Features3); + getAvailableFeatures(ECX, EDX, MaxLeaf, Features); + // These aren't consumed in this file, but we try to keep some source code the + // same or similar to compiler-rt. unsigned Type = 0; unsigned Subtype = 0; + StringRef CPU; + if (Vendor == SIG_INTEL) { - getIntelProcessorTypeAndSubtype(Family, Model, Brand_id, Features, - Features2, Features3, &Type, &Subtype); + CPU = getIntelProcessorTypeAndSubtype(Family, Model, Features, &Type, + &Subtype); } else if (Vendor == SIG_AMD) { - getAMDProcessorTypeAndSubtype(Family, Model, Features, &Type, &Subtype); + CPU = getAMDProcessorTypeAndSubtype(Family, Model, Features, &Type, + &Subtype); } - // Check subtypes first since those are more specific. -#define X86_CPU_SUBTYPE(ARCHNAME, ENUM) \ - if (Subtype == X86::ENUM) \ - return ARCHNAME; -#include "llvm/Support/X86TargetParser.def" - - // Now check types. -#define X86_CPU_TYPE(ARCHNAME, ENUM) \ - if (Type == X86::ENUM) \ - return ARCHNAME; -#include "llvm/Support/X86TargetParser.def" + if (!CPU.empty()) + return CPU; return "generic"; } @@ -1255,18 +1216,25 @@ StringRef sys::getHostCPUName() { return "swift"; default:; } - + return "generic"; } #else StringRef sys::getHostCPUName() { return "generic"; } #endif -#if defined(__linux__) && defined(__x86_64__) +#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__)) // On Linux, the number of physical cores can be computed from /proc/cpuinfo, // using the number of unique physical/core id pairs. The following // implementation reads the /proc/cpuinfo format on an x86_64 system. -static int computeHostNumPhysicalCores() { +int computeHostNumPhysicalCores() { + // Enabled represents the number of physical id/core id pairs with at least + // one processor id enabled by the CPU affinity mask. + cpu_set_t Affinity, Enabled; + if (sched_getaffinity(0, sizeof(Affinity), &Affinity) != 0) + return -1; + CPU_ZERO(&Enabled); + // Read /proc/cpuinfo as a stream (until EOF reached). It cannot be // mmapped because it appears to have 0 size. llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text = @@ -1279,40 +1247,36 @@ static int computeHostNumPhysicalCores() { SmallVector<StringRef, 8> strs; (*Text)->getBuffer().split(strs, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + int CurProcessor = -1; int CurPhysicalId = -1; + int CurSiblings = -1; int CurCoreId = -1; - SmallSet<std::pair<int, int>, 32> UniqueItems; - for (auto &Line : strs) { - Line = Line.trim(); - if (!Line.startswith("physical id") && !Line.startswith("core id")) - continue; + for (StringRef Line : strs) { std::pair<StringRef, StringRef> Data = Line.split(':'); auto Name = Data.first.trim(); auto Val = Data.second.trim(); - if (Name == "physical id") { - assert(CurPhysicalId == -1 && - "Expected a core id before seeing another physical id"); + // These fields are available if the kernel is configured with CONFIG_SMP. + if (Name == "processor") + Val.getAsInteger(10, CurProcessor); + else if (Name == "physical id") Val.getAsInteger(10, CurPhysicalId); - } - if (Name == "core id") { - assert(CurCoreId == -1 && - "Expected a physical id before seeing another core id"); + else if (Name == "siblings") + Val.getAsInteger(10, CurSiblings); + else if (Name == "core id") { Val.getAsInteger(10, CurCoreId); - } - if (CurPhysicalId != -1 && CurCoreId != -1) { - UniqueItems.insert(std::make_pair(CurPhysicalId, CurCoreId)); - CurPhysicalId = -1; - CurCoreId = -1; + // The processor id corresponds to an index into cpu_set_t. + if (CPU_ISSET(CurProcessor, &Affinity)) + CPU_SET(CurPhysicalId * CurSiblings + CurCoreId, &Enabled); } } - return UniqueItems.size(); + return CPU_COUNT(&Enabled); } #elif defined(__APPLE__) && defined(__x86_64__) #include <sys/param.h> #include <sys/sysctl.h> // Gets the number of *physical cores* on the machine. -static int computeHostNumPhysicalCores() { +int computeHostNumPhysicalCores() { uint32_t count; size_t len = sizeof(count); sysctlbyname("hw.physicalcpu", &count, &len, NULL, 0); @@ -1326,6 +1290,9 @@ static int computeHostNumPhysicalCores() { } return count; } +#elif defined(_WIN32) && LLVM_ENABLE_THREADS != 0 +// Defined in llvm/lib/Support/Windows/Threading.inc +int computeHostNumPhysicalCores(); #else // On other systems, return -1 to indicate unknown. static int computeHostNumPhysicalCores() { return -1; } @@ -1341,13 +1308,8 @@ int sys::getHostNumPhysicalCores() { bool sys::getHostCPUFeatures(StringMap<bool> &Features) { unsigned EAX = 0, EBX = 0, ECX = 0, EDX = 0; unsigned MaxLevel; - union { - unsigned u[3]; - char c[12]; - } text; - if (getX86CpuIDAndInfo(0, &MaxLevel, text.u + 0, text.u + 2, text.u + 1) || - MaxLevel < 1) + if (getX86CpuIDAndInfo(0, &MaxLevel, &EBX, &ECX, &EDX) || MaxLevel < 1) return false; getX86CpuIDAndInfo(1, &EAX, &EBX, &ECX, &EDX); @@ -1373,8 +1335,8 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) { // If CPUID indicates support for XSAVE, XRESTORE and AVX, and XGETBV // indicates that the AVX registers will be saved and restored on context // switch, then we have full AVX support. - bool HasAVXSave = ((ECX >> 27) & 1) && ((ECX >> 28) & 1) && - !getX86XCR0(&EAX, &EDX) && ((EAX & 0x6) == 0x6); + bool HasXSave = ((ECX >> 27) & 1) && !getX86XCR0(&EAX, &EDX); + bool HasAVXSave = HasXSave && ((ECX >> 28) & 1) && ((EAX & 0x6) == 0x6); #if defined(__APPLE__) // Darwin lazily saves the AVX512 context on first use: trust that the OS will // save the AVX512 context if we use AVX512 instructions, even the bit is not @@ -1384,6 +1346,9 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) { // AVX512 requires additional context to be saved by the OS. bool HasAVX512Save = HasAVXSave && ((EAX & 0xe0) == 0xe0); #endif + // AMX requires additional context to be saved by the OS. + const unsigned AMXBits = (1 << 17) | (1 << 18); + bool HasAMXSave = HasXSave && ((EAX & AMXBits) == AMXBits); Features["avx"] = HasAVXSave; Features["fma"] = ((ECX >> 12) & 1) && HasAVXSave; @@ -1459,6 +1424,10 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) { Features["movdir64b"] = HasLeaf7 && ((ECX >> 28) & 1); Features["enqcmd"] = HasLeaf7 && ((ECX >> 29) & 1); + Features["avx512vp2intersect"] = + HasLeaf7 && ((EDX >> 8) & 1) && HasAVX512Save; + Features["serialize"] = HasLeaf7 && ((EDX >> 14) & 1); + Features["tsxldtrk"] = HasLeaf7 && ((EDX >> 16) & 1); // There are two CPUID leafs which information associated with the pconfig // instruction: // EAX=0x7, ECX=0x0 indicates the availability of the instruction (via the 18th @@ -1470,6 +1439,9 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) { // detecting features using the "-march=native" flag. // For more info, see X86 ISA docs. Features["pconfig"] = HasLeaf7 && ((EDX >> 18) & 1); + Features["amx-bf16"] = HasLeaf7 && ((EDX >> 22) & 1) && HasAMXSave; + Features["amx-tile"] = HasLeaf7 && ((EDX >> 24) & 1) && HasAMXSave; + Features["amx-int8"] = HasLeaf7 && ((EDX >> 25) & 1) && HasAMXSave; bool HasLeaf7Subleaf1 = MaxLevel >= 7 && !getX86CpuIDAndInfoEx(0x7, 0x1, &EAX, &EBX, &ECX, &EDX); Features["avx512bf16"] = HasLeaf7Subleaf1 && ((EAX >> 5) & 1) && HasAVX512Save; diff --git a/llvm/lib/Support/InitLLVM.cpp b/llvm/lib/Support/InitLLVM.cpp index bb9b569d2de69..5c56b773ea698 100644 --- a/llvm/lib/Support/InitLLVM.cpp +++ b/llvm/lib/Support/InitLLVM.cpp @@ -15,7 +15,7 @@ #include <string> #ifdef _WIN32 -#include "Windows/WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #endif using namespace llvm; diff --git a/llvm/lib/Support/IntEqClasses.cpp b/llvm/lib/Support/IntEqClasses.cpp index 4a976dcefc65f..ebb02e6c01e52 100644 --- a/llvm/lib/Support/IntEqClasses.cpp +++ b/llvm/lib/Support/IntEqClasses.cpp @@ -18,6 +18,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/IntEqClasses.h" +#include <cassert> using namespace llvm; diff --git a/llvm/lib/Support/IntervalMap.cpp b/llvm/lib/Support/IntervalMap.cpp index f15c7c9403c36..674e0f962fa1b 100644 --- a/llvm/lib/Support/IntervalMap.cpp +++ b/llvm/lib/Support/IntervalMap.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/IntervalMap.h" +#include <cassert> namespace llvm { namespace IntervalMapImpl { diff --git a/llvm/lib/Support/ItaniumManglingCanonicalizer.cpp b/llvm/lib/Support/ItaniumManglingCanonicalizer.cpp index bbc06d186fba4..9d3cf61459dd8 100644 --- a/llvm/lib/Support/ItaniumManglingCanonicalizer.cpp +++ b/llvm/lib/Support/ItaniumManglingCanonicalizer.cpp @@ -7,16 +7,12 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/ItaniumManglingCanonicalizer.h" - +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/ItaniumDemangle.h" #include "llvm/Support/Allocator.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/StringRef.h" - using namespace llvm; using llvm::itanium_demangle::ForwardTemplateReference; using llvm::itanium_demangle::Node; @@ -30,9 +26,8 @@ struct FoldingSetNodeIDBuilder { void operator()(StringView Str) { ID.AddString(llvm::StringRef(Str.begin(), Str.size())); } - template<typename T> - typename std::enable_if<std::is_integral<T>::value || - std::is_enum<T>::value>::type + template <typename T> + std::enable_if_t<std::is_integral<T>::value || std::is_enum<T>::value> operator()(T V) { ID.AddInteger((unsigned long long)V); } diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp index 8f3f4aa8caeaf..1ff66d504cbea 100644 --- a/llvm/lib/Support/KnownBits.cpp +++ b/llvm/lib/Support/KnownBits.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/KnownBits.h" +#include <cassert> using namespace llvm; @@ -81,3 +82,28 @@ KnownBits KnownBits::computeForAddSub(bool Add, bool NSW, return KnownOut; } + +KnownBits &KnownBits::operator&=(const KnownBits &RHS) { + // Result bit is 0 if either operand bit is 0. + Zero |= RHS.Zero; + // Result bit is 1 if both operand bits are 1. + One &= RHS.One; + return *this; +} + +KnownBits &KnownBits::operator|=(const KnownBits &RHS) { + // Result bit is 0 if both operand bits are 0. + Zero &= RHS.Zero; + // Result bit is 1 if either operand bit is 1. + One |= RHS.One; + return *this; +} + +KnownBits &KnownBits::operator^=(const KnownBits &RHS) { + // Result bit is 0 if both operand bits are 0 or both are 1. + APInt Z = (Zero & RHS.Zero) | (One & RHS.One); + // Result bit is 1 if one operand bit is 0 and the other is 1. + One = (Zero & RHS.One) | (One & RHS.Zero); + Zero = std::move(Z); + return *this; +} diff --git a/llvm/lib/Support/LockFileManager.cpp b/llvm/lib/Support/LockFileManager.cpp index 5c6508c3b007b..a2b56ab295c42 100644 --- a/llvm/lib/Support/LockFileManager.cpp +++ b/llvm/lib/Support/LockFileManager.cpp @@ -14,15 +14,20 @@ #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" #include <cerrno> +#include <chrono> #include <ctime> #include <memory> +#include <random> #include <sys/stat.h> #include <sys/types.h> #include <system_error> +#include <thread> #include <tuple> + #ifdef _WIN32 #include <windows.h> #endif @@ -158,7 +163,7 @@ LockFileManager::LockFileManager(StringRef FileName) this->FileName = FileName; if (std::error_code EC = sys::fs::make_absolute(this->FileName)) { std::string S("failed to obtain absolute path for "); - S.append(this->FileName.str()); + S.append(std::string(this->FileName.str())); setError(EC, S); return; } @@ -177,7 +182,7 @@ LockFileManager::LockFileManager(StringRef FileName) if (std::error_code EC = sys::fs::createUniqueFile( UniqueLockFileName, UniqueLockFileID, UniqueLockFileName)) { std::string S("failed to create unique file "); - S.append(UniqueLockFileName.str()); + S.append(std::string(UniqueLockFileName.str())); setError(EC, S); return; } @@ -191,19 +196,14 @@ LockFileManager::LockFileManager(StringRef FileName) } raw_fd_ostream Out(UniqueLockFileID, /*shouldClose=*/true); - Out << HostID << ' '; -#if LLVM_ON_UNIX - Out << getpid(); -#else - Out << "1"; -#endif + Out << HostID << ' ' << sys::Process::getProcessId(); Out.close(); if (Out.has_error()) { // We failed to write out PID, so report the error, remove the // unique lock file, and fail. std::string S("failed to write to "); - S.append(UniqueLockFileName.str()); + S.append(std::string(UniqueLockFileName.str())); setError(Out.error(), S); sys::fs::remove(UniqueLockFileName); return; @@ -249,7 +249,7 @@ LockFileManager::LockFileManager(StringRef FileName) // ownership. if ((EC = sys::fs::remove(LockFileName))) { std::string S("failed to remove lockfile "); - S.append(UniqueLockFileName.str()); + S.append(std::string(UniqueLockFileName.str())); setError(EC, S); return; } @@ -295,23 +295,29 @@ LockFileManager::waitForUnlock(const unsigned MaxSeconds) { if (getState() != LFS_Shared) return Res_Success; -#ifdef _WIN32 - unsigned long Interval = 1; -#else - struct timespec Interval; - Interval.tv_sec = 0; - Interval.tv_nsec = 1000000; -#endif + // Since we don't yet have an event-based method to wait for the lock file, + // implement randomized exponential backoff, similar to Ethernet collision + // algorithm. This improves performance on machines with high core counts + // when the file lock is heavily contended by multiple clang processes + const unsigned long MinWaitDurationMS = 10; + const unsigned long MaxWaitMultiplier = 50; // 500ms max wait + unsigned long WaitMultiplier = 1; + unsigned long ElapsedTimeSeconds = 0; + + std::random_device Device; + std::default_random_engine Engine(Device()); + + auto StartTime = std::chrono::steady_clock::now(); + do { + // FIXME: implement event-based waiting + // Sleep for the designated interval, to allow the owning process time to // finish up and remove the lock file. - // FIXME: Should we hook in to system APIs to get a notification when the - // lock file is deleted? -#ifdef _WIN32 - Sleep(Interval); -#else - nanosleep(&Interval, nullptr); -#endif + std::uniform_int_distribution<unsigned long> Distribution(1, + WaitMultiplier); + unsigned long WaitDurationMS = MinWaitDurationMS * Distribution(Engine); + std::this_thread::sleep_for(std::chrono::milliseconds(WaitDurationMS)); if (sys::fs::access(LockFileName.c_str(), sys::fs::AccessMode::Exist) == errc::no_such_file_or_directory) { @@ -325,24 +331,16 @@ LockFileManager::waitForUnlock(const unsigned MaxSeconds) { if (!processStillExecuting((*Owner).first, (*Owner).second)) return Res_OwnerDied; - // Exponentially increase the time we wait for the lock to be removed. -#ifdef _WIN32 - Interval *= 2; -#else - Interval.tv_sec *= 2; - Interval.tv_nsec *= 2; - if (Interval.tv_nsec >= 1000000000) { - ++Interval.tv_sec; - Interval.tv_nsec -= 1000000000; + WaitMultiplier *= 2; + if (WaitMultiplier > MaxWaitMultiplier) { + WaitMultiplier = MaxWaitMultiplier; } -#endif - } while ( -#ifdef _WIN32 - Interval < MaxSeconds * 1000 -#else - Interval.tv_sec < (time_t)MaxSeconds -#endif - ); + + ElapsedTimeSeconds = std::chrono::duration_cast<std::chrono::seconds>( + std::chrono::steady_clock::now() - StartTime) + .count(); + + } while (ElapsedTimeSeconds < MaxSeconds); // Give up. return Res_Timeout; diff --git a/llvm/lib/Support/MD5.cpp b/llvm/lib/Support/MD5.cpp index 9b02f62912fa3..5e0b076f176e8 100644 --- a/llvm/lib/Support/MD5.cpp +++ b/llvm/lib/Support/MD5.cpp @@ -39,6 +39,7 @@ #include "llvm/Support/MD5.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Format.h" diff --git a/llvm/lib/Support/MemAlloc.cpp b/llvm/lib/Support/MemAlloc.cpp new file mode 100644 index 0000000000000..7aaa0dc6e205a --- /dev/null +++ b/llvm/lib/Support/MemAlloc.cpp @@ -0,0 +1,34 @@ +//===- MemAlloc.cpp - Memory allocation functions -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/MemAlloc.h" + +// These are out of line to have __cpp_aligned_new not affect ABI. + +LLVM_ATTRIBUTE_RETURNS_NONNULL LLVM_ATTRIBUTE_RETURNS_NOALIAS void * +llvm::allocate_buffer(size_t Size, size_t Alignment) { + return ::operator new(Size +#ifdef __cpp_aligned_new + , + std::align_val_t(Alignment) +#endif + ); +} + +void llvm::deallocate_buffer(void *Ptr, size_t Size, size_t Alignment) { + ::operator delete(Ptr +#ifdef __cpp_sized_deallocation + , + Size +#endif +#ifdef __cpp_aligned_new + , + std::align_val_t(Alignment) +#endif + ); +} diff --git a/llvm/lib/Support/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp index e4027ca7bbfd5..248fb72c49689 100644 --- a/llvm/lib/Support/MemoryBuffer.cpp +++ b/llvm/lib/Support/MemoryBuffer.cpp @@ -162,6 +162,20 @@ MemoryBuffer::getFileSlice(const Twine &FilePath, uint64_t MapSize, //===----------------------------------------------------------------------===// namespace { + +template <typename MB> +constexpr sys::fs::mapped_file_region::mapmode Mapmode = + sys::fs::mapped_file_region::readonly; +template <> +constexpr sys::fs::mapped_file_region::mapmode Mapmode<MemoryBuffer> = + sys::fs::mapped_file_region::readonly; +template <> +constexpr sys::fs::mapped_file_region::mapmode Mapmode<WritableMemoryBuffer> = + sys::fs::mapped_file_region::priv; +template <> +constexpr sys::fs::mapped_file_region::mapmode + Mapmode<WriteThroughMemoryBuffer> = sys::fs::mapped_file_region::readwrite; + /// Memory maps a file descriptor using sys::fs::mapped_file_region. /// /// This handles converting the offset into a legal offset on the platform. @@ -184,7 +198,7 @@ class MemoryBufferMMapFile : public MB { public: MemoryBufferMMapFile(bool RequiresNullTerminator, sys::fs::file_t FD, uint64_t Len, uint64_t Offset, std::error_code &EC) - : MFR(FD, MB::Mapmode, getLegalMapSize(Len, Offset), + : MFR(FD, Mapmode<MB>, getLegalMapSize(Len, Offset), getLegalMapOffset(Offset), EC) { if (!EC) { const char *Start = getStart(Len, Offset); @@ -315,7 +329,7 @@ static bool shouldUseMmap(sys::fs::file_t FD, // 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 (IsVolatile) + if (IsVolatile && RequiresNullTerminator) return false; // We don't use mmap for small files because this can severely fragment our diff --git a/llvm/lib/Support/NativeFormatting.cpp b/llvm/lib/Support/NativeFormatting.cpp index 3731e0c563599..ae4bffbd94c1a 100644 --- a/llvm/lib/Support/NativeFormatting.cpp +++ b/llvm/lib/Support/NativeFormatting.cpp @@ -7,12 +7,11 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/NativeFormatting.h" - #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/Format.h" - +#include "llvm/Support/raw_ostream.h" #include <float.h> using namespace llvm; @@ -89,7 +88,7 @@ static void write_signed(raw_ostream &S, T N, size_t MinDigits, IntegerStyle Style) { static_assert(std::is_signed<T>::value, "Value is not signed!"); - using UnsignedT = typename std::make_unsigned<T>::type; + using UnsignedT = std::make_unsigned_t<T>; if (N >= 0) { write_unsigned(S, static_cast<UnsignedT>(N), MinDigits, Style); diff --git a/llvm/lib/Support/OptimizedStructLayout.cpp b/llvm/lib/Support/OptimizedStructLayout.cpp new file mode 100644 index 0000000000000..9bbd767c5ce9b --- /dev/null +++ b/llvm/lib/Support/OptimizedStructLayout.cpp @@ -0,0 +1,449 @@ +//===--- OptimizedStructLayout.cpp - Optimal data layout algorithm ----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the performOptimizedStructLayout interface. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/OptimizedStructLayout.h" + +using namespace llvm; + +using Field = OptimizedStructLayoutField; + +#ifndef NDEBUG +static void checkValidLayout(ArrayRef<Field> Fields, uint64_t Size, + Align MaxAlign) { + uint64_t LastEnd = 0; + Align ComputedMaxAlign; + for (auto &Field : Fields) { + assert(Field.hasFixedOffset() && + "didn't assign a fixed offset to field"); + assert(isAligned(Field.Alignment, Field.Offset) && + "didn't assign a correctly-aligned offset to field"); + assert(Field.Offset >= LastEnd && + "didn't assign offsets in ascending order"); + LastEnd = Field.getEndOffset(); + assert(Field.Alignment <= MaxAlign && + "didn't compute MaxAlign correctly"); + ComputedMaxAlign = std::max(Field.Alignment, MaxAlign); + } + assert(LastEnd == Size && "didn't compute LastEnd correctly"); + assert(ComputedMaxAlign == MaxAlign && "didn't compute MaxAlign correctly"); +} +#endif + +std::pair<uint64_t, Align> +llvm::performOptimizedStructLayout(MutableArrayRef<Field> Fields) { +#ifndef NDEBUG + // Do some simple precondition checks. + { + bool InFixedPrefix = true; + size_t LastEnd = 0; + for (auto &Field : Fields) { + assert(Field.Size > 0 && "field of zero size"); + if (Field.hasFixedOffset()) { + assert(InFixedPrefix && + "fixed-offset fields are not a strict prefix of array"); + assert(LastEnd <= Field.Offset && + "fixed-offset fields overlap or are not in order"); + LastEnd = Field.getEndOffset(); + assert(LastEnd > Field.Offset && + "overflow in fixed-offset end offset"); + } else { + InFixedPrefix = false; + } + } + } +#endif + + // Do an initial pass over the fields. + Align MaxAlign; + + // Find the first flexible-offset field, tracking MaxAlign. + auto FirstFlexible = Fields.begin(), E = Fields.end(); + while (FirstFlexible != E && FirstFlexible->hasFixedOffset()) { + MaxAlign = std::max(MaxAlign, FirstFlexible->Alignment); + ++FirstFlexible; + } + + // If there are no flexible fields, we're done. + if (FirstFlexible == E) { + uint64_t Size = 0; + if (!Fields.empty()) + Size = Fields.back().getEndOffset(); + +#ifndef NDEBUG + checkValidLayout(Fields, Size, MaxAlign); +#endif + return std::make_pair(Size, MaxAlign); + } + + // Walk over the flexible-offset fields, tracking MaxAlign and + // assigning them a unique number in order of their appearance. + // We'll use this unique number in the comparison below so that + // we can use array_pod_sort, which isn't stable. We won't use it + // past that point. + { + uintptr_t UniqueNumber = 0; + for (auto I = FirstFlexible; I != E; ++I) { + I->Scratch = reinterpret_cast<void*>(UniqueNumber++); + MaxAlign = std::max(MaxAlign, I->Alignment); + } + } + + // Sort the flexible elements in order of decreasing alignment, + // then decreasing size, and then the original order as recorded + // in Scratch. The decreasing-size aspect of this is only really + // important if we get into the gap-filling stage below, but it + // doesn't hurt here. + array_pod_sort(FirstFlexible, E, + [](const Field *lhs, const Field *rhs) -> int { + // Decreasing alignment. + if (lhs->Alignment != rhs->Alignment) + return (lhs->Alignment < rhs->Alignment ? 1 : -1); + + // Decreasing size. + if (lhs->Size != rhs->Size) + return (lhs->Size < rhs->Size ? 1 : -1); + + // Original order. + auto lhsNumber = reinterpret_cast<uintptr_t>(lhs->Scratch); + auto rhsNumber = reinterpret_cast<uintptr_t>(rhs->Scratch); + if (lhsNumber != rhsNumber) + return (lhsNumber < rhsNumber ? -1 : 1); + + return 0; + }); + + // Do a quick check for whether that sort alone has given us a perfect + // layout with no interior padding. This is very common: if the + // fixed-layout fields have no interior padding, and they end at a + // sufficiently-aligned offset for all the flexible-layout fields, + // and the flexible-layout fields all have sizes that are multiples + // of their alignment, then this will reliably trigger. + { + bool HasPadding = false; + uint64_t LastEnd = 0; + + // Walk the fixed-offset fields. + for (auto I = Fields.begin(); I != FirstFlexible; ++I) { + assert(I->hasFixedOffset()); + if (LastEnd != I->Offset) { + HasPadding = true; + break; + } + LastEnd = I->getEndOffset(); + } + + // Walk the flexible-offset fields, optimistically assigning fixed + // offsets. Note that we maintain a strict division between the + // fixed-offset and flexible-offset fields, so if we end up + // discovering padding later in this loop, we can just abandon this + // work and we'll ignore the offsets we already assigned. + if (!HasPadding) { + for (auto I = FirstFlexible; I != E; ++I) { + auto Offset = alignTo(LastEnd, I->Alignment); + if (LastEnd != Offset) { + HasPadding = true; + break; + } + I->Offset = Offset; + LastEnd = I->getEndOffset(); + } + } + + // If we already have a perfect layout, we're done. + if (!HasPadding) { +#ifndef NDEBUG + checkValidLayout(Fields, LastEnd, MaxAlign); +#endif + return std::make_pair(LastEnd, MaxAlign); + } + } + + // The algorithm sketch at this point is as follows. + // + // Consider the padding gaps between fixed-offset fields in ascending + // order. Let LastEnd be the offset of the first byte following the + // field before the gap, or 0 if the gap is at the beginning of the + // structure. Find the "best" flexible-offset field according to the + // criteria below. If no such field exists, proceed to the next gap. + // Otherwise, add the field at the first properly-aligned offset for + // that field that is >= LastEnd, then update LastEnd and repeat in + // order to fill any remaining gap following that field. + // + // Next, let LastEnd to be the offset of the first byte following the + // last fixed-offset field, or 0 if there are no fixed-offset fields. + // While there are flexible-offset fields remaining, find the "best" + // flexible-offset field according to the criteria below, add it at + // the first properly-aligned offset for that field that is >= LastEnd, + // and update LastEnd to the first byte following the field. + // + // The "best" field is chosen by the following criteria, considered + // strictly in order: + // + // - When filling a gap betweeen fields, the field must fit. + // - A field is preferred if it requires less padding following LastEnd. + // - A field is preferred if it is more aligned. + // - A field is preferred if it is larger. + // - A field is preferred if it appeared earlier in the initial order. + // + // Minimizing leading padding is a greedy attempt to avoid padding + // entirely. Preferring more-aligned fields is an attempt to eliminate + // stricter constraints earlier, with the idea that weaker alignment + // constraints may be resolvable with less padding elsewhere. These + // These two rules are sufficient to ensure that we get the optimal + // layout in the "C-style" case. Preferring larger fields tends to take + // better advantage of large gaps and may be more likely to have a size + // that's a multiple of a useful alignment. Preferring the initial + // order may help somewhat with locality but is mostly just a way of + // ensuring deterministic output. + // + // Note that this algorithm does not guarantee a minimal layout. Picking + // a larger object greedily may leave a gap that cannot be filled as + // efficiently. Unfortunately, solving this perfectly is an NP-complete + // problem (by reduction from bin-packing: let B_i be the bin sizes and + // O_j be the object sizes; add fixed-offset fields such that the gaps + // between them have size B_i, and add flexible-offset fields with + // alignment 1 and size O_j; if the layout size is equal to the end of + // the last fixed-layout field, the objects fit in the bins; note that + // this doesn't even require the complexity of alignment). + + // The implementation below is essentially just an optimized version of + // scanning the list of remaining fields looking for the best, which + // would be O(n^2). In the worst case, it doesn't improve on that. + // However, in practice it'll just scan the array of alignment bins + // and consider the first few elements from one or two bins. The + // number of bins is bounded by a small constant: alignments are powers + // of two that are vanishingly unlikely to be over 64 and fairly unlikely + // to be over 8. And multiple elements only need to be considered when + // filling a gap between fixed-offset fields, which doesn't happen very + // often. We could use a data structure within bins that optimizes for + // finding the best-sized match, but it would require allocating memory + // and copying data, so it's unlikely to be worthwhile. + + + // Start by organizing the flexible-offset fields into bins according to + // their alignment. We expect a small enough number of bins that we + // don't care about the asymptotic costs of walking this. + struct AlignmentQueue { + /// The minimum size of anything currently in this queue. + uint64_t MinSize; + + /// The head of the queue. A singly-linked list. The order here should + /// be consistent with the earlier sort, i.e. the elements should be + /// monotonically descending in size and otherwise in the original order. + /// + /// We remove the queue from the array as soon as this is empty. + OptimizedStructLayoutField *Head; + + /// The alignment requirement of the queue. + Align Alignment; + + static Field *getNext(Field *Cur) { + return static_cast<Field *>(Cur->Scratch); + } + }; + SmallVector<AlignmentQueue, 8> FlexibleFieldsByAlignment; + for (auto I = FirstFlexible; I != E; ) { + auto Head = I; + auto Alignment = I->Alignment; + + uint64_t MinSize = I->Size; + auto LastInQueue = I; + for (++I; I != E && I->Alignment == Alignment; ++I) { + LastInQueue->Scratch = I; + LastInQueue = I; + MinSize = std::min(MinSize, I->Size); + } + LastInQueue->Scratch = nullptr; + + FlexibleFieldsByAlignment.push_back({MinSize, Head, Alignment}); + } + +#ifndef NDEBUG + // Verify that we set the queues up correctly. + auto checkQueues = [&]{ + bool FirstQueue = true; + Align LastQueueAlignment; + for (auto &Queue : FlexibleFieldsByAlignment) { + assert((FirstQueue || Queue.Alignment < LastQueueAlignment) && + "bins not in order of descending alignment"); + LastQueueAlignment = Queue.Alignment; + FirstQueue = false; + + assert(Queue.Head && "queue was empty"); + uint64_t LastSize = ~(uint64_t)0; + for (auto I = Queue.Head; I; I = Queue.getNext(I)) { + assert(I->Alignment == Queue.Alignment && "bad field in queue"); + assert(I->Size <= LastSize && "queue not in descending size order"); + LastSize = I->Size; + } + } + }; + checkQueues(); +#endif + + /// Helper function to remove a field from a queue. + auto spliceFromQueue = [&](AlignmentQueue *Queue, Field *Last, Field *Cur) { + assert(Last ? Queue->getNext(Last) == Cur : Queue->Head == Cur); + + // If we're removing Cur from a non-initial position, splice it out + // of the linked list. + if (Last) { + Last->Scratch = Cur->Scratch; + + // If Cur was the last field in the list, we need to update MinSize. + // We can just use the last field's size because the list is in + // descending order of size. + if (!Cur->Scratch) + Queue->MinSize = Last->Size; + + // Otherwise, replace the head. + } else { + if (auto NewHead = Queue->getNext(Cur)) + Queue->Head = NewHead; + + // If we just emptied the queue, destroy its bin. + else + FlexibleFieldsByAlignment.erase(Queue); + } + }; + + // Do layout into a local array. Doing this in-place on Fields is + // not really feasible. + SmallVector<Field, 16> Layout; + Layout.reserve(Fields.size()); + + // The offset that we're currently looking to insert at (or after). + uint64_t LastEnd = 0; + + // Helper function to splice Cur out of the given queue and add it + // to the layout at the given offset. + auto addToLayout = [&](AlignmentQueue *Queue, Field *Last, Field *Cur, + uint64_t Offset) -> bool { + assert(Offset == alignTo(LastEnd, Cur->Alignment)); + + // Splice out. This potentially invalidates Queue. + spliceFromQueue(Queue, Last, Cur); + + // Add Cur to the layout. + Layout.push_back(*Cur); + Layout.back().Offset = Offset; + LastEnd = Layout.back().getEndOffset(); + + // Always return true so that we can be tail-called. + return true; + }; + + // Helper function to try to find a field in the given queue that'll + // fit starting at StartOffset but before EndOffset (if present). + // Note that this never fails if EndOffset is not provided. + auto tryAddFillerFromQueue = [&](AlignmentQueue *Queue, + uint64_t StartOffset, + Optional<uint64_t> EndOffset) -> bool { + assert(Queue->Head); + assert(StartOffset == alignTo(LastEnd, Queue->Alignment)); + + // Figure out the maximum size that a field can be, and ignore this + // queue if there's nothing in it that small. + auto MaxViableSize = + (EndOffset ? *EndOffset - StartOffset : ~(uint64_t)0); + if (Queue->MinSize > MaxViableSize) return false; + + // Find the matching field. Note that this should always find + // something because of the MinSize check above. + for (Field *Cur = Queue->Head, *Last = nullptr; true; + Last = Cur, Cur = Queue->getNext(Cur)) { + assert(Cur && "didn't find a match in queue despite its MinSize"); + if (Cur->Size <= MaxViableSize) + return addToLayout(Queue, Last, Cur, StartOffset); + } + + llvm_unreachable("didn't find a match in queue despite its MinSize"); + }; + + // Helper function to find the "best" flexible-offset field according + // to the criteria described above. + auto tryAddBestField = [&](Optional<uint64_t> BeforeOffset) -> bool { + auto QueueB = FlexibleFieldsByAlignment.begin(); + auto QueueE = FlexibleFieldsByAlignment.end(); + + // Start by looking for the most-aligned queue that doesn't need any + // leading padding after LastEnd. + auto FirstQueueToSearch = QueueB; + for (; FirstQueueToSearch != QueueE; ++FirstQueueToSearch) { + if (isAligned(FirstQueueToSearch->Alignment, LastEnd)) + break; + } + + uint64_t Offset = LastEnd; + while (true) { + // Invariant: all of the queues in [FirstQueueToSearch, QueueE) + // require the same initial padding offset. + + // Search those queues in descending order of alignment for a + // satisfactory field. + for (auto Queue = FirstQueueToSearch; Queue != QueueE; ++Queue) { + if (tryAddFillerFromQueue(Queue, Offset, BeforeOffset)) + return true; + } + + // Okay, we don't need to scan those again. + QueueE = FirstQueueToSearch; + + // If we started from the first queue, we're done. + if (FirstQueueToSearch == QueueB) + return false; + + // Otherwise, scan backwards to find the most-aligned queue that + // still has minimal leading padding after LastEnd. + --FirstQueueToSearch; + Offset = alignTo(LastEnd, FirstQueueToSearch->Alignment); + while (FirstQueueToSearch != QueueB && + Offset == alignTo(LastEnd, FirstQueueToSearch[-1].Alignment)) + --FirstQueueToSearch; + } + }; + + // Phase 1: fill the gaps between fixed-offset fields with the best + // flexible-offset field that fits. + for (auto I = Fields.begin(); I != FirstFlexible; ++I) { + while (LastEnd != I->Offset) { + if (!tryAddBestField(I->Offset)) + break; + } + Layout.push_back(*I); + LastEnd = I->getEndOffset(); + } + +#ifndef NDEBUG + checkQueues(); +#endif + + // Phase 2: repeatedly add the best flexible-offset field until + // they're all gone. + while (!FlexibleFieldsByAlignment.empty()) { + bool Success = tryAddBestField(None); + assert(Success && "didn't find a field with no fixed limit?"); + (void) Success; + } + + // Copy the layout back into place. + assert(Layout.size() == Fields.size()); + memcpy(Fields.data(), Layout.data(), + Fields.size() * sizeof(OptimizedStructLayoutField)); + +#ifndef NDEBUG + // Make a final check that the layout is valid. + checkValidLayout(Fields, LastEnd, MaxAlign); +#endif + + return std::make_pair(LastEnd, MaxAlign); +} diff --git a/llvm/lib/Support/Parallel.cpp b/llvm/lib/Support/Parallel.cpp index 523665d14b029..9a2e1003da5a2 100644 --- a/llvm/lib/Support/Parallel.cpp +++ b/llvm/lib/Support/Parallel.cpp @@ -9,9 +9,6 @@ #include "llvm/Support/Parallel.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/ManagedStatic.h" - -#if LLVM_ENABLE_THREADS - #include "llvm/Support/Threading.h" #include <atomic> @@ -20,6 +17,10 @@ #include <thread> #include <vector> +llvm::ThreadPoolStrategy llvm::parallel::strategy; + +#if LLVM_ENABLE_THREADS + namespace llvm { namespace parallel { namespace detail { @@ -39,20 +40,21 @@ public: /// in filo order. class ThreadPoolExecutor : public Executor { public: - explicit ThreadPoolExecutor(unsigned ThreadCount = hardware_concurrency()) { + explicit ThreadPoolExecutor(ThreadPoolStrategy S = hardware_concurrency()) { + unsigned ThreadCount = S.compute_thread_count(); // Spawn all but one of the threads in another thread as spawning threads // can take a while. Threads.reserve(ThreadCount); Threads.resize(1); std::lock_guard<std::mutex> Lock(Mutex); - Threads[0] = std::thread([&, ThreadCount] { - for (unsigned i = 1; i < ThreadCount; ++i) { - Threads.emplace_back([=] { work(); }); + Threads[0] = std::thread([this, ThreadCount, S] { + for (unsigned I = 1; I < ThreadCount; ++I) { + Threads.emplace_back([=] { work(S, I); }); if (Stop) break; } ThreadsCreated.set_value(); - work(); + work(S, 0); }); } @@ -77,6 +79,9 @@ public: T.join(); } + struct Creator { + static void *call() { return new ThreadPoolExecutor(strategy); } + }; struct Deleter { static void call(void *Ptr) { ((ThreadPoolExecutor *)Ptr)->stop(); } }; @@ -90,7 +95,8 @@ public: } private: - void work() { + void work(ThreadPoolStrategy S, unsigned ThreadID) { + S.apply_thread_strategy(ThreadID); while (true) { std::unique_lock<std::mutex> Lock(Mutex); Cond.wait(Lock, [&] { return Stop || !WorkStack.empty(); }); @@ -129,7 +135,8 @@ Executor *Executor::getDefaultExecutor() { // are more frequent with the debug static runtime. // // This also prevents intermittent deadlocks on exit with the MinGW runtime. - static ManagedStatic<ThreadPoolExecutor, object_creator<ThreadPoolExecutor>, + + static ManagedStatic<ThreadPoolExecutor, ThreadPoolExecutor::Creator, ThreadPoolExecutor::Deleter> ManagedExec; static std::unique_ptr<ThreadPoolExecutor> Exec(&(*ManagedExec)); diff --git a/llvm/lib/Support/Path.cpp b/llvm/lib/Support/Path.cpp index 3c9a08cb4077d..37b3086fddf5d 100644 --- a/llvm/lib/Support/Path.cpp +++ b/llvm/lib/Support/Path.cpp @@ -496,49 +496,44 @@ void replace_extension(SmallVectorImpl<char> &path, const Twine &extension, path.append(ext.begin(), ext.end()); } -bool replace_path_prefix(SmallVectorImpl<char> &Path, - const StringRef &OldPrefix, const StringRef &NewPrefix, - Style style, bool strict) { +static bool starts_with(StringRef Path, StringRef Prefix, + Style style = Style::native) { + // Windows prefix matching : case and separator insensitive + if (real_style(style) == Style::windows) { + if (Path.size() < Prefix.size()) + return false; + for (size_t I = 0, E = Prefix.size(); I != E; ++I) { + bool SepPath = is_separator(Path[I], style); + bool SepPrefix = is_separator(Prefix[I], style); + if (SepPath != SepPrefix) + return false; + if (!SepPath && toLower(Path[I]) != toLower(Prefix[I])) + return false; + } + return true; + } + return Path.startswith(Prefix); +} + +bool replace_path_prefix(SmallVectorImpl<char> &Path, StringRef OldPrefix, + StringRef NewPrefix, Style style) { if (OldPrefix.empty() && NewPrefix.empty()) return false; StringRef OrigPath(Path.begin(), Path.size()); - StringRef OldPrefixDir; - - if (!strict && OldPrefix.size() > OrigPath.size()) + if (!starts_with(OrigPath, OldPrefix, style)) return false; - // Ensure OldPrefixDir does not have a trailing separator. - if (!OldPrefix.empty() && is_separator(OldPrefix.back())) - OldPrefixDir = parent_path(OldPrefix, style); - else - OldPrefixDir = OldPrefix; - - if (!OrigPath.startswith(OldPrefixDir)) - return false; - - if (OrigPath.size() > OldPrefixDir.size()) - if (!is_separator(OrigPath[OldPrefixDir.size()], style) && strict) - return false; - // If prefixes have the same size we can simply copy the new one over. - if (OldPrefixDir.size() == NewPrefix.size() && !strict) { + if (OldPrefix.size() == NewPrefix.size()) { llvm::copy(NewPrefix, Path.begin()); return true; } - StringRef RelPath = OrigPath.substr(OldPrefixDir.size()); + StringRef RelPath = OrigPath.substr(OldPrefix.size()); SmallString<256> NewPath; - path::append(NewPath, style, NewPrefix); - if (!RelPath.empty()) { - if (!is_separator(RelPath[0], style) || !strict) - path::append(NewPath, style, RelPath); - else - path::append(NewPath, style, relative_path(RelPath, style)); - } - + (Twine(NewPrefix) + RelPath).toVector(NewPath); Path.swap(NewPath); - return true; } @@ -564,21 +559,15 @@ void native(SmallVectorImpl<char> &Path, Style style) { 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 = '/'; - } - } + for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) + if (*PI == '\\') + *PI = '/'; } } std::string convert_to_slash(StringRef path, Style style) { if (real_style(style) != Style::windows) - return path; + return std::string(path); std::string s = path.str(); std::replace(s.begin(), s.end(), '\\', '/'); @@ -708,43 +697,69 @@ StringRef remove_leading_dotslash(StringRef Path, Style style) { return Path; } -static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot, - Style style) { +// Remove path traversal components ("." and "..") when possible, and +// canonicalize slashes. +bool remove_dots(SmallVectorImpl<char> &the_path, bool remove_dot_dot, + Style style) { + style = real_style(style); + StringRef remaining(the_path.data(), the_path.size()); + bool needs_change = false; SmallVector<StringRef, 16> components; - // Skip the root path, then look for traversal in the components. - 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. - if (remove_dot_dot && C == "..") { + // Consume the root path, if present. + StringRef root = path::root_path(remaining, style); + bool absolute = !root.empty(); + if (absolute) + remaining = remaining.drop_front(root.size()); + + // Loop over path components manually. This makes it easier to detect + // non-preferred slashes and double separators that must be canonicalized. + while (!remaining.empty()) { + size_t next_slash = remaining.find_first_of(separators(style)); + if (next_slash == StringRef::npos) + next_slash = remaining.size(); + StringRef component = remaining.take_front(next_slash); + remaining = remaining.drop_front(next_slash); + + // Eat the slash, and check if it is the preferred separator. + if (!remaining.empty()) { + needs_change |= remaining.front() != preferred_separator(style); + remaining = remaining.drop_front(); + // The path needs to be rewritten if it has a trailing slash. + // FIXME: This is emergent behavior that could be removed. + needs_change |= remaining.empty(); + } + + // Check for path traversal components or double separators. + if (component.empty() || component == ".") { + needs_change = true; + } else if (remove_dot_dot && component == "..") { + needs_change = true; + // Do not allow ".." to remove the root component. If this is the + // beginning of a relative path, keep the ".." component. if (!components.empty() && components.back() != "..") { components.pop_back(); - continue; + } else if (!absolute) { + components.push_back(component); } - if (path::is_absolute(path, style)) - continue; + } else { + components.push_back(component); } - components.push_back(C); } - SmallString<256> buffer = path::root_path(path, style); - for (StringRef C : components) - path::append(buffer, style, C); - return buffer; -} - -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, style); - if (result == path) + // Avoid rewriting the path unless we have to. + if (!needs_change) return false; - path.swap(result); + SmallString<256> buffer = root; + if (!components.empty()) { + buffer += components[0]; + for (StringRef C : makeArrayRef(components).drop_front()) { + buffer += preferred_separator(style); + buffer += C; + } + } + the_path.swap(buffer); return true; } @@ -1114,7 +1129,7 @@ void directory_entry::replace_filename(const Twine &Filename, file_type Type, basic_file_status Status) { SmallString<128> PathStr = path::parent_path(Path); path::append(PathStr, Filename); - this->Path = PathStr.str(); + this->Path = std::string(PathStr.str()); this->Type = Type; this->Status = Status; } @@ -1142,7 +1157,8 @@ ErrorOr<perms> getPermissions(const Twine &Path) { namespace llvm { namespace sys { namespace fs { -TempFile::TempFile(StringRef Name, int FD) : TmpName(Name), FD(FD) {} +TempFile::TempFile(StringRef Name, int FD) + : TmpName(std::string(Name)), FD(FD) {} TempFile::TempFile(TempFile &&Other) { *this = std::move(Other); } TempFile &TempFile::operator=(TempFile &&Other) { TmpName = std::move(Other.TmpName); diff --git a/llvm/lib/Support/PrettyStackTrace.cpp b/llvm/lib/Support/PrettyStackTrace.cpp index bfb238cc85391..9072f9d2d2eef 100644 --- a/llvm/lib/Support/PrettyStackTrace.cpp +++ b/llvm/lib/Support/PrettyStackTrace.cpp @@ -22,6 +22,7 @@ #include "llvm/Support/raw_ostream.h" #include <atomic> +#include <cassert> #include <cstdarg> #include <cstdio> #include <tuple> @@ -32,6 +33,10 @@ using namespace llvm; +static const char *BugReportMsg = + "PLEASE submit a bug report to " BUG_REPORT_URL + " and include the crash backtrace.\n"; + // If backtrace support is not enabled, compile out support for pretty stack // traces. This has the secondary effect of not requiring thread local storage // when backtrace support is disabled. @@ -144,6 +149,8 @@ static CrashHandlerStringStorage crashHandlerStringStorage; /// This callback is run if a fatal signal is delivered to the process, it /// prints the pretty stack trace. static void CrashHandler(void *) { + errs() << BugReportMsg ; + #ifndef __APPLE__ // On non-apple systems, just emit the crash stack trace to stderr. PrintCurStackTrace(errs()); @@ -195,6 +202,14 @@ static void printForSigInfoIfNeeded() { #endif // ENABLE_BACKTRACES +void llvm::setBugReportMsg(const char *Msg) { + BugReportMsg = Msg; +} + +const char *llvm::getBugReportMsg() { + return BugReportMsg; +} + PrettyStackTraceEntry::PrettyStackTraceEntry() { #if ENABLE_BACKTRACES // Handle SIGINFO first, because we haven't finished constructing yet. diff --git a/llvm/lib/Support/Process.cpp b/llvm/lib/Support/Process.cpp index 5b64710081597..9e6e233b26ac2 100644 --- a/llvm/lib/Support/Process.cpp +++ b/llvm/lib/Support/Process.cpp @@ -13,8 +13,9 @@ #include "llvm/Support/Process.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Config/llvm-config.h" #include "llvm/Config/config.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" @@ -55,7 +56,7 @@ Optional<std::string> Process::FindInEnvPath(StringRef EnvName, SmallString<128> FilePath(Dir); path::append(FilePath, FileName); if (fs::exists(Twine(FilePath))) { - FoundPath = FilePath.str(); + FoundPath = std::string(FilePath.str()); break; } } @@ -88,6 +89,13 @@ static bool coreFilesPrevented = !LLVM_ENABLE_CRASH_DUMPS; bool Process::AreCoreFilesPrevented() { return coreFilesPrevented; } +LLVM_ATTRIBUTE_NORETURN +void Process::Exit(int RetCode) { + if (CrashRecoveryContext *CRC = CrashRecoveryContext::GetCurrent()) + CRC->HandleExit(RetCode); + ::exit(RetCode); +} + // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/Process.inc" diff --git a/llvm/lib/Support/Program.cpp b/llvm/lib/Support/Program.cpp index 0a9363c59fc68..5294f65bd5a54 100644 --- a/llvm/lib/Support/Program.cpp +++ b/llvm/lib/Support/Program.cpp @@ -13,6 +13,7 @@ #include "llvm/Support/Program.h" #include "llvm/ADT/StringRef.h" #include "llvm/Config/llvm-config.h" +#include "llvm/Support/raw_ostream.h" #include <system_error> using namespace llvm; using namespace sys; @@ -31,14 +32,16 @@ int sys::ExecuteAndWait(StringRef Program, ArrayRef<StringRef> Args, Optional<ArrayRef<StringRef>> Env, ArrayRef<Optional<StringRef>> Redirects, unsigned SecondsToWait, unsigned MemoryLimit, - std::string *ErrMsg, bool *ExecutionFailed) { + std::string *ErrMsg, bool *ExecutionFailed, + Optional<ProcessStatistics> *ProcStat) { assert(Redirects.empty() || Redirects.size() == 3); ProcessInfo PI; if (Execute(PI, Program, Args, Env, Redirects, MemoryLimit, ErrMsg)) { if (ExecutionFailed) *ExecutionFailed = false; - ProcessInfo Result = Wait( - PI, SecondsToWait, /*WaitUntilTerminates=*/SecondsToWait == 0, ErrMsg); + ProcessInfo Result = + Wait(PI, SecondsToWait, /*WaitUntilTerminates=*/SecondsToWait == 0, + ErrMsg, ProcStat); return Result.ReturnCode; } @@ -73,6 +76,24 @@ bool sys::commandLineFitsWithinSystemLimits(StringRef Program, return commandLineFitsWithinSystemLimits(Program, StringRefArgs); } +void sys::printArg(raw_ostream &OS, StringRef Arg, bool Quote) { + const bool Escape = Arg.find_first_of(" \"\\$") != StringRef::npos; + + if (!Quote && !Escape) { + OS << Arg; + return; + } + + // Quote and escape. This isn't really complete, but good enough. + OS << '"'; + for (const auto c : Arg) { + if (c == '"' || c == '\\' || c == '$') + OS << '\\'; + OS << c; + } + OS << '"'; +} + // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/Program.inc" diff --git a/llvm/lib/Support/RISCVAttributeParser.cpp b/llvm/lib/Support/RISCVAttributeParser.cpp new file mode 100644 index 0000000000000..393861c73a4a9 --- /dev/null +++ b/llvm/lib/Support/RISCVAttributeParser.cpp @@ -0,0 +1,67 @@ +//===-- RISCVAttributeParser.cpp - RISCV Attribute Parser -----------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/RISCVAttributeParser.h" +#include "llvm/ADT/StringExtras.h" + +using namespace llvm; + +const RISCVAttributeParser::DisplayHandler + RISCVAttributeParser::displayRoutines[] = { + { + RISCVAttrs::ARCH, + &ELFAttributeParser::stringAttribute, + }, + { + RISCVAttrs::PRIV_SPEC, + &ELFAttributeParser::integerAttribute, + }, + { + RISCVAttrs::PRIV_SPEC_MINOR, + &ELFAttributeParser::integerAttribute, + }, + { + RISCVAttrs::PRIV_SPEC_REVISION, + &ELFAttributeParser::integerAttribute, + }, + { + RISCVAttrs::STACK_ALIGN, + &RISCVAttributeParser::stackAlign, + }, + { + RISCVAttrs::UNALIGNED_ACCESS, + &RISCVAttributeParser::unalignedAccess, + }}; + +Error RISCVAttributeParser::unalignedAccess(unsigned tag) { + static const char *strings[] = {"No unaligned access", "Unaligned access"}; + return parseStringAttribute("Unaligned_access", tag, makeArrayRef(strings)); +} + +Error RISCVAttributeParser::stackAlign(unsigned tag) { + uint64_t value = de.getULEB128(cursor); + std::string description = + "Stack alignment is " + utostr(value) + std::string("-bytes"); + printAttribute(tag, value, description); + return Error::success(); +} + +Error RISCVAttributeParser::handler(uint64_t tag, bool &handled) { + handled = false; + for (unsigned AHI = 0, AHE = array_lengthof(displayRoutines); AHI != AHE; + ++AHI) { + if (uint64_t(displayRoutines[AHI].attribute) == tag) { + if (Error e = (this->*displayRoutines[AHI].routine)(tag)) + return e; + handled = true; + break; + } + } + + return Error::success(); +} diff --git a/llvm/lib/Support/RISCVAttributes.cpp b/llvm/lib/Support/RISCVAttributes.cpp new file mode 100644 index 0000000000000..201048e03009b --- /dev/null +++ b/llvm/lib/Support/RISCVAttributes.cpp @@ -0,0 +1,25 @@ +//===-- RISCVAttributes.cpp - RISCV Attributes ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/RISCVAttributes.h" + +using namespace llvm; +using namespace llvm::RISCVAttrs; + +static const TagNameItem tagData[] = { + {STACK_ALIGN, "Tag_stack_align"}, + {ARCH, "Tag_arch"}, + {UNALIGNED_ACCESS, "Tag_unaligned_access"}, + {PRIV_SPEC, "Tag_priv_spec"}, + {PRIV_SPEC_MINOR, "Tag_priv_spec_minor"}, + {PRIV_SPEC_REVISION, "Tag_priv_spec_revision"}, +}; + +const TagNameMap llvm::RISCVAttrs::RISCVAttributeTags(tagData, + sizeof(tagData) / + sizeof(TagNameItem)); diff --git a/llvm/lib/Support/RandomNumberGenerator.cpp b/llvm/lib/Support/RandomNumberGenerator.cpp index 09fad19799859..f9c41ee5eaaf0 100644 --- a/llvm/lib/Support/RandomNumberGenerator.cpp +++ b/llvm/lib/Support/RandomNumberGenerator.cpp @@ -17,7 +17,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/raw_ostream.h" #ifdef _WIN32 -#include "Windows/WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #else #include "Unix/Unix.h" #endif diff --git a/llvm/lib/Support/Regex.cpp b/llvm/lib/Support/Regex.cpp index 8da345d4f1404..0d5cc1c00db11 100644 --- a/llvm/lib/Support/Regex.cpp +++ b/llvm/lib/Support/Regex.cpp @@ -14,6 +14,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include <cassert> #include <string> // Important this comes last because it defines "_REGEX_H_". At least on @@ -25,7 +26,7 @@ using namespace llvm; Regex::Regex() : preg(nullptr), error(REG_BADPAT) {} -Regex::Regex(StringRef regex, unsigned Flags) { +Regex::Regex(StringRef regex, RegexFlags Flags) { unsigned flags = 0; preg = new llvm_regex(); preg->re_endp = regex.end(); @@ -38,6 +39,9 @@ Regex::Regex(StringRef regex, unsigned Flags) { error = llvm_regcomp(preg, regex.data(), flags|REG_PEND); } +Regex::Regex(StringRef regex, unsigned Flags) + : Regex(regex, static_cast<RegexFlags>(Flags)) {} + Regex::Regex(Regex &®ex) { preg = regex.preg; error = regex.error; @@ -135,7 +139,7 @@ std::string Regex::sub(StringRef Repl, StringRef String, // Return the input if there was no match. if (!match(String, &Matches, Error)) - return String; + return std::string(String); // Otherwise splice in the replacement string, starting with the prefix before // the match. diff --git a/llvm/lib/Support/SHA1.cpp b/llvm/lib/Support/SHA1.cpp index a98ca41a33545..417b13fea05a4 100644 --- a/llvm/lib/Support/SHA1.cpp +++ b/llvm/lib/Support/SHA1.cpp @@ -16,13 +16,13 @@ #include "llvm/Support/SHA1.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Host.h" -using namespace llvm; - -#include <stdint.h> #include <string.h> +using namespace llvm; + #if defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN #define SHA_BIG_ENDIAN #endif @@ -238,6 +238,11 @@ void SHA1::update(ArrayRef<uint8_t> Data) { addUncounted(C); } +void SHA1::update(StringRef Str) { + update( + ArrayRef<uint8_t>((uint8_t *)const_cast<char *>(Str.data()), Str.size())); +} + void SHA1::pad() { // Implement SHA-1 padding (fips180-2 5.1.1) diff --git a/llvm/lib/Support/Signals.cpp b/llvm/lib/Support/Signals.cpp index add6fde0eb5ea..2cfdf2d42a4a9 100644 --- a/llvm/lib/Support/Signals.cpp +++ b/llvm/lib/Support/Signals.cpp @@ -131,7 +131,7 @@ static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace, // If we don't know argv0 or the address of main() at this point, try // to guess it anyway (it's possible on some platforms). std::string MainExecutableName = - sys::fs::exists(Argv0) ? (std::string)Argv0 + sys::fs::exists(Argv0) ? (std::string)std::string(Argv0) : sys::fs::getMainExecutable(nullptr, nullptr); BumpPtrAllocator Allocator; StringSaver StrPool(Allocator); diff --git a/llvm/lib/Support/SmallVector.cpp b/llvm/lib/Support/SmallVector.cpp index 36f0a81f6b00d..6d5fe7165f633 100644 --- a/llvm/lib/Support/SmallVector.cpp +++ b/llvm/lib/Support/SmallVector.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "llvm/ADT/SmallVector.h" +#include <cstdint> using namespace llvm; // Check that no bytes are wasted and everything is well-aligned. @@ -37,17 +38,30 @@ static_assert(sizeof(SmallVector<void *, 1>) == sizeof(unsigned) * 2 + sizeof(void *) * 2, "wasted space in SmallVector size 1"); -/// grow_pod - This is an implementation of the grow() method which only works -/// on POD-like datatypes and is out of line to reduce code duplication. -void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity, - size_t TSize) { - // Ensure we can fit the new capacity in 32 bits. - if (MinCapacity > UINT32_MAX) +static_assert(sizeof(SmallVector<char, 0>) == + sizeof(void *) * 2 + sizeof(void *), + "1 byte elements have word-sized type for size and capacity"); + +// Note: Moving this function into the header may cause performance regression. +template <class Size_T> +void SmallVectorBase<Size_T>::grow_pod(void *FirstEl, size_t MinCapacity, + size_t TSize) { + // Ensure we can fit the new capacity. + // This is only going to be applicable when the capacity is 32 bit. + if (MinCapacity > SizeTypeMax()) report_bad_alloc_error("SmallVector capacity overflow during allocation"); + // Ensure we can meet the guarantee of space for at least one more element. + // The above check alone will not catch the case where grow is called with a + // default MinCapacity of 0, but the current capacity cannot be increased. + // This is only going to be applicable when the capacity is 32 bit. + if (capacity() == SizeTypeMax()) + report_bad_alloc_error("SmallVector capacity unable to grow"); + + // In theory 2*capacity can overflow if the capacity is 64 bit, but the + // original capacity would never be large enough for this to be a problem. size_t NewCapacity = 2 * capacity() + 1; // Always grow. - NewCapacity = - std::min(std::max(NewCapacity, MinCapacity), size_t(UINT32_MAX)); + NewCapacity = std::min(std::max(NewCapacity, MinCapacity), SizeTypeMax()); void *NewElts; if (BeginX == FirstEl) { @@ -63,3 +77,20 @@ void SmallVectorBase::grow_pod(void *FirstEl, size_t MinCapacity, this->BeginX = NewElts; this->Capacity = NewCapacity; } + +template class llvm::SmallVectorBase<uint32_t>; + +// Disable the uint64_t instantiation for 32-bit builds. +// Both uint32_t and uint64_t instantations are needed for 64-bit builds. +// This instantiation will never be used in 32-bit builds, and will cause +// warnings when sizeof(Size_T) > sizeof(size_t). +#if SIZE_MAX > UINT32_MAX +template class llvm::SmallVectorBase<uint64_t>; + +// Assertions to ensure this #if stays in sync with SmallVectorSizeType. +static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint64_t), + "Expected SmallVectorBase<uint64_t> variant to be in use."); +#else +static_assert(sizeof(SmallVectorSizeType<char>) == sizeof(uint32_t), + "Expected SmallVectorBase<uint32_t> variant to be in use."); +#endif diff --git a/llvm/lib/Support/SourceMgr.cpp b/llvm/lib/Support/SourceMgr.cpp index 2a241f18c3627..9cc69732a9647 100644 --- a/llvm/lib/Support/SourceMgr.cpp +++ b/llvm/lib/Support/SourceMgr.cpp @@ -42,7 +42,7 @@ unsigned SourceMgr::AddIncludeFile(const std::string &Filename, std::string &IncludedFile) { IncludedFile = Filename; ErrorOr<std::unique_ptr<MemoryBuffer>> NewBufOrErr = - MemoryBuffer::getFile(IncludedFile); + MemoryBuffer::getFile(IncludedFile); // If the file didn't exist directly, see if it's in an include path. for (unsigned i = 0, e = IncludeDirectories.size(); i != e && !NewBufOrErr; @@ -69,54 +69,109 @@ unsigned SourceMgr::FindBufferContainingLoc(SMLoc Loc) const { } template <typename T> -unsigned SourceMgr::SrcBuffer::getLineNumber(const char *Ptr) const { - - // Ensure OffsetCache is allocated and populated with offsets of all the - // '\n' bytes. - std::vector<T> *Offsets = nullptr; - if (OffsetCache.isNull()) { - Offsets = new std::vector<T>(); - OffsetCache = Offsets; - size_t Sz = Buffer->getBufferSize(); - assert(Sz <= std::numeric_limits<T>::max()); - StringRef S = Buffer->getBuffer(); - for (size_t N = 0; N < Sz; ++N) { - if (S[N] == '\n') { - Offsets->push_back(static_cast<T>(N)); - } - } - } else { - Offsets = OffsetCache.get<std::vector<T> *>(); +static std::vector<T> &GetOrCreateOffsetCache(void *&OffsetCache, + MemoryBuffer *Buffer) { + if (OffsetCache) + return *static_cast<std::vector<T> *>(OffsetCache); + + // Lazily fill in the offset cache. + auto *Offsets = new std::vector<T>(); + size_t Sz = Buffer->getBufferSize(); + assert(Sz <= std::numeric_limits<T>::max()); + StringRef S = Buffer->getBuffer(); + for (size_t N = 0; N < Sz; ++N) { + if (S[N] == '\n') + Offsets->push_back(static_cast<T>(N)); } + OffsetCache = Offsets; + return *Offsets; +} + +template <typename T> +unsigned SourceMgr::SrcBuffer::getLineNumberSpecialized(const char *Ptr) const { + std::vector<T> &Offsets = + GetOrCreateOffsetCache<T>(OffsetCache, Buffer.get()); + const char *BufStart = Buffer->getBufferStart(); assert(Ptr >= BufStart && Ptr <= Buffer->getBufferEnd()); ptrdiff_t PtrDiff = Ptr - BufStart; - assert(PtrDiff >= 0 && static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max()); + assert(PtrDiff >= 0 && + static_cast<size_t>(PtrDiff) <= std::numeric_limits<T>::max()); T PtrOffset = static_cast<T>(PtrDiff); // llvm::lower_bound gives the number of EOL before PtrOffset. Add 1 to get // the line number. - return llvm::lower_bound(*Offsets, PtrOffset) - Offsets->begin() + 1; + return llvm::lower_bound(Offsets, PtrOffset) - Offsets.begin() + 1; +} + +/// Look up a given \p Ptr in in the buffer, determining which line it came +/// from. +unsigned SourceMgr::SrcBuffer::getLineNumber(const char *Ptr) const { + size_t Sz = Buffer->getBufferSize(); + if (Sz <= std::numeric_limits<uint8_t>::max()) + return getLineNumberSpecialized<uint8_t>(Ptr); + else if (Sz <= std::numeric_limits<uint16_t>::max()) + return getLineNumberSpecialized<uint16_t>(Ptr); + else if (Sz <= std::numeric_limits<uint32_t>::max()) + return getLineNumberSpecialized<uint32_t>(Ptr); + else + return getLineNumberSpecialized<uint64_t>(Ptr); +} + +template <typename T> +const char *SourceMgr::SrcBuffer::getPointerForLineNumberSpecialized( + unsigned LineNo) const { + std::vector<T> &Offsets = + GetOrCreateOffsetCache<T>(OffsetCache, Buffer.get()); + + // We start counting line and column numbers from 1. + if (LineNo != 0) + --LineNo; + + const char *BufStart = Buffer->getBufferStart(); + + // The offset cache contains the location of the \n for the specified line, + // we want the start of the line. As such, we look for the previous entry. + if (LineNo == 0) + return BufStart; + if (LineNo > Offsets.size()) + return nullptr; + return BufStart + Offsets[LineNo - 1] + 1; +} + +/// Return a pointer to the first character of the specified line number or +/// null if the line number is invalid. +const char * +SourceMgr::SrcBuffer::getPointerForLineNumber(unsigned LineNo) const { + size_t Sz = Buffer->getBufferSize(); + if (Sz <= std::numeric_limits<uint8_t>::max()) + return getPointerForLineNumberSpecialized<uint8_t>(LineNo); + else if (Sz <= std::numeric_limits<uint16_t>::max()) + return getPointerForLineNumberSpecialized<uint16_t>(LineNo); + else if (Sz <= std::numeric_limits<uint32_t>::max()) + return getPointerForLineNumberSpecialized<uint32_t>(LineNo); + else + return getPointerForLineNumberSpecialized<uint64_t>(LineNo); } SourceMgr::SrcBuffer::SrcBuffer(SourceMgr::SrcBuffer &&Other) - : Buffer(std::move(Other.Buffer)), - OffsetCache(Other.OffsetCache), - IncludeLoc(Other.IncludeLoc) { + : Buffer(std::move(Other.Buffer)), OffsetCache(Other.OffsetCache), + IncludeLoc(Other.IncludeLoc) { Other.OffsetCache = nullptr; } SourceMgr::SrcBuffer::~SrcBuffer() { - if (!OffsetCache.isNull()) { - if (OffsetCache.is<std::vector<uint8_t>*>()) - delete OffsetCache.get<std::vector<uint8_t>*>(); - else if (OffsetCache.is<std::vector<uint16_t>*>()) - delete OffsetCache.get<std::vector<uint16_t>*>(); - else if (OffsetCache.is<std::vector<uint32_t>*>()) - delete OffsetCache.get<std::vector<uint32_t>*>(); + if (OffsetCache) { + size_t Sz = Buffer->getBufferSize(); + if (Sz <= std::numeric_limits<uint8_t>::max()) + delete static_cast<std::vector<uint8_t> *>(OffsetCache); + else if (Sz <= std::numeric_limits<uint16_t>::max()) + delete static_cast<std::vector<uint16_t> *>(OffsetCache); + else if (Sz <= std::numeric_limits<uint32_t>::max()) + delete static_cast<std::vector<uint32_t> *>(OffsetCache); else - delete OffsetCache.get<std::vector<uint64_t>*>(); + delete static_cast<std::vector<uint64_t> *>(OffsetCache); OffsetCache = nullptr; } } @@ -130,39 +185,58 @@ SourceMgr::getLineAndColumn(SMLoc Loc, unsigned BufferID) const { auto &SB = getBufferInfo(BufferID); const char *Ptr = Loc.getPointer(); - size_t Sz = SB.Buffer->getBufferSize(); - unsigned LineNo; - if (Sz <= std::numeric_limits<uint8_t>::max()) - LineNo = SB.getLineNumber<uint8_t>(Ptr); - else if (Sz <= std::numeric_limits<uint16_t>::max()) - LineNo = SB.getLineNumber<uint16_t>(Ptr); - else if (Sz <= std::numeric_limits<uint32_t>::max()) - LineNo = SB.getLineNumber<uint32_t>(Ptr); - else - LineNo = SB.getLineNumber<uint64_t>(Ptr); - + unsigned LineNo = SB.getLineNumber(Ptr); const char *BufStart = SB.Buffer->getBufferStart(); - size_t NewlineOffs = StringRef(BufStart, Ptr-BufStart).find_last_of("\n\r"); - if (NewlineOffs == StringRef::npos) NewlineOffs = ~(size_t)0; - return std::make_pair(LineNo, Ptr-BufStart-NewlineOffs); + size_t NewlineOffs = StringRef(BufStart, Ptr - BufStart).find_last_of("\n\r"); + if (NewlineOffs == StringRef::npos) + NewlineOffs = ~(size_t)0; + return std::make_pair(LineNo, Ptr - BufStart - NewlineOffs); +} + +/// Given a line and column number in a mapped buffer, turn it into an SMLoc. +/// This will return a null SMLoc if the line/column location is invalid. +SMLoc SourceMgr::FindLocForLineAndColumn(unsigned BufferID, unsigned LineNo, + unsigned ColNo) { + auto &SB = getBufferInfo(BufferID); + const char *Ptr = SB.getPointerForLineNumber(LineNo); + if (!Ptr) + return SMLoc(); + + // We start counting line and column numbers from 1. + if (ColNo != 0) + --ColNo; + + // If we have a column number, validate it. + if (ColNo) { + // Make sure the location is within the current line. + if (Ptr + ColNo > SB.Buffer->getBufferEnd()) + return SMLoc(); + + // Make sure there is no newline in the way. + if (StringRef(Ptr, ColNo).find_first_of("\n\r") != StringRef::npos) + return SMLoc(); + + Ptr += ColNo; + } + + return SMLoc::getFromPointer(Ptr); } void SourceMgr::PrintIncludeStack(SMLoc IncludeLoc, raw_ostream &OS) const { - if (IncludeLoc == SMLoc()) return; // Top of stack. + if (IncludeLoc == SMLoc()) + return; // Top of stack. unsigned CurBuf = FindBufferContainingLoc(IncludeLoc); assert(CurBuf && "Invalid or unspecified location!"); PrintIncludeStack(getBufferInfo(CurBuf).IncludeLoc, OS); - OS << "Included from " - << getBufferInfo(CurBuf).Buffer->getBufferIdentifier() + OS << "Included from " << getBufferInfo(CurBuf).Buffer->getBufferIdentifier() << ":" << FindLineNumber(IncludeLoc, CurBuf) << ":\n"; } SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind, - const Twine &Msg, - ArrayRef<SMRange> Ranges, + 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. @@ -196,7 +270,8 @@ SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind, // location. for (unsigned i = 0, e = Ranges.size(); i != e; ++i) { SMRange R = Ranges[i]; - if (!R.isValid()) continue; + if (!R.isValid()) + continue; // If the line doesn't contain any part of the range, then ignore it. if (R.Start.getPointer() > LineEnd || R.End.getPointer() < LineStart) @@ -210,16 +285,16 @@ SMDiagnostic SourceMgr::GetMessage(SMLoc Loc, SourceMgr::DiagKind Kind, // Translate from SMLoc ranges to column ranges. // FIXME: Handle multibyte characters. - ColRanges.push_back(std::make_pair(R.Start.getPointer()-LineStart, - R.End.getPointer()-LineStart)); + ColRanges.push_back(std::make_pair(R.Start.getPointer() - LineStart, + R.End.getPointer() - LineStart)); } LineAndCol = getLineAndColumn(Loc, CurBuf); } return SMDiagnostic(*this, Loc, BufferID, LineAndCol.first, - LineAndCol.second-1, Kind, Msg.str(), - LineStr, ColRanges, FixIts); + LineAndCol.second - 1, Kind, Msg.str(), LineStr, + ColRanges, FixIts); } void SourceMgr::PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic, @@ -240,9 +315,9 @@ void SourceMgr::PrintMessage(raw_ostream &OS, const SMDiagnostic &Diagnostic, } void SourceMgr::PrintMessage(raw_ostream &OS, SMLoc Loc, - SourceMgr::DiagKind Kind, - const Twine &Msg, ArrayRef<SMRange> Ranges, - ArrayRef<SMFixIt> FixIts, bool ShowColors) const { + SourceMgr::DiagKind Kind, const Twine &Msg, + ArrayRef<SMRange> Ranges, ArrayRef<SMFixIt> FixIts, + bool ShowColors) const { PrintMessage(OS, GetMessage(Loc, Kind, Msg, Ranges, FixIts), ShowColors); } @@ -253,22 +328,32 @@ void SourceMgr::PrintMessage(SMLoc Loc, SourceMgr::DiagKind Kind, } //===----------------------------------------------------------------------===// +// SMFixIt Implementation +//===----------------------------------------------------------------------===// + +SMFixIt::SMFixIt(SMRange R, const Twine &Replacement) + : Range(R), Text(Replacement.str()) { + assert(R.isValid()); +} + +//===----------------------------------------------------------------------===// // SMDiagnostic Implementation //===----------------------------------------------------------------------===// -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, +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<SMFixIt> Hints) - : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind), - Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()), - FixIts(Hints.begin(), Hints.end()) { + : SM(&sm), Loc(L), Filename(std::string(FN)), LineNo(Line), ColumnNo(Col), + Kind(Kind), Message(std::string(Msg)), LineContents(std::string(LineStr)), + Ranges(Ranges.vec()), FixIts(Hints.begin(), Hints.end()) { llvm::sort(FixIts); } static void buildFixItLine(std::string &CaretLine, std::string &FixItLine, - ArrayRef<SMFixIt> FixIts, ArrayRef<char> SourceLine){ + ArrayRef<SMFixIt> FixIts, + ArrayRef<char> SourceLine) { if (FixIts.empty()) return; @@ -277,8 +362,8 @@ static void buildFixItLine(std::string &CaretLine, std::string &FixItLine, size_t PrevHintEndCol = 0; - for (ArrayRef<SMFixIt>::iterator I = FixIts.begin(), E = FixIts.end(); - I != E; ++I) { + for (ArrayRef<SMFixIt>::iterator I = FixIts.begin(), E = FixIts.end(); I != E; + ++I) { // If the fixit contains a newline or tab, ignore it. if (I->getText().find_first_of("\n\r\t") != StringRef::npos) continue; @@ -361,14 +446,14 @@ static void printSourceLine(raw_ostream &S, StringRef LineContents) { S << '\n'; } -static bool isNonASCII(char c) { - return c & 0x80; -} +static bool isNonASCII(char c) { return c & 0x80; } + +void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, bool ShowColors, + bool ShowKindLabel) const { + ColorMode Mode = ShowColors ? ColorMode::Auto : ColorMode::Disable; -void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, - bool ShowColors, bool ShowKindLabel) const { { - WithColor S(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors); + WithColor S(OS, raw_ostream::SAVEDCOLOR, true, false, Mode); if (ProgName && ProgName[0]) S << ProgName << ": "; @@ -405,8 +490,7 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, } } - WithColor(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors) - << Message << '\n'; + WithColor(OS, raw_ostream::SAVEDCOLOR, true, false, Mode) << Message << '\n'; if (LineNo == -1 || ColumnNo == -1) return; @@ -423,22 +507,21 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, size_t NumColumns = LineContents.size(); // Build the line with the caret and ranges. - std::string CaretLine(NumColumns+1, ' '); + std::string CaretLine(NumColumns + 1, ' '); // Expand any ranges. for (unsigned r = 0, e = Ranges.size(); r != e; ++r) { std::pair<unsigned, unsigned> R = Ranges[r]; std::fill(&CaretLine[R.first], - &CaretLine[std::min((size_t)R.second, CaretLine.size())], - '~'); + &CaretLine[std::min((size_t)R.second, CaretLine.size())], '~'); } // Add any fix-its. // FIXME: Find the beginning of the line properly for multibyte characters. std::string FixItInsertionLine; - buildFixItLine(CaretLine, FixItInsertionLine, FixIts, - makeArrayRef(Loc.getPointer() - ColumnNo, - LineContents.size())); + buildFixItLine( + CaretLine, FixItInsertionLine, FixIts, + makeArrayRef(Loc.getPointer() - ColumnNo, LineContents.size())); // Finally, plop on the caret. if (unsigned(ColumnNo) <= NumColumns) @@ -449,12 +532,13 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, // ... and remove trailing whitespace so the output doesn't wrap for it. We // know that the line isn't completely empty because it has the caret in it at // least. - CaretLine.erase(CaretLine.find_last_not_of(' ')+1); + CaretLine.erase(CaretLine.find_last_not_of(' ') + 1); printSourceLine(OS, LineContents); { - WithColor S(OS, raw_ostream::GREEN, true, false, !ShowColors); + ColorMode Mode = ShowColors ? ColorMode::Auto : ColorMode::Disable; + WithColor S(OS, raw_ostream::GREEN, true, false, Mode); // Print out the caret line, matching tabs in the source line. for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) { diff --git a/llvm/lib/Support/SpecialCaseList.cpp b/llvm/lib/Support/SpecialCaseList.cpp index d1ff44cefb086..73f852624a697 100644 --- a/llvm/lib/Support/SpecialCaseList.cpp +++ b/llvm/lib/Support/SpecialCaseList.cpp @@ -126,7 +126,7 @@ bool SpecialCaseList::createInternal(const MemoryBuffer *MB, bool SpecialCaseList::parse(const MemoryBuffer *MB, StringMap<size_t> &SectionsMap, std::string &Error) { - // Iterate through each line in the blacklist file. + // Iterate through each line in the exclusion list file. SmallVector<StringRef, 16> Lines; MB->getBuffer().split(Lines, '\n'); @@ -172,14 +172,14 @@ bool SpecialCaseList::parse(const MemoryBuffer *MB, } std::pair<StringRef, StringRef> SplitRegexp = SplitLine.second.split("="); - std::string Regexp = SplitRegexp.first; + std::string Regexp = std::string(SplitRegexp.first); StringRef Category = SplitRegexp.second; // Create this section if it has not been seen before. if (SectionsMap.find(Section) == SectionsMap.end()) { std::unique_ptr<Matcher> M = std::make_unique<Matcher>(); std::string REError; - if (!M->insert(Section, LineNo, REError)) { + if (!M->insert(std::string(Section), LineNo, REError)) { Error = (Twine("malformed section ") + Section + ": '" + REError).str(); return false; } diff --git a/llvm/lib/Support/Statistic.cpp b/llvm/lib/Support/Statistic.cpp index 25f13871e2e42..e9308ab575abe 100644 --- a/llvm/lib/Support/Statistic.cpp +++ b/llvm/lib/Support/Statistic.cpp @@ -246,7 +246,7 @@ void llvm::PrintStatistics() { // Get the stream to write to. std::unique_ptr<raw_ostream> OutStream = CreateInfoOutputFile(); (*OutStream) << "Statistics are disabled. " - << "Build with asserts or with -DLLVM_ENABLE_STATS\n"; + << "Build with asserts or with -DLLVM_FORCE_ENABLE_STATS\n"; } #endif } diff --git a/llvm/lib/Support/StringExtras.cpp b/llvm/lib/Support/StringExtras.cpp index af8dd463e125d..c206bd214519c 100644 --- a/llvm/lib/Support/StringExtras.cpp +++ b/llvm/lib/Support/StringExtras.cpp @@ -13,6 +13,8 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/raw_ostream.h" +#include <cctype> + using namespace llvm; /// StrInStrNoCase - Portable version of strcasestr. Locates the first @@ -90,3 +92,46 @@ void llvm::printLowerCase(StringRef String, raw_ostream &Out) { for (const char C : String) Out << toLower(C); } + +std::string llvm::convertToSnakeFromCamelCase(StringRef input) { + if (input.empty()) + return ""; + + std::string snakeCase; + snakeCase.reserve(input.size()); + for (char c : input) { + if (!std::isupper(c)) { + snakeCase.push_back(c); + continue; + } + + if (!snakeCase.empty() && snakeCase.back() != '_') + snakeCase.push_back('_'); + snakeCase.push_back(llvm::toLower(c)); + } + return snakeCase; +} + +std::string llvm::convertToCamelFromSnakeCase(StringRef input, + bool capitalizeFirst) { + if (input.empty()) + return ""; + + std::string output; + output.reserve(input.size()); + + // Push the first character, capatilizing if necessary. + if (capitalizeFirst && std::islower(input.front())) + output.push_back(llvm::toUpper(input.front())); + else + output.push_back(input.front()); + + // Walk the input converting any `*_[a-z]` snake case into `*[A-Z]` camelCase. + for (size_t pos = 1, e = input.size(); pos < e; ++pos) { + if (input[pos] == '_' && pos != (e - 1) && std::islower(input[pos + 1])) + output.push_back(llvm::toUpper(input[++pos])); + else + output.push_back(input[pos]); + } + return output; +} diff --git a/llvm/lib/Support/StringMap.cpp b/llvm/lib/Support/StringMap.cpp index 6b5ea020dd46d..f65d3846623c8 100644 --- a/llvm/lib/Support/StringMap.cpp +++ b/llvm/lib/Support/StringMap.cpp @@ -12,10 +12,8 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringExtras.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/DJB.h" #include "llvm/Support/MathExtras.h" -#include <cassert> using namespace llvm; @@ -50,23 +48,22 @@ StringMapImpl::StringMapImpl(unsigned InitSize, unsigned itemSize) { } void StringMapImpl::init(unsigned InitSize) { - assert((InitSize & (InitSize-1)) == 0 && + assert((InitSize & (InitSize - 1)) == 0 && "Init Size must be a power of 2 or zero!"); unsigned NewNumBuckets = InitSize ? InitSize : 16; NumItems = 0; NumTombstones = 0; - TheTable = static_cast<StringMapEntryBase **>( - safe_calloc(NewNumBuckets+1, - sizeof(StringMapEntryBase **) + sizeof(unsigned))); + TheTable = static_cast<StringMapEntryBase **>(safe_calloc( + NewNumBuckets + 1, sizeof(StringMapEntryBase **) + sizeof(unsigned))); // Set the member only if TheTable was successfully allocated NumBuckets = NewNumBuckets; // Allocate one extra bucket, set it to look filled so the iterators stop at // end. - TheTable[NumBuckets] = (StringMapEntryBase*)2; + TheTable[NumBuckets] = (StringMapEntryBase *)2; } /// LookupBucketFor - Look up the bucket that the specified string should end @@ -76,12 +73,12 @@ void StringMapImpl::init(unsigned InitSize) { /// of the string. unsigned StringMapImpl::LookupBucketFor(StringRef Name) { unsigned HTSize = NumBuckets; - if (HTSize == 0) { // Hash table unallocated so far? + if (HTSize == 0) { // Hash table unallocated so far? init(16); HTSize = NumBuckets; } unsigned FullHashValue = djbHash(Name, 0); - unsigned BucketNo = FullHashValue & (HTSize-1); + unsigned BucketNo = FullHashValue & (HTSize - 1); unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); unsigned ProbeAmt = 1; @@ -103,7 +100,8 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) { if (BucketItem == getTombstoneVal()) { // Skip over tombstones. However, remember the first one we see. - if (FirstTombstone == -1) FirstTombstone = BucketNo; + if (FirstTombstone == -1) + FirstTombstone = BucketNo; } else if (LLVM_LIKELY(HashTable[BucketNo] == FullHashValue)) { // If the full hash value matches, check deeply for a match. The common // case here is that we are only looking at the buckets (for item info @@ -112,7 +110,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) { // Do the comparison like this because Name isn't necessarily // null-terminated! - char *ItemStr = (char*)BucketItem+ItemSize; + char *ItemStr = (char *)BucketItem + ItemSize; if (Name == StringRef(ItemStr, BucketItem->getKeyLength())) { // We found a match! return BucketNo; @@ -120,7 +118,7 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) { } // Okay, we didn't find the item. Probe to the next bucket. - BucketNo = (BucketNo+ProbeAmt) & (HTSize-1); + BucketNo = (BucketNo + ProbeAmt) & (HTSize - 1); // Use quadratic probing, it has fewer clumping artifacts than linear // probing and has good cache behavior in the common case. @@ -133,9 +131,10 @@ unsigned StringMapImpl::LookupBucketFor(StringRef Name) { /// This does not modify the map. int StringMapImpl::FindKey(StringRef Key) const { unsigned HTSize = NumBuckets; - if (HTSize == 0) return -1; // Really empty table? + if (HTSize == 0) + return -1; // Really empty table? unsigned FullHashValue = djbHash(Key, 0); - unsigned BucketNo = FullHashValue & (HTSize-1); + unsigned BucketNo = FullHashValue & (HTSize - 1); unsigned *HashTable = (unsigned *)(TheTable + NumBuckets + 1); unsigned ProbeAmt = 1; @@ -155,7 +154,7 @@ int StringMapImpl::FindKey(StringRef Key) const { // Do the comparison like this because NameStart isn't necessarily // null-terminated! - char *ItemStr = (char*)BucketItem+ItemSize; + char *ItemStr = (char *)BucketItem + ItemSize; if (Key == StringRef(ItemStr, BucketItem->getKeyLength())) { // We found a match! return BucketNo; @@ -163,7 +162,7 @@ int StringMapImpl::FindKey(StringRef Key) const { } // Okay, we didn't find the item. Probe to the next bucket. - BucketNo = (BucketNo+ProbeAmt) & (HTSize-1); + BucketNo = (BucketNo + ProbeAmt) & (HTSize - 1); // Use quadratic probing, it has fewer clumping artifacts than linear // probing and has good cache behavior in the common case. @@ -174,7 +173,7 @@ int StringMapImpl::FindKey(StringRef Key) const { /// RemoveKey - Remove the specified StringMapEntry from the table, but do not /// delete it. This aborts if the value isn't in the table. void StringMapImpl::RemoveKey(StringMapEntryBase *V) { - const char *VStr = (char*)V + ItemSize; + const char *VStr = (char *)V + ItemSize; StringMapEntryBase *V2 = RemoveKey(StringRef(VStr, V->getKeyLength())); (void)V2; assert(V == V2 && "Didn't find key?"); @@ -184,7 +183,8 @@ void StringMapImpl::RemoveKey(StringMapEntryBase *V) { /// table, returning it. If the key is not in the table, this returns null. StringMapEntryBase *StringMapImpl::RemoveKey(StringRef Key) { int Bucket = FindKey(Key); - if (Bucket == -1) return nullptr; + if (Bucket == -1) + return nullptr; StringMapEntryBase *Result = TheTable[Bucket]; TheTable[Bucket] = getTombstoneVal(); @@ -205,7 +205,7 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { // the buckets are empty (meaning that many are filled with tombstones), // grow/rehash the table. if (LLVM_UNLIKELY(NumItems * 4 > NumBuckets * 3)) { - NewSize = NumBuckets*2; + NewSize = NumBuckets * 2; } else if (LLVM_UNLIKELY(NumBuckets - (NumItems + NumTombstones) <= NumBuckets / 8)) { NewSize = NumBuckets; @@ -216,11 +216,11 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { unsigned NewBucketNo = BucketNo; // Allocate one extra bucket which will always be non-empty. This allows the // iterators to stop at end. - auto NewTableArray = static_cast<StringMapEntryBase **>( - safe_calloc(NewSize+1, sizeof(StringMapEntryBase *) + sizeof(unsigned))); + auto NewTableArray = static_cast<StringMapEntryBase **>(safe_calloc( + NewSize + 1, sizeof(StringMapEntryBase *) + sizeof(unsigned))); unsigned *NewHashArray = (unsigned *)(NewTableArray + NewSize + 1); - NewTableArray[NewSize] = (StringMapEntryBase*)2; + NewTableArray[NewSize] = (StringMapEntryBase *)2; // Rehash all the items into their new buckets. Luckily :) we already have // the hash values available, so we don't have to rehash any strings. @@ -229,10 +229,10 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { if (Bucket && Bucket != getTombstoneVal()) { // Fast case, bucket available. unsigned FullHash = HashTable[I]; - unsigned NewBucket = FullHash & (NewSize-1); + unsigned NewBucket = FullHash & (NewSize - 1); if (!NewTableArray[NewBucket]) { - NewTableArray[FullHash & (NewSize-1)] = Bucket; - NewHashArray[FullHash & (NewSize-1)] = FullHash; + NewTableArray[FullHash & (NewSize - 1)] = Bucket; + NewHashArray[FullHash & (NewSize - 1)] = FullHash; if (I == BucketNo) NewBucketNo = NewBucket; continue; @@ -241,7 +241,7 @@ unsigned StringMapImpl::RehashTable(unsigned BucketNo) { // Otherwise probe for a spot. unsigned ProbeSize = 1; do { - NewBucket = (NewBucket + ProbeSize++) & (NewSize-1); + NewBucket = (NewBucket + ProbeSize++) & (NewSize - 1); } while (NewTableArray[NewBucket]); // Finally found a slot. Fill it in. diff --git a/llvm/lib/Support/StringPool.cpp b/llvm/lib/Support/StringPool.cpp deleted file mode 100644 index 82351017b8cca..0000000000000 --- a/llvm/lib/Support/StringPool.cpp +++ /dev/null @@ -1,34 +0,0 @@ -//===-- StringPool.cpp - Interned string pool -----------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file implements the StringPool class. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/StringPool.h" -#include "llvm/ADT/StringRef.h" - -using namespace llvm; - -StringPool::StringPool() {} - -StringPool::~StringPool() { - assert(InternTable.empty() && "PooledStringPtr leaked!"); -} - -PooledStringPtr StringPool::intern(StringRef Key) { - table_t::iterator I = InternTable.find(Key); - if (I != InternTable.end()) - return PooledStringPtr(&*I); - - entry_t *S = entry_t::Create(Key); - S->getValue().Pool = this; - InternTable.insert(S); - - return PooledStringPtr(S); -} diff --git a/llvm/lib/Support/StringRef.cpp b/llvm/lib/Support/StringRef.cpp index 104482de4ad70..ab67ef9ce85c1 100644 --- a/llvm/lib/Support/StringRef.cpp +++ b/llvm/lib/Support/StringRef.cpp @@ -19,7 +19,7 @@ using namespace llvm; // MSVC emits references to this into the translation units which reference it. #ifndef _MSC_VER -const size_t StringRef::npos; +constexpr size_t StringRef::npos; #endif // strncasecmp() is not available on non-POSIX systems, so define an @@ -106,19 +106,13 @@ unsigned StringRef::edit_distance(llvm::StringRef Other, //===----------------------------------------------------------------------===// std::string StringRef::lower() const { - std::string Result(size(), char()); - for (size_type i = 0, e = size(); i != e; ++i) { - Result[i] = toLower(Data[i]); - } - return Result; + return std::string(map_iterator(begin(), toLower), + map_iterator(end(), toLower)); } std::string StringRef::upper() const { - std::string Result(size(), char()); - for (size_type i = 0, e = size(); i != e; ++i) { - Result[i] = toUpper(Data[i]); - } - return Result; + return std::string(map_iterator(begin(), toUpper), + map_iterator(end(), toUpper)); } //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Support/SuffixTree.cpp b/llvm/lib/Support/SuffixTree.cpp new file mode 100644 index 0000000000000..0d419f12cd1d6 --- /dev/null +++ b/llvm/lib/Support/SuffixTree.cpp @@ -0,0 +1,210 @@ +//===- llvm/Support/SuffixTree.cpp - Implement Suffix Tree ------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements the Suffix Tree class. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/SuffixTree.h" +#include "llvm/Support/Allocator.h" +#include <vector> + +using namespace llvm; + +SuffixTree::SuffixTree(const std::vector<unsigned> &Str) : Str(Str) { + Root = insertInternalNode(nullptr, EmptyIdx, EmptyIdx, 0); + Active.Node = Root; + + // Keep track of the number of suffixes we have to add of the current + // prefix. + unsigned SuffixesToAdd = 0; + + // Construct the suffix tree iteratively on each prefix of the string. + // PfxEndIdx is the end index of the current prefix. + // End is one past the last element in the string. + for (unsigned PfxEndIdx = 0, End = Str.size(); PfxEndIdx < End; PfxEndIdx++) { + SuffixesToAdd++; + LeafEndIdx = PfxEndIdx; // Extend each of the leaves. + SuffixesToAdd = extend(PfxEndIdx, SuffixesToAdd); + } + + // Set the suffix indices of each leaf. + assert(Root && "Root node can't be nullptr!"); + setSuffixIndices(); +} + +SuffixTreeNode *SuffixTree::insertLeaf(SuffixTreeNode &Parent, + unsigned StartIdx, unsigned Edge) { + + assert(StartIdx <= LeafEndIdx && "String can't start after it ends!"); + + SuffixTreeNode *N = new (NodeAllocator.Allocate()) + SuffixTreeNode(StartIdx, &LeafEndIdx, nullptr); + Parent.Children[Edge] = N; + + return N; +} + +SuffixTreeNode *SuffixTree::insertInternalNode(SuffixTreeNode *Parent, + unsigned StartIdx, + unsigned EndIdx, unsigned Edge) { + + assert(StartIdx <= EndIdx && "String can't start after it ends!"); + assert(!(!Parent && StartIdx != EmptyIdx) && + "Non-root internal nodes must have parents!"); + + unsigned *E = new (InternalEndIdxAllocator) unsigned(EndIdx); + SuffixTreeNode *N = + new (NodeAllocator.Allocate()) SuffixTreeNode(StartIdx, E, Root); + if (Parent) + Parent->Children[Edge] = N; + + return N; +} + +void SuffixTree::setSuffixIndices() { + // List of nodes we need to visit along with the current length of the + // string. + std::vector<std::pair<SuffixTreeNode *, unsigned>> ToVisit; + + // Current node being visited. + SuffixTreeNode *CurrNode = Root; + + // Sum of the lengths of the nodes down the path to the current one. + unsigned CurrNodeLen = 0; + ToVisit.push_back({CurrNode, CurrNodeLen}); + while (!ToVisit.empty()) { + std::tie(CurrNode, CurrNodeLen) = ToVisit.back(); + ToVisit.pop_back(); + CurrNode->ConcatLen = CurrNodeLen; + for (auto &ChildPair : CurrNode->Children) { + assert(ChildPair.second && "Node had a null child!"); + ToVisit.push_back( + {ChildPair.second, CurrNodeLen + ChildPair.second->size()}); + } + + // No children, so we are at the end of the string. + if (CurrNode->Children.size() == 0 && !CurrNode->isRoot()) + CurrNode->SuffixIdx = Str.size() - CurrNodeLen; + } +} + +unsigned SuffixTree::extend(unsigned EndIdx, unsigned SuffixesToAdd) { + SuffixTreeNode *NeedsLink = nullptr; + + while (SuffixesToAdd > 0) { + + // Are we waiting to add anything other than just the last character? + if (Active.Len == 0) { + // If not, then say the active index is the end index. + Active.Idx = EndIdx; + } + + assert(Active.Idx <= EndIdx && "Start index can't be after end index!"); + + // The first character in the current substring we're looking at. + unsigned FirstChar = Str[Active.Idx]; + + // Have we inserted anything starting with FirstChar at the current node? + if (Active.Node->Children.count(FirstChar) == 0) { + // If not, then we can just insert a leaf and move to the next step. + insertLeaf(*Active.Node, EndIdx, FirstChar); + + // The active node is an internal node, and we visited it, so it must + // need a link if it doesn't have one. + if (NeedsLink) { + NeedsLink->Link = Active.Node; + NeedsLink = nullptr; + } + } else { + // There's a match with FirstChar, so look for the point in the tree to + // insert a new node. + SuffixTreeNode *NextNode = Active.Node->Children[FirstChar]; + + unsigned SubstringLen = NextNode->size(); + + // Is the current suffix we're trying to insert longer than the size of + // the child we want to move to? + if (Active.Len >= SubstringLen) { + // If yes, then consume the characters we've seen and move to the next + // node. + Active.Idx += SubstringLen; + Active.Len -= SubstringLen; + Active.Node = NextNode; + continue; + } + + // Otherwise, the suffix we're trying to insert must be contained in the + // next node we want to move to. + unsigned LastChar = Str[EndIdx]; + + // Is the string we're trying to insert a substring of the next node? + if (Str[NextNode->StartIdx + Active.Len] == LastChar) { + // If yes, then we're done for this step. Remember our insertion point + // and move to the next end index. At this point, we have an implicit + // suffix tree. + if (NeedsLink && !Active.Node->isRoot()) { + NeedsLink->Link = Active.Node; + NeedsLink = nullptr; + } + + Active.Len++; + break; + } + + // The string we're trying to insert isn't a substring of the next node, + // but matches up to a point. Split the node. + // + // For example, say we ended our search at a node n and we're trying to + // insert ABD. Then we'll create a new node s for AB, reduce n to just + // representing C, and insert a new leaf node l to represent d. This + // allows us to ensure that if n was a leaf, it remains a leaf. + // + // | ABC ---split---> | AB + // n s + // C / \ D + // n l + + // The node s from the diagram + SuffixTreeNode *SplitNode = + insertInternalNode(Active.Node, NextNode->StartIdx, + NextNode->StartIdx + Active.Len - 1, FirstChar); + + // Insert the new node representing the new substring into the tree as + // a child of the split node. This is the node l from the diagram. + insertLeaf(*SplitNode, EndIdx, LastChar); + + // Make the old node a child of the split node and update its start + // index. This is the node n from the diagram. + NextNode->StartIdx += Active.Len; + SplitNode->Children[Str[NextNode->StartIdx]] = NextNode; + + // SplitNode is an internal node, update the suffix link. + if (NeedsLink) + NeedsLink->Link = SplitNode; + + NeedsLink = SplitNode; + } + + // We've added something new to the tree, so there's one less suffix to + // add. + SuffixesToAdd--; + + if (Active.Node->isRoot()) { + if (Active.Len > 0) { + Active.Len--; + Active.Idx = EndIdx - SuffixesToAdd + 1; + } + } else { + // Start the next phase at the next smallest suffix. + Active.Node = Active.Node->Link; + } + } + + return SuffixesToAdd; +} diff --git a/llvm/lib/Support/SystemUtils.cpp b/llvm/lib/Support/SystemUtils.cpp index 47e0c72ec7c13..f1149e48dce5d 100644 --- a/llvm/lib/Support/SystemUtils.cpp +++ b/llvm/lib/Support/SystemUtils.cpp @@ -15,15 +15,12 @@ #include "llvm/Support/raw_ostream.h" using namespace llvm; -bool llvm::CheckBitcodeOutputToConsole(raw_ostream &stream_to_check, - bool print_warning) { +bool llvm::CheckBitcodeOutputToConsole(raw_ostream &stream_to_check) { if (stream_to_check.is_displayed()) { - if (print_warning) { - errs() << "WARNING: You're attempting to print out a bitcode file.\n" - "This is inadvisable as it may cause display problems. If\n" - "you REALLY want to taste LLVM bitcode first-hand, you\n" - "can force output with the `-f' option.\n\n"; - } + errs() << "WARNING: You're attempting to print out a bitcode file.\n" + "This is inadvisable as it may cause display problems. If\n" + "you REALLY want to taste LLVM bitcode first-hand, you\n" + "can force output with the `-f' option.\n\n"; return true; } return false; diff --git a/llvm/lib/Support/TarWriter.cpp b/llvm/lib/Support/TarWriter.cpp index 6136e92197672..c7a744f0fc98c 100644 --- a/llvm/lib/Support/TarWriter.cpp +++ b/llvm/lib/Support/TarWriter.cpp @@ -131,7 +131,17 @@ static bool splitUstar(StringRef Path, StringRef &Prefix, StringRef &Name) { return true; } - size_t Sep = Path.rfind('/', sizeof(UstarHeader::Prefix) + 1); + // tar 1.13 and earlier unconditionally look at the tar header interpreted + // as an 'oldgnu_header', which has an 'isextended' byte at offset 482 in the + // header, corresponding to offset 137 in the prefix. That's the version of + // tar in gnuwin, so only use 137 of the 155 bytes in the prefix. This means + // we'll need a pax header after 237 bytes of path instead of after 255, + // but in return paths up to 237 bytes work with gnuwin, instead of just + // 137 bytes of directory + 100 bytes of basename previously. + // (tar-1.13 also doesn't support pax headers, but in practice all paths in + // llvm's test suite are short enough for that to not matter.) + const int MaxPrefix = 137; + size_t Sep = Path.rfind('/', MaxPrefix + 1); if (Sep == StringRef::npos) return false; if (Path.size() - Sep - 1 >= sizeof(UstarHeader::Name)) @@ -167,7 +177,8 @@ Expected<std::unique_ptr<TarWriter>> TarWriter::create(StringRef OutputPath, } TarWriter::TarWriter(int FD, StringRef BaseDir) - : OS(FD, /*shouldClose=*/true, /*unbuffered=*/false), BaseDir(BaseDir) {} + : OS(FD, /*shouldClose=*/true, /*unbuffered=*/false), + BaseDir(std::string(BaseDir)) {} // Append a given file to an archive. void TarWriter::append(StringRef Path, StringRef Data) { diff --git a/llvm/lib/Support/TargetParser.cpp b/llvm/lib/Support/TargetParser.cpp index 84ead58b98cd8..be9b541237c74 100644 --- a/llvm/lib/Support/TargetParser.cpp +++ b/llvm/lib/Support/TargetParser.cpp @@ -62,7 +62,7 @@ constexpr GPUInfo R600GPUs[26] = { // This table should be sorted by the value of GPUKind // Don't bother listing the implicitly true features -constexpr GPUInfo AMDGCNGPUs[37] = { +constexpr GPUInfo AMDGCNGPUs[38] = { // Name Canonical Kind Features // Name {{"gfx600"}, {"gfx600"}, GK_GFX600, FEATURE_FAST_FMA_F32}, @@ -99,9 +99,10 @@ constexpr GPUInfo AMDGCNGPUs[37] = { {{"gfx906"}, {"gfx906"}, GK_GFX906, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, {{"gfx908"}, {"gfx908"}, GK_GFX908, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, {{"gfx909"}, {"gfx909"}, GK_GFX909, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, - {{"gfx1010"}, {"gfx1010"}, GK_GFX1010, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, - {{"gfx1011"}, {"gfx1011"}, GK_GFX1011, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, - {{"gfx1012"}, {"gfx1012"}, GK_GFX1012, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, + {{"gfx1010"}, {"gfx1010"}, GK_GFX1010, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32}, + {{"gfx1011"}, {"gfx1011"}, GK_GFX1011, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32}, + {{"gfx1012"}, {"gfx1012"}, GK_GFX1012, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32}, + {{"gfx1030"}, {"gfx1030"}, GK_GFX1030, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32|FEATURE_WAVE32}, }; const GPUInfo *getArchEntry(AMDGPU::GPUKind AK, ArrayRef<GPUInfo> Table) { @@ -203,6 +204,7 @@ AMDGPU::IsaVersion AMDGPU::getIsaVersion(StringRef GPU) { case GK_GFX1010: return {10, 1, 0}; case GK_GFX1011: return {10, 1, 1}; case GK_GFX1012: return {10, 1, 2}; + case GK_GFX1030: return {10, 3, 0}; default: return {0, 0, 0}; } } diff --git a/llvm/lib/Support/ThreadPool.cpp b/llvm/lib/Support/ThreadPool.cpp index 40982d777914d..46a1990cd7196 100644 --- a/llvm/lib/Support/ThreadPool.cpp +++ b/llvm/lib/Support/ThreadPool.cpp @@ -20,16 +20,14 @@ using namespace llvm; #if LLVM_ENABLE_THREADS -// Default to hardware_concurrency -ThreadPool::ThreadPool() : ThreadPool(hardware_concurrency()) {} - -ThreadPool::ThreadPool(unsigned ThreadCount) - : ActiveThreads(0), EnableFlag(true) { +ThreadPool::ThreadPool(ThreadPoolStrategy S) + : ThreadCount(S.compute_thread_count()) { // Create ThreadCount threads that will loop forever, wait on QueueCondition // for tasks to be queued or the Pool to be destroyed. Threads.reserve(ThreadCount); for (unsigned ThreadID = 0; ThreadID < ThreadCount; ++ThreadID) { - Threads.emplace_back([&] { + Threads.emplace_back([S, ThreadID, this] { + S.apply_thread_strategy(ThreadID); while (true) { PackagedTaskTy Task; { @@ -45,24 +43,24 @@ ThreadPool::ThreadPool(unsigned ThreadCount) // We first need to signal that we are active before popping the queue // in order for wait() to properly detect that even if the queue is // empty, there is still a task in flight. - { - std::unique_lock<std::mutex> LockGuard(CompletionLock); - ++ActiveThreads; - } + ++ActiveThreads; Task = std::move(Tasks.front()); Tasks.pop(); } // Run the task we just grabbed Task(); + bool Notify; { // Adjust `ActiveThreads`, in case someone waits on ThreadPool::wait() - std::unique_lock<std::mutex> LockGuard(CompletionLock); + std::lock_guard<std::mutex> LockGuard(QueueLock); --ActiveThreads; + Notify = workCompletedUnlocked(); } - - // Notify task completion, in case someone waits on ThreadPool::wait() - CompletionCondition.notify_all(); + // Notify task completion if this is the last active thread, in case + // someone waits on ThreadPool::wait(). + if (Notify) + CompletionCondition.notify_all(); } }); } @@ -70,12 +68,8 @@ ThreadPool::ThreadPool(unsigned ThreadCount) void ThreadPool::wait() { // Wait for all threads to complete and the queue to be empty - std::unique_lock<std::mutex> LockGuard(CompletionLock); - // The order of the checks for ActiveThreads and Tasks.empty() matters because - // any active threads might be modifying the Tasks queue, and this would be a - // race. - CompletionCondition.wait(LockGuard, - [&] { return !ActiveThreads && Tasks.empty(); }); + std::unique_lock<std::mutex> LockGuard(QueueLock); + CompletionCondition.wait(LockGuard, [&] { return workCompletedUnlocked(); }); } std::shared_future<void> ThreadPool::asyncImpl(TaskTy Task) { @@ -108,12 +102,10 @@ ThreadPool::~ThreadPool() { #else // LLVM_ENABLE_THREADS Disabled -ThreadPool::ThreadPool() : ThreadPool(0) {} - // No threads are launched, issue a warning if ThreadCount is not 0 -ThreadPool::ThreadPool(unsigned ThreadCount) - : ActiveThreads(0) { - if (ThreadCount) { +ThreadPool::ThreadPool(ThreadPoolStrategy S) + : ThreadCount(S.compute_thread_count()) { + if (ThreadCount != 1) { errs() << "Warning: request a ThreadPool with " << ThreadCount << " threads, but LLVM_ENABLE_THREADS has been turned off\n"; } @@ -138,8 +130,6 @@ std::shared_future<void> ThreadPool::asyncImpl(TaskTy Task) { return Future; } -ThreadPool::~ThreadPool() { - wait(); -} +ThreadPool::~ThreadPool() { wait(); } #endif diff --git a/llvm/lib/Support/Threading.cpp b/llvm/lib/Support/Threading.cpp index 48750cef5ec22..61f8ee5be5b31 100644 --- a/llvm/lib/Support/Threading.cpp +++ b/llvm/lib/Support/Threading.cpp @@ -45,10 +45,6 @@ void llvm::llvm_execute_on_thread(void (*Fn)(void *), void *UserData, Fn(UserData); } -unsigned llvm::heavyweight_hardware_concurrency() { return 1; } - -unsigned llvm::hardware_concurrency() { return 1; } - uint64_t llvm::get_threadid() { return 0; } uint32_t llvm::get_max_thread_name_length() { return 0; } @@ -57,6 +53,13 @@ void llvm::set_thread_name(const Twine &Name) {} void llvm::get_thread_name(SmallVectorImpl<char> &Name) { Name.clear(); } +llvm::BitVector llvm::get_thread_affinity_mask() { return {}; } + +unsigned llvm::ThreadPoolStrategy::compute_thread_count() const { + // When threads are disabled, ensure clients will loop at least once. + return 1; +} + #if LLVM_ENABLE_THREADS == 0 void llvm::llvm_execute_on_thread_async( llvm::unique_function<void()> Func, @@ -78,30 +81,18 @@ void llvm::llvm_execute_on_thread_async( #else -#include <thread> -unsigned llvm::heavyweight_hardware_concurrency() { - // 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 std::thread::hardware_concurrency(); - return NumPhysical; -} - -unsigned llvm::hardware_concurrency() { -#if defined(HAVE_SCHED_GETAFFINITY) && defined(HAVE_CPU_COUNT) - cpu_set_t Set; - if (sched_getaffinity(0, sizeof(Set), &Set)) - return CPU_COUNT(&Set); -#endif - // Guard against std::thread::hardware_concurrency() returning 0. - if (unsigned Val = std::thread::hardware_concurrency()) - return Val; - return 1; +int computeHostNumHardwareThreads(); + +unsigned llvm::ThreadPoolStrategy::compute_thread_count() const { + int MaxThreadCount = UseHyperThreads ? computeHostNumHardwareThreads() + : sys::getHostNumPhysicalCores(); + if (MaxThreadCount <= 0) + MaxThreadCount = 1; + if (ThreadsRequested == 0) + return MaxThreadCount; + if (!Limit) + return ThreadsRequested; + return std::min((unsigned)MaxThreadCount, ThreadsRequested); } namespace { @@ -140,3 +131,23 @@ void llvm::llvm_execute_on_thread_async( } #endif + +Optional<ThreadPoolStrategy> +llvm::get_threadpool_strategy(StringRef Num, ThreadPoolStrategy Default) { + if (Num == "all") + return llvm::hardware_concurrency(); + if (Num.empty()) + return Default; + unsigned V; + if (Num.getAsInteger(10, V)) + return None; // malformed 'Num' value + if (V == 0) + return Default; + + // Do not take the Default into account. This effectively disables + // heavyweight_hardware_concurrency() if the user asks for any number of + // threads on the cmd-line. + ThreadPoolStrategy S = llvm::hardware_concurrency(); + S.ThreadsRequested = V; + return S; +} diff --git a/llvm/lib/Support/TimeProfiler.cpp b/llvm/lib/Support/TimeProfiler.cpp index a7c85509064ec..93bf6f57e3480 100644 --- a/llvm/lib/Support/TimeProfiler.cpp +++ b/llvm/lib/Support/TimeProfiler.cpp @@ -11,20 +11,33 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/TimeProfiler.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/JSON.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Threading.h" +#include <algorithm> #include <cassert> #include <chrono> +#include <mutex> #include <string> #include <vector> using namespace std::chrono; +using namespace llvm; -namespace llvm { +static std::mutex Mu; +// List of all instances +static std::vector<TimeTraceProfiler *> + ThreadTimeTraceProfilerInstances; // GUARDED_BY(Mu) +// Per Thread instance +static LLVM_THREAD_LOCAL TimeTraceProfiler *TimeTraceProfilerInstance = nullptr; -TimeTraceProfiler *TimeTraceProfilerInstance = nullptr; +TimeTraceProfiler *llvm::getTimeTraceProfilerInstance() { + return TimeTraceProfilerInstance; +} typedef duration<steady_clock::rep, steady_clock::period> DurationType; typedef time_point<steady_clock> TimePointType; @@ -32,6 +45,7 @@ typedef std::pair<size_t, DurationType> CountAndDurationType; typedef std::pair<std::string, CountAndDurationType> NameAndCountAndDurationType; +namespace { struct Entry { const TimePointType Start; TimePointType End; @@ -57,11 +71,15 @@ struct Entry { .count(); } }; +} // namespace -struct TimeTraceProfiler { +struct llvm::TimeTraceProfiler { TimeTraceProfiler(unsigned TimeTraceGranularity = 0, StringRef ProcName = "") - : StartTime(steady_clock::now()), ProcName(ProcName), - TimeTraceGranularity(TimeTraceGranularity) {} + : BeginningOfTime(system_clock::now()), StartTime(steady_clock::now()), + ProcName(ProcName), Pid(sys::Process::getProcessId()), + Tid(llvm::get_threadid()), TimeTraceGranularity(TimeTraceGranularity) { + llvm::get_thread_name(ThreadName); + } void begin(std::string Name, llvm::function_ref<std::string()> Detail) { Stack.emplace_back(steady_clock::now(), TimePointType(), std::move(Name), @@ -70,7 +88,7 @@ struct TimeTraceProfiler { void end() { assert(!Stack.empty() && "Must call begin() first"); - auto &E = Stack.back(); + Entry &E = Stack.back(); E.End = steady_clock::now(); // Check that end times monotonically increase. @@ -103,22 +121,30 @@ struct TimeTraceProfiler { Stack.pop_back(); } - void Write(raw_pwrite_stream &OS) { + // Write events from this TimeTraceProfilerInstance and + // ThreadTimeTraceProfilerInstances. + void write(raw_pwrite_stream &OS) { + // Acquire Mutex as reading ThreadTimeTraceProfilerInstances. + std::lock_guard<std::mutex> Lock(Mu); assert(Stack.empty() && - "All profiler sections should be ended when calling Write"); + "All profiler sections should be ended when calling write"); + assert(llvm::all_of(ThreadTimeTraceProfilerInstances, + [](const auto &TTP) { return TTP->Stack.empty(); }) && + "All profiler sections should be ended when calling write"); + json::OStream J(OS); J.objectBegin(); J.attributeBegin("traceEvents"); J.arrayBegin(); // Emit all events for the main flame graph. - for (const auto &E : Entries) { + auto writeEvent = [&](const auto &E, uint64_t Tid) { auto StartUs = E.getFlameGraphStartUs(StartTime); auto DurUs = E.getFlameGraphDurUs(); - J.object([&]{ - J.attribute("pid", 1); - J.attribute("tid", 0); + J.object([&] { + J.attribute("pid", Pid); + J.attribute("tid", int64_t(Tid)); J.attribute("ph", "X"); J.attribute("ts", StartUs); J.attribute("dur", DurUs); @@ -127,100 +153,178 @@ struct TimeTraceProfiler { J.attributeObject("args", [&] { J.attribute("detail", E.Detail); }); } }); - } + }; + for (const Entry &E : Entries) + writeEvent(E, this->Tid); + for (const TimeTraceProfiler *TTP : ThreadTimeTraceProfilerInstances) + for (const Entry &E : TTP->Entries) + writeEvent(E, TTP->Tid); // Emit totals by section name as additional "thread" events, sorted from // longest one. - int Tid = 1; + // Find highest used thread id. + uint64_t MaxTid = this->Tid; + for (const TimeTraceProfiler *TTP : ThreadTimeTraceProfilerInstances) + MaxTid = std::max(MaxTid, TTP->Tid); + + // Combine all CountAndTotalPerName from threads into one. + StringMap<CountAndDurationType> AllCountAndTotalPerName; + auto combineStat = [&](const auto &Stat) { + StringRef Key = Stat.getKey(); + auto Value = Stat.getValue(); + auto &CountAndTotal = AllCountAndTotalPerName[Key]; + CountAndTotal.first += Value.first; + CountAndTotal.second += Value.second; + }; + for (const auto &Stat : CountAndTotalPerName) + combineStat(Stat); + for (const TimeTraceProfiler *TTP : ThreadTimeTraceProfilerInstances) + for (const auto &Stat : TTP->CountAndTotalPerName) + combineStat(Stat); + std::vector<NameAndCountAndDurationType> SortedTotals; - SortedTotals.reserve(CountAndTotalPerName.size()); - for (const auto &E : CountAndTotalPerName) - SortedTotals.emplace_back(E.getKey(), E.getValue()); - - llvm::sort(SortedTotals.begin(), SortedTotals.end(), - [](const NameAndCountAndDurationType &A, - const NameAndCountAndDurationType &B) { - return A.second.second > B.second.second; - }); - for (const auto &E : SortedTotals) { - auto DurUs = duration_cast<microseconds>(E.second.second).count(); - auto Count = CountAndTotalPerName[E.first].first; - - J.object([&]{ - J.attribute("pid", 1); - J.attribute("tid", Tid); + SortedTotals.reserve(AllCountAndTotalPerName.size()); + for (const auto &Total : AllCountAndTotalPerName) + SortedTotals.emplace_back(std::string(Total.getKey()), Total.getValue()); + + llvm::sort(SortedTotals, [](const NameAndCountAndDurationType &A, + const NameAndCountAndDurationType &B) { + return A.second.second > B.second.second; + }); + + // Report totals on separate threads of tracing file. + uint64_t TotalTid = MaxTid + 1; + for (const NameAndCountAndDurationType &Total : SortedTotals) { + auto DurUs = duration_cast<microseconds>(Total.second.second).count(); + auto Count = AllCountAndTotalPerName[Total.first].first; + + J.object([&] { + J.attribute("pid", Pid); + J.attribute("tid", int64_t(TotalTid)); J.attribute("ph", "X"); J.attribute("ts", 0); J.attribute("dur", DurUs); - J.attribute("name", "Total " + E.first); + J.attribute("name", "Total " + Total.first); J.attributeObject("args", [&] { J.attribute("count", int64_t(Count)); J.attribute("avg ms", int64_t(DurUs / Count / 1000)); }); }); - ++Tid; + ++TotalTid; } - // Emit metadata event with process name. - J.object([&] { - J.attribute("cat", ""); - J.attribute("pid", 1); - J.attribute("tid", 0); - J.attribute("ts", 0); - J.attribute("ph", "M"); - J.attribute("name", "process_name"); - J.attributeObject("args", [&] { J.attribute("name", ProcName); }); - }); + auto writeMetadataEvent = [&](const char *Name, uint64_t Tid, + StringRef arg) { + J.object([&] { + J.attribute("cat", ""); + J.attribute("pid", Pid); + J.attribute("tid", int64_t(Tid)); + J.attribute("ts", 0); + J.attribute("ph", "M"); + J.attribute("name", Name); + J.attributeObject("args", [&] { J.attribute("name", arg); }); + }); + }; + + writeMetadataEvent("process_name", Tid, ProcName); + writeMetadataEvent("thread_name", Tid, ThreadName); + for (const TimeTraceProfiler *TTP : ThreadTimeTraceProfilerInstances) + writeMetadataEvent("thread_name", TTP->Tid, TTP->ThreadName); J.arrayEnd(); J.attributeEnd(); + + // Emit the absolute time when this TimeProfiler started. + // This can be used to combine the profiling data from + // multiple processes and preserve actual time intervals. + J.attribute("beginningOfTime", + time_point_cast<microseconds>(BeginningOfTime) + .time_since_epoch() + .count()); + J.objectEnd(); } SmallVector<Entry, 16> Stack; SmallVector<Entry, 128> Entries; StringMap<CountAndDurationType> CountAndTotalPerName; + const time_point<system_clock> BeginningOfTime; const TimePointType StartTime; const std::string ProcName; + const sys::Process::Pid Pid; + SmallString<0> ThreadName; + const uint64_t Tid; // Minimum time granularity (in microseconds) const unsigned TimeTraceGranularity; }; -void timeTraceProfilerInitialize(unsigned TimeTraceGranularity, - StringRef ProcName) { +void llvm::timeTraceProfilerInitialize(unsigned TimeTraceGranularity, + StringRef ProcName) { assert(TimeTraceProfilerInstance == nullptr && "Profiler should not be initialized"); TimeTraceProfilerInstance = new TimeTraceProfiler( TimeTraceGranularity, llvm::sys::path::filename(ProcName)); } -void timeTraceProfilerCleanup() { +// Removes all TimeTraceProfilerInstances. +// Called from main thread. +void llvm::timeTraceProfilerCleanup() { delete TimeTraceProfilerInstance; + std::lock_guard<std::mutex> Lock(Mu); + for (auto TTP : ThreadTimeTraceProfilerInstances) + delete TTP; + ThreadTimeTraceProfilerInstances.clear(); +} + +// Finish TimeTraceProfilerInstance on a worker thread. +// This doesn't remove the instance, just moves the pointer to global vector. +void llvm::timeTraceProfilerFinishThread() { + std::lock_guard<std::mutex> Lock(Mu); + ThreadTimeTraceProfilerInstances.push_back(TimeTraceProfilerInstance); TimeTraceProfilerInstance = nullptr; } -void timeTraceProfilerWrite(raw_pwrite_stream &OS) { +void llvm::timeTraceProfilerWrite(raw_pwrite_stream &OS) { assert(TimeTraceProfilerInstance != nullptr && "Profiler object can't be null"); - TimeTraceProfilerInstance->Write(OS); + TimeTraceProfilerInstance->write(OS); } -void timeTraceProfilerBegin(StringRef Name, StringRef Detail) { +Error llvm::timeTraceProfilerWrite(StringRef PreferredFileName, + StringRef FallbackFileName) { + assert(TimeTraceProfilerInstance != nullptr && + "Profiler object can't be null"); + + std::string Path = PreferredFileName.str(); + if (Path.empty()) { + Path = FallbackFileName == "-" ? "out" : FallbackFileName.str(); + Path += ".time-trace"; + } + + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::OF_Text); + if (EC) + return createStringError(EC, "Could not open " + Path); + + timeTraceProfilerWrite(OS); + return Error::success(); +} + +void llvm::timeTraceProfilerBegin(StringRef Name, StringRef Detail) { if (TimeTraceProfilerInstance != nullptr) - TimeTraceProfilerInstance->begin(Name, [&]() { return Detail; }); + TimeTraceProfilerInstance->begin(std::string(Name), + [&]() { return std::string(Detail); }); } -void timeTraceProfilerBegin(StringRef Name, - llvm::function_ref<std::string()> Detail) { +void llvm::timeTraceProfilerBegin(StringRef Name, + llvm::function_ref<std::string()> Detail) { if (TimeTraceProfilerInstance != nullptr) - TimeTraceProfilerInstance->begin(Name, Detail); + TimeTraceProfilerInstance->begin(std::string(Name), Detail); } -void timeTraceProfilerEnd() { +void llvm::timeTraceProfilerEnd() { if (TimeTraceProfilerInstance != nullptr) TimeTraceProfilerInstance->end(); } - -} // namespace llvm diff --git a/llvm/lib/Support/Timer.cpp b/llvm/lib/Support/Timer.cpp index 613d2eaae6d35..c97538cb560a3 100644 --- a/llvm/lib/Support/Timer.cpp +++ b/llvm/lib/Support/Timer.cpp @@ -247,7 +247,8 @@ TimerGroup::TimerGroup(StringRef Name, StringRef Description, : TimerGroup(Name, Description) { TimersToPrint.reserve(Records.size()); for (const auto &P : Records) - TimersToPrint.emplace_back(P.getValue(), P.getKey(), P.getKey()); + TimersToPrint.emplace_back(P.getValue(), std::string(P.getKey()), + std::string(P.getKey())); assert(TimersToPrint.size() == Records.size() && "Size mismatch"); } @@ -441,3 +442,7 @@ const char *TimerGroup::printAllJSONValues(raw_ostream &OS, const char *delim) { void TimerGroup::ConstructTimerLists() { (void)*NamedGroupedTimers; } + +std::unique_ptr<TimerGroup> TimerGroup::aquireDefaultGroup() { + return std::unique_ptr<TimerGroup>(DefaultTimerGroup.claim()); +} diff --git a/llvm/lib/Support/ToolOutputFile.cpp b/llvm/lib/Support/ToolOutputFile.cpp index ed3a247f01155..c2ca97a59c620 100644 --- a/llvm/lib/Support/ToolOutputFile.cpp +++ b/llvm/lib/Support/ToolOutputFile.cpp @@ -15,31 +15,45 @@ #include "llvm/Support/Signals.h" using namespace llvm; +static bool isStdout(StringRef Filename) { return Filename == "-"; } + ToolOutputFile::CleanupInstaller::CleanupInstaller(StringRef Filename) - : Filename(Filename), Keep(false) { + : Filename(std::string(Filename)), Keep(false) { // Arrange for the file to be deleted if the process is killed. - if (Filename != "-") + if (!isStdout(Filename)) sys::RemoveFileOnSignal(Filename); } ToolOutputFile::CleanupInstaller::~CleanupInstaller() { + if (isStdout(Filename)) + return; + // Delete the file if the client hasn't told us not to. - if (!Keep && Filename != "-") + if (!Keep) sys::fs::remove(Filename); // Ok, the file is successfully written and closed, or deleted. There's no // further need to clean it up on signals. - if (Filename != "-") - sys::DontRemoveFileOnSignal(Filename); + sys::DontRemoveFileOnSignal(Filename); } ToolOutputFile::ToolOutputFile(StringRef Filename, std::error_code &EC, sys::fs::OpenFlags Flags) - : Installer(Filename), OS(Filename, EC, Flags) { + : Installer(Filename) { + if (isStdout(Filename)) { + OS = &outs(); + EC = std::error_code(); + return; + } + OSHolder.emplace(Filename, EC, Flags); + OS = OSHolder.getPointer(); // If open fails, no cleanup is needed. if (EC) Installer.Keep = true; } ToolOutputFile::ToolOutputFile(StringRef Filename, int FD) - : Installer(Filename), OS(FD, true) {} + : Installer(Filename) { + OSHolder.emplace(FD, true); + OS = OSHolder.getPointer(); +} diff --git a/llvm/lib/Support/TrigramIndex.cpp b/llvm/lib/Support/TrigramIndex.cpp index 94810b56db8ec..88375e6e78639 100644 --- a/llvm/lib/Support/TrigramIndex.cpp +++ b/llvm/lib/Support/TrigramIndex.cpp @@ -16,6 +16,7 @@ #include "llvm/Support/TrigramIndex.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" #include <set> #include <string> diff --git a/llvm/lib/Support/Triple.cpp b/llvm/lib/Support/Triple.cpp index 2c480c1094a5c..fec1985ccacae 100644 --- a/llvm/lib/Support/Triple.cpp +++ b/llvm/lib/Support/Triple.cpp @@ -9,10 +9,14 @@ #include "llvm/ADT/Triple.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Host.h" +#include "llvm/Support/SwapByteOrder.h" #include "llvm/Support/TargetParser.h" +#include "llvm/Support/VersionTuple.h" +#include <cassert> #include <cstring> using namespace llvm; @@ -625,6 +629,8 @@ static Triple::SubArchType parseSubArch(StringRef SubArchName) { return Triple::ARMSubArch_v8_4a; case ARM::ArchKind::ARMV8_5A: return Triple::ARMSubArch_v8_5a; + case ARM::ArchKind::ARMV8_6A: + return Triple::ARMSubArch_v8_6a; case ARM::ArchKind::ARMV8R: return Triple::ARMSubArch_v8r; case ARM::ArchKind::ARMV8MBaseline: @@ -710,9 +716,7 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { case Triple::ppc64: case Triple::ppc: - if (T.isOSDarwin()) - return Triple::MachO; - else if (T.isOSAIX()) + if (T.isOSAIX()) return Triple::XCOFF; return Triple::ELF; @@ -983,12 +987,7 @@ std::string Triple::normalize(StringRef Str) { } // Stick the corrected components back together to form the normalized string. - std::string Normalized; - for (unsigned i = 0, e = Components.size(); i != e; ++i) { - if (i) Normalized += '-'; - Normalized += Components[i]; - } - return Normalized; + return join(Components, "-"); } StringRef Triple::getArchName() const { @@ -1088,17 +1087,23 @@ bool Triple::getMacOSXVersion(unsigned &Major, unsigned &Minor, // Darwin version numbers are skewed from OS X versions. if (Major < 4) return false; - Micro = 0; - Minor = Major - 4; - Major = 10; + if (Major <= 19) { + Micro = 0; + Minor = Major - 4; + Major = 10; + } else { + Micro = 0; + Minor = 0; + // darwin20+ corresponds to macOS 11+. + Major = 11 + Major - 20; + } break; case MacOSX: // Default to 10.4. if (Major == 0) { Major = 10; Minor = 4; - } - if (Major != 10) + } else if (Major < 10) return false; break; case IOS: @@ -1602,6 +1607,52 @@ std::string Triple::merge(const Triple &Other) const { return Other.str(); } +bool Triple::isMacOSXVersionLT(unsigned Major, unsigned Minor, + unsigned Micro) const { + assert(isMacOSX() && "Not an OS X triple!"); + + // If this is OS X, expect a sane version number. + if (getOS() == Triple::MacOSX) + return isOSVersionLT(Major, Minor, Micro); + + // Otherwise, compare to the "Darwin" number. + if (Major == 10) { + return isOSVersionLT(Minor + 4, Micro, 0); + } else { + assert(Major >= 11 && "Unexpected major version"); + return isOSVersionLT(Major - 11 + 20, Minor, Micro); + } +} + +VersionTuple Triple::getMinimumSupportedOSVersion() const { + if (getVendor() != Triple::Apple || getArch() != Triple::aarch64) + return VersionTuple(); + switch (getOS()) { + case Triple::MacOSX: + // ARM64 slice is supported starting from macOS 11.0+. + return VersionTuple(11, 0, 0); + case Triple::IOS: + // ARM64 slice is supported starting from Mac Catalyst 14 (macOS 11). + // ARM64 simulators are supported for iOS 14+. + if (isMacCatalystEnvironment() || isSimulatorEnvironment()) + return VersionTuple(14, 0, 0); + break; + case Triple::TvOS: + // ARM64 simulators are supported for tvOS 14+. + if (isSimulatorEnvironment()) + return VersionTuple(14, 0, 0); + break; + case Triple::WatchOS: + // ARM64 simulators are supported for watchOS 7+. + if (isSimulatorEnvironment()) + return VersionTuple(7, 0, 0); + break; + default: + break; + } + return VersionTuple(); +} + StringRef Triple::getARMCPUForArch(StringRef MArch) const { if (MArch.empty()) MArch = getArchName(); @@ -1664,3 +1715,16 @@ StringRef Triple::getARMCPUForArch(StringRef MArch) const { llvm_unreachable("invalid arch name"); } + +VersionTuple Triple::getCanonicalVersionForOS(OSType OSKind, + const VersionTuple &Version) { + switch (OSKind) { + case MacOSX: + // macOS 10.16 is canonicalized to macOS 11. + if (Version == VersionTuple(10, 16)) + return VersionTuple(11, 0); + LLVM_FALLTHROUGH; + default: + return Version; + } +} diff --git a/llvm/lib/Support/Unix/Host.inc b/llvm/lib/Support/Unix/Host.inc index 17d78dc18be75..dfcfdd0dee68c 100644 --- a/llvm/lib/Support/Unix/Host.inc +++ b/llvm/lib/Support/Unix/Host.inc @@ -56,7 +56,7 @@ static std::string updateTripleOSVersion(std::string TargetTripleString) { if (TT.getOS() == Triple::AIX && !TT.getOSMajorVersion()) { struct utsname name; if (uname(&name) != -1) { - std::string NewOSName = Triple::getOSTypeName(Triple::AIX); + std::string NewOSName = std::string(Triple::getOSTypeName(Triple::AIX)); NewOSName += name.version; NewOSName += '.'; NewOSName += name.release; diff --git a/llvm/lib/Support/Unix/Memory.inc b/llvm/lib/Support/Unix/Memory.inc index 79b1759359e1f..be88e7db14002 100644 --- a/llvm/lib/Support/Unix/Memory.inc +++ b/llvm/lib/Support/Unix/Memory.inc @@ -12,6 +12,7 @@ #include "Unix.h" #include "llvm/Config/config.h" +#include "llvm/Support/Alignment.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Process.h" diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc index 2a03dc682bced..d91b269cc6d33 100644 --- a/llvm/lib/Support/Unix/Path.inc +++ b/llvm/lib/Support/Unix/Path.inc @@ -48,6 +48,8 @@ extern char **environ; #endif #elif defined(__DragonFly__) #include <sys/mount.h> +#elif defined(__MVS__) +#include <sys/ps.h> #endif // Both stdio.h and cstdio are included via different paths and @@ -56,10 +58,13 @@ extern char **environ; #undef ferror #undef feof +#if !defined(PATH_MAX) // For GNU Hurd -#if defined(__GNU__) && !defined(PATH_MAX) -# define PATH_MAX 4096 -# define MAXPATHLEN 4096 +#if defined(__GNU__) +#define PATH_MAX 4096 +#elif defined(__MVS__) +#define PATH_MAX _XOPEN_PATH_MAX +#endif #endif #include <sys/types.h> @@ -101,7 +106,8 @@ typedef uint_t uint; #define STATVFS_F_FRSIZE(vfs) static_cast<uint64_t>(vfs.f_bsize) #endif -#if defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__) +#if defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__) || \ + defined(__MVS__) #define STATVFS_F_FLAG(vfs) (vfs).f_flag #else #define STATVFS_F_FLAG(vfs) (vfs).f_flags @@ -184,10 +190,10 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { // On OS X the executable path is saved to the stack by dyld. Reading it // from there is much faster than calling dladdr, especially for large // binaries with symbols. - char exe_path[MAXPATHLEN]; + char exe_path[PATH_MAX]; uint32_t size = sizeof(exe_path); if (_NSGetExecutablePath(exe_path, &size) == 0) { - char link_path[MAXPATHLEN]; + char link_path[PATH_MAX]; if (realpath(exe_path, link_path)) return link_path; } @@ -208,14 +214,9 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { while (*p++ != 0) ; // Iterate through auxiliary vectors for AT_EXECPATH. - for (;;) { - switch (*(uintptr_t *)p++) { - case AT_EXECPATH: + for (; *(uintptr_t *)p != AT_NULL; p++) { + if (*(uintptr_t *)p++ == AT_EXECPATH) return *p; - case AT_NULL: - break; - } - p++; } #endif // Fall back to argv[0] if auxiliary vectors are not available. @@ -239,7 +240,7 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { if (getprogpath(exe_path, argv0) != NULL) return exe_path; #elif defined(__linux__) || defined(__CYGWIN__) || defined(__gnu_hurd__) - char exe_path[MAXPATHLEN]; + char exe_path[PATH_MAX]; const char *aPath = "/proc/self/exe"; if (sys::fs::exists(aPath)) { // /proc is not always mounted under Linux (chroot for example). @@ -263,7 +264,7 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { return ret; } #else - char real_path[MAXPATHLEN]; + char real_path[PATH_MAX]; if (realpath(exe_path, real_path)) return std::string(real_path); #endif @@ -271,6 +272,26 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { // Fall back to the classical detection. if (getprogpath(exe_path, argv0)) return exe_path; +#elif defined(__MVS__) + int token = 0; + W_PSPROC buf; + char exe_path[PS_PATHBLEN]; + pid_t pid = getpid(); + + memset(&buf, 0, sizeof(buf)); + buf.ps_pathptr = exe_path; + buf.ps_pathlen = sizeof(exe_path); + + while (true) { + if ((token = w_getpsent(token, &buf, sizeof(buf))) <= 0) + break; + if (buf.ps_pid != pid) + continue; + char real_path[PATH_MAX]; + if (realpath(exe_path, real_path)) + return std::string(real_path); + break; // Found entry, but realpath failed. + } #elif defined(HAVE_DLFCN_H) && defined(HAVE_DLADDR) // Use dladdr to get executable path if available. Dl_info DLInfo; @@ -280,7 +301,7 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { // If the filename is a symlink, we need to resolve and return the location of // the actual executable. - char link_path[MAXPATHLEN]; + char link_path[PATH_MAX]; if (realpath(DLInfo.dli_fname, link_path)) return link_path; #else @@ -330,12 +351,7 @@ std::error_code current_path(SmallVectorImpl<char> &result) { return std::error_code(); } -#ifdef MAXPATHLEN - result.reserve(MAXPATHLEN); -#else -// For GNU Hurd - result.reserve(1024); -#endif + result.reserve(PATH_MAX); while (true) { if (::getcwd(result.data(), result.capacity()) == nullptr) { @@ -504,6 +520,10 @@ static bool is_local_impl(struct STATVFS &Vfs) { // vmount entry not found; "remote" is the conservative answer. return false; +#elif defined(__MVS__) + // The file system can have an arbitrary structure on z/OS; must go with the + // conservative answer. + return false; #else return !!(STATVFS_F_FLAG(Vfs) & MNT_LOCAL); #endif @@ -998,7 +1018,7 @@ std::error_code openFileForRead(const Twine &Name, int &ResultFD, #if defined(F_GETPATH) // When F_GETPATH is availble, it is the quickest way to get // the real path name. - char Buffer[MAXPATHLEN]; + char Buffer[PATH_MAX]; if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1) RealPath->append(Buffer, Buffer + strlen(Buffer)); #else @@ -1169,6 +1189,51 @@ static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) { return false; } +bool user_config_directory(SmallVectorImpl<char> &result) { +#ifdef __APPLE__ + // Mac: ~/Library/Preferences/ + if (home_directory(result)) { + append(result, "Library", "Preferences"); + return true; + } +#else + // XDG_CONFIG_HOME as defined in the XDG Base Directory Specification: + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + if (const char *RequestedDir = getenv("XDG_CONFIG_HOME")) { + result.clear(); + result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + return true; + } +#endif + // Fallback: ~/.config + if (!home_directory(result)) { + return false; + } + append(result, ".config"); + return true; +} + +bool cache_directory(SmallVectorImpl<char> &result) { +#ifdef __APPLE__ + if (getDarwinConfDir(false/*tempDir*/, result)) { + return true; + } +#else + // XDG_CACHE_HOME as defined in the XDG Base Directory Specification: + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + if (const char *RequestedDir = getenv("XDG_CACHE_HOME")) { + result.clear(); + result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); + return true; + } +#endif + if (!home_directory(result)) { + return false; + } + append(result, ".cache"); + return true; +} + static const char *getEnvTempDir() { // Check whether the temporary directory is specified by an environment // variable. diff --git a/llvm/lib/Support/Unix/Process.inc b/llvm/lib/Support/Unix/Process.inc index dfe81d7e28337..24f16b51af7be 100644 --- a/llvm/lib/Support/Unix/Process.inc +++ b/llvm/lib/Support/Unix/Process.inc @@ -66,6 +66,12 @@ static std::pair<std::chrono::microseconds, std::chrono::microseconds> getRUsage #endif } +Process::Pid Process::getProcessId() { + static_assert(sizeof(Pid) >= sizeof(pid_t), + "Process::Pid should be big enough to store pid_t"); + return Pid(::getpid()); +} + // On Cygwin, getpagesize() returns 64k(AllocationGranularity) and // offset in mmap(3) should be aligned to the AllocationGranularity. Expected<unsigned> Process::getPageSize() { @@ -280,7 +286,7 @@ bool Process::FileDescriptorIsDisplayed(int fd) { #endif } -static unsigned getColumns(int FileID) { +static unsigned getColumns() { // If COLUMNS is defined in the environment, wrap to that many columns. if (const char *ColumnsStr = std::getenv("COLUMNS")) { int Columns = std::atoi(ColumnsStr); @@ -288,31 +294,23 @@ static unsigned getColumns(int FileID) { return Columns; } - unsigned Columns = 0; - -#if defined(HAVE_SYS_IOCTL_H) && defined(HAVE_TERMIOS_H) \ - && !(defined(_XOPEN_SOURCE) || defined(_POSIX_C_SOURCE)) - // Try to determine the width of the terminal. - struct winsize ws; - if (ioctl(FileID, TIOCGWINSZ, &ws) == 0) - Columns = ws.ws_col; -#endif - - return Columns; + // We used to call ioctl TIOCGWINSZ to determine the width. It is considered + // unuseful. + return 0; } unsigned Process::StandardOutColumns() { if (!StandardOutIsDisplayed()) return 0; - return getColumns(1); + return getColumns(); } unsigned Process::StandardErrColumns() { if (!StandardErrIsDisplayed()) return 0; - return getColumns(2); + return getColumns(); } #ifdef HAVE_TERMINFO diff --git a/llvm/lib/Support/Unix/Program.inc b/llvm/lib/Support/Unix/Program.inc index 520685a0e9878..8f41fc0151635 100644 --- a/llvm/lib/Support/Unix/Program.inc +++ b/llvm/lib/Support/Unix/Program.inc @@ -15,6 +15,8 @@ //=== is guaranteed to work on *all* UNIX variants. //===----------------------------------------------------------------------===// +#include "llvm/Support/Program.h" + #include "Unix.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/config.h" @@ -59,8 +61,7 @@ #endif #endif -namespace llvm { - +using namespace llvm; using namespace sys; ProcessInfo::ProcessInfo() : Pid(0), ReturnCode(0) {} @@ -70,8 +71,7 @@ ErrorOr<std::string> sys::findProgramByName(StringRef Name, assert(!Name.empty() && "Must have a name!"); // Use the given path verbatim if it contains any slashes; this matches // the behavior of sh(1) and friends. - if (Name.find('/') != StringRef::npos) - return std::string(Name); + if (Name.find('/') != StringRef::npos) return std::string(Name); SmallVector<StringRef, 16> EnvironmentPaths; if (Paths.empty()) @@ -88,7 +88,7 @@ ErrorOr<std::string> sys::findProgramByName(StringRef Name, SmallString<128> FilePath(Path); sys::path::append(FilePath, Name); if (sys::fs::can_execute(FilePath.c_str())) - return std::string(FilePath.str()); // Found the executable! + return std::string(FilePath.str()); // Found the executable! } return errc::no_such_file_or_directory; } @@ -101,7 +101,7 @@ static bool RedirectIO(Optional<StringRef> Path, int FD, std::string* ErrMsg) { // Redirect empty paths to /dev/null File = "/dev/null"; else - File = *Path; + File = std::string(*Path); // Open the file int InFD = open(File.c_str(), FD == 0 ? O_RDONLY : O_WRONLY|O_CREAT, 0666); @@ -162,8 +162,6 @@ static void SetMemoryLimits(unsigned size) { #endif } -} - static std::vector<const char *> toNullTerminatedCStringArray(ArrayRef<StringRef> Strings, StringSaver &Saver) { std::vector<const char *> Result; @@ -213,7 +211,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, std::string *RedirectsStr[3] = {nullptr, nullptr, nullptr}; for (int I = 0; I < 3; ++I) { if (Redirects[I]) { - RedirectsStorage[I] = *Redirects[I]; + RedirectsStorage[I] = std::string(*Redirects[I]); RedirectsStr[I] = &RedirectsStorage[I]; } } @@ -304,7 +302,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, } // Execute! - std::string PathStr = Program; + std::string PathStr = std::string(Program); if (Envp != nullptr) execve(PathStr.c_str(), const_cast<char **>(Argv), const_cast<char **>(Envp)); @@ -331,9 +329,54 @@ static bool Execute(ProcessInfo &PI, StringRef Program, } namespace llvm { +namespace sys { + +#ifndef _AIX +using ::wait4; +#else +static pid_t (wait4)(pid_t pid, int *status, int options, struct rusage *usage); +#endif -ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, - bool WaitUntilTerminates, std::string *ErrMsg) { +} // namespace sys +} // namespace llvm + +#ifdef _AIX +#ifndef _ALL_SOURCE +extern "C" pid_t (wait4)(pid_t pid, int *status, int options, + struct rusage *usage); +#endif +pid_t (llvm::sys::wait4)(pid_t pid, int *status, int options, + struct rusage *usage) { + assert(pid > 0 && "Only expecting to handle actual PID values!"); + assert((options & ~WNOHANG) == 0 && "Expecting WNOHANG at most!"); + assert(usage && "Expecting usage collection!"); + + // AIX wait4 does not work well with WNOHANG. + if (!(options & WNOHANG)) + return ::wait4(pid, status, options, usage); + + // For WNOHANG, we use waitid (which supports WNOWAIT) until the child process + // has terminated. + siginfo_t WaitIdInfo; + WaitIdInfo.si_pid = 0; + int WaitIdRetVal = + waitid(P_PID, pid, &WaitIdInfo, WNOWAIT | WEXITED | options); + + if (WaitIdRetVal == -1 || WaitIdInfo.si_pid == 0) + return WaitIdRetVal; + + assert(WaitIdInfo.si_pid == pid); + + // The child has already terminated, so a blocking wait on it is okay in the + // absence of indiscriminate `wait` calls from the current process (which + // would cause the call here to fail with ECHILD). + return ::wait4(pid, status, options & ~WNOHANG, usage); +} +#endif + +ProcessInfo llvm::sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, + bool WaitUntilTerminates, std::string *ErrMsg, + Optional<ProcessStatistics> *ProcStat) { struct sigaction Act, Old; assert(PI.Pid && "invalid pid to wait on, process not started?"); @@ -349,6 +392,7 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, Act.sa_handler = TimeOutHandler; sigemptyset(&Act.sa_mask); sigaction(SIGALRM, &Act, &Old); + // FIXME The alarm signal may be delivered to another thread. alarm(SecondsToWait); } else if (SecondsToWait == 0) WaitPidOptions = WNOHANG; @@ -356,9 +400,12 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, // Parent process: Wait for the child process to terminate. int status; ProcessInfo WaitResult; + rusage Info; + if (ProcStat) + ProcStat->reset(); do { - WaitResult.Pid = waitpid(ChildPid, &status, WaitPidOptions); + WaitResult.Pid = sys::wait4(ChildPid, &status, WaitPidOptions, &Info); } while (WaitUntilTerminates && WaitResult.Pid == -1 && errno == EINTR); if (WaitResult.Pid != PI.Pid) { @@ -375,6 +422,8 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, sigaction(SIGALRM, &Old, nullptr); // Wait for child to die + // FIXME This could grab some other child process out from another + // waiting thread and then leave a zombie anyway. if (wait(&status) != ChildPid) MakeErrMsg(ErrMsg, "Child timed out but wouldn't die"); else @@ -396,6 +445,13 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, sigaction(SIGALRM, &Old, nullptr); } + if (ProcStat) { + std::chrono::microseconds UserT = toDuration(Info.ru_utime); + std::chrono::microseconds KernelT = toDuration(Info.ru_stime); + uint64_t PeakMemory = static_cast<uint64_t>(Info.ru_maxrss); + *ProcStat = ProcessStatistics{UserT + KernelT, UserT, PeakMemory}; + } + // Return the proper exit status. Detect error conditions // so we can return -1 for them and set ErrMsg informatively. int result = 0; @@ -430,12 +486,12 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, return WaitResult; } -std::error_code sys::ChangeStdinToBinary() { +std::error_code llvm::sys::ChangeStdinToBinary() { // Do nothing, as Unix doesn't differentiate between text and binary. return std::error_code(); } -std::error_code sys::ChangeStdoutToBinary() { +std::error_code llvm::sys::ChangeStdoutToBinary() { // Do nothing, as Unix doesn't differentiate between text and binary. return std::error_code(); } @@ -497,4 +553,3 @@ bool llvm::sys::commandLineFitsWithinSystemLimits(StringRef Program, return true; } -} diff --git a/llvm/lib/Support/Unix/Threading.inc b/llvm/lib/Support/Unix/Threading.inc index afb887fc10960..2d0aacabf0920 100644 --- a/llvm/lib/Support/Unix/Threading.inc +++ b/llvm/lib/Support/Unix/Threading.inc @@ -37,7 +37,12 @@ #include <lwp.h> // For _lwp_self() #endif +#if defined(__OpenBSD__) +#include <unistd.h> // For getthrid() +#endif + #if defined(__linux__) +#include <sched.h> // For sched_getaffinity #include <sys/syscall.h> // For syscall codes #include <unistd.h> // For syscall() #endif @@ -89,6 +94,10 @@ llvm_execute_on_thread_impl(void *(*ThreadFunc)(void *), void *Arg, if ((errnum = ::pthread_join(Thread, nullptr)) != 0) { ReportErrnumFatal("pthread_join failed", errnum); } + } else if (JP == JoiningPolicy::Detach) { + if ((errnum = ::pthread_detach(Thread)) != 0) { + ReportErrnumFatal("pthread_detach failed", errnum); + } } } @@ -104,6 +113,8 @@ uint64_t llvm::get_threadid() { return uint64_t(pthread_getthreadid_np()); #elif defined(__NetBSD__) return uint64_t(_lwp_self()); +#elif defined(__OpenBSD__) + return uint64_t(getthrid()); #elif defined(__ANDROID__) return uint64_t(gettid()); #elif defined(__linux__) @@ -267,3 +278,27 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { #endif return SetThreadPriorityResult::FAILURE; } + +#include <thread> + +int computeHostNumHardwareThreads() { +#ifdef __linux__ + cpu_set_t Set; + if (sched_getaffinity(0, sizeof(Set), &Set) == 0) + return CPU_COUNT(&Set); +#endif + // Guard against std::thread::hardware_concurrency() returning 0. + if (unsigned Val = std::thread::hardware_concurrency()) + return Val; + return 1; +} + +void llvm::ThreadPoolStrategy::apply_thread_strategy( + unsigned ThreadPoolNum) const {} + +llvm::BitVector llvm::get_thread_affinity_mask() { + // FIXME: Implement + llvm_unreachable("Not implemented!"); +} + +unsigned llvm::get_cpus() { return 1; } diff --git a/llvm/lib/Support/Unix/Unix.h b/llvm/lib/Support/Unix/Unix.h index 1fc9a414f7494..60929139598b9 100644 --- a/llvm/lib/Support/Unix/Unix.h +++ b/llvm/lib/Support/Unix/Unix.h @@ -36,10 +36,6 @@ #include <unistd.h> #endif -#ifdef HAVE_SYS_PARAM_H -#include <sys/param.h> -#endif - #ifdef HAVE_SYS_TIME_H # include <sys/time.h> #endif diff --git a/llvm/lib/Support/VersionTuple.cpp b/llvm/lib/Support/VersionTuple.cpp index 60b59424fbb49..6a516481ac25c 100644 --- a/llvm/lib/Support/VersionTuple.cpp +++ b/llvm/lib/Support/VersionTuple.cpp @@ -10,8 +10,11 @@ // the form major[.minor[.subminor]]. // //===----------------------------------------------------------------------===// + #include "llvm/Support/VersionTuple.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" +#include <cassert> using namespace llvm; diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp index edd4234fe5016..5b757c9ea80db 100644 --- a/llvm/lib/Support/VirtualFileSystem.cpp +++ b/llvm/lib/Support/VirtualFileSystem.cpp @@ -306,12 +306,12 @@ RealFileSystem::openFileForRead(const Twine &Name) { llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const { if (WD) - return WD->Specified.str(); + return std::string(WD->Specified.str()); SmallString<128> Dir; if (std::error_code EC = llvm::sys::fs::current_path(Dir)) return EC; - return Dir.str(); + return std::string(Dir.str()); } std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) { @@ -535,7 +535,8 @@ class InMemoryNode { public: InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind) - : Kind(Kind), FileName(llvm::sys::path::filename(FileName)) {} + : Kind(Kind), FileName(std::string(llvm::sys::path::filename(FileName))) { + } virtual ~InMemoryNode() = default; /// Get the filename of this node (the name without the directory part). @@ -904,7 +905,7 @@ class InMemoryDirIterator : public llvm::vfs::detail::DirIterImpl { Type = sys::fs::file_type::directory_file; break; } - CurrentEntry = directory_entry(Path.str(), Type); + CurrentEntry = directory_entry(std::string(Path.str()), Type); } else { // When we're at the end, make CurrentEntry invalid and DirIterImpl will // do the rest. @@ -960,7 +961,7 @@ std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) { llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); if (!Path.empty()) - WorkingDirectory = Path.str(); + WorkingDirectory = std::string(Path.str()); return {}; } @@ -989,6 +990,28 @@ std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) { // RedirectingFileSystem implementation //===-----------------------------------------------------------------------===/ +namespace { + +/// Removes leading "./" as well as path components like ".." and ".". +static llvm::SmallString<256> canonicalize(llvm::StringRef Path) { + // First detect the path style in use by checking the first separator. + llvm::sys::path::Style style = llvm::sys::path::Style::native; + const size_t n = Path.find_first_of("/\\"); + if (n != static_cast<size_t>(-1)) + style = (Path[n] == '/') ? llvm::sys::path::Style::posix + : llvm::sys::path::Style::windows; + + // Now remove the dots. Explicitly specifying the path style prevents the + // direction of the slashes from changing. + llvm::SmallString<256> result = + llvm::sys::path::remove_leading_dotslash(Path, style); + llvm::sys::path::remove_dots(result, /*remove_dot_dot=*/true, style); + return result; +} + +} // anonymous namespace + + RedirectingFileSystem::RedirectingFileSystem(IntrusiveRefCntPtr<FileSystem> FS) : ExternalFS(std::move(FS)) { if (ExternalFS) @@ -1064,7 +1087,7 @@ RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) { Path.toVector(AbsolutePath); if (std::error_code EC = makeAbsolute(AbsolutePath)) return EC; - WorkingDirectory = AbsolutePath.str(); + WorkingDirectory = std::string(AbsolutePath.str()); return {}; } @@ -1082,7 +1105,23 @@ std::error_code RedirectingFileSystem::makeAbsolute(SmallVectorImpl<char> &Path) if (!WorkingDir) return WorkingDir.getError(); - llvm::sys::fs::make_absolute(WorkingDir.get(), Path); + // We can't use sys::fs::make_absolute because that assumes the path style + // is native and there is no way to override that. Since we know WorkingDir + // is absolute, we can use it to determine which style we actually have and + // append Path ourselves. + sys::path::Style style = sys::path::Style::windows; + if (sys::path::is_absolute(WorkingDir.get(), sys::path::Style::posix)) { + style = sys::path::Style::posix; + } + + std::string Result = WorkingDir.get(); + StringRef Dir(Result); + if (!Dir.endswith(sys::path::get_separator(style))) { + Result += sys::path::get_separator(style); + } + Result.append(Path.data(), Path.size()); + Path.assign(Result.begin(), Result.end()); + return {}; } @@ -1317,8 +1356,8 @@ class llvm::vfs::RedirectingFileSystemParser { bool HasContents = false; // external or otherwise std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> EntryArrayContents; - std::string ExternalContentsPath; - std::string Name; + SmallString<256> ExternalContentsPath; + SmallString<256> Name; yaml::Node *NameValueNode = nullptr; auto UseExternalName = RedirectingFileSystem::RedirectingFileEntry::NK_NotSet; @@ -1341,16 +1380,9 @@ class llvm::vfs::RedirectingFileSystemParser { return nullptr; NameValueNode = I.getValue(); - if (FS->UseCanonicalizedPaths) { - SmallString<256> Path(Value); - // Guarantee that old YAML files containing paths with ".." and "." - // are properly canonicalized before read into the VFS. - Path = sys::path::remove_leading_dotslash(Path); - sys::path::remove_dots(Path, /*remove_dot_dot=*/true); - Name = Path.str(); - } else { - Name = Value; - } + // Guarantee that old YAML files containing paths with ".." and "." + // are properly canonicalized before read into the VFS. + Name = canonicalize(Value).str(); } else if (Key == "type") { if (!parseScalarString(I.getValue(), Value, Buffer)) return nullptr; @@ -1403,12 +1435,9 @@ class llvm::vfs::RedirectingFileSystemParser { FullPath = Value; } - if (FS->UseCanonicalizedPaths) { - // Guarantee that old YAML files containing paths with ".." and "." - // are properly canonicalized before read into the VFS. - FullPath = sys::path::remove_leading_dotslash(FullPath); - sys::path::remove_dots(FullPath, /*remove_dot_dot=*/true); - } + // Guarantee that old YAML files containing paths with ".." and "." + // are properly canonicalized before read into the VFS. + FullPath = canonicalize(FullPath); ExternalContentsPath = FullPath.str(); } else if (Key == "use-external-name") { bool Val; @@ -1653,14 +1682,10 @@ RedirectingFileSystem::lookupPath(const Twine &Path_) const { if (std::error_code EC = makeAbsolute(Path)) return EC; - // Canonicalize path by removing ".", "..", "./", etc components. This is - // a VFS request, do bot bother about symlinks in the path components + // Canonicalize path by removing ".", "..", "./", components. This is + // a VFS request, do not bother about symlinks in the path components // but canonicalize in order to perform the correct entry search. - if (UseCanonicalizedPaths) { - Path = sys::path::remove_leading_dotslash(Path); - sys::path::remove_dots(Path, /*remove_dot_dot=*/true); - } - + Path = canonicalize(Path); if (Path.empty()) return make_error_code(llvm::errc::invalid_argument); @@ -1679,16 +1704,9 @@ ErrorOr<RedirectingFileSystem::Entry *> RedirectingFileSystem::lookupPath(sys::path::const_iterator Start, sys::path::const_iterator End, RedirectingFileSystem::Entry *From) const { -#ifndef _WIN32 assert(!isTraversalComponent(*Start) && !isTraversalComponent(From->getName()) && "Paths should not contain traversal components"); -#else - // FIXME: this is here to support windows, remove it once canonicalized - // paths become globally default. - if (Start->equals(".")) - ++Start; -#endif StringRef FromName = From->getName(); @@ -1894,11 +1912,21 @@ UniqueID vfs::getNextVirtualUniqueID() { return UniqueID(std::numeric_limits<uint64_t>::max(), ID); } -void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { +void YAMLVFSWriter::addEntry(StringRef VirtualPath, StringRef RealPath, + bool IsDirectory) { assert(sys::path::is_absolute(VirtualPath) && "virtual path not absolute"); assert(sys::path::is_absolute(RealPath) && "real path not absolute"); assert(!pathHasTraversal(VirtualPath) && "path traversal is not supported"); - Mappings.emplace_back(VirtualPath, RealPath); + Mappings.emplace_back(VirtualPath, RealPath, IsDirectory); +} + +void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { + addEntry(VirtualPath, RealPath, /*IsDirectory=*/false); +} + +void YAMLVFSWriter::addDirectoryMapping(StringRef VirtualPath, + StringRef RealPath) { + addEntry(VirtualPath, RealPath, /*IsDirectory=*/true); } namespace { @@ -1999,7 +2027,10 @@ void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries, if (!Entries.empty()) { const YAMLVFSEntry &Entry = Entries.front(); - startDirectory(path::parent_path(Entry.VPath)); + + startDirectory( + Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath) + ); StringRef RPath = Entry.RPath; if (UseOverlayRelative) { @@ -2009,19 +2040,31 @@ void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries, RPath = RPath.slice(OverlayDirLen, RPath.size()); } - writeEntry(path::filename(Entry.VPath), RPath); + bool IsCurrentDirEmpty = true; + if (!Entry.IsDirectory) { + writeEntry(path::filename(Entry.VPath), RPath); + IsCurrentDirEmpty = false; + } for (const auto &Entry : Entries.slice(1)) { - StringRef Dir = path::parent_path(Entry.VPath); - if (Dir == DirStack.back()) - OS << ",\n"; - else { + StringRef Dir = + Entry.IsDirectory ? Entry.VPath : path::parent_path(Entry.VPath); + if (Dir == DirStack.back()) { + if (!IsCurrentDirEmpty) { + OS << ",\n"; + } + } else { + bool IsDirPoppedFromStack = false; while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) { OS << "\n"; endDirectory(); + IsDirPoppedFromStack = true; + } + if (IsDirPoppedFromStack || !IsCurrentDirEmpty) { + OS << ",\n"; } - OS << ",\n"; startDirectory(Dir); + IsCurrentDirEmpty = true; } StringRef RPath = Entry.RPath; if (UseOverlayRelative) { @@ -2030,7 +2073,10 @@ void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries, "Overlay dir must be contained in RPath"); RPath = RPath.slice(OverlayDirLen, RPath.size()); } - writeEntry(path::filename(Entry.VPath), RPath); + if (!Entry.IsDirectory) { + writeEntry(path::filename(Entry.VPath), RPath); + IsCurrentDirEmpty = false; + } } while (!DirStack.empty()) { @@ -2104,7 +2150,7 @@ std::error_code VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime) { Type = sys::fs::file_type::regular_file; break; } - CurrentEntry = directory_entry(PathStr.str(), Type); + CurrentEntry = directory_entry(std::string(PathStr.str()), Type); return {}; } return incrementExternal(); diff --git a/llvm/lib/Support/Windows/DynamicLibrary.inc b/llvm/lib/Support/Windows/DynamicLibrary.inc index 71b206c4cf9ee..a3f78fb0d6ba5 100644 --- a/llvm/lib/Support/Windows/DynamicLibrary.inc +++ b/llvm/lib/Support/Windows/DynamicLibrary.inc @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#include "WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/raw_ostream.h" diff --git a/llvm/lib/Support/Windows/Host.inc b/llvm/lib/Support/Windows/Host.inc index 21b947f26df3e..5583db909045a 100644 --- a/llvm/lib/Support/Windows/Host.inc +++ b/llvm/lib/Support/Windows/Host.inc @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#include "WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #include <cstdio> #include <string> diff --git a/llvm/lib/Support/Windows/Memory.inc b/llvm/lib/Support/Windows/Memory.inc index c5566f9910a56..1b2de1915ec46 100644 --- a/llvm/lib/Support/Windows/Memory.inc +++ b/llvm/lib/Support/Windows/Memory.inc @@ -17,7 +17,7 @@ #include "llvm/Support/WindowsError.h" // The Windows.h header must be the last one included. -#include "WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" static DWORD getWindowsProtectionFlags(unsigned Flags) { switch (Flags & llvm::sys::Memory::MF_RWE_MASK) { diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc index c3b13abef5def..e352beb77616b 100644 --- a/llvm/lib/Support/Windows/Path.inc +++ b/llvm/lib/Support/Windows/Path.inc @@ -25,7 +25,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 "llvm/Support/Windows/WindowsSupport.h" #include <shellapi.h> #include <shlobj.h> @@ -47,7 +47,7 @@ using namespace llvm; using llvm::sys::windows::UTF8ToUTF16; using llvm::sys::windows::CurCPToUTF16; using llvm::sys::windows::UTF16ToUTF8; -using llvm::sys::path::widenPath; +using llvm::sys::windows::widenPath; static bool is_separator(const wchar_t value) { switch (value) { @@ -61,64 +61,64 @@ static bool is_separator(const wchar_t value) { namespace llvm { namespace sys { -namespace path { +namespace windows { -// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the -// path is longer than CreateDirectory can tolerate, make it absolute and -// prefixed by '\\?\'. -std::error_code widenPath(const Twine &Path8, - SmallVectorImpl<wchar_t> &Path16) { - const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename. +// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the path +// is longer than the limit that the Win32 Unicode File API can tolerate, make +// it an absolute normalized path prefixed by '\\?\'. +std::error_code widenPath(const Twine &Path8, SmallVectorImpl<wchar_t> &Path16, + size_t MaxPathLen) { + assert(MaxPathLen <= MAX_PATH); - // Several operations would convert Path8 to SmallString; more efficient to - // do it once up front. - SmallString<128> Path8Str; + // Several operations would convert Path8 to SmallString; more efficient to do + // it once up front. + SmallString<MAX_PATH> Path8Str; Path8.toVector(Path8Str); - // If we made this path absolute, how much longer would it get? + if (std::error_code EC = UTF8ToUTF16(Path8Str, Path16)) + return EC; + + const bool IsAbsolute = llvm::sys::path::is_absolute(Path8); size_t CurPathLen; - if (llvm::sys::path::is_absolute(Twine(Path8Str))) + if (IsAbsolute) CurPathLen = 0; // No contribution from current_path needed. else { - CurPathLen = ::GetCurrentDirectoryW(0, NULL); + CurPathLen = ::GetCurrentDirectoryW( + 0, NULL); // Returns the size including the null terminator. if (CurPathLen == 0) return mapWindowsError(::GetLastError()); } - // Would the absolute path be longer than our limit? - if ((Path8Str.size() + CurPathLen) >= MaxDirLen && - !Path8Str.startswith("\\\\?\\")) { - SmallString<2*MAX_PATH> FullPath("\\\\?\\"); - if (CurPathLen) { - SmallString<80> CurPath; - if (std::error_code EC = llvm::sys::fs::current_path(CurPath)) - return EC; - FullPath.append(CurPath); - } - // Traverse the requested path, canonicalizing . and .. (because the \\?\ - // prefix is documented to treat them as real components). Ignore - // separators, which can be returned from the iterator if the path has a - // drive name. We don't need to call native() on the result since append() - // always attaches preferred_separator. - for (llvm::sys::path::const_iterator I = llvm::sys::path::begin(Path8Str), - E = llvm::sys::path::end(Path8Str); - I != E; ++I) { - if (I->size() == 1 && is_separator((*I)[0])) - continue; - if (I->size() == 1 && *I == ".") - continue; - if (I->size() == 2 && *I == "..") - llvm::sys::path::remove_filename(FullPath); - else - llvm::sys::path::append(FullPath, *I); - } - return UTF8ToUTF16(FullPath, Path16); + const char *const LongPathPrefix = "\\\\?\\"; + + if ((Path16.size() + CurPathLen) < MaxPathLen || + Path8Str.startswith(LongPathPrefix)) + return std::error_code(); + + if (!IsAbsolute) { + if (std::error_code EC = llvm::sys::fs::make_absolute(Path8Str)) + return EC; } - // Just use the caller's original path. - return UTF8ToUTF16(Path8Str, Path16); + // Remove '.' and '..' because long paths treat these as real path components. + llvm::sys::path::native(Path8Str, path::Style::windows); + llvm::sys::path::remove_dots(Path8Str, true); + + const StringRef RootName = llvm::sys::path::root_name(Path8Str); + assert(!RootName.empty() && + "Root name cannot be empty for an absolute path!"); + + SmallString<2 * MAX_PATH> FullPath(LongPathPrefix); + if (RootName[1] != ':') { // Check if UNC. + FullPath.append("UNC\\"); + FullPath.append(Path8Str.begin() + 2, Path8Str.end()); + } else + FullPath.append(Path8Str); + + return UTF8ToUTF16(FullPath, Path16); } -} // end namespace path + +} // end namespace windows namespace fs { @@ -227,7 +227,9 @@ std::error_code create_directory(const Twine &path, bool IgnoreExisting, perms Perms) { SmallVector<wchar_t, 128> path_utf16; - if (std::error_code ec = widenPath(path, path_utf16)) + // CreateDirectoryW has a lower maximum path length as it must leave room for + // an 8.3 filename. + if (std::error_code ec = widenPath(path, path_utf16, MAX_PATH - 12)) return ec; if (!::CreateDirectoryW(path_utf16.begin(), NULL)) { @@ -553,6 +555,11 @@ std::error_code rename(const Twine &From, const Twine &To) { NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (FromHandle) break; + + // We don't want to loop if the file doesn't exist. + auto EC = mapWindowsError(GetLastError()); + if (EC == errc::no_such_file_or_directory) + return EC; } if (!FromHandle) return mapWindowsError(GetLastError()); @@ -950,9 +957,9 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &IT, return EC; // Convert path to the format that Windows is happy with. - if (PathUTF16.size() > 0 && - !is_separator(PathUTF16[Path.size() - 1]) && - PathUTF16[Path.size() - 1] != L':') { + size_t PathUTF16Len = PathUTF16.size(); + if (PathUTF16Len > 0 && !is_separator(PathUTF16[PathUTF16Len - 1]) && + PathUTF16[PathUTF16Len - 1] != L':') { PathUTF16.push_back(L'\\'); PathUTF16.push_back(L'*'); } else { @@ -1365,6 +1372,16 @@ bool home_directory(SmallVectorImpl<char> &result) { return getKnownFolderPath(FOLDERID_Profile, result); } +bool user_config_directory(SmallVectorImpl<char> &result) { + // Either local or roaming appdata may be suitable in some cases, depending + // on the data. Local is more conservative, Roaming may not always be correct. + return getKnownFolderPath(FOLDERID_LocalAppData, result); +} + +bool cache_directory(SmallVectorImpl<char> &result) { + return getKnownFolderPath(FOLDERID_LocalAppData, result); +} + static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl<char> &Res) { SmallVector<wchar_t, 1024> Buf; size_t Size = 1024; diff --git a/llvm/lib/Support/Windows/Process.inc b/llvm/lib/Support/Windows/Process.inc index 3526e3dee6fa1..8064d4e17b295 100644 --- a/llvm/lib/Support/Windows/Process.inc +++ b/llvm/lib/Support/Windows/Process.inc @@ -19,7 +19,7 @@ #include <malloc.h> // The Windows.h header must be after LLVM and standard headers. -#include "WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #include <direct.h> #include <io.h> @@ -43,6 +43,12 @@ using namespace llvm; +Process::Pid Process::getProcessId() { + static_assert(sizeof(Pid) >= sizeof(DWORD), + "Process::Pid should be big enough to store DWORD"); + return Pid(::GetCurrentProcessId()); +} + // This function retrieves the page size using GetNativeSystemInfo() and is // present solely so it can be called once to initialize the self_process member // below. @@ -439,18 +445,38 @@ const char *Process::ResetColor() { return 0; } +static unsigned GetRandomNumberSeed() { + // Generate a random number seed from the millisecond-resolution Windows + // system clock and the current process id. + FILETIME Time; + GetSystemTimeAsFileTime(&Time); + DWORD Pid = GetCurrentProcessId(); + return hash_combine(Time.dwHighDateTime, Time.dwLowDateTime, Pid); +} + +static unsigned GetPseudoRandomNumber() { + // Arrange to call srand once when this function is first used, and + // otherwise (if GetRandomNumber always succeeds in using + // CryptGenRandom) don't bother at all. + static int x = (static_cast<void>(::srand(GetRandomNumberSeed())), 0); + (void)x; + return ::rand(); +} + unsigned Process::GetRandomNumber() { + // Try to use CryptGenRandom. HCRYPTPROV HCPC; - if (!::CryptAcquireContextW(&HCPC, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) - ReportLastErrorFatal("Could not acquire a cryptographic context"); - - ScopedCryptContext CryptoProvider(HCPC); - unsigned Ret; - if (!::CryptGenRandom(CryptoProvider, sizeof(Ret), - reinterpret_cast<BYTE *>(&Ret))) - ReportLastErrorFatal("Could not generate a random number"); - return Ret; + if (::CryptAcquireContextW(&HCPC, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) { + ScopedCryptContext CryptoProvider(HCPC); + unsigned Ret; + if (::CryptGenRandom(CryptoProvider, sizeof(Ret), + reinterpret_cast<BYTE *>(&Ret))) + return Ret; + } + + // If that fails, fall back to pseudo-random numbers. + return GetPseudoRandomNumber(); } typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); diff --git a/llvm/lib/Support/Windows/Program.inc b/llvm/lib/Support/Windows/Program.inc index a1482bf17c604..9fe05d24ec2eb 100644 --- a/llvm/lib/Support/Windows/Program.inc +++ b/llvm/lib/Support/Windows/Program.inc @@ -10,14 +10,15 @@ // //===----------------------------------------------------------------------===// -#include "WindowsSupport.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Windows/WindowsSupport.h" #include "llvm/Support/WindowsError.h" #include "llvm/Support/raw_ostream.h" +#include <psapi.h> #include <cstdio> #include <fcntl.h> #include <io.h> @@ -138,7 +139,7 @@ static HANDLE RedirectIO(Optional<StringRef> Path, int fd, if (Path->empty()) fname = "NUL"; else - fname = *Path; + fname = std::string(*Path); SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); @@ -151,7 +152,7 @@ static HANDLE RedirectIO(Optional<StringRef> Path, int fd, if (windows::UTF8ToUTF16(fname, fnameUnicode)) return INVALID_HANDLE_VALUE; } else { - if (path::widenPath(fname, fnameUnicode)) + if (sys::windows::widenPath(fname, fnameUnicode)) return INVALID_HANDLE_VALUE; } h = CreateFileW(fnameUnicode.data(), fd ? GENERIC_WRITE : GENERIC_READ, @@ -263,7 +264,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, fflush(stderr); SmallVector<wchar_t, MAX_PATH> ProgramUtf16; - if (std::error_code ec = path::widenPath(Program, ProgramUtf16)) { + if (std::error_code ec = sys::windows::widenPath(Program, ProgramUtf16)) { SetLastError(ec.value()); MakeErrMsg(ErrMsg, std::string("Unable to convert application name to UTF-16")); @@ -390,7 +391,8 @@ std::string sys::flattenWindowsCommandLine(ArrayRef<StringRef> Args) { } ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, - bool WaitUntilChildTerminates, std::string *ErrMsg) { + bool WaitUntilChildTerminates, std::string *ErrMsg, + Optional<ProcessStatistics> *ProcStat) { assert(PI.Pid && "invalid pid to wait on, process not started?"); assert((PI.Process && PI.Process != INVALID_HANDLE_VALUE) && "invalid process handle to wait on, process not started?"); @@ -401,6 +403,8 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, milliSecondsToWait = SecondsToWait * 1000; ProcessInfo WaitResult = PI; + if (ProcStat) + ProcStat->reset(); DWORD WaitStatus = WaitForSingleObject(PI.Process, milliSecondsToWait); if (WaitStatus == WAIT_TIMEOUT) { if (SecondsToWait) { @@ -421,6 +425,22 @@ ProcessInfo sys::Wait(const ProcessInfo &PI, unsigned SecondsToWait, } } + // Get process execution statistics. + if (ProcStat) { + FILETIME CreationTime, ExitTime, KernelTime, UserTime; + PROCESS_MEMORY_COUNTERS MemInfo; + if (GetProcessTimes(PI.Process, &CreationTime, &ExitTime, &KernelTime, + &UserTime) && + GetProcessMemoryInfo(PI.Process, &MemInfo, sizeof(MemInfo))) { + auto UserT = std::chrono::duration_cast<std::chrono::microseconds>( + toDuration(UserTime)); + auto KernelT = std::chrono::duration_cast<std::chrono::microseconds>( + toDuration(KernelTime)); + uint64_t PeakMemory = MemInfo.PeakPagefileUsage / 1024; + *ProcStat = ProcessStatistics{UserT + KernelT, UserT, PeakMemory}; + } + } + // Get its exit status. DWORD status; BOOL rc = GetExitCodeProcess(PI.Process, &status); diff --git a/llvm/lib/Support/Windows/Signals.inc b/llvm/lib/Support/Windows/Signals.inc index 8b525f1bd4ac4..0c3681fa96548 100644 --- a/llvm/lib/Support/Windows/Signals.inc +++ b/llvm/lib/Support/Windows/Signals.inc @@ -23,7 +23,7 @@ #include "llvm/Support/raw_ostream.h" // The Windows.h header must be after LLVM and standard headers. -#include "WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #ifdef __MINGW32__ #include <imagehlp.h> @@ -460,7 +460,7 @@ bool sys::RemoveFileOnSignal(StringRef Filename, std::string* ErrMsg) { if (FilesToRemove == NULL) FilesToRemove = new std::vector<std::string>; - FilesToRemove->push_back(Filename); + FilesToRemove->push_back(std::string(Filename)); LeaveCriticalSection(&CriticalSection); return false; @@ -584,7 +584,7 @@ void llvm::sys::AddSignalHandler(sys::SignalHandlerCallback FnPtr, LeaveCriticalSection(&CriticalSection); } -static void Cleanup() { +static void Cleanup(bool ExecuteSignalHandlers) { if (CleanupExecuted) return; @@ -600,7 +600,10 @@ static void Cleanup() { llvm::sys::fs::remove(FilesToRemove->back()); FilesToRemove->pop_back(); } - llvm::sys::RunSignalHandlers(); + + if (ExecuteSignalHandlers) + llvm::sys::RunSignalHandlers(); + LeaveCriticalSection(&CriticalSection); } @@ -610,7 +613,7 @@ void llvm::sys::RunInterruptHandlers() { // error handler). We must ensure that the critical section is properly // initialized. InitializeThreading(); - Cleanup(); + Cleanup(true); } /// Find the Windows Registry Key for a given location. @@ -803,7 +806,7 @@ void sys::CleanupOnSignal(uintptr_t Context) { } static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { - Cleanup(); + Cleanup(true); // We'll automatically write a Minidump file here to help diagnose // the nasty sorts of crashes that aren't 100% reproducible from a set of @@ -820,7 +823,13 @@ static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { << "\n"; } - LocalPrintStackTrace(llvm::errs(), ep ? ep->ContextRecord : nullptr); + // Stack unwinding appears to modify the context. Copy it to preserve the + // caller's context. + CONTEXT ContextCopy; + if (ep) + memcpy(&ContextCopy, ep->ContextRecord, sizeof(ContextCopy)); + + LocalPrintStackTrace(llvm::errs(), ep ? &ContextCopy : nullptr); return EXCEPTION_EXECUTE_HANDLER; } @@ -828,7 +837,10 @@ static LONG WINAPI LLVMUnhandledExceptionFilter(LPEXCEPTION_POINTERS ep) { static BOOL WINAPI LLVMConsoleCtrlHandler(DWORD dwCtrlType) { // We are running in our very own thread, courtesy of Windows. EnterCriticalSection(&CriticalSection); - Cleanup(); + // This function is only ever called when a CTRL-C or similar control signal + // is fired. Killing a process in this way is normal, so don't trigger the + // signal handlers. + Cleanup(false); // If an interrupt function has been set, go and run one it; otherwise, // the process dies. diff --git a/llvm/lib/Support/Windows/ThreadLocal.inc b/llvm/lib/Support/Windows/ThreadLocal.inc index 1e0ed955e9abe..696e5c843ead4 100644 --- a/llvm/lib/Support/Windows/ThreadLocal.inc +++ b/llvm/lib/Support/Windows/ThreadLocal.inc @@ -15,7 +15,7 @@ //=== is guaranteed to work on *all* Win32 variants. //===----------------------------------------------------------------------===// -#include "WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #include "llvm/Support/ThreadLocal.h" namespace llvm { diff --git a/llvm/lib/Support/Windows/Threading.inc b/llvm/lib/Support/Windows/Threading.inc index 9456efa686ffc..296e87b776959 100644 --- a/llvm/lib/Support/Windows/Threading.inc +++ b/llvm/lib/Support/Windows/Threading.inc @@ -13,9 +13,11 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" -#include "WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #include <process.h> +#include <bitset> + // Windows will at times define MemoryFence. #ifdef MemoryFence #undef MemoryFence @@ -122,3 +124,175 @@ SetThreadPriorityResult llvm::set_thread_priority(ThreadPriority Priority) { ? SetThreadPriorityResult::SUCCESS : SetThreadPriorityResult::FAILURE; } + +struct ProcessorGroup { + unsigned ID; + unsigned AllThreads; + unsigned UsableThreads; + unsigned ThreadsPerCore; + uint64_t Affinity; + + unsigned useableCores() const { + return std::max(1U, UsableThreads / ThreadsPerCore); + } +}; + +template <typename F> +static bool IterateProcInfo(LOGICAL_PROCESSOR_RELATIONSHIP Relationship, F Fn) { + DWORD Len = 0; + BOOL R = ::GetLogicalProcessorInformationEx(Relationship, NULL, &Len); + if (R || GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + return false; + } + auto *Info = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)calloc(1, Len); + R = ::GetLogicalProcessorInformationEx(Relationship, Info, &Len); + if (R) { + auto *End = + (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)((uint8_t *)Info + Len); + for (auto *Curr = Info; Curr < End; + Curr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)((uint8_t *)Curr + + Curr->Size)) { + if (Curr->Relationship != Relationship) + continue; + Fn(Curr); + } + } + free(Info); + return true; +} + +static ArrayRef<ProcessorGroup> getProcessorGroups() { + auto computeGroups = []() { + SmallVector<ProcessorGroup, 4> Groups; + + auto HandleGroup = [&](SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *ProcInfo) { + GROUP_RELATIONSHIP &El = ProcInfo->Group; + for (unsigned J = 0; J < El.ActiveGroupCount; ++J) { + ProcessorGroup G; + G.ID = Groups.size(); + G.AllThreads = El.GroupInfo[J].MaximumProcessorCount; + G.UsableThreads = El.GroupInfo[J].ActiveProcessorCount; + assert(G.UsableThreads <= 64); + G.Affinity = El.GroupInfo[J].ActiveProcessorMask; + Groups.push_back(G); + } + }; + + if (!IterateProcInfo(RelationGroup, HandleGroup)) + return std::vector<ProcessorGroup>(); + + auto HandleProc = [&](SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *ProcInfo) { + PROCESSOR_RELATIONSHIP &El = ProcInfo->Processor; + assert(El.GroupCount == 1); + unsigned NumHyperThreads = 1; + // If the flag is set, each core supports more than one hyper-thread. + if (El.Flags & LTP_PC_SMT) + NumHyperThreads = std::bitset<64>(El.GroupMask[0].Mask).count(); + unsigned I = El.GroupMask[0].Group; + Groups[I].ThreadsPerCore = NumHyperThreads; + }; + + if (!IterateProcInfo(RelationProcessorCore, HandleProc)) + return std::vector<ProcessorGroup>(); + + // If there's an affinity mask set on one of the CPUs, then assume the user + // wants to constrain the current process to only a single CPU. + for (auto &G : Groups) { + if (G.UsableThreads != G.AllThreads) { + ProcessorGroup NewG{G}; + Groups.clear(); + Groups.push_back(NewG); + break; + } + } + + return std::vector<ProcessorGroup>(Groups.begin(), Groups.end()); + }; + static auto Groups = computeGroups(); + return ArrayRef<ProcessorGroup>(Groups); +} + +template <typename R, typename UnaryPredicate> +static unsigned aggregate(R &&Range, UnaryPredicate P) { + unsigned I{}; + for (const auto &It : Range) + I += P(It); + return I; +} + +// for sys::getHostNumPhysicalCores +int computeHostNumPhysicalCores() { + static unsigned Cores = + aggregate(getProcessorGroups(), [](const ProcessorGroup &G) { + return G.UsableThreads / G.ThreadsPerCore; + }); + return Cores; +} + +int computeHostNumHardwareThreads() { + static unsigned Threads = + aggregate(getProcessorGroups(), + [](const ProcessorGroup &G) { return G.UsableThreads; }); + return Threads; +} + +// Finds the proper CPU socket where a thread number should go. Returns 'None' +// if the thread shall remain on the actual CPU socket. +Optional<unsigned> +llvm::ThreadPoolStrategy::compute_cpu_socket(unsigned ThreadPoolNum) const { + ArrayRef<ProcessorGroup> Groups = getProcessorGroups(); + // Only one CPU socket in the system or process affinity was set, no need to + // move the thread(s) to another CPU socket. + if (Groups.size() <= 1) + return None; + + // We ask for less threads than there are hardware threads per CPU socket, no + // need to dispatch threads to other CPU sockets. + unsigned MaxThreadsPerSocket = + UseHyperThreads ? Groups[0].UsableThreads : Groups[0].useableCores(); + if (compute_thread_count() <= MaxThreadsPerSocket) + return None; + + assert(ThreadPoolNum < compute_thread_count() && + "The thread index is not within thread strategy's range!"); + + // Assumes the same number of hardware threads per CPU socket. + return (ThreadPoolNum * Groups.size()) / compute_thread_count(); +} + +// Assign the current thread to a more appropriate CPU socket or CPU group +void llvm::ThreadPoolStrategy::apply_thread_strategy( + unsigned ThreadPoolNum) const { + Optional<unsigned> Socket = compute_cpu_socket(ThreadPoolNum); + if (!Socket) + return; + ArrayRef<ProcessorGroup> Groups = getProcessorGroups(); + GROUP_AFFINITY Affinity{}; + Affinity.Group = Groups[*Socket].ID; + Affinity.Mask = Groups[*Socket].Affinity; + SetThreadGroupAffinity(GetCurrentThread(), &Affinity, nullptr); +} + +llvm::BitVector llvm::get_thread_affinity_mask() { + GROUP_AFFINITY Affinity{}; + GetThreadGroupAffinity(GetCurrentThread(), &Affinity); + + static unsigned All = + aggregate(getProcessorGroups(), + [](const ProcessorGroup &G) { return G.AllThreads; }); + + unsigned StartOffset = + aggregate(getProcessorGroups(), [&](const ProcessorGroup &G) { + return G.ID < Affinity.Group ? G.AllThreads : 0; + }); + + llvm::BitVector V; + V.resize(All); + for (unsigned I = 0; I < sizeof(KAFFINITY) * 8; ++I) { + if ((Affinity.Mask >> I) & 1) + V.set(StartOffset + I); + } + return V; +} + +unsigned llvm::get_cpus() { return getProcessorGroups().size(); } diff --git a/llvm/lib/Support/Windows/WindowsSupport.h b/llvm/lib/Support/Windows/WindowsSupport.h deleted file mode 100644 index bb7e79b860180..0000000000000 --- a/llvm/lib/Support/Windows/WindowsSupport.h +++ /dev/null @@ -1,243 +0,0 @@ -//===- WindowsSupport.h - Common Windows Include File -----------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines things specific to Windows implementations. In addition to -// providing some helpers for working with win32 APIs, this header wraps -// <windows.h> with some portability macros. Always include WindowsSupport.h -// instead of including <windows.h> directly. -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -//=== WARNING: Implementation here must contain only generic Win32 code that -//=== is guaranteed to work on *all* Win32 variants. -//===----------------------------------------------------------------------===// - -#ifndef LLVM_SUPPORT_WINDOWSSUPPORT_H -#define LLVM_SUPPORT_WINDOWSSUPPORT_H - -// mingw-w64 tends to define it as 0x0502 in its headers. -#undef _WIN32_WINNT -#undef _WIN32_IE - -// Require at least Windows 7 API. -#define _WIN32_WINNT 0x0601 -#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed. -#define WIN32_LEAN_AND_MEAN -#ifndef NOMINMAX -#define NOMINMAX -#endif - -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/ADT/Twine.h" -#include "llvm/Config/config.h" // Get build system configuration settings -#include "llvm/Support/Allocator.h" -#include "llvm/Support/Chrono.h" -#include "llvm/Support/Compiler.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/VersionTuple.h" -#include <cassert> -#include <string> -#include <system_error> -#include <windows.h> - -// Must be included after windows.h -#include <wincrypt.h> - -namespace llvm { - -/// Determines if the program is running on Windows 8 or newer. This -/// reimplements one of the helpers in the Windows 8.1 SDK, which are intended -/// to supercede raw calls to GetVersionEx. Old SDKs, Cygwin, and MinGW don't -/// yet have VersionHelpers.h, so we have our own helper. -bool RunningWindows8OrGreater(); - -/// Returns the Windows version as Major.Minor.0.BuildNumber. Uses -/// RtlGetVersion or GetVersionEx under the hood depending on what is available. -/// GetVersionEx is deprecated, but this API exposes the build number which can -/// be useful for working around certain kernel bugs. -llvm::VersionTuple GetWindowsOSVersion(); - -bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix); - -// Include GetLastError() in a fatal error message. -LLVM_ATTRIBUTE_NORETURN inline void ReportLastErrorFatal(const char *Msg) { - std::string ErrMsg; - MakeErrMsg(&ErrMsg, Msg); - llvm::report_fatal_error(ErrMsg); -} - -template <typename HandleTraits> -class ScopedHandle { - typedef typename HandleTraits::handle_type handle_type; - handle_type Handle; - - ScopedHandle(const ScopedHandle &other) = delete; - void operator=(const ScopedHandle &other) = delete; -public: - ScopedHandle() - : Handle(HandleTraits::GetInvalid()) {} - - explicit ScopedHandle(handle_type h) - : Handle(h) {} - - ~ScopedHandle() { - if (HandleTraits::IsValid(Handle)) - HandleTraits::Close(Handle); - } - - handle_type take() { - handle_type t = Handle; - Handle = HandleTraits::GetInvalid(); - return t; - } - - ScopedHandle &operator=(handle_type h) { - if (HandleTraits::IsValid(Handle)) - HandleTraits::Close(Handle); - Handle = h; - return *this; - } - - // True if Handle is valid. - explicit operator bool() const { - return HandleTraits::IsValid(Handle) ? true : false; - } - - operator handle_type() const { - return Handle; - } -}; - -struct CommonHandleTraits { - typedef HANDLE handle_type; - - static handle_type GetInvalid() { - return INVALID_HANDLE_VALUE; - } - - static void Close(handle_type h) { - ::CloseHandle(h); - } - - static bool IsValid(handle_type h) { - return h != GetInvalid(); - } -}; - -struct JobHandleTraits : CommonHandleTraits { - static handle_type GetInvalid() { - return NULL; - } -}; - -struct CryptContextTraits : CommonHandleTraits { - typedef HCRYPTPROV handle_type; - - static handle_type GetInvalid() { - return 0; - } - - static void Close(handle_type h) { - ::CryptReleaseContext(h, 0); - } - - static bool IsValid(handle_type h) { - return h != GetInvalid(); - } -}; - -struct RegTraits : CommonHandleTraits { - typedef HKEY handle_type; - - static handle_type GetInvalid() { - return NULL; - } - - static void Close(handle_type h) { - ::RegCloseKey(h); - } - - static bool IsValid(handle_type h) { - return h != GetInvalid(); - } -}; - -struct FindHandleTraits : CommonHandleTraits { - static void Close(handle_type h) { - ::FindClose(h); - } -}; - -struct FileHandleTraits : CommonHandleTraits {}; - -typedef ScopedHandle<CommonHandleTraits> ScopedCommonHandle; -typedef ScopedHandle<FileHandleTraits> ScopedFileHandle; -typedef ScopedHandle<CryptContextTraits> ScopedCryptContext; -typedef ScopedHandle<RegTraits> ScopedRegHandle; -typedef ScopedHandle<FindHandleTraits> ScopedFindHandle; -typedef ScopedHandle<JobHandleTraits> ScopedJobHandle; - -template <class T> -class SmallVectorImpl; - -template <class T> -typename SmallVectorImpl<T>::const_pointer -c_str(SmallVectorImpl<T> &str) { - str.push_back(0); - str.pop_back(); - return str.data(); -} - -namespace sys { - -inline std::chrono::nanoseconds toDuration(FILETIME Time) { - ULARGE_INTEGER TimeInteger; - TimeInteger.LowPart = Time.dwLowDateTime; - TimeInteger.HighPart = Time.dwHighDateTime; - - // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond) - return std::chrono::nanoseconds(100 * TimeInteger.QuadPart); -} - -inline TimePoint<> toTimePoint(FILETIME Time) { - ULARGE_INTEGER TimeInteger; - TimeInteger.LowPart = Time.dwLowDateTime; - TimeInteger.HighPart = Time.dwHighDateTime; - - // Adjust for different epoch - TimeInteger.QuadPart -= 11644473600ll * 10000000; - - // FILETIME's are # of 100 nanosecond ticks (1/10th of a microsecond) - return TimePoint<>(std::chrono::nanoseconds(100 * TimeInteger.QuadPart)); -} - -inline FILETIME toFILETIME(TimePoint<> TP) { - ULARGE_INTEGER TimeInteger; - TimeInteger.QuadPart = TP.time_since_epoch().count() / 100; - TimeInteger.QuadPart += 11644473600ll * 10000000; - - FILETIME Time; - Time.dwLowDateTime = TimeInteger.LowPart; - Time.dwHighDateTime = TimeInteger.HighPart; - return Time; -} - -namespace windows { -// Returns command line arguments. Unlike arguments given to main(), -// this function guarantees that the returned arguments are encoded in -// UTF-8 regardless of the current code page setting. -std::error_code GetCommandLineArguments(SmallVectorImpl<const char *> &Args, - BumpPtrAllocator &Alloc); -} // end namespace windows -} // end namespace sys -} // end namespace llvm. - -#endif diff --git a/llvm/lib/Support/WithColor.cpp b/llvm/lib/Support/WithColor.cpp index 345dd9cf39492..cb5f413d44b78 100644 --- a/llvm/lib/Support/WithColor.cpp +++ b/llvm/lib/Support/WithColor.cpp @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/WithColor.h" -#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/CommandLine.h" using namespace llvm; @@ -18,8 +18,8 @@ static cl::opt<cl::boolOrDefault> cl::desc("Use colors in output (default=autodetect)"), cl::init(cl::BOU_UNSET)); -WithColor::WithColor(raw_ostream &OS, HighlightColor Color, bool DisableColors) - : OS(OS), DisableColors(DisableColors) { +WithColor::WithColor(raw_ostream &OS, HighlightColor Color, ColorMode Mode) + : OS(OS), Mode(Mode) { // Detect color from terminal type unless the user passed the --color option. if (colorsEnabled()) { switch (Color) { @@ -69,7 +69,9 @@ raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix, bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Error, DisableColors).get() + return WithColor(OS, HighlightColor::Error, + DisableColors ? ColorMode::Disable : ColorMode::Auto) + .get() << "error: "; } @@ -77,7 +79,9 @@ raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix, bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Warning, DisableColors).get() + return WithColor(OS, HighlightColor::Warning, + DisableColors ? ColorMode::Disable : ColorMode::Auto) + .get() << "warning: "; } @@ -85,23 +89,33 @@ raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix, bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Note, DisableColors).get() << "note: "; + return WithColor(OS, HighlightColor::Note, + DisableColors ? ColorMode::Disable : ColorMode::Auto) + .get() + << "note: "; } raw_ostream &WithColor::remark(raw_ostream &OS, StringRef Prefix, bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Remark, DisableColors).get() + return WithColor(OS, HighlightColor::Remark, + DisableColors ? ColorMode::Disable : ColorMode::Auto) + .get() << "remark: "; } bool WithColor::colorsEnabled() { - if (DisableColors) + switch (Mode) { + case ColorMode::Enable: + return true; + case ColorMode::Disable: return false; - if (UseColor == cl::BOU_UNSET) - return OS.has_colors(); - return UseColor == cl::BOU_TRUE; + case ColorMode::Auto: + return UseColor == cl::BOU_UNSET ? OS.has_colors() + : UseColor == cl::BOU_TRUE; + } + llvm_unreachable("All cases handled above."); } WithColor &WithColor::changeColor(raw_ostream::Colors Color, bool Bold, @@ -118,3 +132,15 @@ WithColor &WithColor::resetColor() { } WithColor::~WithColor() { resetColor(); } + +void WithColor::defaultErrorHandler(Error Err) { + handleAllErrors(std::move(Err), [](ErrorInfoBase &Info) { + WithColor::error() << Info.message() << '\n'; + }); +} + +void WithColor::defaultWarningHandler(Error Warning) { + handleAllErrors(std::move(Warning), [](ErrorInfoBase &Info) { + WithColor::warning() << Info.message() << '\n'; + }); +} diff --git a/llvm/lib/Support/X86TargetParser.cpp b/llvm/lib/Support/X86TargetParser.cpp new file mode 100644 index 0000000000000..572d1203aaf21 --- /dev/null +++ b/llvm/lib/Support/X86TargetParser.cpp @@ -0,0 +1,595 @@ +//===-- X86TargetParser - Parser for X86 features ---------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a target parser to recognise X86 hardware features. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/X86TargetParser.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" + +using namespace llvm; +using namespace llvm::X86; + +namespace { + +/// Container class for CPU features. +/// This is a constexpr reimplementation of a subset of std::bitset. It would be +/// nice to use std::bitset directly, but it doesn't support constant +/// initialization. +class FeatureBitset { + static constexpr unsigned NUM_FEATURE_WORDS = + (X86::CPU_FEATURE_MAX + 31) / 32; + + // This cannot be a std::array, operator[] is not constexpr until C++17. + uint32_t Bits[NUM_FEATURE_WORDS] = {}; + +public: + constexpr FeatureBitset() = default; + constexpr FeatureBitset(std::initializer_list<unsigned> Init) { + for (auto I : Init) + set(I); + } + + constexpr FeatureBitset &set(unsigned I) { + // GCC <6.2 crashes if this is written in a single statement. + uint32_t NewBits = Bits[I / 32] | (uint32_t(1) << (I % 32)); + Bits[I / 32] = NewBits; + return *this; + } + + constexpr bool operator[](unsigned I) const { + uint32_t Mask = uint32_t(1) << (I % 32); + return (Bits[I / 32] & Mask) != 0; + } + + constexpr FeatureBitset &operator&=(const FeatureBitset &RHS) { + for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) { + // GCC <6.2 crashes if this is written in a single statement. + uint32_t NewBits = Bits[I] & RHS.Bits[I]; + Bits[I] = NewBits; + } + return *this; + } + + constexpr FeatureBitset &operator|=(const FeatureBitset &RHS) { + for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) { + // GCC <6.2 crashes if this is written in a single statement. + uint32_t NewBits = Bits[I] | RHS.Bits[I]; + Bits[I] = NewBits; + } + return *this; + } + + // gcc 5.3 miscompiles this if we try to write this using operator&=. + constexpr FeatureBitset operator&(const FeatureBitset &RHS) const { + FeatureBitset Result; + for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) + Result.Bits[I] = Bits[I] & RHS.Bits[I]; + return Result; + } + + // gcc 5.3 miscompiles this if we try to write this using operator&=. + constexpr FeatureBitset operator|(const FeatureBitset &RHS) const { + FeatureBitset Result; + for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) + Result.Bits[I] = Bits[I] | RHS.Bits[I]; + return Result; + } + + constexpr FeatureBitset operator~() const { + FeatureBitset Result; + for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) + Result.Bits[I] = ~Bits[I]; + return Result; + } +}; + +struct ProcInfo { + StringLiteral Name; + X86::CPUKind Kind; + unsigned KeyFeature; + FeatureBitset Features; +}; + +struct FeatureInfo { + StringLiteral Name; + FeatureBitset ImpliedFeatures; +}; + +} // end anonymous namespace + +#define X86_FEATURE(ENUM, STRING) \ + static constexpr FeatureBitset Feature##ENUM = {X86::FEATURE_##ENUM}; +#include "llvm/Support/X86TargetParser.def" + +// Pentium with MMX. +static constexpr FeatureBitset FeaturesPentiumMMX = + FeatureX87 | FeatureCMPXCHG8B | FeatureMMX; + +// Pentium 2 and 3. +static constexpr FeatureBitset FeaturesPentium2 = + FeatureX87 | FeatureCMPXCHG8B | FeatureMMX | FeatureFXSR; +static constexpr FeatureBitset FeaturesPentium3 = FeaturesPentium2 | FeatureSSE; + +// Pentium 4 CPUs +static constexpr FeatureBitset FeaturesPentium4 = + FeaturesPentium3 | FeatureSSE2; +static constexpr FeatureBitset FeaturesPrescott = + FeaturesPentium4 | FeatureSSE3; +static constexpr FeatureBitset FeaturesNocona = + FeaturesPrescott | Feature64BIT | FeatureCMPXCHG16B; + +// Basic 64-bit capable CPU. +static constexpr FeatureBitset FeaturesX86_64 = FeaturesPentium4 | Feature64BIT; + +// Intel Core CPUs +static constexpr FeatureBitset FeaturesCore2 = + FeaturesNocona | FeatureSAHF | FeatureSSSE3; +static constexpr FeatureBitset FeaturesPenryn = FeaturesCore2 | FeatureSSE4_1; +static constexpr FeatureBitset FeaturesNehalem = + FeaturesPenryn | FeaturePOPCNT | FeatureSSE4_2; +static constexpr FeatureBitset FeaturesWestmere = + FeaturesNehalem | FeaturePCLMUL; +static constexpr FeatureBitset FeaturesSandyBridge = + FeaturesWestmere | FeatureAVX | FeatureXSAVE | FeatureXSAVEOPT; +static constexpr FeatureBitset FeaturesIvyBridge = + FeaturesSandyBridge | FeatureF16C | FeatureFSGSBASE | FeatureRDRND; +static constexpr FeatureBitset FeaturesHaswell = + FeaturesIvyBridge | FeatureAVX2 | FeatureBMI | FeatureBMI2 | FeatureFMA | + FeatureINVPCID | FeatureLZCNT | FeatureMOVBE; +static constexpr FeatureBitset FeaturesBroadwell = + FeaturesHaswell | FeatureADX | FeaturePRFCHW | FeatureRDSEED; + +// Intel Knights Landing and Knights Mill +// Knights Landing has feature parity with Broadwell. +static constexpr FeatureBitset FeaturesKNL = + FeaturesBroadwell | FeatureAES | FeatureAVX512F | FeatureAVX512CD | + FeatureAVX512ER | FeatureAVX512PF | FeaturePREFETCHWT1; +static constexpr FeatureBitset FeaturesKNM = + FeaturesKNL | FeatureAVX512VPOPCNTDQ; + +// Intel Skylake processors. +static constexpr FeatureBitset FeaturesSkylakeClient = + FeaturesBroadwell | FeatureAES | FeatureCLFLUSHOPT | FeatureXSAVEC | + FeatureXSAVES | FeatureSGX; +// SkylakeServer inherits all SkylakeClient features except SGX. +// FIXME: That doesn't match gcc. +static constexpr FeatureBitset FeaturesSkylakeServer = + (FeaturesSkylakeClient & ~FeatureSGX) | FeatureAVX512F | FeatureAVX512CD | + FeatureAVX512DQ | FeatureAVX512BW | FeatureAVX512VL | FeatureCLWB | + FeaturePKU; +static constexpr FeatureBitset FeaturesCascadeLake = + FeaturesSkylakeServer | FeatureAVX512VNNI; +static constexpr FeatureBitset FeaturesCooperLake = + FeaturesCascadeLake | FeatureAVX512BF16; + +// Intel 10nm processors. +static constexpr FeatureBitset FeaturesCannonlake = + FeaturesSkylakeClient | FeatureAVX512F | FeatureAVX512CD | FeatureAVX512DQ | + FeatureAVX512BW | FeatureAVX512VL | FeatureAVX512IFMA | FeatureAVX512VBMI | + FeaturePKU | FeatureSHA; +static constexpr FeatureBitset FeaturesICLClient = + FeaturesCannonlake | FeatureAVX512BITALG | FeatureAVX512VBMI2 | + FeatureAVX512VNNI | FeatureAVX512VPOPCNTDQ | FeatureCLWB | FeatureGFNI | + FeatureRDPID | FeatureVAES | FeatureVPCLMULQDQ; +static constexpr FeatureBitset FeaturesICLServer = + FeaturesICLClient | FeaturePCONFIG | FeatureWBNOINVD; +static constexpr FeatureBitset FeaturesTigerlake = + FeaturesICLClient | FeatureAVX512VP2INTERSECT | FeatureMOVDIR64B | + FeatureMOVDIRI | FeatureSHSTK; + +// Intel Atom processors. +// Bonnell has feature parity with Core2 and adds MOVBE. +static constexpr FeatureBitset FeaturesBonnell = FeaturesCore2 | FeatureMOVBE; +// Silvermont has parity with Westmere and Bonnell plus PRFCHW and RDRND. +static constexpr FeatureBitset FeaturesSilvermont = + FeaturesBonnell | FeaturesWestmere | FeaturePRFCHW | FeatureRDRND; +static constexpr FeatureBitset FeaturesGoldmont = + FeaturesSilvermont | FeatureAES | FeatureCLFLUSHOPT | FeatureFSGSBASE | + FeatureRDSEED | FeatureSHA | FeatureXSAVE | FeatureXSAVEC | + FeatureXSAVEOPT | FeatureXSAVES; +static constexpr FeatureBitset FeaturesGoldmontPlus = + FeaturesGoldmont | FeaturePTWRITE | FeatureRDPID | FeatureSGX; +static constexpr FeatureBitset FeaturesTremont = + FeaturesGoldmontPlus | FeatureCLWB | FeatureGFNI; + +// Geode Processor. +static constexpr FeatureBitset FeaturesGeode = + FeatureX87 | FeatureCMPXCHG8B | FeatureMMX | Feature3DNOW | Feature3DNOWA; + +// K6 processor. +static constexpr FeatureBitset FeaturesK6 = + FeatureX87 | FeatureCMPXCHG8B | FeatureMMX; + +// K7 and K8 architecture processors. +static constexpr FeatureBitset FeaturesAthlon = + FeatureX87 | FeatureCMPXCHG8B | FeatureMMX | Feature3DNOW | Feature3DNOWA; +static constexpr FeatureBitset FeaturesAthlonXP = + FeaturesAthlon | FeatureFXSR | FeatureSSE; +static constexpr FeatureBitset FeaturesK8 = + FeaturesAthlonXP | FeatureSSE2 | Feature64BIT; +static constexpr FeatureBitset FeaturesK8SSE3 = FeaturesK8 | FeatureSSE3; +static constexpr FeatureBitset FeaturesAMDFAM10 = + FeaturesK8SSE3 | FeatureCMPXCHG16B | FeatureLZCNT | FeaturePOPCNT | + FeaturePRFCHW | FeatureSAHF | FeatureSSE4_A; + +// Bobcat architecture processors. +static constexpr FeatureBitset FeaturesBTVER1 = + FeatureX87 | FeatureCMPXCHG8B | FeatureCMPXCHG16B | Feature64BIT | + FeatureFXSR | FeatureLZCNT | FeatureMMX | FeaturePOPCNT | FeaturePRFCHW | + FeatureSSE | FeatureSSE2 | FeatureSSE3 | FeatureSSSE3 | FeatureSSE4_A | + FeatureSAHF; +static constexpr FeatureBitset FeaturesBTVER2 = + FeaturesBTVER1 | FeatureAES | FeatureAVX | FeatureBMI | FeatureF16C | + FeatureMOVBE | FeaturePCLMUL | FeatureXSAVE | FeatureXSAVEOPT; + +// AMD Bulldozer architecture processors. +static constexpr FeatureBitset FeaturesBDVER1 = + FeatureX87 | FeatureAES | FeatureAVX | FeatureCMPXCHG8B | + FeatureCMPXCHG16B | Feature64BIT | FeatureFMA4 | FeatureFXSR | FeatureLWP | + FeatureLZCNT | FeatureMMX | FeaturePCLMUL | FeaturePOPCNT | FeaturePRFCHW | + FeatureSAHF | FeatureSSE | FeatureSSE2 | FeatureSSE3 | FeatureSSSE3 | + FeatureSSE4_1 | FeatureSSE4_2 | FeatureSSE4_A | FeatureXOP | FeatureXSAVE; +static constexpr FeatureBitset FeaturesBDVER2 = + FeaturesBDVER1 | FeatureBMI | FeatureFMA | FeatureF16C | FeatureTBM; +static constexpr FeatureBitset FeaturesBDVER3 = + FeaturesBDVER2 | FeatureFSGSBASE | FeatureXSAVEOPT; +static constexpr FeatureBitset FeaturesBDVER4 = + FeaturesBDVER3 | FeatureAVX2 | FeatureBMI2 | FeatureMOVBE | FeatureMWAITX | + FeatureRDRND; + +// AMD Zen architecture processors. +static constexpr FeatureBitset FeaturesZNVER1 = + FeatureX87 | FeatureADX | FeatureAES | FeatureAVX | FeatureAVX2 | + FeatureBMI | FeatureBMI2 | FeatureCLFLUSHOPT | FeatureCLZERO | + FeatureCMPXCHG8B | FeatureCMPXCHG16B | Feature64BIT | FeatureF16C | + FeatureFMA | FeatureFSGSBASE | FeatureFXSR | FeatureLZCNT | FeatureMMX | + FeatureMOVBE | FeatureMWAITX | FeaturePCLMUL | FeaturePOPCNT | + FeaturePRFCHW | FeatureRDRND | FeatureRDSEED | FeatureSAHF | FeatureSHA | + FeatureSSE | FeatureSSE2 | FeatureSSE3 | FeatureSSSE3 | FeatureSSE4_1 | + FeatureSSE4_2 | FeatureSSE4_A | FeatureXSAVE | FeatureXSAVEC | + FeatureXSAVEOPT | FeatureXSAVES; +static constexpr FeatureBitset FeaturesZNVER2 = + FeaturesZNVER1 | FeatureCLWB | FeatureRDPID | FeatureWBNOINVD; + +static constexpr ProcInfo Processors[] = { + // Empty processor. Include X87 and CMPXCHG8 for backwards compatibility. + { {""}, CK_None, ~0U, FeatureX87 | FeatureCMPXCHG8B }, + // i386-generation processors. + { {"i386"}, CK_i386, ~0U, FeatureX87 }, + // i486-generation processors. + { {"i486"}, CK_i486, ~0U, FeatureX87 }, + { {"winchip-c6"}, CK_WinChipC6, ~0U, FeaturesPentiumMMX }, + { {"winchip2"}, CK_WinChip2, ~0U, FeaturesPentiumMMX | Feature3DNOW }, + { {"c3"}, CK_C3, ~0U, FeaturesPentiumMMX | Feature3DNOW }, + // i586-generation processors, P5 microarchitecture based. + { {"i586"}, CK_i586, ~0U, FeatureX87 | FeatureCMPXCHG8B }, + { {"pentium"}, CK_Pentium, ~0U, FeatureX87 | FeatureCMPXCHG8B }, + { {"pentium-mmx"}, CK_PentiumMMX, ~0U, FeaturesPentiumMMX }, + // i686-generation processors, P6 / Pentium M microarchitecture based. + { {"pentiumpro"}, CK_PentiumPro, ~0U, FeatureX87 | FeatureCMPXCHG8B }, + { {"i686"}, CK_i686, ~0U, FeatureX87 | FeatureCMPXCHG8B }, + { {"pentium2"}, CK_Pentium2, ~0U, FeaturesPentium2 }, + { {"pentium3"}, CK_Pentium3, ~0U, FeaturesPentium3 }, + { {"pentium3m"}, CK_Pentium3, ~0U, FeaturesPentium3 }, + { {"pentium-m"}, CK_PentiumM, ~0U, FeaturesPentium4 }, + { {"c3-2"}, CK_C3_2, ~0U, FeaturesPentium3 }, + { {"yonah"}, CK_Yonah, ~0U, FeaturesPrescott }, + // Netburst microarchitecture based processors. + { {"pentium4"}, CK_Pentium4, ~0U, FeaturesPentium4 }, + { {"pentium4m"}, CK_Pentium4, ~0U, FeaturesPentium4 }, + { {"prescott"}, CK_Prescott, ~0U, FeaturesPrescott }, + { {"nocona"}, CK_Nocona, ~0U, FeaturesNocona }, + // Core microarchitecture based processors. + { {"core2"}, CK_Core2, ~0U, FeaturesCore2 }, + { {"penryn"}, CK_Penryn, ~0U, FeaturesPenryn }, + // Atom processors + { {"bonnell"}, CK_Bonnell, FEATURE_SSSE3, FeaturesBonnell }, + { {"atom"}, CK_Bonnell, FEATURE_SSSE3, FeaturesBonnell }, + { {"silvermont"}, CK_Silvermont, FEATURE_SSE4_2, FeaturesSilvermont }, + { {"slm"}, CK_Silvermont, FEATURE_SSE4_2, FeaturesSilvermont }, + { {"goldmont"}, CK_Goldmont, FEATURE_SSE4_2, FeaturesGoldmont }, + { {"goldmont-plus"}, CK_GoldmontPlus, FEATURE_SSE4_2, FeaturesGoldmontPlus }, + { {"tremont"}, CK_Tremont, FEATURE_SSE4_2, FeaturesTremont }, + // Nehalem microarchitecture based processors. + { {"nehalem"}, CK_Nehalem, FEATURE_SSE4_2, FeaturesNehalem }, + { {"corei7"}, CK_Nehalem, FEATURE_SSE4_2, FeaturesNehalem }, + // Westmere microarchitecture based processors. + { {"westmere"}, CK_Westmere, FEATURE_PCLMUL, FeaturesWestmere }, + // Sandy Bridge microarchitecture based processors. + { {"sandybridge"}, CK_SandyBridge, FEATURE_AVX, FeaturesSandyBridge }, + { {"corei7-avx"}, CK_SandyBridge, FEATURE_AVX, FeaturesSandyBridge }, + // Ivy Bridge microarchitecture based processors. + { {"ivybridge"}, CK_IvyBridge, FEATURE_AVX, FeaturesIvyBridge }, + { {"core-avx-i"}, CK_IvyBridge, FEATURE_AVX, FeaturesIvyBridge }, + // Haswell microarchitecture based processors. + { {"haswell"}, CK_Haswell, FEATURE_AVX2, FeaturesHaswell }, + { {"core-avx2"}, CK_Haswell, FEATURE_AVX2, FeaturesHaswell }, + // Broadwell microarchitecture based processors. + { {"broadwell"}, CK_Broadwell, FEATURE_AVX2, FeaturesBroadwell }, + // Skylake client microarchitecture based processors. + { {"skylake"}, CK_SkylakeClient, FEATURE_AVX2, FeaturesSkylakeClient }, + // Skylake server microarchitecture based processors. + { {"skylake-avx512"}, CK_SkylakeServer, FEATURE_AVX512F, FeaturesSkylakeServer }, + { {"skx"}, CK_SkylakeServer, FEATURE_AVX512F, FeaturesSkylakeServer }, + // Cascadelake Server microarchitecture based processors. + { {"cascadelake"}, CK_Cascadelake, FEATURE_AVX512VNNI, FeaturesCascadeLake }, + // Cooperlake Server microarchitecture based processors. + { {"cooperlake"}, CK_Cooperlake, FEATURE_AVX512BF16, FeaturesCooperLake }, + // Cannonlake client microarchitecture based processors. + { {"cannonlake"}, CK_Cannonlake, FEATURE_AVX512VBMI, FeaturesCannonlake }, + // Icelake client microarchitecture based processors. + { {"icelake-client"}, CK_IcelakeClient, FEATURE_AVX512VBMI2, FeaturesICLClient }, + // Icelake server microarchitecture based processors. + { {"icelake-server"}, CK_IcelakeServer, FEATURE_AVX512VBMI2, FeaturesICLServer }, + // Tigerlake microarchitecture based processors. + { {"tigerlake"}, CK_Tigerlake, FEATURE_AVX512VP2INTERSECT, FeaturesTigerlake }, + // Knights Landing processor. + { {"knl"}, CK_KNL, FEATURE_AVX512F, FeaturesKNL }, + // Knights Mill processor. + { {"knm"}, CK_KNM, FEATURE_AVX5124FMAPS, FeaturesKNM }, + // Lakemont microarchitecture based processors. + { {"lakemont"}, CK_Lakemont, ~0U, FeatureCMPXCHG8B }, + // K6 architecture processors. + { {"k6"}, CK_K6, ~0U, FeaturesK6 }, + { {"k6-2"}, CK_K6_2, ~0U, FeaturesK6 | Feature3DNOW }, + { {"k6-3"}, CK_K6_3, ~0U, FeaturesK6 | Feature3DNOW }, + // K7 architecture processors. + { {"athlon"}, CK_Athlon, ~0U, FeaturesAthlon }, + { {"athlon-tbird"}, CK_Athlon, ~0U, FeaturesAthlon }, + { {"athlon-xp"}, CK_AthlonXP, ~0U, FeaturesAthlonXP }, + { {"athlon-mp"}, CK_AthlonXP, ~0U, FeaturesAthlonXP }, + { {"athlon-4"}, CK_AthlonXP, ~0U, FeaturesAthlonXP }, + // K8 architecture processors. + { {"k8"}, CK_K8, ~0U, FeaturesK8 }, + { {"athlon64"}, CK_K8, ~0U, FeaturesK8 }, + { {"athlon-fx"}, CK_K8, ~0U, FeaturesK8 }, + { {"opteron"}, CK_K8, ~0U, FeaturesK8 }, + { {"k8-sse3"}, CK_K8SSE3, ~0U, FeaturesK8SSE3 }, + { {"athlon64-sse3"}, CK_K8SSE3, ~0U, FeaturesK8SSE3 }, + { {"opteron-sse3"}, CK_K8SSE3, ~0U, FeaturesK8SSE3 }, + { {"amdfam10"}, CK_AMDFAM10, FEATURE_SSE4_A, FeaturesAMDFAM10 }, + { {"barcelona"}, CK_AMDFAM10, FEATURE_SSE4_A, FeaturesAMDFAM10 }, + // Bobcat architecture processors. + { {"btver1"}, CK_BTVER1, FEATURE_SSE4_A, FeaturesBTVER1 }, + { {"btver2"}, CK_BTVER2, FEATURE_BMI, FeaturesBTVER2 }, + // Bulldozer architecture processors. + { {"bdver1"}, CK_BDVER1, FEATURE_XOP, FeaturesBDVER1 }, + { {"bdver2"}, CK_BDVER2, FEATURE_FMA, FeaturesBDVER2 }, + { {"bdver3"}, CK_BDVER3, FEATURE_FMA, FeaturesBDVER3 }, + { {"bdver4"}, CK_BDVER4, FEATURE_AVX2, FeaturesBDVER4 }, + // Zen architecture processors. + { {"znver1"}, CK_ZNVER1, FEATURE_AVX2, FeaturesZNVER1 }, + { {"znver2"}, CK_ZNVER2, FEATURE_AVX2, FeaturesZNVER2 }, + // Generic 64-bit processor. + { {"x86-64"}, CK_x86_64, ~0U, FeaturesX86_64 }, + // Geode processors. + { {"geode"}, CK_Geode, ~0U, FeaturesGeode }, +}; + +X86::CPUKind llvm::X86::parseArchX86(StringRef CPU, bool Only64Bit) { + for (const auto &P : Processors) + if (P.Name == CPU && (P.Features[FEATURE_64BIT] || !Only64Bit)) + return P.Kind; + + return CK_None; +} + +void llvm::X86::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values, + bool Only64Bit) { + for (const auto &P : Processors) + if (!P.Name.empty() && (P.Features[FEATURE_64BIT] || !Only64Bit)) + Values.emplace_back(P.Name); +} + +ProcessorFeatures llvm::X86::getKeyFeature(X86::CPUKind Kind) { + // FIXME: Can we avoid a linear search here? The table might be sorted by + // CPUKind so we could binary search? + for (const auto &P : Processors) { + if (P.Kind == Kind) { + assert(P.KeyFeature != ~0U && "Processor does not have a key feature."); + return static_cast<ProcessorFeatures>(P.KeyFeature); + } + } + + llvm_unreachable("Unable to find CPU kind!"); +} + +// Features with no dependencies. +static constexpr FeatureBitset ImpliedFeatures64BIT = {}; +static constexpr FeatureBitset ImpliedFeaturesADX = {}; +static constexpr FeatureBitset ImpliedFeaturesBMI = {}; +static constexpr FeatureBitset ImpliedFeaturesBMI2 = {}; +static constexpr FeatureBitset ImpliedFeaturesCLDEMOTE = {}; +static constexpr FeatureBitset ImpliedFeaturesCLFLUSHOPT = {}; +static constexpr FeatureBitset ImpliedFeaturesCLWB = {}; +static constexpr FeatureBitset ImpliedFeaturesCLZERO = {}; +static constexpr FeatureBitset ImpliedFeaturesCMOV = {}; +static constexpr FeatureBitset ImpliedFeaturesCMPXCHG16B = {}; +static constexpr FeatureBitset ImpliedFeaturesCMPXCHG8B = {}; +static constexpr FeatureBitset ImpliedFeaturesENQCMD = {}; +static constexpr FeatureBitset ImpliedFeaturesFSGSBASE = {}; +static constexpr FeatureBitset ImpliedFeaturesFXSR = {}; +static constexpr FeatureBitset ImpliedFeaturesINVPCID = {}; +static constexpr FeatureBitset ImpliedFeaturesLWP = {}; +static constexpr FeatureBitset ImpliedFeaturesLZCNT = {}; +static constexpr FeatureBitset ImpliedFeaturesMWAITX = {}; +static constexpr FeatureBitset ImpliedFeaturesMOVBE = {}; +static constexpr FeatureBitset ImpliedFeaturesMOVDIR64B = {}; +static constexpr FeatureBitset ImpliedFeaturesMOVDIRI = {}; +static constexpr FeatureBitset ImpliedFeaturesPCONFIG = {}; +static constexpr FeatureBitset ImpliedFeaturesPOPCNT = {}; +static constexpr FeatureBitset ImpliedFeaturesPKU = {}; +static constexpr FeatureBitset ImpliedFeaturesPREFETCHWT1 = {}; +static constexpr FeatureBitset ImpliedFeaturesPRFCHW = {}; +static constexpr FeatureBitset ImpliedFeaturesPTWRITE = {}; +static constexpr FeatureBitset ImpliedFeaturesRDPID = {}; +static constexpr FeatureBitset ImpliedFeaturesRDRND = {}; +static constexpr FeatureBitset ImpliedFeaturesRDSEED = {}; +static constexpr FeatureBitset ImpliedFeaturesRTM = {}; +static constexpr FeatureBitset ImpliedFeaturesSAHF = {}; +static constexpr FeatureBitset ImpliedFeaturesSERIALIZE = {}; +static constexpr FeatureBitset ImpliedFeaturesSGX = {}; +static constexpr FeatureBitset ImpliedFeaturesSHSTK = {}; +static constexpr FeatureBitset ImpliedFeaturesTBM = {}; +static constexpr FeatureBitset ImpliedFeaturesTSXLDTRK = {}; +static constexpr FeatureBitset ImpliedFeaturesWAITPKG = {}; +static constexpr FeatureBitset ImpliedFeaturesWBNOINVD = {}; +static constexpr FeatureBitset ImpliedFeaturesVZEROUPPER = {}; +static constexpr FeatureBitset ImpliedFeaturesX87 = {}; +static constexpr FeatureBitset ImpliedFeaturesXSAVE = {}; + +// Not really CPU features, but need to be in the table because clang uses +// target features to communicate them to the backend. +static constexpr FeatureBitset ImpliedFeaturesRETPOLINE_EXTERNAL_THUNK = {}; +static constexpr FeatureBitset ImpliedFeaturesRETPOLINE_INDIRECT_BRANCHES = {}; +static constexpr FeatureBitset ImpliedFeaturesRETPOLINE_INDIRECT_CALLS = {}; +static constexpr FeatureBitset ImpliedFeaturesLVI_CFI = {}; +static constexpr FeatureBitset ImpliedFeaturesLVI_LOAD_HARDENING = {}; + +// XSAVE features are dependent on basic XSAVE. +static constexpr FeatureBitset ImpliedFeaturesXSAVEC = FeatureXSAVE; +static constexpr FeatureBitset ImpliedFeaturesXSAVEOPT = FeatureXSAVE; +static constexpr FeatureBitset ImpliedFeaturesXSAVES = FeatureXSAVE; + +// MMX->3DNOW->3DNOWA chain. +static constexpr FeatureBitset ImpliedFeaturesMMX = {}; +static constexpr FeatureBitset ImpliedFeatures3DNOW = FeatureMMX; +static constexpr FeatureBitset ImpliedFeatures3DNOWA = Feature3DNOW; + +// SSE/AVX/AVX512F chain. +static constexpr FeatureBitset ImpliedFeaturesSSE = {}; +static constexpr FeatureBitset ImpliedFeaturesSSE2 = FeatureSSE; +static constexpr FeatureBitset ImpliedFeaturesSSE3 = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesSSSE3 = FeatureSSE3; +static constexpr FeatureBitset ImpliedFeaturesSSE4_1 = FeatureSSSE3; +static constexpr FeatureBitset ImpliedFeaturesSSE4_2 = FeatureSSE4_1; +static constexpr FeatureBitset ImpliedFeaturesAVX = FeatureSSE4_2; +static constexpr FeatureBitset ImpliedFeaturesAVX2 = FeatureAVX; +static constexpr FeatureBitset ImpliedFeaturesAVX512F = + FeatureAVX2 | FeatureF16C | FeatureFMA; + +// Vector extensions that build on SSE or AVX. +static constexpr FeatureBitset ImpliedFeaturesAES = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesF16C = FeatureAVX; +static constexpr FeatureBitset ImpliedFeaturesFMA = FeatureAVX; +static constexpr FeatureBitset ImpliedFeaturesGFNI = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesPCLMUL = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesSHA = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesVAES = FeatureAES | FeatureAVX; +static constexpr FeatureBitset ImpliedFeaturesVPCLMULQDQ = + FeatureAVX | FeaturePCLMUL; + +// AVX512 features. +static constexpr FeatureBitset ImpliedFeaturesAVX512CD = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512BW = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512DQ = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512ER = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512PF = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512VL = FeatureAVX512F; + +static constexpr FeatureBitset ImpliedFeaturesAVX512BF16 = FeatureAVX512BW; +static constexpr FeatureBitset ImpliedFeaturesAVX512BITALG = FeatureAVX512BW; +static constexpr FeatureBitset ImpliedFeaturesAVX512IFMA = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512VNNI = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512VPOPCNTDQ = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512VBMI = FeatureAVX512BW; +static constexpr FeatureBitset ImpliedFeaturesAVX512VBMI2 = FeatureAVX512BW; +static constexpr FeatureBitset ImpliedFeaturesAVX512VP2INTERSECT = + FeatureAVX512F; + +// FIXME: These two aren't really implemented and just exist in the feature +// list for __builtin_cpu_supports. So omit their dependencies. +static constexpr FeatureBitset ImpliedFeaturesAVX5124FMAPS = {}; +static constexpr FeatureBitset ImpliedFeaturesAVX5124VNNIW = {}; + +// SSE4_A->FMA4->XOP chain. +static constexpr FeatureBitset ImpliedFeaturesSSE4_A = FeatureSSSE3; +static constexpr FeatureBitset ImpliedFeaturesFMA4 = FeatureAVX | FeatureSSE4_A; +static constexpr FeatureBitset ImpliedFeaturesXOP = FeatureFMA4; + +// AMX Features +static constexpr FeatureBitset ImpliedFeaturesAMX_TILE = {}; +static constexpr FeatureBitset ImpliedFeaturesAMX_BF16 = FeatureAMX_TILE; +static constexpr FeatureBitset ImpliedFeaturesAMX_INT8 = FeatureAMX_TILE; + +static constexpr FeatureInfo FeatureInfos[X86::CPU_FEATURE_MAX] = { +#define X86_FEATURE(ENUM, STR) {{STR}, ImpliedFeatures##ENUM}, +#include "llvm/Support/X86TargetParser.def" +}; + +// Convert the set bits in FeatureBitset to a list of strings. +static void getFeatureBitsAsStrings(const FeatureBitset &Bits, + SmallVectorImpl<StringRef> &Features) { + for (unsigned i = 0; i != CPU_FEATURE_MAX; ++i) + if (Bits[i] && !FeatureInfos[i].Name.empty()) + Features.push_back(FeatureInfos[i].Name); +} + +void llvm::X86::getFeaturesForCPU(StringRef CPU, + SmallVectorImpl<StringRef> &EnabledFeatures) { + auto I = llvm::find_if(Processors, + [&](const ProcInfo &P) { return P.Name == CPU; }); + assert(I != std::end(Processors) && "Processor not found!"); + + FeatureBitset Bits = I->Features; + + // Remove the 64-bit feature which we only use to validate if a CPU can + // be used with 64-bit mode. + Bits &= ~Feature64BIT; + + // Add the string version of all set bits. + getFeatureBitsAsStrings(Bits, EnabledFeatures); +} + +// For each feature that is (transitively) implied by this feature, set it. +static void getImpliedEnabledFeatures(FeatureBitset &Bits, + const FeatureBitset &Implies) { + Bits |= Implies; + for (unsigned i = 0; i != CPU_FEATURE_MAX; ++i) { + if (Implies[i]) + getImpliedEnabledFeatures(Bits, FeatureInfos[i].ImpliedFeatures); + } +} + +/// Create bit vector of features that are implied disabled if the feature +/// passed in Value is disabled. +static void getImpliedDisabledFeatures(FeatureBitset &Bits, unsigned Value) { + // Check all features looking for any dependent on this feature. If we find + // one, mark it and recursively find any feature that depend on it. + for (unsigned i = 0; i != CPU_FEATURE_MAX; ++i) { + if (FeatureInfos[i].ImpliedFeatures[Value]) { + Bits.set(i); + getImpliedDisabledFeatures(Bits, i); + } + } +} + +void llvm::X86::getImpliedFeatures( + StringRef Feature, bool Enabled, + SmallVectorImpl<StringRef> &ImpliedFeatures) { + auto I = llvm::find_if( + FeatureInfos, [&](const FeatureInfo &FI) { return FI.Name == Feature; }); + if (I == std::end(FeatureInfos)) { + // FIXME: This shouldn't happen, but may not have all features in the table + // yet. + return; + } + + FeatureBitset ImpliedBits; + if (Enabled) + getImpliedEnabledFeatures(ImpliedBits, I->ImpliedFeatures); + else + getImpliedDisabledFeatures(ImpliedBits, + std::distance(std::begin(FeatureInfos), I)); + + // Convert all the found bits into strings. + getFeatureBitsAsStrings(ImpliedBits, ImpliedFeatures); +} diff --git a/llvm/lib/Support/YAMLParser.cpp b/llvm/lib/Support/YAMLParser.cpp index d17e7b227f4a8..ca8ffdc47afa6 100644 --- a/llvm/lib/Support/YAMLParser.cpp +++ b/llvm/lib/Support/YAMLParser.cpp @@ -268,8 +268,8 @@ public: } void setError(const Twine &Message, StringRef::iterator Position) { - if (Current >= End) - Current = End - 1; + if (Position >= End) + Position = End - 1; // propagate the error if possible if (EC) @@ -278,14 +278,10 @@ public: // Don't print out more errors after the first one we encounter. The rest // are just the result of the first, and have no meaning. if (!Failed) - printError(SMLoc::getFromPointer(Current), SourceMgr::DK_Error, Message); + printError(SMLoc::getFromPointer(Position), SourceMgr::DK_Error, Message); Failed = true; } - void setError(const Twine &Message) { - setError(Message, Current); - } - /// Returns true if an error occurred while parsing. bool failed() { return Failed; @@ -934,13 +930,13 @@ void Scanner::scan_ns_uri_char() { bool Scanner::consume(uint32_t Expected) { if (Expected >= 0x80) { - setError("Cannot consume non-ascii characters"); + setError("Cannot consume non-ascii characters", Current); return false; } if (Current == End) return false; if (uint8_t(*Current) >= 0x80) { - setError("Cannot consume non-ascii characters"); + setError("Cannot consume non-ascii characters", Current); return false; } if (uint8_t(*Current) == Expected) { @@ -1642,7 +1638,7 @@ bool Scanner::scanBlockScalar(bool IsLiteral) { Token T; T.Kind = Token::TK_BlockScalar; T.Range = StringRef(Start, Current - Start); - T.Value = Str.str().str(); + T.Value = std::string(Str); TokenQueue.push_back(T); return true; } @@ -1763,7 +1759,7 @@ bool Scanner::fetchMoreTokens() { && !isBlankOrBreak(Current + 2))) return scanPlainScalar(); - setError("Unrecognized character while tokenizing."); + setError("Unrecognized character while tokenizing.", Current); return false; } @@ -1819,11 +1815,11 @@ std::string Node::getVerbatimTag() const { if (!Raw.empty() && Raw != "!") { std::string Ret; if (Raw.find_last_of('!') == 0) { - Ret = Doc->getTagMap().find("!")->second; + Ret = std::string(Doc->getTagMap().find("!")->second); Ret += Raw.substr(1); return Ret; } else if (Raw.startswith("!!")) { - Ret = Doc->getTagMap().find("!!")->second; + Ret = std::string(Doc->getTagMap().find("!!")->second); Ret += Raw.substr(2); return Ret; } else { @@ -1831,7 +1827,7 @@ std::string Node::getVerbatimTag() const { std::map<StringRef, StringRef>::const_iterator It = Doc->getTagMap().find(TagHandle); if (It != Doc->getTagMap().end()) - Ret = It->second; + Ret = std::string(It->second); else { Token T; T.Kind = Token::TK_Tag; diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp index 5f0cedc71829a..9ac7c65e19f73 100644 --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -166,6 +166,8 @@ bool Input::preflightKey(const char *Key, bool Required, bool, bool &UseDefault, if (!MN) { if (Required || !isa<EmptyHNode>(CurrentNode)) setError(CurrentNode, "not a mapping"); + else + UseDefault = true; return false; } MN->ValidKeys.push_back(Key); @@ -738,7 +740,7 @@ bool Output::canElideEmptySequence() { // the whole key/value can be not written. But, that produces wrong yaml // if the key/value is the only thing in the map and the map is used in // a sequence. This detects if the this sequence is the first key/value - // in map that itself is embedded in a sequnce. + // in map that itself is embedded in a sequence. if (StateStack.size() < 2) return true; if (StateStack.back() != inMapFirstKey) @@ -876,12 +878,12 @@ StringRef ScalarTraits<StringRef>::input(StringRef Scalar, void *, } void ScalarTraits<std::string>::output(const std::string &Val, void *, - raw_ostream &Out) { + raw_ostream &Out) { Out << Val; } StringRef ScalarTraits<std::string>::input(StringRef Scalar, void *, - std::string &Val) { + std::string &Val) { Val = Scalar.str(); return StringRef(); } diff --git a/llvm/lib/Support/Z3Solver.cpp b/llvm/lib/Support/Z3Solver.cpp index a83d0f441a4bd..9485536d13120 100644 --- a/llvm/lib/Support/Z3Solver.cpp +++ b/llvm/lib/Support/Z3Solver.cpp @@ -6,6 +6,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/config.h" #include "llvm/Support/SMTAPI.h" @@ -516,16 +517,16 @@ public: SMTExprRef RoundingMode = getFloatRoundingMode(); return newExprRef( Z3Expr(Context, - Z3_mk_fpa_mul(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); + Z3_mk_fpa_mul(Context.Context, toZ3Expr(*RoundingMode).AST, + toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST))); } SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override { SMTExprRef RoundingMode = getFloatRoundingMode(); return newExprRef( Z3Expr(Context, - Z3_mk_fpa_div(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); + Z3_mk_fpa_div(Context.Context, toZ3Expr(*RoundingMode).AST, + toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST))); } SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override { @@ -538,16 +539,16 @@ public: SMTExprRef RoundingMode = getFloatRoundingMode(); return newExprRef( Z3Expr(Context, - Z3_mk_fpa_add(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); + Z3_mk_fpa_add(Context.Context, toZ3Expr(*RoundingMode).AST, + toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST))); } SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override { SMTExprRef RoundingMode = getFloatRoundingMode(); return newExprRef( Z3Expr(Context, - Z3_mk_fpa_sub(Context.Context, toZ3Expr(*LHS).AST, - toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST))); + Z3_mk_fpa_sub(Context.Context, toZ3Expr(*RoundingMode).AST, + toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST))); } SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) override { @@ -723,10 +724,25 @@ public: } SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) override { - const SMTSortRef Sort = getBitvectorSort(BitWidth); - return newExprRef( - Z3Expr(Context, Z3_mk_numeral(Context.Context, Int.toString(10).c_str(), - toZ3Sort(*Sort).Sort))); + const Z3_sort Z3Sort = toZ3Sort(*getBitvectorSort(BitWidth)).Sort; + + // Slow path, when 64 bits are not enough. + if (LLVM_UNLIKELY(Int.getBitWidth() > 64u)) { + SmallString<40> Buffer; + Int.toString(Buffer, 10); + return newExprRef(Z3Expr( + Context, Z3_mk_numeral(Context.Context, Buffer.c_str(), Z3Sort))); + } + + const int64_t BitReprAsSigned = Int.getExtValue(); + const uint64_t BitReprAsUnsigned = + reinterpret_cast<const uint64_t &>(BitReprAsSigned); + + Z3_ast Literal = + Int.isSigned() + ? Z3_mk_int64(Context.Context, BitReprAsSigned, Z3Sort) + : Z3_mk_unsigned_int64(Context.Context, BitReprAsUnsigned, Z3Sort); + return newExprRef(Z3Expr(Context, Literal)); } SMTExprRef mkFloat(const llvm::APFloat Float) override { diff --git a/llvm/lib/Support/raw_ostream.cpp b/llvm/lib/Support/raw_ostream.cpp index 4bb315f824af2..f2d78d7732397 100644 --- a/llvm/lib/Support/raw_ostream.cpp +++ b/llvm/lib/Support/raw_ostream.cpp @@ -60,21 +60,21 @@ #ifdef _WIN32 #include "llvm/Support/ConvertUTF.h" -#include "Windows/WindowsSupport.h" +#include "llvm/Support/Windows/WindowsSupport.h" #endif using namespace llvm; -const raw_ostream::Colors raw_ostream::BLACK; -const raw_ostream::Colors raw_ostream::RED; -const raw_ostream::Colors raw_ostream::GREEN; -const raw_ostream::Colors raw_ostream::YELLOW; -const raw_ostream::Colors raw_ostream::BLUE; -const raw_ostream::Colors raw_ostream::MAGENTA; -const raw_ostream::Colors raw_ostream::CYAN; -const raw_ostream::Colors raw_ostream::WHITE; -const raw_ostream::Colors raw_ostream::SAVEDCOLOR; -const raw_ostream::Colors raw_ostream::RESET; +constexpr raw_ostream::Colors raw_ostream::BLACK; +constexpr raw_ostream::Colors raw_ostream::RED; +constexpr raw_ostream::Colors raw_ostream::GREEN; +constexpr raw_ostream::Colors raw_ostream::YELLOW; +constexpr raw_ostream::Colors raw_ostream::BLUE; +constexpr raw_ostream::Colors raw_ostream::MAGENTA; +constexpr raw_ostream::Colors raw_ostream::CYAN; +constexpr raw_ostream::Colors raw_ostream::WHITE; +constexpr raw_ostream::Colors raw_ostream::SAVEDCOLOR; +constexpr raw_ostream::Colors raw_ostream::RESET; raw_ostream::~raw_ostream() { // raw_ostream's subclasses should take care to flush the buffer @@ -216,7 +216,7 @@ void raw_ostream::flush_nonempty() { assert(OutBufCur > OutBufStart && "Invalid call to flush_nonempty."); size_t Length = OutBufCur - OutBufStart; OutBufCur = OutBufStart; - write_impl(OutBufStart, Length); + flush_tied_then_write(OutBufStart, Length); } raw_ostream &raw_ostream::write(unsigned char C) { @@ -224,7 +224,7 @@ raw_ostream &raw_ostream::write(unsigned char C) { if (LLVM_UNLIKELY(OutBufCur >= OutBufEnd)) { if (LLVM_UNLIKELY(!OutBufStart)) { if (BufferMode == BufferKind::Unbuffered) { - write_impl(reinterpret_cast<char*>(&C), 1); + flush_tied_then_write(reinterpret_cast<char *>(&C), 1); return *this; } // Set up a buffer and start over. @@ -244,7 +244,7 @@ raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) { if (LLVM_UNLIKELY(size_t(OutBufEnd - OutBufCur) < Size)) { if (LLVM_UNLIKELY(!OutBufStart)) { if (BufferMode == BufferKind::Unbuffered) { - write_impl(Ptr, Size); + flush_tied_then_write(Ptr, Size); return *this; } // Set up a buffer and start over. @@ -260,7 +260,7 @@ raw_ostream &raw_ostream::write(const char *Ptr, size_t Size) { if (LLVM_UNLIKELY(OutBufCur == OutBufStart)) { assert(NumBytes != 0 && "undefined behavior"); size_t BytesToWrite = Size - (Size % NumBytes); - write_impl(Ptr, BytesToWrite); + flush_tied_then_write(Ptr, BytesToWrite); size_t BytesRemaining = Size - BytesToWrite; if (BytesRemaining > size_t(OutBufEnd - OutBufCur)) { // Too much left over to copy into our buffer. @@ -301,6 +301,12 @@ void raw_ostream::copy_to_buffer(const char *Ptr, size_t Size) { OutBufCur += Size; } +void raw_ostream::flush_tied_then_write(const char *Ptr, size_t Size) { + if (TiedStream) + TiedStream->flush(); + write_impl(Ptr, Size); +} + // Formatted output. raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) { // If we have more than a few bytes left in our output buffer, try @@ -343,36 +349,33 @@ raw_ostream &raw_ostream::operator<<(const format_object_base &Fmt) { } raw_ostream &raw_ostream::operator<<(const formatv_object_base &Obj) { - SmallString<128> S; Obj.format(*this); return *this; } raw_ostream &raw_ostream::operator<<(const FormattedString &FS) { - if (FS.Str.size() >= FS.Width || FS.Justify == FormattedString::JustifyNone) { - this->operator<<(FS.Str); - return *this; - } - const size_t Difference = FS.Width - FS.Str.size(); - switch (FS.Justify) { - case FormattedString::JustifyLeft: - this->operator<<(FS.Str); - this->indent(Difference); - break; - case FormattedString::JustifyRight: - this->indent(Difference); - this->operator<<(FS.Str); - break; - case FormattedString::JustifyCenter: { - int PadAmount = Difference / 2; - this->indent(PadAmount); - this->operator<<(FS.Str); - this->indent(Difference - PadAmount); - break; - } - default: - llvm_unreachable("Bad Justification"); + unsigned LeftIndent = 0; + unsigned RightIndent = 0; + const ssize_t Difference = FS.Width - FS.Str.size(); + if (Difference > 0) { + switch (FS.Justify) { + case FormattedString::JustifyNone: + break; + case FormattedString::JustifyLeft: + RightIndent = Difference; + break; + case FormattedString::JustifyRight: + LeftIndent = Difference; + break; + case FormattedString::JustifyCenter: + LeftIndent = Difference / 2; + RightIndent = Difference - LeftIndent; + break; + } } + indent(LeftIndent); + (*this) << FS.Str; + indent(RightIndent); return *this; } @@ -502,6 +505,53 @@ raw_ostream &raw_ostream::write_zeros(unsigned NumZeros) { return write_padding<'\0'>(*this, NumZeros); } +bool raw_ostream::prepare_colors() { + // Colors were explicitly disabled. + if (!ColorEnabled) + return false; + + // Colors require changing the terminal but this stream is not going to a + // terminal. + if (sys::Process::ColorNeedsFlush() && !is_displayed()) + return false; + + if (sys::Process::ColorNeedsFlush()) + flush(); + + return true; +} + +raw_ostream &raw_ostream::changeColor(enum Colors colors, bool bold, bool bg) { + if (!prepare_colors()) + return *this; + + const char *colorcode = + (colors == SAVEDCOLOR) + ? sys::Process::OutputBold(bg) + : sys::Process::OutputColor(static_cast<char>(colors), bold, bg); + if (colorcode) + write(colorcode, strlen(colorcode)); + return *this; +} + +raw_ostream &raw_ostream::resetColor() { + if (!prepare_colors()) + return *this; + + if (const char *colorcode = sys::Process::ResetColor()) + write(colorcode, strlen(colorcode)); + return *this; +} + +raw_ostream &raw_ostream::reverseColor() { + if (!prepare_colors()) + return *this; + + if (const char *colorcode = sys::Process::OutputReverse()) + write(colorcode, strlen(colorcode)); + return *this; +} + void raw_ostream::anchor() {} //===----------------------------------------------------------------------===// @@ -577,6 +627,8 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered) return; } + enable_colors(true); + // Do not attempt to close stdout or stderr. We used to try to maintain the // property that tools that support writing file to stdout should not also // write informational output to stdout, but in practice we were never able to @@ -792,7 +844,7 @@ size_t raw_fd_ostream::preferred_buffer_size() const { // If this is a terminal, don't use buffering. Line buffering // would be a more traditional thing to do, but it's not worth // the complexity. - if (S_ISCHR(statbuf.st_mode) && isatty(FD)) + if (S_ISCHR(statbuf.st_mode) && is_displayed()) return 0; // Return the preferred block size. return statbuf.st_blksize; @@ -801,58 +853,6 @@ size_t raw_fd_ostream::preferred_buffer_size() const { #endif } -raw_ostream &raw_fd_ostream::changeColor(enum Colors colors, bool bold, - bool bg) { - if (!ColorEnabled) - return *this; - - if (sys::Process::ColorNeedsFlush()) - flush(); - const char *colorcode = - (colors == SAVEDCOLOR) - ? sys::Process::OutputBold(bg) - : sys::Process::OutputColor(static_cast<char>(colors), bold, bg); - if (colorcode) { - size_t len = strlen(colorcode); - write(colorcode, len); - // don't account colors towards output characters - pos -= len; - } - return *this; -} - -raw_ostream &raw_fd_ostream::resetColor() { - if (!ColorEnabled) - return *this; - - if (sys::Process::ColorNeedsFlush()) - flush(); - const char *colorcode = sys::Process::ResetColor(); - if (colorcode) { - size_t len = strlen(colorcode); - write(colorcode, len); - // don't account colors towards output characters - pos -= len; - } - return *this; -} - -raw_ostream &raw_fd_ostream::reverseColor() { - if (!ColorEnabled) - return *this; - - if (sys::Process::ColorNeedsFlush()) - flush(); - const char *colorcode = sys::Process::OutputReverse(); - if (colorcode) { - size_t len = strlen(colorcode); - write(colorcode, len); - // don't account colors towards output characters - pos -= len; - } - return *this; -} - bool raw_fd_ostream::is_displayed() const { return sys::Process::FileDescriptorIsDisplayed(FD); } @@ -867,9 +867,7 @@ void raw_fd_ostream::anchor() {} // outs(), errs(), nulls() //===----------------------------------------------------------------------===// -/// outs() - This returns a reference to a raw_ostream for standard output. -/// Use it like: outs() << "foo" << "bar"; -raw_ostream &llvm::outs() { +raw_fd_ostream &llvm::outs() { // Set buffer settings to model stdout behavior. std::error_code EC; static raw_fd_ostream S("-", EC, sys::fs::OF_None); @@ -877,10 +875,8 @@ raw_ostream &llvm::outs() { return S; } -/// errs() - This returns a reference to a raw_ostream for standard error. -/// Use it like: errs() << "foo" << "bar"; -raw_ostream &llvm::errs() { - // Set standard error to be unbuffered by default. +raw_fd_ostream &llvm::errs() { + // Set standard error to be unbuffered and tied to outs() by default. static raw_fd_ostream S(STDERR_FILENO, false, true); return S; } |