summaryrefslogtreecommitdiff
path: root/lib/Support
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Support')
-rw-r--r--lib/Support/AArch64TargetParser.cpp206
-rw-r--r--lib/Support/APInt.cpp336
-rw-r--r--lib/Support/ARMTargetParser.cpp577
-rw-r--r--lib/Support/BinaryStreamError.cpp2
-rw-r--r--lib/Support/BuryPointer.cpp31
-rw-r--r--lib/Support/CMakeLists.txt15
-rw-r--r--lib/Support/COM.cpp2
-rw-r--r--lib/Support/CachePruning.cpp47
-rw-r--r--lib/Support/CodeGenCoverage.cpp4
-rw-r--r--lib/Support/CommandLine.cpp50
-rw-r--r--lib/Support/Compression.cpp28
-rw-r--r--lib/Support/DebugCounter.cpp39
-rw-r--r--lib/Support/Error.cpp47
-rw-r--r--lib/Support/FileCheck.cpp1446
-rw-r--r--lib/Support/FileOutputBuffer.cpp6
-rw-r--r--lib/Support/FoldingSet.cpp2
-rw-r--r--lib/Support/FormatVariadic.cpp2
-rw-r--r--lib/Support/Hashing.cpp4
-rw-r--r--lib/Support/Host.cpp167
-rw-r--r--lib/Support/ItaniumManglingCanonicalizer.cpp322
-rw-r--r--lib/Support/JSON.cpp2
-rw-r--r--lib/Support/Locale.cpp13
-rw-r--r--lib/Support/LockFileManager.cpp10
-rw-r--r--lib/Support/Path.cpp149
-rw-r--r--lib/Support/Process.cpp10
-rw-r--r--lib/Support/RandomNumberGenerator.cpp2
-rw-r--r--lib/Support/Signals.cpp14
-rw-r--r--lib/Support/SourceMgr.cpp124
-rw-r--r--lib/Support/StringSaver.cpp3
-rw-r--r--lib/Support/SymbolRemappingReader.cpp81
-rw-r--r--lib/Support/TargetParser.cpp1022
-rw-r--r--lib/Support/TargetRegistry.cpp2
-rw-r--r--lib/Support/Timer.cpp17
-rw-r--r--lib/Support/Triple.cpp50
-rw-r--r--lib/Support/Unix/Path.inc193
-rw-r--r--lib/Support/Unix/Process.inc5
-rw-r--r--lib/Support/Unix/Signals.inc5
-rw-r--r--lib/Support/VirtualFileSystem.cpp2070
-rw-r--r--lib/Support/Windows/Path.inc130
-rw-r--r--lib/Support/Windows/Process.inc133
-rw-r--r--lib/Support/Windows/Program.inc21
-rw-r--r--lib/Support/Windows/Threading.inc2
-rw-r--r--lib/Support/Windows/WindowsSupport.h51
-rw-r--r--lib/Support/WithColor.cpp63
-rw-r--r--lib/Support/YAMLTraits.cpp144
-rw-r--r--lib/Support/raw_ostream.cpp94
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 &current_directory,
- SmallVectorImpl<char> &path,
- bool use_current_directory) {
+void make_absolute(const Twine &current_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 &current_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 &current_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 &current_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 &current_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 &current_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() {}