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

[FIRRTL] Add FormatStringType #8342

Merged
merged 5 commits into from
Mar 25, 2025
Merged
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
24 changes: 24 additions & 0 deletions include/circt/Dialect/FIRRTL/FIRRTLExpressions.td
Original file line number Diff line number Diff line change
Expand Up @@ -1681,4 +1681,28 @@ def LTLClockIntrinsicOp : LTLIntrinsicOp<"clock"> {
}];
}

//===----------------------------------------------------------------------===//
// Format String Expression operations
//===----------------------------------------------------------------------===//

def TimeOp : FIRRTLOp<"fstring.time", [HasCustomSSAName, Pure]> {

let summary = "an operation that represents the current simulation time";
let description = [{
This operation represents the current simulation time. It returns a format
string type which can only be used as a substitution inside a printf. This
operation is not represented in the FIRRTL spec.

This operation is the FIRRTL Dialect representation of the special
substitution `{{SimulationTime}}`.
}];

let arguments = (ins);
let results = (outs FStringType:$result);
let assemblyFormat = [{
attr-dict `:` type($result)
}];

}

#endif // CIRCT_DIALECT_FIRRTL_FIRRTLEXPRESSIONS_TD
2 changes: 1 addition & 1 deletion include/circt/Dialect/FIRRTL/FIRRTLStatements.td
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def PrintFOp : FIRRTLOp<"printf"> {
let summary = "Formatted Print Statement";

let arguments = (ins ClockType:$clock, UInt1Type:$cond, StrAttr:$formatString,
Variadic<FIRRTLBaseType>:$substitutions, StrAttr:$name);
Variadic<PrintfOperandType>:$substitutions, StrAttr:$name);
let results = (outs);

let assemblyFormat = [{
Expand Down
3 changes: 2 additions & 1 deletion include/circt/Dialect/FIRRTL/FIRRTLTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ class PathType;
class BoolType;
class DoubleType;
class BaseTypeAliasType;
class FStringType;

/// A collection of bits indicating the recursive properties of a type.
struct RecursiveTypeProperties {
Expand Down Expand Up @@ -185,7 +186,7 @@ class FIRRTLBaseType
static bool classof(Type type) {
return llvm::isa<FIRRTLDialect>(type.getDialect()) &&
!llvm::isa<PropertyType, RefType, LHSType, OpenBundleType,
OpenVectorType>(type);
OpenVectorType, FStringType>(type);
}

/// Returns true if this is a non-const "passive" that which is not analog.
Expand Down
21 changes: 19 additions & 2 deletions include/circt/Dialect/FIRRTL/FIRRTLTypes.td
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ def PassiveType : FIRRTLDialectType<

def SizedType : FIRRTLDialectType<CPred<"type_isa<FIRRTLBaseType>($_self) && "
"!type_cast<FIRRTLBaseType>($_self).hasUninferredWidth()">,
"a sized type (contains no uninferred widths)",
"a sized type (contains no uninferred widths)",
"::circt::firrtl::FIRRTLBaseType">;
def SizedOrForeignType : AnyTypeOf<[SizedType, ForeignType]>;
def SizedPassiveType : FIRRTLDialectType<And<[SizedType.predicate,PassiveType.predicate]>,
"a sized passive base type (contains no uninferred widths, or flips)",
"a sized passive base type (contains no uninferred widths, or flips)",
"::circt::firrtl::FIRRTLBaseType">;
def SizedPassiveOrForeignType : AnyTypeOf<[SizedPassiveType, ForeignType]>;

Expand Down Expand Up @@ -236,4 +236,21 @@ def DoubleType : FIRRTLDialectTypeHelper<"DoubleType", "double type">,
def PathType : FIRRTLDialectTypeHelper<"PathType", "path type">,
BuildableType<"::circt::firrtl::PathType::get($_builder.getContext())">;

//===----------------------------------------------------------------------===//
// Format String Types
//===----------------------------------------------------------------------===//

def FStringType :
FIRRTLDialectTypeHelper<"FStringType",
"a format string type">,
BuildableType<"::circt::firrtl::FStringType::get($_builder.getContext())">;

// TODO: Migrate off of this by making the operands for `PrintfOp` use only
// `FStringType`. This requires conversion ops that convert from
// `FIRRTLBaseType` to `FStringType`.
def PrintfOperandType : FIRRTLDialectType<
CPred<"type_isa<FIRRTLBaseType, FStringType>($_self)">,
"a printf operand type (a FIRRTL base type or a format string type)",
"::circt::firrtl::FIRRTLType">;

#endif // CIRCT_DIALECT_FIRRTL_FIRRTLTYPES_TD
7 changes: 7 additions & 0 deletions include/circt/Dialect/FIRRTL/FIRRTLTypesImpl.td
Original file line number Diff line number Diff line change
Expand Up @@ -578,4 +578,11 @@ def DoubleImpl : PropImplType<"Double"> {
let typeName = "firrtl.propDouble";
}

def FStringImpl : FIRRTLImplType<"FString", [], "::circt::firrtl::FIRRTLType" > {
let summary = "A format string type";
let parameters = (ins);
let genStorageClass = true;
let typeName = "firrtl.fstring";
}

#endif // CIRCT_DIALECT_FIRRTL_FIRRTLTYPESIMPL_TD
12 changes: 8 additions & 4 deletions include/circt/Dialect/FIRRTL/FIRRTLVisitors.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ class ExprVisitor {
// Property expressions.
StringConstantOp, FIntegerConstantOp, BoolConstantOp,
DoubleConstantOp, ListCreateOp, ListConcatOp, UnresolvedPathOp,
PathOp, IntegerAddOp, IntegerMulOp, IntegerShrOp>(
[&](auto expr) -> ResultType {
return thisCast->visitExpr(expr, args...);
})
PathOp, IntegerAddOp, IntegerMulOp, IntegerShrOp,
// Format String expressions
TimeOp>([&](auto expr) -> ResultType {
return thisCast->visitExpr(expr, args...);
})
.Default([&](auto expr) -> ResultType {
return thisCast->visitInvalidExpr(op, args...);
});
Expand Down Expand Up @@ -225,6 +226,9 @@ class ExprVisitor {
HANDLE(IntegerAddOp, Unhandled);
HANDLE(IntegerMulOp, Unhandled);
HANDLE(IntegerShrOp, Unhandled);

// Format string expressions
HANDLE(TimeOp, Unhandled);
#undef HANDLE
};

Expand Down
70 changes: 61 additions & 9 deletions lib/Dialect/FIRRTL/Export/FIREmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ struct Emitter {
void emitExpression(ShlPrimOp op) { emitPrimExpr("shl", op, op.getAmount()); }
void emitExpression(ShrPrimOp op) { emitPrimExpr("shr", op, op.getAmount()); }

void emitExpression(TimeOp op){};

// Funnel all ops without attrs into `emitPrimExpr`.
#define HANDLE(OPTYPE, MNEMONIC) \
void emitExpression(OPTYPE op) { emitPrimExpr(MNEMONIC, op); }
Expand Down Expand Up @@ -372,7 +374,7 @@ struct Emitter {
SymbolTable symbolTable;
hw::InnerSymbolTableCollection istc;
hw::InnerRefNamespace irn{symbolTable, istc};
SymInfos(Operation *op) : symbolTable(op), istc(op) {};
SymInfos(Operation *op) : symbolTable(op), istc(op){};
};
std::optional<std::reference_wrapper<SymInfos>> symInfos;

Expand Down Expand Up @@ -834,8 +836,55 @@ void Emitter::emitStatement(PrintFOp op) {
ps << "," << PP::space;
emitExpression(op.getCond());
ps << "," << PP::space;
ps.writeQuotedEscaped(op.getFormatString());
for (auto operand : op.getSubstitutions()) {

// Replace the generic "{{}}" special substitutions with their attributes.
// E.g.:
//
// "hello {{}} world"(%time)
//
// Becomes:
//
// "hello {{SimulationTime}} world"
SmallString<64> formatString;
auto origFormatString = op.getFormatString();
SmallVector<Value, 4> substitutions;
for (size_t i = 0, e = origFormatString.size(), opIdx = 0; i != e; ++i) {
auto c = origFormatString[i];
switch (c) {
case '%': {
formatString.push_back(c);
c = origFormatString[++i];
switch (c) {
case 'b':
case 'c':
case 'd':
case 'x':
substitutions.push_back(op.getSubstitutions()[opIdx++]);
break;
default:
break;
}
formatString.push_back(c);
break;
}
case '{':
if (origFormatString.slice(i, i + 4) == "{{}}") {
formatString.append("{{");
if (isa<TimeOp>(op.getSubstitutions()[opIdx++].getDefiningOp()))
formatString.append("SimulationTime");
else
emitError(op, "unsupported fstring substitution type");
formatString.append("}}");
}
i += 3;
break;
default:
formatString.push_back(c);
}
}
ps.writeQuotedEscaped(formatString);

for (auto operand : substitutions) {
ps << "," << PP::space;
emitExpression(operand);
}
Expand Down Expand Up @@ -884,7 +933,8 @@ void Emitter::emitStatement(ConnectOp op) {
} else {
auto emitLHS = [&]() { emitExpression(op.getDest()); };
if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
emitAssignLike(emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
emitAssignLike(
emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
} else {
emitAssignLike(
emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
Expand All @@ -910,7 +960,8 @@ void Emitter::emitStatement(MatchingConnectOp op) {
} else {
auto emitLHS = [&]() { emitExpression(op.getDest()); };
if (op.getSrc().getDefiningOp<InvalidValueOp>()) {
emitAssignLike(emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
emitAssignLike(
emitLHS, [&]() { ps << "invalid"; }, PPExtString("is"));
} else {
emitAssignLike(
emitLHS, [&]() { emitExpression(op.getSrc()); }, PPExtString("<="));
Expand Down Expand Up @@ -1228,10 +1279,11 @@ void Emitter::emitExpression(Value value) {
FIntegerConstantOp, BoolConstantOp, DoubleConstantOp, ListCreateOp,
UnresolvedPathOp, GenericIntrinsicOp,
// Reference expressions
RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp>(
[&](auto op) {
ps.scopedBox(PP::ibox0, [&]() { emitExpression(op); });
})
RefSendOp, RefResolveOp, RefSubOp, RWProbeOp, RefCastOp,
// Format String expressions
TimeOp>([&](auto op) {
ps.scopedBox(PP::ibox0, [&]() { emitExpression(op); });
})
.Default([&](auto op) {
emitOpError(op, "not supported as expression");
ps << "<unsupported-expr-" << PPExtString(op->getName().stripDialect())
Expand Down
8 changes: 8 additions & 0 deletions lib/Dialect/FIRRTL/FIRRTLOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6304,6 +6304,14 @@ LayerBlockOp::verifySymbolUses(SymbolTableCollection &symbolTable) {
return success();
}

//===----------------------------------------------------------------------===//
// Format Sring operations
//===----------------------------------------------------------------------===//

void TimeOp::getAsmResultNames(OpAsmSetValueNameFn setNameFn) {
setNameFn(getResult(), "time");
}

//===----------------------------------------------------------------------===//
// TblGen Generated Logic.
//===----------------------------------------------------------------------===//
Expand Down
8 changes: 8 additions & 0 deletions lib/Dialect/FIRRTL/FIRRTLTypes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ static LogicalResult customTypePrinter(Type type, AsmPrinter &os) {
os << ">";
})
.Case<AnyRefType>([&](AnyRefType type) { os << "anyref"; })
.Case<FStringType>([&](auto) { os << "fstring"; })
.Default([&](auto) { anyFailed = true; });
return failure(anyFailed);
}
Expand Down Expand Up @@ -466,6 +467,9 @@ static OptionalParseResult customTypeParser(AsmParser &parser, StringRef name,
BaseTypeAliasType::get(StringAttr::get(context, name), type),
success();
}
if (name == "fstring") {
return result = FStringType::get(context), success();
}

return {};
}
Expand Down Expand Up @@ -675,6 +679,10 @@ RecursiveTypeProperties FIRRTLType::getRecursiveTypeProperties() const {
})
.Case<LHSType>(
[](auto type) { return type.getType().getRecursiveTypeProperties(); })
.Case<FStringType>([](auto type) {
return RecursiveTypeProperties{true, false, false, false,
false, false, false};
})
.Default([](Type) {
llvm_unreachable("unknown FIRRTL type");
return RecursiveTypeProperties{};
Expand Down
Loading