blob: 72f7119846131f3bfa02b5d9fec449ba49f7e0ea [file] [log] [blame]
//===--- SILDebugInfoGenerator.cpp - Writes a SIL file for debugging ------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
#define DEBUG_TYPE "gsil-gen"
#include "swift/AST/SILOptions.h"
#include "swift/SIL/SILPrintContext.h"
#include "swift/SIL/SILModule.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
using namespace swift;
namespace {
/// A pass for generating debug info on SIL level.
///
/// This pass is only enabled if SILOptions::SILOutputFileNameForDebugging is
/// set (i.e. if the -gsil command line option is specified).
/// The pass writes all SIL functions into one or multiple output files,
/// depending on the size of the SIL. The names of the output files are derived
/// from the main output file.
///
/// output file name = <main-output-filename>.gsil_<n>.sil
///
/// Where <n> is a consecutive number. The files are stored in the same
/// same directory as the main output file.
/// The debug locations and scopes of all functions and instructions are changed
/// to point to the generated SIL output files.
/// This enables debugging and profiling on SIL level.
class SILDebugInfoGenerator : public SILModuleTransform {
enum {
/// To prevent extra large output files, e.g. when compiling the stdlib.
LineLimitPerFile = 10000
};
/// A stream for counting line numbers.
struct LineCountStream : public llvm::raw_ostream {
llvm::raw_ostream &Underlying;
int LineNum = 1;
uint64_t Pos = 0;
void write_impl(const char *Ptr, size_t Size) override {
for (size_t Idx = 0; Idx < Size; Idx++) {
char c = Ptr[Idx];
if (c == '\n')
++LineNum;
}
Underlying.write(Ptr, Size);
Pos += Size;
}
uint64_t current_pos() const override { return Pos; }
LineCountStream(llvm::raw_ostream &Underlying) :
llvm::raw_ostream(/* unbuffered = */ true),
Underlying(Underlying) { }
~LineCountStream() override {
flush();
}
};
/// A print context which records the line numbers where instructions are
/// printed.
struct PrintContext : public SILPrintContext {
LineCountStream LCS;
llvm::DenseMap<const SILInstruction *, int> LineNums;
void printInstructionCallBack(const SILInstruction *I) override {
// Record the current line number of the instruction.
LineNums[I] = LCS.LineNum;
}
PrintContext(llvm::raw_ostream &OS) : SILPrintContext(LCS), LCS(OS) { }
~PrintContext() override { }
};
void run() override {
SILModule *M = getModule();
StringRef FileBaseName = M->getOptions().SILOutputFileNameForDebugging;
if (FileBaseName.empty())
return;
DEBUG(llvm::dbgs() << "** SILDebugInfoGenerator **\n");
std::vector<SILFunction *> PrintedFuncs;
int FileIdx = 0;
auto FIter = M->begin();
while (FIter != M->end()) {
std::string FileName;
llvm::raw_string_ostream NameOS(FileName);
NameOS << FileBaseName << ".gsil_" << FileIdx++ << ".sil";
NameOS.flush();
char *FileNameBuf = (char *)M->allocate(FileName.size() + 1, 1);
strcpy(FileNameBuf, FileName.c_str());
DEBUG(llvm::dbgs() << "Write debug SIL file " << FileName << '\n');
std::error_code EC;
llvm::raw_fd_ostream OutFile(FileName, EC,
llvm::sys::fs::OpenFlags::F_None);
assert(!OutFile.has_error() && !EC && "Can't write SIL debug file");
PrintContext Ctx(OutFile);
// Write functions until we reach the LineLimitPerFile.
do {
SILFunction *F = &*FIter++;
PrintedFuncs.push_back(F);
// Set the debug scope for the function.
SILLocation::DebugLoc DL(Ctx.LCS.LineNum, 1, FileNameBuf);
RegularLocation Loc(DL);
SILDebugScope *Scope = new (*M) SILDebugScope(Loc, F);
F->setDebugScope(Scope);
// Ensure that the function is visible for debugging.
F->setBare(IsNotBare);
// Print it to the output file.
F->print(Ctx);
} while (FIter != M->end() && Ctx.LCS.LineNum < LineLimitPerFile);
// Set the debug locations of all instructions.
for (SILFunction *F : PrintedFuncs) {
const SILDebugScope *Scope = F->getDebugScope();
for (SILBasicBlock &BB : *F) {
for (SILInstruction &I : BB) {
SILLocation Loc = I.getLoc();
SILLocation::DebugLoc DL(Ctx.LineNums[&I], 1, FileNameBuf);
assert(DL.Line && "no line set for instruction");
if (Loc.is<ReturnLocation>() || Loc.is<ImplicitReturnLocation>()) {
Loc.setDebugInfoLoc(DL);
I.setDebugLocation(SILDebugLocation(Loc, Scope));
} else {
RegularLocation RLoc(DL);
I.setDebugLocation(SILDebugLocation(RLoc, Scope));
}
}
}
}
PrintedFuncs.clear();
}
}
};
} // end anonymous namespace
SILTransform *swift::createSILDebugInfoGenerator() {
return new SILDebugInfoGenerator();
}