diff options
Diffstat (limited to 'lib/Support')
46 files changed, 6214 insertions, 1529 deletions
diff --git a/lib/Support/AArch64TargetParser.cpp b/lib/Support/AArch64TargetParser.cpp new file mode 100644 index 000000000000..e897137df680 --- /dev/null +++ b/lib/Support/AArch64TargetParser.cpp @@ -0,0 +1,206 @@ +//===-- AArch64TargetParser - Parser for AArch64 features -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a target parser to recognise AArch64 hardware features +// such as FPU/CPU/ARCH and extension names. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/AArch64TargetParser.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include <cctype> + +using namespace llvm; + +static unsigned checkArchVersion(llvm::StringRef Arch) { + if (Arch.size() >= 2 && Arch[0] == 'v' && std::isdigit(Arch[1])) + return (Arch[1] - 48); + return 0; +} + +unsigned AArch64::getDefaultFPU(StringRef CPU, AArch64::ArchKind AK) { + if (CPU == "generic") + return AArch64ARCHNames[static_cast<unsigned>(AK)].DefaultFPU; + + return StringSwitch<unsigned>(CPU) +#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ + .Case(NAME, ARM::DEFAULT_FPU) +#include "../../include/llvm/Support/AArch64TargetParser.def" + .Default(ARM::FK_INVALID); +} + +unsigned AArch64::getDefaultExtensions(StringRef CPU, AArch64::ArchKind AK) { + if (CPU == "generic") + return AArch64ARCHNames[static_cast<unsigned>(AK)].ArchBaseExtensions; + + return StringSwitch<unsigned>(CPU) +#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ + .Case(NAME, AArch64ARCHNames[static_cast<unsigned>(ArchKind::ID)] \ + .ArchBaseExtensions | \ + DEFAULT_EXT) +#include "../../include/llvm/Support/AArch64TargetParser.def" + .Default(AArch64::AEK_INVALID); +} + +AArch64::ArchKind AArch64::getCPUArchKind(StringRef CPU) { + if (CPU == "generic") + return ArchKind::ARMV8A; + + return StringSwitch<AArch64::ArchKind>(CPU) +#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ + .Case(NAME, ArchKind::ID) +#include "../../include/llvm/Support/AArch64TargetParser.def" + .Default(ArchKind::INVALID); +} + +bool AArch64::getExtensionFeatures(unsigned Extensions, + std::vector<StringRef> &Features) { + if (Extensions == AArch64::AEK_INVALID) + return false; + + if (Extensions & AEK_FP) + Features.push_back("+fp-armv8"); + if (Extensions & AEK_SIMD) + Features.push_back("+neon"); + if (Extensions & AEK_CRC) + Features.push_back("+crc"); + if (Extensions & AEK_CRYPTO) + Features.push_back("+crypto"); + if (Extensions & AEK_DOTPROD) + Features.push_back("+dotprod"); + if (Extensions & AEK_FP16FML) + Features.push_back("+fp16fml"); + if (Extensions & AEK_FP16) + Features.push_back("+fullfp16"); + if (Extensions & AEK_PROFILE) + Features.push_back("+spe"); + if (Extensions & AEK_RAS) + Features.push_back("+ras"); + if (Extensions & AEK_LSE) + Features.push_back("+lse"); + if (Extensions & AEK_RDM) + Features.push_back("+rdm"); + if (Extensions & AEK_SVE) + Features.push_back("+sve"); + if (Extensions & AEK_RCPC) + Features.push_back("+rcpc"); + + return true; +} + +bool AArch64::getArchFeatures(AArch64::ArchKind AK, + std::vector<StringRef> &Features) { + if (AK == ArchKind::ARMV8_1A) + Features.push_back("+v8.1a"); + if (AK == ArchKind::ARMV8_2A) + Features.push_back("+v8.2a"); + if (AK == ArchKind::ARMV8_3A) + Features.push_back("+v8.3a"); + if (AK == ArchKind::ARMV8_4A) + Features.push_back("+v8.4a"); + if (AK == ArchKind::ARMV8_5A) + Features.push_back("+v8.5a"); + + return AK != ArchKind::INVALID; +} + +StringRef AArch64::getArchName(AArch64::ArchKind AK) { + return AArch64ARCHNames[static_cast<unsigned>(AK)].getName(); +} + +StringRef AArch64::getCPUAttr(AArch64::ArchKind AK) { + return AArch64ARCHNames[static_cast<unsigned>(AK)].getCPUAttr(); +} + +StringRef AArch64::getSubArch(AArch64::ArchKind AK) { + return AArch64ARCHNames[static_cast<unsigned>(AK)].getSubArch(); +} + +unsigned AArch64::getArchAttr(AArch64::ArchKind AK) { + return AArch64ARCHNames[static_cast<unsigned>(AK)].ArchAttr; +} + +StringRef AArch64::getArchExtName(unsigned ArchExtKind) { + for (const auto &AE : AArch64ARCHExtNames) + if (ArchExtKind == AE.ID) + return AE.getName(); + return StringRef(); +} + +StringRef AArch64::getArchExtFeature(StringRef ArchExt) { + if (ArchExt.startswith("no")) { + StringRef ArchExtBase(ArchExt.substr(2)); + for (const auto &AE : AArch64ARCHExtNames) { + if (AE.NegFeature && ArchExtBase == AE.getName()) + return StringRef(AE.NegFeature); + } + } + + for (const auto &AE : AArch64ARCHExtNames) + if (AE.Feature && ArchExt == AE.getName()) + return StringRef(AE.Feature); + return StringRef(); +} + +StringRef AArch64::getDefaultCPU(StringRef Arch) { + ArchKind AK = parseArch(Arch); + if (AK == ArchKind::INVALID) + return StringRef(); + + // Look for multiple AKs to find the default for pair AK+Name. + for (const auto &CPU : AArch64CPUNames) + if (CPU.ArchID == AK && CPU.Default) + return CPU.getName(); + + // If we can't find a default then target the architecture instead + return "generic"; +} + +void AArch64::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) { + for (const auto &Arch : AArch64CPUNames) { + if (Arch.ArchID != ArchKind::INVALID) + Values.push_back(Arch.getName()); + } +} + +bool AArch64::isX18ReservedByDefault(const Triple &TT) { + return TT.isAndroid() || TT.isOSDarwin() || TT.isOSFuchsia() || + TT.isOSWindows(); +} + +// Allows partial match, ex. "v8a" matches "armv8a". +AArch64::ArchKind AArch64::parseArch(StringRef Arch) { + Arch = ARM::getCanonicalArchName(Arch); + if (checkArchVersion(Arch) < 8) + return ArchKind::INVALID; + + StringRef Syn = ARM::getArchSynonym(Arch); + for (const auto A : AArch64ARCHNames) { + if (A.getName().endswith(Syn)) + return A.ID; + } + return ArchKind::INVALID; +} + +AArch64::ArchExtKind AArch64::parseArchExt(StringRef ArchExt) { + for (const auto A : AArch64ARCHExtNames) { + if (ArchExt == A.getName()) + return static_cast<ArchExtKind>(A.ID); + } + return AArch64::AEK_INVALID; +} + +AArch64::ArchKind AArch64::parseCPUArch(StringRef CPU) { + for (const auto C : AArch64CPUNames) { + if (CPU == C.getName()) + return C.ArchID; + } + return ArchKind::INVALID; +} diff --git a/lib/Support/APInt.cpp b/lib/Support/APInt.cpp index 1fae0e9b8d6d..a5f4f98c489a 100644 --- a/lib/Support/APInt.cpp +++ b/lib/Support/APInt.cpp @@ -16,8 +16,10 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/bit.h" #include "llvm/Config/llvm-config.h" #include "llvm/Support/Debug.h" #include "llvm/Support/ErrorHandling.h" @@ -78,7 +80,7 @@ void APInt::initSlowCase(uint64_t val, bool isSigned) { U.pVal[0] = val; if (isSigned && int64_t(val) < 0) for (unsigned i = 1; i < getNumWords(); ++i) - U.pVal[i] = WORD_MAX; + U.pVal[i] = WORDTYPE_MAX; clearUnusedBits(); } @@ -304,13 +306,13 @@ void APInt::setBitsSlowCase(unsigned loBit, unsigned hiBit) { unsigned hiWord = whichWord(hiBit); // Create an initial mask for the low word with zeros below loBit. - uint64_t loMask = WORD_MAX << whichBit(loBit); + uint64_t loMask = WORDTYPE_MAX << whichBit(loBit); // If hiBit is not aligned, we need a high mask. unsigned hiShiftAmt = whichBit(hiBit); if (hiShiftAmt != 0) { // Create a high mask with zeros above hiBit. - uint64_t hiMask = WORD_MAX >> (APINT_BITS_PER_WORD - hiShiftAmt); + uint64_t hiMask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - hiShiftAmt); // If loWord and hiWord are equal, then we combine the masks. Otherwise, // set the bits in hiWord. if (hiWord == loWord) @@ -323,7 +325,7 @@ void APInt::setBitsSlowCase(unsigned loBit, unsigned hiBit) { // Fill any words between loWord and hiWord with all ones. for (unsigned word = loWord + 1; word < hiWord; ++word) - U.pVal[word] = WORD_MAX; + U.pVal[word] = WORDTYPE_MAX; } /// Toggle every bit to its opposite value. @@ -354,7 +356,7 @@ void APInt::insertBits(const APInt &subBits, unsigned bitPosition) { // Single word result can be done as a direct bitmask. if (isSingleWord()) { - uint64_t mask = WORD_MAX >> (APINT_BITS_PER_WORD - subBitWidth); + uint64_t mask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - subBitWidth); U.VAL &= ~(mask << bitPosition); U.VAL |= (subBits.U.VAL << bitPosition); return; @@ -366,7 +368,7 @@ void APInt::insertBits(const APInt &subBits, unsigned bitPosition) { // Insertion within a single word can be done as a direct bitmask. if (loWord == hi1Word) { - uint64_t mask = WORD_MAX >> (APINT_BITS_PER_WORD - subBitWidth); + uint64_t mask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - subBitWidth); U.pVal[loWord] &= ~(mask << loBit); U.pVal[loWord] |= (subBits.U.VAL << loBit); return; @@ -382,7 +384,7 @@ void APInt::insertBits(const APInt &subBits, unsigned bitPosition) { // Mask+insert remaining bits. unsigned remainingBits = subBitWidth % APINT_BITS_PER_WORD; if (remainingBits != 0) { - uint64_t mask = WORD_MAX >> (APINT_BITS_PER_WORD - remainingBits); + uint64_t mask = WORDTYPE_MAX >> (APINT_BITS_PER_WORD - remainingBits); U.pVal[hi1Word] &= ~mask; U.pVal[hi1Word] |= subBits.getWord(subBitWidth - 1); } @@ -558,7 +560,7 @@ unsigned APInt::countLeadingOnesSlowCase() const { unsigned Count = llvm::countLeadingOnes(U.pVal[i] << shift); if (Count == highWordBits) { for (i--; i >= 0; --i) { - if (U.pVal[i] == WORD_MAX) + if (U.pVal[i] == WORDTYPE_MAX) Count += APINT_BITS_PER_WORD; else { Count += llvm::countLeadingOnes(U.pVal[i]); @@ -582,7 +584,7 @@ unsigned APInt::countTrailingZerosSlowCase() const { unsigned APInt::countTrailingOnesSlowCase() const { unsigned Count = 0; unsigned i = 0; - for (; i < getNumWords() && U.pVal[i] == WORD_MAX; ++i) + for (; i < getNumWords() && U.pVal[i] == WORDTYPE_MAX; ++i) Count += APINT_BITS_PER_WORD; if (i < getNumWords()) Count += llvm::countTrailingOnes(U.pVal[i]); @@ -711,24 +713,20 @@ APInt llvm::APIntOps::GreatestCommonDivisor(APInt A, APInt B) { } APInt llvm::APIntOps::RoundDoubleToAPInt(double Double, unsigned width) { - union { - double D; - uint64_t I; - } T; - T.D = Double; + uint64_t I = bit_cast<uint64_t>(Double); // Get the sign bit from the highest order bit - bool isNeg = T.I >> 63; + bool isNeg = I >> 63; // Get the 11-bit exponent and adjust for the 1023 bit bias - int64_t exp = ((T.I >> 52) & 0x7ff) - 1023; + int64_t exp = ((I >> 52) & 0x7ff) - 1023; // If the exponent is negative, the value is < 0 so just return 0. if (exp < 0) return APInt(width, 0u); // Extract the mantissa by clearing the top 12 bits (sign + exponent). - uint64_t mantissa = (T.I & (~0ULL >> 12)) | 1ULL << 52; + uint64_t mantissa = (I & (~0ULL >> 12)) | 1ULL << 52; // If the exponent doesn't shift all bits out of the mantissa if (exp < 52) @@ -805,12 +803,8 @@ double APInt::roundToDouble(bool isSigned) const { // The leading bit of mantissa is implicit, so get rid of it. uint64_t sign = isNeg ? (1ULL << (APINT_BITS_PER_WORD - 1)) : 0; - union { - double D; - uint64_t I; - } T; - T.I = sign | (exp << 52) | mantissa; - return T.D; + uint64_t I = sign | (exp << 52) | mantissa; + return bit_cast<double>(I); } // Truncate to new width. @@ -1253,20 +1247,18 @@ static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r, // The DEBUG macros here tend to be spam in the debug output if you're not // debugging this code. Disable them unless KNUTH_DEBUG is defined. -#pragma push_macro("LLVM_DEBUG") -#ifndef KNUTH_DEBUG -#undef LLVM_DEBUG -#define LLVM_DEBUG(X) \ - do { \ - } while (false) +#ifdef KNUTH_DEBUG +#define DEBUG_KNUTH(X) LLVM_DEBUG(X) +#else +#define DEBUG_KNUTH(X) do {} while(false) #endif - LLVM_DEBUG(dbgs() << "KnuthDiv: m=" << m << " n=" << n << '\n'); - LLVM_DEBUG(dbgs() << "KnuthDiv: original:"); - LLVM_DEBUG(for (int i = m + n; i >= 0; i--) dbgs() << " " << u[i]); - LLVM_DEBUG(dbgs() << " by"); - LLVM_DEBUG(for (int i = n; i > 0; i--) dbgs() << " " << v[i - 1]); - LLVM_DEBUG(dbgs() << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: m=" << m << " n=" << n << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: original:"); + DEBUG_KNUTH(for (int i = m + n; i >= 0; i--) dbgs() << " " << u[i]); + DEBUG_KNUTH(dbgs() << " by"); + DEBUG_KNUTH(for (int i = n; i > 0; i--) dbgs() << " " << v[i - 1]); + DEBUG_KNUTH(dbgs() << '\n'); // D1. [Normalize.] Set d = b / (v[n-1] + 1) and multiply all the digits of // u and v by d. Note that we have taken Knuth's advice here to use a power // of 2 value for d such that d * v[n-1] >= b/2 (b is the base). A power of @@ -1292,16 +1284,16 @@ static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r, } u[m+n] = u_carry; - LLVM_DEBUG(dbgs() << "KnuthDiv: normal:"); - LLVM_DEBUG(for (int i = m + n; i >= 0; i--) dbgs() << " " << u[i]); - LLVM_DEBUG(dbgs() << " by"); - LLVM_DEBUG(for (int i = n; i > 0; i--) dbgs() << " " << v[i - 1]); - LLVM_DEBUG(dbgs() << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: normal:"); + DEBUG_KNUTH(for (int i = m + n; i >= 0; i--) dbgs() << " " << u[i]); + DEBUG_KNUTH(dbgs() << " by"); + DEBUG_KNUTH(for (int i = n; i > 0; i--) dbgs() << " " << v[i - 1]); + DEBUG_KNUTH(dbgs() << '\n'); // D2. [Initialize j.] Set j to m. This is the loop counter over the places. int j = m; do { - LLVM_DEBUG(dbgs() << "KnuthDiv: quotient digit #" << j << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: quotient digit #" << j << '\n'); // D3. [Calculate q'.]. // Set qp = (u[j+n]*b + u[j+n-1]) / v[n-1]. (qp=qprime=q') // Set rp = (u[j+n]*b + u[j+n-1]) % v[n-1]. (rp=rprime=r') @@ -1311,7 +1303,7 @@ static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r, // value qp is one too large, and it eliminates all cases where qp is two // too large. uint64_t dividend = Make_64(u[j+n], u[j+n-1]); - LLVM_DEBUG(dbgs() << "KnuthDiv: dividend == " << dividend << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: dividend == " << dividend << '\n'); uint64_t qp = dividend / v[n-1]; uint64_t rp = dividend % v[n-1]; if (qp == b || qp*v[n-2] > b*rp + u[j+n-2]) { @@ -1320,7 +1312,7 @@ static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r, if (rp < b && (qp == b || qp*v[n-2] > b*rp + u[j+n-2])) qp--; } - LLVM_DEBUG(dbgs() << "KnuthDiv: qp == " << qp << ", rp == " << rp << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: qp == " << qp << ", rp == " << rp << '\n'); // D4. [Multiply and subtract.] Replace (u[j+n]u[j+n-1]...u[j]) with // (u[j+n]u[j+n-1]..u[j]) - qp * (v[n-1]...v[1]v[0]). This computation @@ -1336,15 +1328,15 @@ static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r, int64_t subres = int64_t(u[j+i]) - borrow - Lo_32(p); u[j+i] = Lo_32(subres); borrow = Hi_32(p) - Hi_32(subres); - LLVM_DEBUG(dbgs() << "KnuthDiv: u[j+i] = " << u[j + i] + DEBUG_KNUTH(dbgs() << "KnuthDiv: u[j+i] = " << u[j + i] << ", borrow = " << borrow << '\n'); } bool isNeg = u[j+n] < borrow; u[j+n] -= Lo_32(borrow); - LLVM_DEBUG(dbgs() << "KnuthDiv: after subtraction:"); - LLVM_DEBUG(for (int i = m + n; i >= 0; i--) dbgs() << " " << u[i]); - LLVM_DEBUG(dbgs() << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: after subtraction:"); + DEBUG_KNUTH(for (int i = m + n; i >= 0; i--) dbgs() << " " << u[i]); + DEBUG_KNUTH(dbgs() << '\n'); // D5. [Test remainder.] Set q[j] = qp. If the result of step D4 was // negative, go to step D6; otherwise go on to step D7. @@ -1365,16 +1357,16 @@ static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r, } u[j+n] += carry; } - LLVM_DEBUG(dbgs() << "KnuthDiv: after correction:"); - LLVM_DEBUG(for (int i = m + n; i >= 0; i--) dbgs() << " " << u[i]); - LLVM_DEBUG(dbgs() << "\nKnuthDiv: digit result = " << q[j] << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: after correction:"); + DEBUG_KNUTH(for (int i = m + n; i >= 0; i--) dbgs() << " " << u[i]); + DEBUG_KNUTH(dbgs() << "\nKnuthDiv: digit result = " << q[j] << '\n'); // D7. [Loop on j.] Decrease j by one. Now if j >= 0, go back to D3. } while (--j >= 0); - LLVM_DEBUG(dbgs() << "KnuthDiv: quotient:"); - LLVM_DEBUG(for (int i = m; i >= 0; i--) dbgs() << " " << q[i]); - LLVM_DEBUG(dbgs() << '\n'); + DEBUG_KNUTH(dbgs() << "KnuthDiv: quotient:"); + DEBUG_KNUTH(for (int i = m; i >= 0; i--) dbgs() << " " << q[i]); + DEBUG_KNUTH(dbgs() << '\n'); // D8. [Unnormalize]. Now q[...] is the desired quotient, and the desired // remainder may be obtained by dividing u[...] by d. If r is non-null we @@ -1385,23 +1377,21 @@ static void KnuthDiv(uint32_t *u, uint32_t *v, uint32_t *q, uint32_t* r, // shift right here. if (shift) { uint32_t carry = 0; - LLVM_DEBUG(dbgs() << "KnuthDiv: remainder:"); + DEBUG_KNUTH(dbgs() << "KnuthDiv: remainder:"); for (int i = n-1; i >= 0; i--) { r[i] = (u[i] >> shift) | carry; carry = u[i] << (32 - shift); - LLVM_DEBUG(dbgs() << " " << r[i]); + DEBUG_KNUTH(dbgs() << " " << r[i]); } } else { for (int i = n-1; i >= 0; i--) { r[i] = u[i]; - LLVM_DEBUG(dbgs() << " " << r[i]); + DEBUG_KNUTH(dbgs() << " " << r[i]); } } - LLVM_DEBUG(dbgs() << '\n'); + DEBUG_KNUTH(dbgs() << '\n'); } - LLVM_DEBUG(dbgs() << '\n'); - -#pragma pop_macro("LLVM_DEBUG") + DEBUG_KNUTH(dbgs() << '\n'); } void APInt::divide(const WordType *LHS, unsigned lhsWords, const WordType *RHS, @@ -1957,7 +1947,43 @@ APInt APInt::ushl_ov(const APInt &ShAmt, bool &Overflow) const { return *this << ShAmt; } +APInt APInt::sadd_sat(const APInt &RHS) const { + bool Overflow; + APInt Res = sadd_ov(RHS, Overflow); + if (!Overflow) + return Res; + + return isNegative() ? APInt::getSignedMinValue(BitWidth) + : APInt::getSignedMaxValue(BitWidth); +} + +APInt APInt::uadd_sat(const APInt &RHS) const { + bool Overflow; + APInt Res = uadd_ov(RHS, Overflow); + if (!Overflow) + return Res; + + return APInt::getMaxValue(BitWidth); +} + +APInt APInt::ssub_sat(const APInt &RHS) const { + bool Overflow; + APInt Res = ssub_ov(RHS, Overflow); + if (!Overflow) + return Res; + return isNegative() ? APInt::getSignedMinValue(BitWidth) + : APInt::getSignedMaxValue(BitWidth); +} + +APInt APInt::usub_sat(const APInt &RHS) const { + bool Overflow; + APInt Res = usub_ov(RHS, Overflow); + if (!Overflow) + return Res; + + return APInt(BitWidth, 0); +} void APInt::fromString(unsigned numbits, StringRef str, uint8_t radix) { @@ -2707,3 +2733,193 @@ APInt llvm::APIntOps::RoundingSDiv(const APInt &A, const APInt &B, } llvm_unreachable("Unknown APInt::Rounding enum"); } + +Optional<APInt> +llvm::APIntOps::SolveQuadraticEquationWrap(APInt A, APInt B, APInt C, + unsigned RangeWidth) { + unsigned CoeffWidth = A.getBitWidth(); + assert(CoeffWidth == B.getBitWidth() && CoeffWidth == C.getBitWidth()); + assert(RangeWidth <= CoeffWidth && + "Value range width should be less than coefficient width"); + assert(RangeWidth > 1 && "Value range bit width should be > 1"); + + LLVM_DEBUG(dbgs() << __func__ << ": solving " << A << "x^2 + " << B + << "x + " << C << ", rw:" << RangeWidth << '\n'); + + // Identify 0 as a (non)solution immediately. + if (C.sextOrTrunc(RangeWidth).isNullValue() ) { + LLVM_DEBUG(dbgs() << __func__ << ": zero solution\n"); + return APInt(CoeffWidth, 0); + } + + // The result of APInt arithmetic has the same bit width as the operands, + // so it can actually lose high bits. A product of two n-bit integers needs + // 2n-1 bits to represent the full value. + // The operation done below (on quadratic coefficients) that can produce + // the largest value is the evaluation of the equation during bisection, + // which needs 3 times the bitwidth of the coefficient, so the total number + // of required bits is 3n. + // + // The purpose of this extension is to simulate the set Z of all integers, + // where n+1 > n for all n in Z. In Z it makes sense to talk about positive + // and negative numbers (not so much in a modulo arithmetic). The method + // used to solve the equation is based on the standard formula for real + // numbers, and uses the concepts of "positive" and "negative" with their + // usual meanings. + CoeffWidth *= 3; + A = A.sext(CoeffWidth); + B = B.sext(CoeffWidth); + C = C.sext(CoeffWidth); + + // Make A > 0 for simplicity. Negate cannot overflow at this point because + // the bit width has increased. + if (A.isNegative()) { + A.negate(); + B.negate(); + C.negate(); + } + + // Solving an equation q(x) = 0 with coefficients in modular arithmetic + // is really solving a set of equations q(x) = kR for k = 0, 1, 2, ..., + // and R = 2^BitWidth. + // Since we're trying not only to find exact solutions, but also values + // that "wrap around", such a set will always have a solution, i.e. an x + // that satisfies at least one of the equations, or such that |q(x)| + // exceeds kR, while |q(x-1)| for the same k does not. + // + // We need to find a value k, such that Ax^2 + Bx + C = kR will have a + // positive solution n (in the above sense), and also such that the n + // will be the least among all solutions corresponding to k = 0, 1, ... + // (more precisely, the least element in the set + // { n(k) | k is such that a solution n(k) exists }). + // + // Consider the parabola (over real numbers) that corresponds to the + // quadratic equation. Since A > 0, the arms of the parabola will point + // up. Picking different values of k will shift it up and down by R. + // + // We want to shift the parabola in such a way as to reduce the problem + // of solving q(x) = kR to solving shifted_q(x) = 0. + // (The interesting solutions are the ceilings of the real number + // solutions.) + APInt R = APInt::getOneBitSet(CoeffWidth, RangeWidth); + APInt TwoA = 2 * A; + APInt SqrB = B * B; + bool PickLow; + + auto RoundUp = [] (const APInt &V, const APInt &A) -> APInt { + assert(A.isStrictlyPositive()); + APInt T = V.abs().urem(A); + if (T.isNullValue()) + return V; + return V.isNegative() ? V+T : V+(A-T); + }; + + // The vertex of the parabola is at -B/2A, but since A > 0, it's negative + // iff B is positive. + if (B.isNonNegative()) { + // If B >= 0, the vertex it at a negative location (or at 0), so in + // order to have a non-negative solution we need to pick k that makes + // C-kR negative. To satisfy all the requirements for the solution + // that we are looking for, it needs to be closest to 0 of all k. + C = C.srem(R); + if (C.isStrictlyPositive()) + C -= R; + // Pick the greater solution. + PickLow = false; + } else { + // If B < 0, the vertex is at a positive location. For any solution + // to exist, the discriminant must be non-negative. This means that + // C-kR <= B^2/4A is a necessary condition for k, i.e. there is a + // lower bound on values of k: kR >= C - B^2/4A. + APInt LowkR = C - SqrB.udiv(2*TwoA); // udiv because all values > 0. + // Round LowkR up (towards +inf) to the nearest kR. + LowkR = RoundUp(LowkR, R); + + // If there exists k meeting the condition above, and such that + // C-kR > 0, there will be two positive real number solutions of + // q(x) = kR. Out of all such values of k, pick the one that makes + // C-kR closest to 0, (i.e. pick maximum k such that C-kR > 0). + // In other words, find maximum k such that LowkR <= kR < C. + if (C.sgt(LowkR)) { + // If LowkR < C, then such a k is guaranteed to exist because + // LowkR itself is a multiple of R. + C -= -RoundUp(-C, R); // C = C - RoundDown(C, R) + // Pick the smaller solution. + PickLow = true; + } else { + // If C-kR < 0 for all potential k's, it means that one solution + // will be negative, while the other will be positive. The positive + // solution will shift towards 0 if the parabola is moved up. + // Pick the kR closest to the lower bound (i.e. make C-kR closest + // to 0, or in other words, out of all parabolas that have solutions, + // pick the one that is the farthest "up"). + // Since LowkR is itself a multiple of R, simply take C-LowkR. + C -= LowkR; + // Pick the greater solution. + PickLow = false; + } + } + + LLVM_DEBUG(dbgs() << __func__ << ": updated coefficients " << A << "x^2 + " + << B << "x + " << C << ", rw:" << RangeWidth << '\n'); + + APInt D = SqrB - 4*A*C; + assert(D.isNonNegative() && "Negative discriminant"); + APInt SQ = D.sqrt(); + + APInt Q = SQ * SQ; + bool InexactSQ = Q != D; + // The calculated SQ may actually be greater than the exact (non-integer) + // value. If that's the case, decremement SQ to get a value that is lower. + if (Q.sgt(D)) + SQ -= 1; + + APInt X; + APInt Rem; + + // SQ is rounded down (i.e SQ * SQ <= D), so the roots may be inexact. + // When using the quadratic formula directly, the calculated low root + // may be greater than the exact one, since we would be subtracting SQ. + // To make sure that the calculated root is not greater than the exact + // one, subtract SQ+1 when calculating the low root (for inexact value + // of SQ). + if (PickLow) + APInt::sdivrem(-B - (SQ+InexactSQ), TwoA, X, Rem); + else + APInt::sdivrem(-B + SQ, TwoA, X, Rem); + + // The updated coefficients should be such that the (exact) solution is + // positive. Since APInt division rounds towards 0, the calculated one + // can be 0, but cannot be negative. + assert(X.isNonNegative() && "Solution should be non-negative"); + + if (!InexactSQ && Rem.isNullValue()) { + LLVM_DEBUG(dbgs() << __func__ << ": solution (root): " << X << '\n'); + return X; + } + + assert((SQ*SQ).sle(D) && "SQ = |_sqrt(D)_|, so SQ*SQ <= D"); + // The exact value of the square root of D should be between SQ and SQ+1. + // This implies that the solution should be between that corresponding to + // SQ (i.e. X) and that corresponding to SQ+1. + // + // The calculated X cannot be greater than the exact (real) solution. + // Actually it must be strictly less than the exact solution, while + // X+1 will be greater than or equal to it. + + APInt VX = (A*X + B)*X + C; + APInt VY = VX + TwoA*X + A + B; + bool SignChange = VX.isNegative() != VY.isNegative() || + VX.isNullValue() != VY.isNullValue(); + // If the sign did not change between X and X+1, X is not a valid solution. + // This could happen when the actual (exact) roots don't have an integer + // between them, so they would both be contained between X and X+1. + if (!SignChange) { + LLVM_DEBUG(dbgs() << __func__ << ": no valid solution\n"); + return None; + } + + X += 1; + LLVM_DEBUG(dbgs() << __func__ << ": solution (wrap): " << X << '\n'); + return X; +} diff --git a/lib/Support/ARMTargetParser.cpp b/lib/Support/ARMTargetParser.cpp new file mode 100644 index 000000000000..07294b0c09a3 --- /dev/null +++ b/lib/Support/ARMTargetParser.cpp @@ -0,0 +1,577 @@ +//===-- ARMTargetParser - Parser for ARM target features --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements a target parser to recognise ARM hardware features +// such as FPU/CPU/ARCH/extensions and specific support such as HWDIV. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ARMTargetParser.h" +#include "llvm/ADT/StringSwitch.h" +#include <cctype> + +using namespace llvm; + +static StringRef getHWDivSynonym(StringRef HWDiv) { + return StringSwitch<StringRef>(HWDiv) + .Case("thumb,arm", "arm,thumb") + .Default(HWDiv); +} + +// Allows partial match, ex. "v7a" matches "armv7a". +ARM::ArchKind ARM::parseArch(StringRef Arch) { + Arch = getCanonicalArchName(Arch); + StringRef Syn = getArchSynonym(Arch); + for (const auto A : ARCHNames) { + if (A.getName().endswith(Syn)) + return A.ID; + } + return ArchKind::INVALID; +} + +// Version number (ex. v7 = 7). +unsigned ARM::parseArchVersion(StringRef Arch) { + Arch = getCanonicalArchName(Arch); + switch (parseArch(Arch)) { + case ArchKind::ARMV2: + case ArchKind::ARMV2A: + return 2; + case ArchKind::ARMV3: + case ArchKind::ARMV3M: + return 3; + case ArchKind::ARMV4: + case ArchKind::ARMV4T: + return 4; + case ArchKind::ARMV5T: + case ArchKind::ARMV5TE: + case ArchKind::IWMMXT: + case ArchKind::IWMMXT2: + case ArchKind::XSCALE: + case ArchKind::ARMV5TEJ: + return 5; + case ArchKind::ARMV6: + case ArchKind::ARMV6K: + case ArchKind::ARMV6T2: + case ArchKind::ARMV6KZ: + case ArchKind::ARMV6M: + return 6; + case ArchKind::ARMV7A: + case ArchKind::ARMV7VE: + case ArchKind::ARMV7R: + case ArchKind::ARMV7M: + case ArchKind::ARMV7S: + case ArchKind::ARMV7EM: + case ArchKind::ARMV7K: + return 7; + case ArchKind::ARMV8A: + case ArchKind::ARMV8_1A: + case ArchKind::ARMV8_2A: + case ArchKind::ARMV8_3A: + case ArchKind::ARMV8_4A: + case ArchKind::ARMV8_5A: + case ArchKind::ARMV8R: + case ArchKind::ARMV8MBaseline: + case ArchKind::ARMV8MMainline: + return 8; + case ArchKind::INVALID: + return 0; + } + llvm_unreachable("Unhandled architecture"); +} + +// Profile A/R/M +ARM::ProfileKind ARM::parseArchProfile(StringRef Arch) { + Arch = getCanonicalArchName(Arch); + switch (parseArch(Arch)) { + case ArchKind::ARMV6M: + case ArchKind::ARMV7M: + case ArchKind::ARMV7EM: + case ArchKind::ARMV8MMainline: + case ArchKind::ARMV8MBaseline: + return ProfileKind::M; + case ArchKind::ARMV7R: + case ArchKind::ARMV8R: + return ProfileKind::R; + case ArchKind::ARMV7A: + case ArchKind::ARMV7VE: + case ArchKind::ARMV7K: + case ArchKind::ARMV8A: + case ArchKind::ARMV8_1A: + case ArchKind::ARMV8_2A: + case ArchKind::ARMV8_3A: + case ArchKind::ARMV8_4A: + case ArchKind::ARMV8_5A: + return ProfileKind::A; + case ArchKind::ARMV2: + case ArchKind::ARMV2A: + case ArchKind::ARMV3: + case ArchKind::ARMV3M: + case ArchKind::ARMV4: + case ArchKind::ARMV4T: + case ArchKind::ARMV5T: + case ArchKind::ARMV5TE: + case ArchKind::ARMV5TEJ: + case ArchKind::ARMV6: + case ArchKind::ARMV6K: + case ArchKind::ARMV6T2: + case ArchKind::ARMV6KZ: + case ArchKind::ARMV7S: + case ArchKind::IWMMXT: + case ArchKind::IWMMXT2: + case ArchKind::XSCALE: + case ArchKind::INVALID: + return ProfileKind::INVALID; + } + llvm_unreachable("Unhandled architecture"); +} + +StringRef ARM::getArchSynonym(StringRef Arch) { + return StringSwitch<StringRef>(Arch) + .Case("v5", "v5t") + .Case("v5e", "v5te") + .Case("v6j", "v6") + .Case("v6hl", "v6k") + .Cases("v6m", "v6sm", "v6s-m", "v6-m") + .Cases("v6z", "v6zk", "v6kz") + .Cases("v7", "v7a", "v7hl", "v7l", "v7-a") + .Case("v7r", "v7-r") + .Case("v7m", "v7-m") + .Case("v7em", "v7e-m") + .Cases("v8", "v8a", "v8l", "aarch64", "arm64", "v8-a") + .Case("v8.1a", "v8.1-a") + .Case("v8.2a", "v8.2-a") + .Case("v8.3a", "v8.3-a") + .Case("v8.4a", "v8.4-a") + .Case("v8.5a", "v8.5-a") + .Case("v8r", "v8-r") + .Case("v8m.base", "v8-m.base") + .Case("v8m.main", "v8-m.main") + .Default(Arch); +} + +bool ARM::getFPUFeatures(unsigned FPUKind, std::vector<StringRef> &Features) { + + if (FPUKind >= FK_LAST || FPUKind == FK_INVALID) + return false; + + // fp-only-sp and d16 subtarget features are independent of each other, so we + // must enable/disable both. + switch (FPUNames[FPUKind].Restriction) { + case FPURestriction::SP_D16: + Features.push_back("+fp-only-sp"); + Features.push_back("+d16"); + break; + case FPURestriction::D16: + Features.push_back("-fp-only-sp"); + Features.push_back("+d16"); + break; + case FPURestriction::None: + Features.push_back("-fp-only-sp"); + Features.push_back("-d16"); + break; + } + + // FPU version subtarget features are inclusive of lower-numbered ones, so + // enable the one corresponding to this version and disable all that are + // higher. We also have to make sure to disable fp16 when vfp4 is disabled, + // as +vfp4 implies +fp16 but -vfp4 does not imply -fp16. + switch (FPUNames[FPUKind].FPUVer) { + case FPUVersion::VFPV5: + Features.push_back("+fp-armv8"); + break; + case FPUVersion::VFPV4: + Features.push_back("+vfp4"); + Features.push_back("-fp-armv8"); + break; + case FPUVersion::VFPV3_FP16: + Features.push_back("+vfp3"); + Features.push_back("+fp16"); + Features.push_back("-vfp4"); + Features.push_back("-fp-armv8"); + break; + case FPUVersion::VFPV3: + Features.push_back("+vfp3"); + Features.push_back("-fp16"); + Features.push_back("-vfp4"); + Features.push_back("-fp-armv8"); + break; + case FPUVersion::VFPV2: + Features.push_back("+vfp2"); + Features.push_back("-vfp3"); + Features.push_back("-fp16"); + Features.push_back("-vfp4"); + Features.push_back("-fp-armv8"); + break; + case FPUVersion::NONE: + Features.push_back("-vfp2"); + Features.push_back("-vfp3"); + Features.push_back("-fp16"); + Features.push_back("-vfp4"); + Features.push_back("-fp-armv8"); + break; + } + + // crypto includes neon, so we handle this similarly to FPU version. + switch (FPUNames[FPUKind].NeonSupport) { + case NeonSupportLevel::Crypto: + Features.push_back("+neon"); + Features.push_back("+crypto"); + break; + case NeonSupportLevel::Neon: + Features.push_back("+neon"); + Features.push_back("-crypto"); + break; + case NeonSupportLevel::None: + Features.push_back("-neon"); + Features.push_back("-crypto"); + break; + } + + return true; +} + +// Little/Big endian +ARM::EndianKind ARM::parseArchEndian(StringRef Arch) { + if (Arch.startswith("armeb") || Arch.startswith("thumbeb") || + Arch.startswith("aarch64_be")) + return EndianKind::BIG; + + if (Arch.startswith("arm") || Arch.startswith("thumb")) { + if (Arch.endswith("eb")) + return EndianKind::BIG; + else + return EndianKind::LITTLE; + } + + if (Arch.startswith("aarch64")) + return EndianKind::LITTLE; + + return EndianKind::INVALID; +} + +// ARM, Thumb, AArch64 +ARM::ISAKind ARM::parseArchISA(StringRef Arch) { + return StringSwitch<ISAKind>(Arch) + .StartsWith("aarch64", ISAKind::AARCH64) + .StartsWith("arm64", ISAKind::AARCH64) + .StartsWith("thumb", ISAKind::THUMB) + .StartsWith("arm", ISAKind::ARM) + .Default(ISAKind::INVALID); +} + +unsigned ARM::parseFPU(StringRef FPU) { + StringRef Syn = getFPUSynonym(FPU); + for (const auto F : FPUNames) { + if (Syn == F.getName()) + return F.ID; + } + return FK_INVALID; +} + +ARM::NeonSupportLevel ARM::getFPUNeonSupportLevel(unsigned FPUKind) { + if (FPUKind >= FK_LAST) + return NeonSupportLevel::None; + return FPUNames[FPUKind].NeonSupport; +} + +// MArch is expected to be of the form (arm|thumb)?(eb)?(v.+)?(eb)?, but +// (iwmmxt|xscale)(eb)? is also permitted. If the former, return +// "v.+", if the latter, return unmodified string, minus 'eb'. +// If invalid, return empty string. +StringRef ARM::getCanonicalArchName(StringRef Arch) { + size_t offset = StringRef::npos; + StringRef A = Arch; + StringRef Error = ""; + + // Begins with "arm" / "thumb", move past it. + if (A.startswith("arm64")) + offset = 5; + else if (A.startswith("arm")) + offset = 3; + else if (A.startswith("thumb")) + offset = 5; + else if (A.startswith("aarch64")) { + offset = 7; + // AArch64 uses "_be", not "eb" suffix. + if (A.find("eb") != StringRef::npos) + return Error; + if (A.substr(offset, 3) == "_be") + offset += 3; + } + + // Ex. "armebv7", move past the "eb". + if (offset != StringRef::npos && A.substr(offset, 2) == "eb") + offset += 2; + // Or, if it ends with eb ("armv7eb"), chop it off. + else if (A.endswith("eb")) + A = A.substr(0, A.size() - 2); + // Trim the head + if (offset != StringRef::npos) + A = A.substr(offset); + + // Empty string means offset reached the end, which means it's valid. + if (A.empty()) + return Arch; + + // Only match non-marketing names + if (offset != StringRef::npos) { + // Must start with 'vN'. + if (A.size() >= 2 && (A[0] != 'v' || !std::isdigit(A[1]))) + return Error; + // Can't have an extra 'eb'. + if (A.find("eb") != StringRef::npos) + return Error; + } + + // Arch will either be a 'v' name (v7a) or a marketing name (xscale). + return A; +} + +StringRef ARM::getFPUSynonym(StringRef FPU) { + return StringSwitch<StringRef>(FPU) + .Cases("fpa", "fpe2", "fpe3", "maverick", "invalid") // Unsupported + .Case("vfp2", "vfpv2") + .Case("vfp3", "vfpv3") + .Case("vfp4", "vfpv4") + .Case("vfp3-d16", "vfpv3-d16") + .Case("vfp4-d16", "vfpv4-d16") + .Cases("fp4-sp-d16", "vfpv4-sp-d16", "fpv4-sp-d16") + .Cases("fp4-dp-d16", "fpv4-dp-d16", "vfpv4-d16") + .Case("fp5-sp-d16", "fpv5-sp-d16") + .Cases("fp5-dp-d16", "fpv5-dp-d16", "fpv5-d16") + // FIXME: Clang uses it, but it's bogus, since neon defaults to vfpv3. + .Case("neon-vfpv3", "neon") + .Default(FPU); +} + +StringRef ARM::getFPUName(unsigned FPUKind) { + if (FPUKind >= FK_LAST) + return StringRef(); + return FPUNames[FPUKind].getName(); +} + +ARM::FPUVersion ARM::getFPUVersion(unsigned FPUKind) { + if (FPUKind >= FK_LAST) + return FPUVersion::NONE; + return FPUNames[FPUKind].FPUVer; +} + +ARM::FPURestriction ARM::getFPURestriction(unsigned FPUKind) { + if (FPUKind >= FK_LAST) + return FPURestriction::None; + return FPUNames[FPUKind].Restriction; +} + +unsigned ARM::getDefaultFPU(StringRef CPU, ARM::ArchKind AK) { + if (CPU == "generic") + return ARM::ARCHNames[static_cast<unsigned>(AK)].DefaultFPU; + + return StringSwitch<unsigned>(CPU) +#define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ + .Case(NAME, DEFAULT_FPU) +#include "llvm/Support/ARMTargetParser.def" + .Default(ARM::FK_INVALID); +} + +unsigned ARM::getDefaultExtensions(StringRef CPU, ARM::ArchKind AK) { + if (CPU == "generic") + return ARM::ARCHNames[static_cast<unsigned>(AK)].ArchBaseExtensions; + + return StringSwitch<unsigned>(CPU) +#define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ + .Case(NAME, \ + ARCHNames[static_cast<unsigned>(ArchKind::ID)].ArchBaseExtensions | \ + DEFAULT_EXT) +#include "llvm/Support/ARMTargetParser.def" + .Default(ARM::AEK_INVALID); +} + +bool ARM::getHWDivFeatures(unsigned HWDivKind, + std::vector<StringRef> &Features) { + + if (HWDivKind == AEK_INVALID) + return false; + + if (HWDivKind & AEK_HWDIVARM) + Features.push_back("+hwdiv-arm"); + else + Features.push_back("-hwdiv-arm"); + + if (HWDivKind & AEK_HWDIVTHUMB) + Features.push_back("+hwdiv"); + else + Features.push_back("-hwdiv"); + + return true; +} + +bool ARM::getExtensionFeatures(unsigned Extensions, + std::vector<StringRef> &Features) { + + if (Extensions == AEK_INVALID) + return false; + + if (Extensions & AEK_CRC) + Features.push_back("+crc"); + else + Features.push_back("-crc"); + + if (Extensions & AEK_DSP) + Features.push_back("+dsp"); + else + Features.push_back("-dsp"); + + if (Extensions & AEK_FP16FML) + Features.push_back("+fp16fml"); + else + Features.push_back("-fp16fml"); + + if (Extensions & AEK_RAS) + Features.push_back("+ras"); + else + Features.push_back("-ras"); + + if (Extensions & AEK_DOTPROD) + Features.push_back("+dotprod"); + else + Features.push_back("-dotprod"); + + return getHWDivFeatures(Extensions, Features); +} + +StringRef ARM::getArchName(ARM::ArchKind AK) { + return ARCHNames[static_cast<unsigned>(AK)].getName(); +} + +StringRef ARM::getCPUAttr(ARM::ArchKind AK) { + return ARCHNames[static_cast<unsigned>(AK)].getCPUAttr(); +} + +StringRef ARM::getSubArch(ARM::ArchKind AK) { + return ARCHNames[static_cast<unsigned>(AK)].getSubArch(); +} + +unsigned ARM::getArchAttr(ARM::ArchKind AK) { + return ARCHNames[static_cast<unsigned>(AK)].ArchAttr; +} + +StringRef ARM::getArchExtName(unsigned ArchExtKind) { + for (const auto AE : ARCHExtNames) { + if (ArchExtKind == AE.ID) + return AE.getName(); + } + return StringRef(); +} + +StringRef ARM::getArchExtFeature(StringRef ArchExt) { + if (ArchExt.startswith("no")) { + StringRef ArchExtBase(ArchExt.substr(2)); + for (const auto AE : ARCHExtNames) { + if (AE.NegFeature && ArchExtBase == AE.getName()) + return StringRef(AE.NegFeature); + } + } + for (const auto AE : ARCHExtNames) { + if (AE.Feature && ArchExt == AE.getName()) + return StringRef(AE.Feature); + } + + return StringRef(); +} + +StringRef ARM::getHWDivName(unsigned HWDivKind) { + for (const auto D : HWDivNames) { + if (HWDivKind == D.ID) + return D.getName(); + } + return StringRef(); +} + +StringRef ARM::getDefaultCPU(StringRef Arch) { + ArchKind AK = parseArch(Arch); + if (AK == ArchKind::INVALID) + return StringRef(); + + // Look for multiple AKs to find the default for pair AK+Name. + for (const auto CPU : CPUNames) { + if (CPU.ArchID == AK && CPU.Default) + return CPU.getName(); + } + + // If we can't find a default then target the architecture instead + return "generic"; +} + +unsigned ARM::parseHWDiv(StringRef HWDiv) { + StringRef Syn = getHWDivSynonym(HWDiv); + for (const auto D : HWDivNames) { + if (Syn == D.getName()) + return D.ID; + } + return AEK_INVALID; +} + +unsigned ARM::parseArchExt(StringRef ArchExt) { + for (const auto A : ARCHExtNames) { + if (ArchExt == A.getName()) + return A.ID; + } + return AEK_INVALID; +} + +ARM::ArchKind ARM::parseCPUArch(StringRef CPU) { + for (const auto C : CPUNames) { + if (CPU == C.getName()) + return C.ArchID; + } + return ArchKind::INVALID; +} + +void ARM::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) { + for (const CpuNames<ArchKind> &Arch : CPUNames) { + if (Arch.ArchID != ArchKind::INVALID) + Values.push_back(Arch.getName()); + } +} + +StringRef ARM::computeDefaultTargetABI(const Triple &TT, StringRef CPU) { + StringRef ArchName = + CPU.empty() ? TT.getArchName() : getArchName(parseCPUArch(CPU)); + + if (TT.isOSBinFormatMachO()) { + if (TT.getEnvironment() == Triple::EABI || + TT.getOS() == Triple::UnknownOS || + parseArchProfile(ArchName) == ProfileKind::M) + return "aapcs"; + if (TT.isWatchABI()) + return "aapcs16"; + return "apcs-gnu"; + } else if (TT.isOSWindows()) + // FIXME: this is invalid for WindowsCE. + return "aapcs"; + + // Select the default based on the platform. + switch (TT.getEnvironment()) { + case Triple::Android: + case Triple::GNUEABI: + case Triple::GNUEABIHF: + case Triple::MuslEABI: + case Triple::MuslEABIHF: + return "aapcs-linux"; + case Triple::EABIHF: + case Triple::EABI: + return "aapcs"; + default: + if (TT.isOSNetBSD()) + return "apcs-gnu"; + if (TT.isOSOpenBSD()) + return "aapcs-linux"; + return "aapcs"; + } +} diff --git a/lib/Support/BinaryStreamError.cpp b/lib/Support/BinaryStreamError.cpp index 60f5e21f041a..cdc811d78d63 100644 --- a/lib/Support/BinaryStreamError.cpp +++ b/lib/Support/BinaryStreamError.cpp @@ -47,7 +47,7 @@ BinaryStreamError::BinaryStreamError(stream_error_code C, StringRef Context) } } -void BinaryStreamError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } +void BinaryStreamError::log(raw_ostream &OS) const { OS << ErrMsg; } StringRef BinaryStreamError::getErrorMessage() const { return ErrMsg; } diff --git a/lib/Support/BuryPointer.cpp b/lib/Support/BuryPointer.cpp new file mode 100644 index 000000000000..6c988b4a0ab2 --- /dev/null +++ b/lib/Support/BuryPointer.cpp @@ -0,0 +1,31 @@ +//===- BuryPointer.cpp - Memory Manipulation/Leak ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/BuryPointer.h" +#include "llvm/Support/Compiler.h" +#include <atomic> + +namespace llvm { + +void BuryPointer(const void *Ptr) { + // This function may be called only a small fixed amount of times per each + // invocation, otherwise we do actually have a leak which we want to report. + // If this function is called more than kGraveYardMaxSize times, the pointers + // will not be properly buried and a leak detector will report a leak, which + // is what we want in such case. + static const size_t kGraveYardMaxSize = 16; + LLVM_ATTRIBUTE_UNUSED static const void *GraveYard[kGraveYardMaxSize]; + static std::atomic<unsigned> GraveYardSize; + unsigned Idx = GraveYardSize++; + if (Idx >= kGraveYardMaxSize) + return; + GraveYard[Idx] = Ptr; +} + +} diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index 5688c1a8aef2..2a6810672b1d 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -38,7 +38,15 @@ elseif( CMAKE_HOST_UNIX ) endif() endif( MSVC OR MINGW ) +# Delay load shell32.dll if possible to speed up process startup. +set (delayload_flags) +if (MSVC) + set (delayload_flags delayimp -delayload:shell32.dll -delayload:ole32.dll) +endif() + add_llvm_library(LLVMSupport + AArch64TargetParser.cpp + ARMTargetParser.cpp AMDGPUMetadata.cpp APFloat.cpp APInt.cpp @@ -53,6 +61,7 @@ add_llvm_library(LLVMSupport BinaryStreamWriter.cpp BlockFrequency.cpp BranchProbability.cpp + BuryPointer.cpp CachePruning.cpp circular_raw_ostream.cpp Chrono.cpp @@ -71,6 +80,7 @@ add_llvm_library(LLVMSupport DJB.cpp Error.cpp ErrorHandling.cpp + FileCheck.cpp FileUtilities.cpp FileOutputBuffer.cpp FoldingSet.cpp @@ -82,6 +92,7 @@ add_llvm_library(LLVMSupport InitLLVM.cpp IntEqClasses.cpp IntervalMap.cpp + ItaniumManglingCanonicalizer.cpp JamCRC.cpp JSON.cpp KnownBits.cpp @@ -114,6 +125,7 @@ add_llvm_library(LLVMSupport StringPool.cpp StringSaver.cpp StringRef.cpp + SymbolRemappingReader.cpp SystemUtils.cpp TarWriter.cpp TargetParser.cpp @@ -126,6 +138,7 @@ add_llvm_library(LLVMSupport Unicode.cpp UnicodeCaseFold.cpp VersionTuple.cpp + VirtualFileSystem.cpp WithColor.cpp YAMLParser.cpp YAMLTraits.cpp @@ -162,7 +175,7 @@ add_llvm_library(LLVMSupport ${LLVM_MAIN_INCLUDE_DIR}/llvm/ADT ${LLVM_MAIN_INCLUDE_DIR}/llvm/Support ${Backtrace_INCLUDE_DIRS} - LINK_LIBS ${system_libs} + LINK_LIBS ${system_libs} ${delayload_flags} ) set_property(TARGET LLVMSupport PROPERTY LLVM_SYSTEM_LIBS "${system_libs}") diff --git a/lib/Support/COM.cpp b/lib/Support/COM.cpp index 2e3ff66843d3..97cd085853b0 100644 --- a/lib/Support/COM.cpp +++ b/lib/Support/COM.cpp @@ -18,6 +18,6 @@ // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX #include "Unix/COM.inc" -#elif _WIN32 +#elif defined(_WIN32) #include "Windows/COM.inc" #endif diff --git a/lib/Support/CachePruning.cpp b/lib/Support/CachePruning.cpp index 7326c4fc91fb..a0aa6024b3ed 100644 --- a/lib/Support/CachePruning.cpp +++ b/lib/Support/CachePruning.cpp @@ -27,6 +27,28 @@ using namespace llvm; +namespace { +struct FileInfo { + sys::TimePoint<> Time; + uint64_t Size; + std::string Path; + + /// Used to determine which files to prune first. Also used to determine + /// set membership, so must take into account all fields. + bool operator<(const FileInfo &Other) const { + if (Time < Other.Time) + return true; + else if (Other.Time < Time) + return false; + if (Other.Size < Size) + return true; + else if (Size < Other.Size) + return false; + return Path < Other.Path; + } +}; +} // anonymous namespace + /// Write a new timestamp file with the given path. This is used for the pruning /// interval option. static void writeTimestampFile(StringRef TimestampFile) { @@ -185,8 +207,9 @@ bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) { writeTimestampFile(TimestampFile); } - // Keep track of space. Needs to be kept ordered by size for determinism. - std::set<std::pair<uint64_t, std::string>> FileSizes; + // Keep track of files to delete to get below the size limit. + // Order by time of last use so that recently used files are preserved. + std::set<FileInfo> FileInfos; uint64_t TotalSize = 0; // Walk the entire directory cache, looking for unused files. @@ -224,22 +247,22 @@ bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) { // Leave it here for now, but add it to the list of size-based pruning. TotalSize += StatusOrErr->getSize(); - FileSizes.insert({StatusOrErr->getSize(), std::string(File->path())}); + FileInfos.insert({FileAccessTime, StatusOrErr->getSize(), File->path()}); } - auto FileAndSize = FileSizes.rbegin(); - size_t NumFiles = FileSizes.size(); + auto FileInfo = FileInfos.begin(); + size_t NumFiles = FileInfos.size(); auto RemoveCacheFile = [&]() { // Remove the file. - sys::fs::remove(FileAndSize->second); + sys::fs::remove(FileInfo->Path); // Update size - TotalSize -= FileAndSize->first; + TotalSize -= FileInfo->Size; NumFiles--; - LLVM_DEBUG(dbgs() << " - Remove " << FileAndSize->second << " (size " - << FileAndSize->first << "), new occupancy is " - << TotalSize << "%\n"); - ++FileAndSize; + LLVM_DEBUG(dbgs() << " - Remove " << FileInfo->Path << " (size " + << FileInfo->Size << "), new occupancy is " << TotalSize + << "%\n"); + ++FileInfo; }; // Prune for number of files. @@ -270,7 +293,7 @@ bool llvm::pruneCache(StringRef Path, CachePruningPolicy Policy) { << Policy.MaxSizeBytes << " bytes\n"); // Remove the oldest accessed files first, till we get below the threshold. - while (TotalSize > TotalSizeTarget && FileAndSize != FileSizes.rend()) + while (TotalSize > TotalSizeTarget && FileInfo != FileInfos.end()) RemoveCacheFile(); } return true; diff --git a/lib/Support/CodeGenCoverage.cpp b/lib/Support/CodeGenCoverage.cpp index f0a53db4e32a..811020e3254a 100644 --- a/lib/Support/CodeGenCoverage.cpp +++ b/lib/Support/CodeGenCoverage.cpp @@ -22,7 +22,7 @@ #if LLVM_ON_UNIX #include <unistd.h> -#elif _WIN32 +#elif defined(_WIN32) #include <windows.h> #endif @@ -93,7 +93,7 @@ bool CodeGenCoverage::emit(StringRef CoveragePrefix, std::string Pid = #if LLVM_ON_UNIX llvm::to_string(::getpid()); -#elif _WIN32 +#elif defined(_WIN32) llvm::to_string(::GetCurrentProcessId()); #else ""; diff --git a/lib/Support/CommandLine.cpp b/lib/Support/CommandLine.cpp index a1e659a01c8e..f7290b54dcf3 100644 --- a/lib/Support/CommandLine.cpp +++ b/lib/Support/CommandLine.cpp @@ -426,12 +426,17 @@ Option *CommandLineParser::LookupOption(SubCommand &Sub, StringRef &Arg, return I != Sub.OptionsMap.end() ? I->second : nullptr; } - // If the argument before the = is a valid option name, we match. If not, - // return Arg unmolested. + // If the argument before the = is a valid option name and the option allows + // non-prefix form (ie is not AlwaysPrefix), we match. If not, signal match + // failure by returning nullptr. auto I = Sub.OptionsMap.find(Arg.substr(0, EqualPos)); if (I == Sub.OptionsMap.end()) return nullptr; + auto O = I->second; + if (O->getFormattingFlag() == cl::AlwaysPrefix) + return nullptr; + Value = Arg.substr(EqualPos + 1); Arg = Arg.substr(0, EqualPos); return I->second; @@ -539,7 +544,9 @@ static inline bool ProvideOption(Option *Handler, StringRef ArgName, switch (Handler->getValueExpectedFlag()) { case ValueRequired: if (!Value.data()) { // No value specified? - if (i + 1 >= argc) + // If no other argument or the option only supports prefix form, we + // cannot look at the next argument. + if (i + 1 >= argc || Handler->getFormattingFlag() == cl::AlwaysPrefix) return Handler->error("requires a value!"); // Steal the next argument, like for '-o filename' assert(argv && "null check"); @@ -597,7 +604,8 @@ static inline bool isGrouping(const Option *O) { return O->getFormattingFlag() == cl::Grouping; } static inline bool isPrefixedOrGrouping(const Option *O) { - return isGrouping(O) || O->getFormattingFlag() == cl::Prefix; + return isGrouping(O) || O->getFormattingFlag() == cl::Prefix || + O->getFormattingFlag() == cl::AlwaysPrefix; } // getOptionPred - Check to see if there are any options that satisfy the @@ -647,7 +655,8 @@ HandlePrefixedOrGroupedOption(StringRef &Arg, StringRef &Value, // If the option is a prefixed option, then the value is simply the // rest of the name... so fall through to later processing, by // setting up the argument name flags and value fields. - if (PGOpt->getFormattingFlag() == cl::Prefix) { + if (PGOpt->getFormattingFlag() == cl::Prefix || + PGOpt->getFormattingFlag() == cl::AlwaysPrefix) { Value = Arg.substr(Length); Arg = Arg.substr(0, Length); assert(OptionsMap.count(Arg) && OptionsMap.find(Arg)->second == PGOpt); @@ -693,6 +702,10 @@ static bool isWhitespace(char C) { return C == ' ' || C == '\t' || C == '\r' || C == '\n'; } +static bool isWhitespaceOrNull(char C) { + return isWhitespace(C) || C == '\0'; +} + static bool isQuote(char C) { return C == '\"' || C == '\''; } void cl::TokenizeGNUCommandLine(StringRef Src, StringSaver &Saver, @@ -808,7 +821,7 @@ void cl::TokenizeWindowsCommandLine(StringRef Src, StringSaver &Saver, // INIT state indicates that the current input index is at the start of // the string or between tokens. if (State == INIT) { - if (isWhitespace(C)) { + if (isWhitespaceOrNull(C)) { // Mark the end of lines in response files if (MarkEOLs && C == '\n') NewArgv.push_back(nullptr); @@ -832,7 +845,7 @@ void cl::TokenizeWindowsCommandLine(StringRef Src, StringSaver &Saver, // quotes. if (State == UNQUOTED) { // Whitespace means the end of the token. - if (isWhitespace(C)) { + if (isWhitespaceOrNull(C)) { NewArgv.push_back(Saver.save(StringRef(Token)).data()); Token.clear(); State = INIT; @@ -1057,8 +1070,27 @@ void cl::ParseEnvironmentOptions(const char *progName, const char *envVar, } bool cl::ParseCommandLineOptions(int argc, const char *const *argv, - StringRef Overview, raw_ostream *Errs) { - return GlobalParser->ParseCommandLineOptions(argc, argv, Overview, + StringRef Overview, raw_ostream *Errs, + const char *EnvVar) { + SmallVector<const char *, 20> NewArgv; + BumpPtrAllocator A; + StringSaver Saver(A); + NewArgv.push_back(argv[0]); + + // Parse options from environment variable. + if (EnvVar) { + if (llvm::Optional<std::string> EnvValue = + sys::Process::GetEnv(StringRef(EnvVar))) + TokenizeGNUCommandLine(*EnvValue, Saver, NewArgv); + } + + // Append options from command line. + for (int I = 1; I < argc; ++I) + NewArgv.push_back(argv[I]); + int NewArgc = static_cast<int>(NewArgv.size()); + + // Parse all options. + return GlobalParser->ParseCommandLineOptions(NewArgc, &NewArgv[0], Overview, Errs); } diff --git a/lib/Support/Compression.cpp b/lib/Support/Compression.cpp index c279d10f6c61..95261d4aad23 100644 --- a/lib/Support/Compression.cpp +++ b/lib/Support/Compression.cpp @@ -29,16 +29,6 @@ static Error createError(StringRef Err) { return make_error<StringError>(Err, inconvertibleErrorCode()); } -static int encodeZlibCompressionLevel(zlib::CompressionLevel Level) { - switch (Level) { - case zlib::NoCompression: return 0; - case zlib::BestSpeedCompression: return 1; - case zlib::DefaultCompression: return Z_DEFAULT_COMPRESSION; - case zlib::BestSizeCompression: return 9; - } - llvm_unreachable("Invalid zlib::CompressionLevel!"); -} - static StringRef convertZlibCodeToString(int Code) { switch (Code) { case Z_MEM_ERROR: @@ -58,18 +48,16 @@ static StringRef convertZlibCodeToString(int Code) { bool zlib::isAvailable() { return true; } Error zlib::compress(StringRef InputBuffer, - SmallVectorImpl<char> &CompressedBuffer, - CompressionLevel Level) { + SmallVectorImpl<char> &CompressedBuffer, int Level) { unsigned long CompressedSize = ::compressBound(InputBuffer.size()); - CompressedBuffer.resize(CompressedSize); - int CLevel = encodeZlibCompressionLevel(Level); - int Res = ::compress2((Bytef *)CompressedBuffer.data(), &CompressedSize, - (const Bytef *)InputBuffer.data(), InputBuffer.size(), - CLevel); + CompressedBuffer.reserve(CompressedSize); + int Res = + ::compress2((Bytef *)CompressedBuffer.data(), &CompressedSize, + (const Bytef *)InputBuffer.data(), InputBuffer.size(), Level); // Tell MemorySanitizer that zlib output buffer is fully initialized. // This avoids a false report when running LLVM with uninstrumented ZLib. __msan_unpoison(CompressedBuffer.data(), CompressedSize); - CompressedBuffer.resize(CompressedSize); + CompressedBuffer.set_size(CompressedSize); return Res ? createError(convertZlibCodeToString(Res)) : Error::success(); } @@ -101,8 +89,7 @@ uint32_t zlib::crc32(StringRef Buffer) { #else bool zlib::isAvailable() { return false; } Error zlib::compress(StringRef InputBuffer, - SmallVectorImpl<char> &CompressedBuffer, - CompressionLevel Level) { + SmallVectorImpl<char> &CompressedBuffer, int Level) { llvm_unreachable("zlib::compress is unavailable"); } Error zlib::uncompress(StringRef InputBuffer, char *UncompressedBuffer, @@ -118,4 +105,3 @@ uint32_t zlib::crc32(StringRef Buffer) { llvm_unreachable("zlib::crc32 is unavailable"); } #endif - diff --git a/lib/Support/DebugCounter.cpp b/lib/Support/DebugCounter.cpp index 5a9cecfc56d4..6598103658da 100644 --- a/lib/Support/DebugCounter.cpp +++ b/lib/Support/DebugCounter.cpp @@ -49,8 +49,18 @@ static DebugCounterList DebugCounterOption( cl::desc("Comma separated list of debug counter skip and count"), cl::CommaSeparated, cl::ZeroOrMore, cl::location(DebugCounter::instance())); +static cl::opt<bool> PrintDebugCounter( + "print-debug-counter", cl::Hidden, cl::init(false), cl::Optional, + cl::desc("Print out debug counter info after all counters accumulated")); + static ManagedStatic<DebugCounter> DC; +// Print information when destroyed, iff command line option is specified. +DebugCounter::~DebugCounter() { + if (isCountingEnabled() && PrintDebugCounter) + print(dbgs()); +} + DebugCounter &DebugCounter::instance() { return *DC; } // This is called by the command line parser when it sees a value for the @@ -82,8 +92,11 @@ void DebugCounter::push_back(const std::string &Val) { << " is not a registered counter\n"; return; } - Counters[CounterID].Skip = CounterVal; - Counters[CounterID].IsSet = true; + enableAllCounters(); + + CounterInfo &Counter = Counters[CounterID]; + Counter.Skip = CounterVal; + Counter.IsSet = true; } else if (CounterPair.first.endswith("-count")) { auto CounterName = CounterPair.first.drop_back(6); unsigned CounterID = getCounterId(CounterName); @@ -92,8 +105,11 @@ void DebugCounter::push_back(const std::string &Val) { << " is not a registered counter\n"; return; } - Counters[CounterID].StopAfter = CounterVal; - Counters[CounterID].IsSet = true; + enableAllCounters(); + + CounterInfo &Counter = Counters[CounterID]; + Counter.StopAfter = CounterVal; + Counter.IsSet = true; } else { errs() << "DebugCounter Error: " << CounterPair.first << " does not end with -skip or -count\n"; @@ -101,11 +117,18 @@ void DebugCounter::push_back(const std::string &Val) { } void DebugCounter::print(raw_ostream &OS) const { + SmallVector<StringRef, 16> CounterNames(RegisteredCounters.begin(), + RegisteredCounters.end()); + sort(CounterNames.begin(), CounterNames.end()); + + auto &Us = instance(); OS << "Counters and values:\n"; - for (const auto &KV : Counters) - OS << left_justify(RegisteredCounters[KV.first], 32) << ": {" - << KV.second.Count << "," << KV.second.Skip << "," - << KV.second.StopAfter << "}\n"; + for (auto &CounterName : CounterNames) { + unsigned CounterID = getCounterId(CounterName); + OS << left_justify(RegisteredCounters[CounterID], 32) << ": {" + << Us.Counters[CounterID].Count << "," << Us.Counters[CounterID].Skip + << "," << Us.Counters[CounterID].StopAfter << "}\n"; + } } LLVM_DUMP_METHOD void DebugCounter::dump() const { diff --git a/lib/Support/Error.cpp b/lib/Support/Error.cpp index 83345bf6edb9..30bfc3e6d2fb 100644 --- a/lib/Support/Error.cpp +++ b/lib/Support/Error.cpp @@ -19,6 +19,7 @@ namespace { enum class ErrorErrorCode : int { MultipleErrors = 1, + FileError, InconvertibleError }; @@ -37,6 +38,8 @@ namespace { return "Inconvertible error value. An error has occurred that could " "not be converted to a known std::error_code. Please file a " "bug."; + case ErrorErrorCode::FileError: + return "A file error occurred."; } llvm_unreachable("Unhandled error code"); } @@ -51,8 +54,10 @@ namespace llvm { void ErrorInfoBase::anchor() {} char ErrorInfoBase::ID = 0; char ErrorList::ID = 0; +void ECError::anchor() {} char ECError::ID = 0; char StringError::ID = 0; +char FileError::ID = 0; void logAllUnhandledErrors(Error E, raw_ostream &OS, Twine ErrorBanner) { if (!E) @@ -75,6 +80,11 @@ std::error_code inconvertibleErrorCode() { *ErrorErrorCat); } +std::error_code FileError::convertToErrorCode() const { + return std::error_code(static_cast<int>(ErrorErrorCode::FileError), + *ErrorErrorCat); +} + Error errorCodeToError(std::error_code EC) { if (!EC) return Error::success(); @@ -103,10 +113,21 @@ void Error::fatalUncheckedError() const { } #endif -StringError::StringError(const Twine &S, std::error_code EC) +StringError::StringError(std::error_code EC, const Twine &S) : Msg(S.str()), EC(EC) {} -void StringError::log(raw_ostream &OS) const { OS << Msg; } +StringError::StringError(const Twine &S, std::error_code EC) + : Msg(S.str()), EC(EC), PrintMsgOnly(true) {} + +void StringError::log(raw_ostream &OS) const { + if (PrintMsgOnly) { + OS << Msg; + } else { + OS << EC.message(); + if (!Msg.empty()) + OS << (" " + Msg); + } +} std::error_code StringError::convertToErrorCode() const { return EC; @@ -121,11 +142,31 @@ void report_fatal_error(Error Err, bool GenCrashDiag) { std::string ErrMsg; { raw_string_ostream ErrStream(ErrMsg); - logAllUnhandledErrors(std::move(Err), ErrStream, ""); + logAllUnhandledErrors(std::move(Err), ErrStream); } report_fatal_error(ErrMsg); } +} // end namespace llvm + +LLVMErrorTypeId LLVMGetErrorTypeId(LLVMErrorRef Err) { + return reinterpret_cast<ErrorInfoBase *>(Err)->dynamicClassID(); +} + +void LLVMConsumeError(LLVMErrorRef Err) { consumeError(unwrap(Err)); } + +char *LLVMGetErrorMessage(LLVMErrorRef Err) { + std::string Tmp = toString(unwrap(Err)); + char *ErrMsg = new char[Tmp.size() + 1]; + memcpy(ErrMsg, Tmp.data(), Tmp.size()); + ErrMsg[Tmp.size()] = '\0'; + return ErrMsg; +} + +void LLVMDisposeErrorMessage(char *ErrMsg) { delete[] ErrMsg; } + +LLVMErrorTypeId LLVMGetStringErrorTypeId() { + return reinterpret_cast<void *>(&StringError::ID); } #ifndef _MSC_VER diff --git a/lib/Support/FileCheck.cpp b/lib/Support/FileCheck.cpp new file mode 100644 index 000000000000..37986c96c081 --- /dev/null +++ b/lib/Support/FileCheck.cpp @@ -0,0 +1,1446 @@ +//===- FileCheck.cpp - Check that File's Contents match what is expected --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// FileCheck does a line-by line check of a file that validates whether it +// contains the expected content. This is useful for regression tests etc. +// +// This file implements most of the API that will be used by the FileCheck utility +// as well as various unittests. +//===----------------------------------------------------------------------===// + +#include "llvm/Support/FileCheck.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/Support/FormatVariadic.h" +#include <cstdint> +#include <list> +#include <map> +#include <tuple> +#include <utility> + +using namespace llvm; + +/// Parses the given string into the Pattern. +/// +/// \p Prefix provides which prefix is being matched, \p SM provides the +/// SourceMgr used for error reports, and \p LineNumber is the line number in +/// the input file from which the pattern string was read. Returns true in +/// case of an error, false otherwise. +bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, + SourceMgr &SM, unsigned LineNumber, + const FileCheckRequest &Req) { + bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot; + + this->LineNumber = LineNumber; + PatternLoc = SMLoc::getFromPointer(PatternStr.data()); + + if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines)) + // Ignore trailing whitespace. + while (!PatternStr.empty() && + (PatternStr.back() == ' ' || PatternStr.back() == '\t')) + PatternStr = PatternStr.substr(0, PatternStr.size() - 1); + + // Check that there is something on the line. + if (PatternStr.empty() && CheckTy != Check::CheckEmpty) { + SM.PrintMessage(PatternLoc, SourceMgr::DK_Error, + "found empty check string with prefix '" + Prefix + ":'"); + return true; + } + + if (!PatternStr.empty() && CheckTy == Check::CheckEmpty) { + SM.PrintMessage( + PatternLoc, SourceMgr::DK_Error, + "found non-empty check string for empty check with prefix '" + Prefix + + ":'"); + return true; + } + + if (CheckTy == Check::CheckEmpty) { + RegExStr = "(\n$)"; + return false; + } + + // Check to see if this is a fixed string, or if it has regex pieces. + if (!MatchFullLinesHere && + (PatternStr.size() < 2 || (PatternStr.find("{{") == StringRef::npos && + PatternStr.find("[[") == StringRef::npos))) { + FixedStr = PatternStr; + return false; + } + + if (MatchFullLinesHere) { + RegExStr += '^'; + if (!Req.NoCanonicalizeWhiteSpace) + RegExStr += " *"; + } + + // Paren value #0 is for the fully matched string. Any new parenthesized + // values add from there. + unsigned CurParen = 1; + + // Otherwise, there is at least one regex piece. Build up the regex pattern + // by escaping scary characters in fixed strings, building up one big regex. + while (!PatternStr.empty()) { + // RegEx matches. + if (PatternStr.startswith("{{")) { + // This is the start of a regex match. Scan for the }}. + size_t End = PatternStr.find("}}"); + if (End == StringRef::npos) { + SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()), + SourceMgr::DK_Error, + "found start of regex string with no end '}}'"); + return true; + } + + // Enclose {{}} patterns in parens just like [[]] even though we're not + // capturing the result for any purpose. This is required in case the + // expression contains an alternation like: CHECK: abc{{x|z}}def. We + // want this to turn into: "abc(x|z)def" not "abcx|zdef". + RegExStr += '('; + ++CurParen; + + if (AddRegExToRegEx(PatternStr.substr(2, End - 2), CurParen, SM)) + return true; + RegExStr += ')'; + + PatternStr = PatternStr.substr(End + 2); + continue; + } + + // Named RegEx matches. These are of two forms: [[foo:.*]] which matches .* + // (or some other regex) and assigns it to the FileCheck variable 'foo'. The + // second form is [[foo]] which is a reference to foo. The variable name + // itself must be of the form "[a-zA-Z_][0-9a-zA-Z_]*", otherwise we reject + // it. This is to catch some common errors. + if (PatternStr.startswith("[[")) { + // Find the closing bracket pair ending the match. End is going to be an + // offset relative to the beginning of the match string. + size_t End = FindRegexVarEnd(PatternStr.substr(2), SM); + + if (End == StringRef::npos) { + SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()), + SourceMgr::DK_Error, + "invalid named regex reference, no ]] found"); + return true; + } + + StringRef MatchStr = PatternStr.substr(2, End); + PatternStr = PatternStr.substr(End + 4); + + // Get the regex name (e.g. "foo"). + size_t NameEnd = MatchStr.find(':'); + StringRef Name = MatchStr.substr(0, NameEnd); + + if (Name.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "invalid name in named regex: empty name"); + return true; + } + + // Verify that the name/expression is well formed. FileCheck currently + // supports @LINE, @LINE+number, @LINE-number expressions. The check here + // is relaxed, more strict check is performed in \c EvaluateExpression. + bool IsExpression = false; + for (unsigned i = 0, e = Name.size(); i != e; ++i) { + if (i == 0) { + if (Name[i] == '$') // Global vars start with '$' + continue; + if (Name[i] == '@') { + if (NameEnd != StringRef::npos) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), + SourceMgr::DK_Error, + "invalid name in named regex definition"); + return true; + } + IsExpression = true; + continue; + } + } + if (Name[i] != '_' && !isalnum(Name[i]) && + (!IsExpression || (Name[i] != '+' && Name[i] != '-'))) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data() + i), + SourceMgr::DK_Error, "invalid name in named regex"); + return true; + } + } + + // Name can't start with a digit. + if (isdigit(static_cast<unsigned char>(Name[0]))) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "invalid name in named regex"); + return true; + } + + // Handle [[foo]]. + if (NameEnd == StringRef::npos) { + // Handle variables that were defined earlier on the same line by + // emitting a backreference. + if (VariableDefs.find(Name) != VariableDefs.end()) { + unsigned VarParenNum = VariableDefs[Name]; + if (VarParenNum < 1 || VarParenNum > 9) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), + SourceMgr::DK_Error, + "Can't back-reference more than 9 variables"); + return true; + } + AddBackrefToRegEx(VarParenNum); + } else { + VariableUses.push_back(std::make_pair(Name, RegExStr.size())); + } + continue; + } + + // Handle [[foo:.*]]. + VariableDefs[Name] = CurParen; + RegExStr += '('; + ++CurParen; + + if (AddRegExToRegEx(MatchStr.substr(NameEnd + 1), CurParen, SM)) + return true; + + RegExStr += ')'; + } + + // Handle fixed string matches. + // Find the end, which is the start of the next regex. + size_t FixedMatchEnd = PatternStr.find("{{"); + FixedMatchEnd = std::min(FixedMatchEnd, PatternStr.find("[[")); + RegExStr += Regex::escape(PatternStr.substr(0, FixedMatchEnd)); + PatternStr = PatternStr.substr(FixedMatchEnd); + } + + if (MatchFullLinesHere) { + if (!Req.NoCanonicalizeWhiteSpace) + RegExStr += " *"; + RegExStr += '$'; + } + + return false; +} + +bool FileCheckPattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) { + Regex R(RS); + std::string Error; + if (!R.isValid(Error)) { + SM.PrintMessage(SMLoc::getFromPointer(RS.data()), SourceMgr::DK_Error, + "invalid regex: " + Error); + return true; + } + + RegExStr += RS.str(); + CurParen += R.getNumMatches(); + return false; +} + +void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) { + assert(BackrefNum >= 1 && BackrefNum <= 9 && "Invalid backref number"); + std::string Backref = std::string("\\") + std::string(1, '0' + BackrefNum); + RegExStr += Backref; +} + +/// Evaluates expression and stores the result to \p Value. +/// +/// Returns true on success and false when the expression has invalid syntax. +bool FileCheckPattern::EvaluateExpression(StringRef Expr, std::string &Value) const { + // The only supported expression is @LINE([\+-]\d+)? + if (!Expr.startswith("@LINE")) + return false; + Expr = Expr.substr(StringRef("@LINE").size()); + int Offset = 0; + if (!Expr.empty()) { + if (Expr[0] == '+') + Expr = Expr.substr(1); + else if (Expr[0] != '-') + return false; + if (Expr.getAsInteger(10, Offset)) + return false; + } + Value = llvm::itostr(LineNumber + Offset); + return true; +} + +/// Matches the pattern string against the input buffer \p Buffer +/// +/// This returns the position that is matched or npos if there is no match. If +/// there is a match, the size of the matched string is returned in \p +/// MatchLen. +/// +/// The \p VariableTable StringMap provides the current values of filecheck +/// variables and is updated if this match defines new values. +size_t FileCheckPattern::Match(StringRef Buffer, size_t &MatchLen, + StringMap<StringRef> &VariableTable) const { + // If this is the EOF pattern, match it immediately. + if (CheckTy == Check::CheckEOF) { + MatchLen = 0; + return Buffer.size(); + } + + // If this is a fixed string pattern, just match it now. + if (!FixedStr.empty()) { + MatchLen = FixedStr.size(); + return Buffer.find(FixedStr); + } + + // Regex match. + + // If there are variable uses, we need to create a temporary string with the + // actual value. + StringRef RegExToMatch = RegExStr; + std::string TmpStr; + if (!VariableUses.empty()) { + TmpStr = RegExStr; + + unsigned InsertOffset = 0; + for (const auto &VariableUse : VariableUses) { + std::string Value; + + if (VariableUse.first[0] == '@') { + if (!EvaluateExpression(VariableUse.first, Value)) + return StringRef::npos; + } else { + StringMap<StringRef>::iterator it = + VariableTable.find(VariableUse.first); + // If the variable is undefined, return an error. + if (it == VariableTable.end()) + return StringRef::npos; + + // Look up the value and escape it so that we can put it into the regex. + Value += Regex::escape(it->second); + } + + // Plop it into the regex at the adjusted offset. + TmpStr.insert(TmpStr.begin() + VariableUse.second + InsertOffset, + Value.begin(), Value.end()); + InsertOffset += Value.size(); + } + + // Match the newly constructed regex. + RegExToMatch = TmpStr; + } + + SmallVector<StringRef, 4> MatchInfo; + if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo)) + return StringRef::npos; + + // Successful regex match. + assert(!MatchInfo.empty() && "Didn't get any match"); + StringRef FullMatch = MatchInfo[0]; + + // If this defines any variables, remember their values. + for (const auto &VariableDef : VariableDefs) { + assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); + VariableTable[VariableDef.first] = MatchInfo[VariableDef.second]; + } + + // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after + // the required preceding newline, which is consumed by the pattern in the + // case of CHECK-EMPTY but not CHECK-NEXT. + size_t MatchStartSkip = CheckTy == Check::CheckEmpty; + MatchLen = FullMatch.size() - MatchStartSkip; + return FullMatch.data() - Buffer.data() + MatchStartSkip; +} + + +/// Computes an arbitrary estimate for the quality of matching this pattern at +/// the start of \p Buffer; a distance of zero should correspond to a perfect +/// match. +unsigned +FileCheckPattern::ComputeMatchDistance(StringRef Buffer, + const StringMap<StringRef> &VariableTable) const { + // Just compute the number of matching characters. For regular expressions, we + // just compare against the regex itself and hope for the best. + // + // FIXME: One easy improvement here is have the regex lib generate a single + // example regular expression which matches, and use that as the example + // string. + StringRef ExampleString(FixedStr); + if (ExampleString.empty()) + ExampleString = RegExStr; + + // Only compare up to the first line in the buffer, or the string size. + StringRef BufferPrefix = Buffer.substr(0, ExampleString.size()); + BufferPrefix = BufferPrefix.split('\n').first; + return BufferPrefix.edit_distance(ExampleString); +} + +void FileCheckPattern::PrintVariableUses(const SourceMgr &SM, StringRef Buffer, + const StringMap<StringRef> &VariableTable, + SMRange MatchRange) const { + // If this was a regular expression using variables, print the current + // variable values. + if (!VariableUses.empty()) { + for (const auto &VariableUse : VariableUses) { + SmallString<256> Msg; + raw_svector_ostream OS(Msg); + StringRef Var = VariableUse.first; + if (Var[0] == '@') { + std::string Value; + if (EvaluateExpression(Var, Value)) { + OS << "with expression \""; + OS.write_escaped(Var) << "\" equal to \""; + OS.write_escaped(Value) << "\""; + } else { + OS << "uses incorrect expression \""; + OS.write_escaped(Var) << "\""; + } + } else { + StringMap<StringRef>::const_iterator it = VariableTable.find(Var); + + // Check for undefined variable references. + if (it == VariableTable.end()) { + OS << "uses undefined variable \""; + OS.write_escaped(Var) << "\""; + } else { + OS << "with variable \""; + OS.write_escaped(Var) << "\" equal to \""; + OS.write_escaped(it->second) << "\""; + } + } + + if (MatchRange.isValid()) + SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, OS.str(), + {MatchRange}); + else + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), + SourceMgr::DK_Note, OS.str()); + } + } +} + +static SMRange ProcessMatchResult(FileCheckDiag::MatchType MatchTy, + const SourceMgr &SM, SMLoc Loc, + Check::FileCheckType CheckTy, + StringRef Buffer, size_t Pos, size_t Len, + std::vector<FileCheckDiag> *Diags, + bool AdjustPrevDiag = false) { + SMLoc Start = SMLoc::getFromPointer(Buffer.data() + Pos); + SMLoc End = SMLoc::getFromPointer(Buffer.data() + Pos + Len); + SMRange Range(Start, End); + if (Diags) { + if (AdjustPrevDiag) + Diags->rbegin()->MatchTy = MatchTy; + else + Diags->emplace_back(SM, CheckTy, Loc, MatchTy, Range); + } + return Range; +} + +void FileCheckPattern::PrintFuzzyMatch( + const SourceMgr &SM, StringRef Buffer, + const StringMap<StringRef> &VariableTable, + std::vector<FileCheckDiag> *Diags) const { + // Attempt to find the closest/best fuzzy match. Usually an error happens + // because some string in the output didn't exactly match. In these cases, we + // would like to show the user a best guess at what "should have" matched, to + // save them having to actually check the input manually. + size_t NumLinesForward = 0; + size_t Best = StringRef::npos; + double BestQuality = 0; + + // Use an arbitrary 4k limit on how far we will search. + for (size_t i = 0, e = std::min(size_t(4096), Buffer.size()); i != e; ++i) { + if (Buffer[i] == '\n') + ++NumLinesForward; + + // Patterns have leading whitespace stripped, so skip whitespace when + // looking for something which looks like a pattern. + if (Buffer[i] == ' ' || Buffer[i] == '\t') + continue; + + // Compute the "quality" of this match as an arbitrary combination of the + // match distance and the number of lines skipped to get to this match. + unsigned Distance = ComputeMatchDistance(Buffer.substr(i), VariableTable); + double Quality = Distance + (NumLinesForward / 100.); + + if (Quality < BestQuality || Best == StringRef::npos) { + Best = i; + BestQuality = Quality; + } + } + + // Print the "possible intended match here" line if we found something + // reasonable and not equal to what we showed in the "scanning from here" + // line. + if (Best && Best != StringRef::npos && BestQuality < 50) { + SMRange MatchRange = + ProcessMatchResult(FileCheckDiag::MatchFuzzy, SM, getLoc(), + getCheckTy(), Buffer, Best, 0, Diags); + SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, + "possible intended match here"); + + // FIXME: If we wanted to be really friendly we would show why the match + // failed, as it can be hard to spot simple one character differences. + } +} + +/// Finds the closing sequence of a regex variable usage or definition. +/// +/// \p Str has to point in the beginning of the definition (right after the +/// opening sequence). Returns the offset of the closing sequence within Str, +/// or npos if it was not found. +size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) { + // Offset keeps track of the current offset within the input Str + size_t Offset = 0; + // [...] Nesting depth + size_t BracketDepth = 0; + + while (!Str.empty()) { + if (Str.startswith("]]") && BracketDepth == 0) + return Offset; + if (Str[0] == '\\') { + // Backslash escapes the next char within regexes, so skip them both. + Str = Str.substr(2); + Offset += 2; + } else { + switch (Str[0]) { + default: + break; + case '[': + BracketDepth++; + break; + case ']': + if (BracketDepth == 0) { + SM.PrintMessage(SMLoc::getFromPointer(Str.data()), + SourceMgr::DK_Error, + "missing closing \"]\" for regex variable"); + exit(1); + } + BracketDepth--; + break; + } + Str = Str.substr(1); + Offset++; + } + } + + return StringRef::npos; +} + +/// Canonicalize whitespaces in the file. Line endings are replaced with +/// UNIX-style '\n'. +StringRef +llvm::FileCheck::CanonicalizeFile(MemoryBuffer &MB, + SmallVectorImpl<char> &OutputBuffer) { + OutputBuffer.reserve(MB.getBufferSize()); + + for (const char *Ptr = MB.getBufferStart(), *End = MB.getBufferEnd(); + Ptr != End; ++Ptr) { + // Eliminate trailing dosish \r. + if (Ptr <= End - 2 && Ptr[0] == '\r' && Ptr[1] == '\n') { + continue; + } + + // If current char is not a horizontal whitespace or if horizontal + // whitespace canonicalization is disabled, dump it to output as is. + if (Req.NoCanonicalizeWhiteSpace || (*Ptr != ' ' && *Ptr != '\t')) { + OutputBuffer.push_back(*Ptr); + continue; + } + + // Otherwise, add one space and advance over neighboring space. + OutputBuffer.push_back(' '); + while (Ptr + 1 != End && (Ptr[1] == ' ' || Ptr[1] == '\t')) + ++Ptr; + } + + // Add a null byte and then return all but that byte. + OutputBuffer.push_back('\0'); + return StringRef(OutputBuffer.data(), OutputBuffer.size() - 1); +} + +FileCheckDiag::FileCheckDiag(const SourceMgr &SM, + const Check::FileCheckType &CheckTy, + SMLoc CheckLoc, MatchType MatchTy, + SMRange InputRange) + : CheckTy(CheckTy), 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 == '_'); +} + +Check::FileCheckType &Check::FileCheckType::setCount(int C) { + assert(Count > 0 && "zero and negative counts are not supported"); + assert((C == 1 || Kind == CheckPlain) && + "count supported only for plain CHECK directives"); + Count = C; + return *this; +} + +// Get a description of the type. +std::string Check::FileCheckType::getDescription(StringRef Prefix) const { + switch (Kind) { + case Check::CheckNone: + return "invalid"; + case Check::CheckPlain: + if (Count > 1) + return Prefix.str() + "-COUNT"; + return Prefix; + case Check::CheckNext: + return Prefix.str() + "-NEXT"; + case Check::CheckSame: + return Prefix.str() + "-SAME"; + case Check::CheckNot: + return Prefix.str() + "-NOT"; + case Check::CheckDAG: + return Prefix.str() + "-DAG"; + case Check::CheckLabel: + return Prefix.str() + "-LABEL"; + case Check::CheckEmpty: + return Prefix.str() + "-EMPTY"; + case Check::CheckEOF: + return "implicit EOF"; + case Check::CheckBadNot: + return "bad NOT"; + case Check::CheckBadCount: + return "bad COUNT"; + } + llvm_unreachable("unknown FileCheckType"); +} + +static std::pair<Check::FileCheckType, StringRef> +FindCheckType(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); + // Verify that the : is present after the prefix. + if (NextChar == ':') + return {Check::CheckPlain, Rest}; + + if (NextChar != '-') + return {Check::CheckNone, StringRef()}; + + if (Rest.consume_front("COUNT-")) { + int64_t Count; + if (Rest.consumeInteger(10, Count)) + // Error happened in parsing integer. + return {Check::CheckBadCount, Rest}; + if (Count <= 0 || Count > INT32_MAX) + return {Check::CheckBadCount, Rest}; + if (!Rest.consume_front(":")) + return {Check::CheckBadCount, Rest}; + return {Check::FileCheckType(Check::CheckPlain).setCount(Count), Rest}; + } + + if (Rest.consume_front("NEXT:")) + return {Check::CheckNext, Rest}; + + if (Rest.consume_front("SAME:")) + return {Check::CheckSame, Rest}; + + if (Rest.consume_front("NOT:")) + return {Check::CheckNot, Rest}; + + if (Rest.consume_front("DAG:")) + return {Check::CheckDAG, Rest}; + + if (Rest.consume_front("LABEL:")) + return {Check::CheckLabel, Rest}; + + if (Rest.consume_front("EMPTY:")) + return {Check::CheckEmpty, Rest}; + + // You can't combine -NOT with another suffix. + if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") || + Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") || + Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:") || + Rest.startswith("EMPTY-NOT:") || Rest.startswith("NOT-EMPTY:")) + return {Check::CheckBadNot, Rest}; + + return {Check::CheckNone, Rest}; +} + +// From the given position, find the next character after the word. +static size_t SkipWord(StringRef Str, size_t Loc) { + while (Loc < Str.size() && IsPartOfWord(Str[Loc])) + ++Loc; + return Loc; +} + +/// Search the buffer for the first prefix in the prefix regular expression. +/// +/// This searches the buffer using the provided regular expression, however it +/// enforces constraints beyond that: +/// 1) The found prefix must not be a suffix of something that looks like +/// a valid prefix. +/// 2) The found prefix must be followed by a valid check type suffix using \c +/// FindCheckType above. +/// +/// Returns a pair of StringRefs into the Buffer, which combines: +/// - the first match of the regular expression to satisfy these two is +/// returned, +/// otherwise an empty StringRef is returned to indicate failure. +/// - buffer rewound to the location right after parsed suffix, for parsing +/// to continue from +/// +/// If this routine returns a valid prefix, it will also shrink \p Buffer to +/// start at the beginning of the returned prefix, increment \p LineNumber for +/// each new line consumed from \p Buffer, and set \p CheckTy to the type of +/// check found by examining the suffix. +/// +/// 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) { + SmallVector<StringRef, 2> Matches; + + while (!Buffer.empty()) { + // Find the first (longest) match using the RE. + if (!PrefixRE.match(Buffer, &Matches)) + // No match at all, bail. + return {StringRef(), StringRef()}; + + StringRef Prefix = Matches[0]; + Matches.clear(); + + assert(Prefix.data() >= Buffer.data() && + Prefix.data() < Buffer.data() + Buffer.size() && + "Prefix doesn't start inside of buffer!"); + size_t Loc = Prefix.data() - Buffer.data(); + StringRef Skipped = Buffer.substr(0, Loc); + Buffer = Buffer.drop_front(Loc); + LineNumber += Skipped.count('\n'); + + // Check that the matched prefix isn't a suffix of some other check-like + // word. + // FIXME: This is a very ad-hoc check. it would be better handled in some + // other way. Among other things it seems hard to distinguish between + // intentional and unintentional uses of this feature. + if (Skipped.empty() || !IsPartOfWord(Skipped.back())) { + // Now extract the type. + StringRef AfterSuffix; + std::tie(CheckTy, AfterSuffix) = FindCheckType(Buffer, Prefix); + + // If we've found a valid check type for this prefix, we're done. + if (CheckTy != Check::CheckNone) + return {Prefix, AfterSuffix}; + } + + // If we didn't successfully find a prefix, we need to skip this invalid + // prefix and continue scanning. We directly skip the prefix that was + // matched and any additional parts of that check-like word. + Buffer = Buffer.drop_front(SkipWord(Buffer, Prefix.size())); + } + + // We ran out of buffer while skipping partial matches so give up. + return {StringRef(), StringRef()}; +} + +/// Read the check file, which specifies the sequence of expected strings. +/// +/// The strings are added to the CheckStrings vector. Returns true in case of +/// an error, false otherwise. +bool llvm::FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, + Regex &PrefixRE, + std::vector<FileCheckString> &CheckStrings) { + std::vector<FileCheckPattern> ImplicitNegativeChecks; + for (const auto &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"); + + StringRef PatternInBuffer = + CmdLine->getBuffer().substr(Prefix.size(), PatternString.size()); + SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + + ImplicitNegativeChecks.push_back(FileCheckPattern(Check::CheckNot)); + ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer, + "IMPLICIT-CHECK", SM, 0, Req); + } + + std::vector<FileCheckPattern> DagNotMatches = ImplicitNegativeChecks; + + // LineNumber keeps track of the line on which CheckPrefix instances are + // found. + unsigned LineNumber = 1; + + while (1) { + Check::FileCheckType CheckTy; + + // See if a prefix occurs in the memory buffer. + StringRef UsedPrefix; + StringRef AfterSuffix; + std::tie(UsedPrefix, AfterSuffix) = + FindFirstMatchingPrefix(PrefixRE, Buffer, LineNumber, CheckTy); + if (UsedPrefix.empty()) + break; + assert(UsedPrefix.data() == Buffer.data() && + "Failed to move Buffer's start forward, or pointed prefix outside " + "of the buffer!"); + assert(AfterSuffix.data() >= Buffer.data() && + AfterSuffix.data() < Buffer.data() + Buffer.size() && + "Parsing after suffix doesn't start inside of buffer!"); + + // Location to use for error messages. + const char *UsedPrefixStart = UsedPrefix.data(); + + // Skip the buffer to the end of parsed suffix (or just prefix, if no good + // suffix was processed). + Buffer = AfterSuffix.empty() ? Buffer.drop_front(UsedPrefix.size()) + : AfterSuffix; + + // Complain about useful-looking but unsupported suffixes. + if (CheckTy == Check::CheckBadNot) { + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error, + "unsupported -NOT combo on prefix '" + UsedPrefix + "'"); + return true; + } + + // Complain about invalid count specification. + if (CheckTy == Check::CheckBadCount) { + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Error, + "invalid count in -COUNT specification on prefix '" + + UsedPrefix + "'"); + return true; + } + + // Okay, we found the prefix, yay. Remember the rest of the line, but ignore + // leading whitespace. + if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines)) + Buffer = Buffer.substr(Buffer.find_first_not_of(" \t")); + + // Scan ahead to the end of line. + size_t EOL = Buffer.find_first_of("\n\r"); + + // Remember the location of the start of the pattern, for diagnostics. + SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); + + // Parse the pattern. + FileCheckPattern P(CheckTy); + if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req)) + return true; + + // Verify that CHECK-LABEL lines do not define or use variables + if ((CheckTy == Check::CheckLabel) && P.hasVariable()) { + SM.PrintMessage( + SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error, + "found '" + UsedPrefix + "-LABEL:'" + " with variable definition or use"); + 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) && + CheckStrings.empty()) { + StringRef Type = CheckTy == Check::CheckNext + ? "NEXT" + : CheckTy == Check::CheckEmpty ? "EMPTY" : "SAME"; + SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), + SourceMgr::DK_Error, + "found '" + UsedPrefix + "-" + Type + + "' without previous '" + UsedPrefix + ": line"); + return true; + } + + // Handle CHECK-DAG/-NOT. + if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) { + DagNotMatches.push_back(P); + continue; + } + + // Okay, add the string we captured to the output vector and move on. + CheckStrings.emplace_back(P, UsedPrefix, PatternLoc); + std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); + 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(FileCheckPattern(Check::CheckEOF), *Req.CheckPrefixes.begin(), + SMLoc::getFromPointer(Buffer.data())); + std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); + } + + if (CheckStrings.empty()) { + 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 (; I != E; ++I) + errs() << ", \'" << *I << ":'"; + + errs() << '\n'; + return true; + } + + return false; +} + +static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, + StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat, + int MatchedCount, StringRef Buffer, + StringMap<StringRef> &VariableTable, size_t MatchPos, + size_t MatchLen, const FileCheckRequest &Req, + std::vector<FileCheckDiag> *Diags) { + if (ExpectedMatch) { + if (!Req.Verbose) + return; + if (!Req.VerboseVerbose && Pat.getCheckTy() == Check::CheckEOF) + return; + } + SMRange MatchRange = ProcessMatchResult( + ExpectedMatch ? FileCheckDiag::MatchFoundAndExpected + : FileCheckDiag::MatchFoundButExcluded, + SM, Loc, Pat.getCheckTy(), Buffer, MatchPos, MatchLen, Diags); + std::string Message = formatv("{0}: {1} string found in input", + Pat.getCheckTy().getDescription(Prefix), + (ExpectedMatch ? "expected" : "excluded")) + .str(); + if (Pat.getCount() > 1) + Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str(); + + SM.PrintMessage( + Loc, ExpectedMatch ? SourceMgr::DK_Remark : SourceMgr::DK_Error, Message); + SM.PrintMessage(MatchRange.Start, SourceMgr::DK_Note, "found here", + {MatchRange}); + Pat.PrintVariableUses(SM, Buffer, VariableTable, MatchRange); +} + +static void PrintMatch(bool ExpectedMatch, const SourceMgr &SM, + const FileCheckString &CheckStr, int MatchedCount, + StringRef Buffer, StringMap<StringRef> &VariableTable, + size_t MatchPos, size_t MatchLen, FileCheckRequest &Req, + std::vector<FileCheckDiag> *Diags) { + PrintMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, + MatchedCount, Buffer, VariableTable, MatchPos, MatchLen, Req, + Diags); +} + +static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, + StringRef Prefix, SMLoc Loc, + const FileCheckPattern &Pat, int MatchedCount, + StringRef Buffer, StringMap<StringRef> &VariableTable, + bool VerboseVerbose, + std::vector<FileCheckDiag> *Diags) { + if (!ExpectedMatch && !VerboseVerbose) + return; + + // Otherwise, we have an error, emit an error message. + std::string Message = formatv("{0}: {1} string not found in input", + Pat.getCheckTy().getDescription(Prefix), + (ExpectedMatch ? "expected" : "excluded")) + .str(); + if (Pat.getCount() > 1) + Message += formatv(" ({0} out of {1})", MatchedCount, Pat.getCount()).str(); + + SM.PrintMessage( + Loc, ExpectedMatch ? SourceMgr::DK_Error : SourceMgr::DK_Remark, Message); + + // Print the "scanning from here" line. If the current position is at the + // end of a line, advance to the start of the next line. + Buffer = Buffer.substr(Buffer.find_first_not_of(" \t\n\r")); + SMRange SearchRange = ProcessMatchResult( + ExpectedMatch ? FileCheckDiag::MatchNoneButExpected + : FileCheckDiag::MatchNoneAndExcluded, + SM, Loc, Pat.getCheckTy(), Buffer, 0, Buffer.size(), Diags); + SM.PrintMessage(SearchRange.Start, SourceMgr::DK_Note, "scanning from here"); + + // Allow the pattern to print additional information if desired. + Pat.PrintVariableUses(SM, Buffer, VariableTable); + + if (ExpectedMatch) + Pat.PrintFuzzyMatch(SM, Buffer, VariableTable, Diags); +} + +static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, + const FileCheckString &CheckStr, int MatchedCount, + StringRef Buffer, StringMap<StringRef> &VariableTable, + bool VerboseVerbose, + std::vector<FileCheckDiag> *Diags) { + PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat, + MatchedCount, Buffer, VariableTable, VerboseVerbose, Diags); +} + +/// Count the number of newlines in the specified range. +static unsigned CountNumNewlinesBetween(StringRef Range, + const char *&FirstNewLine) { + unsigned NumNewLines = 0; + while (1) { + // Scan for newline. + Range = Range.substr(Range.find_first_of("\n\r")); + if (Range.empty()) + return NumNewLines; + + ++NumNewLines; + + // Handle \n\r and \r\n as a single newline. + if (Range.size() > 1 && (Range[1] == '\n' || Range[1] == '\r') && + (Range[0] != Range[1])) + Range = Range.substr(1); + Range = Range.substr(1); + + if (NumNewLines == 1) + FirstNewLine = Range.begin(); + } +} + +/// Match check string and its "not strings" and/or "dag strings". +size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer, + bool IsLabelScanMode, size_t &MatchLen, + StringMap<StringRef> &VariableTable, + FileCheckRequest &Req, + std::vector<FileCheckDiag> *Diags) const { + size_t LastPos = 0; + std::vector<const FileCheckPattern *> NotStrings; + + // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL + // bounds; we have not processed variable definitions within the bounded block + // yet so cannot handle any final CHECK-DAG yet; this is handled when going + // over the block again (including the last CHECK-LABEL) in normal mode. + if (!IsLabelScanMode) { + // Match "dag strings" (with mixed "not strings" if any). + LastPos = CheckDag(SM, Buffer, NotStrings, VariableTable, Req, Diags); + if (LastPos == StringRef::npos) + return StringRef::npos; + } + + // Match itself from the last position after matching CHECK-DAG. + size_t LastMatchEnd = LastPos; + size_t FirstMatchPos = 0; + // Go match the pattern Count times. Majority of patterns only match with + // count 1 though. + assert(Pat.getCount() != 0 && "pattern count can not be zero"); + for (int i = 1; i <= Pat.getCount(); i++) { + StringRef MatchBuffer = Buffer.substr(LastMatchEnd); + size_t CurrentMatchLen; + // get a match at current start point + size_t MatchPos = Pat.Match(MatchBuffer, CurrentMatchLen, VariableTable); + if (i == 1) + FirstMatchPos = LastPos + MatchPos; + + // report + if (MatchPos == StringRef::npos) { + PrintNoMatch(true, SM, *this, i, MatchBuffer, VariableTable, + Req.VerboseVerbose, Diags); + return StringRef::npos; + } + PrintMatch(true, SM, *this, i, MatchBuffer, VariableTable, MatchPos, + CurrentMatchLen, Req, Diags); + + // move start point after the match + LastMatchEnd += MatchPos + CurrentMatchLen; + } + // Full match len counts from first match pos. + MatchLen = LastMatchEnd - FirstMatchPos; + + // Similar to the above, in "label-scan mode" we can't yet handle CHECK-NEXT + // or CHECK-NOT + if (!IsLabelScanMode) { + size_t MatchPos = FirstMatchPos - LastPos; + StringRef MatchBuffer = Buffer.substr(LastPos); + StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos); + + // If this check is a "CHECK-NEXT", verify that the previous match was on + // the previous line (i.e. that there is one newline between them). + if (CheckNext(SM, SkippedRegion)) { + ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc, + Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen, + Diags, Req.Verbose); + return StringRef::npos; + } + + // If this check is a "CHECK-SAME", verify that the previous match was on + // the same line (i.e. that there is no newline between them). + if (CheckSame(SM, SkippedRegion)) { + ProcessMatchResult(FileCheckDiag::MatchFoundButWrongLine, SM, Loc, + Pat.getCheckTy(), MatchBuffer, MatchPos, MatchLen, + Diags, Req.Verbose); + return StringRef::npos; + } + + // If this match had "not strings", verify that they don't exist in the + // skipped region. + if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags)) + return StringRef::npos; + } + + return FirstMatchPos; +} + +/// Verify there is a single line in the given buffer. +bool FileCheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { + if (Pat.getCheckTy() != Check::CheckNext && + Pat.getCheckTy() != Check::CheckEmpty) + return false; + + Twine CheckName = + Prefix + + Twine(Pat.getCheckTy() == Check::CheckEmpty ? "-EMPTY" : "-NEXT"); + + // Count the number of newlines between the previous match and this one. + assert(Buffer.data() != + SM.getMemoryBuffer(SM.FindBufferContainingLoc( + SMLoc::getFromPointer(Buffer.data()))) + ->getBufferStart() && + "CHECK-NEXT and CHECK-EMPTY can't be the first check in a file"); + + const char *FirstNewLine = nullptr; + unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine); + + if (NumNewLines == 0) { + SM.PrintMessage(Loc, SourceMgr::DK_Error, + CheckName + ": is on the same line as previous match"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note, + "'next' match was here"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note, + "previous match ended here"); + return true; + } + + if (NumNewLines != 1) { + SM.PrintMessage(Loc, SourceMgr::DK_Error, + CheckName + + ": is not on the line after the previous match"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note, + "'next' match was here"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note, + "previous match ended here"); + SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note, + "non-matching line after previous match is here"); + return true; + } + + return false; +} + +/// Verify there is no newline in the given buffer. +bool FileCheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const { + if (Pat.getCheckTy() != Check::CheckSame) + return false; + + // Count the number of newlines between the previous match and this one. + assert(Buffer.data() != + SM.getMemoryBuffer(SM.FindBufferContainingLoc( + SMLoc::getFromPointer(Buffer.data()))) + ->getBufferStart() && + "CHECK-SAME can't be the first check in a file"); + + const char *FirstNewLine = nullptr; + unsigned NumNewLines = CountNumNewlinesBetween(Buffer, FirstNewLine); + + if (NumNewLines != 0) { + SM.PrintMessage(Loc, SourceMgr::DK_Error, + Prefix + + "-SAME: is not on the same line as the previous match"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.end()), SourceMgr::DK_Note, + "'next' match was here"); + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note, + "previous match ended here"); + return true; + } + + return false; +} + +/// Verify there's no "not strings" in the given buffer. +bool FileCheckString::CheckNot( + const SourceMgr &SM, StringRef Buffer, + const std::vector<const FileCheckPattern *> &NotStrings, + StringMap<StringRef> &VariableTable, const FileCheckRequest &Req, + std::vector<FileCheckDiag> *Diags) const { + for (const FileCheckPattern *Pat : NotStrings) { + assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); + + size_t MatchLen = 0; + size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable); + + if (Pos == StringRef::npos) { + PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, + VariableTable, Req.VerboseVerbose, Diags); + continue; + } + + PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, VariableTable, + Pos, MatchLen, Req, Diags); + + return true; + } + + return false; +} + +/// Match "dag strings" and their mixed "not strings". +size_t +FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer, + std::vector<const FileCheckPattern *> &NotStrings, + StringMap<StringRef> &VariableTable, + const FileCheckRequest &Req, + std::vector<FileCheckDiag> *Diags) const { + if (DagNotStrings.empty()) + return 0; + + // The start of the search range. + size_t StartPos = 0; + + struct MatchRange { + size_t Pos; + size_t End; + }; + // A sorted list of ranges for non-overlapping CHECK-DAG matches. Match + // ranges are erased from this list once they are no longer in the search + // range. + std::list<MatchRange> MatchRanges; + + // We need PatItr and PatEnd later for detecting the end of a CHECK-DAG + // group, so we don't use a range-based for loop here. + for (auto PatItr = DagNotStrings.begin(), PatEnd = DagNotStrings.end(); + PatItr != PatEnd; ++PatItr) { + const FileCheckPattern &Pat = *PatItr; + assert((Pat.getCheckTy() == Check::CheckDAG || + Pat.getCheckTy() == Check::CheckNot) && + "Invalid CHECK-DAG or CHECK-NOT!"); + + if (Pat.getCheckTy() == Check::CheckNot) { + NotStrings.push_back(&Pat); + continue; + } + + assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!"); + + // CHECK-DAG always matches from the start. + size_t MatchLen = 0, MatchPos = StartPos; + + // Search for a match that doesn't overlap a previous match in this + // CHECK-DAG group. + for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) { + StringRef MatchBuffer = Buffer.substr(MatchPos); + size_t MatchPosBuf = Pat.Match(MatchBuffer, MatchLen, VariableTable); + // With a group of CHECK-DAGs, a single mismatching means the match on + // that group of CHECK-DAGs fails immediately. + if (MatchPosBuf == StringRef::npos) { + PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer, + VariableTable, Req.VerboseVerbose, Diags); + return StringRef::npos; + } + // Re-calc it as the offset relative to the start of the original string. + MatchPos += MatchPosBuf; + if (Req.VerboseVerbose) + PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, + VariableTable, MatchPos, MatchLen, Req, Diags); + MatchRange M{MatchPos, MatchPos + MatchLen}; + if (Req.AllowDeprecatedDagOverlap) { + // We don't need to track all matches in this mode, so we just maintain + // one match range that encompasses the current CHECK-DAG group's + // matches. + if (MatchRanges.empty()) + MatchRanges.insert(MatchRanges.end(), M); + else { + auto Block = MatchRanges.begin(); + Block->Pos = std::min(Block->Pos, M.Pos); + Block->End = std::max(Block->End, M.End); + } + break; + } + // Iterate previous matches until overlapping match or insertion point. + bool Overlap = false; + for (; MI != ME; ++MI) { + if (M.Pos < MI->End) { + // !Overlap => New match has no overlap and is before this old match. + // Overlap => New match overlaps this old match. + Overlap = MI->Pos < M.End; + break; + } + } + if (!Overlap) { + // Insert non-overlapping match into list. + MatchRanges.insert(MI, M); + break; + } + if (Req.VerboseVerbose) { + SMLoc OldStart = SMLoc::getFromPointer(Buffer.data() + MI->Pos); + SMLoc OldEnd = SMLoc::getFromPointer(Buffer.data() + MI->End); + SMRange OldRange(OldStart, OldEnd); + SM.PrintMessage(OldStart, SourceMgr::DK_Note, + "match discarded, overlaps earlier DAG match here", + {OldRange}); + if (Diags) + Diags->rbegin()->MatchTy = FileCheckDiag::MatchFoundButDiscarded; + } + MatchPos = MI->End; + } + if (!Req.VerboseVerbose) + PrintMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, Buffer, VariableTable, + MatchPos, MatchLen, Req, Diags); + + // Handle the end of a CHECK-DAG group. + if (std::next(PatItr) == PatEnd || + std::next(PatItr)->getCheckTy() == Check::CheckNot) { + if (!NotStrings.empty()) { + // If there are CHECK-NOTs between two CHECK-DAGs or from CHECK to + // CHECK-DAG, verify that there are no 'not' strings occurred in that + // region. + StringRef SkippedRegion = + Buffer.slice(StartPos, MatchRanges.begin()->Pos); + if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable, Req, Diags)) + return StringRef::npos; + // Clear "not strings". + NotStrings.clear(); + } + // All subsequent CHECK-DAGs and CHECK-NOTs should be matched from the + // end of this CHECK-DAG group's match range. + StartPos = MatchRanges.rbegin()->End; + // Don't waste time checking for (impossible) overlaps before that. + MatchRanges.clear(); + } + } + + return StartPos; +} + +// A check prefix must contain only alphanumeric, hyphens and underscores. +static bool ValidateCheckPrefix(StringRef CheckPrefix) { + Regex Validator("^[a-zA-Z0-9_-]*$"); + return Validator.match(CheckPrefix); +} + +bool llvm::FileCheck::ValidateCheckPrefixes() { + StringSet<> PrefixSet; + + for (StringRef Prefix : Req.CheckPrefixes) { + // Reject empty prefixes. + if (Prefix == "") + return false; + + if (!PrefixSet.insert(Prefix).second) + return false; + + if (!ValidateCheckPrefix(Prefix)) + return false; + } + + return true; +} + +// Combines the check prefixes into a single regex so that we can efficiently +// scan for any of the set. +// +// The semantics are that the longest-match wins which matches our regex +// library. +Regex llvm::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"); + + // We already validated the contents of CheckPrefixes so just concatenate + // them as alternatives. + SmallString<32> PrefixRegexStr; + for (StringRef Prefix : Req.CheckPrefixes) { + if (Prefix != Req.CheckPrefixes.front()) + PrefixRegexStr.push_back('|'); + + PrefixRegexStr.append(Prefix); + } + + return Regex(PrefixRegexStr); +} + +// Remove local variables from \p VariableTable. Global variables +// (start with '$') are preserved. +static void ClearLocalVars(StringMap<StringRef> &VariableTable) { + SmallVector<StringRef, 16> LocalVars; + for (const auto &Var : VariableTable) + if (Var.first()[0] != '$') + LocalVars.push_back(Var.first()); + + for (const auto &Var : LocalVars) + VariableTable.erase(Var); +} + +/// Check the input to FileCheck provided in the \p Buffer against the \p +/// CheckStrings read from the check file. +/// +/// Returns false if the input fails to satisfy the checks. +bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer, + ArrayRef<FileCheckString> CheckStrings, + std::vector<FileCheckDiag> *Diags) { + bool ChecksFailed = false; + + /// VariableTable - This holds all the current filecheck variables. + StringMap<StringRef> VariableTable; + + for (const auto& Def : Req.GlobalDefines) + VariableTable.insert(StringRef(Def).split('=')); + + unsigned i = 0, j = 0, e = CheckStrings.size(); + while (true) { + StringRef CheckRegion; + if (j == e) { + CheckRegion = Buffer; + } else { + const FileCheckString &CheckLabelStr = CheckStrings[j]; + if (CheckLabelStr.Pat.getCheckTy() != Check::CheckLabel) { + ++j; + continue; + } + + // Scan to next CHECK-LABEL match, ignoring CHECK-NOT and CHECK-DAG + size_t MatchLabelLen = 0; + size_t MatchLabelPos = CheckLabelStr.Check( + SM, Buffer, true, MatchLabelLen, VariableTable, Req, Diags); + if (MatchLabelPos == StringRef::npos) + // Immediately bail of CHECK-LABEL fails, nothing else we can do. + return false; + + CheckRegion = Buffer.substr(0, MatchLabelPos + MatchLabelLen); + Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen); + ++j; + } + + if (Req.EnableVarScope) + ClearLocalVars(VariableTable); + + for (; i != j; ++i) { + const FileCheckString &CheckStr = CheckStrings[i]; + + // Check each string within the scanned region, including a second check + // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG) + size_t MatchLen = 0; + size_t MatchPos = CheckStr.Check(SM, CheckRegion, false, MatchLen, + VariableTable, Req, Diags); + + if (MatchPos == StringRef::npos) { + ChecksFailed = true; + i = j; + break; + } + + CheckRegion = CheckRegion.substr(MatchPos + MatchLen); + } + + if (j == e) + break; + } + + // Success if no checks failed. + return !ChecksFailed; +} diff --git a/lib/Support/FileOutputBuffer.cpp b/lib/Support/FileOutputBuffer.cpp index 1214b5a0ba1f..b8223126227d 100644 --- a/lib/Support/FileOutputBuffer.cpp +++ b/lib/Support/FileOutputBuffer.cpp @@ -61,6 +61,12 @@ public: consumeError(Temp.discard()); } + void discard() override { + // Delete the temp file if it still was open, but keeping the mapping + // active. + consumeError(Temp.discard()); + } + private: std::unique_ptr<fs::mapped_file_region> Buffer; fs::TempFile Temp; diff --git a/lib/Support/FoldingSet.cpp b/lib/Support/FoldingSet.cpp index cf9847faccd1..ee69a64ac97b 100644 --- a/lib/Support/FoldingSet.cpp +++ b/lib/Support/FoldingSet.cpp @@ -275,7 +275,7 @@ void FoldingSetBase::GrowBucketCount(unsigned NewBucketCount) { // Clear out new buckets. Buckets = AllocateBuckets(NewBucketCount); - // Set NumBuckets only if allocation of new buckets was succesful + // Set NumBuckets only if allocation of new buckets was successful. NumBuckets = NewBucketCount; NumNodes = 0; diff --git a/lib/Support/FormatVariadic.cpp b/lib/Support/FormatVariadic.cpp index 6dd133e6c50a..1f3505d5f74f 100644 --- a/lib/Support/FormatVariadic.cpp +++ b/lib/Support/FormatVariadic.cpp @@ -152,3 +152,5 @@ formatv_object_base::parseFormatString(StringRef Fmt) { } return Replacements; } + +void detail::format_adapter::anchor() { } diff --git a/lib/Support/Hashing.cpp b/lib/Support/Hashing.cpp index c69efb7c3cc9..7de25cec7371 100644 --- a/lib/Support/Hashing.cpp +++ b/lib/Support/Hashing.cpp @@ -20,10 +20,10 @@ using namespace llvm; // Provide a definition and static initializer for the fixed seed. This // initializer should always be zero to ensure its value can never appear to be // non-zero, even during dynamic initialization. -size_t llvm::hashing::detail::fixed_seed_override = 0; +uint64_t llvm::hashing::detail::fixed_seed_override = 0; // Implement the function for forced setting of the fixed seed. // FIXME: Use atomic operations here so that there is no data race. -void llvm::set_fixed_execution_hash_seed(size_t fixed_value) { +void llvm::set_fixed_execution_hash_seed(uint64_t fixed_value) { hashing::detail::fixed_seed_override = fixed_value; } diff --git a/lib/Support/Host.cpp b/lib/Support/Host.cpp index 2c718dd3f5a8..d5a688c7fb9b 100644 --- a/lib/Support/Host.cpp +++ b/lib/Support/Host.cpp @@ -196,6 +196,32 @@ StringRef sys::detail::getHostCPUNameForARM(StringRef ProcCpuinfoContent) { .Default("generic"); } + if (Implementer == "0x42" || Implementer == "0x43") { // Broadcom | Cavium. + 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("0x516", "thunderx2t99") + .Case("0x0516", "thunderx2t99") + .Case("0xaf", "thunderx2t99") + .Case("0x0af", "thunderx2t99") + .Case("0xa1", "thunderxt88") + .Case("0x0a1", "thunderxt88") + .Default("generic"); + } + } + } + + if (Implementer == "0x48") // HiSilicon Technologies, Inc. + // Look for the CPU part line. + for (unsigned I = 0, E = Lines.size(); I != E; ++I) + if (Lines[I].startswith("CPU part")) + // The CPU part is a 3 digit hexadecimal number with a 0x prefix. The + // values correspond to the "Part number" in the CP15/c0 register. The + // contents are specified in the various processor manuals. + return StringSwitch<const char *>(Lines[I].substr(8).ltrim("\t :")) + .Case("0xd01", "tsv110") + .Default("generic"); + if (Implementer == "0x51") // Qualcomm Technologies, Inc. // Look for the CPU part line. for (unsigned I = 0, E = Lines.size(); I != E; ++I) @@ -496,8 +522,8 @@ static void detectX86FamilyModel(unsigned EAX, unsigned *Family, static void getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, unsigned Brand_id, unsigned Features, - unsigned Features2, unsigned *Type, - unsigned *Subtype) { + unsigned Features2, unsigned Features3, + unsigned *Type, unsigned *Subtype) { if (Brand_id != 0) return; switch (Family) { @@ -664,12 +690,24 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, break; default: // Unknown family 6 CPU, try to guess. + 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_AVX512VNNI - 32))) { + *Type = X86::INTEL_COREI7; + *Subtype = X86::INTEL_COREI7_CASCADELAKE; + break; + } + if (Features & (1 << X86::FEATURE_AVX512VL)) { *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_SKYLAKE_AVX512; @@ -681,8 +719,8 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, break; } - if (Features2 & (1 << (X86::FEATURE_CLFLUSHOPT - 32))) { - if (Features2 & (1 << (X86::FEATURE_SHA - 32))) { + if (Features3 & (1 << (X86::FEATURE_CLFLUSHOPT - 64))) { + if (Features3 & (1 << (X86::FEATURE_SHA - 64))) { *Type = X86::INTEL_GOLDMONT; } else { *Type = X86::INTEL_COREI7; @@ -690,7 +728,7 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, } break; } - if (Features2 & (1 << (X86::FEATURE_ADX - 32))) { + if (Features3 & (1 << (X86::FEATURE_ADX - 64))) { *Type = X86::INTEL_COREI7; *Subtype = X86::INTEL_COREI7_BROADWELL; break; @@ -706,7 +744,7 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, break; } if (Features & (1 << X86::FEATURE_SSE4_2)) { - if (Features2 & (1 << (X86::FEATURE_MOVBE - 32))) { + if (Features3 & (1 << (X86::FEATURE_MOVBE - 64))) { *Type = X86::INTEL_SILVERMONT; } else { *Type = X86::INTEL_COREI7; @@ -720,7 +758,7 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, break; } if (Features & (1 << X86::FEATURE_SSSE3)) { - if (Features2 & (1 << (X86::FEATURE_MOVBE - 32))) { + if (Features3 & (1 << (X86::FEATURE_MOVBE - 64))) { *Type = X86::INTEL_BONNELL; // "bonnell" } else { *Type = X86::INTEL_CORE2; // "core2" @@ -728,7 +766,7 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, } break; } - if (Features2 & (1 << (X86::FEATURE_EM64T - 32))) { + if (Features3 & (1 << (X86::FEATURE_EM64T - 64))) { *Type = X86::INTEL_CORE2; // "core2" *Subtype = X86::INTEL_CORE2_65; break; @@ -754,7 +792,7 @@ getIntelProcessorTypeAndSubtype(unsigned Family, unsigned Model, } break; case 15: { - if (Features2 & (1 << (X86::FEATURE_EM64T - 32))) { + if (Features3 & (1 << (X86::FEATURE_EM64T - 64))) { *Type = X86::INTEL_NOCONA; break; } @@ -862,40 +900,52 @@ static void getAMDProcessorTypeAndSubtype(unsigned Family, unsigned Model, } static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf, - unsigned *FeaturesOut, - unsigned *Features2Out) { + unsigned *FeaturesOut, unsigned *Features2Out, + unsigned *Features3Out) { unsigned Features = 0; unsigned Features2 = 0; + unsigned Features3 = 0; 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"); + }; + if ((EDX >> 15) & 1) - Features |= 1 << X86::FEATURE_CMOV; + setFeature(X86::FEATURE_CMOV); if ((EDX >> 23) & 1) - Features |= 1 << X86::FEATURE_MMX; + setFeature(X86::FEATURE_MMX); if ((EDX >> 25) & 1) - Features |= 1 << X86::FEATURE_SSE; + setFeature(X86::FEATURE_SSE); if ((EDX >> 26) & 1) - Features |= 1 << X86::FEATURE_SSE2; + setFeature(X86::FEATURE_SSE2); if ((ECX >> 0) & 1) - Features |= 1 << X86::FEATURE_SSE3; + setFeature(X86::FEATURE_SSE3); if ((ECX >> 1) & 1) - Features |= 1 << X86::FEATURE_PCLMUL; + setFeature(X86::FEATURE_PCLMUL); if ((ECX >> 9) & 1) - Features |= 1 << X86::FEATURE_SSSE3; + setFeature(X86::FEATURE_SSSE3); if ((ECX >> 12) & 1) - Features |= 1 << X86::FEATURE_FMA; + setFeature(X86::FEATURE_FMA); if ((ECX >> 19) & 1) - Features |= 1 << X86::FEATURE_SSE4_1; + setFeature(X86::FEATURE_SSE4_1); if ((ECX >> 20) & 1) - Features |= 1 << X86::FEATURE_SSE4_2; + setFeature(X86::FEATURE_SSE4_2); if ((ECX >> 23) & 1) - Features |= 1 << X86::FEATURE_POPCNT; + setFeature(X86::FEATURE_POPCNT); if ((ECX >> 25) & 1) - Features |= 1 << X86::FEATURE_AES; + setFeature(X86::FEATURE_AES); if ((ECX >> 22) & 1) - Features2 |= 1 << (X86::FEATURE_MOVBE - 32); + setFeature(X86::FEATURE_MOVBE); // If CPUID indicates support for XSAVE, XRESTORE and AVX, and XGETBV // indicates that the AVX registers will be saved and restored on context @@ -906,49 +956,59 @@ static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf, bool HasAVX512Save = HasAVX && ((EAX & 0xe0) == 0xe0); if (HasAVX) - Features |= 1 << X86::FEATURE_AVX; + setFeature(X86::FEATURE_AVX); bool HasLeaf7 = MaxLeaf >= 0x7 && !getX86CpuIDAndInfoEx(0x7, 0x0, &EAX, &EBX, &ECX, &EDX); if (HasLeaf7 && ((EBX >> 3) & 1)) - Features |= 1 << X86::FEATURE_BMI; + setFeature(X86::FEATURE_BMI); if (HasLeaf7 && ((EBX >> 5) & 1) && HasAVX) - Features |= 1 << X86::FEATURE_AVX2; + setFeature(X86::FEATURE_AVX2); if (HasLeaf7 && ((EBX >> 9) & 1)) - Features |= 1 << X86::FEATURE_BMI2; + setFeature(X86::FEATURE_BMI2); if (HasLeaf7 && ((EBX >> 16) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512F; + setFeature(X86::FEATURE_AVX512F); if (HasLeaf7 && ((EBX >> 17) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512DQ; + setFeature(X86::FEATURE_AVX512DQ); if (HasLeaf7 && ((EBX >> 19) & 1)) - Features2 |= 1 << (X86::FEATURE_ADX - 32); + setFeature(X86::FEATURE_ADX); if (HasLeaf7 && ((EBX >> 21) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512IFMA; + setFeature(X86::FEATURE_AVX512IFMA); if (HasLeaf7 && ((EBX >> 23) & 1)) - Features2 |= 1 << (X86::FEATURE_CLFLUSHOPT - 32); + setFeature(X86::FEATURE_CLFLUSHOPT); if (HasLeaf7 && ((EBX >> 26) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512PF; + setFeature(X86::FEATURE_AVX512PF); if (HasLeaf7 && ((EBX >> 27) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512ER; + setFeature(X86::FEATURE_AVX512ER); if (HasLeaf7 && ((EBX >> 28) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512CD; + setFeature(X86::FEATURE_AVX512CD); if (HasLeaf7 && ((EBX >> 29) & 1)) - Features2 |= 1 << (X86::FEATURE_SHA - 32); + setFeature(X86::FEATURE_SHA); if (HasLeaf7 && ((EBX >> 30) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512BW; + setFeature(X86::FEATURE_AVX512BW); if (HasLeaf7 && ((EBX >> 31) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512VL; + setFeature(X86::FEATURE_AVX512VL); if (HasLeaf7 && ((ECX >> 1) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512VBMI; + setFeature(X86::FEATURE_AVX512VBMI); + if (HasLeaf7 && ((ECX >> 6) & 1) && HasAVX512Save) + setFeature(X86::FEATURE_AVX512VBMI2); + if (HasLeaf7 && ((ECX >> 8) & 1)) + setFeature(X86::FEATURE_GFNI); + if (HasLeaf7 && ((ECX >> 10) & 1) && HasAVX) + setFeature(X86::FEATURE_VPCLMULQDQ); + if (HasLeaf7 && ((ECX >> 11) & 1) && HasAVX512Save) + setFeature(X86::FEATURE_AVX512VNNI); + if (HasLeaf7 && ((ECX >> 12) & 1) && HasAVX512Save) + setFeature(X86::FEATURE_AVX512BITALG); if (HasLeaf7 && ((ECX >> 14) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX512VPOPCNTDQ; + setFeature(X86::FEATURE_AVX512VPOPCNTDQ); if (HasLeaf7 && ((EDX >> 2) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX5124VNNIW; + setFeature(X86::FEATURE_AVX5124VNNIW); if (HasLeaf7 && ((EDX >> 3) & 1) && HasAVX512Save) - Features |= 1 << X86::FEATURE_AVX5124FMAPS; + setFeature(X86::FEATURE_AVX5124FMAPS); unsigned MaxExtLevel; getX86CpuIDAndInfo(0x80000000, &MaxExtLevel, &EBX, &ECX, &EDX); @@ -956,17 +1016,18 @@ static void getAvailableFeatures(unsigned ECX, unsigned EDX, unsigned MaxLeaf, bool HasExtLeaf1 = MaxExtLevel >= 0x80000001 && !getX86CpuIDAndInfo(0x80000001, &EAX, &EBX, &ECX, &EDX); if (HasExtLeaf1 && ((ECX >> 6) & 1)) - Features |= 1 << X86::FEATURE_SSE4_A; + setFeature(X86::FEATURE_SSE4_A); if (HasExtLeaf1 && ((ECX >> 11) & 1)) - Features |= 1 << X86::FEATURE_XOP; + setFeature(X86::FEATURE_XOP); if (HasExtLeaf1 && ((ECX >> 16) & 1)) - Features |= 1 << X86::FEATURE_FMA4; + setFeature(X86::FEATURE_FMA4); if (HasExtLeaf1 && ((EDX >> 29) & 1)) - Features2 |= 1 << (X86::FEATURE_EM64T - 32); + setFeature(X86::FEATURE_EM64T); *FeaturesOut = Features; *Features2Out = Features2; + *Features3Out = Features3; } StringRef sys::getHostCPUName() { @@ -987,16 +1048,16 @@ StringRef sys::getHostCPUName() { unsigned Brand_id = EBX & 0xff; unsigned Family = 0, Model = 0; - unsigned Features = 0, Features2 = 0; + unsigned Features = 0, Features2 = 0, Features3 = 0; detectX86FamilyModel(EAX, &Family, &Model); - getAvailableFeatures(ECX, EDX, MaxLeaf, &Features, &Features2); + getAvailableFeatures(ECX, EDX, MaxLeaf, &Features, &Features2, &Features3); unsigned Type = 0; unsigned Subtype = 0; if (Vendor == SIG_INTEL) { getIntelProcessorTypeAndSubtype(Family, Model, Brand_id, Features, - Features2, &Type, &Subtype); + Features2, Features3, &Type, &Subtype); } else if (Vendor == SIG_AMD) { getAMDProcessorTypeAndSubtype(Family, Model, Features, &Type, &Subtype); } @@ -1022,8 +1083,10 @@ StringRef sys::getHostCPUName() { mach_msg_type_number_t infoCount; infoCount = HOST_BASIC_INFO_COUNT; - host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, + mach_port_t hostPort = mach_host_self(); + host_info(hostPort, HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount); + mach_port_deallocate(mach_task_self(), hostPort); if (hostInfo.cpu_type != CPU_TYPE_POWERPC) return "generic"; @@ -1215,6 +1278,8 @@ bool sys::getHostCPUFeatures(StringMap<bool> &Features) { Features["tbm"] = HasExtLeaf1 && ((ECX >> 21) & 1); Features["mwaitx"] = HasExtLeaf1 && ((ECX >> 29) & 1); + Features["64bit"] = HasExtLeaf1 && ((EDX >> 29) & 1); + // Miscellaneous memory related features, detected by // using the 0x80000008 leaf of the CPUID instruction bool HasExtLeaf8 = MaxExtLevel >= 0x80000008 && diff --git a/lib/Support/ItaniumManglingCanonicalizer.cpp b/lib/Support/ItaniumManglingCanonicalizer.cpp new file mode 100644 index 000000000000..e55dcd761809 --- /dev/null +++ b/lib/Support/ItaniumManglingCanonicalizer.cpp @@ -0,0 +1,322 @@ +//===----------------- ItaniumManglingCanonicalizer.cpp -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/ItaniumManglingCanonicalizer.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; +using llvm::itanium_demangle::NodeKind; + +namespace { +struct FoldingSetNodeIDBuilder { + llvm::FoldingSetNodeID &ID; + void operator()(const Node *P) { ID.AddPointer(P); } + 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 + operator()(T V) { + ID.AddInteger((unsigned long long)V); + } + void operator()(itanium_demangle::NodeOrString NS) { + if (NS.isNode()) { + ID.AddInteger(0); + (*this)(NS.asNode()); + } else if (NS.isString()) { + ID.AddInteger(1); + (*this)(NS.asString()); + } else { + ID.AddInteger(2); + } + } + void operator()(itanium_demangle::NodeArray A) { + ID.AddInteger(A.size()); + for (const Node *N : A) + (*this)(N); + } +}; + +template<typename ...T> +void profileCtor(llvm::FoldingSetNodeID &ID, Node::Kind K, T ...V) { + FoldingSetNodeIDBuilder Builder = {ID}; + Builder(K); + int VisitInOrder[] = { + (Builder(V), 0) ..., + 0 // Avoid empty array if there are no arguments. + }; + (void)VisitInOrder; +} + +// FIXME: Convert this to a generic lambda when possible. +template<typename NodeT> struct ProfileSpecificNode { + FoldingSetNodeID &ID; + template<typename ...T> void operator()(T ...V) { + profileCtor(ID, NodeKind<NodeT>::Kind, V...); + } +}; + +struct ProfileNode { + FoldingSetNodeID &ID; + template<typename NodeT> void operator()(const NodeT *N) { + N->match(ProfileSpecificNode<NodeT>{ID}); + } +}; + +template<> void ProfileNode::operator()(const ForwardTemplateReference *N) { + llvm_unreachable("should never canonicalize a ForwardTemplateReference"); +} + +void profileNode(llvm::FoldingSetNodeID &ID, const Node *N) { + N->visit(ProfileNode{ID}); +} + +class FoldingNodeAllocator { + class alignas(alignof(Node *)) NodeHeader : public llvm::FoldingSetNode { + public: + // 'Node' in this context names the injected-class-name of the base class. + itanium_demangle::Node *getNode() { + return reinterpret_cast<itanium_demangle::Node *>(this + 1); + } + void Profile(llvm::FoldingSetNodeID &ID) { profileNode(ID, getNode()); } + }; + + BumpPtrAllocator RawAlloc; + llvm::FoldingSet<NodeHeader> Nodes; + +public: + void reset() {} + + template <typename T, typename... Args> + std::pair<Node *, bool> getOrCreateNode(bool CreateNewNodes, Args &&... As) { + // FIXME: Don't canonicalize forward template references for now, because + // they contain state (the resolved template node) that's not known at their + // point of creation. + if (std::is_same<T, ForwardTemplateReference>::value) { + // Note that we don't use if-constexpr here and so we must still write + // this code in a generic form. + return {new (RawAlloc.Allocate(sizeof(T), alignof(T))) + T(std::forward<Args>(As)...), + true}; + } + + llvm::FoldingSetNodeID ID; + profileCtor(ID, NodeKind<T>::Kind, As...); + + void *InsertPos; + if (NodeHeader *Existing = Nodes.FindNodeOrInsertPos(ID, InsertPos)) + return {static_cast<T*>(Existing->getNode()), false}; + + if (!CreateNewNodes) + return {nullptr, true}; + + static_assert(alignof(T) <= alignof(NodeHeader), + "underaligned node header for specific node kind"); + void *Storage = + RawAlloc.Allocate(sizeof(NodeHeader) + sizeof(T), alignof(NodeHeader)); + NodeHeader *New = new (Storage) NodeHeader; + T *Result = new (New->getNode()) T(std::forward<Args>(As)...); + Nodes.InsertNode(New, InsertPos); + return {Result, true}; + } + + template<typename T, typename... Args> + Node *makeNode(Args &&...As) { + return getOrCreateNode<T>(true, std::forward<Args>(As)...).first; + } + + void *allocateNodeArray(size_t sz) { + return RawAlloc.Allocate(sizeof(Node *) * sz, alignof(Node *)); + } +}; + +class CanonicalizerAllocator : public FoldingNodeAllocator { + Node *MostRecentlyCreated = nullptr; + Node *TrackedNode = nullptr; + bool TrackedNodeIsUsed = false; + bool CreateNewNodes = true; + llvm::SmallDenseMap<Node*, Node*, 32> Remappings; + + template<typename T, typename ...Args> Node *makeNodeSimple(Args &&...As) { + std::pair<Node *, bool> Result = + getOrCreateNode<T>(CreateNewNodes, std::forward<Args>(As)...); + if (Result.second) { + // Node is new. Make a note of that. + MostRecentlyCreated = Result.first; + } else if (Result.first) { + // Node is pre-existing; check if it's in our remapping table. + if (auto *N = Remappings.lookup(Result.first)) { + Result.first = N; + assert(Remappings.find(Result.first) == Remappings.end() && + "should never need multiple remap steps"); + } + if (Result.first == TrackedNode) + TrackedNodeIsUsed = true; + } + return Result.first; + } + + /// Helper to allow makeNode to be partially-specialized on T. + template<typename T> struct MakeNodeImpl { + CanonicalizerAllocator &Self; + template<typename ...Args> Node *make(Args &&...As) { + return Self.makeNodeSimple<T>(std::forward<Args>(As)...); + } + }; + +public: + template<typename T, typename ...Args> Node *makeNode(Args &&...As) { + return MakeNodeImpl<T>{*this}.make(std::forward<Args>(As)...); + } + + void reset() { MostRecentlyCreated = nullptr; } + + void setCreateNewNodes(bool CNN) { CreateNewNodes = CNN; } + + void addRemapping(Node *A, Node *B) { + // Note, we don't need to check whether B is also remapped, because if it + // was we would have already remapped it when building it. + Remappings.insert(std::make_pair(A, B)); + } + + bool isMostRecentlyCreated(Node *N) const { return MostRecentlyCreated == N; } + + void trackUsesOf(Node *N) { + TrackedNode = N; + TrackedNodeIsUsed = false; + } + bool trackedNodeIsUsed() const { return TrackedNodeIsUsed; } +}; + +/// Convert St3foo to NSt3fooE so that equivalences naming one also affect the +/// other. +template<> +struct CanonicalizerAllocator::MakeNodeImpl< + itanium_demangle::StdQualifiedName> { + CanonicalizerAllocator &Self; + Node *make(Node *Child) { + Node *StdNamespace = Self.makeNode<itanium_demangle::NameType>("std"); + if (!StdNamespace) + return nullptr; + return Self.makeNode<itanium_demangle::NestedName>(StdNamespace, Child); + } +}; + +// FIXME: Also expand built-in substitutions? + +using CanonicalizingDemangler = + itanium_demangle::ManglingParser<CanonicalizerAllocator>; +} + +struct ItaniumManglingCanonicalizer::Impl { + CanonicalizingDemangler Demangler = {nullptr, nullptr}; +}; + +ItaniumManglingCanonicalizer::ItaniumManglingCanonicalizer() : P(new Impl) {} +ItaniumManglingCanonicalizer::~ItaniumManglingCanonicalizer() { delete P; } + +ItaniumManglingCanonicalizer::EquivalenceError +ItaniumManglingCanonicalizer::addEquivalence(FragmentKind Kind, StringRef First, + StringRef Second) { + auto &Alloc = P->Demangler.ASTAllocator; + Alloc.setCreateNewNodes(true); + + auto Parse = [&](StringRef Str) { + P->Demangler.reset(Str.begin(), Str.end()); + Node *N = nullptr; + switch (Kind) { + // A <name>, with minor extensions to allow arbitrary namespace and + // template names that can't easily be written as <name>s. + case FragmentKind::Name: + // Very special case: allow "St" as a shorthand for "3std". It's not + // valid as a <name> mangling, but is nonetheless the most natural + // way to name the 'std' namespace. + if (Str.size() == 2 && P->Demangler.consumeIf("St")) + N = P->Demangler.make<itanium_demangle::NameType>("std"); + // We permit substitutions to name templates without their template + // arguments. This mostly just falls out, as almost all template names + // are valid as <name>s, but we also want to parse <substitution>s as + // <name>s, even though they're not. + else if (Str.startswith("S")) + // Parse the substitution and optional following template arguments. + N = P->Demangler.parseType(); + else + N = P->Demangler.parseName(); + break; + + // A <type>. + case FragmentKind::Type: + N = P->Demangler.parseType(); + break; + + // An <encoding>. + case FragmentKind::Encoding: + N = P->Demangler.parseEncoding(); + break; + } + + // If we have trailing junk, the mangling is invalid. + if (P->Demangler.numLeft() != 0) + N = nullptr; + + // If any node was created after N, then we cannot safely remap it because + // it might already be in use by another node. + return std::make_pair(N, Alloc.isMostRecentlyCreated(N)); + }; + + Node *FirstNode, *SecondNode; + bool FirstIsNew, SecondIsNew; + + std::tie(FirstNode, FirstIsNew) = Parse(First); + if (!FirstNode) + return EquivalenceError::InvalidFirstMangling; + + Alloc.trackUsesOf(FirstNode); + std::tie(SecondNode, SecondIsNew) = Parse(Second); + if (!SecondNode) + return EquivalenceError::InvalidSecondMangling; + + // If they're already equivalent, there's nothing to do. + if (FirstNode == SecondNode) + return EquivalenceError::Success; + + if (FirstIsNew && !Alloc.trackedNodeIsUsed()) + Alloc.addRemapping(FirstNode, SecondNode); + else if (SecondIsNew) + Alloc.addRemapping(SecondNode, FirstNode); + else + return EquivalenceError::ManglingAlreadyUsed; + + return EquivalenceError::Success; +} + +ItaniumManglingCanonicalizer::Key +ItaniumManglingCanonicalizer::canonicalize(StringRef Mangling) { + P->Demangler.ASTAllocator.setCreateNewNodes(true); + P->Demangler.reset(Mangling.begin(), Mangling.end()); + return reinterpret_cast<Key>(P->Demangler.parse()); +} + +ItaniumManglingCanonicalizer::Key +ItaniumManglingCanonicalizer::lookup(StringRef Mangling) { + P->Demangler.ASTAllocator.setCreateNewNodes(false); + P->Demangler.reset(Mangling.begin(), Mangling.end()); + return reinterpret_cast<Key>(P->Demangler.parse()); +} diff --git a/lib/Support/JSON.cpp b/lib/Support/JSON.cpp index a5dae7a7c2e0..d468013fb94a 100644 --- a/lib/Support/JSON.cpp +++ b/lib/Support/JSON.cpp @@ -517,7 +517,7 @@ static std::vector<const Object::value_type *> sortedElements(const Object &O) { std::vector<const Object::value_type *> Elements; for (const auto &E : O) Elements.push_back(&E); - llvm::sort(Elements.begin(), Elements.end(), + llvm::sort(Elements, [](const Object::value_type *L, const Object::value_type *R) { return L->first < R->first; }); diff --git a/lib/Support/Locale.cpp b/lib/Support/Locale.cpp index e57d377c9ab5..1b3300b90f2a 100644 --- a/lib/Support/Locale.cpp +++ b/lib/Support/Locale.cpp @@ -7,24 +7,11 @@ namespace sys { namespace locale { int columnWidth(StringRef Text) { -#if _WIN32 - return Text.size(); -#else return llvm::sys::unicode::columnWidthUTF8(Text); -#endif } bool isPrint(int UCS) { -#if _WIN32 - // Restrict characters that we'll try to print to the lower part of ASCII - // except for the control characters (0x20 - 0x7E). In general one can not - // reliably output code points U+0080 and higher using narrow character C/C++ - // output functions in Windows, because the meaning of the upper 128 codes is - // determined by the active code page in the console. - return ' ' <= UCS && UCS <= '~'; -#else return llvm::sys::unicode::isPrintable(UCS); -#endif } } // namespace locale diff --git a/lib/Support/LockFileManager.cpp b/lib/Support/LockFileManager.cpp index 77baf7ac4bdd..c166230ba3a3 100644 --- a/lib/Support/LockFileManager.cpp +++ b/lib/Support/LockFileManager.cpp @@ -24,7 +24,7 @@ #include <sys/types.h> #include <system_error> #include <tuple> -#if _WIN32 +#ifdef _WIN32 #include <windows.h> #endif #if LLVM_ON_UNIX @@ -295,7 +295,7 @@ LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { if (getState() != LFS_Shared) return Res_Success; -#if _WIN32 +#ifdef _WIN32 unsigned long Interval = 1; #else struct timespec Interval; @@ -310,7 +310,7 @@ LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { // 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? -#if _WIN32 +#ifdef _WIN32 Sleep(Interval); #else nanosleep(&Interval, nullptr); @@ -329,7 +329,7 @@ LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { return Res_OwnerDied; // Exponentially increase the time we wait for the lock to be removed. -#if _WIN32 +#ifdef _WIN32 Interval *= 2; #else Interval.tv_sec *= 2; @@ -340,7 +340,7 @@ LockFileManager::WaitForUnlockResult LockFileManager::waitForUnlock() { } #endif } while ( -#if _WIN32 +#ifdef _WIN32 Interval < MaxSeconds * 1000 #else Interval.tv_sec < (time_t)MaxSeconds diff --git a/lib/Support/Path.cpp b/lib/Support/Path.cpp index 098230290ed2..5ce2f50ebdaa 100644 --- a/lib/Support/Path.cpp +++ b/lib/Support/Path.cpp @@ -190,48 +190,57 @@ createUniqueEntity(const Twine &Model, int &ResultFD, ResultPath.push_back(0); ResultPath.pop_back(); -retry_random_path: - // Replace '%' with random chars. - for (unsigned i = 0, e = ModelStorage.size(); i != e; ++i) { - if (ModelStorage[i] == '%') - ResultPath[i] = "0123456789abcdef"[sys::Process::GetRandomNumber() & 15]; - } - - // Try to open + create the file. - switch (Type) { - case FS_File: { - if (std::error_code EC = - sys::fs::openFileForReadWrite(Twine(ResultPath.begin()), ResultFD, - sys::fs::CD_CreateNew, Flags, Mode)) { - if (EC == errc::file_exists) - goto retry_random_path; - return EC; + // Limit the number of attempts we make, so that we don't infinite loop. E.g. + // "permission denied" could be for a specific file (so we retry with a + // different name) or for the whole directory (retry would always fail). + // Checking which is racy, so we try a number of times, then give up. + std::error_code EC; + for (int Retries = 128; Retries > 0; --Retries) { + // Replace '%' with random chars. + for (unsigned i = 0, e = ModelStorage.size(); i != e; ++i) { + if (ModelStorage[i] == '%') + ResultPath[i] = + "0123456789abcdef"[sys::Process::GetRandomNumber() & 15]; } - return std::error_code(); - } + // Try to open + create the file. + switch (Type) { + case FS_File: { + EC = sys::fs::openFileForReadWrite(Twine(ResultPath.begin()), ResultFD, + sys::fs::CD_CreateNew, Flags, Mode); + if (EC) { + // errc::permission_denied happens on Windows when we try to open a file + // that has been marked for deletion. + if (EC == errc::file_exists || EC == errc::permission_denied) + continue; + return EC; + } - case FS_Name: { - std::error_code EC = - sys::fs::access(ResultPath.begin(), sys::fs::AccessMode::Exist); - if (EC == errc::no_such_file_or_directory) return std::error_code(); - if (EC) - return EC; - goto retry_random_path; - } + } - case FS_Dir: { - if (std::error_code EC = - sys::fs::create_directory(ResultPath.begin(), false)) { - if (EC == errc::file_exists) - goto retry_random_path; - return EC; + case FS_Name: { + EC = sys::fs::access(ResultPath.begin(), sys::fs::AccessMode::Exist); + if (EC == errc::no_such_file_or_directory) + return std::error_code(); + if (EC) + return EC; + continue; } - return std::error_code(); - } + + case FS_Dir: { + EC = sys::fs::create_directory(ResultPath.begin(), false); + if (EC) { + if (EC == errc::file_exists) + continue; + return EC; + } + return std::error_code(); + } + } + llvm_unreachable("Invalid Type"); } - llvm_unreachable("Invalid Type"); + return EC; } namespace llvm { @@ -524,7 +533,7 @@ void replace_path_prefix(SmallVectorImpl<char> &Path, // If prefixes have the same size we can simply copy the new one over. if (OldPrefix.size() == NewPrefix.size()) { - std::copy(NewPrefix.begin(), NewPrefix.end(), Path.begin()); + llvm::copy(NewPrefix, Path.begin()); return; } @@ -840,9 +849,8 @@ getPotentiallyUniqueTempFileName(const Twine &Prefix, StringRef Suffix, return createTemporaryFile(Prefix, Suffix, Dummy, ResultPath, FS_Name); } -static std::error_code make_absolute(const Twine ¤t_directory, - SmallVectorImpl<char> &path, - bool use_current_directory) { +void make_absolute(const Twine ¤t_directory, + SmallVectorImpl<char> &path) { StringRef p(path.data(), path.size()); bool rootDirectory = path::has_root_directory(p); @@ -851,14 +859,11 @@ static std::error_code make_absolute(const Twine ¤t_directory, // Already absolute. if (rootName && rootDirectory) - return std::error_code(); + return; // All of the following conditions will need the current directory. SmallString<128> current_dir; - if (use_current_directory) - current_directory.toVector(current_dir); - else if (std::error_code ec = current_path(current_dir)) - return ec; + current_directory.toVector(current_dir); // Relative path. Prepend the current directory. if (!rootName && !rootDirectory) { @@ -866,7 +871,7 @@ static std::error_code make_absolute(const Twine ¤t_directory, path::append(current_dir, p); // Set path to the result. path.swap(current_dir); - return std::error_code(); + return; } if (!rootName && rootDirectory) { @@ -875,7 +880,7 @@ static std::error_code make_absolute(const Twine ¤t_directory, path::append(curDirRootName, p); // Set path to the result. path.swap(curDirRootName); - return std::error_code(); + return; } if (rootName && !rootDirectory) { @@ -887,20 +892,23 @@ static std::error_code make_absolute(const Twine ¤t_directory, SmallString<128> res; path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath); path.swap(res); - return std::error_code(); + return; } llvm_unreachable("All rootName and rootDirectory combinations should have " "occurred above!"); } -std::error_code make_absolute(const Twine ¤t_directory, - SmallVectorImpl<char> &path) { - return make_absolute(current_directory, path, true); -} - std::error_code make_absolute(SmallVectorImpl<char> &path) { - return make_absolute(Twine(), path, false); + if (path::is_absolute(path)) + return {}; + + SmallString<128> current_dir; + if (std::error_code ec = current_path(current_dir)) + return ec; + + make_absolute(current_dir, path); + return {}; } std::error_code create_directories(const Twine &Path, bool IgnoreExisting, @@ -1076,12 +1084,13 @@ std::error_code is_other(const Twine &Path, bool &Result) { return std::error_code(); } -void directory_entry::replace_filename(const Twine &filename, - basic_file_status st) { - SmallString<128> path = path::parent_path(Path); - path::append(path, filename); - Path = path.str(); - Status = st; +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->Type = Type; + this->Status = Status; } ErrorOr<perms> getPermissions(const Twine &Path) { @@ -1150,8 +1159,16 @@ Error TempFile::keep(const Twine &Name) { // If we can't cancel the delete don't rename. auto H = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); std::error_code RenameEC = setDeleteDisposition(H, false); - if (!RenameEC) + if (!RenameEC) { RenameEC = rename_fd(FD, Name); + // If rename failed because it's cross-device, copy instead + if (RenameEC == + std::error_code(ERROR_NOT_SAME_DEVICE, std::system_category())) { + RenameEC = copy_file(TmpName, Name); + setDeleteDisposition(H, true); + } + } + // If we can't rename, discard the temporary file. if (RenameEC) setDeleteDisposition(H, true); @@ -1222,17 +1239,5 @@ Expected<TempFile> TempFile::create(const Twine &Model, unsigned Mode) { } } -namespace path { - -bool user_cache_directory(SmallVectorImpl<char> &Result, const Twine &Path1, - const Twine &Path2, const Twine &Path3) { - if (getUserCacheDir(Result)) { - append(Result, Path1, Path2, Path3); - return true; - } - return false; -} - -} // end namespace path } // end namsspace sys } // end namespace llvm diff --git a/lib/Support/Process.cpp b/lib/Support/Process.cpp index 3f5a9d722ca0..f32355aefbb7 100644 --- a/lib/Support/Process.cpp +++ b/lib/Support/Process.cpp @@ -15,6 +15,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Config/llvm-config.h" +#include "llvm/Config/config.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" @@ -82,12 +83,11 @@ static const char colorcodes[2][2][8][10] = { { ALLCOLORS("4",""), ALLCOLORS("4","1;") } }; -// This is set to true when Process::PreventCoreFiles() is called. -static bool coreFilesPrevented = false; +// A CMake option controls wheter we emit core dumps by default. An application +// may disable core dumps by calling Process::PreventCoreFiles(). +static bool coreFilesPrevented = !LLVM_ENABLE_CRASH_DUMPS; -bool Process::AreCoreFilesPrevented() { - return coreFilesPrevented; -} +bool Process::AreCoreFilesPrevented() { return coreFilesPrevented; } // Include the platform-specific parts of this class. #ifdef LLVM_ON_UNIX diff --git a/lib/Support/RandomNumberGenerator.cpp b/lib/Support/RandomNumberGenerator.cpp index f1f22af82a81..df0d87fab021 100644 --- a/lib/Support/RandomNumberGenerator.cpp +++ b/lib/Support/RandomNumberGenerator.cpp @@ -49,7 +49,7 @@ RandomNumberGenerator::RandomNumberGenerator(StringRef Salt) { Data[0] = Seed; Data[1] = Seed >> 32; - std::copy(Salt.begin(), Salt.end(), Data.begin() + 2); + llvm::copy(Salt, Data.begin() + 2); std::seed_seq SeedSeq(Data.begin(), Data.end()); Generator.seed(SeedSeq); diff --git a/lib/Support/Signals.cpp b/lib/Support/Signals.cpp index 6534ff69b84c..333f492d4589 100644 --- a/lib/Support/Signals.cpp +++ b/lib/Support/Signals.cpp @@ -20,6 +20,8 @@ #include "llvm/Support/FileSystem.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/FormatAdapters.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Mutex.h" @@ -155,7 +157,7 @@ static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace, } Optional<StringRef> Redirects[] = {StringRef(InputFile), - StringRef(OutputFile), llvm::None}; + StringRef(OutputFile), StringRef("")}; StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining", #ifdef _WIN32 // Pass --relative-address on Windows so that we don't @@ -180,8 +182,14 @@ static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace, auto CurLine = Lines.begin(); int frame_no = 0; for (int i = 0; i < Depth; i++) { + auto PrintLineHeader = [&]() { + OS << right_justify(formatv("#{0}", frame_no++).str(), + std::log10(Depth) + 2) + << ' ' << format_ptr(StackTrace[i]) << ' '; + }; if (!Modules[i]) { - OS << '#' << frame_no++ << ' ' << format_ptr(StackTrace[i]) << '\n'; + PrintLineHeader(); + OS << '\n'; continue; } // Read pairs of lines (function name and file/line info) until we @@ -192,7 +200,7 @@ static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace, StringRef FunctionName = *CurLine++; if (FunctionName.empty()) break; - OS << '#' << frame_no++ << ' ' << format_ptr(StackTrace[i]) << ' '; + PrintLineHeader(); if (!FunctionName.startswith("??")) OS << FunctionName << ' '; if (CurLine == Lines.end()) diff --git a/lib/Support/SourceMgr.cpp b/lib/Support/SourceMgr.cpp index d8fde7fa8990..a55ad881d012 100644 --- a/lib/Support/SourceMgr.cpp +++ b/lib/Support/SourceMgr.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/SMLoc.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> @@ -269,7 +270,7 @@ SMDiagnostic::SMDiagnostic(const SourceMgr &sm, SMLoc L, StringRef FN, : SM(&sm), Loc(L), Filename(FN), LineNo(Line), ColumnNo(Col), Kind(Kind), Message(Msg), LineContents(LineStr), Ranges(Ranges.vec()), FixIts(Hints.begin(), Hints.end()) { - llvm::sort(FixIts.begin(), FixIts.end()); + llvm::sort(FixIts); } static void buildFixItLine(std::string &CaretLine, std::string &FixItLine, @@ -345,12 +346,18 @@ static void buildFixItLine(std::string &CaretLine, std::string &FixItLine, static void printSourceLine(raw_ostream &S, StringRef LineContents) { // Print out the source line one character at a time, so we can expand tabs. for (unsigned i = 0, e = LineContents.size(), OutCol = 0; i != e; ++i) { - if (LineContents[i] != '\t') { - S << LineContents[i]; - ++OutCol; - continue; + size_t NextTab = LineContents.find('\t', i); + // If there were no tabs left, print the rest, we are done. + if (NextTab == StringRef::npos) { + S << LineContents.drop_front(i); + break; } + // Otherwise, print from i to NextTab. + S << LineContents.slice(i, NextTab); + OutCol += NextTab - i; + i = NextTab; + // If we have a tab, emit at least one space, then round up to 8 columns. do { S << ' '; @@ -364,65 +371,48 @@ static bool isNonASCII(char c) { return c & 0x80; } -void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, - bool ShowKindLabel) const { - // Display colors only if OS supports colors. - ShowColors &= S.has_colors(); - - if (ShowColors) - S.changeColor(raw_ostream::SAVEDCOLOR, true); +void SMDiagnostic::print(const char *ProgName, raw_ostream &OS, + bool ShowColors, bool ShowKindLabel) const { + { + WithColor S(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors); - if (ProgName && ProgName[0]) - S << ProgName << ": "; + if (ProgName && ProgName[0]) + S << ProgName << ": "; - if (!Filename.empty()) { - if (Filename == "-") - S << "<stdin>"; - else - S << Filename; + if (!Filename.empty()) { + if (Filename == "-") + S << "<stdin>"; + else + S << Filename; - if (LineNo != -1) { - S << ':' << LineNo; - if (ColumnNo != -1) - S << ':' << (ColumnNo+1); + if (LineNo != -1) { + S << ':' << LineNo; + if (ColumnNo != -1) + S << ':' << (ColumnNo + 1); + } + S << ": "; } - S << ": "; } if (ShowKindLabel) { switch (Kind) { case SourceMgr::DK_Error: - if (ShowColors) - S.changeColor(raw_ostream::RED, true); - S << "error: "; + WithColor::error(OS, "", !ShowColors); break; case SourceMgr::DK_Warning: - if (ShowColors) - S.changeColor(raw_ostream::MAGENTA, true); - S << "warning: "; + WithColor::warning(OS, "", !ShowColors); break; case SourceMgr::DK_Note: - if (ShowColors) - S.changeColor(raw_ostream::BLACK, true); - S << "note: "; + WithColor::note(OS, "", !ShowColors); break; case SourceMgr::DK_Remark: - if (ShowColors) - S.changeColor(raw_ostream::BLUE, true); - S << "remark: "; + WithColor::remark(OS, "", !ShowColors); break; } - - if (ShowColors) { - S.resetColor(); - S.changeColor(raw_ostream::SAVEDCOLOR, true); - } } - S << Message << '\n'; - - if (ShowColors) - S.resetColor(); + WithColor(OS, raw_ostream::SAVEDCOLOR, true, false, !ShowColors) + << Message << '\n'; if (LineNo == -1 || ColumnNo == -1) return; @@ -433,7 +423,7 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, // expanding them later, and bail out rather than show incorrect ranges and // misaligned fixits for any other odd characters. if (find_if(LineContents, isNonASCII) != LineContents.end()) { - printSourceLine(S, LineContents); + printSourceLine(OS, LineContents); return; } size_t NumColumns = LineContents.size(); @@ -467,29 +457,27 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, // least. CaretLine.erase(CaretLine.find_last_not_of(' ')+1); - printSourceLine(S, LineContents); + printSourceLine(OS, LineContents); - if (ShowColors) - S.changeColor(raw_ostream::GREEN, true); + { + WithColor S(OS, raw_ostream::GREEN, true, false, !ShowColors); - // Print out the caret line, matching tabs in the source line. - for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) { - if (i >= LineContents.size() || LineContents[i] != '\t') { - S << CaretLine[i]; - ++OutCol; - continue; - } + // Print out the caret line, matching tabs in the source line. + for (unsigned i = 0, e = CaretLine.size(), OutCol = 0; i != e; ++i) { + if (i >= LineContents.size() || LineContents[i] != '\t') { + S << CaretLine[i]; + ++OutCol; + continue; + } - // Okay, we have a tab. Insert the appropriate number of characters. - do { - S << CaretLine[i]; - ++OutCol; - } while ((OutCol % TabStop) != 0); + // Okay, we have a tab. Insert the appropriate number of characters. + do { + S << CaretLine[i]; + ++OutCol; + } while ((OutCol % TabStop) != 0); + } + S << '\n'; } - S << '\n'; - - if (ShowColors) - S.resetColor(); // Print out the replacement line, matching tabs in the source line. if (FixItInsertionLine.empty()) @@ -497,14 +485,14 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, for (size_t i = 0, e = FixItInsertionLine.size(), OutCol = 0; i < e; ++i) { if (i >= LineContents.size() || LineContents[i] != '\t') { - S << FixItInsertionLine[i]; + OS << FixItInsertionLine[i]; ++OutCol; continue; } // Okay, we have a tab. Insert the appropriate number of characters. do { - S << FixItInsertionLine[i]; + OS << FixItInsertionLine[i]; // FIXME: This is trying not to break up replacements, but then to re-sync // with the tabs between replacements. This will fail, though, if two // fix-it replacements are exactly adjacent, or if a fix-it contains a @@ -515,5 +503,5 @@ void SMDiagnostic::print(const char *ProgName, raw_ostream &S, bool ShowColors, ++OutCol; } while (((OutCol % TabStop) != 0) && i != e); } - S << '\n'; + OS << '\n'; } diff --git a/lib/Support/StringSaver.cpp b/lib/Support/StringSaver.cpp index 1ded2bdb09de..bf0ac8de9821 100644 --- a/lib/Support/StringSaver.cpp +++ b/lib/Support/StringSaver.cpp @@ -13,7 +13,8 @@ using namespace llvm; StringRef StringSaver::save(StringRef S) { char *P = Alloc.Allocate<char>(S.size() + 1); - memcpy(P, S.data(), S.size()); + if (!S.empty()) + memcpy(P, S.data(), S.size()); P[S.size()] = '\0'; return StringRef(P, S.size()); } diff --git a/lib/Support/SymbolRemappingReader.cpp b/lib/Support/SymbolRemappingReader.cpp new file mode 100644 index 000000000000..264c890ce8f1 --- /dev/null +++ b/lib/Support/SymbolRemappingReader.cpp @@ -0,0 +1,81 @@ +//===- SymbolRemappingReader.cpp - Read symbol remapping file -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains definitions needed for reading and applying symbol +// remapping files. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/SymbolRemappingReader.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/LineIterator.h" + +using namespace llvm; + +char SymbolRemappingParseError::ID; + +/// Load a set of name remappings from a text file. +/// +/// See the documentation at the top of the file for an explanation of +/// the expected format. +Error SymbolRemappingReader::read(MemoryBuffer &B) { + line_iterator LineIt(B, /*SkipBlanks=*/true, '#'); + + auto ReportError = [&](Twine Msg) { + return llvm::make_error<SymbolRemappingParseError>( + B.getBufferIdentifier(), LineIt.line_number(), Msg); + }; + + for (; !LineIt.is_at_eof(); ++LineIt) { + StringRef Line = *LineIt; + Line = Line.ltrim(' '); + // line_iterator only detects comments starting in column 1. + if (Line.startswith("#") || Line.empty()) + continue; + + SmallVector<StringRef, 4> Parts; + Line.split(Parts, ' ', /*MaxSplits*/-1, /*KeepEmpty*/false); + + if (Parts.size() != 3) + return ReportError("Expected 'kind mangled_name mangled_name', " + "found '" + Line + "'"); + + using FK = ItaniumManglingCanonicalizer::FragmentKind; + Optional<FK> FragmentKind = StringSwitch<Optional<FK>>(Parts[0]) + .Case("name", FK::Name) + .Case("type", FK::Type) + .Case("encoding", FK::Encoding) + .Default(None); + if (!FragmentKind) + return ReportError("Invalid kind, expected 'name', 'type', or 'encoding'," + " found '" + Parts[0] + "'"); + + using EE = ItaniumManglingCanonicalizer::EquivalenceError; + switch (Canonicalizer.addEquivalence(*FragmentKind, Parts[1], Parts[2])) { + case EE::Success: + break; + + case EE::ManglingAlreadyUsed: + return ReportError("Manglings '" + Parts[1] + "' and '" + Parts[2] + "' " + "have both been used in prior remappings. Move this " + "remapping earlier in the file."); + + case EE::InvalidFirstMangling: + return ReportError("Could not demangle '" + Parts[1] + "' " + "as a <" + Parts[0] + ">; invalid mangling?"); + + case EE::InvalidSecondMangling: + return ReportError("Could not demangle '" + Parts[2] + "' " + "as a <" + Parts[0] + ">; invalid mangling?"); + } + } + + return Error::success(); +} diff --git a/lib/Support/TargetParser.cpp b/lib/Support/TargetParser.cpp index 2c167a4d086c..bdc0dc52c5e2 100644 --- a/lib/Support/TargetParser.cpp +++ b/lib/Support/TargetParser.cpp @@ -14,926 +14,186 @@ #include "llvm/Support/ARMBuildAttributes.h" #include "llvm/Support/TargetParser.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" -#include <cctype> using namespace llvm; -using namespace ARM; -using namespace AArch64; +using namespace AMDGPU; namespace { -// List of canonical FPU names (use getFPUSynonym) and which architectural -// features they correspond to (use getFPUFeatures). -// FIXME: TableGen this. -// The entries must appear in the order listed in ARM::FPUKind for correct indexing -static const struct { - const char *NameCStr; - size_t NameLength; - ARM::FPUKind ID; - ARM::FPUVersion FPUVersion; - ARM::NeonSupportLevel NeonSupport; - ARM::FPURestriction Restriction; - - StringRef getName() const { return StringRef(NameCStr, NameLength); } -} FPUNames[] = { -#define ARM_FPU(NAME, KIND, VERSION, NEON_SUPPORT, RESTRICTION) \ - { NAME, sizeof(NAME) - 1, KIND, VERSION, NEON_SUPPORT, RESTRICTION }, -#include "llvm/Support/ARMTargetParser.def" -}; - -// List of canonical arch names (use getArchSynonym). -// This table also provides the build attribute fields for CPU arch -// and Arch ID, according to the Addenda to the ARM ABI, chapters -// 2.4 and 2.3.5.2 respectively. -// FIXME: SubArch values were simplified to fit into the expectations -// of the triples and are not conforming with their official names. -// Check to see if the expectation should be changed. -// FIXME: TableGen this. -template <typename T> struct ArchNames { - const char *NameCStr; - size_t NameLength; - const char *CPUAttrCStr; - size_t CPUAttrLength; - const char *SubArchCStr; - size_t SubArchLength; - unsigned DefaultFPU; - unsigned ArchBaseExtensions; - T ID; - ARMBuildAttrs::CPUArch ArchAttr; // Arch ID in build attributes. - - StringRef getName() const { return StringRef(NameCStr, NameLength); } - - // CPU class in build attributes. - StringRef getCPUAttr() const { return StringRef(CPUAttrCStr, CPUAttrLength); } - - // Sub-Arch name. - StringRef getSubArch() const { return StringRef(SubArchCStr, SubArchLength); } +struct GPUInfo { + StringLiteral Name; + StringLiteral CanonicalName; + AMDGPU::GPUKind Kind; + unsigned Features; }; -ArchNames<ARM::ArchKind> ARCHNames[] = { -#define ARM_ARCH(NAME, ID, CPU_ATTR, SUB_ARCH, ARCH_ATTR, ARCH_FPU, ARCH_BASE_EXT) \ - {NAME, sizeof(NAME) - 1, CPU_ATTR, sizeof(CPU_ATTR) - 1, SUB_ARCH, \ - sizeof(SUB_ARCH) - 1, ARCH_FPU, ARCH_BASE_EXT, ARM::ArchKind::ID, ARCH_ATTR}, -#include "llvm/Support/ARMTargetParser.def" -}; - -ArchNames<AArch64::ArchKind> AArch64ARCHNames[] = { - #define AARCH64_ARCH(NAME, ID, CPU_ATTR, SUB_ARCH, ARCH_ATTR, ARCH_FPU, ARCH_BASE_EXT) \ - {NAME, sizeof(NAME) - 1, CPU_ATTR, sizeof(CPU_ATTR) - 1, SUB_ARCH, \ - sizeof(SUB_ARCH) - 1, ARCH_FPU, ARCH_BASE_EXT, AArch64::ArchKind::ID, ARCH_ATTR}, - #include "llvm/Support/AArch64TargetParser.def" - }; - -// List of Arch Extension names. -// FIXME: TableGen this. -static const struct { - const char *NameCStr; - size_t NameLength; - unsigned ID; - const char *Feature; - const char *NegFeature; - - StringRef getName() const { return StringRef(NameCStr, NameLength); } -} ARCHExtNames[] = { -#define ARM_ARCH_EXT_NAME(NAME, ID, FEATURE, NEGFEATURE) \ - { NAME, sizeof(NAME) - 1, ID, FEATURE, NEGFEATURE }, -#include "llvm/Support/ARMTargetParser.def" -},AArch64ARCHExtNames[] = { -#define AARCH64_ARCH_EXT_NAME(NAME, ID, FEATURE, NEGFEATURE) \ - { NAME, sizeof(NAME) - 1, ID, FEATURE, NEGFEATURE }, -#include "llvm/Support/AArch64TargetParser.def" +constexpr GPUInfo R600GPUs[26] = { + // Name Canonical Kind Features + // Name + {{"r600"}, {"r600"}, GK_R600, FEATURE_NONE }, + {{"rv630"}, {"r600"}, GK_R600, FEATURE_NONE }, + {{"rv635"}, {"r600"}, GK_R600, FEATURE_NONE }, + {{"r630"}, {"r630"}, GK_R630, FEATURE_NONE }, + {{"rs780"}, {"rs880"}, GK_RS880, FEATURE_NONE }, + {{"rs880"}, {"rs880"}, GK_RS880, FEATURE_NONE }, + {{"rv610"}, {"rs880"}, GK_RS880, FEATURE_NONE }, + {{"rv620"}, {"rs880"}, GK_RS880, FEATURE_NONE }, + {{"rv670"}, {"rv670"}, GK_RV670, FEATURE_NONE }, + {{"rv710"}, {"rv710"}, GK_RV710, FEATURE_NONE }, + {{"rv730"}, {"rv730"}, GK_RV730, FEATURE_NONE }, + {{"rv740"}, {"rv770"}, GK_RV770, FEATURE_NONE }, + {{"rv770"}, {"rv770"}, GK_RV770, FEATURE_NONE }, + {{"cedar"}, {"cedar"}, GK_CEDAR, FEATURE_NONE }, + {{"palm"}, {"cedar"}, GK_CEDAR, FEATURE_NONE }, + {{"cypress"}, {"cypress"}, GK_CYPRESS, FEATURE_FMA }, + {{"hemlock"}, {"cypress"}, GK_CYPRESS, FEATURE_FMA }, + {{"juniper"}, {"juniper"}, GK_JUNIPER, FEATURE_NONE }, + {{"redwood"}, {"redwood"}, GK_REDWOOD, FEATURE_NONE }, + {{"sumo"}, {"sumo"}, GK_SUMO, FEATURE_NONE }, + {{"sumo2"}, {"sumo"}, GK_SUMO, FEATURE_NONE }, + {{"barts"}, {"barts"}, GK_BARTS, FEATURE_NONE }, + {{"caicos"}, {"caicos"}, GK_CAICOS, FEATURE_NONE }, + {{"aruba"}, {"cayman"}, GK_CAYMAN, FEATURE_FMA }, + {{"cayman"}, {"cayman"}, GK_CAYMAN, FEATURE_FMA }, + {{"turks"}, {"turks"}, GK_TURKS, FEATURE_NONE } }; -// List of HWDiv names (use getHWDivSynonym) and which architectural -// features they correspond to (use getHWDivFeatures). -// FIXME: TableGen this. -static const struct { - const char *NameCStr; - size_t NameLength; - unsigned ID; - - StringRef getName() const { return StringRef(NameCStr, NameLength); } -} HWDivNames[] = { -#define ARM_HW_DIV_NAME(NAME, ID) { NAME, sizeof(NAME) - 1, ID }, -#include "llvm/Support/ARMTargetParser.def" +// This table should be sorted by the value of GPUKind +// Don't bother listing the implicitly true features +constexpr GPUInfo AMDGCNGPUs[33] = { + // Name Canonical Kind Features + // Name + {{"gfx600"}, {"gfx600"}, GK_GFX600, FEATURE_FAST_FMA_F32}, + {{"tahiti"}, {"gfx600"}, GK_GFX600, FEATURE_FAST_FMA_F32}, + {{"gfx601"}, {"gfx601"}, GK_GFX601, FEATURE_NONE}, + {{"hainan"}, {"gfx601"}, GK_GFX601, FEATURE_NONE}, + {{"oland"}, {"gfx601"}, GK_GFX601, FEATURE_NONE}, + {{"pitcairn"}, {"gfx601"}, GK_GFX601, FEATURE_NONE}, + {{"verde"}, {"gfx601"}, GK_GFX601, FEATURE_NONE}, + {{"gfx700"}, {"gfx700"}, GK_GFX700, FEATURE_NONE}, + {{"kaveri"}, {"gfx700"}, GK_GFX700, FEATURE_NONE}, + {{"gfx701"}, {"gfx701"}, GK_GFX701, FEATURE_FAST_FMA_F32}, + {{"hawaii"}, {"gfx701"}, GK_GFX701, FEATURE_FAST_FMA_F32}, + {{"gfx702"}, {"gfx702"}, GK_GFX702, FEATURE_FAST_FMA_F32}, + {{"gfx703"}, {"gfx703"}, GK_GFX703, FEATURE_NONE}, + {{"kabini"}, {"gfx703"}, GK_GFX703, FEATURE_NONE}, + {{"mullins"}, {"gfx703"}, GK_GFX703, FEATURE_NONE}, + {{"gfx704"}, {"gfx704"}, GK_GFX704, FEATURE_NONE}, + {{"bonaire"}, {"gfx704"}, GK_GFX704, FEATURE_NONE}, + {{"gfx801"}, {"gfx801"}, GK_GFX801, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, + {{"carrizo"}, {"gfx801"}, GK_GFX801, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, + {{"gfx802"}, {"gfx802"}, GK_GFX802, FEATURE_FAST_DENORMAL_F32}, + {{"iceland"}, {"gfx802"}, GK_GFX802, FEATURE_FAST_DENORMAL_F32}, + {{"tonga"}, {"gfx802"}, GK_GFX802, FEATURE_FAST_DENORMAL_F32}, + {{"gfx803"}, {"gfx803"}, GK_GFX803, FEATURE_FAST_DENORMAL_F32}, + {{"fiji"}, {"gfx803"}, GK_GFX803, FEATURE_FAST_DENORMAL_F32}, + {{"polaris10"}, {"gfx803"}, GK_GFX803, FEATURE_FAST_DENORMAL_F32}, + {{"polaris11"}, {"gfx803"}, GK_GFX803, FEATURE_FAST_DENORMAL_F32}, + {{"gfx810"}, {"gfx810"}, GK_GFX810, FEATURE_FAST_DENORMAL_F32}, + {{"stoney"}, {"gfx810"}, GK_GFX810, FEATURE_FAST_DENORMAL_F32}, + {{"gfx900"}, {"gfx900"}, GK_GFX900, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, + {{"gfx902"}, {"gfx902"}, GK_GFX902, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, + {{"gfx904"}, {"gfx904"}, GK_GFX904, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, + {{"gfx906"}, {"gfx906"}, GK_GFX906, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, + {{"gfx909"}, {"gfx909"}, GK_GFX909, FEATURE_FAST_FMA_F32|FEATURE_FAST_DENORMAL_F32}, }; -// List of CPU names and their arches. -// The same CPU can have multiple arches and can be default on multiple arches. -// When finding the Arch for a CPU, first-found prevails. Sort them accordingly. -// When this becomes table-generated, we'd probably need two tables. -// FIXME: TableGen this. -template <typename T> struct CpuNames { - const char *NameCStr; - size_t NameLength; - T ArchID; - bool Default; // is $Name the default CPU for $ArchID ? - unsigned DefaultExtensions; - - StringRef getName() const { return StringRef(NameCStr, NameLength); } -}; -CpuNames<ARM::ArchKind> CPUNames[] = { -#define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - { NAME, sizeof(NAME) - 1, ARM::ArchKind::ID, IS_DEFAULT, DEFAULT_EXT }, -#include "llvm/Support/ARMTargetParser.def" -}; - -CpuNames<AArch64::ArchKind> AArch64CPUNames[] = { - #define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - { NAME, sizeof(NAME) - 1, AArch64::ArchKind::ID, IS_DEFAULT, DEFAULT_EXT }, - #include "llvm/Support/AArch64TargetParser.def" - }; - -} // namespace - -// ======================================================= // -// Information by ID -// ======================================================= // - -StringRef ARM::getFPUName(unsigned FPUKind) { - if (FPUKind >= ARM::FK_LAST) - return StringRef(); - return FPUNames[FPUKind].getName(); -} - -FPUVersion ARM::getFPUVersion(unsigned FPUKind) { - if (FPUKind >= ARM::FK_LAST) - return FPUVersion::NONE; - return FPUNames[FPUKind].FPUVersion; -} - -ARM::NeonSupportLevel ARM::getFPUNeonSupportLevel(unsigned FPUKind) { - if (FPUKind >= ARM::FK_LAST) - return ARM::NeonSupportLevel::None; - return FPUNames[FPUKind].NeonSupport; -} - -ARM::FPURestriction ARM::getFPURestriction(unsigned FPUKind) { - if (FPUKind >= ARM::FK_LAST) - return ARM::FPURestriction::None; - return FPUNames[FPUKind].Restriction; -} - -unsigned llvm::ARM::getDefaultFPU(StringRef CPU, ArchKind AK) { - if (CPU == "generic") - return ARCHNames[static_cast<unsigned>(AK)].DefaultFPU; - - return StringSwitch<unsigned>(CPU) -#define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, DEFAULT_FPU) -#include "llvm/Support/ARMTargetParser.def" - .Default(ARM::FK_INVALID); -} - -unsigned llvm::ARM::getDefaultExtensions(StringRef CPU, ArchKind AK) { - if (CPU == "generic") - return ARCHNames[static_cast<unsigned>(AK)].ArchBaseExtensions; - - return StringSwitch<unsigned>(CPU) -#define ARM_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, ARCHNames[static_cast<unsigned>(ARM::ArchKind::ID)]\ - .ArchBaseExtensions | DEFAULT_EXT) -#include "llvm/Support/ARMTargetParser.def" - .Default(ARM::AEK_INVALID); -} - -bool llvm::ARM::getHWDivFeatures(unsigned HWDivKind, - std::vector<StringRef> &Features) { - - if (HWDivKind == ARM::AEK_INVALID) - return false; - - if (HWDivKind & ARM::AEK_HWDIVARM) - Features.push_back("+hwdiv-arm"); - else - Features.push_back("-hwdiv-arm"); - - if (HWDivKind & ARM::AEK_HWDIVTHUMB) - Features.push_back("+hwdiv"); - else - Features.push_back("-hwdiv"); - - return true; -} - -bool llvm::ARM::getExtensionFeatures(unsigned Extensions, - std::vector<StringRef> &Features) { - - if (Extensions == ARM::AEK_INVALID) - return false; - - if (Extensions & ARM::AEK_CRC) - Features.push_back("+crc"); - else - Features.push_back("-crc"); - - if (Extensions & ARM::AEK_DSP) - Features.push_back("+dsp"); - else - Features.push_back("-dsp"); - - if (Extensions & ARM::AEK_RAS) - Features.push_back("+ras"); - else - Features.push_back("-ras"); - - if (Extensions & ARM::AEK_DOTPROD) - Features.push_back("+dotprod"); - else - Features.push_back("-dotprod"); - - return getHWDivFeatures(Extensions, Features); -} - -bool llvm::ARM::getFPUFeatures(unsigned FPUKind, - std::vector<StringRef> &Features) { - - if (FPUKind >= ARM::FK_LAST || FPUKind == ARM::FK_INVALID) - return false; - - // fp-only-sp and d16 subtarget features are independent of each other, so we - // must enable/disable both. - switch (FPUNames[FPUKind].Restriction) { - case ARM::FPURestriction::SP_D16: - Features.push_back("+fp-only-sp"); - Features.push_back("+d16"); - break; - case ARM::FPURestriction::D16: - Features.push_back("-fp-only-sp"); - Features.push_back("+d16"); - break; - case ARM::FPURestriction::None: - Features.push_back("-fp-only-sp"); - Features.push_back("-d16"); - break; - } - - // FPU version subtarget features are inclusive of lower-numbered ones, so - // enable the one corresponding to this version and disable all that are - // higher. We also have to make sure to disable fp16 when vfp4 is disabled, - // as +vfp4 implies +fp16 but -vfp4 does not imply -fp16. - switch (FPUNames[FPUKind].FPUVersion) { - case ARM::FPUVersion::VFPV5: - Features.push_back("+fp-armv8"); - break; - case ARM::FPUVersion::VFPV4: - Features.push_back("+vfp4"); - Features.push_back("-fp-armv8"); - break; - case ARM::FPUVersion::VFPV3_FP16: - Features.push_back("+vfp3"); - Features.push_back("+fp16"); - Features.push_back("-vfp4"); - Features.push_back("-fp-armv8"); - break; - case ARM::FPUVersion::VFPV3: - Features.push_back("+vfp3"); - Features.push_back("-fp16"); - Features.push_back("-vfp4"); - Features.push_back("-fp-armv8"); - break; - case ARM::FPUVersion::VFPV2: - Features.push_back("+vfp2"); - Features.push_back("-vfp3"); - Features.push_back("-fp16"); - Features.push_back("-vfp4"); - Features.push_back("-fp-armv8"); - break; - case ARM::FPUVersion::NONE: - Features.push_back("-vfp2"); - Features.push_back("-vfp3"); - Features.push_back("-fp16"); - Features.push_back("-vfp4"); - Features.push_back("-fp-armv8"); - break; - } - - // crypto includes neon, so we handle this similarly to FPU version. - switch (FPUNames[FPUKind].NeonSupport) { - case ARM::NeonSupportLevel::Crypto: - Features.push_back("+neon"); - Features.push_back("+crypto"); - break; - case ARM::NeonSupportLevel::Neon: - Features.push_back("+neon"); - Features.push_back("-crypto"); - break; - case ARM::NeonSupportLevel::None: - Features.push_back("-neon"); - Features.push_back("-crypto"); - break; - } +const GPUInfo *getArchEntry(AMDGPU::GPUKind AK, ArrayRef<GPUInfo> Table) { + GPUInfo Search = { {""}, {""}, AK, AMDGPU::FEATURE_NONE }; - return true; -} - -StringRef llvm::ARM::getArchName(ArchKind AK) { - return ARCHNames[static_cast<unsigned>(AK)].getName(); -} + auto I = std::lower_bound(Table.begin(), Table.end(), Search, + [](const GPUInfo &A, const GPUInfo &B) { + return A.Kind < B.Kind; + }); -StringRef llvm::ARM::getCPUAttr(ArchKind AK) { - return ARCHNames[static_cast<unsigned>(AK)].getCPUAttr(); + if (I == Table.end()) + return nullptr; + return I; } -StringRef llvm::ARM::getSubArch(ArchKind AK) { - return ARCHNames[static_cast<unsigned>(AK)].getSubArch(); -} - -unsigned llvm::ARM::getArchAttr(ArchKind AK) { - return ARCHNames[static_cast<unsigned>(AK)].ArchAttr; -} - -StringRef llvm::ARM::getArchExtName(unsigned ArchExtKind) { - for (const auto AE : ARCHExtNames) { - if (ArchExtKind == AE.ID) - return AE.getName(); - } - return StringRef(); -} - -StringRef llvm::ARM::getArchExtFeature(StringRef ArchExt) { - if (ArchExt.startswith("no")) { - StringRef ArchExtBase(ArchExt.substr(2)); - for (const auto AE : ARCHExtNames) { - if (AE.NegFeature && ArchExtBase == AE.getName()) - return StringRef(AE.NegFeature); - } - } - for (const auto AE : ARCHExtNames) { - if (AE.Feature && ArchExt == AE.getName()) - return StringRef(AE.Feature); - } - - return StringRef(); -} - -StringRef llvm::ARM::getHWDivName(unsigned HWDivKind) { - for (const auto D : HWDivNames) { - if (HWDivKind == D.ID) - return D.getName(); - } - return StringRef(); -} - -StringRef llvm::ARM::getDefaultCPU(StringRef Arch) { - ArchKind AK = parseArch(Arch); - if (AK == ARM::ArchKind::INVALID) - return StringRef(); - - // Look for multiple AKs to find the default for pair AK+Name. - for (const auto CPU : CPUNames) { - if (CPU.ArchID == AK && CPU.Default) - return CPU.getName(); - } - - // If we can't find a default then target the architecture instead - return "generic"; -} - -StringRef llvm::AArch64::getFPUName(unsigned FPUKind) { - return ARM::getFPUName(FPUKind); -} - -ARM::FPUVersion AArch64::getFPUVersion(unsigned FPUKind) { - return ARM::getFPUVersion(FPUKind); -} - -ARM::NeonSupportLevel AArch64::getFPUNeonSupportLevel(unsigned FPUKind) { - return ARM::getFPUNeonSupportLevel( FPUKind); -} - -ARM::FPURestriction AArch64::getFPURestriction(unsigned FPUKind) { - return ARM::getFPURestriction(FPUKind); -} - -unsigned llvm::AArch64::getDefaultFPU(StringRef CPU, ArchKind AK) { - if (CPU == "generic") - return AArch64ARCHNames[static_cast<unsigned>(AK)].DefaultFPU; - - return StringSwitch<unsigned>(CPU) -#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, DEFAULT_FPU) -#include "llvm/Support/AArch64TargetParser.def" - .Default(ARM::FK_INVALID); -} - -unsigned llvm::AArch64::getDefaultExtensions(StringRef CPU, ArchKind AK) { - if (CPU == "generic") - return AArch64ARCHNames[static_cast<unsigned>(AK)].ArchBaseExtensions; - - return StringSwitch<unsigned>(CPU) -#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, \ - AArch64ARCHNames[static_cast<unsigned>(AArch64::ArchKind::ID)] \ - .ArchBaseExtensions | \ - DEFAULT_EXT) -#include "llvm/Support/AArch64TargetParser.def" - .Default(AArch64::AEK_INVALID); -} - -AArch64::ArchKind llvm::AArch64::getCPUArchKind(StringRef CPU) { - if (CPU == "generic") - return AArch64::ArchKind::ARMV8A; - - return StringSwitch<AArch64::ArchKind>(CPU) -#define AARCH64_CPU_NAME(NAME, ID, DEFAULT_FPU, IS_DEFAULT, DEFAULT_EXT) \ - .Case(NAME, AArch64::ArchKind:: ID) -#include "llvm/Support/AArch64TargetParser.def" - .Default(AArch64::ArchKind::INVALID); -} - -bool llvm::AArch64::getExtensionFeatures(unsigned Extensions, - std::vector<StringRef> &Features) { - - if (Extensions == AArch64::AEK_INVALID) - return false; - - if (Extensions & AArch64::AEK_FP) - Features.push_back("+fp-armv8"); - if (Extensions & AArch64::AEK_SIMD) - Features.push_back("+neon"); - if (Extensions & AArch64::AEK_CRC) - Features.push_back("+crc"); - if (Extensions & AArch64::AEK_CRYPTO) - Features.push_back("+crypto"); - if (Extensions & AArch64::AEK_DOTPROD) - Features.push_back("+dotprod"); - if (Extensions & AArch64::AEK_FP16) - Features.push_back("+fullfp16"); - if (Extensions & AArch64::AEK_PROFILE) - Features.push_back("+spe"); - if (Extensions & AArch64::AEK_RAS) - Features.push_back("+ras"); - if (Extensions & AArch64::AEK_LSE) - Features.push_back("+lse"); - if (Extensions & AArch64::AEK_RDM) - Features.push_back("+rdm"); - if (Extensions & AArch64::AEK_SVE) - Features.push_back("+sve"); - if (Extensions & AArch64::AEK_RCPC) - Features.push_back("+rcpc"); - - return true; -} - -bool llvm::AArch64::getFPUFeatures(unsigned FPUKind, - std::vector<StringRef> &Features) { - return ARM::getFPUFeatures(FPUKind, Features); -} - -bool llvm::AArch64::getArchFeatures(AArch64::ArchKind AK, - std::vector<StringRef> &Features) { - if (AK == AArch64::ArchKind::ARMV8_1A) - Features.push_back("+v8.1a"); - if (AK == AArch64::ArchKind::ARMV8_2A) - Features.push_back("+v8.2a"); - if (AK == AArch64::ArchKind::ARMV8_3A) - Features.push_back("+v8.3a"); - if (AK == AArch64::ArchKind::ARMV8_4A) - Features.push_back("+v8.4a"); - - return AK != AArch64::ArchKind::INVALID; -} - -StringRef llvm::AArch64::getArchName(ArchKind AK) { - return AArch64ARCHNames[static_cast<unsigned>(AK)].getName(); -} - -StringRef llvm::AArch64::getCPUAttr(ArchKind AK) { - return AArch64ARCHNames[static_cast<unsigned>(AK)].getCPUAttr(); -} - -StringRef llvm::AArch64::getSubArch(ArchKind AK) { - return AArch64ARCHNames[static_cast<unsigned>(AK)].getSubArch(); -} - -unsigned llvm::AArch64::getArchAttr(ArchKind AK) { - return AArch64ARCHNames[static_cast<unsigned>(AK)].ArchAttr; -} - -StringRef llvm::AArch64::getArchExtName(unsigned ArchExtKind) { - for (const auto &AE : AArch64ARCHExtNames) - if (ArchExtKind == AE.ID) - return AE.getName(); - return StringRef(); -} - -StringRef llvm::AArch64::getArchExtFeature(StringRef ArchExt) { - if (ArchExt.startswith("no")) { - StringRef ArchExtBase(ArchExt.substr(2)); - for (const auto &AE : AArch64ARCHExtNames) { - if (AE.NegFeature && ArchExtBase == AE.getName()) - return StringRef(AE.NegFeature); - } - } - - for (const auto &AE : AArch64ARCHExtNames) - if (AE.Feature && ArchExt == AE.getName()) - return StringRef(AE.Feature); - return StringRef(); -} - -StringRef llvm::AArch64::getDefaultCPU(StringRef Arch) { - AArch64::ArchKind AK = parseArch(Arch); - if (AK == ArchKind::INVALID) - return StringRef(); - - // Look for multiple AKs to find the default for pair AK+Name. - for (const auto &CPU : AArch64CPUNames) - if (CPU.ArchID == AK && CPU.Default) - return CPU.getName(); - - // If we can't find a default then target the architecture instead - return "generic"; -} - -unsigned llvm::AArch64::checkArchVersion(StringRef Arch) { - if (Arch.size() >= 2 && Arch[0] == 'v' && std::isdigit(Arch[1])) - return (Arch[1] - 48); - return 0; -} - -// ======================================================= // -// Parsers -// ======================================================= // - -static StringRef getHWDivSynonym(StringRef HWDiv) { - return StringSwitch<StringRef>(HWDiv) - .Case("thumb,arm", "arm,thumb") - .Default(HWDiv); -} - -static StringRef getFPUSynonym(StringRef FPU) { - return StringSwitch<StringRef>(FPU) - .Cases("fpa", "fpe2", "fpe3", "maverick", "invalid") // Unsupported - .Case("vfp2", "vfpv2") - .Case("vfp3", "vfpv3") - .Case("vfp4", "vfpv4") - .Case("vfp3-d16", "vfpv3-d16") - .Case("vfp4-d16", "vfpv4-d16") - .Cases("fp4-sp-d16", "vfpv4-sp-d16", "fpv4-sp-d16") - .Cases("fp4-dp-d16", "fpv4-dp-d16", "vfpv4-d16") - .Case("fp5-sp-d16", "fpv5-sp-d16") - .Cases("fp5-dp-d16", "fpv5-dp-d16", "fpv5-d16") - // FIXME: Clang uses it, but it's bogus, since neon defaults to vfpv3. - .Case("neon-vfpv3", "neon") - .Default(FPU); -} - -static StringRef getArchSynonym(StringRef Arch) { - return StringSwitch<StringRef>(Arch) - .Case("v5", "v5t") - .Case("v5e", "v5te") - .Case("v6j", "v6") - .Case("v6hl", "v6k") - .Cases("v6m", "v6sm", "v6s-m", "v6-m") - .Cases("v6z", "v6zk", "v6kz") - .Cases("v7", "v7a", "v7hl", "v7l", "v7-a") - .Case("v7r", "v7-r") - .Case("v7m", "v7-m") - .Case("v7em", "v7e-m") - .Cases("v8", "v8a", "v8l", "aarch64", "arm64", "v8-a") - .Case("v8.1a", "v8.1-a") - .Case("v8.2a", "v8.2-a") - .Case("v8.3a", "v8.3-a") - .Case("v8.4a", "v8.4-a") - .Case("v8r", "v8-r") - .Case("v8m.base", "v8-m.base") - .Case("v8m.main", "v8-m.main") - .Default(Arch); -} - -// MArch is expected to be of the form (arm|thumb)?(eb)?(v.+)?(eb)?, but -// (iwmmxt|xscale)(eb)? is also permitted. If the former, return -// "v.+", if the latter, return unmodified string, minus 'eb'. -// If invalid, return empty string. -StringRef llvm::ARM::getCanonicalArchName(StringRef Arch) { - size_t offset = StringRef::npos; - StringRef A = Arch; - StringRef Error = ""; - - // Begins with "arm" / "thumb", move past it. - if (A.startswith("arm64")) - offset = 5; - else if (A.startswith("arm")) - offset = 3; - else if (A.startswith("thumb")) - offset = 5; - else if (A.startswith("aarch64")) { - offset = 7; - // AArch64 uses "_be", not "eb" suffix. - if (A.find("eb") != StringRef::npos) - return Error; - if (A.substr(offset, 3) == "_be") - offset += 3; - } - - // Ex. "armebv7", move past the "eb". - if (offset != StringRef::npos && A.substr(offset, 2) == "eb") - offset += 2; - // Or, if it ends with eb ("armv7eb"), chop it off. - else if (A.endswith("eb")) - A = A.substr(0, A.size() - 2); - // Trim the head - if (offset != StringRef::npos) - A = A.substr(offset); - - // Empty string means offset reached the end, which means it's valid. - if (A.empty()) - return Arch; - - // Only match non-marketing names - if (offset != StringRef::npos) { - // Must start with 'vN'. - if (A.size() >= 2 && (A[0] != 'v' || !std::isdigit(A[1]))) - return Error; - // Can't have an extra 'eb'. - if (A.find("eb") != StringRef::npos) - return Error; - } - - // Arch will either be a 'v' name (v7a) or a marketing name (xscale). - return A; -} - -unsigned llvm::ARM::parseHWDiv(StringRef HWDiv) { - StringRef Syn = getHWDivSynonym(HWDiv); - for (const auto D : HWDivNames) { - if (Syn == D.getName()) - return D.ID; - } - return ARM::AEK_INVALID; -} - -unsigned llvm::ARM::parseFPU(StringRef FPU) { - StringRef Syn = getFPUSynonym(FPU); - for (const auto F : FPUNames) { - if (Syn == F.getName()) - return F.ID; - } - return ARM::FK_INVALID; -} - -// Allows partial match, ex. "v7a" matches "armv7a". -ARM::ArchKind ARM::parseArch(StringRef Arch) { - Arch = getCanonicalArchName(Arch); - StringRef Syn = getArchSynonym(Arch); - for (const auto A : ARCHNames) { - if (A.getName().endswith(Syn)) - return A.ID; - } - return ARM::ArchKind::INVALID; -} +} // namespace -unsigned llvm::ARM::parseArchExt(StringRef ArchExt) { - for (const auto A : ARCHExtNames) { - if (ArchExt == A.getName()) - return A.ID; - } - return ARM::AEK_INVALID; +StringRef llvm::AMDGPU::getArchNameAMDGCN(GPUKind AK) { + if (const auto *Entry = getArchEntry(AK, AMDGCNGPUs)) + return Entry->CanonicalName; + return ""; } -ARM::ArchKind llvm::ARM::parseCPUArch(StringRef CPU) { - for (const auto C : CPUNames) { - if (CPU == C.getName()) - return C.ArchID; - } - return ARM::ArchKind::INVALID; +StringRef llvm::AMDGPU::getArchNameR600(GPUKind AK) { + if (const auto *Entry = getArchEntry(AK, R600GPUs)) + return Entry->CanonicalName; + return ""; } -void llvm::ARM::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) { - for (const CpuNames<ARM::ArchKind> &Arch : CPUNames) { - if (Arch.ArchID != ARM::ArchKind::INVALID) - Values.push_back(Arch.getName()); +AMDGPU::GPUKind llvm::AMDGPU::parseArchAMDGCN(StringRef CPU) { + for (const auto C : AMDGCNGPUs) { + if (CPU == C.Name) + return C.Kind; } -} -void llvm::AArch64::fillValidCPUArchList(SmallVectorImpl<StringRef> &Values) { - for (const CpuNames<AArch64::ArchKind> &Arch : AArch64CPUNames) { - if (Arch.ArchID != AArch64::ArchKind::INVALID) - Values.push_back(Arch.getName()); - } -} - -// ARM, Thumb, AArch64 -ARM::ISAKind ARM::parseArchISA(StringRef Arch) { - return StringSwitch<ARM::ISAKind>(Arch) - .StartsWith("aarch64", ARM::ISAKind::AARCH64) - .StartsWith("arm64", ARM::ISAKind::AARCH64) - .StartsWith("thumb", ARM::ISAKind::THUMB) - .StartsWith("arm", ARM::ISAKind::ARM) - .Default(ARM::ISAKind::INVALID); + return AMDGPU::GPUKind::GK_NONE; } -// Little/Big endian -ARM::EndianKind ARM::parseArchEndian(StringRef Arch) { - if (Arch.startswith("armeb") || Arch.startswith("thumbeb") || - Arch.startswith("aarch64_be")) - return ARM::EndianKind::BIG; - - if (Arch.startswith("arm") || Arch.startswith("thumb")) { - if (Arch.endswith("eb")) - return ARM::EndianKind::BIG; - else - return ARM::EndianKind::LITTLE; +AMDGPU::GPUKind llvm::AMDGPU::parseArchR600(StringRef CPU) { + for (const auto C : R600GPUs) { + if (CPU == C.Name) + return C.Kind; } - if (Arch.startswith("aarch64")) - return ARM::EndianKind::LITTLE; - - return ARM::EndianKind::INVALID; + return AMDGPU::GPUKind::GK_NONE; } -// Profile A/R/M -ARM::ProfileKind ARM::parseArchProfile(StringRef Arch) { - Arch = getCanonicalArchName(Arch); - switch (parseArch(Arch)) { - case ARM::ArchKind::ARMV6M: - case ARM::ArchKind::ARMV7M: - case ARM::ArchKind::ARMV7EM: - case ARM::ArchKind::ARMV8MMainline: - case ARM::ArchKind::ARMV8MBaseline: - return ARM::ProfileKind::M; - case ARM::ArchKind::ARMV7R: - case ARM::ArchKind::ARMV8R: - return ARM::ProfileKind::R; - case ARM::ArchKind::ARMV7A: - case ARM::ArchKind::ARMV7VE: - case ARM::ArchKind::ARMV7K: - case ARM::ArchKind::ARMV8A: - case ARM::ArchKind::ARMV8_1A: - case ARM::ArchKind::ARMV8_2A: - case ARM::ArchKind::ARMV8_3A: - case ARM::ArchKind::ARMV8_4A: - return ARM::ProfileKind::A; - case ARM::ArchKind::ARMV2: - case ARM::ArchKind::ARMV2A: - case ARM::ArchKind::ARMV3: - case ARM::ArchKind::ARMV3M: - case ARM::ArchKind::ARMV4: - case ARM::ArchKind::ARMV4T: - case ARM::ArchKind::ARMV5T: - case ARM::ArchKind::ARMV5TE: - case ARM::ArchKind::ARMV5TEJ: - case ARM::ArchKind::ARMV6: - case ARM::ArchKind::ARMV6K: - case ARM::ArchKind::ARMV6T2: - case ARM::ArchKind::ARMV6KZ: - case ARM::ArchKind::ARMV7S: - case ARM::ArchKind::IWMMXT: - case ARM::ArchKind::IWMMXT2: - case ARM::ArchKind::XSCALE: - case ARM::ArchKind::INVALID: - return ARM::ProfileKind::INVALID; - } - llvm_unreachable("Unhandled architecture"); +unsigned AMDGPU::getArchAttrAMDGCN(GPUKind AK) { + if (const auto *Entry = getArchEntry(AK, AMDGCNGPUs)) + return Entry->Features; + return FEATURE_NONE; } -// Version number (ex. v7 = 7). -unsigned llvm::ARM::parseArchVersion(StringRef Arch) { - Arch = getCanonicalArchName(Arch); - switch (parseArch(Arch)) { - case ARM::ArchKind::ARMV2: - case ARM::ArchKind::ARMV2A: - return 2; - case ARM::ArchKind::ARMV3: - case ARM::ArchKind::ARMV3M: - return 3; - case ARM::ArchKind::ARMV4: - case ARM::ArchKind::ARMV4T: - return 4; - case ARM::ArchKind::ARMV5T: - case ARM::ArchKind::ARMV5TE: - case ARM::ArchKind::IWMMXT: - case ARM::ArchKind::IWMMXT2: - case ARM::ArchKind::XSCALE: - case ARM::ArchKind::ARMV5TEJ: - return 5; - case ARM::ArchKind::ARMV6: - case ARM::ArchKind::ARMV6K: - case ARM::ArchKind::ARMV6T2: - case ARM::ArchKind::ARMV6KZ: - case ARM::ArchKind::ARMV6M: - return 6; - case ARM::ArchKind::ARMV7A: - case ARM::ArchKind::ARMV7VE: - case ARM::ArchKind::ARMV7R: - case ARM::ArchKind::ARMV7M: - case ARM::ArchKind::ARMV7S: - case ARM::ArchKind::ARMV7EM: - case ARM::ArchKind::ARMV7K: - return 7; - case ARM::ArchKind::ARMV8A: - case ARM::ArchKind::ARMV8_1A: - case ARM::ArchKind::ARMV8_2A: - case ARM::ArchKind::ARMV8_3A: - case ARM::ArchKind::ARMV8_4A: - case ARM::ArchKind::ARMV8R: - case ARM::ArchKind::ARMV8MBaseline: - case ARM::ArchKind::ARMV8MMainline: - return 8; - case ARM::ArchKind::INVALID: - return 0; - } - llvm_unreachable("Unhandled architecture"); +unsigned AMDGPU::getArchAttrR600(GPUKind AK) { + if (const auto *Entry = getArchEntry(AK, R600GPUs)) + return Entry->Features; + return FEATURE_NONE; } -StringRef llvm::ARM::computeDefaultTargetABI(const Triple &TT, StringRef CPU) { - StringRef ArchName = - CPU.empty() ? TT.getArchName() : ARM::getArchName(ARM::parseCPUArch(CPU)); - - if (TT.isOSBinFormatMachO()) { - if (TT.getEnvironment() == Triple::EABI || - TT.getOS() == Triple::UnknownOS || - llvm::ARM::parseArchProfile(ArchName) == ARM::ProfileKind::M) - return "aapcs"; - if (TT.isWatchABI()) - return "aapcs16"; - return "apcs-gnu"; - } else if (TT.isOSWindows()) - // FIXME: this is invalid for WindowsCE. - return "aapcs"; - - // Select the default based on the platform. - switch (TT.getEnvironment()) { - case Triple::Android: - case Triple::GNUEABI: - case Triple::GNUEABIHF: - case Triple::MuslEABI: - case Triple::MuslEABIHF: - return "aapcs-linux"; - case Triple::EABIHF: - case Triple::EABI: - return "aapcs"; - default: - if (TT.isOSNetBSD()) - return "apcs-gnu"; - if (TT.isOSOpenBSD()) - return "aapcs-linux"; - return "aapcs"; - } +void AMDGPU::fillValidArchListAMDGCN(SmallVectorImpl<StringRef> &Values) { + // XXX: Should this only report unique canonical names? + for (const auto C : AMDGCNGPUs) + Values.push_back(C.Name); } -StringRef llvm::AArch64::getCanonicalArchName(StringRef Arch) { - return ARM::getCanonicalArchName(Arch); +void AMDGPU::fillValidArchListR600(SmallVectorImpl<StringRef> &Values) { + for (const auto C : R600GPUs) + Values.push_back(C.Name); } -unsigned llvm::AArch64::parseFPU(StringRef FPU) { - return ARM::parseFPU(FPU); -} +AMDGPU::IsaVersion AMDGPU::getIsaVersion(StringRef GPU) { + if (GPU == "generic") + return {7, 0, 0}; -// Allows partial match, ex. "v8a" matches "armv8a". -AArch64::ArchKind AArch64::parseArch(StringRef Arch) { - Arch = getCanonicalArchName(Arch); - if (checkArchVersion(Arch) < 8) - return ArchKind::INVALID; - - StringRef Syn = getArchSynonym(Arch); - for (const auto A : AArch64ARCHNames) { - if (A.getName().endswith(Syn)) - return A.ID; - } - return ArchKind::INVALID; -} - -AArch64::ArchExtKind llvm::AArch64::parseArchExt(StringRef ArchExt) { - for (const auto A : AArch64ARCHExtNames) { - if (ArchExt == A.getName()) - return static_cast<ArchExtKind>(A.ID); - } - return AArch64::AEK_INVALID; -} + AMDGPU::GPUKind AK = parseArchAMDGCN(GPU); + if (AK == AMDGPU::GPUKind::GK_NONE) + return {0, 0, 0}; -AArch64::ArchKind llvm::AArch64::parseCPUArch(StringRef CPU) { - for (const auto C : AArch64CPUNames) { - if (CPU == C.getName()) - return C.ArchID; + switch (AK) { + case GK_GFX600: return {6, 0, 0}; + case GK_GFX601: return {6, 0, 1}; + case GK_GFX700: return {7, 0, 0}; + case GK_GFX701: return {7, 0, 1}; + case GK_GFX702: return {7, 0, 2}; + case GK_GFX703: return {7, 0, 3}; + case GK_GFX704: return {7, 0, 4}; + case GK_GFX801: return {8, 0, 1}; + case GK_GFX802: return {8, 0, 2}; + case GK_GFX803: return {8, 0, 3}; + case GK_GFX810: return {8, 1, 0}; + case GK_GFX900: return {9, 0, 0}; + case GK_GFX902: return {9, 0, 2}; + case GK_GFX904: return {9, 0, 4}; + case GK_GFX906: return {9, 0, 6}; + case GK_GFX909: return {9, 0, 9}; + default: return {0, 0, 0}; } - return ArchKind::INVALID; -} - -// ARM, Thumb, AArch64 -ARM::ISAKind AArch64::parseArchISA(StringRef Arch) { - return ARM::parseArchISA(Arch); -} - -// Little/Big endian -ARM::EndianKind AArch64::parseArchEndian(StringRef Arch) { - return ARM::parseArchEndian(Arch); -} - -// Profile A/R/M -ARM::ProfileKind AArch64::parseArchProfile(StringRef Arch) { - return ARM::parseArchProfile(Arch); -} - -// Version number (ex. v8 = 8). -unsigned llvm::AArch64::parseArchVersion(StringRef Arch) { - return ARM::parseArchVersion(Arch); -} - -bool llvm::AArch64::isX18ReservedByDefault(const Triple &TT) { - return TT.isOSDarwin() || TT.isOSFuchsia() || TT.isOSWindows(); } diff --git a/lib/Support/TargetRegistry.cpp b/lib/Support/TargetRegistry.cpp index c5eba5714766..bb63891cd713 100644 --- a/lib/Support/TargetRegistry.cpp +++ b/lib/Support/TargetRegistry.cpp @@ -72,7 +72,7 @@ const Target *TargetRegistry::lookupTarget(const std::string &TT, auto I = find_if(targets(), ArchMatch); if (I == targets().end()) { - Error = "No available targets are compatible with this triple."; + Error = "No available targets are compatible with triple \"" + TT + "\""; return nullptr; } diff --git a/lib/Support/Timer.cpp b/lib/Support/Timer.cpp index 61d3b6c6e319..82f5810dd107 100644 --- a/lib/Support/Timer.cpp +++ b/lib/Support/Timer.cpp @@ -295,7 +295,7 @@ void TimerGroup::addTimer(Timer &T) { void TimerGroup::PrintQueuedTimers(raw_ostream &OS) { // Sort the timers in descending order by amount of time taken. - llvm::sort(TimersToPrint.begin(), TimersToPrint.end()); + llvm::sort(TimersToPrint); TimeRecord Total; for (const PrintRecord &Record : TimersToPrint) @@ -343,8 +343,7 @@ void TimerGroup::PrintQueuedTimers(raw_ostream &OS) { } void TimerGroup::prepareToPrintList() { - // See if any of our timers were started, if so add them to TimersToPrint and - // reset them. + // See if any of our timers were started, if so add them to TimersToPrint. for (Timer *T = FirstTimer; T; T = T->Next) { if (!T->hasTriggered()) continue; bool WasRunning = T->isRunning(); @@ -368,6 +367,12 @@ void TimerGroup::print(raw_ostream &OS) { PrintQueuedTimers(OS); } +void TimerGroup::clear() { + sys::SmartScopedLock<true> L(*TimerLock); + for (Timer *T = FirstTimer; T; T = T->Next) + T->clear(); +} + void TimerGroup::printAll(raw_ostream &OS) { sys::SmartScopedLock<true> L(*TimerLock); @@ -375,6 +380,12 @@ void TimerGroup::printAll(raw_ostream &OS) { TG->print(OS); } +void TimerGroup::clearAll() { + sys::SmartScopedLock<true> L(*TimerLock); + for (TimerGroup *TG = TimerGroupList; TG; TG = TG->Next) + TG->clear(); +} + void TimerGroup::printJSONValue(raw_ostream &OS, const PrintRecord &R, const char *suffix, double Value) { assert(yaml::needsQuotes(Name) == yaml::QuotingType::None && diff --git a/lib/Support/Triple.cpp b/lib/Support/Triple.cpp index b14d6492b1ed..26d9327f6208 100644 --- a/lib/Support/Triple.cpp +++ b/lib/Support/Triple.cpp @@ -35,7 +35,6 @@ StringRef Triple::getArchTypeName(ArchType Kind) { case mips64: return "mips64"; case mips64el: return "mips64el"; case msp430: return "msp430"; - case nios2: return "nios2"; case ppc64: return "powerpc64"; case ppc64le: return "powerpc64le"; case ppc: return "powerpc"; @@ -102,8 +101,6 @@ StringRef Triple::getArchTypePrefix(ArchType Kind) { case mips64: case mips64el: return "mips"; - case nios2: return "nios2"; - case hexagon: return "hexagon"; case amdgcn: return "amdgcn"; @@ -209,6 +206,9 @@ StringRef Triple::getOSTypeName(OSType Kind) { case Mesa3D: return "mesa3d"; case Contiki: return "contiki"; case AMDPAL: return "amdpal"; + case HermitCore: return "hermit"; + case Hurd: return "hurd"; + case WASI: return "wasi"; } llvm_unreachable("Invalid OSType"); @@ -271,7 +271,6 @@ Triple::ArchType Triple::getArchTypeForLLVMName(StringRef Name) { .Case("mips64", mips64) .Case("mips64el", mips64el) .Case("msp430", msp430) - .Case("nios2", nios2) .Case("ppc64", ppc64) .Case("ppc32", ppc) .Case("ppc", ppc) @@ -398,11 +397,14 @@ static Triple::ArchType parseArch(StringRef ArchName) { .Case("thumbeb", Triple::thumbeb) .Case("avr", Triple::avr) .Case("msp430", Triple::msp430) - .Cases("mips", "mipseb", "mipsallegrex", Triple::mips) - .Cases("mipsel", "mipsallegrexel", Triple::mipsel) - .Cases("mips64", "mips64eb", Triple::mips64) - .Case("mips64el", Triple::mips64el) - .Case("nios2", Triple::nios2) + .Cases("mips", "mipseb", "mipsallegrex", "mipsisa32r6", + "mipsr6", Triple::mips) + .Cases("mipsel", "mipsallegrexel", "mipsisa32r6el", "mipsr6el", + Triple::mipsel) + .Cases("mips64", "mips64eb", "mipsn32", "mipsisa64r6", + "mips64r6", "mipsn32r6", Triple::mips64) + .Cases("mips64el", "mipsn32el", "mipsisa64r6el", "mips64r6el", + "mipsn32r6el", Triple::mips64el) .Case("r600", Triple::r600) .Case("amdgcn", Triple::amdgcn) .Case("riscv32", Triple::riscv32) @@ -502,6 +504,9 @@ static Triple::OSType parseOS(StringRef OSName) { .StartsWith("mesa3d", Triple::Mesa3D) .StartsWith("contiki", Triple::Contiki) .StartsWith("amdpal", Triple::AMDPAL) + .StartsWith("hermit", Triple::HermitCore) + .StartsWith("hurd", Triple::Hurd) + .StartsWith("wasi", Triple::WASI) .Default(Triple::UnknownOS); } @@ -538,6 +543,10 @@ static Triple::ObjectFormatType parseFormat(StringRef EnvironmentName) { } static Triple::SubArchType parseSubArch(StringRef SubArchName) { + if (SubArchName.startswith("mips") && + (SubArchName.endswith("r6el") || SubArchName.endswith("r6"))) + return Triple::MipsSubArch_r6; + StringRef ARMSubArch = ARM::getCanonicalArchName(SubArchName); // For now, this is the small part. Early return. @@ -594,6 +603,8 @@ static Triple::SubArchType parseSubArch(StringRef SubArchName) { return Triple::ARMSubArch_v8_3a; case ARM::ArchKind::ARMV8_4A: return Triple::ARMSubArch_v8_4a; + case ARM::ArchKind::ARMV8_5A: + return Triple::ARMSubArch_v8_5a; case ARM::ArchKind::ARMV8R: return Triple::ARMSubArch_v8r; case ARM::ArchKind::ARMV8MBaseline: @@ -651,7 +662,6 @@ static Triple::ObjectFormatType getDefaultFormat(const Triple &T) { case Triple::mips64el: case Triple::mipsel: case Triple::msp430: - case Triple::nios2: case Triple::nvptx: case Triple::nvptx64: case Triple::ppc64le: @@ -709,6 +719,15 @@ Triple::Triple(const Twine &Str) ObjectFormat = parseFormat(Components[3]); } } + } else { + Environment = + StringSwitch<Triple::EnvironmentType>(Components[0]) + .StartsWith("mipsn32", Triple::GNUABIN32) + .StartsWith("mips64", Triple::GNUABI64) + .StartsWith("mipsisa64", Triple::GNUABI64) + .StartsWith("mipsisa32", Triple::GNU) + .Cases("mips", "mipsel", "mipsr6", "mipsr6el", Triple::GNU) + .Default(UnknownEnvironment); } } if (ObjectFormat == UnknownObjectFormat) @@ -887,6 +906,12 @@ std::string Triple::normalize(StringRef Str) { } } + // Replace empty components with "unknown" value. + for (unsigned i = 0, e = Components.size(); i < e; ++i) { + if (Components[i].empty()) + Components[i] = "unknown"; + } + // Special case logic goes here. At this point Arch, Vendor and OS have the // correct values for the computed components. std::string NormalizedEnvironment; @@ -1194,7 +1219,6 @@ static unsigned getArchPointerBitWidth(llvm::Triple::ArchType Arch) { case llvm::Triple::le32: case llvm::Triple::mips: case llvm::Triple::mipsel: - case llvm::Triple::nios2: case llvm::Triple::nvptx: case llvm::Triple::ppc: case llvm::Triple::r600: @@ -1279,7 +1303,6 @@ Triple Triple::get32BitArchVariant() const { case Triple::le32: case Triple::mips: case Triple::mipsel: - case Triple::nios2: case Triple::nvptx: case Triple::ppc: case Triple::r600: @@ -1328,7 +1351,6 @@ Triple Triple::get64BitArchVariant() const { case Triple::kalimba: case Triple::lanai: case Triple::msp430: - case Triple::nios2: case Triple::r600: case Triple::tce: case Triple::tcele: @@ -1400,7 +1422,6 @@ Triple Triple::getBigEndianArchVariant() const { case Triple::le32: case Triple::le64: case Triple::msp430: - case Triple::nios2: case Triple::nvptx64: case Triple::nvptx: case Triple::r600: @@ -1487,7 +1508,6 @@ bool Triple::isLittleEndian() const { case Triple::mips64el: case Triple::mipsel: case Triple::msp430: - case Triple::nios2: case Triple::nvptx64: case Triple::nvptx: case Triple::ppc64le: diff --git a/lib/Support/Unix/Path.inc b/lib/Support/Unix/Path.inc index 7ad57d892ff1..d7cc0d627d09 100644 --- a/lib/Support/Unix/Path.inc +++ b/lib/Support/Unix/Path.inc @@ -38,6 +38,8 @@ #ifdef __APPLE__ #include <mach-o/dyld.h> #include <sys/attr.h> +#elif defined(__DragonFly__) +#include <sys/mount.h> #endif // Both stdio.h and cstdio are included via different paths and @@ -49,11 +51,12 @@ // For GNU Hurd #if defined(__GNU__) && !defined(PATH_MAX) # define PATH_MAX 4096 +# define MAXPATHLEN 4096 #endif #include <sys/types.h> #if !defined(__APPLE__) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && \ - !defined(__linux__) + !defined(__linux__) && !defined(__FreeBSD_kernel__) #include <sys/statvfs.h> #define STATVFS statvfs #define FSTATVFS fstatvfs @@ -82,7 +85,7 @@ #define STATVFS_F_FRSIZE(vfs) static_cast<uint64_t>(vfs.f_bsize) #endif -#if defined(__NetBSD__) +#if defined(__NetBSD__) || defined(__DragonFly__) || defined(__GNU__) #define STATVFS_F_FLAG(vfs) (vfs).f_flag #else #define STATVFS_F_FLAG(vfs) (vfs).f_flags @@ -98,7 +101,7 @@ const file_t kInvalidFile = -1; #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \ defined(__minix) || defined(__FreeBSD_kernel__) || defined(__linux__) || \ - defined(__CYGWIN__) || defined(__DragonFly__) || defined(_AIX) + defined(__CYGWIN__) || defined(__DragonFly__) || defined(_AIX) || defined(__GNU__) static int test_dir(char ret[PATH_MAX], const char *dir, const char *bin) { @@ -178,14 +181,34 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { char exe_path[MAXPATHLEN]; StringRef aPath("/proc/self/exe"); if (sys::fs::exists(aPath)) { - // /proc is not always mounted under Linux (chroot for example). - ssize_t len = readlink(aPath.str().c_str(), exe_path, sizeof(exe_path)); - if (len >= 0) - return std::string(exe_path, len); + // /proc is not always mounted under Linux (chroot for example). + ssize_t len = readlink(aPath.str().c_str(), exe_path, sizeof(exe_path)); + if (len < 0) + return ""; + + // Null terminate the string for realpath. readlink never null + // terminates its output. + len = std::min(len, ssize_t(sizeof(exe_path) - 1)); + exe_path[len] = '\0'; + + // On Linux, /proc/self/exe always looks through symlinks. However, on + // GNU/Hurd, /proc/self/exe is a symlink to the path that was used to start + // the program, and not the eventual binary file. Therefore, call realpath + // so this behaves the same on all platforms. +#if _POSIX_VERSION >= 200112 || defined(__GLIBC__) + char *real_path = realpath(exe_path, NULL); + std::string ret = std::string(real_path); + free(real_path); + return ret; +#else + char real_path[MAXPATHLEN]; + realpath(exe_path, real_path); + return std::string(real_path); +#endif } else { - // Fall back to the classical detection. - if (getprogpath(exe_path, argv0)) - return exe_path; + // Fall back to the classical detection. + if (getprogpath(exe_path, argv0)) + return exe_path; } #elif defined(HAVE_DLFCN_H) && defined(HAVE_DLADDR) // Use dladdr to get executable path if available. @@ -206,11 +229,11 @@ std::string getMainExecutable(const char *argv0, void *MainAddr) { } TimePoint<> basic_file_status::getLastAccessedTime() const { - return toTimePoint(fs_st_atime); + return toTimePoint(fs_st_atime, fs_st_atime_nsec); } TimePoint<> basic_file_status::getLastModificationTime() const { - return toTimePoint(fs_st_mtime); + return toTimePoint(fs_st_mtime, fs_st_mtime_nsec); } UniqueID file_status::getUniqueID() const { @@ -347,7 +370,7 @@ std::error_code remove(const Twine &path, bool IgnoreNonExisting) { } static bool is_local_impl(struct STATVFS &Vfs) { -#if defined(__linux__) +#if defined(__linux__) || defined(__GNU__) #ifndef NFS_SUPER_MAGIC #define NFS_SUPER_MAGIC 0x6969 #endif @@ -357,7 +380,11 @@ static bool is_local_impl(struct STATVFS &Vfs) { #ifndef CIFS_MAGIC_NUMBER #define CIFS_MAGIC_NUMBER 0xFF534D42 #endif +#ifdef __GNU__ + switch ((uint32_t)Vfs.__f_type) { +#else switch ((uint32_t)Vfs.f_type) { +#endif case NFS_SUPER_MAGIC: case SMB_SUPER_MAGIC: case CIFS_MAGIC_NUMBER: @@ -523,37 +550,62 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) { llvm::sys::path::append(Path, Storage); } + +void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) { + dest.clear(); + if (path.isTriviallyEmpty()) + return; + + path.toVector(dest); + expandTildeExpr(dest); + + return; +} + +static file_type typeForMode(mode_t Mode) { + if (S_ISDIR(Mode)) + return file_type::directory_file; + else if (S_ISREG(Mode)) + return file_type::regular_file; + else if (S_ISBLK(Mode)) + return file_type::block_file; + else if (S_ISCHR(Mode)) + return file_type::character_file; + else if (S_ISFIFO(Mode)) + return file_type::fifo_file; + else if (S_ISSOCK(Mode)) + return file_type::socket_file; + else if (S_ISLNK(Mode)) + return file_type::symlink_file; + return file_type::type_unknown; +} + static std::error_code fillStatus(int StatRet, const struct stat &Status, file_status &Result) { if (StatRet != 0) { - std::error_code ec(errno, std::generic_category()); - if (ec == errc::no_such_file_or_directory) + std::error_code EC(errno, std::generic_category()); + if (EC == errc::no_such_file_or_directory) Result = file_status(file_type::file_not_found); else Result = file_status(file_type::status_error); - return ec; + return EC; } - file_type Type = file_type::type_unknown; - - if (S_ISDIR(Status.st_mode)) - Type = file_type::directory_file; - else if (S_ISREG(Status.st_mode)) - Type = file_type::regular_file; - else if (S_ISBLK(Status.st_mode)) - Type = file_type::block_file; - else if (S_ISCHR(Status.st_mode)) - Type = file_type::character_file; - else if (S_ISFIFO(Status.st_mode)) - Type = file_type::fifo_file; - else if (S_ISSOCK(Status.st_mode)) - Type = file_type::socket_file; - else if (S_ISLNK(Status.st_mode)) - Type = file_type::symlink_file; + uint32_t atime_nsec, mtime_nsec; +#if defined(HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC) + atime_nsec = Status.st_atimespec.tv_nsec; + mtime_nsec = Status.st_mtimespec.tv_nsec; +#elif defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC) + atime_nsec = Status.st_atim.tv_nsec; + mtime_nsec = Status.st_mtim.tv_nsec; +#else + atime_nsec = mtime_nsec = 0; +#endif perms Perms = static_cast<perms>(Status.st_mode) & all_perms; - Result = file_status(Type, Perms, Status.st_dev, Status.st_nlink, - Status.st_ino, Status.st_atime, Status.st_mtime, + Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev, + Status.st_nlink, Status.st_ino, + Status.st_atime, atime_nsec, Status.st_mtime, mtime_nsec, Status.st_uid, Status.st_gid, Status.st_size); return std::error_code(); @@ -583,17 +635,22 @@ std::error_code setPermissions(const Twine &Path, perms Permissions) { return std::error_code(); } -std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { +std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime, + TimePoint<> ModificationTime) { #if defined(HAVE_FUTIMENS) timespec Times[2]; - Times[0] = Times[1] = sys::toTimeSpec(Time); + Times[0] = sys::toTimeSpec(AccessTime); + Times[1] = sys::toTimeSpec(ModificationTime); if (::futimens(FD, Times)) return std::error_code(errno, std::generic_category()); return std::error_code(); #elif defined(HAVE_FUTIMES) timeval Times[2]; - Times[0] = Times[1] = sys::toTimeVal( - std::chrono::time_point_cast<std::chrono::microseconds>(Time)); + Times[0] = sys::toTimeVal( + std::chrono::time_point_cast<std::chrono::microseconds>(AccessTime)); + Times[1] = + sys::toTimeVal(std::chrono::time_point_cast<std::chrono::microseconds>( + ModificationTime)); if (::futimes(FD, Times)) return std::error_code(errno, std::generic_category()); return std::error_code(); @@ -691,19 +748,30 @@ std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) { return std::error_code(); } -std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { +static file_type direntType(dirent* Entry) { + // Most platforms provide the file type in the dirent: Linux/BSD/Mac. + // The DTTOIF macro lets us reuse our status -> type conversion. +#if defined(_DIRENT_HAVE_D_TYPE) && defined(DTTOIF) + return typeForMode(DTTOIF(Entry->d_type)); +#else + // Other platforms such as Solaris require a stat() to get the type. + return file_type::type_unknown; +#endif +} + +std::error_code detail::directory_iterator_increment(detail::DirIterState &It) { errno = 0; - dirent *cur_dir = ::readdir(reinterpret_cast<DIR *>(it.IterationHandle)); - if (cur_dir == nullptr && errno != 0) { + dirent *CurDir = ::readdir(reinterpret_cast<DIR *>(It.IterationHandle)); + if (CurDir == nullptr && errno != 0) { return std::error_code(errno, std::generic_category()); - } else if (cur_dir != nullptr) { - StringRef name(cur_dir->d_name); - if ((name.size() == 1 && name[0] == '.') || - (name.size() == 2 && name[0] == '.' && name[1] == '.')) - return directory_iterator_increment(it); - it.CurrentEntry.replace_filename(name); + } else if (CurDir != nullptr) { + StringRef Name(CurDir->d_name); + if ((Name.size() == 1 && Name[0] == '.') || + (Name.size() == 2 && Name[0] == '.' && Name[1] == '.')) + return directory_iterator_increment(It); + It.CurrentEntry.replace_filename(Name, direntType(CurDir)); } else - return directory_iterator_destruct(it); + return directory_iterator_destruct(It); return std::error_code(); } @@ -769,8 +837,10 @@ std::error_code openFile(const Twine &Name, int &ResultFD, SmallString<128> Storage; StringRef P = Name.toNullTerminatedStringRef(Storage); - if ((ResultFD = sys::RetryAfterSignal(-1, ::open, P.begin(), OpenFlags, Mode)) < - 0) + // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal + // when open is overloaded, such as in Bionic. + auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); }; + if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0) return std::error_code(errno, std::generic_category()); #ifndef O_CLOEXEC if (!(Flags & OF_ChildInherit)) { @@ -950,29 +1020,6 @@ static bool getDarwinConfDir(bool TempDir, SmallVectorImpl<char> &Result) { return false; } -static bool getUserCacheDir(SmallVectorImpl<char> &Result) { - // First try using XDG_CACHE_HOME env variable, - // as specified in XDG Base Directory Specification at - // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html - if (const char *XdgCacheDir = std::getenv("XDG_CACHE_HOME")) { - Result.clear(); - Result.append(XdgCacheDir, XdgCacheDir + strlen(XdgCacheDir)); - return true; - } - - // Try Darwin configuration query - if (getDarwinConfDir(false, Result)) - return true; - - // Use "$HOME/.cache" if $HOME is available - if (home_directory(Result)) { - append(Result, ".cache"); - return true; - } - - return false; -} - static const char *getEnvTempDir() { // Check whether the temporary directory is specified by an environment // variable. diff --git a/lib/Support/Unix/Process.inc b/lib/Support/Unix/Process.inc index fa515d44f3f2..3185f45a3a61 100644 --- a/lib/Support/Unix/Process.inc +++ b/lib/Support/Unix/Process.inc @@ -211,7 +211,10 @@ std::error_code Process::FixupStandardFileDescriptors() { assert(errno == EBADF && "expected errno to have EBADF at this point!"); if (NullFD < 0) { - if ((NullFD = RetryAfterSignal(-1, ::open, "/dev/null", O_RDWR)) < 0) + // Call ::open in a lambda to avoid overload resolution in + // RetryAfterSignal when open is overloaded, such as in Bionic. + auto Open = [&]() { return ::open("/dev/null", O_RDWR); }; + if ((NullFD = RetryAfterSignal(-1, Open)) < 0) return std::error_code(errno, std::generic_category()); } diff --git a/lib/Support/Unix/Signals.inc b/lib/Support/Unix/Signals.inc index de26695d64ea..ad88d5e96906 100644 --- a/lib/Support/Unix/Signals.inc +++ b/lib/Support/Unix/Signals.inc @@ -47,6 +47,7 @@ #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <string> +#include <sysexits.h> #ifdef HAVE_BACKTRACE # include BACKTRACE_HEADER // For backtrace(). #endif @@ -334,6 +335,10 @@ static RETSIGTYPE SignalHandler(int Sig) { if (auto OldInterruptFunction = InterruptFunction.exchange(nullptr)) return OldInterruptFunction(); + // Send a special return code that drivers can check for, from sysexits.h. + if (Sig == SIGPIPE) + exit(EX_IOERR); + raise(Sig); // Execute the default handler. return; } diff --git a/lib/Support/VirtualFileSystem.cpp b/lib/Support/VirtualFileSystem.cpp new file mode 100644 index 000000000000..f2a8a1bb27af --- /dev/null +++ b/lib/Support/VirtualFileSystem.cpp @@ -0,0 +1,2070 @@ +//===- VirtualFileSystem.cpp - Virtual File System Layer ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the VirtualFileSystem interface. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/VirtualFileSystem.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/IntrusiveRefCntPtr.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/Twine.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Config/llvm-config.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Chrono.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/SMLoc.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <atomic> +#include <cassert> +#include <cstdint> +#include <iterator> +#include <limits> +#include <map> +#include <memory> +#include <mutex> +#include <string> +#include <system_error> +#include <utility> +#include <vector> + +using namespace llvm; +using namespace llvm::vfs; + +using llvm::sys::fs::file_status; +using llvm::sys::fs::file_type; +using llvm::sys::fs::perms; +using llvm::sys::fs::UniqueID; + +Status::Status(const file_status &Status) + : UID(Status.getUniqueID()), MTime(Status.getLastModificationTime()), + User(Status.getUser()), Group(Status.getGroup()), Size(Status.getSize()), + Type(Status.type()), Perms(Status.permissions()) {} + +Status::Status(StringRef Name, UniqueID UID, sys::TimePoint<> MTime, + uint32_t User, uint32_t Group, uint64_t Size, file_type Type, + perms Perms) + : Name(Name), UID(UID), MTime(MTime), User(User), Group(Group), Size(Size), + Type(Type), Perms(Perms) {} + +Status Status::copyWithNewName(const Status &In, StringRef NewName) { + return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), + In.getUser(), In.getGroup(), In.getSize(), In.getType(), + In.getPermissions()); +} + +Status Status::copyWithNewName(const file_status &In, StringRef NewName) { + return Status(NewName, In.getUniqueID(), In.getLastModificationTime(), + In.getUser(), In.getGroup(), In.getSize(), In.type(), + In.permissions()); +} + +bool Status::equivalent(const Status &Other) const { + assert(isStatusKnown() && Other.isStatusKnown()); + return getUniqueID() == Other.getUniqueID(); +} + +bool Status::isDirectory() const { return Type == file_type::directory_file; } + +bool Status::isRegularFile() const { return Type == file_type::regular_file; } + +bool Status::isOther() const { + return exists() && !isRegularFile() && !isDirectory() && !isSymlink(); +} + +bool Status::isSymlink() const { return Type == file_type::symlink_file; } + +bool Status::isStatusKnown() const { return Type != file_type::status_error; } + +bool Status::exists() const { + return isStatusKnown() && Type != file_type::file_not_found; +} + +File::~File() = default; + +FileSystem::~FileSystem() = default; + +ErrorOr<std::unique_ptr<MemoryBuffer>> +FileSystem::getBufferForFile(const llvm::Twine &Name, int64_t FileSize, + bool RequiresNullTerminator, bool IsVolatile) { + auto F = openFileForRead(Name); + if (!F) + return F.getError(); + + return (*F)->getBuffer(Name, FileSize, RequiresNullTerminator, IsVolatile); +} + +std::error_code FileSystem::makeAbsolute(SmallVectorImpl<char> &Path) const { + if (llvm::sys::path::is_absolute(Path)) + return {}; + + auto WorkingDir = getCurrentWorkingDirectory(); + if (!WorkingDir) + return WorkingDir.getError(); + + llvm::sys::fs::make_absolute(WorkingDir.get(), Path); + return {}; +} + +std::error_code FileSystem::getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const { + return errc::operation_not_permitted; +} + +std::error_code FileSystem::isLocal(const Twine &Path, bool &Result) { + return errc::operation_not_permitted; +} + +bool FileSystem::exists(const Twine &Path) { + auto Status = status(Path); + return Status && Status->exists(); +} + +#ifndef NDEBUG +static bool isTraversalComponent(StringRef Component) { + return Component.equals("..") || Component.equals("."); +} + +static bool pathHasTraversal(StringRef Path) { + using namespace llvm::sys; + + for (StringRef Comp : llvm::make_range(path::begin(Path), path::end(Path))) + if (isTraversalComponent(Comp)) + return true; + return false; +} +#endif + +//===-----------------------------------------------------------------------===/ +// RealFileSystem implementation +//===-----------------------------------------------------------------------===/ + +namespace { + +/// Wrapper around a raw file descriptor. +class RealFile : public File { + friend class RealFileSystem; + + int FD; + Status S; + std::string RealName; + + RealFile(int FD, StringRef NewName, StringRef NewRealPathName) + : FD(FD), S(NewName, {}, {}, {}, {}, {}, + llvm::sys::fs::file_type::status_error, {}), + RealName(NewRealPathName.str()) { + assert(FD >= 0 && "Invalid or inactive file descriptor"); + } + +public: + ~RealFile() override; + + ErrorOr<Status> status() override; + ErrorOr<std::string> getName() override; + ErrorOr<std::unique_ptr<MemoryBuffer>> getBuffer(const Twine &Name, + int64_t FileSize, + bool RequiresNullTerminator, + bool IsVolatile) override; + std::error_code close() override; +}; + +} // namespace + +RealFile::~RealFile() { close(); } + +ErrorOr<Status> RealFile::status() { + assert(FD != -1 && "cannot stat closed file"); + if (!S.isStatusKnown()) { + file_status RealStatus; + if (std::error_code EC = sys::fs::status(FD, RealStatus)) + return EC; + S = Status::copyWithNewName(RealStatus, S.getName()); + } + return S; +} + +ErrorOr<std::string> RealFile::getName() { + return RealName.empty() ? S.getName().str() : RealName; +} + +ErrorOr<std::unique_ptr<MemoryBuffer>> +RealFile::getBuffer(const Twine &Name, int64_t FileSize, + bool RequiresNullTerminator, bool IsVolatile) { + assert(FD != -1 && "cannot get buffer for closed file"); + return MemoryBuffer::getOpenFile(FD, Name, FileSize, RequiresNullTerminator, + IsVolatile); +} + +std::error_code RealFile::close() { + std::error_code EC = sys::Process::SafelyCloseFileDescriptor(FD); + FD = -1; + return EC; +} + +namespace { + +/// The file system according to your operating system. +class RealFileSystem : public FileSystem { +public: + ErrorOr<Status> status(const Twine &Path) override; + ErrorOr<std::unique_ptr<File>> openFileForRead(const Twine &Path) override; + directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override; + + llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override; + std::error_code setCurrentWorkingDirectory(const Twine &Path) override; + std::error_code isLocal(const Twine &Path, bool &Result) override; + std::error_code getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const override; + +private: + mutable std::mutex CWDMutex; + mutable std::string CWDCache; +}; + +} // namespace + +ErrorOr<Status> RealFileSystem::status(const Twine &Path) { + sys::fs::file_status RealStatus; + if (std::error_code EC = sys::fs::status(Path, RealStatus)) + return EC; + return Status::copyWithNewName(RealStatus, Path.str()); +} + +ErrorOr<std::unique_ptr<File>> +RealFileSystem::openFileForRead(const Twine &Name) { + int FD; + SmallString<256> RealName; + if (std::error_code EC = + sys::fs::openFileForRead(Name, FD, sys::fs::OF_None, &RealName)) + return EC; + return std::unique_ptr<File>(new RealFile(FD, Name.str(), RealName.str())); +} + +llvm::ErrorOr<std::string> RealFileSystem::getCurrentWorkingDirectory() const { + std::lock_guard<std::mutex> Lock(CWDMutex); + if (!CWDCache.empty()) + return CWDCache; + SmallString<256> Dir; + if (std::error_code EC = llvm::sys::fs::current_path(Dir)) + return EC; + CWDCache = Dir.str(); + return CWDCache; +} + +std::error_code RealFileSystem::setCurrentWorkingDirectory(const Twine &Path) { + // FIXME: chdir is thread hostile; on the other hand, creating the same + // behavior as chdir is complex: chdir resolves the path once, thus + // guaranteeing that all subsequent relative path operations work + // on the same path the original chdir resulted in. This makes a + // difference for example on network filesystems, where symlinks might be + // switched during runtime of the tool. Fixing this depends on having a + // file system abstraction that allows openat() style interactions. + if (auto EC = llvm::sys::fs::set_current_path(Path)) + return EC; + + // Invalidate cache. + std::lock_guard<std::mutex> Lock(CWDMutex); + CWDCache.clear(); + return std::error_code(); +} + +std::error_code RealFileSystem::isLocal(const Twine &Path, bool &Result) { + return llvm::sys::fs::is_local(Path, Result); +} + +std::error_code +RealFileSystem::getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const { + return llvm::sys::fs::real_path(Path, Output); +} + +IntrusiveRefCntPtr<FileSystem> vfs::getRealFileSystem() { + static IntrusiveRefCntPtr<FileSystem> FS = new RealFileSystem(); + return FS; +} + +namespace { + +class RealFSDirIter : public llvm::vfs::detail::DirIterImpl { + llvm::sys::fs::directory_iterator Iter; + +public: + RealFSDirIter(const Twine &Path, std::error_code &EC) : Iter(Path, EC) { + if (Iter != llvm::sys::fs::directory_iterator()) + CurrentEntry = directory_entry(Iter->path(), Iter->type()); + } + + std::error_code increment() override { + std::error_code EC; + Iter.increment(EC); + CurrentEntry = (Iter == llvm::sys::fs::directory_iterator()) + ? directory_entry() + : directory_entry(Iter->path(), Iter->type()); + return EC; + } +}; + +} // namespace + +directory_iterator RealFileSystem::dir_begin(const Twine &Dir, + std::error_code &EC) { + return directory_iterator(std::make_shared<RealFSDirIter>(Dir, EC)); +} + +//===-----------------------------------------------------------------------===/ +// OverlayFileSystem implementation +//===-----------------------------------------------------------------------===/ + +OverlayFileSystem::OverlayFileSystem(IntrusiveRefCntPtr<FileSystem> BaseFS) { + FSList.push_back(std::move(BaseFS)); +} + +void OverlayFileSystem::pushOverlay(IntrusiveRefCntPtr<FileSystem> FS) { + FSList.push_back(FS); + // Synchronize added file systems by duplicating the working directory from + // the first one in the list. + FS->setCurrentWorkingDirectory(getCurrentWorkingDirectory().get()); +} + +ErrorOr<Status> OverlayFileSystem::status(const Twine &Path) { + // FIXME: handle symlinks that cross file systems + for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) { + ErrorOr<Status> Status = (*I)->status(Path); + if (Status || Status.getError() != llvm::errc::no_such_file_or_directory) + return Status; + } + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +ErrorOr<std::unique_ptr<File>> +OverlayFileSystem::openFileForRead(const llvm::Twine &Path) { + // FIXME: handle symlinks that cross file systems + for (iterator I = overlays_begin(), E = overlays_end(); I != E; ++I) { + auto Result = (*I)->openFileForRead(Path); + if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) + return Result; + } + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +llvm::ErrorOr<std::string> +OverlayFileSystem::getCurrentWorkingDirectory() const { + // All file systems are synchronized, just take the first working directory. + return FSList.front()->getCurrentWorkingDirectory(); +} + +std::error_code +OverlayFileSystem::setCurrentWorkingDirectory(const Twine &Path) { + for (auto &FS : FSList) + if (std::error_code EC = FS->setCurrentWorkingDirectory(Path)) + return EC; + return {}; +} + +std::error_code OverlayFileSystem::isLocal(const Twine &Path, bool &Result) { + for (auto &FS : FSList) + if (FS->exists(Path)) + return FS->isLocal(Path, Result); + return errc::no_such_file_or_directory; +} + +std::error_code +OverlayFileSystem::getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const { + for (auto &FS : FSList) + if (FS->exists(Path)) + return FS->getRealPath(Path, Output); + return errc::no_such_file_or_directory; +} + +llvm::vfs::detail::DirIterImpl::~DirIterImpl() = default; + +namespace { + +class OverlayFSDirIterImpl : public llvm::vfs::detail::DirIterImpl { + OverlayFileSystem &Overlays; + std::string Path; + OverlayFileSystem::iterator CurrentFS; + directory_iterator CurrentDirIter; + llvm::StringSet<> SeenNames; + + std::error_code incrementFS() { + assert(CurrentFS != Overlays.overlays_end() && "incrementing past end"); + ++CurrentFS; + for (auto E = Overlays.overlays_end(); CurrentFS != E; ++CurrentFS) { + std::error_code EC; + CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC); + if (EC && EC != errc::no_such_file_or_directory) + return EC; + if (CurrentDirIter != directory_iterator()) + break; // found + } + return {}; + } + + std::error_code incrementDirIter(bool IsFirstTime) { + assert((IsFirstTime || CurrentDirIter != directory_iterator()) && + "incrementing past end"); + std::error_code EC; + if (!IsFirstTime) + CurrentDirIter.increment(EC); + if (!EC && CurrentDirIter == directory_iterator()) + EC = incrementFS(); + return EC; + } + + std::error_code incrementImpl(bool IsFirstTime) { + while (true) { + std::error_code EC = incrementDirIter(IsFirstTime); + if (EC || CurrentDirIter == directory_iterator()) { + CurrentEntry = directory_entry(); + return EC; + } + CurrentEntry = *CurrentDirIter; + StringRef Name = llvm::sys::path::filename(CurrentEntry.path()); + if (SeenNames.insert(Name).second) + return EC; // name not seen before + } + llvm_unreachable("returned above"); + } + +public: + OverlayFSDirIterImpl(const Twine &Path, OverlayFileSystem &FS, + std::error_code &EC) + : Overlays(FS), Path(Path.str()), CurrentFS(Overlays.overlays_begin()) { + CurrentDirIter = (*CurrentFS)->dir_begin(Path, EC); + EC = incrementImpl(true); + } + + std::error_code increment() override { return incrementImpl(false); } +}; + +} // namespace + +directory_iterator OverlayFileSystem::dir_begin(const Twine &Dir, + std::error_code &EC) { + return directory_iterator( + std::make_shared<OverlayFSDirIterImpl>(Dir, *this, EC)); +} + +void ProxyFileSystem::anchor() {} + +namespace llvm { +namespace vfs { + +namespace detail { + +enum InMemoryNodeKind { IME_File, IME_Directory, IME_HardLink }; + +/// The in memory file system is a tree of Nodes. Every node can either be a +/// file , hardlink or a directory. +class InMemoryNode { + InMemoryNodeKind Kind; + std::string FileName; + +public: + InMemoryNode(llvm::StringRef FileName, InMemoryNodeKind Kind) + : Kind(Kind), FileName(llvm::sys::path::filename(FileName)) {} + virtual ~InMemoryNode() = default; + + /// Get the filename of this node (the name without the directory part). + StringRef getFileName() const { return FileName; } + InMemoryNodeKind getKind() const { return Kind; } + virtual std::string toString(unsigned Indent) const = 0; +}; + +class InMemoryFile : public InMemoryNode { + Status Stat; + std::unique_ptr<llvm::MemoryBuffer> Buffer; + +public: + InMemoryFile(Status Stat, std::unique_ptr<llvm::MemoryBuffer> Buffer) + : InMemoryNode(Stat.getName(), IME_File), Stat(std::move(Stat)), + Buffer(std::move(Buffer)) {} + + /// Return the \p Status for this node. \p RequestedName should be the name + /// through which the caller referred to this node. It will override + /// \p Status::Name in the return value, to mimic the behavior of \p RealFile. + Status getStatus(StringRef RequestedName) const { + return Status::copyWithNewName(Stat, RequestedName); + } + llvm::MemoryBuffer *getBuffer() const { return Buffer.get(); } + + std::string toString(unsigned Indent) const override { + return (std::string(Indent, ' ') + Stat.getName() + "\n").str(); + } + + static bool classof(const InMemoryNode *N) { + return N->getKind() == IME_File; + } +}; + +namespace { + +class InMemoryHardLink : public InMemoryNode { + const InMemoryFile &ResolvedFile; + +public: + InMemoryHardLink(StringRef Path, const InMemoryFile &ResolvedFile) + : InMemoryNode(Path, IME_HardLink), ResolvedFile(ResolvedFile) {} + const InMemoryFile &getResolvedFile() const { return ResolvedFile; } + + std::string toString(unsigned Indent) const override { + return std::string(Indent, ' ') + "HardLink to -> " + + ResolvedFile.toString(0); + } + + static bool classof(const InMemoryNode *N) { + return N->getKind() == IME_HardLink; + } +}; + +/// Adapt a InMemoryFile for VFS' File interface. The goal is to make +/// \p InMemoryFileAdaptor mimic as much as possible the behavior of +/// \p RealFile. +class InMemoryFileAdaptor : public File { + const InMemoryFile &Node; + /// The name to use when returning a Status for this file. + std::string RequestedName; + +public: + explicit InMemoryFileAdaptor(const InMemoryFile &Node, + std::string RequestedName) + : Node(Node), RequestedName(std::move(RequestedName)) {} + + llvm::ErrorOr<Status> status() override { + return Node.getStatus(RequestedName); + } + + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> + getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, + bool IsVolatile) override { + llvm::MemoryBuffer *Buf = Node.getBuffer(); + return llvm::MemoryBuffer::getMemBuffer( + Buf->getBuffer(), Buf->getBufferIdentifier(), RequiresNullTerminator); + } + + std::error_code close() override { return {}; } +}; +} // namespace + +class InMemoryDirectory : public InMemoryNode { + Status Stat; + llvm::StringMap<std::unique_ptr<InMemoryNode>> Entries; + +public: + InMemoryDirectory(Status Stat) + : InMemoryNode(Stat.getName(), IME_Directory), Stat(std::move(Stat)) {} + + /// Return the \p Status for this node. \p RequestedName should be the name + /// through which the caller referred to this node. It will override + /// \p Status::Name in the return value, to mimic the behavior of \p RealFile. + Status getStatus(StringRef RequestedName) const { + return Status::copyWithNewName(Stat, RequestedName); + } + InMemoryNode *getChild(StringRef Name) { + auto I = Entries.find(Name); + if (I != Entries.end()) + return I->second.get(); + return nullptr; + } + + InMemoryNode *addChild(StringRef Name, std::unique_ptr<InMemoryNode> Child) { + return Entries.insert(make_pair(Name, std::move(Child))) + .first->second.get(); + } + + using const_iterator = decltype(Entries)::const_iterator; + + const_iterator begin() const { return Entries.begin(); } + const_iterator end() const { return Entries.end(); } + + std::string toString(unsigned Indent) const override { + std::string Result = + (std::string(Indent, ' ') + Stat.getName() + "\n").str(); + for (const auto &Entry : Entries) + Result += Entry.second->toString(Indent + 2); + return Result; + } + + static bool classof(const InMemoryNode *N) { + return N->getKind() == IME_Directory; + } +}; + +namespace { +Status getNodeStatus(const InMemoryNode *Node, StringRef RequestedName) { + if (auto Dir = dyn_cast<detail::InMemoryDirectory>(Node)) + return Dir->getStatus(RequestedName); + if (auto File = dyn_cast<detail::InMemoryFile>(Node)) + return File->getStatus(RequestedName); + if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) + return Link->getResolvedFile().getStatus(RequestedName); + llvm_unreachable("Unknown node type"); +} +} // namespace +} // namespace detail + +InMemoryFileSystem::InMemoryFileSystem(bool UseNormalizedPaths) + : Root(new detail::InMemoryDirectory( + Status("", getNextVirtualUniqueID(), llvm::sys::TimePoint<>(), 0, 0, + 0, llvm::sys::fs::file_type::directory_file, + llvm::sys::fs::perms::all_all))), + UseNormalizedPaths(UseNormalizedPaths) {} + +InMemoryFileSystem::~InMemoryFileSystem() = default; + +std::string InMemoryFileSystem::toString() const { + return Root->toString(/*Indent=*/0); +} + +bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, + std::unique_ptr<llvm::MemoryBuffer> Buffer, + Optional<uint32_t> User, + Optional<uint32_t> Group, + Optional<llvm::sys::fs::file_type> Type, + Optional<llvm::sys::fs::perms> Perms, + const detail::InMemoryFile *HardLinkTarget) { + SmallString<128> Path; + P.toVector(Path); + + // Fix up relative paths. This just prepends the current working directory. + std::error_code EC = makeAbsolute(Path); + assert(!EC); + (void)EC; + + if (useNormalizedPaths()) + llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + + if (Path.empty()) + return false; + + detail::InMemoryDirectory *Dir = Root.get(); + auto I = llvm::sys::path::begin(Path), E = sys::path::end(Path); + const auto ResolvedUser = User.getValueOr(0); + const auto ResolvedGroup = Group.getValueOr(0); + const auto ResolvedType = Type.getValueOr(sys::fs::file_type::regular_file); + const auto ResolvedPerms = Perms.getValueOr(sys::fs::all_all); + assert(!(HardLinkTarget && Buffer) && "HardLink cannot have a buffer"); + // Any intermediate directories we create should be accessible by + // the owner, even if Perms says otherwise for the final path. + const auto NewDirectoryPerms = ResolvedPerms | sys::fs::owner_all; + while (true) { + StringRef Name = *I; + detail::InMemoryNode *Node = Dir->getChild(Name); + ++I; + if (!Node) { + if (I == E) { + // End of the path. + std::unique_ptr<detail::InMemoryNode> Child; + if (HardLinkTarget) + Child.reset(new detail::InMemoryHardLink(P.str(), *HardLinkTarget)); + else { + // Create a new file or directory. + Status Stat(P.str(), getNextVirtualUniqueID(), + llvm::sys::toTimePoint(ModificationTime), ResolvedUser, + ResolvedGroup, Buffer->getBufferSize(), ResolvedType, + ResolvedPerms); + if (ResolvedType == sys::fs::file_type::directory_file) { + Child.reset(new detail::InMemoryDirectory(std::move(Stat))); + } else { + Child.reset( + new detail::InMemoryFile(std::move(Stat), std::move(Buffer))); + } + } + Dir->addChild(Name, std::move(Child)); + return true; + } + + // Create a new directory. Use the path up to here. + Status Stat( + StringRef(Path.str().begin(), Name.end() - Path.str().begin()), + getNextVirtualUniqueID(), llvm::sys::toTimePoint(ModificationTime), + ResolvedUser, ResolvedGroup, 0, sys::fs::file_type::directory_file, + NewDirectoryPerms); + Dir = cast<detail::InMemoryDirectory>(Dir->addChild( + Name, llvm::make_unique<detail::InMemoryDirectory>(std::move(Stat)))); + continue; + } + + if (auto *NewDir = dyn_cast<detail::InMemoryDirectory>(Node)) { + Dir = NewDir; + } else { + assert((isa<detail::InMemoryFile>(Node) || + isa<detail::InMemoryHardLink>(Node)) && + "Must be either file, hardlink or directory!"); + + // Trying to insert a directory in place of a file. + if (I != E) + return false; + + // Return false only if the new file is different from the existing one. + if (auto Link = dyn_cast<detail::InMemoryHardLink>(Node)) { + return Link->getResolvedFile().getBuffer()->getBuffer() == + Buffer->getBuffer(); + } + return cast<detail::InMemoryFile>(Node)->getBuffer()->getBuffer() == + Buffer->getBuffer(); + } + } +} + +bool InMemoryFileSystem::addFile(const Twine &P, time_t ModificationTime, + std::unique_ptr<llvm::MemoryBuffer> Buffer, + Optional<uint32_t> User, + Optional<uint32_t> Group, + Optional<llvm::sys::fs::file_type> Type, + Optional<llvm::sys::fs::perms> Perms) { + return addFile(P, ModificationTime, std::move(Buffer), User, Group, Type, + Perms, /*HardLinkTarget=*/nullptr); +} + +bool InMemoryFileSystem::addFileNoOwn(const Twine &P, time_t ModificationTime, + llvm::MemoryBuffer *Buffer, + Optional<uint32_t> User, + Optional<uint32_t> Group, + Optional<llvm::sys::fs::file_type> Type, + Optional<llvm::sys::fs::perms> Perms) { + return addFile(P, ModificationTime, + llvm::MemoryBuffer::getMemBuffer( + Buffer->getBuffer(), Buffer->getBufferIdentifier()), + std::move(User), std::move(Group), std::move(Type), + std::move(Perms)); +} + +static ErrorOr<const detail::InMemoryNode *> +lookupInMemoryNode(const InMemoryFileSystem &FS, detail::InMemoryDirectory *Dir, + const Twine &P) { + SmallString<128> Path; + P.toVector(Path); + + // Fix up relative paths. This just prepends the current working directory. + std::error_code EC = FS.makeAbsolute(Path); + assert(!EC); + (void)EC; + + if (FS.useNormalizedPaths()) + llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + + if (Path.empty()) + return Dir; + + auto I = llvm::sys::path::begin(Path), E = llvm::sys::path::end(Path); + while (true) { + detail::InMemoryNode *Node = Dir->getChild(*I); + ++I; + if (!Node) + return errc::no_such_file_or_directory; + + // Return the file if it's at the end of the path. + if (auto File = dyn_cast<detail::InMemoryFile>(Node)) { + if (I == E) + return File; + return errc::no_such_file_or_directory; + } + + // If Node is HardLink then return the resolved file. + if (auto File = dyn_cast<detail::InMemoryHardLink>(Node)) { + if (I == E) + return &File->getResolvedFile(); + return errc::no_such_file_or_directory; + } + // Traverse directories. + Dir = cast<detail::InMemoryDirectory>(Node); + if (I == E) + return Dir; + } +} + +bool InMemoryFileSystem::addHardLink(const Twine &FromPath, + const Twine &ToPath) { + auto FromNode = lookupInMemoryNode(*this, Root.get(), FromPath); + auto ToNode = lookupInMemoryNode(*this, Root.get(), ToPath); + // FromPath must not have been added before. ToPath must have been added + // before. Resolved ToPath must be a File. + if (!ToNode || FromNode || !isa<detail::InMemoryFile>(*ToNode)) + return false; + return this->addFile(FromPath, 0, nullptr, None, None, None, None, + cast<detail::InMemoryFile>(*ToNode)); +} + +llvm::ErrorOr<Status> InMemoryFileSystem::status(const Twine &Path) { + auto Node = lookupInMemoryNode(*this, Root.get(), Path); + if (Node) + return detail::getNodeStatus(*Node, Path.str()); + return Node.getError(); +} + +llvm::ErrorOr<std::unique_ptr<File>> +InMemoryFileSystem::openFileForRead(const Twine &Path) { + auto Node = lookupInMemoryNode(*this, Root.get(), Path); + if (!Node) + return Node.getError(); + + // When we have a file provide a heap-allocated wrapper for the memory buffer + // to match the ownership semantics for File. + if (auto *F = dyn_cast<detail::InMemoryFile>(*Node)) + return std::unique_ptr<File>( + new detail::InMemoryFileAdaptor(*F, Path.str())); + + // FIXME: errc::not_a_file? + return make_error_code(llvm::errc::invalid_argument); +} + +namespace { + +/// Adaptor from InMemoryDir::iterator to directory_iterator. +class InMemoryDirIterator : public llvm::vfs::detail::DirIterImpl { + detail::InMemoryDirectory::const_iterator I; + detail::InMemoryDirectory::const_iterator E; + std::string RequestedDirName; + + void setCurrentEntry() { + if (I != E) { + SmallString<256> Path(RequestedDirName); + llvm::sys::path::append(Path, I->second->getFileName()); + sys::fs::file_type Type; + switch (I->second->getKind()) { + case detail::IME_File: + case detail::IME_HardLink: + Type = sys::fs::file_type::regular_file; + break; + case detail::IME_Directory: + Type = sys::fs::file_type::directory_file; + break; + } + CurrentEntry = directory_entry(Path.str(), Type); + } else { + // When we're at the end, make CurrentEntry invalid and DirIterImpl will + // do the rest. + CurrentEntry = directory_entry(); + } + } + +public: + InMemoryDirIterator() = default; + + explicit InMemoryDirIterator(const detail::InMemoryDirectory &Dir, + std::string RequestedDirName) + : I(Dir.begin()), E(Dir.end()), + RequestedDirName(std::move(RequestedDirName)) { + setCurrentEntry(); + } + + std::error_code increment() override { + ++I; + setCurrentEntry(); + return {}; + } +}; + +} // namespace + +directory_iterator InMemoryFileSystem::dir_begin(const Twine &Dir, + std::error_code &EC) { + auto Node = lookupInMemoryNode(*this, Root.get(), Dir); + if (!Node) { + EC = Node.getError(); + return directory_iterator(std::make_shared<InMemoryDirIterator>()); + } + + if (auto *DirNode = dyn_cast<detail::InMemoryDirectory>(*Node)) + return directory_iterator( + std::make_shared<InMemoryDirIterator>(*DirNode, Dir.str())); + + EC = make_error_code(llvm::errc::not_a_directory); + return directory_iterator(std::make_shared<InMemoryDirIterator>()); +} + +std::error_code InMemoryFileSystem::setCurrentWorkingDirectory(const Twine &P) { + SmallString<128> Path; + P.toVector(Path); + + // Fix up relative paths. This just prepends the current working directory. + std::error_code EC = makeAbsolute(Path); + assert(!EC); + (void)EC; + + if (useNormalizedPaths()) + llvm::sys::path::remove_dots(Path, /*remove_dot_dot=*/true); + + if (!Path.empty()) + WorkingDirectory = Path.str(); + return {}; +} + +std::error_code +InMemoryFileSystem::getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const { + auto CWD = getCurrentWorkingDirectory(); + if (!CWD || CWD->empty()) + return errc::operation_not_permitted; + Path.toVector(Output); + if (auto EC = makeAbsolute(Output)) + return EC; + llvm::sys::path::remove_dots(Output, /*remove_dot_dot=*/true); + return {}; +} + +std::error_code InMemoryFileSystem::isLocal(const Twine &Path, bool &Result) { + Result = false; + return {}; +} + +} // namespace vfs +} // namespace llvm + +//===-----------------------------------------------------------------------===/ +// RedirectingFileSystem implementation +//===-----------------------------------------------------------------------===/ + +// FIXME: reuse implementation common with OverlayFSDirIterImpl as these +// iterators are conceptually similar. +class llvm::vfs::VFSFromYamlDirIterImpl + : public llvm::vfs::detail::DirIterImpl { + std::string Dir; + RedirectingFileSystem::RedirectingDirectoryEntry::iterator Current, End; + + // To handle 'fallthrough' mode we need to iterate at first through + // RedirectingDirectoryEntry and then through ExternalFS. These operations are + // done sequentially, we just need to keep a track of what kind of iteration + // we are currently performing. + + /// Flag telling if we should iterate through ExternalFS or stop at the last + /// RedirectingDirectoryEntry::iterator. + bool IterateExternalFS; + /// Flag telling if we have switched to iterating through ExternalFS. + bool IsExternalFSCurrent = false; + FileSystem &ExternalFS; + directory_iterator ExternalDirIter; + llvm::StringSet<> SeenNames; + + /// To combine multiple iterations, different methods are responsible for + /// different iteration steps. + /// @{ + + /// Responsible for dispatching between RedirectingDirectoryEntry iteration + /// and ExternalFS iteration. + std::error_code incrementImpl(bool IsFirstTime); + /// Responsible for RedirectingDirectoryEntry iteration. + std::error_code incrementContent(bool IsFirstTime); + /// Responsible for ExternalFS iteration. + std::error_code incrementExternal(); + /// @} + +public: + VFSFromYamlDirIterImpl( + const Twine &Path, + RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin, + RedirectingFileSystem::RedirectingDirectoryEntry::iterator End, + bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC); + + std::error_code increment() override; +}; + +llvm::ErrorOr<std::string> +RedirectingFileSystem::getCurrentWorkingDirectory() const { + return ExternalFS->getCurrentWorkingDirectory(); +} + +std::error_code +RedirectingFileSystem::setCurrentWorkingDirectory(const Twine &Path) { + return ExternalFS->setCurrentWorkingDirectory(Path); +} + +std::error_code RedirectingFileSystem::isLocal(const Twine &Path, + bool &Result) { + return ExternalFS->isLocal(Path, Result); +} + +directory_iterator RedirectingFileSystem::dir_begin(const Twine &Dir, + std::error_code &EC) { + ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Dir); + if (!E) { + EC = E.getError(); + if (IsFallthrough && EC == errc::no_such_file_or_directory) + return ExternalFS->dir_begin(Dir, EC); + return {}; + } + ErrorOr<Status> S = status(Dir, *E); + if (!S) { + EC = S.getError(); + return {}; + } + if (!S->isDirectory()) { + EC = std::error_code(static_cast<int>(errc::not_a_directory), + std::system_category()); + return {}; + } + + auto *D = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(*E); + return directory_iterator(std::make_shared<VFSFromYamlDirIterImpl>( + Dir, D->contents_begin(), D->contents_end(), + /*IterateExternalFS=*/IsFallthrough, *ExternalFS, EC)); +} + +void RedirectingFileSystem::setExternalContentsPrefixDir(StringRef PrefixDir) { + ExternalContentsPrefixDir = PrefixDir.str(); +} + +StringRef RedirectingFileSystem::getExternalContentsPrefixDir() const { + return ExternalContentsPrefixDir; +} + +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +LLVM_DUMP_METHOD void RedirectingFileSystem::dump() const { + for (const auto &Root : Roots) + dumpEntry(Root.get()); +} + +LLVM_DUMP_METHOD void +RedirectingFileSystem::dumpEntry(RedirectingFileSystem::Entry *E, + int NumSpaces) const { + StringRef Name = E->getName(); + for (int i = 0, e = NumSpaces; i < e; ++i) + dbgs() << " "; + dbgs() << "'" << Name.str().c_str() << "'" + << "\n"; + + if (E->getKind() == RedirectingFileSystem::EK_Directory) { + auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E); + assert(DE && "Should be a directory"); + + for (std::unique_ptr<Entry> &SubEntry : + llvm::make_range(DE->contents_begin(), DE->contents_end())) + dumpEntry(SubEntry.get(), NumSpaces + 2); + } +} +#endif + +/// A helper class to hold the common YAML parsing state. +class llvm::vfs::RedirectingFileSystemParser { + yaml::Stream &Stream; + + void error(yaml::Node *N, const Twine &Msg) { Stream.printError(N, Msg); } + + // false on error + bool parseScalarString(yaml::Node *N, StringRef &Result, + SmallVectorImpl<char> &Storage) { + const auto *S = dyn_cast<yaml::ScalarNode>(N); + + if (!S) { + error(N, "expected string"); + return false; + } + Result = S->getValue(Storage); + return true; + } + + // false on error + bool parseScalarBool(yaml::Node *N, bool &Result) { + SmallString<5> Storage; + StringRef Value; + if (!parseScalarString(N, Value, Storage)) + return false; + + if (Value.equals_lower("true") || Value.equals_lower("on") || + Value.equals_lower("yes") || Value == "1") { + Result = true; + return true; + } else if (Value.equals_lower("false") || Value.equals_lower("off") || + Value.equals_lower("no") || Value == "0") { + Result = false; + return true; + } + + error(N, "expected boolean value"); + return false; + } + + struct KeyStatus { + bool Required; + bool Seen = false; + + KeyStatus(bool Required = false) : Required(Required) {} + }; + + using KeyStatusPair = std::pair<StringRef, KeyStatus>; + + // false on error + bool checkDuplicateOrUnknownKey(yaml::Node *KeyNode, StringRef Key, + DenseMap<StringRef, KeyStatus> &Keys) { + if (!Keys.count(Key)) { + error(KeyNode, "unknown key"); + return false; + } + KeyStatus &S = Keys[Key]; + if (S.Seen) { + error(KeyNode, Twine("duplicate key '") + Key + "'"); + return false; + } + S.Seen = true; + return true; + } + + // false on error + bool checkMissingKeys(yaml::Node *Obj, DenseMap<StringRef, KeyStatus> &Keys) { + for (const auto &I : Keys) { + if (I.second.Required && !I.second.Seen) { + error(Obj, Twine("missing key '") + I.first + "'"); + return false; + } + } + return true; + } + + RedirectingFileSystem::Entry * + lookupOrCreateEntry(RedirectingFileSystem *FS, StringRef Name, + RedirectingFileSystem::Entry *ParentEntry = nullptr) { + if (!ParentEntry) { // Look for a existent root + for (const auto &Root : FS->Roots) { + if (Name.equals(Root->getName())) { + ParentEntry = Root.get(); + return ParentEntry; + } + } + } else { // Advance to the next component + auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>( + ParentEntry); + for (std::unique_ptr<RedirectingFileSystem::Entry> &Content : + llvm::make_range(DE->contents_begin(), DE->contents_end())) { + auto *DirContent = + dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>( + Content.get()); + if (DirContent && Name.equals(Content->getName())) + return DirContent; + } + } + + // ... or create a new one + std::unique_ptr<RedirectingFileSystem::Entry> E = + llvm::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>( + Name, Status("", getNextVirtualUniqueID(), + std::chrono::system_clock::now(), 0, 0, 0, + file_type::directory_file, sys::fs::all_all)); + + if (!ParentEntry) { // Add a new root to the overlay + FS->Roots.push_back(std::move(E)); + ParentEntry = FS->Roots.back().get(); + return ParentEntry; + } + + auto *DE = + dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(ParentEntry); + DE->addContent(std::move(E)); + return DE->getLastContent(); + } + + void uniqueOverlayTree(RedirectingFileSystem *FS, + RedirectingFileSystem::Entry *SrcE, + RedirectingFileSystem::Entry *NewParentE = nullptr) { + StringRef Name = SrcE->getName(); + switch (SrcE->getKind()) { + case RedirectingFileSystem::EK_Directory: { + auto *DE = + dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(SrcE); + assert(DE && "Must be a directory"); + // Empty directories could be present in the YAML as a way to + // describe a file for a current directory after some of its subdir + // is parsed. This only leads to redundant walks, ignore it. + if (!Name.empty()) + NewParentE = lookupOrCreateEntry(FS, Name, NewParentE); + for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry : + llvm::make_range(DE->contents_begin(), DE->contents_end())) + uniqueOverlayTree(FS, SubEntry.get(), NewParentE); + break; + } + case RedirectingFileSystem::EK_File: { + auto *FE = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(SrcE); + assert(FE && "Must be a file"); + assert(NewParentE && "Parent entry must exist"); + auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>( + NewParentE); + DE->addContent( + llvm::make_unique<RedirectingFileSystem::RedirectingFileEntry>( + Name, FE->getExternalContentsPath(), FE->getUseName())); + break; + } + } + } + + std::unique_ptr<RedirectingFileSystem::Entry> + parseEntry(yaml::Node *N, RedirectingFileSystem *FS, bool IsRootEntry) { + auto *M = dyn_cast<yaml::MappingNode>(N); + if (!M) { + error(N, "expected mapping node for file or directory entry"); + return nullptr; + } + + KeyStatusPair Fields[] = { + KeyStatusPair("name", true), + KeyStatusPair("type", true), + KeyStatusPair("contents", false), + KeyStatusPair("external-contents", false), + KeyStatusPair("use-external-name", false), + }; + + DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); + + bool HasContents = false; // external or otherwise + std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> + EntryArrayContents; + std::string ExternalContentsPath; + std::string Name; + yaml::Node *NameValueNode; + auto UseExternalName = + RedirectingFileSystem::RedirectingFileEntry::NK_NotSet; + RedirectingFileSystem::EntryKind Kind; + + for (auto &I : *M) { + StringRef Key; + // Reuse the buffer for key and value, since we don't look at key after + // parsing value. + SmallString<256> Buffer; + if (!parseScalarString(I.getKey(), Key, Buffer)) + return nullptr; + + if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) + return nullptr; + + StringRef Value; + if (Key == "name") { + if (!parseScalarString(I.getValue(), Value, Buffer)) + 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; + } + } else if (Key == "type") { + if (!parseScalarString(I.getValue(), Value, Buffer)) + return nullptr; + if (Value == "file") + Kind = RedirectingFileSystem::EK_File; + else if (Value == "directory") + Kind = RedirectingFileSystem::EK_Directory; + else { + error(I.getValue(), "unknown value for 'type'"); + return nullptr; + } + } else if (Key == "contents") { + if (HasContents) { + error(I.getKey(), + "entry already has 'contents' or 'external-contents'"); + return nullptr; + } + HasContents = true; + auto *Contents = dyn_cast<yaml::SequenceNode>(I.getValue()); + if (!Contents) { + // FIXME: this is only for directories, what about files? + error(I.getValue(), "expected array"); + return nullptr; + } + + for (auto &I : *Contents) { + if (std::unique_ptr<RedirectingFileSystem::Entry> E = + parseEntry(&I, FS, /*IsRootEntry*/ false)) + EntryArrayContents.push_back(std::move(E)); + else + return nullptr; + } + } else if (Key == "external-contents") { + if (HasContents) { + error(I.getKey(), + "entry already has 'contents' or 'external-contents'"); + return nullptr; + } + HasContents = true; + if (!parseScalarString(I.getValue(), Value, Buffer)) + return nullptr; + + SmallString<256> FullPath; + if (FS->IsRelativeOverlay) { + FullPath = FS->getExternalContentsPrefixDir(); + assert(!FullPath.empty() && + "External contents prefix directory must exist"); + llvm::sys::path::append(FullPath, Value); + } else { + 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); + } + ExternalContentsPath = FullPath.str(); + } else if (Key == "use-external-name") { + bool Val; + if (!parseScalarBool(I.getValue(), Val)) + return nullptr; + UseExternalName = + Val ? RedirectingFileSystem::RedirectingFileEntry::NK_External + : RedirectingFileSystem::RedirectingFileEntry::NK_Virtual; + } else { + llvm_unreachable("key missing from Keys"); + } + } + + if (Stream.failed()) + return nullptr; + + // check for missing keys + if (!HasContents) { + error(N, "missing key 'contents' or 'external-contents'"); + return nullptr; + } + if (!checkMissingKeys(N, Keys)) + return nullptr; + + // check invalid configuration + if (Kind == RedirectingFileSystem::EK_Directory && + UseExternalName != + RedirectingFileSystem::RedirectingFileEntry::NK_NotSet) { + error(N, "'use-external-name' is not supported for directories"); + return nullptr; + } + + if (IsRootEntry && !sys::path::is_absolute(Name)) { + assert(NameValueNode && "Name presence should be checked earlier"); + error(NameValueNode, + "entry with relative path at the root level is not discoverable"); + return nullptr; + } + + // Remove trailing slash(es), being careful not to remove the root path + StringRef Trimmed(Name); + size_t RootPathLen = sys::path::root_path(Trimmed).size(); + while (Trimmed.size() > RootPathLen && + sys::path::is_separator(Trimmed.back())) + Trimmed = Trimmed.slice(0, Trimmed.size() - 1); + // Get the last component + StringRef LastComponent = sys::path::filename(Trimmed); + + std::unique_ptr<RedirectingFileSystem::Entry> Result; + switch (Kind) { + case RedirectingFileSystem::EK_File: + Result = llvm::make_unique<RedirectingFileSystem::RedirectingFileEntry>( + LastComponent, std::move(ExternalContentsPath), UseExternalName); + break; + case RedirectingFileSystem::EK_Directory: + Result = + llvm::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>( + LastComponent, std::move(EntryArrayContents), + Status("", getNextVirtualUniqueID(), + std::chrono::system_clock::now(), 0, 0, 0, + file_type::directory_file, sys::fs::all_all)); + break; + } + + StringRef Parent = sys::path::parent_path(Trimmed); + if (Parent.empty()) + return Result; + + // if 'name' contains multiple components, create implicit directory entries + for (sys::path::reverse_iterator I = sys::path::rbegin(Parent), + E = sys::path::rend(Parent); + I != E; ++I) { + std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> Entries; + Entries.push_back(std::move(Result)); + Result = + llvm::make_unique<RedirectingFileSystem::RedirectingDirectoryEntry>( + *I, std::move(Entries), + Status("", getNextVirtualUniqueID(), + std::chrono::system_clock::now(), 0, 0, 0, + file_type::directory_file, sys::fs::all_all)); + } + return Result; + } + +public: + RedirectingFileSystemParser(yaml::Stream &S) : Stream(S) {} + + // false on error + bool parse(yaml::Node *Root, RedirectingFileSystem *FS) { + auto *Top = dyn_cast<yaml::MappingNode>(Root); + if (!Top) { + error(Root, "expected mapping node"); + return false; + } + + KeyStatusPair Fields[] = { + KeyStatusPair("version", true), + KeyStatusPair("case-sensitive", false), + KeyStatusPair("use-external-names", false), + KeyStatusPair("overlay-relative", false), + KeyStatusPair("fallthrough", false), + KeyStatusPair("roots", true), + }; + + DenseMap<StringRef, KeyStatus> Keys(std::begin(Fields), std::end(Fields)); + std::vector<std::unique_ptr<RedirectingFileSystem::Entry>> RootEntries; + + // Parse configuration and 'roots' + for (auto &I : *Top) { + SmallString<10> KeyBuffer; + StringRef Key; + if (!parseScalarString(I.getKey(), Key, KeyBuffer)) + return false; + + if (!checkDuplicateOrUnknownKey(I.getKey(), Key, Keys)) + return false; + + if (Key == "roots") { + auto *Roots = dyn_cast<yaml::SequenceNode>(I.getValue()); + if (!Roots) { + error(I.getValue(), "expected array"); + return false; + } + + for (auto &I : *Roots) { + if (std::unique_ptr<RedirectingFileSystem::Entry> E = + parseEntry(&I, FS, /*IsRootEntry*/ true)) + RootEntries.push_back(std::move(E)); + else + return false; + } + } else if (Key == "version") { + StringRef VersionString; + SmallString<4> Storage; + if (!parseScalarString(I.getValue(), VersionString, Storage)) + return false; + int Version; + if (VersionString.getAsInteger<int>(10, Version)) { + error(I.getValue(), "expected integer"); + return false; + } + if (Version < 0) { + error(I.getValue(), "invalid version number"); + return false; + } + if (Version != 0) { + error(I.getValue(), "version mismatch, expected 0"); + return false; + } + } else if (Key == "case-sensitive") { + if (!parseScalarBool(I.getValue(), FS->CaseSensitive)) + return false; + } else if (Key == "overlay-relative") { + if (!parseScalarBool(I.getValue(), FS->IsRelativeOverlay)) + return false; + } else if (Key == "use-external-names") { + if (!parseScalarBool(I.getValue(), FS->UseExternalNames)) + return false; + } else if (Key == "fallthrough") { + if (!parseScalarBool(I.getValue(), FS->IsFallthrough)) + return false; + } else { + llvm_unreachable("key missing from Keys"); + } + } + + if (Stream.failed()) + return false; + + if (!checkMissingKeys(Top, Keys)) + return false; + + // Now that we sucessefully parsed the YAML file, canonicalize the internal + // representation to a proper directory tree so that we can search faster + // inside the VFS. + for (auto &E : RootEntries) + uniqueOverlayTree(FS, E.get()); + + return true; + } +}; + +RedirectingFileSystem * +RedirectingFileSystem::create(std::unique_ptr<MemoryBuffer> Buffer, + SourceMgr::DiagHandlerTy DiagHandler, + StringRef YAMLFilePath, void *DiagContext, + IntrusiveRefCntPtr<FileSystem> ExternalFS) { + SourceMgr SM; + yaml::Stream Stream(Buffer->getMemBufferRef(), SM); + + SM.setDiagHandler(DiagHandler, DiagContext); + yaml::document_iterator DI = Stream.begin(); + yaml::Node *Root = DI->getRoot(); + if (DI == Stream.end() || !Root) { + SM.PrintMessage(SMLoc(), SourceMgr::DK_Error, "expected root node"); + return nullptr; + } + + RedirectingFileSystemParser P(Stream); + + std::unique_ptr<RedirectingFileSystem> FS( + new RedirectingFileSystem(std::move(ExternalFS))); + + if (!YAMLFilePath.empty()) { + // Use the YAML path from -ivfsoverlay to compute the dir to be prefixed + // to each 'external-contents' path. + // + // Example: + // -ivfsoverlay dummy.cache/vfs/vfs.yaml + // yields: + // FS->ExternalContentsPrefixDir => /<absolute_path_to>/dummy.cache/vfs + // + SmallString<256> OverlayAbsDir = sys::path::parent_path(YAMLFilePath); + std::error_code EC = llvm::sys::fs::make_absolute(OverlayAbsDir); + assert(!EC && "Overlay dir final path must be absolute"); + (void)EC; + FS->setExternalContentsPrefixDir(OverlayAbsDir); + } + + if (!P.parse(Root, FS.get())) + return nullptr; + + return FS.release(); +} + +ErrorOr<RedirectingFileSystem::Entry *> +RedirectingFileSystem::lookupPath(const Twine &Path_) const { + SmallString<256> Path; + Path_.toVector(Path); + + // Handle relative paths + 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 + // 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); + } + + if (Path.empty()) + return make_error_code(llvm::errc::invalid_argument); + + sys::path::const_iterator Start = sys::path::begin(Path); + sys::path::const_iterator End = sys::path::end(Path); + for (const auto &Root : Roots) { + ErrorOr<RedirectingFileSystem::Entry *> Result = + lookupPath(Start, End, Root.get()); + if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) + return Result; + } + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +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(); + + // Forward the search to the next component in case this is an empty one. + if (!FromName.empty()) { + if (CaseSensitive ? !Start->equals(FromName) + : !Start->equals_lower(FromName)) + // failure to match + return make_error_code(llvm::errc::no_such_file_or_directory); + + ++Start; + + if (Start == End) { + // Match! + return From; + } + } + + auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(From); + if (!DE) + return make_error_code(llvm::errc::not_a_directory); + + for (const std::unique_ptr<RedirectingFileSystem::Entry> &DirEntry : + llvm::make_range(DE->contents_begin(), DE->contents_end())) { + ErrorOr<RedirectingFileSystem::Entry *> Result = + lookupPath(Start, End, DirEntry.get()); + if (Result || Result.getError() != llvm::errc::no_such_file_or_directory) + return Result; + } + return make_error_code(llvm::errc::no_such_file_or_directory); +} + +static Status getRedirectedFileStatus(const Twine &Path, bool UseExternalNames, + Status ExternalStatus) { + Status S = ExternalStatus; + if (!UseExternalNames) + S = Status::copyWithNewName(S, Path.str()); + S.IsVFSMapped = true; + return S; +} + +ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path, + RedirectingFileSystem::Entry *E) { + assert(E != nullptr); + if (auto *F = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(E)) { + ErrorOr<Status> S = ExternalFS->status(F->getExternalContentsPath()); + assert(!S || S->getName() == F->getExternalContentsPath()); + if (S) + return getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), + *S); + return S; + } else { // directory + auto *DE = cast<RedirectingFileSystem::RedirectingDirectoryEntry>(E); + return Status::copyWithNewName(DE->getStatus(), Path.str()); + } +} + +ErrorOr<Status> RedirectingFileSystem::status(const Twine &Path) { + ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path); + if (!Result) { + if (IsFallthrough && + Result.getError() == llvm::errc::no_such_file_or_directory) { + return ExternalFS->status(Path); + } + return Result.getError(); + } + return status(Path, *Result); +} + +namespace { + +/// Provide a file wrapper with an overriden status. +class FileWithFixedStatus : public File { + std::unique_ptr<File> InnerFile; + Status S; + +public: + FileWithFixedStatus(std::unique_ptr<File> InnerFile, Status S) + : InnerFile(std::move(InnerFile)), S(std::move(S)) {} + + ErrorOr<Status> status() override { return S; } + ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> + + getBuffer(const Twine &Name, int64_t FileSize, bool RequiresNullTerminator, + bool IsVolatile) override { + return InnerFile->getBuffer(Name, FileSize, RequiresNullTerminator, + IsVolatile); + } + + std::error_code close() override { return InnerFile->close(); } +}; + +} // namespace + +ErrorOr<std::unique_ptr<File>> +RedirectingFileSystem::openFileForRead(const Twine &Path) { + ErrorOr<RedirectingFileSystem::Entry *> E = lookupPath(Path); + if (!E) { + if (IsFallthrough && + E.getError() == llvm::errc::no_such_file_or_directory) { + return ExternalFS->openFileForRead(Path); + } + return E.getError(); + } + + auto *F = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(*E); + if (!F) // FIXME: errc::not_a_file? + return make_error_code(llvm::errc::invalid_argument); + + auto Result = ExternalFS->openFileForRead(F->getExternalContentsPath()); + if (!Result) + return Result; + + auto ExternalStatus = (*Result)->status(); + if (!ExternalStatus) + return ExternalStatus.getError(); + + // FIXME: Update the status with the name and VFSMapped. + Status S = getRedirectedFileStatus(Path, F->useExternalName(UseExternalNames), + *ExternalStatus); + return std::unique_ptr<File>( + llvm::make_unique<FileWithFixedStatus>(std::move(*Result), S)); +} + +std::error_code +RedirectingFileSystem::getRealPath(const Twine &Path, + SmallVectorImpl<char> &Output) const { + ErrorOr<RedirectingFileSystem::Entry *> Result = lookupPath(Path); + if (!Result) { + if (IsFallthrough && + Result.getError() == llvm::errc::no_such_file_or_directory) { + return ExternalFS->getRealPath(Path, Output); + } + return Result.getError(); + } + + if (auto *F = + dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(*Result)) { + return ExternalFS->getRealPath(F->getExternalContentsPath(), Output); + } + // Even if there is a directory entry, fall back to ExternalFS if allowed, + // because directories don't have a single external contents path. + return IsFallthrough ? ExternalFS->getRealPath(Path, Output) + : llvm::errc::invalid_argument; +} + +IntrusiveRefCntPtr<FileSystem> +vfs::getVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, + SourceMgr::DiagHandlerTy DiagHandler, + StringRef YAMLFilePath, void *DiagContext, + IntrusiveRefCntPtr<FileSystem> ExternalFS) { + return RedirectingFileSystem::create(std::move(Buffer), DiagHandler, + YAMLFilePath, DiagContext, + std::move(ExternalFS)); +} + +static void getVFSEntries(RedirectingFileSystem::Entry *SrcE, + SmallVectorImpl<StringRef> &Path, + SmallVectorImpl<YAMLVFSEntry> &Entries) { + auto Kind = SrcE->getKind(); + if (Kind == RedirectingFileSystem::EK_Directory) { + auto *DE = dyn_cast<RedirectingFileSystem::RedirectingDirectoryEntry>(SrcE); + assert(DE && "Must be a directory"); + for (std::unique_ptr<RedirectingFileSystem::Entry> &SubEntry : + llvm::make_range(DE->contents_begin(), DE->contents_end())) { + Path.push_back(SubEntry->getName()); + getVFSEntries(SubEntry.get(), Path, Entries); + Path.pop_back(); + } + return; + } + + assert(Kind == RedirectingFileSystem::EK_File && "Must be a EK_File"); + auto *FE = dyn_cast<RedirectingFileSystem::RedirectingFileEntry>(SrcE); + assert(FE && "Must be a file"); + SmallString<128> VPath; + for (auto &Comp : Path) + llvm::sys::path::append(VPath, Comp); + Entries.push_back(YAMLVFSEntry(VPath.c_str(), FE->getExternalContentsPath())); +} + +void vfs::collectVFSFromYAML(std::unique_ptr<MemoryBuffer> Buffer, + SourceMgr::DiagHandlerTy DiagHandler, + StringRef YAMLFilePath, + SmallVectorImpl<YAMLVFSEntry> &CollectedEntries, + void *DiagContext, + IntrusiveRefCntPtr<FileSystem> ExternalFS) { + RedirectingFileSystem *VFS = RedirectingFileSystem::create( + std::move(Buffer), DiagHandler, YAMLFilePath, DiagContext, + std::move(ExternalFS)); + ErrorOr<RedirectingFileSystem::Entry *> RootE = VFS->lookupPath("/"); + if (!RootE) + return; + SmallVector<StringRef, 8> Components; + Components.push_back("/"); + getVFSEntries(*RootE, Components, CollectedEntries); +} + +UniqueID vfs::getNextVirtualUniqueID() { + static std::atomic<unsigned> UID; + unsigned ID = ++UID; + // The following assumes that uint64_t max will never collide with a real + // dev_t value from the OS. + return UniqueID(std::numeric_limits<uint64_t>::max(), ID); +} + +void YAMLVFSWriter::addFileMapping(StringRef VirtualPath, StringRef RealPath) { + 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); +} + +namespace { + +class JSONWriter { + llvm::raw_ostream &OS; + SmallVector<StringRef, 16> DirStack; + + unsigned getDirIndent() { return 4 * DirStack.size(); } + unsigned getFileIndent() { return 4 * (DirStack.size() + 1); } + bool containedIn(StringRef Parent, StringRef Path); + StringRef containedPart(StringRef Parent, StringRef Path); + void startDirectory(StringRef Path); + void endDirectory(); + void writeEntry(StringRef VPath, StringRef RPath); + +public: + JSONWriter(llvm::raw_ostream &OS) : OS(OS) {} + + void write(ArrayRef<YAMLVFSEntry> Entries, Optional<bool> UseExternalNames, + Optional<bool> IsCaseSensitive, Optional<bool> IsOverlayRelative, + StringRef OverlayDir); +}; + +} // namespace + +bool JSONWriter::containedIn(StringRef Parent, StringRef Path) { + using namespace llvm::sys; + + // Compare each path component. + auto IParent = path::begin(Parent), EParent = path::end(Parent); + for (auto IChild = path::begin(Path), EChild = path::end(Path); + IParent != EParent && IChild != EChild; ++IParent, ++IChild) { + if (*IParent != *IChild) + return false; + } + // Have we exhausted the parent path? + return IParent == EParent; +} + +StringRef JSONWriter::containedPart(StringRef Parent, StringRef Path) { + assert(!Parent.empty()); + assert(containedIn(Parent, Path)); + return Path.slice(Parent.size() + 1, StringRef::npos); +} + +void JSONWriter::startDirectory(StringRef Path) { + StringRef Name = + DirStack.empty() ? Path : containedPart(DirStack.back(), Path); + DirStack.push_back(Path); + unsigned Indent = getDirIndent(); + OS.indent(Indent) << "{\n"; + OS.indent(Indent + 2) << "'type': 'directory',\n"; + OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(Name) << "\",\n"; + OS.indent(Indent + 2) << "'contents': [\n"; +} + +void JSONWriter::endDirectory() { + unsigned Indent = getDirIndent(); + OS.indent(Indent + 2) << "]\n"; + OS.indent(Indent) << "}"; + + DirStack.pop_back(); +} + +void JSONWriter::writeEntry(StringRef VPath, StringRef RPath) { + unsigned Indent = getFileIndent(); + OS.indent(Indent) << "{\n"; + OS.indent(Indent + 2) << "'type': 'file',\n"; + OS.indent(Indent + 2) << "'name': \"" << llvm::yaml::escape(VPath) << "\",\n"; + OS.indent(Indent + 2) << "'external-contents': \"" + << llvm::yaml::escape(RPath) << "\"\n"; + OS.indent(Indent) << "}"; +} + +void JSONWriter::write(ArrayRef<YAMLVFSEntry> Entries, + Optional<bool> UseExternalNames, + Optional<bool> IsCaseSensitive, + Optional<bool> IsOverlayRelative, + StringRef OverlayDir) { + using namespace llvm::sys; + + OS << "{\n" + " 'version': 0,\n"; + if (IsCaseSensitive.hasValue()) + OS << " 'case-sensitive': '" + << (IsCaseSensitive.getValue() ? "true" : "false") << "',\n"; + if (UseExternalNames.hasValue()) + OS << " 'use-external-names': '" + << (UseExternalNames.getValue() ? "true" : "false") << "',\n"; + bool UseOverlayRelative = false; + if (IsOverlayRelative.hasValue()) { + UseOverlayRelative = IsOverlayRelative.getValue(); + OS << " 'overlay-relative': '" << (UseOverlayRelative ? "true" : "false") + << "',\n"; + } + OS << " 'roots': [\n"; + + if (!Entries.empty()) { + const YAMLVFSEntry &Entry = Entries.front(); + startDirectory(path::parent_path(Entry.VPath)); + + StringRef RPath = Entry.RPath; + if (UseOverlayRelative) { + unsigned OverlayDirLen = OverlayDir.size(); + assert(RPath.substr(0, OverlayDirLen) == OverlayDir && + "Overlay dir must be contained in RPath"); + RPath = RPath.slice(OverlayDirLen, RPath.size()); + } + + writeEntry(path::filename(Entry.VPath), RPath); + + for (const auto &Entry : Entries.slice(1)) { + StringRef Dir = path::parent_path(Entry.VPath); + if (Dir == DirStack.back()) + OS << ",\n"; + else { + while (!DirStack.empty() && !containedIn(DirStack.back(), Dir)) { + OS << "\n"; + endDirectory(); + } + OS << ",\n"; + startDirectory(Dir); + } + StringRef RPath = Entry.RPath; + if (UseOverlayRelative) { + unsigned OverlayDirLen = OverlayDir.size(); + assert(RPath.substr(0, OverlayDirLen) == OverlayDir && + "Overlay dir must be contained in RPath"); + RPath = RPath.slice(OverlayDirLen, RPath.size()); + } + writeEntry(path::filename(Entry.VPath), RPath); + } + + while (!DirStack.empty()) { + OS << "\n"; + endDirectory(); + } + OS << "\n"; + } + + OS << " ]\n" + << "}\n"; +} + +void YAMLVFSWriter::write(llvm::raw_ostream &OS) { + llvm::sort(Mappings, [](const YAMLVFSEntry &LHS, const YAMLVFSEntry &RHS) { + return LHS.VPath < RHS.VPath; + }); + + JSONWriter(OS).write(Mappings, UseExternalNames, IsCaseSensitive, + IsOverlayRelative, OverlayDir); +} + +VFSFromYamlDirIterImpl::VFSFromYamlDirIterImpl( + const Twine &_Path, + RedirectingFileSystem::RedirectingDirectoryEntry::iterator Begin, + RedirectingFileSystem::RedirectingDirectoryEntry::iterator End, + bool IterateExternalFS, FileSystem &ExternalFS, std::error_code &EC) + : Dir(_Path.str()), Current(Begin), End(End), + IterateExternalFS(IterateExternalFS), ExternalFS(ExternalFS) { + EC = incrementImpl(/*IsFirstTime=*/true); +} + +std::error_code VFSFromYamlDirIterImpl::increment() { + return incrementImpl(/*IsFirstTime=*/false); +} + +std::error_code VFSFromYamlDirIterImpl::incrementExternal() { + assert(!(IsExternalFSCurrent && ExternalDirIter == directory_iterator()) && + "incrementing past end"); + std::error_code EC; + if (IsExternalFSCurrent) { + ExternalDirIter.increment(EC); + } else if (IterateExternalFS) { + ExternalDirIter = ExternalFS.dir_begin(Dir, EC); + IsExternalFSCurrent = true; + if (EC && EC != errc::no_such_file_or_directory) + return EC; + EC = {}; + } + if (EC || ExternalDirIter == directory_iterator()) { + CurrentEntry = directory_entry(); + } else { + CurrentEntry = *ExternalDirIter; + } + return EC; +} + +std::error_code VFSFromYamlDirIterImpl::incrementContent(bool IsFirstTime) { + assert((IsFirstTime || Current != End) && "cannot iterate past end"); + if (!IsFirstTime) + ++Current; + while (Current != End) { + SmallString<128> PathStr(Dir); + llvm::sys::path::append(PathStr, (*Current)->getName()); + sys::fs::file_type Type; + switch ((*Current)->getKind()) { + case RedirectingFileSystem::EK_Directory: + Type = sys::fs::file_type::directory_file; + break; + case RedirectingFileSystem::EK_File: + Type = sys::fs::file_type::regular_file; + break; + } + CurrentEntry = directory_entry(PathStr.str(), Type); + return {}; + } + return incrementExternal(); +} + +std::error_code VFSFromYamlDirIterImpl::incrementImpl(bool IsFirstTime) { + while (true) { + std::error_code EC = IsExternalFSCurrent ? incrementExternal() + : incrementContent(IsFirstTime); + if (EC || CurrentEntry.path().empty()) + return EC; + StringRef Name = llvm::sys::path::filename(CurrentEntry.path()); + if (SeenNames.insert(Name).second) + return EC; // name not seen before + } + llvm_unreachable("returned above"); +} + +vfs::recursive_directory_iterator::recursive_directory_iterator( + FileSystem &FS_, const Twine &Path, std::error_code &EC) + : FS(&FS_) { + directory_iterator I = FS->dir_begin(Path, EC); + if (I != directory_iterator()) { + State = std::make_shared<detail::RecDirIterState>(); + State->Stack.push(I); + } +} + +vfs::recursive_directory_iterator & +recursive_directory_iterator::increment(std::error_code &EC) { + assert(FS && State && !State->Stack.empty() && "incrementing past end"); + assert(!State->Stack.top()->path().empty() && "non-canonical end iterator"); + vfs::directory_iterator End; + + if (State->HasNoPushRequest) + State->HasNoPushRequest = false; + else { + if (State->Stack.top()->type() == sys::fs::file_type::directory_file) { + vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC); + if (I != End) { + State->Stack.push(I); + return *this; + } + } + } + + while (!State->Stack.empty() && State->Stack.top().increment(EC) == End) + State->Stack.pop(); + + if (State->Stack.empty()) + State.reset(); // end iterator + + return *this; +} diff --git a/lib/Support/Windows/Path.inc b/lib/Support/Windows/Path.inc index b64b013d7407..d34aa763124c 100644 --- a/lib/Support/Windows/Path.inc +++ b/lib/Support/Windows/Path.inc @@ -416,7 +416,7 @@ static std::error_code rename_internal(HANDLE FromHandle, const Twine &To, *reinterpret_cast<FILE_RENAME_INFO *>(RenameInfoBuf.data()); RenameInfo.ReplaceIfExists = ReplaceIfExists; RenameInfo.RootDirectory = 0; - RenameInfo.FileNameLength = ToWide.size(); + RenameInfo.FileNameLength = ToWide.size() * sizeof(wchar_t); std::copy(ToWide.begin(), ToWide.end(), &RenameInfo.FileName[0]); SetLastError(ERROR_SUCCESS); @@ -450,7 +450,7 @@ static std::error_code rename_handle(HANDLE FromHandle, const Twine &To) { if (std::error_code EC2 = realPathFromHandle(FromHandle, WideFrom)) return EC2; if (::MoveFileExW(WideFrom.begin(), WideTo.begin(), - MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED)) + MOVEFILE_REPLACE_EXISTING)) return std::error_code(); return mapWindowsError(GetLastError()); } @@ -766,10 +766,12 @@ std::error_code setPermissions(const Twine &Path, perms Permissions) { return std::error_code(); } -std::error_code setLastModificationAndAccessTime(int FD, TimePoint<> Time) { - FILETIME FT = toFILETIME(Time); +std::error_code setLastAccessAndModificationTime(int FD, TimePoint<> AccessTime, + TimePoint<> ModificationTime) { + FILETIME AccessFT = toFILETIME(AccessTime); + FILETIME ModifyFT = toFILETIME(ModificationTime); HANDLE FileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(FD)); - if (!SetFileTime(FileHandle, NULL, &FT, &FT)) + if (!SetFileTime(FileHandle, NULL, &AccessFT, &ModifyFT)) return mapWindowsError(::GetLastError()); return std::error_code(); } @@ -852,16 +854,37 @@ mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, Mapping = 0; } +static bool hasFlushBufferKernelBug() { + static bool Ret{GetWindowsOSVersion() < llvm::VersionTuple(10, 0, 0, 17763)}; + return Ret; +} + +static bool isEXE(StringRef Magic) { + static const char PEMagic[] = {'P', 'E', '\0', '\0'}; + if (Magic.startswith(StringRef("MZ")) && Magic.size() >= 0x3c + 4) { + uint32_t off = read32le(Magic.data() + 0x3c); + // PE/COFF file, either EXE or DLL. + if (Magic.substr(off).startswith(StringRef(PEMagic, sizeof(PEMagic)))) + return true; + } + return false; +} + mapped_file_region::~mapped_file_region() { if (Mapping) { + + bool Exe = isEXE(StringRef((char *)Mapping, Size)); + ::UnmapViewOfFile(Mapping); - if (Mode == mapmode::readwrite) { + if (Mode == mapmode::readwrite && Exe && hasFlushBufferKernelBug()) { // There is a Windows kernel bug, the exact trigger conditions of which // are not well understood. When triggered, dirty pages are not properly // flushed and subsequent process's attempts to read a file can return // invalid data. Calling FlushFileBuffers on the write handle is // sufficient to ensure that this bug is not triggered. + // The bug only occurs when writing an executable and executing it right + // after, under high I/O pressure. ::FlushFileBuffers(FileHandle); } @@ -900,28 +923,28 @@ static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) { FindData->nFileSizeHigh, FindData->nFileSizeLow); } -std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path, - bool follow_symlinks) { - SmallVector<wchar_t, 128> path_utf16; +std::error_code detail::directory_iterator_construct(detail::DirIterState &IT, + StringRef Path, + bool FollowSymlinks) { + SmallVector<wchar_t, 128> PathUTF16; - if (std::error_code ec = widenPath(path, path_utf16)) - return ec; + if (std::error_code EC = widenPath(Path, PathUTF16)) + return EC; // Convert path to the format that Windows is happy with. - if (path_utf16.size() > 0 && - !is_separator(path_utf16[path.size() - 1]) && - path_utf16[path.size() - 1] != L':') { - path_utf16.push_back(L'\\'); - path_utf16.push_back(L'*'); + if (PathUTF16.size() > 0 && + !is_separator(PathUTF16[Path.size() - 1]) && + PathUTF16[Path.size() - 1] != L':') { + PathUTF16.push_back(L'\\'); + PathUTF16.push_back(L'*'); } else { - path_utf16.push_back(L'*'); + PathUTF16.push_back(L'*'); } // Get the first directory entry. WIN32_FIND_DATAW FirstFind; ScopedFindHandle FindHandle(::FindFirstFileExW( - c_str(path_utf16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch, + c_str(PathUTF16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH)); if (!FindHandle) return mapWindowsError(::GetLastError()); @@ -934,43 +957,45 @@ std::error_code detail::directory_iterator_construct(detail::DirIterState &it, DWORD LastError = ::GetLastError(); // Check for end. if (LastError == ERROR_NO_MORE_FILES) - return detail::directory_iterator_destruct(it); + return detail::directory_iterator_destruct(IT); return mapWindowsError(LastError); } else FilenameLen = ::wcslen(FirstFind.cFileName); // Construct the current directory entry. - SmallString<128> directory_entry_name_utf8; - if (std::error_code ec = + SmallString<128> DirectoryEntryNameUTF8; + if (std::error_code EC = UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName), - directory_entry_name_utf8)) - return ec; + DirectoryEntryNameUTF8)) + return EC; - it.IterationHandle = intptr_t(FindHandle.take()); - SmallString<128> directory_entry_path(path); - path::append(directory_entry_path, directory_entry_name_utf8); - it.CurrentEntry = directory_entry(directory_entry_path, follow_symlinks, - status_from_find_data(&FirstFind)); + IT.IterationHandle = intptr_t(FindHandle.take()); + SmallString<128> DirectoryEntryPath(Path); + path::append(DirectoryEntryPath, DirectoryEntryNameUTF8); + IT.CurrentEntry = + directory_entry(DirectoryEntryPath, FollowSymlinks, + file_type_from_attrs(FirstFind.dwFileAttributes), + status_from_find_data(&FirstFind)); return std::error_code(); } -std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) { - if (it.IterationHandle != 0) +std::error_code detail::directory_iterator_destruct(detail::DirIterState &IT) { + if (IT.IterationHandle != 0) // Closes the handle if it's valid. - ScopedFindHandle close(HANDLE(it.IterationHandle)); - it.IterationHandle = 0; - it.CurrentEntry = directory_entry(); + ScopedFindHandle close(HANDLE(IT.IterationHandle)); + IT.IterationHandle = 0; + IT.CurrentEntry = directory_entry(); return std::error_code(); } -std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { +std::error_code detail::directory_iterator_increment(detail::DirIterState &IT) { WIN32_FIND_DATAW FindData; - if (!::FindNextFileW(HANDLE(it.IterationHandle), &FindData)) { + if (!::FindNextFileW(HANDLE(IT.IterationHandle), &FindData)) { DWORD LastError = ::GetLastError(); // Check for end. if (LastError == ERROR_NO_MORE_FILES) - return detail::directory_iterator_destruct(it); + return detail::directory_iterator_destruct(IT); return mapWindowsError(LastError); } @@ -978,16 +1003,18 @@ std::error_code detail::directory_iterator_increment(detail::DirIterState &it) { if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') || (FilenameLen == 2 && FindData.cFileName[0] == L'.' && FindData.cFileName[1] == L'.')) - return directory_iterator_increment(it); + return directory_iterator_increment(IT); - SmallString<128> directory_entry_path_utf8; - if (std::error_code ec = + SmallString<128> DirectoryEntryPathUTF8; + if (std::error_code EC = UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName), - directory_entry_path_utf8)) - return ec; + DirectoryEntryPathUTF8)) + return EC; - it.CurrentEntry.replace_filename(Twine(directory_entry_path_utf8), - status_from_find_data(&FindData)); + IT.CurrentEntry.replace_filename( + Twine(DirectoryEntryPathUTF8), + file_type_from_attrs(FindData.dwFileAttributes), + status_from_find_data(&FindData)); return std::error_code(); } @@ -1226,6 +1253,17 @@ static void expandTildeExpr(SmallVectorImpl<char> &Path) { Path.insert(Path.begin() + 1, HomeDir.begin() + 1, HomeDir.end()); } +void expand_tilde(const Twine &path, SmallVectorImpl<char> &dest) { + dest.clear(); + if (path.isTriviallyEmpty()) + return; + + path.toVector(dest); + expandTildeExpr(dest); + + return; +} + std::error_code real_path(const Twine &path, SmallVectorImpl<char> &dest, bool expand_tilde) { dest.clear(); @@ -1264,10 +1302,6 @@ static bool getKnownFolderPath(KNOWNFOLDERID folderId, return ok; } -bool getUserCacheDir(SmallVectorImpl<char> &Result) { - return getKnownFolderPath(FOLDERID_LocalAppData, Result); -} - bool home_directory(SmallVectorImpl<char> &result) { return getKnownFolderPath(FOLDERID_Profile, result); } diff --git a/lib/Support/Windows/Process.inc b/lib/Support/Windows/Process.inc index 30126568769c..2b2d79231434 100644 --- a/lib/Support/Windows/Process.inc +++ b/lib/Support/Windows/Process.inc @@ -12,8 +12,10 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/Allocator.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/WindowsError.h" #include <malloc.h> @@ -140,73 +142,59 @@ Optional<std::string> Process::GetEnv(StringRef Name) { return std::string(Res.data()); } -static const char *AllocateString(const SmallVectorImpl<char> &S, - BumpPtrAllocator &Alloc) { - char *Buf = reinterpret_cast<char *>(Alloc.Allocate(S.size() + 1, 1)); - ::memcpy(Buf, S.data(), S.size()); - Buf[S.size()] = '\0'; - return Buf; -} - -/// Convert Arg from UTF-16 to UTF-8 and push it onto Args. -static std::error_code ConvertAndPushArg(const wchar_t *Arg, - SmallVectorImpl<const char *> &Args, - BumpPtrAllocator &Alloc) { - SmallVector<char, MAX_PATH> ArgString; - if (std::error_code ec = windows::UTF16ToUTF8(Arg, wcslen(Arg), ArgString)) - return ec; - Args.push_back(AllocateString(ArgString, Alloc)); - return std::error_code(); -} - -/// Perform wildcard expansion of Arg, or just push it into Args if it -/// doesn't have wildcards or doesn't match any files. -static std::error_code WildcardExpand(const wchar_t *Arg, +/// Perform wildcard expansion of Arg, or just push it into Args if it doesn't +/// have wildcards or doesn't match any files. +static std::error_code WildcardExpand(StringRef Arg, SmallVectorImpl<const char *> &Args, - BumpPtrAllocator &Alloc) { - if (!wcspbrk(Arg, L"*?")) { - // Arg does not contain any wildcard characters. This is the common case. - return ConvertAndPushArg(Arg, Args, Alloc); - } + StringSaver &Saver) { + std::error_code EC; - if (wcscmp(Arg, L"/?") == 0 || wcscmp(Arg, L"-?") == 0) { - // Don't wildcard expand /?. Always treat it as an option. - return ConvertAndPushArg(Arg, Args, Alloc); + // Don't expand Arg if it does not contain any wildcard characters. This is + // the common case. Also don't wildcard expand /?. Always treat it as an + // option. + if (Arg.find_first_of("*?") == StringRef::npos || Arg == "/?" || + Arg == "-?") { + Args.push_back(Arg.data()); + return EC; } - // Extract any directory part of the argument. - SmallVector<char, MAX_PATH> Dir; - if (std::error_code ec = windows::UTF16ToUTF8(Arg, wcslen(Arg), Dir)) - return ec; - sys::path::remove_filename(Dir); - const int DirSize = Dir.size(); + // Convert back to UTF-16 so we can call FindFirstFileW. + SmallVector<wchar_t, MAX_PATH> ArgW; + EC = windows::UTF8ToUTF16(Arg, ArgW); + if (EC) + return EC; // Search for matching files. // FIXME: This assumes the wildcard is only in the file name and not in the // directory portion of the file path. For example, it doesn't handle // "*\foo.c" nor "s?c\bar.cpp". WIN32_FIND_DATAW FileData; - HANDLE FindHandle = FindFirstFileW(Arg, &FileData); + HANDLE FindHandle = FindFirstFileW(ArgW.data(), &FileData); if (FindHandle == INVALID_HANDLE_VALUE) { - return ConvertAndPushArg(Arg, Args, Alloc); + Args.push_back(Arg.data()); + return EC; } - std::error_code ec; + // Extract any directory part of the argument. + SmallString<MAX_PATH> Dir = Arg; + sys::path::remove_filename(Dir); + const int DirSize = Dir.size(); + do { - SmallVector<char, MAX_PATH> FileName; - ec = windows::UTF16ToUTF8(FileData.cFileName, wcslen(FileData.cFileName), + SmallString<MAX_PATH> FileName; + EC = windows::UTF16ToUTF8(FileData.cFileName, wcslen(FileData.cFileName), FileName); - if (ec) + if (EC) break; // Append FileName to Dir, and remove it afterwards. - llvm::sys::path::append(Dir, StringRef(FileName.data(), FileName.size())); - Args.push_back(AllocateString(Dir, Alloc)); + llvm::sys::path::append(Dir, FileName); + Args.push_back(Saver.save(StringRef(Dir)).data()); Dir.resize(DirSize); } while (FindNextFileW(FindHandle, &FileData)); FindClose(FindHandle); - return ec; + return EC; } static std::error_code GetExecutableName(SmallVectorImpl<char> &Filename) { @@ -243,18 +231,20 @@ static std::error_code GetExecutableName(SmallVectorImpl<char> &Filename) { std::error_code windows::GetCommandLineArguments(SmallVectorImpl<const char *> &Args, BumpPtrAllocator &Alloc) { - int ArgCount; - std::unique_ptr<wchar_t *[], decltype(&LocalFree)> UnicodeCommandLine{ - CommandLineToArgvW(GetCommandLineW(), &ArgCount), &LocalFree}; - if (!UnicodeCommandLine) - return mapWindowsError(::GetLastError()); - + const wchar_t *CmdW = GetCommandLineW(); + assert(CmdW); std::error_code EC; + SmallString<MAX_PATH> Cmd; + EC = windows::UTF16ToUTF8(CmdW, wcslen(CmdW), Cmd); + if (EC) + return EC; - Args.reserve(ArgCount); + SmallVector<const char *, 20> TmpArgs; + StringSaver Saver(Alloc); + cl::TokenizeWindowsCommandLine(Cmd, Saver, TmpArgs, /*MarkEOLs=*/false); - for (int I = 0; I < ArgCount; ++I) { - EC = WildcardExpand(UnicodeCommandLine[I], Args, Alloc); + for (const char *Arg : TmpArgs) { + EC = WildcardExpand(Arg, Args, Saver); if (EC) return EC; } @@ -266,7 +256,7 @@ windows::GetCommandLineArguments(SmallVectorImpl<const char *> &Args, if (EC) return EC; sys::path::append(Arg0, Filename); - Args[0] = AllocateString(Arg0, Alloc); + Args[0] = Saver.save(Arg0).data(); return std::error_code(); } @@ -328,6 +318,15 @@ bool Process::StandardErrHasColors() { static bool UseANSI = false; void Process::UseANSIEscapeCodes(bool enable) { +#if defined(ENABLE_VIRTUAL_TERMINAL_PROCESSING) + if (enable) { + HANDLE Console = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD Mode; + GetConsoleMode(Console, &Mode); + Mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(Console, Mode); + } +#endif UseANSI = enable; } @@ -461,3 +460,27 @@ unsigned Process::GetRandomNumber() { ReportLastErrorFatal("Could not generate a random number"); return Ret; } + +typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); +#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) + +llvm::VersionTuple llvm::GetWindowsOSVersion() { + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + auto getVer = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (getVer) { + RTL_OSVERSIONINFOEXW info{}; + info.dwOSVersionInfoSize = sizeof(info); + if (getVer((PRTL_OSVERSIONINFOW)&info) == STATUS_SUCCESS) { + return llvm::VersionTuple(info.dwMajorVersion, info.dwMinorVersion, 0, + info.dwBuildNumber); + } + } + } + return llvm::VersionTuple(0, 0, 0, 0); +} + +bool llvm::RunningWindows8OrGreater() { + // Windows 8 is version 6.2, service pack 0. + return GetWindowsOSVersion() >= llvm::VersionTuple(6, 2, 0, 0); +} diff --git a/lib/Support/Windows/Program.inc b/lib/Support/Windows/Program.inc index cb68c5b10e52..c037956603f2 100644 --- a/lib/Support/Windows/Program.inc +++ b/lib/Support/Windows/Program.inc @@ -105,6 +105,25 @@ ErrorOr<std::string> sys::findProgramByName(StringRef Name, return std::string(U8Result.begin(), U8Result.end()); } +bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) { + if (!ErrMsg) + return true; + char *buffer = NULL; + DWORD LastError = GetLastError(); + DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, LastError, 0, (LPSTR)&buffer, 1, NULL); + if (R) + *ErrMsg = prefix + ": " + buffer; + else + *ErrMsg = prefix + ": Unknown error"; + *ErrMsg += " (0x" + llvm::utohexstr(LastError) + ")"; + + LocalFree(buffer); + return R != 0; +} + static HANDLE RedirectIO(Optional<StringRef> Path, int fd, std::string *ErrMsg) { HANDLE h; @@ -317,7 +336,7 @@ static bool Execute(ProcessInfo &PI, StringRef Program, static bool argNeedsQuotes(StringRef Arg) { if (Arg.empty()) return true; - return StringRef::npos != Arg.find_first_of("\t \"&\'()*<>\\`^|"); + return StringRef::npos != Arg.find_first_of("\t \"&\'()*<>\\`^|\n"); } static std::string quoteSingleArg(StringRef Arg) { diff --git a/lib/Support/Windows/Threading.inc b/lib/Support/Windows/Threading.inc index decb48887af2..0bd92f66c6b8 100644 --- a/lib/Support/Windows/Threading.inc +++ b/lib/Support/Windows/Threading.inc @@ -14,7 +14,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/Twine.h" -#include "Windows/WindowsSupport.h" +#include "WindowsSupport.h" #include <process.h> // Windows will at times define MemoryFence. diff --git a/lib/Support/Windows/WindowsSupport.h b/lib/Support/Windows/WindowsSupport.h index c2fd6bb982d4..979cc5d01390 100644 --- a/lib/Support/Windows/WindowsSupport.h +++ b/lib/Support/Windows/WindowsSupport.h @@ -41,6 +41,7 @@ #include "llvm/Config/config.h" // Get build system configuration settings #include "llvm/Support/Chrono.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/VersionTuple.h" #include <cassert> #include <string> #include <system_error> @@ -49,54 +50,29 @@ // 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. -inline bool RunningWindows8OrGreater() { - // Windows 8 is version 6.2, service pack 0. - OSVERSIONINFOEXW osvi = {}; - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - osvi.dwMajorVersion = 6; - osvi.dwMinorVersion = 2; - osvi.wServicePackMajor = 0; - - DWORDLONG Mask = 0; - Mask = VerSetConditionMask(Mask, VER_MAJORVERSION, VER_GREATER_EQUAL); - Mask = VerSetConditionMask(Mask, VER_MINORVERSION, VER_GREATER_EQUAL); - Mask = VerSetConditionMask(Mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); - - return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | - VER_SERVICEPACKMAJOR, - Mask) != FALSE; -} +bool RunningWindows8OrGreater(); -inline bool MakeErrMsg(std::string *ErrMsg, const std::string &prefix) { - if (!ErrMsg) - return true; - char *buffer = NULL; - DWORD LastError = GetLastError(); - DWORD R = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, LastError, 0, (LPSTR)&buffer, 1, NULL); - if (R) - *ErrMsg = prefix + ": " + buffer; - else - *ErrMsg = prefix + ": Unknown error"; - *ErrMsg += " (0x" + llvm::utohexstr(LastError) + ")"; - - LocalFree(buffer); - return R != 0; -} +/// 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); 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; + ScopedHandle(const ScopedHandle &other) = delete; + void operator=(const ScopedHandle &other) = delete; public: ScopedHandle() : Handle(HandleTraits::GetInvalid()) {} @@ -201,7 +177,6 @@ typedef ScopedHandle<RegTraits> ScopedRegHandle; typedef ScopedHandle<FindHandleTraits> ScopedFindHandle; typedef ScopedHandle<JobHandleTraits> ScopedJobHandle; -namespace llvm { template <class T> class SmallVectorImpl; diff --git a/lib/Support/WithColor.cpp b/lib/Support/WithColor.cpp index d2e13f0e86de..cf4c10956f21 100644 --- a/lib/Support/WithColor.cpp +++ b/lib/Support/WithColor.cpp @@ -19,15 +19,10 @@ static cl::opt<cl::boolOrDefault> cl::desc("Use colors in output (default=autodetect)"), cl::init(cl::BOU_UNSET)); -bool WithColor::colorsEnabled(raw_ostream &OS) { - if (UseColor == cl::BOU_UNSET) - return OS.has_colors(); - return UseColor == cl::BOU_TRUE; -} - -WithColor::WithColor(raw_ostream &OS, HighlightColor Color) : OS(OS) { +WithColor::WithColor(raw_ostream &OS, HighlightColor Color, bool DisableColors) + : OS(OS), DisableColors(DisableColors) { // Detect color from terminal type unless the user passed the --color option. - if (colorsEnabled(OS)) { + if (colorsEnabled()) { switch (Color) { case HighlightColor::Address: OS.changeColor(raw_ostream::YELLOW); @@ -56,6 +51,9 @@ WithColor::WithColor(raw_ostream &OS, HighlightColor Color) : OS(OS) { case HighlightColor::Note: OS.changeColor(raw_ostream::BLACK, true); break; + case HighlightColor::Remark: + OS.changeColor(raw_ostream::BLUE, true); + break; } } } @@ -66,25 +64,58 @@ raw_ostream &WithColor::warning() { return warning(errs()); } raw_ostream &WithColor::note() { return note(errs()); } -raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::remark() { return remark(errs()); } + +raw_ostream &WithColor::error(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Error).get() << "error: "; + return WithColor(OS, HighlightColor::Error, DisableColors).get() + << "error: "; } -raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::warning(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Warning).get() << "warning: "; + return WithColor(OS, HighlightColor::Warning, DisableColors).get() + << "warning: "; } -raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix) { +raw_ostream &WithColor::note(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { if (!Prefix.empty()) OS << Prefix << ": "; - return WithColor(OS, HighlightColor::Note).get() << "note: "; + return WithColor(OS, HighlightColor::Note, DisableColors).get() << "note: "; } -WithColor::~WithColor() { - if (colorsEnabled(OS)) +raw_ostream &WithColor::remark(raw_ostream &OS, StringRef Prefix, + bool DisableColors) { + if (!Prefix.empty()) + OS << Prefix << ": "; + return WithColor(OS, HighlightColor::Remark, DisableColors).get() + << "remark: "; +} + +bool WithColor::colorsEnabled() { + if (DisableColors) + return false; + if (UseColor == cl::BOU_UNSET) + return OS.has_colors(); + return UseColor == cl::BOU_TRUE; +} + +WithColor &WithColor::changeColor(raw_ostream::Colors Color, bool Bold, + bool BG) { + if (colorsEnabled()) + OS.changeColor(Color, Bold, BG); + return *this; +} + +WithColor &WithColor::resetColor() { + if (colorsEnabled()) OS.resetColor(); + return *this; } + +WithColor::~WithColor() { resetColor(); } diff --git a/lib/Support/YAMLTraits.cpp b/lib/Support/YAMLTraits.cpp index d6345efd00cd..b9bbee7883c6 100644 --- a/lib/Support/YAMLTraits.cpp +++ b/lib/Support/YAMLTraits.cpp @@ -98,7 +98,7 @@ bool Input::setCurrentDocument() { ++DocIterator; return setCurrentDocument(); } - TopNode = this->createHNodes(N); + TopNode = createHNodes(N); CurrentNode = TopNode.get(); return true; } @@ -341,9 +341,23 @@ void Input::scalarString(StringRef &S, QuotingType) { void Input::blockScalarString(StringRef &S) { scalarString(S, QuotingType::None); } +void Input::scalarTag(std::string &Tag) { + Tag = CurrentNode->_node->getVerbatimTag(); +} + void Input::setError(HNode *hnode, const Twine &message) { assert(hnode && "HNode must not be NULL"); - this->setError(hnode->_node, message); + setError(hnode->_node, message); +} + +NodeKind Input::getNodeKind() { + if (isa<ScalarHNode>(CurrentNode)) + return NodeKind::Scalar; + else if (isa<MapHNode>(CurrentNode)) + return NodeKind::Map; + else if (isa<SequenceHNode>(CurrentNode)) + return NodeKind::Sequence; + llvm_unreachable("Unsupported node kind"); } void Input::setError(Node *node, const Twine &message) { @@ -366,7 +380,7 @@ std::unique_ptr<Input::HNode> Input::createHNodes(Node *N) { } else if (SequenceNode *SQ = dyn_cast<SequenceNode>(N)) { auto SQHNode = llvm::make_unique<SequenceHNode>(N); for (Node &SN : *SQ) { - auto Entry = this->createHNodes(&SN); + auto Entry = createHNodes(&SN); if (EC) break; SQHNode->Entries.push_back(std::move(Entry)); @@ -391,7 +405,7 @@ std::unique_ptr<Input::HNode> Input::createHNodes(Node *N) { // Copy string to permanent storage KeyStr = StringStorage.str().copy(StringAllocator); } - auto ValueHNode = this->createHNodes(Value); + auto ValueHNode = createHNodes(Value); if (EC) break; mapHNode->Mapping[KeyStr] = std::move(ValueHNode); @@ -406,7 +420,7 @@ std::unique_ptr<Input::HNode> Input::createHNodes(Node *N) { } void Input::setError(const Twine &Message) { - this->setError(CurrentNode, Message); + setError(CurrentNode, Message); } bool Input::canElideEmptySequence() { @@ -436,15 +450,17 @@ bool Output::mapTag(StringRef Tag, bool Use) { // If this tag is being written inside a sequence we should write the start // of the sequence before writing the tag, otherwise the tag won't be // attached to the element in the sequence, but rather the sequence itself. - bool SequenceElement = - StateStack.size() > 1 && (StateStack[StateStack.size() - 2] == inSeq || - StateStack[StateStack.size() - 2] == inFlowSeq); + bool SequenceElement = false; + if (StateStack.size() > 1) { + auto &E = StateStack[StateStack.size() - 2]; + SequenceElement = inSeqAnyElement(E) || inFlowSeqAnyElement(E); + } if (SequenceElement && StateStack.back() == inMapFirstKey) { - this->newLineCheck(); + newLineCheck(); } else { - this->output(" "); + output(" "); } - this->output(Tag); + output(Tag); if (SequenceElement) { // If we're writing the tag during the first element of a map, the tag // takes the place of the first element in the sequence. @@ -461,6 +477,9 @@ bool Output::mapTag(StringRef Tag, bool Use) { } void Output::endMapping() { + // If we did not map anything, we should explicitly emit an empty map + if (StateStack.back() == inMapFirstKey) + output("{}"); StateStack.pop_back(); } @@ -476,8 +495,8 @@ bool Output::preflightKey(const char *Key, bool Required, bool SameAsDefault, if (State == inFlowMapFirstKey || State == inFlowMapOtherKey) { flowKey(Key); } else { - this->newLineCheck(); - this->paddedKey(Key); + newLineCheck(); + paddedKey(Key); } return true; } @@ -496,23 +515,23 @@ void Output::postflightKey(void *) { void Output::beginFlowMapping() { StateStack.push_back(inFlowMapFirstKey); - this->newLineCheck(); + newLineCheck(); ColumnAtMapFlowStart = Column; output("{ "); } void Output::endFlowMapping() { StateStack.pop_back(); - this->outputUpToEndOfLine(" }"); + outputUpToEndOfLine(" }"); } void Output::beginDocuments() { - this->outputUpToEndOfLine("---"); + outputUpToEndOfLine("---"); } bool Output::preflightDocument(unsigned index) { if (index > 0) - this->outputUpToEndOfLine("\n---"); + outputUpToEndOfLine("\n---"); return true; } @@ -524,12 +543,15 @@ void Output::endDocuments() { } unsigned Output::beginSequence() { - StateStack.push_back(inSeq); + StateStack.push_back(inSeqFirstElement); NeedsNewLine = true; return 0; } void Output::endSequence() { + // If we did not emit anything, we should explicitly emit an empty sequence + if (StateStack.back() == inSeqFirstElement) + output("[]"); StateStack.pop_back(); } @@ -538,11 +560,18 @@ bool Output::preflightElement(unsigned, void *&) { } void Output::postflightElement(void *) { + if (StateStack.back() == inSeqFirstElement) { + StateStack.pop_back(); + StateStack.push_back(inSeqOtherElement); + } else if (StateStack.back() == inFlowSeqFirstElement) { + StateStack.pop_back(); + StateStack.push_back(inFlowSeqOtherElement); + } } unsigned Output::beginFlowSequence() { - StateStack.push_back(inFlowSeq); - this->newLineCheck(); + StateStack.push_back(inFlowSeqFirstElement); + newLineCheck(); ColumnAtFlowStart = Column; output("[ "); NeedFlowSequenceComma = false; @@ -551,7 +580,7 @@ unsigned Output::beginFlowSequence() { void Output::endFlowSequence() { StateStack.pop_back(); - this->outputUpToEndOfLine(" ]"); + outputUpToEndOfLine(" ]"); } bool Output::preflightFlowElement(unsigned, void *&) { @@ -577,8 +606,8 @@ void Output::beginEnumScalar() { bool Output::matchEnumScalar(const char *Str, bool Match) { if (Match && !EnumerationMatchFound) { - this->newLineCheck(); - this->outputUpToEndOfLine(Str); + newLineCheck(); + outputUpToEndOfLine(Str); EnumerationMatchFound = true; } return false; @@ -597,7 +626,7 @@ void Output::endEnumScalar() { } bool Output::beginBitSetScalar(bool &DoClear) { - this->newLineCheck(); + newLineCheck(); output("[ "); NeedBitValueComma = false; DoClear = false; @@ -608,27 +637,27 @@ bool Output::bitSetMatch(const char *Str, bool Matches) { if (Matches) { if (NeedBitValueComma) output(", "); - this->output(Str); + output(Str); NeedBitValueComma = true; } return false; } void Output::endBitSetScalar() { - this->outputUpToEndOfLine(" ]"); + outputUpToEndOfLine(" ]"); } void Output::scalarString(StringRef &S, QuotingType MustQuote) { - this->newLineCheck(); + newLineCheck(); if (S.empty()) { // Print '' for the empty string because leaving the field empty is not // allowed. - this->outputUpToEndOfLine("''"); + outputUpToEndOfLine("''"); return; } if (MustQuote == QuotingType::None) { // Only quote if we must. - this->outputUpToEndOfLine(S); + outputUpToEndOfLine(S); return; } @@ -645,7 +674,7 @@ void Output::scalarString(StringRef &S, QuotingType MustQuote) { // escapes. This is handled in yaml::escape. if (MustQuote == QuotingType::Double) { output(yaml::escape(Base, /* EscapePrintable= */ false)); - this->outputUpToEndOfLine(Quote); + outputUpToEndOfLine(Quote); return; } @@ -659,7 +688,7 @@ void Output::scalarString(StringRef &S, QuotingType MustQuote) { ++j; } output(StringRef(&Base[i], j - i)); - this->outputUpToEndOfLine(Quote); // Ending quote. + outputUpToEndOfLine(Quote); // Ending quote. } void Output::blockScalarString(StringRef &S) { @@ -680,6 +709,14 @@ void Output::blockScalarString(StringRef &S) { } } +void Output::scalarTag(std::string &Tag) { + if (Tag.empty()) + return; + newLineCheck(); + output(Tag); + output(" "); +} + void Output::setError(const Twine &message) { } @@ -693,7 +730,7 @@ bool Output::canElideEmptySequence() { return true; if (StateStack.back() != inMapFirstKey) return true; - return (StateStack[StateStack.size()-2] != inSeq); + return !inSeqAnyElement(StateStack[StateStack.size() - 2]); } void Output::output(StringRef s) { @@ -702,10 +739,9 @@ void Output::output(StringRef s) { } void Output::outputUpToEndOfLine(StringRef s) { - this->output(s); - if (StateStack.empty() || (StateStack.back() != inFlowSeq && - StateStack.back() != inFlowMapFirstKey && - StateStack.back() != inFlowMapOtherKey)) + output(s); + if (StateStack.empty() || (!inFlowSeqAnyElement(StateStack.back()) && + !inFlowMapAnyKey(StateStack.back()))) NeedsNewLine = true; } @@ -723,18 +759,22 @@ void Output::newLineCheck() { return; NeedsNewLine = false; - this->outputNewLine(); + outputNewLine(); + + if (StateStack.size() == 0) + return; - assert(StateStack.size() > 0); unsigned Indent = StateStack.size() - 1; bool OutputDash = false; - if (StateStack.back() == inSeq) { + if (StateStack.back() == inSeqFirstElement || + StateStack.back() == inSeqOtherElement) { OutputDash = true; - } else if ((StateStack.size() > 1) && ((StateStack.back() == inMapFirstKey) || - (StateStack.back() == inFlowSeq) || - (StateStack.back() == inFlowMapFirstKey)) && - (StateStack[StateStack.size() - 2] == inSeq)) { + } else if ((StateStack.size() > 1) && + ((StateStack.back() == inMapFirstKey) || + inFlowSeqAnyElement(StateStack.back()) || + (StateStack.back() == inFlowMapFirstKey)) && + inSeqAnyElement(StateStack[StateStack.size() - 2])) { --Indent; OutputDash = true; } @@ -772,6 +812,24 @@ void Output::flowKey(StringRef Key) { output(": "); } +NodeKind Output::getNodeKind() { report_fatal_error("invalid call"); } + +bool Output::inSeqAnyElement(InState State) { + return State == inSeqFirstElement || State == inSeqOtherElement; +} + +bool Output::inFlowSeqAnyElement(InState State) { + return State == inFlowSeqFirstElement || State == inFlowSeqOtherElement; +} + +bool Output::inMapAnyKey(InState State) { + return State == inMapFirstKey || State == inMapOtherKey; +} + +bool Output::inFlowMapAnyKey(InState State) { + return State == inFlowMapFirstKey || State == inFlowMapOtherKey; +} + //===----------------------------------------------------------------------===// // traits for built-in types //===----------------------------------------------------------------------===// diff --git a/lib/Support/raw_ostream.cpp b/lib/Support/raw_ostream.cpp index 038ad00bd608..21dde7ff914a 100644 --- a/lib/Support/raw_ostream.cpp +++ b/lib/Support/raw_ostream.cpp @@ -60,6 +60,7 @@ #endif #ifdef _WIN32 +#include "llvm/Support/ConvertUTF.h" #include "Windows/WindowsSupport.h" #endif @@ -567,6 +568,12 @@ raw_fd_ostream::raw_fd_ostream(int fd, bool shouldClose, bool unbuffered) if (FD <= STDERR_FILENO) ShouldClose = false; +#ifdef _WIN32 + // Check if this is a console device. This is not equivalent to isatty. + IsWindowsConsole = + ::GetFileType((HANDLE)::_get_osfhandle(fd)) == FILE_TYPE_CHAR; +#endif + // Get the starting position. off_t loc = ::lseek(FD, 0, SEEK_CUR); #ifdef _WIN32 @@ -609,25 +616,77 @@ raw_fd_ostream::~raw_fd_ostream() { /*GenCrashDiag=*/false); } +#if defined(_WIN32) +// The most reliable way to print unicode in a Windows console is with +// WriteConsoleW. To use that, first transcode from UTF-8 to UTF-16. This +// assumes that LLVM programs always print valid UTF-8 to the console. The data +// might not be UTF-8 for two major reasons: +// 1. The program is printing binary (-filetype=obj -o -), in which case it +// would have been gibberish anyway. +// 2. The program is printing text in a semi-ascii compatible codepage like +// shift-jis or cp1252. +// +// Most LLVM programs don't produce non-ascii text unless they are quoting +// user source input. A well-behaved LLVM program should either validate that +// the input is UTF-8 or transcode from the local codepage to UTF-8 before +// quoting it. If they don't, this may mess up the encoding, but this is still +// probably the best compromise we can make. +static bool write_console_impl(int FD, StringRef Data) { + SmallVector<wchar_t, 256> WideText; + + // Fall back to ::write if it wasn't valid UTF-8. + if (auto EC = sys::windows::UTF8ToUTF16(Data, WideText)) + return false; + + // On Windows 7 and earlier, WriteConsoleW has a low maximum amount of data + // that can be written to the console at a time. + size_t MaxWriteSize = WideText.size(); + if (!RunningWindows8OrGreater()) + MaxWriteSize = 32767; + + size_t WCharsWritten = 0; + do { + size_t WCharsToWrite = + std::min(MaxWriteSize, WideText.size() - WCharsWritten); + DWORD ActuallyWritten; + bool Success = + ::WriteConsoleW((HANDLE)::_get_osfhandle(FD), &WideText[WCharsWritten], + WCharsToWrite, &ActuallyWritten, + /*Reserved=*/nullptr); + + // The most likely reason for WriteConsoleW to fail is that FD no longer + // points to a console. Fall back to ::write. If this isn't the first loop + // iteration, something is truly wrong. + if (!Success) + return false; + + WCharsWritten += ActuallyWritten; + } while (WCharsWritten != WideText.size()); + return true; +} +#endif + void raw_fd_ostream::write_impl(const char *Ptr, size_t Size) { assert(FD >= 0 && "File already closed."); pos += Size; - // The maximum write size is limited to SSIZE_MAX because a write - // greater than SSIZE_MAX is implementation-defined in POSIX. - // Since SSIZE_MAX is not portable, we use SIZE_MAX >> 1 instead. - size_t MaxWriteSize = SIZE_MAX >> 1; +#if defined(_WIN32) + // If this is a Windows console device, try re-encoding from UTF-8 to UTF-16 + // and using WriteConsoleW. If that fails, fall back to plain write(). + if (IsWindowsConsole) + if (write_console_impl(FD, StringRef(Ptr, Size))) + return; +#endif + + // The maximum write size is limited to INT32_MAX. A write + // greater than SSIZE_MAX is implementation-defined in POSIX, + // and Windows _write requires 32 bit input. + size_t MaxWriteSize = INT32_MAX; #if defined(__linux__) // It is observed that Linux returns EINVAL for a very large write (>2G). // Make it a reasonably small value. MaxWriteSize = 1024 * 1024 * 1024; -#elif defined(_WIN32) - // Writing a large size of output to Windows console returns ENOMEM. It seems - // that, prior to Windows 8, WriteFile() is redirecting to WriteConsole(), and - // the latter has a size limit (66000 bytes or less, depending on heap usage). - if (::_isatty(FD) && !RunningWindows8OrGreater()) - MaxWriteSize = 32767; #endif do { @@ -696,8 +755,17 @@ void raw_fd_ostream::pwrite_impl(const char *Ptr, size_t Size, } size_t raw_fd_ostream::preferred_buffer_size() const { -#if !defined(_MSC_VER) && !defined(__MINGW32__) && !defined(__minix) - // Windows and Minix have no st_blksize. +#if defined(_WIN32) + // Disable buffering for console devices. Console output is re-encoded from + // UTF-8 to UTF-16 on Windows, and buffering it would require us to split the + // buffer on a valid UTF-8 codepoint boundary. Terminal buffering is disabled + // below on most other OSs, so do the same thing on Windows and avoid that + // complexity. + if (IsWindowsConsole) + return 0; + return raw_ostream::preferred_buffer_size(); +#elif !defined(__minix) + // Minix has no st_blksize. assert(FD >= 0 && "File not yet open!"); struct stat statbuf; if (fstat(FD, &statbuf) != 0) @@ -846,3 +914,5 @@ void raw_null_ostream::pwrite_impl(const char *Ptr, size_t Size, uint64_t Offset) {} void raw_pwrite_stream::anchor() {} + +void buffer_ostream::anchor() {} |