diff --git a/CODEOWNERS b/CODEOWNERS index b1b8a9bc3068..adeaaa75e429 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -9,6 +9,7 @@ tools/arcilator @fabianschuiki @maerhart **/Conversion/CalyxToFSM @mortbopet **/Conversion/CalyxToHW @mortbopet @mikeurbach **/Conversion/SCFToCalyx @mikeurbach @mortbopet +**/Conversion/CalyxNative @rachitnigam **/Conversion/LoopScheduleToCalyx @andrewb1999 @mikeurbach # DC diff --git a/include/circt/Conversion/CalyxNative.h b/include/circt/Conversion/CalyxNative.h new file mode 100644 index 000000000000..d46e2d314e7d --- /dev/null +++ b/include/circt/Conversion/CalyxNative.h @@ -0,0 +1,31 @@ +//===- CalyxNative.h - Calyx Native pass ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares passes which together will lower the Calyx dialect to the +// HW dialect. +// +//===----------------------------------------------------------------------===// + +#ifndef CIRCT_CONVERSION_CALYXNATIVE_H +#define CIRCT_CONVERSION_CALYXNATIVE_H + +#include "circt/Dialect/Calyx/CalyxOps.h" +#include "circt/Support/LLVM.h" +#include + +namespace mlir { +class Pass; +} // namespace mlir + +namespace circt { + +std::unique_ptr createCalyxNativePass(); + +} // namespace circt + +#endif // CIRCT_CONVERSION_CALYXNATIVE_H diff --git a/include/circt/Conversion/Passes.h b/include/circt/Conversion/Passes.h index 0becdf493198..23dada21d8ad 100644 --- a/include/circt/Conversion/Passes.h +++ b/include/circt/Conversion/Passes.h @@ -15,6 +15,7 @@ #include "circt/Conversion/AffineToLoopSchedule.h" #include "circt/Conversion/ArcToLLVM.h" +#include "circt/Conversion/CalyxNative.h" #include "circt/Conversion/CalyxToFSM.h" #include "circt/Conversion/CalyxToHW.h" #include "circt/Conversion/CombToArith.h" diff --git a/include/circt/Conversion/Passes.td b/include/circt/Conversion/Passes.td index aa7fcecef997..b94a443614ee 100644 --- a/include/circt/Conversion/Passes.td +++ b/include/circt/Conversion/Passes.td @@ -238,6 +238,25 @@ def CalyxToHW : Pass<"lower-calyx-to-hw", "mlir::ModuleOp"> { "seq::SeqDialect", "sv::SVDialect"]; } +//===----------------------------------------------------------------------===// +// CalyxNative +//===----------------------------------------------------------------------===// +def CalyxNative : Pass<"calyx-native", "mlir::ModuleOp"> { + let summary = "Callout to the Calyx native compiler and run a pass pipeline"; + let description = [{ + This pass calls out to the native, Rust-based Calyx compiler to run passes + with it and generate a new, valid, calyx dialect program. + }]; + let constructor = "circt::createCalyxNativePass()"; + let dependentDialects = ["calyx::CalyxDialect"]; + let options = [ + Option<"passPipeline", "pass-pipeline", "std::string", + "", "Passes to run with the native compiler">, + ]; +} + + + //===----------------------------------------------------------------------===// // CalyxToFSM //===----------------------------------------------------------------------===// diff --git a/lib/CAPI/Conversion/CMakeLists.txt b/lib/CAPI/Conversion/CMakeLists.txt index 9671335974f1..f2735caa5bb2 100644 --- a/lib/CAPI/Conversion/CMakeLists.txt +++ b/lib/CAPI/Conversion/CMakeLists.txt @@ -6,6 +6,7 @@ add_mlir_public_c_api_library(CIRCTCAPIConversion CIRCTArcToLLVM CIRCTCalyxToFSM CIRCTCalyxToHW + CIRCTCalyxNative CIRCTCombToArith CIRCTCombToLLVM CIRCTConvertToArcs diff --git a/lib/Conversion/CMakeLists.txt b/lib/Conversion/CMakeLists.txt index 5d65e7c4776f..c98cf18c3086 100644 --- a/lib/Conversion/CMakeLists.txt +++ b/lib/Conversion/CMakeLists.txt @@ -25,3 +25,4 @@ add_subdirectory(SCFToCalyx) add_subdirectory(SeqToSV) add_subdirectory(StandardToHandshake) add_subdirectory(VerifToSV) +add_subdirectory(CalyxNative) \ No newline at end of file diff --git a/lib/Conversion/CalyxNative/CMakeLists.txt b/lib/Conversion/CalyxNative/CMakeLists.txt new file mode 100644 index 000000000000..488022b984c4 --- /dev/null +++ b/lib/Conversion/CalyxNative/CMakeLists.txt @@ -0,0 +1,19 @@ +add_circt_library(CIRCTCalyxNative + CalyxNative.cpp + + ADDITIONAL_HEADER_DIRS + ${MLIR_MAIN_INCLUDE_DIR}/mlir/Conversion/CalyxNative + + DEPENDS + CIRCTConversionPassIncGen + + LINK_COMPONENTS + Core + + LINK_LIBS PUBLIC + CIRCTCalyx + CIRCTExportCalyx + MLIRIR + MLIRPass + MLIRSupport + ) diff --git a/lib/Conversion/CalyxNative/CalyxNative.cpp b/lib/Conversion/CalyxNative/CalyxNative.cpp new file mode 100644 index 000000000000..88a8ea4afb3d --- /dev/null +++ b/lib/Conversion/CalyxNative/CalyxNative.cpp @@ -0,0 +1,160 @@ +//===- CalyxNative.cpp - Invoke the native Calyx compiler +//----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +//===----------------------------------------------------------------------===// +// +// Calls out to the native, Rust-based Calyx compiler using the `calyx` binary +// to run passes. +// +//===----------------------------------------------------------------------===// + +#include "../PassDetail.h" + +#include "circt/Conversion/CalyxNative.h" +#include "circt/Dialect/Calyx/CalyxEmitter.h" +#include "mlir/Parser/Parser.h" +#include "mlir/Support/FileUtilities.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/ToolOutputFile.h" + +using namespace mlir; +using namespace circt; + +/// ConversionPatterns. + +/// Pass entrypoint. + +namespace { +class CalyxNativePass : public CalyxNativeBase { +public: + void runOnOperation() override; + +private: + LogicalResult runOnModule(ModuleOp root); +}; +} // end anonymous namespace + +void CalyxNativePass::runOnOperation() { + ModuleOp mod = getOperation(); + if (failed(runOnModule(mod))) + return signalPassFailure(); +} + +LogicalResult CalyxNativePass::runOnModule(ModuleOp root) { + SmallString<32> execName = llvm::sys::path::filename("calyx"); + llvm::ErrorOr exeMb = llvm::sys::findProgramByName(execName); + + // If cannot find the executable, then nothing to do, return. + if (!exeMb) { + root.emitError() << "cannot find the `calyx` executable in PATH. " + << "Consider installing `calyx` using `cargo install " + "calyx` or reading the instructions here: " + "https://docs.calyxir.org/#compiler-installation"; + return failure(); + } + StringRef calyxExe = exeMb.get(); + + std::string errMsg; + SmallString<32> nativeInputFileName; + std::error_code errCode = llvm::sys::fs::getPotentiallyUniqueTempFileName( + "calyxNativeTemp", /*suffix=*/"", nativeInputFileName); + + if (std::error_code ok; errCode != ok) { + root.emitError( + "cannot generate a unique temporary file for input to Calyx compiler"); + return failure(); + } + + // Emit the current program into a file so the native compiler can operate + // over it. + std::unique_ptr inputFile = + mlir::openOutputFile(nativeInputFileName, &errMsg); + if (inputFile == nullptr) { + root.emitError(errMsg); + return failure(); + } + + auto res = circt::calyx::exportCalyx(root, inputFile->os()); + if (failed(res)) + return failure(); + inputFile->os().flush(); + + // Create a file for the native compiler to write the results into + SmallString<32> nativeOutputFileName; + errCode = llvm::sys::fs::getPotentiallyUniqueTempFileName( + "calyxNativeOutTemp", /*suffix=*/"", nativeOutputFileName); + if (std::error_code ok; errCode != ok) { + root.emitError( + "cannot generate a unique temporary file name to store output"); + return failure(); + } + + llvm::SmallVector calyxArgs = { + calyxExe, nativeInputFileName, "-o", nativeOutputFileName, "-b", "mlir"}; + + // Configure the native pass pipeline. If passPipeline is provided, we expect + // it to be a comma separated list of passes to run. + if (!passPipeline.empty()) { + llvm::SmallVector passArgs; + llvm::StringRef ppRef = passPipeline; + ppRef.split(passArgs, ","); + for (auto pass : passArgs) { + if (pass.empty()) + continue; + calyxArgs.push_back("-p"); + calyxArgs.push_back(pass); + } + } else { + // If no arguments are specified, use the default pipeline. + calyxArgs.push_back("-p"); + calyxArgs.push_back("all"); + } + + std::optional redirects[] = {/*stdin=*/std::nullopt, + /*stdout=*/nativeOutputFileName, + /*stderr=*/std::nullopt}; + + int result = llvm::sys::ExecuteAndWait( + calyxExe, calyxArgs, /*Env=*/std::nullopt, + /*Redirects=*/redirects, + /*SecondsToWait=*/0, /*MemoryLimit=*/0, &errMsg); + + if (result != 0) { + root.emitError() << errMsg; + return failure(); + } + + // Parse the output buffer into a Calyx operation so that we can insert it + // back into the program. + auto bufferRead = llvm::MemoryBuffer::getFile(nativeInputFileName); + if (!bufferRead || !*bufferRead) { + root.emitError("execution of '" + calyxExe + + "' did not produce any output file named '" + + nativeInputFileName + "'"); + return failure(); + } + + // Load the output from the native compiler as a ModuleOp + auto loadedMod = + parseSourceFile(nativeOutputFileName.str(), root.getContext()); + auto *loadedBlock = loadedMod->getBody(); + + // XXX(rachitnigam): This is quite baroque. We insert the new block before the + // previous one and then remove the old block. A better thing to do would be + // to replace the moduleOp completely but I couldn't figure out how to do + // that. + auto *oldBlock = root.getBody(); + loadedBlock->moveBefore(oldBlock); + oldBlock->erase(); + return success(); +} + +std::unique_ptr circt::createCalyxNativePass() { + return std::make_unique(); +} diff --git a/tools/circt-opt/CMakeLists.txt b/tools/circt-opt/CMakeLists.txt index 19c36b601f52..4dbd6d680d68 100644 --- a/tools/circt-opt/CMakeLists.txt +++ b/tools/circt-opt/CMakeLists.txt @@ -15,6 +15,7 @@ target_link_libraries(circt-opt CIRCTArcTransforms CIRCTCalyx CIRCTCalyxToHW + CIRCTCalyxNative CIRCTCalyxToFSM CIRCTCalyxTransforms CIRCTComb diff --git a/tools/hlstool/CMakeLists.txt b/tools/hlstool/CMakeLists.txt index fd1622cb49da..3ad3f92c5f30 100644 --- a/tools/hlstool/CMakeLists.txt +++ b/tools/hlstool/CMakeLists.txt @@ -25,6 +25,7 @@ target_link_libraries(hlstool CIRCTSVTransforms CIRCTSCFToCalyx CIRCTCalyx + CIRCTCalyxNative CIRCTCalyxTransforms CIRCTCalyxToHW CIRCTCalyxToFSM