[ExportSystemC] Basic infrastructure for emission (#3726)

This commit introduces the base classes and utilities to implement
emission patterns and an emission printer which takes the role of a
driver that can be called in a circt-translate pass. Also includes
emission patterns for the builtin module and SCModule operations to
(1) allow to implement some basic unit and integration tests
(2) show the reviewer how emission patterns look like using this
infrastructure.
This commit is contained in:
Martin Erhart 2022-08-17 00:55:08 +02:00 committed by GitHub
parent d323a775bf
commit dd2e9120c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 830 additions and 8 deletions

View File

@ -13,7 +13,7 @@ jobs:
name: Build and Test
runs-on: ubuntu-18.04
container:
image: ghcr.io/circt/images/circt-integration-test:v10.3
image: ghcr.io/circt/images/circt-integration-test:v11
strategy:
fail-fast: false
matrix:

View File

@ -23,7 +23,7 @@ jobs:
name: Build and Test
runs-on: ubuntu-18.04
container:
image: ghcr.io/circt/images/circt-integration-test:v10.3
image: ghcr.io/circt/images/circt-integration-test:v11
strategy:
# Keep the 'matrix' strategy with one data point to make it obvious that
# this is one point in the overall matrix.

View File

@ -259,6 +259,50 @@ else()
endif()
endif()
#-------------------------------------------------------------------------------
# Clang-Tidy Configuration (for integration tests to check SystemC linting)
#-------------------------------------------------------------------------------
# If clang-tidy hasn't been explicitly disabled, find it.
option(CLANG_TIDY_DISABLE "Disable the clang-tidy lint tests.")
if (CLANG_TIDY_DISABLE)
message(STATUS "Disabling clang-tidy lint tests.")
else()
if (EXISTS ${CLANG_TIDY_PATH})
get_filename_component(CLANG_TIDY_PATH ${CLANG_TIDY_PATH} DIRECTORY)
message(STATUS "Setting clang-tidy path to ${CLANG_TIDY_PATH}.")
else()
# Search for the `clang-tidy` command.
find_program(CLANG_TIDY_PATH "clang-tidy")
if(EXISTS ${CLANG_TIDY_PATH})
# Then strip the filename.
get_filename_component(CLANG_TIDY_PATH ${CLANG_TIDY_PATH} DIRECTORY)
message(STATUS "Found clang-tidy at ${CLANG_TIDY_PATH}.")
else()
set(CLANG_TIDY_PATH "")
message(STATUS "Did NOT find clang-tidy.")
endif()
endif()
endif()
#-------------------------------------------------------------------------------
# SystemC Configuration
#-------------------------------------------------------------------------------
# If SystemC hasn't been explicitly disabled, find it.
option(SYSTEMC_DISABLE "Disable the systemc tests.")
if (SYSTEMC_DISABLE)
message(STATUS "Disabling systemc tests.")
else()
find_file(HAVE_SYSTEMC systemc PATH /usr/include /usr/local/include ${SYSTEMC_PATH})
if(HAVE_SYSTEMC)
message(STATUS "Found systemc headers.")
else()
set(HAVE_SYSTEMC "")
message(STATUS "Did NOT find systemc headers.")
endif()
endif()
#-------------------------------------------------------------------------------
# Quartus Configuration
#-------------------------------------------------------------------------------

View File

@ -161,6 +161,7 @@ struct CallInterfaceCallable;
struct LogicalResult;
struct MemRefAccess;
struct OperationState;
class OperationName;
template <typename T>
class FailureOr;
@ -237,6 +238,7 @@ using mlir::OpBuilder; // NOLINT(misc-unused-using-decls)
using mlir::OpConversionPattern; // NOLINT(misc-unused-using-decls)
using mlir::OperandRange; // NOLINT(misc-unused-using-decls)
using mlir::Operation; // NOLINT(misc-unused-using-decls)
using mlir::OperationName; // NOLINT(misc-unused-using-decls)
using mlir::OperationPass; // NOLINT(misc-unused-using-decls)
using mlir::OperationState; // NOLINT(misc-unused-using-decls)
using mlir::OpFoldResult; // NOLINT(misc-unused-using-decls)

View File

@ -0,0 +1 @@
Checks: 'misc-*,-misc-unused-parameters,-misc-non-private-member-variables-in-classes,readability-identifier-naming'

View File

@ -0,0 +1,5 @@
// REQUIRES: clang-tidy, systemc
// RUN: circt-translate %s --export-systemc > %t.cpp
// RUN: clang-tidy --extra-arg=-frtti %t.cpp
systemc.module @module () { }

View File

@ -177,6 +177,16 @@ if config.bindings_python_enabled:
if config.bindings_tcl_enabled:
config.available_features.add('bindings_tcl')
# Enable clang-tidy if it has been detected.
if config.clang_tidy_path != "":
tool_dirs.append(config.clang_tidy_path)
tools.append('clang-tidy')
config.available_features.add('clang-tidy')
# Enable systemc if it has been detected.
if config.have_systemc != "":
config.available_features.add('systemc')
llvm_config.add_tool_substitutions(tools, tool_dirs)
# cocotb availability

View File

@ -46,6 +46,8 @@ config.quartus_path = "@QUARTUS_PATH@"
config.vivado_path = "@VIVADO_PATH@"
config.questa_path = "@QUESTA_PATH@"
config.iverilog_path = "@IVERILOG_PATH@"
config.clang_tidy_path = "@CLANG_TIDY_PATH@"
config.have_systemc = "@HAVE_SYSTEMC@"
config.esi_capnp = "@ESI_CAPNP@"
config.bindings_python_enabled = @CIRCT_BINDINGS_PYTHON_ENABLED@
config.bindings_tcl_enabled = @CIRCT_BINDINGS_TCL_ENABLED@

View File

@ -1,5 +1,7 @@
add_circt_translation_library(CIRCTExportSystemC
EmissionPrinter.cpp
ExportSystemC.cpp
Patterns/SystemCEmissionPatterns.cpp
ADDITIONAL_HEADER_DIRS

View File

@ -0,0 +1,308 @@
//===- EmissionPattern.h - Emission Pattern Base and Utility --------------===//
//
// 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 declares the emission pattern base and utility classes.
//
//===----------------------------------------------------------------------===//
#ifndef CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPATTERN_H
#define CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPATTERN_H
#include "EmissionPatternSupport.h"
#include "mlir/IR/Operation.h"
#include <any>
namespace circt {
namespace ExportSystemC {
// Forward declarations.
class EmissionPrinter;
//===----------------------------------------------------------------------===//
// Inline emission utilities.
//===----------------------------------------------------------------------===//
/// This enum encodes the precedence of C++ expressions. A lower number means
/// higher precedence. Source:
/// https://en.cppreference.com/w/cpp/language/operator_precedence
enum class Precedence {
LIT = 0,
VAR = 0,
SCOPE_RESOLUTION = 1,
POSTFIX_INC = 2,
POSTFIX_DEC = 2,
FUNCTIONAL_CAST = 2,
FUNCTION_CALL = 2,
SUBSCRIPT = 2,
MEMBER_ACCESS = 2,
PREFIX_INC = 3,
PREFIX_DEC = 3,
NOT = 3,
CAST = 3,
DEREFERENCE = 3,
ADDRESS_OF = 3,
SIZEOF = 3,
NEW = 3,
DELETE = 3,
POINTER_TO_MEMBER = 4,
MUL = 5,
DIV = 5,
MOD = 5,
ADD = 6,
SUB = 6,
SHL = 7,
SHR = 7,
RELATIONAL = 9,
EQUALITY = 10,
BITWISE_AND = 11,
BITWISE_XOR = 12,
BITWISE_OR = 13,
LOGICAL_AND = 14,
LOGICAL_OR = 15,
TERNARY = 16,
THROW = 16,
ASSIGN = 16,
COMMA = 17
};
/// This class allows a pattern's match function for inlining to pass its
/// result's precedence to the pattern that requested the expression.
class MatchResult {
public:
MatchResult() = default;
MatchResult(Precedence precedence)
: isFailure(false), precedence(precedence) {}
bool failed() const { return isFailure; }
Precedence getPrecedence() const { return precedence; }
private:
bool isFailure = true;
Precedence precedence;
};
/// This class is returned to a pattern that requested inlined emission of a
/// value. It allows the pattern to emit additional characters before the
/// requested expression depending on the precedence.
class InlineEmitter {
public:
InlineEmitter(std::function<void()> emitter, Precedence precedence)
: precedence(precedence), emitter(std::move(emitter)) {}
Precedence getPrecedence() const { return precedence; }
void emit() const { emitter(); }
private:
Precedence precedence;
std::function<void()> emitter;
};
//===----------------------------------------------------------------------===//
// Emission pattern base classes.
//===----------------------------------------------------------------------===//
/// This is indented to be the base class for all emission patterns.
class PatternBase {
public:
explicit PatternBase(const void *rootValue) : rootValue(rootValue) {}
template <typename E, typename... Args>
static std::unique_ptr<E> create(Args &&...args) {
std::unique_ptr<E> pattern =
std::make_unique<E>(std::forward<Args>(args)...);
return pattern;
}
/// Get a unique identifier for the C++ type the pattern is matching on. This
/// could be a specific MLIR type or operation.
const void *getRootValue() const { return rootValue; }
private:
const void *rootValue;
};
/// This is intended to be the base class for all emission patterns matching on
/// operations.
struct OpEmissionPatternBase : PatternBase {
OpEmissionPatternBase(StringRef operationName, MLIRContext *context)
: PatternBase(
OperationName(operationName, context).getAsOpaquePointer()) {}
virtual ~OpEmissionPatternBase() = default;
/// Checks if this pattern is applicable to the given value to emit an
/// inlinable expression. Additionally returns information such as the
/// precedence to the pattern where this pattern's result is to be inlined.
virtual MatchResult matchInlinable(Value value) = 0;
/// Checks if this pattern is applicable to the given operation for statement
/// emission.
virtual bool matchStatement(mlir::Operation *op) = 0;
/// Emit the expression for the given value.
virtual void emitInlined(mlir::Value value, EmissionPrinter &p) = 0;
/// Emit zero or more statements for the given operation.
virtual void emitStatement(mlir::Operation *op, EmissionPrinter &p) = 0;
};
/// This is intended to be the base class for all emission patterns matching on
/// types.
struct TypeEmissionPatternBase : PatternBase {
explicit TypeEmissionPatternBase(TypeID typeId)
: PatternBase(typeId.getAsOpaquePointer()) {}
virtual ~TypeEmissionPatternBase() = default;
/// Checks if this pattern is applicable to the given type.
virtual bool match(Type type) = 0;
/// Emit the given type to the emission printer.
virtual void emitType(Type type, EmissionPrinter &p) = 0;
};
/// This is a convenience class providing default implementations for operation
/// emission patterns.
template <typename Op>
struct OpEmissionPattern : OpEmissionPatternBase {
explicit OpEmissionPattern(MLIRContext *context)
: OpEmissionPatternBase(Op::getOperationName(), context) {}
void emitStatement(mlir::Operation *op, EmissionPrinter &p) final {
return emitStatement(cast<Op>(op), p);
}
/// Checks if this pattern is applicable to the given value to emit an
/// inlinable expression. Additionally returns information such as the
/// precedence to the pattern where this pattern's result is to be inlined.
/// Defaults to never match.
MatchResult matchInlinable(Value value) override { return MatchResult(); }
/// Checks if this pattern is applicable to the given operation for statement
/// emission. When not overriden this matches on all operations of the type
/// given as template parameter and emits nothing.
bool matchStatement(mlir::Operation *op) override { return isa<Op>(op); }
/// Emit the expression for the given value. This has to be overriden whenever
/// the 'matchInlinable' function is overriden and emit a valid expression.
void emitInlined(mlir::Value value, EmissionPrinter &p) override {}
/// Emit zero (default) or more statements for the given operation.
virtual void emitStatement(Op op, EmissionPrinter &p) {}
};
/// This is a convenience class providing default implementations for type
/// emission patterns.
template <typename Ty>
struct TypeEmissionPattern : TypeEmissionPatternBase {
TypeEmissionPattern() : TypeEmissionPatternBase(TypeID::get<Ty>()) {}
void emitType(Type type, EmissionPrinter &p) final {
emitType(type.cast<Ty>(), p);
}
/// Checks if this pattern is applicable to the given type. Matches to the
/// type given as template argument by default.
bool match(Type type) override { return type.isa<Ty>(); }
/// Emit the given type to the emission printer.
virtual void emitType(Ty type, EmissionPrinter &p) = 0;
};
//===----------------------------------------------------------------------===//
// Emission pattern sets.
//===----------------------------------------------------------------------===//
/// This class collects a set of emission patterns with base type 'PatternTy'.
template <typename PatternTy>
class EmissionPatternSet {
public:
/// Add a new emission pattern that requires additional constructor arguments
/// to this set.
template <typename... Es, typename ConstructorArg,
typename... ConstructorArgs,
typename = std::enable_if_t<sizeof...(Es) != 0>>
void add(ConstructorArg &&arg, ConstructorArgs &&...args) {
(void)std::initializer_list<int>{
0, (addImpl<Es>(std::forward<ConstructorArg>(arg),
std::forward<ConstructorArgs>(args)...),
0)...};
}
/// Add a new emission pattern to the set.
template <typename... Es, typename = std::enable_if_t<sizeof...(Es) != 0>>
void add() {
(void)std::initializer_list<int>{0, (addImpl<Es>(), 0)...};
}
/// Get all the emission patterns added to this set.
std::vector<std::unique_ptr<PatternTy>> &getNativePatterns() {
return patterns;
}
private:
template <typename E, typename... Args>
std::enable_if_t<std::is_base_of<PatternTy, E>::value>
addImpl(Args &&...args) {
std::unique_ptr<E> pattern =
PatternBase::create<E>(std::forward<Args>(args)...);
patterns.emplace_back(std::move(pattern));
}
private:
std::vector<std::unique_ptr<PatternTy>> patterns;
};
/// This class intends to collect a set of emission patterns in a way to provide
/// fast lookups, but does not allow to add more patterns after construction.
template <typename PatternTy, typename KeyTy>
class FrozenEmissionPatternSet {
using NativePatternListT = std::vector<std::unique_ptr<PatternTy>>;
public:
/// A map of type specific native patterns.
using OpSpecificNativePatternListT =
DenseMap<KeyTy, std::vector<PatternTy *>>;
FrozenEmissionPatternSet() : impl(std::make_shared<Impl>()) {}
/// Freeze the patterns held in `patterns`, and take ownership.
FrozenEmissionPatternSet(EmissionPatternSet<PatternTy> &&patterns)
: impl(std::make_shared<Impl>()) {
for (std::unique_ptr<PatternTy> &pat : patterns.getNativePatterns()) {
impl->nativeOpSpecificPatternMap[KeyTy::getFromOpaquePointer(
pat->getRootValue())]
.push_back(pat.get());
impl->nativeOpSpecificPatternList.push_back(std::move(pat));
}
}
/// Return the native patterns held by this set.
const OpSpecificNativePatternListT &getSpecificNativePatterns() const {
return impl->nativeOpSpecificPatternMap;
}
private:
/// The internal implementation of the frozen pattern set.
struct Impl {
/// The set of emission patterns that are matched to specific kinds.
OpSpecificNativePatternListT nativeOpSpecificPatternMap;
/// The full native rewrite list. This allows for the map above
/// to contain duplicate patterns, e.g. for interfaces and traits.
NativePatternListT nativeOpSpecificPatternList;
};
/// A pointer to the internal pattern list. This uses a shared_ptr to avoid
/// the need to compile the same pattern list multiple times. For example,
/// during multi-threaded pass execution, all copies of a pass can share the
/// same pattern list.
std::shared_ptr<Impl> impl;
};
} // namespace ExportSystemC
} // namespace circt
#endif // CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPATTERN_H

View File

@ -0,0 +1,42 @@
//===- EmissionPatternSupport.h - Emission Pattern forward declarations ---===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Forward declarations and shorthands for various emission pattern classes
// such that emission pattern implementations of dialects don't have to
// #include all the template classes.
//
//===----------------------------------------------------------------------===//
#ifndef CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPATTERNSUPPORT_H
#define CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPATTERNSUPPORT_H
#include "circt/Support/LLVM.h"
namespace circt {
namespace ExportSystemC {
// Forward declarations.
template <typename Ty>
class EmissionPatternSet;
template <typename PatternTy, typename KeyTy>
class FrozenEmissionPatternSet;
struct OpEmissionPatternBase;
struct TypeEmissionPatternBase;
using OpEmissionPatternSet = EmissionPatternSet<OpEmissionPatternBase>;
using TypeEmissionPatternSet = EmissionPatternSet<TypeEmissionPatternBase>;
using FrozenOpEmissionPatternSet =
FrozenEmissionPatternSet<OpEmissionPatternBase, OperationName>;
using FrozenTypeEmissionPatternSet =
FrozenEmissionPatternSet<TypeEmissionPatternBase, TypeID>;
} // namespace ExportSystemC
} // namespace circt
#endif // CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPATTERNSUPPORT_H

View File

@ -0,0 +1,99 @@
//===- EmissionPrinter.cpp - EmissionPrinter implementation ---------------===//
//
// 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 implements the EmissionPrinter class.
//
//===----------------------------------------------------------------------===//
#include "EmissionPrinter.h"
using namespace circt;
using namespace circt::ExportSystemC;
void EmissionPrinter::emitOp(Operation *op) {
auto patterns = opPatterns.getSpecificNativePatterns().lookup(op->getName());
for (auto *pat : patterns) {
if (pat->matchStatement(op)) {
pat->emitStatement(op, *this);
return;
}
}
// Emit a placeholder to the output and an error to stderr in case no valid
// emission pattern was found.
mlir::emitError(op->getLoc(), "no emission pattern found for '")
<< op->getName() << "'\n";
os << "\n<<UNSUPPORTED OPERATION (" << op->getName() << ")>>\n";
emissionFailed = true;
}
void EmissionPrinter::emitType(Type type) {
auto patterns =
typePatterns.getSpecificNativePatterns().lookup(type.getTypeID());
for (auto *pat : patterns) {
if (pat->match(type)) {
pat->emitType(type, *this);
return;
}
}
// Emit a placeholder to the output and an error to stderr in case no valid
// emission pattern was found.
mlir::emitError(UnknownLoc::get(type.getContext()),
"no emission pattern found for type ")
<< type << "\n";
os << "<<UNSUPPORTED TYPE (" << type << ")>>";
emissionFailed = true;
}
InlineEmitter EmissionPrinter::getInlinable(Value value) {
auto *op = value.isa<BlockArgument>() ? value.getParentRegion()->getParentOp()
: value.getDefiningOp();
auto patterns = opPatterns.getSpecificNativePatterns().lookup(op->getName());
for (auto *pat : patterns) {
MatchResult match = pat->matchInlinable(value);
if (!match.failed()) {
return InlineEmitter([=]() { pat->emitInlined(value, *this); },
match.getPrecedence());
}
}
// Emit a placeholder to the output and an error to stderr in case no valid
// emission pattern was found.
emissionFailed = true;
mlir::emitError(value.getLoc(), "inlining not supported for value '")
<< value << "'\n";
return InlineEmitter(
[&]() { os << "<<INVALID VALUE TO INLINE (" << value << ")>>"; },
Precedence::LIT);
}
void EmissionPrinter::emitRegion(Region &region) {
auto scope = os.scope("{\n", "}\n");
emitRegion(region, scope);
}
void EmissionPrinter::emitRegion(
Region &region, mlir::raw_indented_ostream::DelimitedScope &scope) {
assert(region.hasOneBlock() &&
"only regions with exactly one block are supported for now");
for (Operation &op : region.getBlocks().front()) {
emitOp(&op);
}
}
EmissionPrinter &EmissionPrinter::operator<<(StringRef str) {
os << str;
return *this;
}
EmissionPrinter &EmissionPrinter::operator<<(int64_t num) {
os << std::to_string(num);
return *this;
}

View File

@ -0,0 +1,99 @@
//===- EmissionPrinter.h - Provides printing utilites to ExportSystemC ----===//
//
// 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 declares the EmissionPrinter class to provide printing utilities to
// ExportSystemC.
//
//===----------------------------------------------------------------------===//
#ifndef CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPRINTER_H
#define CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPRINTER_H
#include "EmissionPattern.h"
#include "mlir/Support/IndentedOstream.h"
namespace circt {
namespace ExportSystemC {
/// This is intended to be the driving class for all pattern-based IR emission.
class EmissionPrinter {
public:
EmissionPrinter(mlir::raw_indented_ostream &os,
const FrozenOpEmissionPatternSet &opPatterns,
const FrozenTypeEmissionPatternSet &typePatterns)
: opPatterns(opPatterns), typePatterns(typePatterns), os(os),
emissionFailed(false) {}
EmissionPrinter(mlir::raw_indented_ostream &os,
OpEmissionPatternSet &opPatterns,
TypeEmissionPatternSet &typePatterns)
: opPatterns(std::move(opPatterns)),
typePatterns(std::move(typePatterns)), os(os), emissionFailed(false) {}
/// Emit the given operation as a statement to the ostream associated with
/// this printer according to the emission patterns registered. An operation
/// might also emit multiple statements, or nothing in case it can only be
/// emitted as an expression. If multiple emission patterns match, the first
/// one in the first one in the pattern set is chosen. If no pattern matches,
/// a remark is left in the output and an error is added to stderr.
/// Additionally, the exit-code to be obtained by the 'exitCode()'
/// member-function is set to 'failure'.
void emitOp(Operation *op);
/// Emit the expression represented by the given value to the ostream
/// associated with this printer according to the emission patterns
/// registered. This will emit exactly one expression and does not emit any
/// statements. If multiple emission patterns match, the first one in the
/// first one in the pattern set is chosen. If no pattern matches, a remark is
/// left in the output and an error is added to stderr. Additionally, the
/// exit-code to be obtained by the 'exitCode()' member-function is set to
/// 'failure'.
InlineEmitter getInlinable(Value value);
/// Emit the given type to the ostream associated with this printer according
/// to the emission patterns registered. If multiple emission patterns match,
/// the first one in the pattern set is chosen. If no pattern matches, a
/// remark is left in the output and an error is added to stderr.
/// Additionally, the exit-code to be obtained by the 'exitCode()'
/// member-function is set to 'failure'.
void emitType(Type type);
/// Emit the given region to the ostream associated with this printer. Only
/// regions with a single basic block are allowed. Prints the operations
/// inside according to 'emitOp()' indented one level deeper and encloses the
/// region in curly-braces.
void emitRegion(Region &region);
/// Emit the given region to the ostream associated with this printer. Only
/// regions with a single basic block are allowed. Prints the operations
/// inside according to 'emitOp()'. The enclosing delimiters and level of
/// indentation is determined by the passed scope.
void emitRegion(Region &region,
mlir::raw_indented_ostream::DelimitedScope &scope);
EmissionPrinter &operator<<(StringRef str);
EmissionPrinter &operator<<(int64_t num);
mlir::raw_indented_ostream &getOstream() const { return os; }
/// Returns whether everything was printed successfully or some error occurred
/// (e.g., there was an operation or type for which no emission pattern was
/// valid).
LogicalResult exitState() const { return failure(emissionFailed); }
private:
FrozenOpEmissionPatternSet opPatterns;
FrozenTypeEmissionPatternSet typePatterns;
mlir::raw_indented_ostream &os;
bool emissionFailed;
};
} // namespace ExportSystemC
} // namespace circt
#endif // CIRCT_TARGET_EXPORTSYSTEMC_EMISSIONPRINTER_H

View File

@ -11,6 +11,10 @@
//===----------------------------------------------------------------------===//
#include "circt/Target/ExportSystemC.h"
#include "EmissionPrinter.h"
#include "RegisterAllEmitters.h"
#include "circt/Dialect/Comb/CombDialect.h"
#include "circt/Dialect/HW/HWDialect.h"
#include "circt/Dialect/SystemC/SystemCDialect.h"
#include "mlir/IR/BuiltinOps.h"
#include "mlir/Support/FileUtilities.h"
@ -18,15 +22,49 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/ToolOutputFile.h"
#include <regex>
using namespace circt;
using namespace circt::ExportSystemC;
#define DEBUG_TYPE "export-systemc"
/// Helper to convert a file-path to a macro name that can be used to guard a
/// header file.
static std::string pathToMacroName(StringRef path) {
// Replace characters that represent a path hierarchy with underscore to match
// the usual header guard formatting.
auto str = std::regex_replace(path.upper(), std::regex("[\\\\./]"), "_");
// Remove invalid characters. TODO: a digit is not allowed as the first
// character, but not fixed here.
return std::regex_replace(str, std::regex("[^a-zA-Z0-9_$]+"), "");
}
/// Emits the given operation to a file represented by the passed ostream and
/// file-path.
static LogicalResult emitFile(Operation *op, StringRef filePath,
raw_ostream &os) {
return op->emitError("Not yet supported!");
mlir::raw_indented_ostream ios(os);
OpEmissionPatternSet opPatterns;
registerAllOpEmitters(opPatterns, op->getContext());
TypeEmissionPatternSet typePatterns;
registerAllTypeEmitters(typePatterns);
EmissionPrinter printer(ios, opPatterns, typePatterns);
printer << "// " << filePath << "\n";
std::string macroname = pathToMacroName(filePath);
printer << "#ifndef " << macroname << "\n";
printer << "#define " << macroname << "\n\n";
// TODO: add support for 'emitc.include' and remove this hard-coded include.
printer << "#include <systemc>\n\n";
printer.emitOp(op);
printer << "\n#endif // " << macroname << "\n\n";
return failure(printer.exitState().failed());
}
//===----------------------------------------------------------------------===//
@ -47,10 +85,10 @@ LogicalResult ExportSystemC::exportSplitSystemC(ModuleOp module,
return module.emitError("cannot create output directory \"")
<< directory << "\": " << error.message();
SmallString<128> filePath(directory);
llvm::sys::path::append(filePath, symbolOp.getName());
// Open or create the output file.
std::string fileName = symbolOp.getName().str() + ".h";
SmallString<128> filePath(directory);
llvm::sys::path::append(filePath, fileName);
std::string errorMessage;
auto output = mlir::openOutputFile(filePath, &errorMessage);
if (!output)
@ -85,7 +123,8 @@ void ExportSystemC::registerExportSystemCTranslation() {
return ExportSystemC::exportSystemC(module, output);
},
[](mlir::DialectRegistry &registry) {
registry.insert<systemc::SystemCDialect>();
registry.insert<hw::HWDialect, comb::CombDialect,
systemc::SystemCDialect>();
});
static mlir::TranslateFromMLIRRegistration toSplitSystemC(
@ -94,6 +133,7 @@ void ExportSystemC::registerExportSystemCTranslation() {
return ExportSystemC::exportSplitSystemC(module, directory);
},
[](mlir::DialectRegistry &registry) {
registry.insert<systemc::SystemCDialect>();
registry.insert<hw::HWDialect, comb::CombDialect,
systemc::SystemCDialect>();
});
}

View File

@ -0,0 +1,83 @@
//===- SystemCEmissionPatterns.cpp - SystemC Dialect Emission Patterns ----===//
//
// 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 implements the emission patterns for the systemc dialect.
//
//===----------------------------------------------------------------------===//
#include "SystemCEmissionPatterns.h"
#include "../EmissionPrinter.h"
#include "circt/Dialect/SystemC/SystemCOps.h"
using namespace circt;
using namespace circt::systemc;
using namespace circt::ExportSystemC;
//===----------------------------------------------------------------------===//
// Operation emission patterns.
//===----------------------------------------------------------------------===//
namespace {
/// Emit a SystemC module using the SC_MODULE macro and emit all ports as fields
/// of the module. Users of the ports request an expression to be inlined and we
/// simply return the name of the port.
struct SCModuleEmitter : OpEmissionPattern<SCModuleOp> {
using OpEmissionPattern::OpEmissionPattern;
MatchResult matchInlinable(Value value) override {
if (value.isa<BlockArgument>() &&
value.getParentRegion()->getParentOfType<SCModuleOp>())
return Precedence::VAR;
return {};
}
void emitInlined(Value value, EmissionPrinter &p) override {
auto module = value.getParentRegion()->getParentOfType<SCModuleOp>();
for (size_t i = 0, e = module.getNumArguments(); i < e; ++i) {
if (module.getArgument(i) == value) {
p << module.getPortNames()[i].cast<StringAttr>().getValue();
return;
}
}
}
void emitStatement(SCModuleOp module, EmissionPrinter &p) override {
p << "\nSC_MODULE(" << module.getModuleName() << ") ";
auto scope = p.getOstream().scope("{\n", "};\n");
for (size_t i = 0, e = module.getNumArguments(); i < e; ++i) {
p.emitType(module.getArgument(i).getType());
auto portName = module.getPortNames()[i].cast<StringAttr>().getValue();
p << " " << portName << ";\n";
}
p.emitRegion(module.getRegion(), scope);
}
};
/// Emit the builtin module op by emitting all children in sequence. As a
/// result, we don't have to hard-code the behavior in ExportSytemC.
struct BuiltinModuleEmitter : OpEmissionPattern<ModuleOp> {
using OpEmissionPattern::OpEmissionPattern;
void emitStatement(ModuleOp op, EmissionPrinter &p) override {
auto scope = p.getOstream().scope("", "", false);
p.emitRegion(op.getRegion(), scope);
}
};
} // namespace
//===----------------------------------------------------------------------===//
// Register Operation and Type emission patterns.
//===----------------------------------------------------------------------===//
void circt::ExportSystemC::populateSystemCOpEmitters(
OpEmissionPatternSet &patterns, MLIRContext *context) {
patterns.add<BuiltinModuleEmitter, SCModuleEmitter>(context);
}
void circt::ExportSystemC::populateSystemCTypeEmitters(
TypeEmissionPatternSet &patterns) {}

View File

@ -0,0 +1,31 @@
//===- SystemCEmissionPatterns.h - SystemC Dialect Emission Patterns ------===//
//
// 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 exposes the emission patterns of the systemc dialect for registration.
//
//===----------------------------------------------------------------------===//
#ifndef CIRCT_TARGET_EXPORTSYSTEMC_PATTERNS_SYSTEMCEMISSIONPATTERNS_H
#define CIRCT_TARGET_EXPORTSYSTEMC_PATTERNS_SYSTEMCEMISSIONPATTERNS_H
#include "../EmissionPatternSupport.h"
namespace circt {
namespace ExportSystemC {
/// Register SystemC operation emission patterns.
void populateSystemCOpEmitters(OpEmissionPatternSet &patterns,
MLIRContext *context);
/// Register SystemC type emission patterns.
void populateSystemCTypeEmitters(TypeEmissionPatternSet &patterns);
} // namespace ExportSystemC
} // namespace circt
#endif // CIRCT_TARGET_EXPORTSYSTEMC_PATTERNS_SYSTEMCEMISSIONPATTERNS_H

View File

@ -0,0 +1,36 @@
//===- RegisterAllEmitters.h - Register all emitters to ExportSystemC -----===//
//
// 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 registers the all the emitters of various dialects to the
// ExportSystemC pass.
//
//===----------------------------------------------------------------------===//
#ifndef CIRCT_TARGET_EXPORTSYSTEMC_REGISTERALLEMITTERS_H
#define CIRCT_TARGET_EXPORTSYSTEMC_REGISTERALLEMITTERS_H
#include "Patterns/SystemCEmissionPatterns.h"
namespace circt {
namespace ExportSystemC {
/// Collects the operation emission patterns of all supported dialects.
inline void registerAllOpEmitters(OpEmissionPatternSet &patterns,
mlir::MLIRContext *context) {
populateSystemCOpEmitters(patterns, context);
}
/// Collects the type emission patterns of all supported dialects.
inline void registerAllTypeEmitters(TypeEmissionPatternSet &patterns) {
populateSystemCTypeEmitters(patterns);
}
} // namespace ExportSystemC
} // namespace circt
#endif // CIRCT_TARGET_EXPORTSYSTEMC_REGISTERALLEMITTERS_H

View File

@ -0,0 +1,13 @@
// RUN: circt-translate %s --export-systemc | FileCheck %s
// CHECK-LABEL: // stdout.h
// CHECK-NEXT: #ifndef STDOUT_H
// CHECK-NEXT: #define STDOUT_H
// CHECK: #include <systemc>
// CHECK-LABEL: SC_MODULE(basic) {
// CHECK-NEXT: };
systemc.module @basic () { }
// CHECK: #endif // STDOUT_H

View File

@ -0,0 +1,5 @@
// RUN: circt-translate %s --export-systemc --verify-diagnostics | FileCheck %s
// CHECK: <<UNSUPPORTED OPERATION (hw.module)>>
// expected-error @+1 {{no emission pattern found for 'hw.module'}}
hw.module @notSupported () -> () { }