Skip to content
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
35 changes: 35 additions & 0 deletions include/circt/Dialect/Moore/MooreOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1645,6 +1645,17 @@ def FormatLiteralOp : MooreOp<"fmt.literal", [Pure]> {
let assemblyFormat = "$literal attr-dict";
}

def FormatStringToStringOp : MooreOp<"fstring_to_string", [Pure]> {
let summary = "A dynamic string created through formatting";
let description = [{
Creates a dynamic string from a format string.
Translates into a no-op.
}];
let arguments = (ins FormatStringType:$fmtstring);
let results = (outs StringType:$result);
let assemblyFormat = "$fmtstring attr-dict";
}

def FormatConcatOp : MooreOp<"fmt.concat", [Pure]> {
let summary = "Concatenate string fragments";
let description = [{
Expand Down Expand Up @@ -1755,6 +1766,30 @@ def FormatTimeOp : MooreOp<"fmt.time", [Pure]> {
}];
}

def FormatStringOp : MooreOp<"fmt.string", [Pure]> {
let summary = "A dynamic string fragment";
let description = [{
Creates a dynamic string fragment to be used as a format string. The
string is printed as is, without any further escaping or processing of its
characters.

Use fmt.literal over this operator for any constant strings / literals.
}];
let arguments = (ins
StringType:$string,
OptionalAttr<I32Attr>:$width,
OptionalAttr<IntAlignAttr>:$alignment,
OptionalAttr<IntPaddingAttr>:$padding);
let results = (outs FormatStringType:$result);
let assemblyFormat = [{
$string
(`,` `width` $width^)?
(`,` `alignment` $alignment^)?
(`,` `padding` $padding^)?
attr-dict
}];
}

//===----------------------------------------------------------------------===//
// Builtin System Tasks and Functions
//===----------------------------------------------------------------------===//
Expand Down
27 changes: 27 additions & 0 deletions lib/Conversion/ImportVerilog/Expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,21 @@ struct RvalueExprVisitor : public ExprVisitor {
FailureOr<Value> result;
Value value;

// $sformatf() and $sformat look like system tasks, but we handle string
// formatting differently from expression evaluation, so handle them
// separately.
// According to IEEE 1800-2023 Section 21.3.3 "Formatting data to a
// string" $sformatf works just like the string formatting but returns
// a StringType.
if (!subroutine.name.compare("$sformatf")) {
// Create the FormatString
auto fmtValue = context.convertFormatString(
expr.arguments(), loc, moore::IntFormat::Decimal, false);
if (failed(fmtValue))
return {};
return fmtValue.value();
}

switch (args.size()) {
case (0):
result = context.convertSystemCallArity0(subroutine, loc);
Expand Down Expand Up @@ -1681,6 +1696,18 @@ Value Context::materializeConversion(Type type, Value value, bool isSigned,
return value;
}

// Convert from FormatStringType to StringType
if (isa<moore::StringType>(type) &&
isa<moore::FormatStringType>(value.getType())) {
return builder.createOrFold<moore::FormatStringToStringOp>(loc, value);
}

// Convert from StringType to FormatStringType
if (isa<moore::FormatStringType>(type) &&
isa<moore::StringType>(value.getType())) {
return builder.createOrFold<moore::FormatStringOp>(loc, value);
}

// TODO: Handle other conversions with dedicated ops.
if (value.getType() != type)
value = moore::ConversionOp::create(builder, loc, type, value);
Expand Down
34 changes: 24 additions & 10 deletions lib/Conversion/ImportVerilog/FormatStrings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,16 +139,7 @@ struct FormatStringParser {
return emitTime(arg, options);

case 's':
// Simplified handling for literals.
if (auto *lit = arg.as_if<slang::ast::StringLiteral>()) {
if (options.width)
return mlir::emitError(loc)
<< "string format specifier with width not supported";
emitLiteral(lit->getValue());
return success();
}
return mlir::emitError(argLoc)
<< "expression cannot be formatted as string";
return emitString(arg, options);

default:
return mlir::emitError(loc)
Expand Down Expand Up @@ -240,6 +231,29 @@ struct FormatStringParser {
return success();
}

LogicalResult emitString(const slang::ast::Expression &arg,
const FormatOptions &options) {
if (options.width)
return mlir::emitError(loc)
<< "string format specifier with width not supported";

// Simplified handling for literals.
if (auto *lit = arg.as_if<slang::ast::StringLiteral>()) {
emitLiteral(lit->getValue());
return success();
}

// Handle expressions
if (auto value = context.convertRvalueExpression(
arg, builder.getType<moore::FormatStringType>())) {
fragments.push_back(value);
return success();
}

return mlir::emitError(context.convertLocation(arg.sourceRange))
<< "expression cannot be formatted as string";
}

/// Emit an expression argument with the appropriate default formatting.
LogicalResult emitDefault(const slang::ast::Expression &expr) {
FormatOptions options;
Expand Down
40 changes: 40 additions & 0 deletions lib/Conversion/ImportVerilog/Statements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,46 @@ struct StmtVisitor {
if (handled == true)
return success();
}

// According to IEEE 1800-2023 Section 21.3.3 "Formatting data to a
// string" the first argument of $sformat is its output; the other
// arguments work like a FormatString.
// In Moore we only support writing to a location if it is a reference;
// However, Section 21.3.3 explains that the output of $sformat is
// assigned as if it were cast from a string literal (Section 5.9),
// so this implementation casts the string to the target value.
if (!call->getSubroutineName().compare("$sformat")) {

// Use the first argument as the output location
auto *lhsExpr = call->arguments().front();
// Format the second and all later arguments as a string
auto fmtValue =
context.convertFormatString(call->arguments().subspan(1), loc,
moore::IntFormat::Decimal, false);
if (failed(fmtValue))
return failure();
// Convert the FormatString to a StringType
auto strValue = moore::FormatStringToStringOp::create(builder, loc,
fmtValue.value());
// The Slang AST produces a `AssignmentExpression` for the first
// argument; the RHS of this expression is invalid though
// (`EmptyArgument`), so we only use the LHS of the
// `AssignmentExpression` and plug in the formatted string for the RHS.
if (auto assignExpr =
lhsExpr->as_if<slang::ast::AssignmentExpression>()) {
auto lhs = context.convertLvalueExpression(assignExpr->left());
if (!lhs)
return failure();

auto convertedValue = context.materializeConversion(
cast<moore::RefType>(lhs.getType()).getNestedType(), strValue,
false, loc);
moore::BlockingAssignOp::create(builder, loc, lhs, convertedValue);
return success();
} else {
return failure();
}
Comment on lines +186 to +203
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

}
}

auto value = context.convertRvalueExpression(stmt.expr);
Expand Down
32 changes: 32 additions & 0 deletions test/Conversion/ImportVerilog/basic.sv
Original file line number Diff line number Diff line change
Expand Up @@ -3213,3 +3213,35 @@ function void StructCreateConversion (logic [7:0][7:0] array, logic [63:0] immed
testStruct ts2 = '{structField: array};

endfunction

// CHECK-LABEL: func.func private @ConcatSformatf(
// CHECK-SAME: [[STR1:%[^,]+]]: !moore.string
// CHECK-SAME: [[STR2:%[^,]+]]: !moore.string
// CHECK-SAME: [[STR3:%[^,]+]]: !moore.ref<string>
function automatic void ConcatSformatf(string testStr, string otherString, ref string outputString);
// CHECK: [[LV:%.+]] = moore.variable : <l64>
logic [63:0] logicVector;
// CHECK: [[FMTSTR1:%.+]] = moore.fmt.string [[STR1]]
// CHECK-NEXT: [[SPC:%.+]] = moore.fmt.literal " "
// CHECK-NEXT: [[FMTSTR2:%.+]] = moore.fmt.string [[STR2]]
// CHECK-NEXT: [[CONCAT:%.+]] = moore.fmt.concat ([[FMTSTR1]], [[SPC]], [[FMTSTR2]])
// CHECK-NEXT: [[STROUT:%.+]] = moore.fstring_to_string [[CONCAT]]
string test = $sformatf("%s %s", testStr, otherString);

// CHECK: [[FMTSTR3:%.+]] = moore.fmt.string [[STR1]]
// CHECK-NEXT: [[SPC2:%.+]] = moore.fmt.literal " "
// CHECK-NEXT: [[FMTSTR4:%.+]] = moore.fmt.string [[STR2]]
// CHECK-NEXT: [[CONCAT2:%.+]] = moore.fmt.concat ([[FMTSTR3]], [[SPC2]], [[FMTSTR4]])
// CHECK-NEXT: [[STROUT2:%.+]] = moore.fstring_to_string [[CONCAT2]]
// CHECK-NEXT: moore.blocking_assign [[STR3]], [[STROUT2]] : string
$sformat(outputString, "%s %s", testStr, otherString);

// CHECK: [[FMTSTR5:%.+]] = moore.fmt.string [[STR1]]
// CHECK-NEXT: [[SPC3:%.+]] = moore.fmt.literal " "
// CHECK-NEXT: [[FMTSTR6:%.+]] = moore.fmt.string [[STR2]]
// CHECK-NEXT: [[CONCAT3:%.+]] = moore.fmt.concat ([[FMTSTR5]], [[SPC3]], [[FMTSTR6]])
// CHECK-NEXT: [[STROUT3:%.+]] = moore.fstring_to_string [[CONCAT3]]
// CHECK-NEXT: [[CONV:%.+]] = moore.conversion [[STROUT3]] : !moore.string -> !moore.l64
// CHECK-NEXT: moore.blocking_assign [[LV]], [[CONV]] : l64
$sformat(logicVector, "%s %s", testStr, otherString);
endfunction