blob: 42947673ea52de5de422cc300b72fcd2ad823a90 [file] [log] [blame]
//===---- FineGrainedDependencyFormat.cpp - reading and writing swiftdeps -===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/AST/FileSystem.h"
#include "swift/AST/FineGrainedDependencies.h"
#include "swift/AST/FineGrainedDependencyFormat.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/Basic/Version.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Bitstream/BitstreamWriter.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/MemoryBuffer.h"
using namespace swift;
using namespace fine_grained_dependencies;
namespace {
class Deserializer {
std::vector<std::string> Identifiers;
llvm::BitstreamCursor Cursor;
SmallVector<uint64_t, 64> Scratch;
StringRef BlobData;
// These all return true if there was an error.
bool readSignature();
bool enterTopLevelBlock();
bool readMetadata();
llvm::Optional<std::string> getIdentifier(unsigned n);
public:
Deserializer(llvm::MemoryBufferRef Data) : Cursor(Data) {}
bool readFineGrainedDependencyGraph(SourceFileDepGraph &g, Purpose purpose);
bool readFineGrainedDependencyGraphFromSwiftModule(SourceFileDepGraph &g);
};
} // end namespace
bool Deserializer::readSignature() {
for (unsigned char byte : FINE_GRAINED_DEPDENENCY_FORMAT_SIGNATURE) {
if (Cursor.AtEndOfStream())
return true;
if (auto maybeRead = Cursor.Read(8)) {
if (maybeRead.get() != byte)
return true;
} else {
return true;
}
}
return false;
}
bool Deserializer::enterTopLevelBlock() {
// Read the BLOCKINFO_BLOCK, which contains metadata used when dumping
// the binary data with llvm-bcanalyzer.
{
auto next = Cursor.advance();
if (!next) {
consumeError(next.takeError());
return true;
}
if (next->Kind != llvm::BitstreamEntry::SubBlock)
return true;
if (next->ID != llvm::bitc::BLOCKINFO_BLOCK_ID)
return true;
if (!Cursor.ReadBlockInfoBlock())
return true;
}
// Enters our subblock, which contains the actual dependency information.
{
auto next = Cursor.advance();
if (!next) {
consumeError(next.takeError());
return true;
}
if (next->Kind != llvm::BitstreamEntry::SubBlock)
return true;
if (next->ID != RECORD_BLOCK_ID)
return true;
if (auto err = Cursor.EnterSubBlock(RECORD_BLOCK_ID)) {
consumeError(std::move(err));
return true;
}
}
return false;
}
bool Deserializer::readMetadata() {
using namespace record_block;
auto entry = Cursor.advance();
if (!entry) {
consumeError(entry.takeError());
return true;
}
if (entry->Kind != llvm::BitstreamEntry::Record)
return true;
auto recordID = Cursor.readRecord(entry->ID, Scratch, &BlobData);
if (!recordID) {
consumeError(recordID.takeError());
return true;
}
if (*recordID != METADATA)
return true;
unsigned majorVersion, minorVersion;
MetadataLayout::readRecord(Scratch, majorVersion, minorVersion);
if (majorVersion != FINE_GRAINED_DEPENDENCY_FORMAT_VERSION_MAJOR ||
minorVersion != FINE_GRAINED_DEPENDENCY_FORMAT_VERSION_MINOR) {
return true;
}
return false;
}
static llvm::Optional<NodeKind> getNodeKind(unsigned nodeKind) {
if (nodeKind < unsigned(NodeKind::kindCount))
return NodeKind(nodeKind);
return None;
}
static llvm::Optional<DeclAspect> getDeclAspect(unsigned declAspect) {
if (declAspect < unsigned(DeclAspect::aspectCount))
return DeclAspect(declAspect);
return None;
}
bool Deserializer::readFineGrainedDependencyGraph(SourceFileDepGraph &g,
Purpose purpose) {
using namespace record_block;
switch (purpose) {
case Purpose::ForSwiftDeps:
if (readSignature())
return true;
if (enterTopLevelBlock())
return true;
LLVM_FALLTHROUGH;
case Purpose::ForSwiftModule:
// N.B. Incremental metadata embedded in swiftmodule files does not have
// a leading signature, and its top-level block has already been
// consumed by the time we get here.
break;
}
if (readMetadata())
return true;
SourceFileDepGraphNode *node = nullptr;
size_t sequenceNumber = 0;
while (!Cursor.AtEndOfStream()) {
auto entry = cantFail(Cursor.advance(), "Advance bitstream cursor");
if (entry.Kind == llvm::BitstreamEntry::EndBlock) {
Cursor.ReadBlockEnd();
assert(Cursor.GetCurrentBitNo() % CHAR_BIT == 0);
break;
}
if (entry.Kind != llvm::BitstreamEntry::Record)
llvm::report_fatal_error("Bad bitstream entry kind");
Scratch.clear();
unsigned recordID = cantFail(
Cursor.readRecord(entry.ID, Scratch, &BlobData),
"Read bitstream record");
switch (recordID) {
case METADATA: {
// METADATA must appear at the beginning and is handled by readMetadata().
llvm::report_fatal_error("Unexpected METADATA record");
break;
}
case SOURCE_FILE_DEP_GRAPH_NODE: {
unsigned nodeKindID, declAspectID, contextID, nameID, isProvides;
SourceFileDepGraphNodeLayout::readRecord(Scratch, nodeKindID, declAspectID,
contextID, nameID, isProvides);
node = new SourceFileDepGraphNode();
node->setSequenceNumber(sequenceNumber++);
g.addNode(node);
auto nodeKind = getNodeKind(nodeKindID);
if (!nodeKind)
llvm::report_fatal_error("Bad node kind");
auto declAspect = getDeclAspect(declAspectID);
if (!declAspect)
llvm::report_fatal_error("Bad decl aspect");
auto context = getIdentifier(contextID);
if (!context)
llvm::report_fatal_error("Bad context");
auto name = getIdentifier(nameID);
if (!name)
llvm::report_fatal_error("Bad identifier");
node->setKey(DependencyKey(*nodeKind, *declAspect, *context, *name));
if (isProvides)
node->setIsProvides();
break;
}
case FINGERPRINT_NODE: {
// FINGERPRINT_NODE must follow a SOURCE_FILE_DEP_GRAPH_NODE.
if (node == nullptr)
llvm::report_fatal_error("Unexpected FINGERPRINT_NODE record");
node->setFingerprint(Fingerprint{BlobData.str()});
break;
}
case DEPENDS_ON_DEFINITION_NODE: {
// DEPENDS_ON_DEFINITION_NODE must follow a SOURCE_FILE_DEP_GRAPH_NODE.
if (node == nullptr)
llvm::report_fatal_error("Unexpected DEPENDS_ON_DEFINITION_NODE record");
unsigned dependsOnDefID;
DependsOnDefNodeLayout::readRecord(Scratch, dependsOnDefID);
node->addDefIDependUpon(dependsOnDefID);
break;
}
case IDENTIFIER_NODE: {
// IDENTIFIER_NODE must come before SOURCE_FILE_DEP_GRAPH_NODE.
if (node != nullptr)
llvm::report_fatal_error("Unexpected IDENTIFIER_NODE record");
IdentifierNodeLayout::readRecord(Scratch);
Identifiers.push_back(BlobData.str());
break;
}
default: {
llvm::report_fatal_error("Unknown record ID");
}
}
}
return false;
}
bool swift::fine_grained_dependencies::readFineGrainedDependencyGraph(
llvm::MemoryBuffer &buffer, SourceFileDepGraph &g) {
Deserializer deserializer(buffer.getMemBufferRef());
return deserializer.readFineGrainedDependencyGraph(g, Purpose::ForSwiftDeps);
}
bool swift::fine_grained_dependencies::readFineGrainedDependencyGraph(
StringRef path, SourceFileDepGraph &g) {
auto buffer = llvm::MemoryBuffer::getFile(path);
if (!buffer)
return false;
return readFineGrainedDependencyGraph(*buffer.get(), g);
}
llvm::Optional<std::string> Deserializer::getIdentifier(unsigned n) {
if (n == 0)
return std::string();
--n;
if (n >= Identifiers.size())
return None;
return Identifiers[n];
}
namespace {
class Serializer {
llvm::StringMap<unsigned, llvm::BumpPtrAllocator> IdentifierIDs;
unsigned LastIdentifierID = 0;
std::vector<StringRef> IdentifiersToWrite;
llvm::BitstreamWriter &Out;
/// A reusable buffer for emitting records.
SmallVector<uint64_t, 64> ScratchRecord;
std::array<unsigned, 256> AbbrCodes;
void addIdentifier(StringRef str);
unsigned getIdentifier(StringRef str);
template <typename Layout>
void registerRecordAbbr() {
using AbbrArrayTy = decltype(AbbrCodes);
static_assert(Layout::Code <= std::tuple_size<AbbrArrayTy>::value,
"layout has invalid record code");
AbbrCodes[Layout::Code] = Layout::emitAbbrev(Out);
}
void emitBlockID(unsigned ID, StringRef name,
SmallVectorImpl<unsigned char> &nameBuffer);
void emitRecordID(unsigned ID, StringRef name,
SmallVectorImpl<unsigned char> &nameBuffer);
void writeSignature();
void writeBlockInfoBlock();
void writeMetadata();
public:
Serializer(llvm::BitstreamWriter &ExistingOut) : Out(ExistingOut) {}
public:
void writeFineGrainedDependencyGraph(const SourceFileDepGraph &g,
Purpose purpose);
};
} // end namespace
/// Record the name of a block.
void Serializer::emitBlockID(unsigned ID, StringRef name,
SmallVectorImpl<unsigned char> &nameBuffer) {
SmallVector<unsigned, 1> idBuffer;
idBuffer.push_back(ID);
Out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETBID, idBuffer);
// Emit the block name if present.
if (name.empty())
return;
nameBuffer.resize(name.size());
memcpy(nameBuffer.data(), name.data(), name.size());
Out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_BLOCKNAME, nameBuffer);
}
void Serializer::emitRecordID(unsigned ID, StringRef name,
SmallVectorImpl<unsigned char> &nameBuffer) {
assert(ID < 256 && "can't fit record ID in next to name");
nameBuffer.resize(name.size()+1);
nameBuffer[0] = ID;
memcpy(nameBuffer.data()+1, name.data(), name.size());
Out.EmitRecord(llvm::bitc::BLOCKINFO_CODE_SETRECORDNAME, nameBuffer);
}
void Serializer::writeSignature() {
for (auto c : FINE_GRAINED_DEPDENENCY_FORMAT_SIGNATURE)
Out.Emit((unsigned) c, 8);
}
void Serializer::writeBlockInfoBlock() {
llvm::BCBlockRAII restoreBlock(Out, llvm::bitc::BLOCKINFO_BLOCK_ID, 2);
SmallVector<unsigned char, 64> nameBuffer;
#define BLOCK(X) emitBlockID(X ## _ID, #X, nameBuffer)
#define BLOCK_RECORD(K, X) emitRecordID(K::X, #X, nameBuffer)
BLOCK(RECORD_BLOCK);
BLOCK_RECORD(record_block, METADATA);
BLOCK_RECORD(record_block, SOURCE_FILE_DEP_GRAPH_NODE);
BLOCK_RECORD(record_block, FINGERPRINT_NODE);
BLOCK_RECORD(record_block, DEPENDS_ON_DEFINITION_NODE);
BLOCK_RECORD(record_block, IDENTIFIER_NODE);
}
void Serializer::writeMetadata() {
using namespace record_block;
MetadataLayout::emitRecord(Out, ScratchRecord,
AbbrCodes[MetadataLayout::Code],
FINE_GRAINED_DEPENDENCY_FORMAT_VERSION_MAJOR,
FINE_GRAINED_DEPENDENCY_FORMAT_VERSION_MINOR,
version::getSwiftFullVersion());
}
void
Serializer::writeFineGrainedDependencyGraph(const SourceFileDepGraph &g,
Purpose purpose) {
unsigned blockID = 0;
switch (purpose) {
case Purpose::ForSwiftDeps:
writeSignature();
writeBlockInfoBlock();
blockID = RECORD_BLOCK_ID;
break;
case Purpose::ForSwiftModule:
blockID = INCREMENTAL_INFORMATION_BLOCK_ID;
break;
}
llvm::BCBlockRAII restoreBlock(Out, blockID, 8);
using namespace record_block;
registerRecordAbbr<MetadataLayout>();
registerRecordAbbr<SourceFileDepGraphNodeLayout>();
registerRecordAbbr<FingerprintNodeLayout>();
registerRecordAbbr<DependsOnDefNodeLayout>();
registerRecordAbbr<IdentifierNodeLayout>();
writeMetadata();
// Make a pass to collect all unique strings
g.forEachNode([&](SourceFileDepGraphNode *node) {
addIdentifier(node->getKey().getContext());
addIdentifier(node->getKey().getName());
});
// Write the strings
for (auto str : IdentifiersToWrite) {
IdentifierNodeLayout::emitRecord(Out, ScratchRecord,
AbbrCodes[IdentifierNodeLayout::Code],
str);
}
size_t sequenceNumber = 0;
// Now write each graph node
g.forEachNode([&](SourceFileDepGraphNode *node) {
SourceFileDepGraphNodeLayout::emitRecord(Out, ScratchRecord,
AbbrCodes[SourceFileDepGraphNodeLayout::Code],
unsigned(node->getKey().getKind()),
unsigned(node->getKey().getAspect()),
getIdentifier(node->getKey().getContext()),
getIdentifier(node->getKey().getName()),
node->getIsProvides() ? 1 : 0);
assert(sequenceNumber == node->getSequenceNumber());
sequenceNumber++;
(void) sequenceNumber;
if (auto fingerprint = node->getFingerprint()) {
FingerprintNodeLayout::emitRecord(Out, ScratchRecord,
AbbrCodes[FingerprintNodeLayout::Code],
fingerprint->getRawValue());
}
node->forEachDefIDependUpon([&](size_t defIDependOn) {
DependsOnDefNodeLayout::emitRecord(Out, ScratchRecord,
AbbrCodes[DependsOnDefNodeLayout::Code],
defIDependOn);
});
});
}
void Serializer::addIdentifier(StringRef str) {
if (str.empty())
return;
decltype(IdentifierIDs)::iterator iter;
bool isNew;
std::tie(iter, isNew) =
IdentifierIDs.insert({str, LastIdentifierID + 1});
if (!isNew)
return;
++LastIdentifierID;
// Note that we use the string data stored in the StringMap.
IdentifiersToWrite.push_back(iter->getKey());
}
unsigned Serializer::getIdentifier(StringRef str) {
if (str.empty())
return 0;
auto iter = IdentifierIDs.find(str);
assert(iter != IdentifierIDs.end());
assert(iter->second != 0);
return iter->second;
}
void swift::fine_grained_dependencies::writeFineGrainedDependencyGraph(
llvm::BitstreamWriter &Out, const SourceFileDepGraph &g,
Purpose purpose) {
Serializer serializer{Out};
serializer.writeFineGrainedDependencyGraph(g, purpose);
}
bool swift::fine_grained_dependencies::writeFineGrainedDependencyGraphToPath(
DiagnosticEngine &diags, StringRef path,
const SourceFileDepGraph &g) {
PrettyStackTraceStringAction stackTrace("saving fine-grained dependency graph", path);
return withOutputFile(diags, path, [&](llvm::raw_ostream &out) {
SmallVector<char, 0> Buffer;
llvm::BitstreamWriter Writer{Buffer};
writeFineGrainedDependencyGraph(Writer, g, Purpose::ForSwiftDeps);
out.write(Buffer.data(), Buffer.size());
out.flush();
return false;
});
}
static bool checkModuleSignature(llvm::BitstreamCursor &cursor,
ArrayRef<unsigned char> signature) {
for (unsigned char byte : signature) {
if (cursor.AtEndOfStream())
return false;
if (llvm::Expected<llvm::SimpleBitstreamCursor::word_t> maybeRead =
cursor.Read(8)) {
if (maybeRead.get() != byte)
return false;
} else {
consumeError(maybeRead.takeError());
return false;
}
}
return true;
}
static bool enterTopLevelModuleBlock(llvm::BitstreamCursor &cursor, unsigned ID,
bool shouldReadBlockInfo = true) {
llvm::Expected<llvm::BitstreamEntry> maybeNext = cursor.advance();
if (!maybeNext) {
consumeError(maybeNext.takeError());
return false;
}
llvm::BitstreamEntry next = maybeNext.get();
if (next.Kind != llvm::BitstreamEntry::SubBlock)
return false;
if (next.ID == llvm::bitc::BLOCKINFO_BLOCK_ID) {
if (shouldReadBlockInfo) {
if (!cursor.ReadBlockInfoBlock())
return false;
} else {
if (cursor.SkipBlock())
return false;
}
return enterTopLevelModuleBlock(cursor, ID, false);
}
if (next.ID != ID)
return false;
if (llvm::Error Err = cursor.EnterSubBlock(ID)) {
consumeError(std::move(Err));
return false;
}
return true;
}
bool swift::fine_grained_dependencies::
readFineGrainedDependencyGraphFromSwiftModule(llvm::MemoryBuffer &buffer,
SourceFileDepGraph &g) {
Deserializer deserializer(buffer.getMemBufferRef());
return deserializer.readFineGrainedDependencyGraphFromSwiftModule(g);
}
bool Deserializer::readFineGrainedDependencyGraphFromSwiftModule(
SourceFileDepGraph &g) {
if (!checkModuleSignature(Cursor, {0xE2, 0x9C, 0xA8, 0x0E}) ||
!enterTopLevelModuleBlock(Cursor, llvm::bitc::FIRST_APPLICATION_BLOCKID, false)) {
return true;
}
llvm::BitstreamEntry topLevelEntry;
bool DidNotReadFineGrainedDependencies = true;
while (!Cursor.AtEndOfStream()) {
llvm::Expected<llvm::BitstreamEntry> maybeEntry =
Cursor.advance(llvm::BitstreamCursor::AF_DontPopBlockAtEnd);
if (!maybeEntry) {
consumeError(maybeEntry.takeError());
return true;
}
topLevelEntry = maybeEntry.get();
if (topLevelEntry.Kind != llvm::BitstreamEntry::SubBlock)
break;
switch (topLevelEntry.ID) {
case INCREMENTAL_INFORMATION_BLOCK_ID: {
if (llvm::Error Err =
Cursor.EnterSubBlock(INCREMENTAL_INFORMATION_BLOCK_ID)) {
consumeError(std::move(Err));
return true;
}
if (readFineGrainedDependencyGraph(g, Purpose::ForSwiftModule)) {
break;
}
DidNotReadFineGrainedDependencies = false;
break;
}
default:
// Unknown top-level block, possibly for use by a future version of the
// module format.
if (Cursor.SkipBlock()) {
return true;
}
break;
}
}
return DidNotReadFineGrainedDependencies;
}