Skip to content

Commit

Permalink
Add vtrace_fmt! function, which is like trace_fmt! but takes a ve…
Browse files Browse the repository at this point in the history
…rbosity argument.

This satisfies part of #1299, the other parts being severity logging functions that lower to Verilog, and a way to specify the severity of an assert.

PiperOrigin-RevId: 694482502
  • Loading branch information
richmckeever authored and copybara-github committed Nov 8, 2024
1 parent a5ec3a1 commit 0af7633
Show file tree
Hide file tree
Showing 23 changed files with 482 additions and 45 deletions.
28 changes: 19 additions & 9 deletions xls/dslx/frontend/ast.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1224,11 +1224,13 @@ std::string AllOnesMacro::ToStringInternal() const {

FormatMacro::FormatMacro(Module* owner, Span span, std::string macro,
std::vector<FormatStep> format,
std::vector<Expr*> args)
std::vector<Expr*> args,
std::optional<Expr*> verbosity)
: Expr(owner, std::move(span)),
macro_(std::move(macro)),
format_(std::move(format)),
args_(std::move(args)) {}
args_(std::move(args)),
verbosity_(std::move(verbosity)) {}

FormatMacro::~FormatMacro() = default;

Expand All @@ -1242,18 +1244,26 @@ std::vector<AstNode*> FormatMacro::GetChildren(bool want_types) const {
}

std::string FormatMacro::ToStringInternal() const {
std::string format_string = "\"";
std::string result = absl::StrCat(macro_, "(");
if (verbosity_.has_value()) {
absl::StrAppendFormat(&result, "%s, ", (*verbosity_)->ToString());
}
absl::StrAppend(&result, "\"");
for (const auto& step : format_) {
if (std::holds_alternative<std::string>(step)) {
absl::StrAppend(&format_string, std::get<std::string>(step));
absl::StrAppend(&result, std::get<std::string>(step));
} else {
absl::StrAppend(&format_string,
std::string(FormatPreferenceToXlsSpecifier(
std::get<FormatPreference>(step))));
absl::StrAppend(&result, std::string(FormatPreferenceToXlsSpecifier(
std::get<FormatPreference>(step))));
}
}
absl::StrAppend(&format_string, "\"");
return absl::StrFormat("%s(%s, %s)", macro_, format_string, FormatArgs());
absl::StrAppend(&result, "\"");
const std::string args = FormatArgs();
if (!args.empty()) {
absl::StrAppendFormat(&result, ", %s", args);
}
absl::StrAppend(&result, ")");
return result;
}

std::string FormatMacro::FormatArgs() const {
Expand Down
5 changes: 4 additions & 1 deletion xls/dslx/frontend/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -2044,7 +2044,8 @@ class ConstAssert : public AstNode {
class FormatMacro : public Expr {
public:
FormatMacro(Module* owner, Span span, std::string macro,
std::vector<FormatStep> format, std::vector<Expr*> args);
std::vector<FormatStep> format, std::vector<Expr*> args,
std::optional<Expr*> verbosity = std::nullopt);

~FormatMacro() override;

Expand All @@ -2065,6 +2066,7 @@ class FormatMacro : public Expr {
const std::string& macro() const { return macro_; }
absl::Span<Expr* const> args() const { return args_; }
absl::Span<const FormatStep> format() const { return format_; }
std::optional<Expr*> verbosity() const { return verbosity_; }

Precedence GetPrecedenceWithoutParens() const final {
return Precedence::kStrongest;
Expand All @@ -2076,6 +2078,7 @@ class FormatMacro : public Expr {
std::string macro_;
std::vector<FormatStep> format_;
std::vector<Expr*> args_;
std::optional<Expr*> verbosity_;
};

// Represents a call to a parametric "make a zero value" macro;
Expand Down
1 change: 1 addition & 0 deletions xls/dslx/frontend/builtins_metadata.cc
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ const absl::flat_hash_map<std::string, BuiltinsData>& GetParametricBuiltins() {
{"all_ones!", {.signature = "() -> T", .is_ast_node = true}},
{"zero!", {.signature = "() -> T", .is_ast_node = true}},
{"trace_fmt!", {.signature = "(T) -> T", .is_ast_node = true}},
{"vtrace_fmt!", {.signature = "(u32, T) -> T", .is_ast_node = true}},

{"update",
{.signature = "(T[...], uN[M]|(uN[M], ...), T) -> T[...]",
Expand Down
87 changes: 58 additions & 29 deletions xls/dslx/frontend/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/substitute.h"
#include "absl/types/span.h"
#include "absl/types/variant.h"
#include "xls/common/casts.h"
Expand Down Expand Up @@ -2085,43 +2086,71 @@ absl::StatusOr<Expr*> Parser::ParseTerm(Bindings& outer_bindings,
}
}

absl::StatusOr<Expr*> Parser::BuildFormatMacroWithVerbosityArgument(
const Span& span, std::string_view name, std::vector<Expr*> args,
const std::vector<ExprOrType>& parametrics) {
if (args.size() < 2) {
return ParseErrorStatus(
span,
absl::Substitute("$0 macro must have at least 2 arguments.", name));
}
// Extract the verbosity argument and pass on the remainder of the arguments.
Expr* verbosity = args[0];
args.erase(args.begin());
return BuildFormatMacro(span, name, std::move(args), parametrics, verbosity);
}

absl::StatusOr<Expr*> Parser::BuildFormatMacro(
const Span& span, std::string_view name, std::vector<Expr*> args,
const std::vector<ExprOrType>& parametrics,
std::optional<Expr*> verbosity) {
if (!parametrics.empty()) {
return ParseErrorStatus(
span, absl::Substitute("$0 macro does not expect parametric arguments.",
name));
}

if (args.empty()) {
return ParseErrorStatus(
span,
absl::Substitute("$0 macro must have at least 1 argument.", name));
}

Expr* format_arg = args[0];
String* format_string = dynamic_cast<String*>(format_arg);
if (!format_string) {
return ParseErrorStatus(
span, absl::Substitute("Expected a literal format string; got `$0`",
format_arg->ToString()));
}

const std::string& format_text = format_string->text();
absl::StatusOr<std::vector<FormatStep>> format_result =
ParseFormatString(format_text);
if (!format_result.ok()) {
return ParseErrorStatus(format_string->span(),
format_result.status().message());
}

// Remove the format string argument before building the macro call.
args.erase(args.begin());
return module_->Make<FormatMacro>(span, std::string(name), *format_result,
args, verbosity);
}

absl::StatusOr<Expr*> Parser::BuildMacroOrInvocation(
const Span& span, Bindings& bindings, Expr* callee, std::vector<Expr*> args,
std::vector<ExprOrType> parametrics) {
if (auto* name_ref = dynamic_cast<NameRef*>(callee)) {
if (auto* builtin = TryGet<BuiltinNameDef*>(name_ref->name_def())) {
std::string name = builtin->identifier();
if (name == "trace_fmt!") {
if (!parametrics.empty()) {
return ParseErrorStatus(
span, absl::StrFormat(
"%s macro does not take parametric arguments", name));
}
if (args.empty()) {
return ParseErrorStatus(
span, absl::StrFormat("%s macro must have at least one argument",
name));
}

Expr* format_arg = args[0];
if (String* format_string = dynamic_cast<String*>(format_arg)) {
const std::string& format_text = format_string->text();
absl::StatusOr<std::vector<FormatStep>> format_result =
ParseFormatString(format_text);
if (!format_result.ok()) {
return ParseErrorStatus(format_string->span(),
format_result.status().message());
}
// Remove the format string argument before building the macro call.
args.erase(args.begin());
return module_->Make<FormatMacro>(span, name, format_result.value(),
args);
}
return BuildFormatMacro(span, name, args, parametrics);
}

return ParseErrorStatus(
span, absl::StrFormat("The first argument of the %s macro must "
"be a literal string.",
name));
if (name == "vtrace_fmt!") {
return BuildFormatMacroWithVerbosityArgument(span, name, args,
parametrics);
}

if (name == "zero!") {
Expand Down
15 changes: 15 additions & 0 deletions xls/dslx/frontend/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,21 @@ class Parser : public TokenParser {
std::vector<Expr*> args,
std::vector<ExprOrType> parametrics = std::vector<ExprOrType>{});

// Helper function that builds a `FormatMacro` corresponding to a DSLX
// invocation with verbosity, like vtrace_fmt!(...). The verbosity should be
// the first element of `args`.
absl::StatusOr<Expr*> BuildFormatMacroWithVerbosityArgument(
const Span& span, std::string_view name, std::vector<Expr*> args,
const std::vector<ExprOrType>& parametrics);

// Helper function that builds a `FormatMacro` corresponding to a DSLX
// invocation like trace_fmt!(...) or vtrace_fmt!(...), after the caller has
// extracted and removed any verbosity argument from `args`.
absl::StatusOr<Expr*> BuildFormatMacro(
const Span& span, std::string_view name, std::vector<Expr*> args,
const std::vector<ExprOrType>& parametrics,
std::optional<Expr*> verbosity = std::nullopt);

// Parses a proc config function.
//
// Args:
Expand Down
27 changes: 27 additions & 0 deletions xls/dslx/frontend/parser_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,33 @@ TEST_F(ParserTest, ParseSimpleProc) {
EXPECT_EQ(p->ToString(), text);
}

TEST_F(ParserTest, TraceFmtNoArgs) {
RoundTrip(R"(fn main() {
trace_fmt!("Hello world!");
})");
}

TEST_F(ParserTest, TraceFmtWithArgs) {
RoundTrip(R"(fn main() {
let foo = u32:2;
trace_fmt!("Hello {} {:x}!", "world", foo + u32:1);
})");
}

TEST_F(ParserTest, VTraceFmtNoArgs) {
RoundTrip(R"(fn main() {
vtrace_fmt!(3, "Hello world!");
})");
}

TEST_F(ParserTest, VTraceFmtWithArgs) {
RoundTrip(R"(fn main() {
const VERBOSITY = u32:4;
let foo = u32:3;
vtrace_fmt!(VERBOSITY + u32:1, "Hello {} {:x}!", "world", foo + u32:1);
})");
}

TEST_F(ParserTest, ParseProcWithConst) {
RoundTrip(R"(proc simple {
const MAX_X = u32:10;
Expand Down
3 changes: 2 additions & 1 deletion xls/dslx/ir_convert/convert_format_macro.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ absl::StatusOr<BValue> ConvertFormatMacro(const FormatMacro& node,
const BValue& entry_token,
const BValue& control_predicate,
absl::Span<const BValue> arg_vals,
int64_t verbosity,
const TypeInfo& current_type_info,
BuilderBase& function_builder) {
// This is the data we build up for the final "trace" operation we place in
Expand Down Expand Up @@ -192,7 +193,7 @@ absl::StatusOr<BValue> ConvertFormatMacro(const FormatMacro& node,
}

return function_builder.Trace(entry_token, control_predicate, ir_args,
fmt_steps);
fmt_steps, verbosity);
}

} // namespace xls::dslx
4 changes: 4 additions & 0 deletions xls/dslx/ir_convert/convert_format_macro.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#ifndef XLS_DSLX_IR_CONVERT_CONVERT_FORMAT_MACRO_H_
#define XLS_DSLX_IR_CONVERT_CONVERT_FORMAT_MACRO_H_

#include <cstdint>

#include "absl/status/statusor.h"
#include "absl/types/span.h"
#include "xls/dslx/frontend/ast.h"
Expand All @@ -35,6 +37,7 @@ namespace xls::dslx {
// control_predicate: The predicate that says whether the trace operation
// should execute.
// arg_vals: Evaluated IR values for the argument expressions (to the node).
// verbosity: The arbitrary integer verbosity level for the message.
// current_type_info: Type information that contains the node.
// function_builder: Function builder that we should place the IR trace
// operation on.
Expand All @@ -44,6 +47,7 @@ absl::StatusOr<BValue> ConvertFormatMacro(const FormatMacro& node,
const BValue& entry_token,
const BValue& control_predicate,
absl::Span<const BValue> arg_vals,
int64_t verbosity,
const TypeInfo& current_type_info,
BuilderBase& function_builder);

Expand Down
30 changes: 28 additions & 2 deletions xls/dslx/ir_convert/function_converter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/strings/substitute.h"
#include "absl/types/span.h"
#include "absl/types/variant.h"
#include "xls/common/casts.h"
Expand Down Expand Up @@ -1972,11 +1973,36 @@ absl::Status FunctionConverter::HandleFormatMacro(const FormatMacro* node) {
args.push_back(argval);
}

int64_t verbosity = 0;
if (node->verbosity().has_value()) {
absl::StatusOr<InterpValue> verbosity_interp_value =
ConstexprEvaluator::EvaluateToValue(
import_data_, current_type_info_, kNoWarningCollector,
ParametricEnv(parametric_env_map_), *node->verbosity(), nullptr);
if (!verbosity_interp_value.ok()) {
return IrConversionErrorStatus(
(*node->verbosity())->span(),
absl::Substitute("$0 verbosity values must be compile-time "
"constants; got `$1`.",
node->macro(), (*node->verbosity())->ToString()),
file_table());
}
absl::StatusOr<int64_t> verbosity_value =
verbosity_interp_value->GetBitValueSigned();
if (!verbosity_value.ok()) {
return IrConversionErrorStatus(
(*node->verbosity())->span(),
absl::Substitute("$0 verbosity values must be integers; got `$1`.",
node->macro(), (*node->verbosity())->ToString()),
file_table());
}
verbosity = *verbosity_value;
}
XLS_ASSIGN_OR_RETURN(
BValue trace_result_token,
ConvertFormatMacro(*node, implicit_token_data_->entry_token,
control_predicate, args, *current_type_info_,
*function_builder_));
control_predicate, args, verbosity,
*current_type_info_, *function_builder_));

implicit_token_data_->control_tokens.push_back(trace_result_token);

Expand Down
47 changes: 47 additions & 0 deletions xls/dslx/ir_convert/ir_converter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2867,6 +2867,53 @@ TEST(IrConverterTest, TraceFmt) {
ExpectIr(converted, TestName());
}

TEST(IrConverterTest, TraceFmtSimpleNoArgs) {
constexpr std::string_view kProgram = R"(fn main() {
trace_fmt!("Hello world!");
})";

XLS_ASSERT_OK_AND_ASSIGN(
std::string converted,
ConvertModuleForTest(kProgram, ConvertOptions{.emit_positions = false}));
ExpectIr(converted, TestName());
}

TEST(IrConverterTest, TraceFmtSimpleWithArgs) {
constexpr std::string_view kProgram = R"(fn main() {
let foo = u32:2;
trace_fmt!("Hello {} {:x}!", "world", foo + u32:1);
})";

XLS_ASSERT_OK_AND_ASSIGN(
std::string converted,
ConvertModuleForTest(kProgram, ConvertOptions{.emit_positions = false}));
ExpectIr(converted, TestName());
}

TEST(IrConverterTest, VTraceFmtSimpleNoArgs) {
constexpr std::string_view kProgram = R"(fn main() {
vtrace_fmt!(u32:3, "Hello world!");
})";

XLS_ASSERT_OK_AND_ASSIGN(
std::string converted,
ConvertModuleForTest(kProgram, ConvertOptions{.emit_positions = false}));
ExpectIr(converted, TestName());
}

TEST(IrConverterTest, VTraceFmtSimpleWithArgs) {
constexpr std::string_view kProgram = R"(fn main() {
const VERBOSITY = u32:4;
let foo = u32:3;
vtrace_fmt!(VERBOSITY + u32:1, "Hello {} {:x}!", "world", foo + u32:1);
})";

XLS_ASSERT_OK_AND_ASSIGN(
std::string converted,
ConvertModuleForTest(kProgram, ConvertOptions{.emit_positions = false}));
ExpectIr(converted, TestName());
}

TEST(IrConverterTest, WideningCastsUnErrors) {
constexpr std::string_view kProgram =
R"(
Expand Down
Loading

0 comments on commit 0af7633

Please sign in to comment.