Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add debugging support of nautilus ir and mlir #39

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion nautilus/include/nautilus/std/cmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ val<float> log10f(val<float> x);
val<float> log2(val<float> x);
val<double> log2(val<double> x);


/**
* @brief natural logarithm (to base e) of 1 plus the given number (ln(1+x))
*
Expand Down
8 changes: 4 additions & 4 deletions nautilus/src/nautilus/api/std/cmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ val<int8_t> abs(val<int8_t> x) {
return invoke<>(
+[](int8_t x) -> int8_t { return std::abs(x); }, x);
}
val<int16_t > abs(val<int16_t > x) {
val<int16_t> abs(val<int16_t> x) {
return invoke<>(
+[](int16_t x) -> int16_t { return std::abs(x); }, x);
}
val<int32_t > abs(val<int32_t > x) {
val<int32_t> abs(val<int32_t> x) {
return invoke<>(
+[](int32_t x) -> int32_t { return std::abs(x); }, x);
}
Expand Down Expand Up @@ -633,11 +633,11 @@ val<double> log10(val<double> x) {

val<float> log2(val<float> x) {
return invoke<>(
+[](float x) { return std::log2(x); }, x);
+[](float x) { return std::log2(x); }, x);
}
val<double> log2(val<double> x) {
return invoke<>(
+[](double x) { return std::log2(x); }, x);
+[](double x) { return std::log2(x); }, x);
}

#if defined(_LIBCPP_VERSION)
Expand Down
11 changes: 10 additions & 1 deletion nautilus/src/nautilus/compiler/DumpHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,21 @@ void DumpHandler::dump(std::string_view dumpName, std::string_view extension, co
}
}

void DumpHandler::dump(std::string_view dumpName, std::string_view extension, const std::function<std::string(const std::string& path)>& dumpFunction) const {
if (shouldDump(dumpName) && dumpToFile()) {
auto filePath = rootPath / fmt::format("{}.{}", dumpName, extension);
auto content = dumpFunction(filePath.string());
common::File::createFile(filePath.string(), content);
fmt::println("{} -- {} -- file://{}", dumpName, id, filePath.native());
}
}

bool DumpHandler::shouldDump(std::string_view dumpName) const {
return options.getOptionOrDefault("dump.all", false) || options.getOptionOrDefault("dump." + std::string(dumpName), false);
}

bool DumpHandler::dumpToConsole() const {
return options.getOptionOrDefault("dump.console", false);
return options.getOptionOrDefault("dump.console", true);
}

bool DumpHandler::dumpToFile() const {
Expand Down
1 change: 1 addition & 0 deletions nautilus/src/nautilus/compiler/DumpHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class DumpHandler {
public:
DumpHandler(const engine::Options& options, const CompilationUnitID& id);
void dump(std::string_view dumpName, std::string_view extension, const std::function<std::string()>& dumpFunction) const;
void dump(std::string_view dumpName, std::string_view extension, const std::function<std::string(const std::string& path)>& dumpFunction) const;

private:
[[nodiscard]] bool shouldDump(std::string_view dumpName) const;
Expand Down
13 changes: 12 additions & 1 deletion nautilus/src/nautilus/compiler/JITCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "nautilus/Executable.hpp"
#include "nautilus/compiler/DumpHandler.hpp"
#include "nautilus/compiler/backends/CompilationBackend.hpp"
#include "nautilus/compiler/ir/IRDumpHandler.hpp"
#include "nautilus/config.hpp"
#include "nautilus/exceptions/RuntimeException.hpp"
#include <chrono>
Expand Down Expand Up @@ -75,7 +76,17 @@ std::unique_ptr<Executable> JITCompiler::compile(JITCompiler::wrapper_function f
// get nautilus ir from trace
auto irGenerationPhase = tracing::TraceToIRConversionPhase();
auto ir = irGenerationPhase.apply(std::move(afterSSA), compilationId);
dumpHandler.dump("after_ir_creation", "ir", [&]() { return ir->toString(); });
//dumpHandler.dump("after_ir_creation", "ir", [&]() { return ir->toString(); });

dumpHandler.dump("final_ir", "ir", [&](const std::string& path) {
std::stringstream ss;
ss << "NautilusIR {\n";
auto irDumper = ir::IRDumpHandler::create(ss, path);
irDumper->dump(ir->getRootOperation());
ss << "}";
return ss.str();
});

// lower to backend
auto backendName = options.getOptionOrDefault<std::string>("engine.backend", "mlir");
auto backend = backends->getBackend(backendName);
Expand Down
12 changes: 7 additions & 5 deletions nautilus/src/nautilus/compiler/backends/mlir/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ if (ENABLE_MLIR_BACKEND)
message(STATUS "extension_libs: ${extension_libs}")
target_link_libraries(nautilus
PRIVATE

${mlir_libs} ${dialect_libs}
${conversion_libs}
MLIRReconcileUnrealizedCasts
MLIRMemRefToLLVM
MLIRExecutionEngine
MLIRFuncAllExtensions
# Dialects
MLIRSCFDialect
MLIRFuncToLLVM
MLIRSCFToControlFlow
MLIRParser
MLIRAsmParser
)

add_source_files(nautilus
Expand Down
14 changes: 13 additions & 1 deletion nautilus/src/nautilus/compiler/backends/mlir/JITCompiler.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

#include "nautilus/compiler/backends/mlir/JITCompiler.hpp"
#include "nautilus/compiler/backends/mlir/MLIRLoweringProvider.hpp"
#include <mlir/Debug/ExecutionContext.h>
#include <mlir/ExecutionEngine/OptUtils.h>
#include <mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h>
#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
Expand All @@ -18,14 +19,21 @@ std::unique_ptr<::mlir::ExecutionEngine> JITCompiler::jitCompileModule(::mlir::O

// Convert the module to LLVM IR in a new LLVM IR context.
llvm::LLVMContext llvmContext;
llvmContext.enableDebugTypeODRUniquing();
auto llvmModule = ::mlir::translateModuleToLLVMIR(mlirModule->getOperation(), llvmContext);
if (!llvmModule) {
llvm::errs() << "Failed to emit LLVM IR\n";
}
llvmModule->convertToNewDbgValues();
llvmModule->debug_compile_units();



// Initialize information about the local machine in LLVM.
LLVMInitializeNativeTarget();
LLVMInitializeNativeAsmPrinter();
LLVMInitializeNativeDisassembler();
LLVMInitializeAllDisassemblers();

//(void) dumpHelper;
// if (compilerOptions.isDumpToConsole() || compilerOptions.isDumpToFile()) {
Expand All @@ -34,8 +42,10 @@ std::unique_ptr<::mlir::ExecutionEngine> JITCompiler::jitCompileModule(::mlir::O

// Create MLIR execution engine (wrapper around LLVM ExecutionEngine).
::mlir::ExecutionEngineOptions options;
options.jitCodeGenOptLevel = llvm::CodeGenOptLevel::Aggressive;
options.jitCodeGenOptLevel = llvm::CodeGenOptLevel::None;
options.transformer = optPipeline;
options.enableGDBNotificationListener = true;
//options.enableObjectDump = true;
auto maybeEngine = ::mlir::ExecutionEngine::create(*mlirModule, options);

assert(maybeEngine && "failed to construct an execution engine");
Expand All @@ -51,7 +61,9 @@ std::unique_ptr<::mlir::ExecutionEngine> JITCompiler::jitCompileModule(::mlir::O
return symbolMap;
};
auto& engine = maybeEngine.get();

engine->registerSymbols(runtimeSymbolMap);

return std::move(engine);
}
} // namespace nautilus::compiler::mlir
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ std::function<llvm::Error(llvm::Module*)> LLVMIROptimizer::getLLVMOptimizerPipel
// JITTargetMachineBuilder for the host");
auto targetMachine = tmBuilderOrError->createTargetMachine();
llvm::TargetMachine* targetMachinePtr = targetMachine->get();
targetMachinePtr->setOptLevel(llvm::CodeGenOptLevel::Aggressive);
targetMachinePtr->setOptLevel(llvm::CodeGenOptLevel::None);

// Add target-specific attributes to the 'execute' function.
llvmIRModule->getFunction("execute")->addAttributeAtIndex(~0, llvm::Attribute::get(llvmIRModule->getContext(), "target-cpu", targetMachinePtr->getTargetCPU()));
Expand All @@ -52,7 +52,7 @@ std::function<llvm::Error(llvm::Module*)> LLVMIROptimizer::getLLVMOptimizerPipel
handler.dump("llvm", "ll", [&]() {
std::string llvmIRString;
llvm::raw_string_ostream llvmStringStream(llvmIRString);
llvmIRModule->print(llvmStringStream, nullptr);
llvmIRModule->print(llvmStringStream, nullptr, false,true);
return llvmIRString;
});

Expand Down
166 changes: 155 additions & 11 deletions nautilus/src/nautilus/compiler/backends/mlir/MLIRCompilationBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,37 @@
#include "nautilus/compiler/backends/mlir/MLIRLoweringProvider.hpp"
#include "nautilus/compiler/backends/mlir/MLIRPassManager.hpp"
#include "nautilus/compiler/ir/IRGraph.hpp"
#include <mlir/Parser/Parser.h>
#include <iostream>
#include <mlir/Debug/DebuggerExecutionContextHook.h>
#include <mlir/Dialect/Func/Extensions/AllExtensions.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/IR/MLIRContext.h>
#include <mlir/Dialect/LLVMIR/LLVMDialect.h>
#include <mlir/Target/LLVMIR/Dialect/Builtin/BuiltinToLLVMIRTranslation.h>
#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
#include "llvm/BinaryFormat/Dwarf.h"

#include "llvm/Support/Debug.h"
#include "llvm/Support/Path.h"
namespace nautilus::compiler::mlir {

static ::mlir::WalkResult interruptIfValidLocation(::mlir::Operation *op) {
return isa<::mlir::UnknownLoc>(op->getLoc()) ? ::mlir::WalkResult::advance()
: ::mlir::WalkResult::interrupt();
}


::mlir::FileLineColLoc extractFileLoc(::mlir::Location loc) {
if (auto fileLoc = dyn_cast<::mlir::FileLineColLoc>(loc))
return fileLoc;
if (auto nameLoc = dyn_cast<::mlir::NameLoc>(loc))
return extractFileLoc(nameLoc.getChildLoc());
if (auto opaqueLoc = dyn_cast<::mlir::OpaqueLoc>(loc))
return extractFileLoc(opaqueLoc.getFallbackLocation());
return ::mlir::FileLineColLoc();
}

std::unique_ptr<Executable> MLIRCompilationBackend::compile(const std::shared_ptr<ir::IRGraph>& ir, const DumpHandler& dumpHandler, const engine::Options& options) {

// 1. Create the MLIRLoweringProvider and lower the given NESIR. Return an
Expand All @@ -24,8 +46,7 @@ std::unique_ptr<Executable> MLIRCompilationBackend::compile(const std::shared_pt
::mlir::func::registerAllExtensions(registry);
registerBuiltinDialectTranslation(registry);
registerLLVMDialectTranslation(registry);

::mlir::MLIRContext context(registry);
::mlir::MLIRContext context(registry);
context.allowsUnregisteredDialects();

auto loweringProvider = std::make_unique<MLIRLoweringProvider>(context);
Expand All @@ -34,28 +55,151 @@ std::unique_ptr<Executable> MLIRCompilationBackend::compile(const std::shared_pt
throw RuntimeException("verification of MLIR module failed!");
};

// 2.a dump MLIR to console or a file
dumpHandler.dump("after_mlir_generation", "mlir", [&]() {
::mlir::OpPrintingFlags flags;
std::string result;
auto output = llvm::raw_string_ostream(result);
mlirModule->print(output, flags);
return result;
});

// 2.b Take the MLIR module from the MLIRLoweringProvider and apply lowering
// and optimization passes.
if (mlir::MLIRPassManager::lowerAndOptimizeMLIRModule(mlirModule, {}, {})) {
throw RuntimeException("Could not lower and optimize MLIR module.");
}

// 2.a dump MLIR to console or a file

context.getDiagEngine().registerHandler([]([[maybe_unused]]::mlir::Diagnostic &diag) {

return;
});
//::mlir::OwningOpRef<::mlir::Operation*> res = ::mlir::parseSourceFile(path, &context);

//auto mod = static_cast<::mlir::ModuleOp>(res.release());
//mlirModule = ::mlir::OwningOpRef<::mlir::ModuleOp>(mod);




// 3. Lower MLIR module to LLVM IR and create LLVM IR optimization pipeline.
auto optPipeline = LLVMIROptimizer::getLLVMOptimizerPipeline(options, dumpHandler);

if (!mlirModule->walk(interruptIfValidLocation).wasInterrupted()){
assert(false);
}


::mlir::LLVM::DICompileUnitAttr compileUnitAttr;
auto fusedCompileUnitAttr =
mlirModule->getLoc()
->findInstanceOf<::mlir::FusedLocWith<::mlir::LLVM::DICompileUnitAttr>>();
if (fusedCompileUnitAttr) {
compileUnitAttr = fusedCompileUnitAttr.getMetadata();
} else {
::mlir::LLVM::DIFileAttr fileAttr;
if (::mlir::FileLineColLoc fileLoc = extractFileLoc(mlirModule->getLoc())) {
::mlir::StringRef inputFilePath = fileLoc.getFilename().getValue();
fileAttr = ::mlir::LLVM::DIFileAttr::get(
mlirModule->getContext(), llvm::sys::path::filename(inputFilePath),
llvm::sys::path::parent_path(inputFilePath));
} else {
fileAttr =::mlir::LLVM::DIFileAttr::get(mlirModule->getContext(), "<unknown>", "");
}

compileUnitAttr = ::mlir::LLVM::DICompileUnitAttr::get(
mlirModule->getContext(), ::mlir::DistinctAttr::create(::mlir::UnitAttr::get(mlirModule->getContext())),
llvm::dwarf::DW_LANG_C, fileAttr, ::mlir::StringAttr::get(mlirModule->getContext(), "NAUTILUS"),
/*isOptimized=*/true, ::mlir::LLVM::DIEmissionKind::Full);
}
mlirModule->walk([&](::mlir::LLVM::LLVMFuncOp llvmFunc) {
::mlir::LLVM::DICompileUnitAttr cu = compileUnitAttr;
::mlir::Location loc = llvmFunc.getLoc();
if (loc->findInstanceOf<::mlir::FusedLocWith<::mlir::LLVM::DISubprogramAttr>>())
return;

::mlir::MLIRContext *context = llvmFunc->getContext();

// Filename, line and colmun to associate to the function.
::mlir::LLVM::DIFileAttr fileAttr;
int64_t line = 1, col = 1;
::mlir::FileLineColLoc fileLoc = extractFileLoc(loc);
if (!fileLoc && compileUnitAttr) {
fileAttr = compileUnitAttr.getFile();
} else if (!fileLoc) {
fileAttr = ::mlir::LLVM::DIFileAttr::get(context, "<unknown>", "");
} else {
line = fileLoc.getLine();
col = fileLoc.getColumn();
::mlir::StringRef inputFilePath = fileLoc.getFilename().getValue();
fileAttr =
::mlir::LLVM::DIFileAttr::get(context, llvm::sys::path::filename(inputFilePath),
llvm::sys::path::parent_path(inputFilePath));
}
auto subroutineTypeAttr =
::mlir::LLVM::DISubroutineTypeAttr::get(context, llvm::dwarf::DW_CC_normal, {});

::mlir::StringAttr funcNameAttr = llvmFunc.getNameAttr();
// Only definitions need a distinct identifier and a compilation unit.
::mlir::DistinctAttr id;
auto subprogramFlags = ::mlir::LLVM::DISubprogramFlags::Optimized;
if (!llvmFunc.isExternal()) {
id = ::mlir::DistinctAttr::create(::mlir::UnitAttr::get(context));
subprogramFlags = subprogramFlags | ::mlir::LLVM::DISubprogramFlags::Definition;
} else {
cu = {};
}
auto subprogramAttr = ::mlir::LLVM::DISubprogramAttr::get(
context, id, cu, fileAttr, funcNameAttr, funcNameAttr,
fileAttr,
/*line=*/line,
/*scopeline=*/col, subprogramFlags, subroutineTypeAttr);
llvmFunc->setLoc(::mlir::FusedLoc::get(context, {loc}, subprogramAttr));
});


mlirModule->walk([&](::mlir::LLVM::AddOp addOp) {
auto lo = addOp.getLoc();
auto nameLoc = dyn_cast<::mlir::NameLoc>(lo);
auto fileLoc = extractFileLoc(nameLoc);
auto fileAttr = compileUnitAttr.getFile();
// TODO: finish the DI logic
unsigned arg = 0;
// TODO: finish the DI logic
unsigned alignInBits = 8;
auto int32_t_str = ::mlir::StringAttr::get(mlirModule->getContext(), "NAUTILUS");
auto diType = ::mlir::LLVM::DIDerivedTypeAttr::get(
mlirModule->getContext(), llvm::dwarf::DW_TAG_typedef,int32_t_str, ::mlir::LLVM::DIBasicTypeAttr::get(
mlirModule->getContext(), llvm::dwarf::DW_TAG_base_type, "int",
32, llvm::dwarf::DW_ATE_signed_char), 32,
0, 0);

auto name = nameLoc.getName();
auto scope = ::mlir::LLVM::DIScopeAttr();
auto varInfo = ::mlir::LLVM::DILocalVariableAttr::get(mlirModule->getContext(), scope, name, fileAttr, fileLoc.getLine(), arg, alignInBits, diType);

addOp->setLoc(::mlir::FusedLoc::get(mlirModule->getContext(), {nameLoc}, varInfo));
});


std::string path;
dumpHandler.dump("after_mlir_generation", "mlir", [&](auto p) {
::mlir::OpPrintingFlags flags;
flags.enableDebugInfo(true, false);
std::string result;
auto output = llvm::raw_string_ostream(result);
mlirModule->print(output, flags);
path = p + "_2";
auto* basicError = new std::error_code();
llvm::raw_fd_ostream fileStream(path, *basicError);
fileStream.write(result.c_str(), result.length());
return result;
});

// 4. JIT compile LLVM IR module and return engine that provides access
// compiled execute function.
auto engine = JITCompiler::jitCompileModule(mlirModule, optPipeline, loweringProvider->getJitProxyFunctionSymbols(), loweringProvider->getJitProxyTargetAddresses());


::mlir::tracing::ExecutionContext executionContext;
::mlir::setupDebuggerExecutionContextHook(executionContext);
::mlirDebuggerPrintContext();

// engine->dumpToObjectFile("./test.ob");

// 5. Get execution function from engine. Create and return execution context.
return std::make_unique<MLIRExecutable>(std::move(engine));
}
Expand Down
Loading