Skip to content

Commit

Permalink
Use topology sort for determining source file checking order
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Sep 8, 2024
1 parent 0fa9ecc commit 001ca0a
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 30 deletions.
49 changes: 24 additions & 25 deletions src/SourceFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,15 @@ void SourceFile::runTypeChecker() { // NOLINT(misc-no-recursion)
// We need two runs here due to generics.
// The first run to determine all concrete substantiations of potentially generic elements
runTypeCheckerPre(); // Visit dependency tree from bottom to top

// Compute best order of the source files to perform type checking
resourceManager.enqueueSourceFilesForTypeChecking();
// The second run to ensure, also generic scopes are type-checked properly
runTypeCheckerPost(); // Visit dependency tree from top to bottom
while (!resourceManager.sourceFileVisitQueue.empty()) {
SourceFile *sourceFile = resourceManager.sourceFileVisitQueue.front();
sourceFile->runTypeCheckerPost();
resourceManager.sourceFileVisitQueue.pop();
}
}

void SourceFile::runTypeCheckerPre() { // NOLINT(misc-no-recursion)
Expand All @@ -265,38 +272,26 @@ void SourceFile::runTypeCheckerPre() { // NOLINT(misc-no-recursion)

void SourceFile::runTypeCheckerPost() { // NOLINT(misc-no-recursion)
// Skip if restored from cache, this stage has already been done or not all dependants finished type checking
if (restoredFromCache || !haveAllDependantsBeenTypeChecked())
if (restoredFromCache)
return;

Timer timer(&compilerOutput.times.typeCheckerPost);
timer.start();

// Start type-checking loop. The type-checker can request a re-execution. The max number of type-checker runs is limited
TypeChecker typeChecker(resourceManager, this, TC_MODE_POST);
unsigned short typeCheckerRuns = 0;
do {
typeCheckerRuns++;
totalTypeCheckerRuns++;
reVisitRequested = false;

// Type-check the current file first. Multiple times, if requested
timer.resume();
typeChecker.visit(ast);
timer.pause();

// Then type-check all dependencies
for (SourceFile *sourceFile : dependencies | std::views::values)
sourceFile->runTypeCheckerPost();
} while (reVisitRequested);
typeChecker.visit(ast);
totalTypeCheckerRuns++;

// Check if there are soft errors and print them
checkForSoftErrors();

// Check if all dyn variables were type-inferred successfully
globalScope->ensureSuccessfulTypeInference();

previousStage = TYPE_CHECKER_POST;
timer.stop();
printStatusMessage("Type Checker Post", IO_AST, IO_AST, compilerOutput.times.typeCheckerPost, typeCheckerRuns);
printStatusMessage("Type Checker Post", IO_AST, IO_AST, compilerOutput.times.typeCheckerPost, totalTypeCheckerRuns);

// Save the JSON version in the compiler output
if (cliOptions.dumpSettings.dumpSymbolTable || cliOptions.testMode)
Expand Down Expand Up @@ -545,8 +540,16 @@ void SourceFile::runFrontEnd() { // NOLINT(misc-no-recursion)
void SourceFile::runMiddleEnd() {
runTypeCheckerPre();
CHECK_ABORT_FLAG_V()
runTypeCheckerPost();
CHECK_ABORT_FLAG_V()

// Compute best order of the source files to perform type checking
resourceManager.enqueueSourceFilesForTypeChecking();
// Visit the source files in this order
while (!resourceManager.sourceFileVisitQueue.empty()) {
SourceFile *sourceFile = resourceManager.sourceFileVisitQueue.front();
sourceFile->runTypeCheckerPost();
CHECK_ABORT_FLAG_V()
resourceManager.sourceFileVisitQueue.pop();
}
}

void SourceFile::runBackEnd() { // NOLINT(misc-no-recursion)
Expand Down Expand Up @@ -716,10 +719,6 @@ bool SourceFile::isRT(RuntimeModule runtimeModule) const {
return exportedNameRegistry.at(topLevelName).targetEntry->scope == globalScope.get();
}

bool SourceFile::haveAllDependantsBeenTypeChecked() const {
return std::ranges::all_of(dependants, [](const SourceFile *dependant) { return dependant->totalTypeCheckerRuns >= 1; });
}

/**
* Acquire all publicly visible symbols from the imported source file and put them in the name registry of the current one.
* But only do that for the symbols that are actually defined in the imported source file. Do not allow transitive dependencies.
Expand Down Expand Up @@ -823,7 +822,7 @@ void SourceFile::printStatusMessage(const char *stage, const CompileStageIOType
outputStr << compilerStageIoTypeName[in] << " --> " << compilerStageIoTypeName[out];
outputStr << " (" << std::to_string(stageRuntime) << " ms";
if (stageRuns > 0)
outputStr << "; " << std::to_string(stageRuns) << " run(s)";
outputStr << "; Run " << std::to_string(stageRuns);
outputStr << ")\n";
// Print
std::cout << outputStr.str();
Expand Down
2 changes: 0 additions & 2 deletions src/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ class SourceFile {
bool alwaysKeepSymbolsOnNameCollision = false;
bool ignoreWarnings = false;
bool restoredFromCache = false;
bool reVisitRequested = false;
CompileStageType previousStage = NONE;
SourceFileAntlrCtx antlrCtx;
CompilerOutput compilerOutput;
Expand All @@ -192,7 +191,6 @@ class SourceFile {
uint8_t totalTypeCheckerRuns = 0;

// Private methods
bool haveAllDependantsBeenTypeChecked() const;
void mergeNameRegistries(const SourceFile &importedSourceFile, const std::string &importName);
void dumpOutput(const std::string &content, const std::string &caption, const std::string &fileSuffix) const;
void visualizerPreamble(std::stringstream &output) const;
Expand Down
32 changes: 31 additions & 1 deletion src/global/GlobalResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ GlobalResourceManager::~GlobalResourceManager() {
SourceFile *GlobalResourceManager::createSourceFile(SourceFile *parent, const std::string &depName,
const std::filesystem::path &path, bool isStdFile) {
// Check if the source file was already added (e.g. by another source file that imports it)
const std::string filePathStr = std::filesystem::weakly_canonical(std::filesystem::absolute(path)).string();
const std::string filePathStr = weakly_canonical(absolute(path)).string();

// Create the new source file if it does not exist yet
if (!sourceFiles.contains(filePathStr))
Expand All @@ -70,6 +70,36 @@ SourceFile *GlobalResourceManager::createSourceFile(SourceFile *parent, const st
return sourceFiles.at(filePathStr).get();
}

void GlobalResourceManager::enqueueSourceFilesForTypeChecking() {
assert(sourceFileVisitQueue.empty());
std::stack<SourceFile *> stack;
std::unordered_set<std::string> visited;

// Call the recursive helper function to store topological sort starting from all files
for (auto &[name, sourceFile] : sourceFiles)
if (!visited.contains(sourceFile->name))
topologicalSortHelper(sourceFile.get(), visited, stack);

while (!stack.empty()) {
sourceFileVisitQueue.push(stack.top());
stack.pop();
}
}

void GlobalResourceManager::topologicalSortHelper(SourceFile *file, std::unordered_set<std::string> &visited,
std::stack<SourceFile *> &stack) {
// Mark the current node as visited
visited.insert(file->name);

// Recur for all the dependencies of this source file
for (auto &[name, sourceFile] : file->dependencies)
if (!visited.contains(name))
topologicalSortHelper(sourceFile, visited, stack);

// Push the current source file to the stack
stack.push(file);
}

uint64_t GlobalResourceManager::getNextCustomTypeId() { return nextCustomTypeId++; }

size_t GlobalResourceManager::getTotalLineCount() const {
Expand Down
4 changes: 3 additions & 1 deletion src/global/GlobalResourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
#include <global/RuntimeModuleManager.h>
#include <linker/ExternalLinkerInterface.h>
#include <util/BlockAllocator.h>
#include <util/CodeLoc.h>
#include <util/Timer.h>

#include <llvm/IR/LLVMContext.h>
Expand Down Expand Up @@ -36,6 +35,8 @@ class GlobalResourceManager {

// Public methods
SourceFile *createSourceFile(SourceFile *parent, const std::string &depName, const std::filesystem::path &path, bool isStdFile);
void enqueueSourceFilesForTypeChecking();
static void topologicalSortHelper(SourceFile* file, std::unordered_set<std::string>& visited, std::stack<SourceFile*>& stack);
uint64_t getNextCustomTypeId();
size_t getTotalLineCount() const;

Expand All @@ -55,6 +56,7 @@ class GlobalResourceManager {
RuntimeModuleManager runtimeModuleManager;
Timer totalTimer;
ErrorManager errorManager;
std::queue<SourceFile *> sourceFileVisitQueue; // Used for queueing type checking in the correct order
bool abortCompilation = false;

private:
Expand Down
3 changes: 2 additions & 1 deletion src/typechecker/TypeChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2667,8 +2667,9 @@ std::vector<const Function *> &TypeChecker::getOpFctPointers(ASTNode *node) cons
* @param fct Function to check
*/
void TypeChecker::requestRevisitIfRequired(const Function *fct) const {
// Push source file to the back of the processing queue
if (fct && !fct->alreadyTypeChecked && !fct->entry->scope->isImportedBy(rootScope))
fct->entry->scope->sourceFile->reVisitRequested = true;
resourceManager.sourceFileVisitQueue.push(fct->entry->scope->sourceFile);
}

/**
Expand Down

0 comments on commit 001ca0a

Please sign in to comment.