Skip to content
Draft
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
2 changes: 2 additions & 0 deletions xls/common/attribute_data.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ std::string AttributeKindToString(AttributeKind kind) {
return "channel_strictness";
case AttributeKind::kFuzzTest:
return "fuzz_test";
case AttributeKind::kFuzzDomain:
return "fuzz_domain";
}
}

Expand Down
1 change: 1 addition & 0 deletions xls/common/attribute_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ enum class AttributeKind : uint8_t {
kQuickcheck,
kChannelStrictness,
kFuzzTest,
kFuzzDomain,
};

// Converts an AttributeKind to a string, e.g., "fuzz_test"
Expand Down
13 changes: 13 additions & 0 deletions xls/dslx/frontend/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -3417,9 +3417,22 @@ class StructDef : public StructDefBase {
return extern_type_name_;
}

void set_is_domain_struct(bool v) { is_domain_struct_ = v; }
bool is_domain_struct() const { return is_domain_struct_; }

void set_is_populated(bool v) { is_populated_ = v; }
bool is_populated() const { return is_populated_; }

void set_original_struct(StructDef* s) { original_struct_ = s; }
StructDef* original_struct() const { return original_struct_; }

private:
// The external verilog type name
std::optional<std::string> extern_type_name_;

bool is_domain_struct_ = false;
bool is_populated_ = false;
StructDef* original_struct_ = nullptr;
};

// Represents a proc declared with struct-like syntax, with the functions in an
Expand Down
52 changes: 48 additions & 4 deletions xls/dslx/frontend/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <variant>
#include <vector>
Expand Down Expand Up @@ -225,6 +226,7 @@ absl::StatusOr<AttributeKind> ParseAttributeKind(Token token,
{"test_proc", AttributeKind::kTestProc},
{"quickcheck", AttributeKind::kQuickcheck},
{"fuzz_test", AttributeKind::kFuzzTest},
{"fuzz_domain", AttributeKind::kFuzzDomain},
{"channel_strictness", AttributeKind::kChannelStrictness}};

const auto it = map->find(token.GetStringValue());
Expand Down Expand Up @@ -605,7 +607,7 @@ absl::StatusOr<std::unique_ptr<Module>> Parser::ParseModule(
TypeAlias * type_alias,
ParseTypeAlias(*module_member_start_pos, is_public, *bindings));
XLS_RETURN_IF_ERROR(
ApplyTypeAttributes(type_alias, pending_attributes));
ApplyTypeAttributes(type_alias, pending_attributes, *bindings));
XLS_RETURN_IF_ERROR(module_->AddTop(type_alias, make_collision_error));
break;
}
Expand All @@ -614,15 +616,16 @@ absl::StatusOr<std::unique_ptr<Module>> Parser::ParseModule(
StructDef * struct_def,
ParseStruct(*module_member_start_pos, is_public, *bindings));
XLS_RETURN_IF_ERROR(
ApplyTypeAttributes(struct_def, pending_attributes));
ApplyTypeAttributes(struct_def, pending_attributes, *bindings));
XLS_RETURN_IF_ERROR(module_->AddTop(struct_def, make_collision_error));
break;
}
case Keyword::kEnum: {
XLS_ASSIGN_OR_RETURN(
EnumDef * enum_def,
ParseEnumDef(*module_member_start_pos, is_public, *bindings));
XLS_RETURN_IF_ERROR(ApplyTypeAttributes(enum_def, pending_attributes));
XLS_RETURN_IF_ERROR(
ApplyTypeAttributes(enum_def, pending_attributes, *bindings));
XLS_RETURN_IF_ERROR(module_->AddTop(enum_def, make_collision_error));
break;
}
Expand Down Expand Up @@ -949,6 +952,7 @@ absl::Status Parser::UnsupportedAttributeError(const Attribute& attribute) {
Span span = *attribute.GetSpan();
switch (attribute.attribute_kind()) {
case AttributeKind::kDerive:
case AttributeKind::kFuzzDomain:
return ParseErrorStatus(
span,
absl::StrCat(attribute.ToString(), " is only valid on a struct."));
Expand Down Expand Up @@ -1143,7 +1147,8 @@ absl::StatusOr<XlsTuple*> Parser::ParseFuzzTestDomains(

template <typename T>
absl::Status Parser::ApplyTypeAttributes(T* node,
std::vector<Attribute*> attributes) {
std::vector<Attribute*> attributes,
Bindings& bindings) {
for (Attribute* next : attributes) {
switch (next->attribute_kind()) {
case AttributeKind::kDerive: {
Expand Down Expand Up @@ -1180,6 +1185,45 @@ absl::Status Parser::ApplyTypeAttributes(T* node,
break;
}

case AttributeKind::kFuzzDomain: {
if constexpr (!std::is_same_v<T, StructDef>) {
return UnsupportedAttributeError(*next);
} else {
if (next->args().size() != 1 ||
!std::holds_alternative<AttributeData::StringLiteralArgument>(
next->args()[0])) {
return ParseErrorStatus(
*next->GetSpan(),
"fuzz_domain attribute requires a string argument.");
}
std::string domain_name =
std::get<AttributeData::StringLiteralArgument>(next->args()[0])
.text;

StructDef* struct_def = node;
Span span = *next->GetSpan();
NameDef* domain_name_def =
module_->Make<NameDef>(span, domain_name, /*definer=*/nullptr);
std::vector<ParametricBinding*> parametric_bindings;
std::vector<StructMemberNode*> members;
StructDef* domain_struct = module_->Make<StructDef>(
span, domain_name_def, std::move(parametric_bindings),
std::move(members), struct_def->is_public());
domain_name_def->set_definer(domain_struct);

domain_struct->set_is_domain_struct(true);
domain_struct->set_original_struct(struct_def);

bindings.Add(domain_name, domain_struct);

auto make_collision_error =
absl::bind_front(&MakeModuleTopCollisionError, file_table());
XLS_RETURN_IF_ERROR(
module_->AddTop(domain_struct, make_collision_error));
}
break;
}

default:
return UnsupportedAttributeError(*next);
}
Expand Down
3 changes: 2 additions & 1 deletion xls/dslx/frontend/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,8 @@ class Parser : public TokenParser {
const std::vector<Attribute*>& attributes);

template <typename T>
absl::Status ApplyTypeAttributes(T* node, std::vector<Attribute*> attributes);
absl::Status ApplyTypeAttributes(T* node, std::vector<Attribute*> attributes,
Bindings& bindings);

absl::StatusOr<QuickCheckTestCases> GetQuickCheckTestCases(
const Attribute& attribute);
Expand Down
23 changes: 23 additions & 0 deletions xls/dslx/frontend/parser_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4964,4 +4964,27 @@ fn f(x: u32, y: u8) {}
EXPECT_TRUE(second_member_tuple->members().empty());
}

TEST_F(ParserTest, FuzzDomainAttribute) {
std::string_view program = R"(
#[fuzz_domain("MyDomain")]
struct MyStruct {
x: u32,
}
)";
auto module = ExpectParsesSuccessfully(program);
ASSERT_NE(module, nullptr);

// Verify MyStruct exists.
XLS_ASSERT_OK_AND_ASSIGN(StructDef * my_struct,
module->GetMemberOrError<StructDef>("MyStruct"));
EXPECT_FALSE(my_struct->is_domain_struct());

// Verify MyDomain exists and is linked.
XLS_ASSERT_OK_AND_ASSIGN(StructDef * my_domain,
module->GetMemberOrError<StructDef>("MyDomain"));
EXPECT_TRUE(my_domain->is_domain_struct());
EXPECT_FALSE(my_domain->is_populated());
EXPECT_EQ(my_domain->original_struct(), my_struct);
}

} // namespace xls::dslx
Loading