[ExportSystemC] Support more emitc operations and types (#3892)

* Implement emission patterns for all emitc operations except variable for which we have our own implementation. Support for template arguments to function calls is not added yet, but can be easily added when there is a need.
* Add emission pattern support for attributes: this is helpful for operations like emitc.constant which can take any attribute and print it. Having them pattern based allows for more modularity and reuse.
* Move the helper function to emit parentheses around an expression to the InlineEmitter to share it across the various files implementing emission patterns and allow for easier use.
This commit is contained in:
Martin Erhart 2022-10-04 15:25:01 +02:00 committed by GitHub
parent d81f0623d5
commit d8d7d332d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 583 additions and 118 deletions

View File

@ -61,3 +61,21 @@ systemc.module @systemCTypes (%p0: !systemc.in<!systemc.int_base>,
%p10: !systemc.in<!systemc.lv_base>,
%p11: !systemc.in<!systemc.lv<1024>>,
%p12: !systemc.in<!systemc.logic>) {}
systemc.module @emitcEmission () {
systemc.ctor {
%0 = "emitc.constant"() {value = #emitc.opaque<"5"> : !emitc.opaque<"int">} : () -> !emitc.opaque<"int">
%five = systemc.cpp.variable %0 : !emitc.opaque<"int">
%1 = emitc.apply "&"(%five) : (!emitc.opaque<"int">) -> !emitc.ptr<!emitc.opaque<"int">>
%2 = emitc.apply "*"(%1) : (!emitc.ptr<!emitc.opaque<"int">>) -> !emitc.opaque<"int">
%3 = emitc.cast %2: !emitc.opaque<"int"> to !emitc.opaque<"long">
emitc.call "printf" (%3) {args=["result: %ld\n", 0 : index]} : (!emitc.opaque<"long">) -> ()
%idx = systemc.cpp.variable : index
%4 = hw.constant 4 : i32
%5 = emitc.call "malloc" (%4) : (i32) -> !emitc.ptr<!emitc.opaque<"void">>
%6 = emitc.cast %5 : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<!emitc.opaque<"int">>
%somePtr = systemc.cpp.variable %6 : !emitc.ptr<!emitc.opaque<"int">>
}
}

View File

@ -1,6 +1,7 @@
add_circt_translation_library(CIRCTExportSystemC
EmissionPrinter.cpp
ExportSystemC.cpp
Patterns/BuiltinEmissionPatterns.cpp
Patterns/EmitCEmissionPatterns.cpp
Patterns/HWEmissionPatterns.cpp
Patterns/SystemCEmissionPatterns.cpp

View File

@ -86,22 +86,6 @@ private:
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.
//===----------------------------------------------------------------------===//
@ -164,6 +148,20 @@ struct TypeEmissionPatternBase : PatternBase {
virtual void emitType(Type type, EmissionPrinter &p) = 0;
};
/// This is intended to be the base class for all emission patterns matching on
/// attributes.
struct AttrEmissionPatternBase : PatternBase {
explicit AttrEmissionPatternBase(TypeID typeId)
: PatternBase(typeId.getAsOpaquePointer()) {}
virtual ~AttrEmissionPatternBase() = default;
/// Checks if this pattern is applicable to the given attribute.
virtual bool match(Attribute attr) = 0;
/// Emit the given attribute to the emission printer.
virtual void emitAttr(Attribute attr, EmissionPrinter &p) = 0;
};
/// This is a convenience class providing default implementations for operation
/// emission patterns.
template <typename Op>
@ -212,6 +210,24 @@ struct TypeEmissionPattern : TypeEmissionPatternBase {
virtual void emitType(Ty type, EmissionPrinter &p) = 0;
};
/// This is a convenience class providing default implementations for attribute
/// emission patterns.
template <typename A>
struct AttrEmissionPattern : AttrEmissionPatternBase {
AttrEmissionPattern() : AttrEmissionPatternBase(TypeID::get<A>()) {}
void emitAttr(Attribute attr, EmissionPrinter &p) final {
emitAttr(attr.cast<A>(), p);
}
/// Checks if this pattern is applicable to the given attribute. Matches to
/// the attribute given as template argument by default.
bool match(Attribute attr) override { return attr.isa<A>(); }
/// Emit the given attribute to the emission printer.
virtual void emitAttr(A attr, EmissionPrinter &p) = 0;
};
//===----------------------------------------------------------------------===//
// Emission pattern sets.
//===----------------------------------------------------------------------===//

View File

@ -28,14 +28,18 @@ template <typename PatternTy, typename KeyTy>
class FrozenEmissionPatternSet;
struct OpEmissionPatternBase;
struct TypeEmissionPatternBase;
struct AttrEmissionPatternBase;
using OpEmissionPatternSet = EmissionPatternSet<OpEmissionPatternBase>;
using TypeEmissionPatternSet = EmissionPatternSet<TypeEmissionPatternBase>;
using AttrEmissionPatternSet = EmissionPatternSet<AttrEmissionPatternBase>;
using FrozenOpEmissionPatternSet =
FrozenEmissionPatternSet<OpEmissionPatternBase, OperationName>;
using FrozenTypeEmissionPatternSet =
FrozenEmissionPatternSet<TypeEmissionPatternBase, TypeID>;
using FrozenAttrEmissionPatternSet =
FrozenEmissionPatternSet<AttrEmissionPatternBase, TypeID>;
} // namespace ExportSystemC
} // namespace circt

View File

@ -51,6 +51,22 @@ void EmissionPrinter::emitType(Type type) {
emissionFailed = true;
}
void EmissionPrinter::emitAttr(Attribute attr) {
auto patterns =
attrPatterns.getSpecificNativePatterns().lookup(attr.getTypeID());
for (auto *pat : patterns) {
if (pat->match(attr)) {
pat->emitAttr(attr, *this);
return;
}
}
mlir::emitError(currentLoc, "no emission pattern found for attribute ")
<< attr << "\n";
os << "<<UNSUPPORTED ATTRIBUTE (" << attr << ")>>";
emissionFailed = true;
}
InlineEmitter EmissionPrinter::getInlinable(Value value) {
auto *op = value.isa<BlockArgument>() ? value.getParentRegion()->getParentOp()
: value.getDefiningOp();
@ -61,7 +77,7 @@ InlineEmitter EmissionPrinter::getInlinable(Value value) {
MatchResult match = pat->matchInlinable(value);
if (!match.failed()) {
return InlineEmitter([=]() { pat->emitInlined(value, *this); },
match.getPrecedence());
match.getPrecedence(), *this);
}
}
@ -74,7 +90,7 @@ InlineEmitter EmissionPrinter::getInlinable(Value value) {
err.attachNote(requestLoc) << "requested to be inlined here";
return InlineEmitter(
[&]() { os << "<<INVALID VALUE TO INLINE (" << value << ")>>"; },
Precedence::LIT);
Precedence::LIT, *this);
}
void EmissionPrinter::emitRegion(Region &region) {
@ -92,6 +108,19 @@ void EmissionPrinter::emitRegion(
}
}
InFlightDiagnostic EmissionPrinter::emitError(Operation *op,
const Twine &message) {
emissionFailed = true;
os << "<<ERROR (" << message << ")>>";
return op->emitOpError();
}
InFlightDiagnostic EmissionPrinter::emitError(const Twine &message) {
emissionFailed = true;
os << "<<ERROR (" << message << ")>>";
return mlir::emitError(currentLoc, message);
}
EmissionPrinter &EmissionPrinter::operator<<(StringRef str) {
os << str;
return *this;
@ -101,3 +130,15 @@ EmissionPrinter &EmissionPrinter::operator<<(int64_t num) {
os << std::to_string(num);
return *this;
}
void InlineEmitter::emitWithParensOnLowerPrecedence(Precedence prec,
StringRef lParen,
StringRef rParen) const {
if (precedence >= prec)
printer << lParen;
emitter();
if (precedence >= prec)
printer << rParen;
}

View File

@ -20,6 +20,8 @@
namespace circt {
namespace ExportSystemC {
// Forward declarations.
class InlineEmitter;
/// This is intended to be the driving class for all pattern-based IR emission.
class EmissionPrinter {
@ -27,15 +29,19 @@ public:
EmissionPrinter(mlir::raw_indented_ostream &os,
const FrozenOpEmissionPatternSet &opPatterns,
const FrozenTypeEmissionPatternSet &typePatterns,
const FrozenAttrEmissionPatternSet &attrPatterns,
Location loc)
: opPatterns(opPatterns), typePatterns(typePatterns), os(os),
emissionFailed(false), currentLoc(loc) {}
: opPatterns(opPatterns), typePatterns(typePatterns),
attrPatterns(attrPatterns), os(os), emissionFailed(false),
currentLoc(loc) {}
EmissionPrinter(mlir::raw_indented_ostream &os,
OpEmissionPatternSet &opPatterns,
TypeEmissionPatternSet &typePatterns, Location loc)
TypeEmissionPatternSet &typePatterns,
AttrEmissionPatternSet &attrPatterns, Location loc)
: opPatterns(std::move(opPatterns)),
typePatterns(std::move(typePatterns)), os(os), emissionFailed(false),
typePatterns(std::move(typePatterns)),
attrPatterns(std::move(attrPatterns)), os(os), emissionFailed(false),
currentLoc(loc) {}
/// Emit the given operation as a statement to the ostream associated with
@ -66,6 +72,14 @@ public:
/// member-function is set to 'failure'.
void emitType(Type type);
/// Emit the given attribute 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 emitAttr(Attribute attr);
/// 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
@ -79,6 +93,15 @@ public:
void emitRegion(Region &region,
mlir::raw_indented_ostream::DelimitedScope &scope);
/// Emit an error on the operation and fail emission. Preferred for
/// operations, especially the ones that will be inlined, because it places
/// the error more precisely.
InFlightDiagnostic emitError(Operation *op, const Twine &message);
/// Emit an error at the current location of the printer (the newest operation
/// to be emitted as a statement). This is primarily intended for types and
/// attributes for which no location is available directly.
InFlightDiagnostic emitError(const Twine &message);
EmissionPrinter &operator<<(StringRef str);
EmissionPrinter &operator<<(int64_t num);
@ -92,11 +115,32 @@ public:
private:
FrozenOpEmissionPatternSet opPatterns;
FrozenTypeEmissionPatternSet typePatterns;
FrozenAttrEmissionPatternSet attrPatterns;
mlir::raw_indented_ostream &os;
bool emissionFailed;
Location currentLoc;
};
/// 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,
EmissionPrinter &printer)
: precedence(precedence), emitter(std::move(emitter)), printer(printer) {}
Precedence getPrecedence() const { return precedence; }
void emit() const { emitter(); }
void emitWithParensOnLowerPrecedence(Precedence prec, StringRef lParen = "(",
StringRef rParen = ")") const;
private:
Precedence precedence;
std::function<void()> emitter;
EmissionPrinter &printer;
};
} // namespace ExportSystemC
} // namespace circt

View File

@ -59,7 +59,9 @@ static LogicalResult emitFile(ArrayRef<Operation *> operations,
registerAllOpEmitters(opPatterns, operations[0]->getContext());
TypeEmissionPatternSet typePatterns;
registerAllTypeEmitters(typePatterns);
EmissionPrinter printer(ios, opPatterns, typePatterns,
AttrEmissionPatternSet attrPatterns;
registerAllAttrEmitters(attrPatterns);
EmissionPrinter printer(ios, opPatterns, typePatterns, attrPatterns,
operations[0]->getLoc());
for (auto *op : operations)

View File

@ -0,0 +1,132 @@
//===- BuiltinEmissionPatterns.cpp - Builtin 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 builtin dialect.
//
//===----------------------------------------------------------------------===//
#include "BuiltinEmissionPatterns.h"
#include "../EmissionPrinter.h"
#include "mlir/IR/BuiltinOps.h"
#include "llvm/ADT/SmallString.h"
using namespace mlir;
using namespace circt;
using namespace circt::ExportSystemC;
//===----------------------------------------------------------------------===//
// Operation emission patterns.
//===----------------------------------------------------------------------===//
namespace {
/// 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 ModuleEmitter : OpEmissionPattern<ModuleOp> {
using OpEmissionPattern::OpEmissionPattern;
void emitStatement(ModuleOp op, EmissionPrinter &p) override {
auto scope = p.getOstream().scope("", "", false);
p.emitRegion(op.getRegion(), scope);
}
};
} // namespace
//===----------------------------------------------------------------------===//
// Type emission patterns.
//===----------------------------------------------------------------------===//
namespace {
/// Emit the builtin integer type to native C integer types.
struct IntegerTypeEmitter : TypeEmissionPattern<IntegerType> {
bool match(Type type) override {
if (!type.isa<IntegerType>())
return false;
unsigned bw = type.getIntOrFloatBitWidth();
return bw == 1 || bw == 8 || bw == 16 || bw == 32 || bw == 64;
}
void emitType(IntegerType type, EmissionPrinter &p) override {
unsigned bitWidth = type.getIntOrFloatBitWidth();
switch (bitWidth) {
case 1:
p << "bool";
break;
case 8:
case 16:
case 32:
case 64:
p << (type.isSigned() ? "" : "u") << "int" << bitWidth << "_t";
break;
default:
p.emitError("in the IntegerType emitter all cases allowed by the 'match' "
"function must be covered")
<< ", got uncovered case " << type;
}
}
};
/// Emit a builtin index type as 'size_t'.
struct IndexTypeEmitter : TypeEmissionPattern<IndexType> {
void emitType(IndexType type, EmissionPrinter &p) override { p << "size_t"; }
};
} // namespace
namespace {
/// Emit a builtin string attribute as a C string literal including the
/// quotation marks.
struct StringAttrEmitter : AttrEmissionPattern<StringAttr> {
void emitAttr(StringAttr attr, EmissionPrinter &p) override {
attr.print(p.getOstream());
}
};
/// Emit a builtin integer attribute as an integer literal. Integers with a
/// bitwidth of one are emitted as boolean literals 'true' and 'false'.
struct IntegerAttrEmitter : AttrEmissionPattern<IntegerAttr> {
void emitAttr(IntegerAttr attr, EmissionPrinter &p) override {
auto val = attr.getValue();
if (val.getBitWidth() == 1) {
p << (val.getBoolValue() ? "true" : "false");
} else {
bool isSigned = false;
if (auto integer = attr.getType().dyn_cast<IntegerType>())
isSigned = integer.isSigned();
SmallString<128> strValue;
val.toString(strValue, 10, isSigned);
p << strValue;
}
}
};
} // namespace
//===----------------------------------------------------------------------===//
// Register Operation and Type emission patterns.
//===----------------------------------------------------------------------===//
void circt::ExportSystemC::populateBuiltinOpEmitters(
OpEmissionPatternSet &patterns, MLIRContext *context) {
patterns.add<ModuleEmitter>(context);
}
void circt::ExportSystemC::populateBuiltinTypeEmitters(
TypeEmissionPatternSet &patterns) {
patterns.add<IntegerTypeEmitter, IndexTypeEmitter>();
}
void circt::ExportSystemC::populateBuiltinAttrEmitters(
AttrEmissionPatternSet &patterns) {
patterns.add<StringAttrEmitter, IntegerAttrEmitter>();
}

View File

@ -0,0 +1,35 @@
//===- BuiltinEmissionPatterns.h - Builtin 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 builtin dialect for registration.
//
//===----------------------------------------------------------------------===//
// NOLINTNEXTLINE(llvm-header-guard)
#ifndef CIRCT_TARGET_EXPORTSYSTEMC_PATTERNS_BUILTINEMISSIONPATTERNS_H
#define CIRCT_TARGET_EXPORTSYSTEMC_PATTERNS_BUILTINEMISSIONPATTERNS_H
#include "../EmissionPatternSupport.h"
namespace circt {
namespace ExportSystemC {
/// Register Builtin operation emission patterns.
void populateBuiltinOpEmitters(OpEmissionPatternSet &patterns,
MLIRContext *context);
/// Register Builtin type emission patterns.
void populateBuiltinTypeEmitters(TypeEmissionPatternSet &patterns);
/// Register Builtin attribute emission patterns.
void populateBuiltinAttrEmitters(AttrEmissionPatternSet &patterns);
} // namespace ExportSystemC
} // namespace circt
#endif // CIRCT_TARGET_EXPORTSYSTEMC_PATTERNS_BUILTINEMISSIONPATTERNS_H

View File

@ -13,6 +13,7 @@
#include "EmitCEmissionPatterns.h"
#include "../EmissionPrinter.h"
#include "mlir/Dialect/EmitC/IR/EmitC.h"
#include "llvm/ADT/SmallString.h"
using namespace mlir::emitc;
using namespace circt;
@ -26,11 +27,171 @@ namespace {
/// Emit emitc.include operations.
struct IncludeEmitter : OpEmissionPattern<IncludeOp> {
using OpEmissionPattern::OpEmissionPattern;
void emitStatement(IncludeOp op, EmissionPrinter &p) override {
p << "#include " << (op.getIsStandardInclude() ? "<" : "\"")
<< op.getInclude() << (op.getIsStandardInclude() ? ">" : "\"") << "\n";
}
};
/// Emit emitc.apply operations.
struct ApplyOpEmitter : OpEmissionPattern<ApplyOp> {
using OpEmissionPattern::OpEmissionPattern;
MatchResult matchInlinable(Value value) override {
if (value.getDefiningOp<ApplyOp>()) {
// We would need to check the 'applicableOperator' to select the
// precedence to return. However, since the dereference and address_of
// operators have the same precedence, we can omit that (for better
// performance).
return Precedence::ADDRESS_OF;
}
return {};
}
void emitInlined(Value value, EmissionPrinter &p) override {
auto applyOp = value.getDefiningOp<ApplyOp>();
p << applyOp.getApplicableOperator();
p.getInlinable(applyOp.getOperand())
.emitWithParensOnLowerPrecedence(Precedence::ADDRESS_OF);
}
};
/// Emit emitc.call operations. Only calls with 0 or 1 results are supported.
/// Calls with no result are emitted as statements whereas calls with exactly
/// one result are always inlined no matter whether it is a pure function or has
/// side effects. To make sure that calls with side effects are not reordered
/// with interferring operations, a pre-pass has to emit VariableOp operations
/// with the result of the call as initial value.
class CallOpEmitter : public OpEmissionPattern<CallOp> {
public:
using OpEmissionPattern::OpEmissionPattern;
MatchResult matchInlinable(Value value) override {
if (auto callOp = value.getDefiningOp<CallOp>()) {
// TODO: template arguments not supported for now.
if (callOp->getNumResults() == 1 && !callOp.getTemplateArgs())
return Precedence::FUNCTION_CALL;
}
return {};
}
void emitInlined(Value value, EmissionPrinter &p) override {
printCallOp(value.getDefiningOp<CallOp>(), p);
}
bool matchStatement(Operation *op) override {
// TODO: template arguments not supported for now.
if (auto callOp = dyn_cast<CallOp>(op))
return callOp->getNumResults() <= 1 && !callOp.getTemplateArgs();
return false;
}
void emitStatement(CallOp callOp, EmissionPrinter &p) override {
if (callOp->getNumResults() != 0)
return;
printCallOp(callOp, p);
p << ";\n";
}
private:
void printCallOp(CallOp callOp, EmissionPrinter &p) {
p << callOp.getCallee();
p << "(";
if (!callOp.getArgs()) {
llvm::interleaveComma(callOp.getOperands(), p, [&](Value operand) {
p.getInlinable(operand).emitWithParensOnLowerPrecedence(
Precedence::COMMA);
});
} else {
llvm::interleaveComma(callOp.getArgs().value(), p, [&](Attribute attr) {
if (auto idx = attr.dyn_cast<IntegerAttr>()) {
if (idx.getType().isa<IndexType>()) {
p.getInlinable(callOp.getOperands()[idx.getInt()])
.emitWithParensOnLowerPrecedence(Precedence::COMMA);
return;
}
}
p.emitAttr(attr);
});
}
p << ")";
}
};
/// Emit emitc.cast operations.
struct CastOpEmitter : OpEmissionPattern<CastOp> {
using OpEmissionPattern::OpEmissionPattern;
MatchResult matchInlinable(Value value) override {
if (value.getDefiningOp<CastOp>())
return Precedence::CAST;
return {};
}
void emitInlined(Value value, EmissionPrinter &p) override {
auto castOp = value.getDefiningOp<CastOp>();
p << "(";
p.emitType(castOp.getDest().getType());
p << ") ";
p.getInlinable(castOp.getSource())
.emitWithParensOnLowerPrecedence(Precedence::CAST);
}
};
/// Emit emitc.constant operations.
struct ConstantEmitter : OpEmissionPattern<ConstantOp> {
using OpEmissionPattern::OpEmissionPattern;
MatchResult matchInlinable(Value value) override {
if (value.getDefiningOp<ConstantOp>())
return Precedence::LIT;
return {};
}
void emitInlined(Value value, EmissionPrinter &p) override {
p.emitAttr(value.getDefiningOp<ConstantOp>().getValue());
}
};
} // namespace
//===----------------------------------------------------------------------===//
// Type emission patterns.
//===----------------------------------------------------------------------===//
namespace {
/// Emit an emitc.opaque type by just printing the contained string without
/// quotation marks.
struct OpaqueTypeEmitter : TypeEmissionPattern<OpaqueType> {
void emitType(OpaqueType type, EmissionPrinter &p) override {
p << type.getValue();
}
};
/// Emit an emitc.ptr type.
struct PointerTypeEmitter : TypeEmissionPattern<PointerType> {
void emitType(PointerType type, EmissionPrinter &p) override {
p.emitType(type.getPointee());
p << "*";
}
};
} // namespace
namespace {
/// Emit an emitc.opaque attribute by just printing the contained string without
/// quotation marks.
struct OpaqueAttrEmitter : AttrEmissionPattern<OpaqueAttr> {
void emitAttr(OpaqueAttr attr, EmissionPrinter &p) override {
p << attr.getValue();
}
};
} // namespace
//===----------------------------------------------------------------------===//
@ -39,5 +200,16 @@ struct IncludeEmitter : OpEmissionPattern<IncludeOp> {
void circt::ExportSystemC::populateEmitCOpEmitters(
OpEmissionPatternSet &patterns, MLIRContext *context) {
patterns.add<IncludeEmitter>(context);
patterns.add<IncludeEmitter, ApplyOpEmitter, CallOpEmitter, CastOpEmitter,
ConstantEmitter>(context);
}
void circt::ExportSystemC::populateEmitCTypeEmitters(
TypeEmissionPatternSet &patterns) {
patterns.add<OpaqueTypeEmitter, PointerTypeEmitter>();
}
void circt::ExportSystemC::populateEmitCAttrEmitters(
AttrEmissionPatternSet &patterns) {
patterns.add<OpaqueAttrEmitter>();
}

View File

@ -23,6 +23,12 @@ namespace ExportSystemC {
void populateEmitCOpEmitters(OpEmissionPatternSet &patterns,
MLIRContext *context);
/// Register EmitC type emission patterns.
void populateEmitCTypeEmitters(TypeEmissionPatternSet &patterns);
/// Register EmitC attribute emission patterns.
void populateEmitCAttrEmitters(AttrEmissionPatternSet &patterns);
} // namespace ExportSystemC
} // namespace circt

View File

@ -37,50 +37,7 @@ struct ConstantEmitter : OpEmissionPattern<ConstantOp> {
}
void emitInlined(Value value, EmissionPrinter &p) override {
APInt val = value.getDefiningOp<ConstantOp>().getValue();
if (val.getBitWidth() == 1) {
p << (val.getBoolValue() ? "true" : "false");
return;
}
SmallString<64> valueString;
val.toStringUnsigned(valueString);
p << valueString;
}
};
} // namespace
//===----------------------------------------------------------------------===//
// Type emission patterns.
//===----------------------------------------------------------------------===//
namespace {
/// Emit the builtin integer type to native C integer types.
struct IntegerTypeEmitter : TypeEmissionPattern<IntegerType> {
bool match(Type type) override {
if (!type.isa<IntegerType>())
return false;
unsigned bw = type.getIntOrFloatBitWidth();
return bw == 1 || bw == 8 || bw == 16 || bw == 32 || bw == 64;
}
void emitType(IntegerType type, EmissionPrinter &p) override {
unsigned bitWidth = type.getIntOrFloatBitWidth();
switch (bitWidth) {
case 1:
p << "bool";
break;
case 8:
case 16:
case 32:
case 64:
p << (type.isSigned() ? "" : "u") << "int" << bitWidth << "_t";
break;
default:
assert(false && "All cases allowed by match function must be covered.");
}
p.emitAttr(value.getDefiningOp<ConstantOp>().getValueAttr());
}
};
} // namespace
@ -93,8 +50,3 @@ void circt::ExportSystemC::populateHWEmitters(OpEmissionPatternSet &patterns,
MLIRContext *context) {
patterns.add<ConstantEmitter>(context);
}
void circt::ExportSystemC::populateHWTypeEmitters(
TypeEmissionPatternSet &patterns) {
patterns.add<IntegerTypeEmitter>();
}

View File

@ -19,7 +19,6 @@
namespace circt {
namespace ExportSystemC {
void populateHWEmitters(OpEmissionPatternSet &patterns, MLIRContext *context);
void populateHWTypeEmitters(TypeEmissionPatternSet &patterns);
} // namespace ExportSystemC
} // namespace circt

View File

@ -18,18 +18,6 @@ using namespace circt;
using namespace circt::systemc;
using namespace circt::ExportSystemC;
static void parenthesizeOnLowerPrecedence(InlineEmitter &emitter,
Precedence prec, EmissionPrinter &p) {
bool needParens = emitter.getPrecedence() >= prec;
if (needParens)
p << "(";
emitter.emit();
if (needParens)
p << ")";
}
//===----------------------------------------------------------------------===//
// Operation emission patterns.
//===----------------------------------------------------------------------===//
@ -73,16 +61,6 @@ struct SCModuleEmitter : OpEmissionPattern<SCModuleOp> {
}
};
/// 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);
}
};
/// Emit a systemc.signal.write operation by using the explicit 'write' member
/// function of the signal and port classes.
struct SignalWriteEmitter : OpEmissionPattern<SignalWriteOp> {
@ -217,16 +195,8 @@ struct BindPortEmitter : OpEmissionPattern<BindPortOp> {
using OpEmissionPattern::OpEmissionPattern;
void emitStatement(BindPortOp op, EmissionPrinter &p) override {
auto instEmitter = p.getInlinable(op.getInstance());
bool parenthesize = instEmitter.getPrecedence() > Precedence::MEMBER_ACCESS;
if (parenthesize)
p << "(";
instEmitter.emit();
if (parenthesize)
p << ")";
p.getInlinable(op.getInstance())
.emitWithParensOnLowerPrecedence(Precedence::MEMBER_ACCESS);
p << "." << op.getPortName() << "(";
p.getInlinable(op.getChannel()).emit();
@ -239,12 +209,11 @@ struct AssignEmitter : OpEmissionPattern<AssignOp> {
using OpEmissionPattern::OpEmissionPattern;
void emitStatement(AssignOp op, EmissionPrinter &p) override {
auto sourceEmitter = p.getInlinable(op.getSource());
auto destEmitter = p.getInlinable(op.getDest());
parenthesizeOnLowerPrecedence(destEmitter, Precedence::ASSIGN, p);
p.getInlinable(op.getDest())
.emitWithParensOnLowerPrecedence(Precedence::ASSIGN);
p << " = ";
parenthesizeOnLowerPrecedence(sourceEmitter, Precedence::ASSIGN, p);
p.getInlinable(op.getSource())
.emitWithParensOnLowerPrecedence(Precedence::ASSIGN);
p << ";\n";
}
};
@ -269,8 +238,8 @@ struct VariableEmitter : OpEmissionPattern<VariableOp> {
if (op.getInit()) {
p << " = ";
auto initEmitter = p.getInlinable(op.getInit());
parenthesizeOnLowerPrecedence(initEmitter, Precedence::ASSIGN, p);
p.getInlinable(op.getInit())
.emitWithParensOnLowerPrecedence(Precedence::ASSIGN);
}
p << ";\n";
@ -326,10 +295,10 @@ struct DynIntegerTypeEmitter : public TypeEmissionPattern<Ty> {
void circt::ExportSystemC::populateSystemCOpEmitters(
OpEmissionPatternSet &patterns, MLIRContext *context) {
patterns.add<BuiltinModuleEmitter, SCModuleEmitter, SignalWriteEmitter,
SignalReadEmitter, CtorEmitter, SCFuncEmitter, MethodEmitter,
ThreadEmitter, SignalEmitter, InstanceDeclEmitter,
BindPortEmitter, AssignEmitter, VariableEmitter>(context);
patterns.add<SCModuleEmitter, SignalWriteEmitter, SignalReadEmitter,
CtorEmitter, SCFuncEmitter, MethodEmitter, ThreadEmitter,
SignalEmitter, InstanceDeclEmitter, BindPortEmitter,
AssignEmitter, VariableEmitter>(context);
}
void circt::ExportSystemC::populateSystemCTypeEmitters(

View File

@ -15,6 +15,7 @@
#ifndef CIRCT_TARGET_EXPORTSYSTEMC_REGISTERALLEMITTERS_H
#define CIRCT_TARGET_EXPORTSYSTEMC_REGISTERALLEMITTERS_H
#include "Patterns/BuiltinEmissionPatterns.h"
#include "Patterns/EmitCEmissionPatterns.h"
#include "Patterns/HWEmissionPatterns.h"
#include "Patterns/SystemCEmissionPatterns.h"
@ -25,6 +26,7 @@ namespace ExportSystemC {
/// Collects the operation emission patterns of all supported dialects.
inline void registerAllOpEmitters(OpEmissionPatternSet &patterns,
MLIRContext *context) {
populateBuiltinOpEmitters(patterns, context);
populateHWEmitters(patterns, context);
populateSystemCOpEmitters(patterns, context);
populateEmitCOpEmitters(patterns, context);
@ -32,8 +34,14 @@ inline void registerAllOpEmitters(OpEmissionPatternSet &patterns,
/// Collects the type emission patterns of all supported dialects.
inline void registerAllTypeEmitters(TypeEmissionPatternSet &patterns) {
populateHWTypeEmitters(patterns);
populateBuiltinTypeEmitters(patterns);
populateSystemCTypeEmitters(patterns);
populateEmitCTypeEmitters(patterns);
}
inline void registerAllAttrEmitters(AttrEmissionPatternSet &patterns) {
populateBuiltinAttrEmitters(patterns);
populateEmitCAttrEmitters(patterns);
}
} // namespace ExportSystemC

View File

@ -133,4 +133,70 @@ systemc.module @systemCTypes (%p0: !systemc.in<!systemc.int_base>,
%p11: !systemc.in<!systemc.lv<1024>>,
%p12: !systemc.in<!systemc.logic>) {}
// CHECK-LABEL: SC_MODULE(emitcEmission)
systemc.module @emitcEmission () {
// CHECK: SC_CTOR
systemc.ctor {
// Test: emitc.constant
// CHECK-NEXT: int five = 5;
%0 = "emitc.constant"() {value = #emitc.opaque<"5"> : !emitc.opaque<"int">} : () -> !emitc.opaque<"int">
%five = systemc.cpp.variable %0 : !emitc.opaque<"int">
// Test: emitc.apply "&" without having to emit parentheses
// CHECK-NEXT: int* fiveptr = &five;
%1 = emitc.apply "&"(%five) : (!emitc.opaque<"int">) -> !emitc.ptr<!emitc.opaque<"int">>
%fiveptr = systemc.cpp.variable %1: !emitc.ptr<!emitc.opaque<"int">>
// Test: emitc.apply "&" with parentheses to conform to the precedence rules
// TODO: add this test-case once we have support for an inlinable operation that has lower precedence
// Test: emitc.apply "*" without having to emit parentheses
// CHECK-NEXT: int fivederef = *fiveptr;
%2 = emitc.apply "*"(%fiveptr) : (!emitc.ptr<!emitc.opaque<"int">>) -> !emitc.opaque<"int">
%fivederef = systemc.cpp.variable %2: !emitc.opaque<"int">
// Test: emitc.apply "*" with parentheses to conform to the precedence rules
// TODO: add this test-case once we have support for an inlinable operation that has lower precedence
// Test: emit.call without a result is emitted as a statement, having operands and attribute arguments
// CHECK-NEXT: printf("result: %d, %d\0A", five, 6);
emitc.call "printf" (%five) {args=["result: %d, %d\n", 0 : index, 6 : i32]} : (!emitc.opaque<"int">) -> ()
// Test: emit.call without a result is emitted as a statement and having attribute arguments only
// CHECK-NEXT: printf("result: %d\0A", 6);
emitc.call "printf" () {args=["result: %d\n", 6 : i32]} : () -> ()
// Test: emit.call without a result is emitted as a statement, no operands, no attribute arguments
// CHECK-NEXT: printf();
emitc.call "printf" () : () -> ()
// Test: emitc.call with a result is inlined properly, having operands only
// CHECK-NEXT: void* v0 = malloc(4);
%3 = hw.constant 4 : i32
%4 = emitc.call "malloc" (%3) : (i32) -> !emitc.ptr<!emitc.opaque<"void">>
%v0 = systemc.cpp.variable %4 : !emitc.ptr<!emitc.opaque<"void">>
// Test: emitc.call with a result is inlined properly, attribute arguments only
// CHECK-NEXT: void* v1 = malloc(4);
%5 = emitc.call "malloc" () {args=[4 : i32]}: () -> !emitc.ptr<!emitc.opaque<"void">>
%v1 = systemc.cpp.variable %5 : !emitc.ptr<!emitc.opaque<"void">>
// Test: emitc.call properly inserts parentheses when an argument has COMMA precedence
// TODO: no operation having COMMA precedence supported yet
// Test: emit.cast adds parentheses around the operand when it has higher precedence than the operand
// TODO: no applicable operation having lower precedence than CAST supported yet
// Test: emit.cast does not add parentheses around the operand when it has lower precedence than the operand
// CHECK-NEXT: int* v2 = (int*) malloc(4);
%6 = emitc.call "malloc" () {args=[4 : i32]} : () -> !emitc.ptr<!emitc.opaque<"void">>
%7 = emitc.cast %6 : !emitc.ptr<!emitc.opaque<"void">> to !emitc.ptr<!emitc.opaque<"int">>
%v2 = systemc.cpp.variable %7 : !emitc.ptr<!emitc.opaque<"int">>
// Test: index -> size_t
// CHECK-NEXT: size_t idx;
%idx = systemc.cpp.variable : index
}
}
// CHECK: #endif // STDOUT_H