Skip to content

Commit

Permalink
Parallelize compiler backend
Browse files Browse the repository at this point in the history
  • Loading branch information
marcauberer committed Nov 29, 2022
1 parent 3d21d40 commit 8e86d3d
Show file tree
Hide file tree
Showing 13 changed files with 106 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .run/spice.run.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -d -symtab -O0 ../../media/test-project/os-test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<configuration default="false" name="spice" type="CMakeRunConfiguration" factoryName="Application" PROGRAM_PARAMS="run -d -symtab -O2 ../../media/test-project/os-test.spice" REDIRECT_INPUT="false" ELEVATE="false" USE_EXTERNAL_CONSOLE="false" PASS_PARENT_ENVS_2="true" PROJECT_NAME="Spice" TARGET_NAME="spice" CONFIG_NAME="Debug" RUN_TARGET_PROJECT_NAME="Spice" RUN_TARGET_NAME="spice">
<envs>
<env name="CONSOLE_WIDTH" value="300" />
<env name="RUN_TESTS" value="OFF" />
Expand Down
5 changes: 3 additions & 2 deletions media/test-project/os-test.spice
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import "os-test2" as s1;

f<int> main() {
dyn s = Vector{Vector1{123}};
printf("Result: %d\n", s.i1.i2);
//dyn s = Vector{Vector1{123}};
//printf("Result: %d\n", s.i1.i2);
printf("Test");
}

/*f<bool> isValid(int input = 12) {
Expand Down
3 changes: 3 additions & 0 deletions setup-libs.bat
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ git clone --depth 1 --branch release-1.12.1 https://github.com/google/googletest
mkdir json
curl -SsL "https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp" --output json/json.hpp

mkdir thread-pool
curl -SsL "https://raw.githubusercontent.com/bshoshany/thread-pool/master/BS_thread_pool.hpp" --output thread-pool/thread-pool.hpp

mkdir cli11
curl -SsL "https://github.com/spicelang/CLI11/releases/download/v2.3.0-spice/CLI11.hpp" --output cli11/CLI11.hpp

Expand Down
3 changes: 3 additions & 0 deletions setup-libs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ git clone --depth 1 --branch release-1.12.1 https://github.com/google/googletest
mkdir json
curl -SsL "https://github.com/nlohmann/json/releases/download/v3.11.2/json.hpp" --output json/json.hpp

mkdir thread-pool
curl -SsL "https://raw.githubusercontent.com/bshoshany/thread-pool/master/BS_thread_pool.hpp" --output thread-pool/thread-pool.hpp

mkdir cli11
curl -SsL "https://github.com/spicelang/CLI11/releases/download/v2.3.0-spice/CLI11.hpp" --output cli11/CLI11.hpp

Expand Down
104 changes: 53 additions & 51 deletions src/SourceFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@

SourceFile::SourceFile(GlobalResourceManager &resourceManager, SourceFile *parent, std::string name, const std::string &filePath,
bool stdFile)
: resourceManager(resourceManager), name(std::move(name)), filePath(filePath), stdFile(stdFile), parent(parent) {
: resourceManager(resourceManager), tout(resourceManager.tout), name(std::move(name)), filePath(filePath), stdFile(stdFile),
parent(parent) {
this->objectFilePath = resourceManager.cliOptions.outputDir + FileUtil::DIR_SEPARATOR + FileUtil::getFileName(filePath) + ".o";

// Deduce fileName and fileDir
Expand Down Expand Up @@ -317,18 +318,14 @@ void SourceFile::runEscapeAnalyzer() { // NOLINT(misc-no-recursion)
printStatusMessage("Escape Analyzer", IO_AST, IO_AST, &timer, compilerOutput.times.escapeAnalyzer);
}

void SourceFile::runIRGenerator() { // NOLINT(misc-no-recursion)
void SourceFile::runIRGenerator() {
// Skip if restored from cache
if (restoredFromCache)
return;

Timer timer;
timer.start();

// Generate the imported source files
for (const auto &sourceFile : dependencies)
sourceFile.second.first->runIRGenerator();

// Create LLVM module for this source file
llvmModule = std::make_unique<llvm::Module>(FileUtil::getFileName(filePath), resourceManager.context);

Expand All @@ -340,17 +337,14 @@ void SourceFile::runIRGenerator() { // NOLINT(misc-no-recursion)
compilerOutput.irString = irGenerator.getIRString();

// Dump unoptimized IR code
if (resourceManager.cliOptions.dumpIR) { // GCOV_EXCL_START
std::cout << "\nUnoptimized IR code:\n";
irGenerator.dumpIR();
std::cout << "\n";
} // GCOV_EXCL_STOP
if (resourceManager.cliOptions.dumpIR) // GCOV_EXCL_LINE
tout.println("\nUnoptimized IR code:\n" + irGenerator.getIRString()); // GCOV_EXCL_LINE

timer.stop();
printStatusMessage("IR Generator", IO_AST, IO_IR, &timer, compilerOutput.times.irGenerator);
printStatusMessage("IR Generator", IO_AST, IO_IR, &timer, compilerOutput.times.irGenerator, true);
}

void SourceFile::runIROptimizer() { // NOLINT(misc-no-recursion)
void SourceFile::runIROptimizer() {
// Skip if restored from cache
if (restoredFromCache)
return;
Expand All @@ -362,10 +356,6 @@ void SourceFile::runIROptimizer() { // NOLINT(misc-no-recursion)
if (resourceManager.cliOptions.optLevel < 1 || resourceManager.cliOptions.optLevel > 5)
return;

// Optimize the imported source files
for (const auto &sourceFile : dependencies)
sourceFile.second.first->runIROptimizer();

// Optimize this source file
IROptimizer irOptimizer(resourceManager, this);
irOptimizer.optimize();
Expand All @@ -374,60 +364,50 @@ void SourceFile::runIROptimizer() { // NOLINT(misc-no-recursion)
compilerOutput.irOptString = irOptimizer.getOptimizedIRString();

// Dump optimized IR code
if (resourceManager.cliOptions.dumpIR) { // GCOV_EXCL_START
std::cout << "\nOptimized IR code:\n";
irOptimizer.dumpOptimizedIR();
std::cout << "\n";
} // GCOV_EXCL_STOP
if (resourceManager.cliOptions.dumpIR) // GCOV_EXCL_LINE
tout.println("\nOptimized IR code:\n" + irOptimizer.getOptimizedIRString()); // GCOV_EXCL_LINE

timer.stop();
printStatusMessage("IR Optimizer", IO_IR, IO_IR, &timer, compilerOutput.times.irOptimizer);
printStatusMessage("IR Optimizer", IO_IR, IO_IR, &timer, compilerOutput.times.irOptimizer, true);
}

void SourceFile::runObjectEmitter() { // NOLINT(misc-no-recursion)
void SourceFile::runObjectEmitter() {
// Skip if restored from cache
if (restoredFromCache)
return;

Timer timer;
timer.start();

// Emit objects for the imported source files
for (const auto &sourceFile : dependencies)
sourceFile.second.first->runIROptimizer();

// Emit object for this source file
ObjectEmitter objectEmitter(resourceManager, this);
objectEmitter.emit();

// Dump assembly code
if (resourceManager.cliOptions.dumpAssembly) { // GCOV_EXCL_START
std::cout << "\nAssembly code:\n";
tout.println("\nAssembly code:\n");
objectEmitter.dumpAsm();
} // GCOV_EXCL_STOP

timer.stop();
printStatusMessage("Object Emitter", IO_IR, IO_OBJECT_FILE, &timer, compilerOutput.times.objectEmitter);
printStatusMessage("Object Emitter", IO_IR, IO_OBJECT_FILE, &timer, compilerOutput.times.objectEmitter, true);
}

void SourceFile::concludeCompilation() { // NOLINT(misc-no-recursion)
// Conclude all dependencies
for (const auto &sourceFile : dependencies)
sourceFile.second.first->concludeCompilation();

void SourceFile::concludeCompilation() {
// Cache the source file
if (!resourceManager.cliOptions.ignoreCache)
resourceManager.cacheManager.cacheSourceFile(this);

// Print warning if verifier is disabled
if (parent == nullptr && resourceManager.cliOptions.disableVerifier) {
std::cout << "\n";
CompilerWarning(VERIFIER_DISABLED, "The LLVM verifier passes are disabled. Please use this cli option carefully.").print();
std::cout << "\n";
const std::string warningMessage =
CompilerWarning(VERIFIER_DISABLED, "The LLVM verifier passes are disabled. Please use this cli option carefully.")
.warningMessage;
tout.println("\n" + warningMessage);
}

if (resourceManager.cliOptions.printDebugOutput)
std::cout << "Finished compiling " << fileName << ".\n";
tout.println("Finished compiling " + fileName);
}

void SourceFile::runFrontEnd() { // NOLINT(misc-no-recursion)
Expand All @@ -448,11 +428,22 @@ void SourceFile::runMiddleEnd() {
runEscapeAnalyzer();
}

void SourceFile::runBackEnd() {
runIRGenerator();
runIROptimizer();
runObjectEmitter();
concludeCompilation();
void SourceFile::runBackEnd() { // NOLINT(misc-no-recursion)
// Run backend for all dependencies first
for (const auto &[importName, sourceFile] : dependencies)
sourceFile.first->runBackEnd();

// Submit source file compilation to the task queue
resourceManager.threadPool.push_task([&]() {
runIRGenerator();
runIROptimizer();
runObjectEmitter();
concludeCompilation();
});

// Wait until all compile tasks are done
if (mainFile)
resourceManager.threadPool.wait_for_tasks();
}

std::shared_ptr<SourceFile> SourceFile::createSourceFile(const std::string &dependencyName, const std::string &path,
Expand Down Expand Up @@ -527,7 +518,6 @@ const NameRegistryEntry *SourceFile::getNameRegistryEntry(std::string symbolName
void SourceFile::mergeNameRegistries(const SourceFile &importedSourceFile, const std::string &importName) {
for (const auto &[originalName, entry] : importedSourceFile.exportedNameRegistry) {
// Add fully qualified name
const bool isFunction = entry.targetEntry == nullptr || entry.targetEntry->getType().isOneOf({TY_FUNCTION, TY_PROCEDURE});
const std::string newName = importName + "::" + originalName;
exportedNameRegistry.insert({newName, {newName, entry.targetEntry, entry.targetScope}});
// Add the shortened name, considering the name collision
Expand Down Expand Up @@ -564,14 +554,26 @@ void SourceFile::visualizerOutput(std::string outputName, const std::string &out
}

void SourceFile::printStatusMessage(const std::string &stage, const CompilerStageIOType &in, const CompilerStageIOType &out,
const Timer *timer, uint64_t &timeCompilerOutput) const {
const Timer *timer, uint64_t &timeCompilerOutput, bool fromThread /*=false*/) const {
if (resourceManager.cliOptions.printDebugOutput) {
const char *const compilerStageIoTypeName[] = {"Code", "Tokens", "CST", "AST", "IR", "OBJECT_FILE"};
std::cout << "[" << stage << "] for " << fileName << ": ";
std::cout << compilerStageIoTypeName[in] << " --> " << compilerStageIoTypeName[out];
if (timer != nullptr) {
timeCompilerOutput = timer->getDurationMilliseconds();
std::cout << " (" << std::to_string(timeCompilerOutput) << " s)\n";
if (fromThread) {
if (timer != nullptr) {
tout.println("[" + stage + "] for " + fileName + ": " + compilerStageIoTypeName[in] + " --> " +
compilerStageIoTypeName[out] + " (" + std::to_string(timeCompilerOutput) + " s)");
} else {
tout.println("[" + stage + "] for " + fileName + ": " + compilerStageIoTypeName[in] + " --> " +
compilerStageIoTypeName[out]);
}
} else {
if (timer != nullptr) {
std::cout << "[" << stage << "] for " << fileName << ": ";
std::cout << compilerStageIoTypeName[in] << " --> " << compilerStageIoTypeName[out];
std::cout << " (" << std::to_string(timeCompilerOutput) << " s)\n";
} else {
std::cout << "[" << stage << "] for " << fileName << ": ";
std::cout << compilerStageIoTypeName[in] << " --> " << compilerStageIoTypeName[out] << "\n";
}
}
}
}
5 changes: 4 additions & 1 deletion src/SourceFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

#include <llvm/IR/IRBuilder.h>

#include "../lib/thread-pool/thread-pool.hpp"

// Forward declarations
class GlobalResourceManager;
class EntryNode;
Expand Down Expand Up @@ -149,11 +151,12 @@ class SourceFile {
private:
// Private fields
GlobalResourceManager &resourceManager;
BS::synced_stream &tout;

// Private methods
void mergeNameRegistries(const SourceFile &importedSourceFile, const std::string &importName);
void visualizerPreamble(std::stringstream &output) const;
void visualizerOutput(std::string outputName, const std::string &output) const;
void printStatusMessage(const std::string &stage, const CompilerStageIOType &in, const CompilerStageIOType &out,
const Timer *timer, uint64_t &timeCompilerOutput) const;
const Timer *timer, uint64_t &timeCompilerOutput, bool fromThread = false) const;
};
2 changes: 2 additions & 0 deletions src/cli/CLIInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ void CLIInterface::addCompileSubcommandOptions(CLI::App *subCmd) {
// --dump-assembly
subCmd->add_flag<bool>("--dump-assembly,-asm,-s", cliOptions.dumpAssembly, "Dump Assembly code");

// --jobs
subCmd->add_option<unsigned short>("--jobs,-j", cliOptions.compileJobCount, "Compile jobs (threads), used for compilation");
// --ignore-cache
subCmd->add_option<bool>("--ignore-cache", cliOptions.ignoreCache, "Force re-compilation of all source files");
// --disable-ast-opt
Expand Down
7 changes: 4 additions & 3 deletions src/cli/CLIInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@ struct CliOptions {
std::string targetVendor;
std::string targetOs;
bool isNativeTarget = false;
std::string cacheDir; // Where the cache files go. Should always be a temp directory
std::string outputDir; // Where the object files go. Should always be a temp directory
std::string outputPath; // Where the output binary goes.
std::string cacheDir; // Where the cache files go. Should always be a temp directory
std::string outputDir; // Where the object files go. Should always be a temp directory
std::string outputPath; // Where the output binary goes.
unsigned short compileJobCount = 0; // 0 for auto
bool ignoreCache = false;
bool printDebugOutput = false;
bool dumpCST = false;
Expand Down
4 changes: 2 additions & 2 deletions src/global/GlobalResourceManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
GlobalResourceManager::GlobalResourceManager(const CliOptions &cliOptions)
: cliOptions(cliOptions), linker(threadFactory, cliOptions), cacheManager(cliOptions.cacheDir) {
// Initialize the required LLVM targets
//llvm::InitializeAllTargetInfos();
if (cliOptions.isNativeTarget) {
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmParser();
llvm::InitializeNativeTargetAsmPrinter();
} else {
llvm::InitializeAllTargets();
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();
}
//llvm::InitializeAllTargetMCs();

// Search after selected target
std::string error;
Expand Down
6 changes: 6 additions & 0 deletions src/global/GlobalResourceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@

#include <llvm/IR/IRBuilder.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Target/TargetMachine.h>

#include "../../lib/thread-pool/thread-pool.hpp"

// Forward declarations
struct CliOptions;

Expand All @@ -32,4 +35,7 @@ class GlobalResourceManager {
llvm::LLVMContext context;
llvm::IRBuilder<> builder = llvm::IRBuilder<>(context);
llvm::TargetMachine *targetMachine;
BS::thread_pool threadPool = BS::thread_pool(cliOptions.compileJobCount);
BS::synced_stream tout;
std::mutex objectEmitLock;
};
32 changes: 22 additions & 10 deletions src/objectemitter/ObjectEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,44 @@
void ObjectEmitter::emit() const {
const std::string &objectFile = sourceFile->objectFilePath;

// Lock the mutex
resourceManager.objectEmitLock.lock();

// GCOV_EXCL_START
if (cliOptions.printDebugOutput && cliOptions.dumpAssembly)
std::cout << "\nEmitting object file for triplet '" << cliOptions.targetTriple << "' to path: " << objectFile << "\n";
resourceManager.tout.println("\nEmitting object file for triplet '" + cliOptions.targetTriple + "' to path: " + objectFile);
// GCOV_EXCL_STOP

// Open file output stream
std::error_code errorCode;
llvm::raw_fd_ostream dest(objectFile, errorCode, llvm::sys::fs::OF_None);
if (errorCode)
llvm::raw_fd_ostream stream(objectFile, errorCode, llvm::sys::fs::OF_None);
if (errorCode) // GCOV_EXCL_LINE
throw IRError(CANT_OPEN_OUTPUT_FILE, "File '" + objectFile + "' could not be opened"); // GCOV_EXCL_LINE

llvm::legacy::PassManager passManager;
if (resourceManager.targetMachine->addPassesToEmitFile(passManager, dest, nullptr, llvm::CGFT_ObjectFile))
throw IRError(WRONG_TYPE, "Target machine can't emit a file of this type"); // GCOV_EXCL_LINE
// GCOV_EXCL_START
if (resourceManager.targetMachine->addPassesToEmitFile(passManager, stream, nullptr, llvm::CGFT_ObjectFile,
cliOptions.disableVerifier))
throw IRError(WRONG_TYPE, "Target machine can't emit a file of this type");
// GCOV_EXCL_STOP

// Emit object file
passManager.run(*module);
dest.flush();
passManager.run(module);
stream.flush();

// Unlock the mutex
resourceManager.objectEmitLock.unlock();
}

void ObjectEmitter::dumpAsm() const {
llvm::legacy::PassManager passManager;
if (resourceManager.targetMachine->addPassesToEmitFile(passManager, llvm::outs(), nullptr, llvm::CGFT_AssemblyFile))
throw IRError(WRONG_TYPE, "Target machine can't emit a file of this type"); // GCOV_EXCL_LINE
// GCOV_EXCL_START
if (resourceManager.targetMachine->addPassesToEmitFile(passManager, llvm::outs(), nullptr, llvm::CGFT_AssemblyFile,
cliOptions.disableVerifier))
throw IRError(WRONG_TYPE, "Target machine can't emit a file of this type");
// GCOV_EXCL_STOP

// Emit object file
passManager.run(*module);
passManager.run(module);
llvm::outs().flush();
}
Loading

0 comments on commit 8e86d3d

Please sign in to comment.