From 282eaeb87fca3dec0421a856af71005a22188001 Mon Sep 17 00:00:00 2001
From: David Plass
Date: Fri, 24 Apr 2026 12:12:34 -0700
Subject: [PATCH] [DSLX Fuzz Testing] Support constant arrays and tuples in
fuzz domains.
Extract handling logic for arrays, ranges and tuples into helper methods
that can be used for either literal or constants.
PiperOrigin-RevId: 905158685
---
xls/dslx/ir_convert/BUILD | 6 +
xls/dslx/ir_convert/function_converter.cc | 118 ++++
xls/dslx/ir_convert/function_converter.h | 23 +
.../ir_convert/function_converter_test.cc | 667 ++++++++++++++++++
4 files changed, 814 insertions(+)
diff --git a/xls/dslx/ir_convert/BUILD b/xls/dslx/ir_convert/BUILD
index 3b154dca43..5fec51320a 100644
--- a/xls/dslx/ir_convert/BUILD
+++ b/xls/dslx/ir_convert/BUILD
@@ -335,6 +335,7 @@ cc_library(
":ir_conversion_utils",
":proc_config_ir_converter",
":proc_scoped_channel_scope",
+ "//xls/common:attribute_data",
"//xls/common:visitor",
"//xls/common/status:ret_check",
"//xls/common/status:status_macros",
@@ -383,6 +384,7 @@ cc_library(
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
"@com_google_absl//absl/types:variant",
+ "@com_google_protobuf//:protobuf",
],
)
@@ -395,6 +397,7 @@ cc_test(
":convert_options",
":function_converter",
":test_utils",
+ "//xls/common:attribute_data",
"//xls/common:proto_test_utils",
"//xls/common:xls_gunit_main",
"//xls/common/status:matchers",
@@ -404,6 +407,9 @@ cc_test(
"//xls/dslx/frontend:ast",
"//xls/ir",
"//xls/ir:xls_ir_interface_cc_proto",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/types:span",
+ "@com_google_protobuf//:protobuf",
"@googletest//:gtest",
],
)
diff --git a/xls/dslx/ir_convert/function_converter.cc b/xls/dslx/ir_convert/function_converter.cc
index 72589a8acf..face09f464 100644
--- a/xls/dslx/ir_convert/function_converter.cc
+++ b/xls/dslx/ir_convert/function_converter.cc
@@ -40,6 +40,8 @@
#include "absl/strings/substitute.h"
#include "absl/types/span.h"
#include "absl/types/variant.h"
+#include "google/protobuf/text_format.h"
+#include "xls/common/attribute_data.h"
#include "xls/common/status/ret_check.h"
#include "xls/common/status/status_macros.h"
#include "xls/common/visitor.h"
@@ -3599,6 +3601,12 @@ absl::Status FunctionConverter::HandleFunction(
VLOG(5) << "Built function: " << ir_fn->name();
XLS_RETURN_IF_ERROR(VerifyFunction(ir_fn));
+ XLS_ASSIGN_OR_RETURN(std::optional fuzz_test_attr,
+ LowerFuzzTestDomains(node));
+ if (fuzz_test_attr.has_value()) {
+ ir_fn->AddAttribute(std::move(*fuzz_test_attr));
+ }
+
// If it's a public fallible function, or it's the entry function for the
// package, we make a wrapper so that the external world (e.g. JIT, verilog
// module) doesn't need to take implicit token arguments.
@@ -3624,6 +3632,116 @@ absl::Status FunctionConverter::HandleFunction(
return absl::OkStatus();
}
+absl::Status FunctionConverter::LowerDomainExpr(
+ Expr* expr, PackageInterfaceProto::FuzzTestDomain* proto) {
+ if (expr->kind() == AstNodeKind::kArray) {
+ return LowerArrayExpr(static_cast(expr), proto);
+ }
+ if (expr->kind() == AstNodeKind::kRange) {
+ Range* range_node = static_cast(expr);
+ return LowerRangeExpr(range_node, proto);
+ }
+ if (expr->kind() == AstNodeKind::kXlsTuple) {
+ return LowerTupleExpr(static_cast(expr), proto);
+ }
+ if (expr->kind() == AstNodeKind::kNameRef) {
+ NameRef* name_ref = static_cast(expr);
+
+ absl::StatusOr const_def =
+ module_->GetMemberOrError(name_ref->identifier());
+ if (const_def.ok()) {
+ Expr* value_expr = (*const_def)->value();
+ return LowerDomainExpr(value_expr, proto);
+ }
+ }
+ return absl::UnimplementedError(
+ absl::StrCat("Unsupported fuzztest domain type: ", expr->ToString()));
+}
+
+absl::Status FunctionConverter::LowerRangeExpr(
+ Range* range_node, PackageInterfaceProto::FuzzTestDomain* proto) {
+ XLS_ASSIGN_OR_RETURN(InterpValue min_val,
+ current_type_info_->GetConstExpr(range_node->start()));
+ XLS_ASSIGN_OR_RETURN(InterpValue max_val,
+ current_type_info_->GetConstExpr(range_node->end()));
+
+ XLS_ASSIGN_OR_RETURN(Value ir_min, InterpValueToValue(min_val));
+ XLS_ASSIGN_OR_RETURN(Value ir_max, InterpValueToValue(max_val));
+
+ XLS_ASSIGN_OR_RETURN(ValueProto min_proto, ir_min.AsProto());
+ XLS_ASSIGN_OR_RETURN(ValueProto max_proto, ir_max.AsProto());
+
+ auto* range_proto = proto->mutable_range();
+ *range_proto->mutable_min() = std::move(min_proto);
+ *range_proto->mutable_max() = std::move(max_proto);
+ return absl::OkStatus();
+}
+
+absl::Status FunctionConverter::LowerArrayExpr(
+ Array* array_node, PackageInterfaceProto::FuzzTestDomain* proto) {
+ auto* element_of_proto = proto->mutable_element_of();
+ for (Expr* member : array_node->members()) {
+ XLS_ASSIGN_OR_RETURN(InterpValue val,
+ current_type_info_->GetConstExpr(member));
+ XLS_ASSIGN_OR_RETURN(Value ir_val, InterpValueToValue(val));
+ XLS_ASSIGN_OR_RETURN(ValueProto val_proto, ir_val.AsProto());
+ *element_of_proto->add_values() = std::move(val_proto);
+ }
+ return absl::OkStatus();
+}
+
+absl::Status FunctionConverter::LowerTupleExpr(
+ XlsTuple* tuple_node, PackageInterfaceProto::FuzzTestDomain* proto) {
+ if (tuple_node->members().empty()) {
+ proto->set_arbitrary(true);
+ return absl::OkStatus();
+ }
+ auto* tuple_proto = proto->mutable_tuple();
+ for (Expr* member : tuple_node->members()) {
+ XLS_RETURN_IF_ERROR(LowerDomainExpr(member, tuple_proto->add_elements()));
+ }
+ return absl::OkStatus();
+}
+
+absl::StatusOr>
+FunctionConverter::LowerFuzzTestDomains(Function* node) {
+ if (node->parent() == nullptr ||
+ node->parent()->kind() != AstNodeKind::kFuzzTestFunction) {
+ return std::nullopt;
+ }
+ FuzzTestFunction* ft = static_cast(node->parent());
+
+ if (ft->domains().has_value()) {
+ XlsTuple* domains_tuple = *ft->domains();
+ // We use a dummy Function proto here solely to get the
+ // `parameter_domains` field name wrapper in the serialized text proto.
+ // This will allow clients to easily parse the string back into a Function
+ // proto and recover the domains therein.
+ PackageInterfaceProto::Function temp_func;
+
+ for (Expr* domain_expr : domains_tuple->members()) {
+ PackageInterfaceProto::FuzzTestDomain* domain_proto =
+ temp_func.add_parameter_domains();
+
+ XLS_RETURN_IF_ERROR(LowerDomainExpr(domain_expr, domain_proto));
+ }
+
+ std::string proto_str;
+ google::protobuf::TextFormat::Printer printer;
+ printer.SetSingleLineMode(true);
+ XLS_RET_CHECK(printer.PrintToString(temp_func, &proto_str));
+
+ std::vector args;
+ args.push_back(
+ AttributeData::StringKeyValueArgument{.first = "domains",
+ .second = std::move(proto_str),
+ .is_backticked = true});
+
+ return AttributeData(AttributeKind::kFuzzTest, std::move(args));
+ }
+ return std::nullopt;
+}
+
absl::Status FunctionConverter::HandleSpawn(const Spawn* node) {
VLOG(5) << "HandleSpawn: " << node->ToString();
if (!options_.lower_to_proc_scoped_channels) {
diff --git a/xls/dslx/ir_convert/function_converter.h b/xls/dslx/ir_convert/function_converter.h
index 2893df11a4..8616ac9997 100644
--- a/xls/dslx/ir_convert/function_converter.h
+++ b/xls/dslx/ir_convert/function_converter.h
@@ -31,6 +31,7 @@
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/types/span.h"
+#include "xls/common/attribute_data.h"
#include "xls/dslx/channel_direction.h"
#include "xls/dslx/frontend/ast.h"
#include "xls/dslx/frontend/pos.h"
@@ -710,6 +711,28 @@ class FunctionConverter {
absl::flat_hash_map state_write_called_by_state_name_;
std::vector> proc_def_instances_;
+
+ // If the function has a kFuzzTest attribute, this method will convert the
+ // fuzz test domains to proto and insert it into the AttributeData for
+ // storage in the IR.
+ absl::StatusOr> LowerFuzzTestDomains(
+ Function* node);
+
+ // Lowers the given expression into the given FuzzTestDomain proto.
+ absl::Status LowerDomainExpr(Expr* expr,
+ PackageInterfaceProto::FuzzTestDomain* proto);
+
+ // Lowers a DSLX Range expression to a FuzzTestDomain proto.
+ absl::Status LowerRangeExpr(Range* range_node,
+ PackageInterfaceProto::FuzzTestDomain* proto);
+
+ // Lowers a DSLX Array expression to a FuzzTestDomain proto (ElementOf).
+ absl::Status LowerArrayExpr(Array* array_node,
+ PackageInterfaceProto::FuzzTestDomain* proto);
+
+ // Lowers a DSLX Tuple expression to a FuzzTestDomain proto.
+ absl::Status LowerTupleExpr(XlsTuple* tuple_node,
+ PackageInterfaceProto::FuzzTestDomain* proto);
};
} // namespace xls::dslx
diff --git a/xls/dslx/ir_convert/function_converter_test.cc b/xls/dslx/ir_convert/function_converter_test.cc
index 72a556102b..ed830757f0 100644
--- a/xls/dslx/ir_convert/function_converter_test.cc
+++ b/xls/dslx/ir_convert/function_converter_test.cc
@@ -19,6 +19,9 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/types/span.h"
+#include "google/protobuf/text_format.h"
+#include "xls/common/attribute_data.h"
#include "xls/common/proto_test_utils.h"
#include "xls/common/status/matchers.h"
#include "xls/dslx/create_import_data.h"
@@ -579,5 +582,669 @@ TEST(FunctionConverterTest, ConvertsFunctionWithUpdate2DBuiltinEmptyTuple) {
)pb"));
}
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithBasicDomains) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+#[fuzz_test(domains = `u32:0..10, ()`)]
+fn f(x: u32, y: u32) -> u32 { x + y }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+
+ absl::Span attributes = ir_fn->attributes();
+ ASSERT_EQ(attributes.size(), 1);
+ const AttributeData::Argument& arg = attributes[0].args()[0];
+ const auto& skv = std::get(arg);
+
+ xls::PackageInterfaceProto::Function function_proto;
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(skv.second, &function_proto));
+ EXPECT_THAT(function_proto, EqualsProto(R"pb(
+ parameter_domains {
+ range {
+ min { bits { bit_count: 32 data: "\000\000\000\000" } }
+ max { bits { bit_count: 32 data: "\n\000\000\000" } }
+ }
+ }
+ parameter_domains { arbitrary: true }
+ )pb"));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithConstantRangeDomain) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+const C = u32:1..u32:10;
+#[fuzz_test(domains = `C`)]
+fn f(x: u32) -> u32 { x }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+
+ absl::Span attributes = ir_fn->attributes();
+ ASSERT_EQ(attributes.size(), 1);
+ const AttributeData& attr = attributes[0];
+ EXPECT_EQ(attr.kind(), AttributeKind::kFuzzTest);
+ ASSERT_EQ(attr.args().size(), 1);
+ const AttributeData::Argument& arg = attr.args()[0];
+ EXPECT_THAT(
+ arg,
+ testing::VariantWith(
+ testing::AllOf(
+ testing::Field(&AttributeData::StringKeyValueArgument::first,
+ "domains"),
+ testing::Field(&AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("range")),
+ testing::Field(&AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("bit_count: 32")),
+ testing::Field(
+ &AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("data: \"\\001\\000\\000\\000\"")),
+ testing::Field(
+ &AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("data: \"\\n\\000\\000\\000\"")))));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithConstantRangeInTuple) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+const C = u32:1..u32:10;
+#[fuzz_test(domains = `(C, u32:0..5)`)]
+fn f(x: (u32, u32)) -> u32 { x.0 + x.1 }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+
+ absl::Span attributes = ir_fn->attributes();
+ ASSERT_EQ(attributes.size(), 1);
+ const AttributeData& attr = attributes[0];
+ EXPECT_EQ(attr.kind(), AttributeKind::kFuzzTest);
+ ASSERT_EQ(attr.args().size(), 1);
+ const AttributeData::Argument& arg = attr.args()[0];
+ EXPECT_THAT(
+ arg,
+ testing::VariantWith(
+ testing::AllOf(
+ testing::Field(&AttributeData::StringKeyValueArgument::first,
+ "domains"),
+ testing::Field(&AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("tuple")),
+ testing::Field(&AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("range")),
+ testing::Field(&AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("bit_count: 32")),
+ testing::Field(
+ &AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("data: \"\\001\\000\\000\\000\"")),
+ testing::Field(
+ &AttributeData::StringKeyValueArgument::second,
+ testing::HasSubstr("data: \"\\005\\000\\000\\000\"")))));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithConstantArrayDomain) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+const A = [u32:1, u32:2, u32:3];
+#[fuzz_test(domains = `A`)]
+fn f(x: u32) -> u32 { x }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ const auto& skv =
+ std::get(attributes[0].args()[0]);
+
+ EXPECT_THAT(skv.second, testing::HasSubstr("element_of"));
+ EXPECT_THAT(skv.second, testing::HasSubstr("data: \"\\001\\000\\000\\000\""));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithConstantTupleDomain) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+const T = (u32:1..u32:5, u32:2..u32:10);
+#[fuzz_test(domains = `T`)]
+fn f(x: (u32, u32)) -> u32 { x.0 }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ const auto& skv =
+ std::get(attributes[0].args()[0]);
+
+ EXPECT_THAT(skv.second, testing::HasSubstr("tuple"));
+ EXPECT_THAT(skv.second, testing::HasSubstr("data: \"\\001\\000\\000\\000\""));
+}
+
+TEST(FunctionConverterTest,
+ ConvertsFuzzTestFunctionWithConstantArrayWithConstantEmbedded) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+const A = u32:1;
+const C = [A, u32:2, u32:3];
+#[fuzz_test(domains = `C`)]
+fn f(x: u32) -> u32 { x }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ const auto& skv =
+ std::get(attributes[0].args()[0]);
+
+ EXPECT_THAT(skv.second, testing::HasSubstr("element_of"));
+ EXPECT_THAT(skv.second, testing::HasSubstr("data: \"\\001\\000\\000\\000\""));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithDifferentBitWidths) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+#[fuzz_test(domains = `u8:0..5, u64:0..100`)]
+fn f(x: u8, y: u64) -> u64 { (x as u64) + y }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ ASSERT_EQ(attributes.size(), 1);
+ const AttributeData::Argument& arg = attributes[0].args()[0];
+ const auto& skv = std::get(arg);
+
+ xls::PackageInterfaceProto::Function function_proto;
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(skv.second, &function_proto));
+ EXPECT_THAT(
+ function_proto, EqualsProto(R"pb(
+ parameter_domains {
+ range {
+ min { bits { bit_count: 8 data: "\000" } }
+ max { bits { bit_count: 8 data: "\005" } }
+ }
+ }
+ parameter_domains {
+ range {
+ min {
+ bits { bit_count: 64 data: "\000\000\000\000\000\000\000\000" }
+ }
+ max { bits { bit_count: 64 data: "d\000\000\000\000\000\000\000" } }
+ }
+ }
+ )pb"));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithRangeEdgeCases) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+#[fuzz_test(domains = `u32:10..10, u32:0..0xFFFFFFFF`)]
+fn f(x: u32, y: u32) -> u32 { x + y }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ ASSERT_EQ(attributes.size(), 1);
+ const AttributeData::Argument& arg = attributes[0].args()[0];
+ const auto& skv = std::get(arg);
+
+ xls::PackageInterfaceProto::Function function_proto;
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(skv.second, &function_proto));
+ EXPECT_THAT(function_proto, EqualsProto(R"pb(
+ parameter_domains {
+ range {
+ min { bits { bit_count: 32 data: "\n\000\000\000" } }
+ max { bits { bit_count: 32 data: "\n\000\000\000" } }
+ }
+ }
+ parameter_domains {
+ range {
+ min { bits { bit_count: 32 data: "\000\000\000\000" } }
+ max { bits { bit_count: 32 data: "\377\377\377\377" } }
+ }
+ }
+ )pb"));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithMultipleSameDomains) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+#[fuzz_test(domains = `u32:0..10, u32:20..30`)]
+fn f(x: u32, y: u32) -> u32 { x + y }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ ASSERT_EQ(attributes.size(), 1);
+ const AttributeData::Argument& arg = attributes[0].args()[0];
+ const auto& skv = std::get(arg);
+
+ xls::PackageInterfaceProto::Function function_proto;
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(skv.second, &function_proto));
+ EXPECT_THAT(function_proto, EqualsProto(R"pb(
+ parameter_domains {
+ range {
+ min { bits { bit_count: 32 data: "\000\000\000\000" } }
+ max { bits { bit_count: 32 data: "\n\000\000\000" } }
+ }
+ }
+ parameter_domains {
+ range {
+ min { bits { bit_count: 32 data: "\024\000\000\000" } }
+ max { bits { bit_count: 32 data: "\036\000\000\000" } }
+ }
+ }
+ )pb"));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithTupleDomain) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+#[fuzz_test(domains = `(u32:0..10, u32:0..20)`)]
+fn f(x: (u32, u32)) -> u32 { x.0 }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ const auto& skv =
+ std::get(attributes[0].args()[0]);
+
+ xls::PackageInterfaceProto::Function function_proto;
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(skv.second, &function_proto));
+ EXPECT_THAT(function_proto, EqualsProto(R"pb(
+ parameter_domains {
+ tuple {
+ elements {
+ range {
+ min { bits { bit_count: 32 data: "\000\000\000\000" } }
+ max { bits { bit_count: 32 data: "\n\000\000\000" } }
+ }
+ }
+ elements {
+ range {
+ min { bits { bit_count: 32 data: "\000\000\000\000" } }
+ max { bits { bit_count: 32 data: "\024\000\000\000" } }
+ }
+ }
+ }
+ }
+ )pb"));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithElementOfDomain) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+#[fuzz_test(domains = `[u32:1, u32:2, u32:3]`)]
+fn f(x: u32) -> u32 { x }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ const auto& skv =
+ std::get(attributes[0].args()[0]);
+
+ xls::PackageInterfaceProto::Function function_proto;
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(skv.second, &function_proto));
+ EXPECT_THAT(function_proto, EqualsProto(R"pb(
+ parameter_domains {
+ element_of {
+ values { bits { bit_count: 32 data: "\001\000\000\000" } }
+ values { bits { bit_count: 32 data: "\002\000\000\000" } }
+ values { bits { bit_count: 32 data: "\003\000\000\000" } }
+ }
+ }
+ )pb"));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithEnumElementOfDomain) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+enum MyEnum : u32 {
+ A = 1,
+ B = 2,
+}
+#[fuzz_test(domains = `[MyEnum::A, MyEnum::B]`)]
+fn f(x: MyEnum) -> MyEnum { x }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ const auto& skv =
+ std::get(attributes[0].args()[0]);
+
+ xls::PackageInterfaceProto::Function function_proto;
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(skv.second, &function_proto));
+ EXPECT_THAT(function_proto, EqualsProto(R"pb(
+ parameter_domains {
+ element_of {
+ values { bits { bit_count: 32 data: "\001\000\000\000" } }
+ values { bits { bit_count: 32 data: "\002\000\000\000" } }
+ }
+ }
+ )pb"));
+}
+
+TEST(FunctionConverterTest, ConvertsFuzzTestFunctionWithNestedTupleDomain) {
+ ImportData import_data = CreateImportDataForTest();
+ XLS_ASSERT_OK_AND_ASSIGN(
+ TypecheckedModule tm,
+ ParseAndTypecheck(R"(
+#[fuzz_test(domains = `u32:0..10, ((), u32:0..5)`)]
+fn f(x: u32, y: ((), u32)) -> u32 { x }
+)",
+ "test_module.x", "test_module", &import_data));
+
+ XLS_ASSERT_OK_AND_ASSIGN(FuzzTestFunction * ft,
+ tm.module->GetMemberOrError("f"));
+ ASSERT_NE(ft, nullptr);
+ Function* f = &ft->fn();
+
+ const ConvertOptions convert_options;
+ PackageConversionData package = MakeConversionData("test_module_package");
+ PackageData package_data{&package};
+ FunctionConverter converter(package_data, tm.module, &import_data,
+ convert_options, /*proc_data=*/nullptr,
+ /*channel_scope=*/nullptr,
+ /*is_top=*/true);
+
+ XLS_ASSERT_OK(
+ converter.HandleFunction(f, tm.type_info, /*parametric_env=*/nullptr));
+
+ ASSERT_FALSE(package_data.conversion_info->package->functions().empty());
+ auto* ir_fn =
+ package_data.conversion_info->package->functions().front().get();
+
+ EXPECT_TRUE(ir_fn->HasAttribute(AttributeKind::kFuzzTest));
+ absl::Span attributes = ir_fn->attributes();
+ const auto& skv =
+ std::get(attributes[0].args()[0]);
+
+ xls::PackageInterfaceProto::Function function_proto;
+ ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString(skv.second, &function_proto));
+ EXPECT_THAT(function_proto, EqualsProto(R"pb(
+ parameter_domains {
+ range {
+ min { bits { bit_count: 32 data: "\000\000\000\000" } }
+ max { bits { bit_count: 32 data: "\n\000\000\000" } }
+ }
+ }
+ parameter_domains {
+ tuple {
+ elements { arbitrary: true }
+ elements {
+ range {
+ min { bits { bit_count: 32 data: "\000\000\000\000" } }
+ max { bits { bit_count: 32 data: "\005\000\000\000" } }
+ }
+ }
+ }
+ }
+ )pb"));
+}
+
} // namespace
} // namespace xls::dslx