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
3 changes: 3 additions & 0 deletions xls/dslx/cpp_transpiler/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ cc_library(
"//xls/dslx/type_system:type_info",
"//xls/dslx/type_system_v2:import_utils",
"//xls/dslx/type_system_v2:type_annotation_utils",
"@com_google_absl//absl/algorithm:container",
"@com_google_absl//absl/base:no_destructor",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
],
)

Expand All @@ -98,6 +100,7 @@ cc_library(
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/types:span",
"@com_google_absl//absl/types:variant",
],
)
Expand Down
83 changes: 74 additions & 9 deletions xls/dslx/cpp_transpiler/cpp_emitter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@

#include "xls/dslx/cpp_transpiler/cpp_emitter.h"

#include <algorithm>
#include <cstdint>
#include <functional>
#include <iterator>
#include <memory>
#include <optional>
#include <string>
Expand All @@ -24,14 +26,17 @@
#include <variant>
#include <vector>

#include "absl/algorithm/container.h"
#include "absl/base/no_destructor.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/types/span.h"
#include "xls/common/case_converters.h"
#include "xls/common/indent.h"
#include "xls/common/status/ret_check.h"
Expand Down Expand Up @@ -418,6 +423,10 @@ class TypeRefCppEmitter : public CppEmitter {
// std::array.
class ArrayCppEmitter : public CppEmitter {
public:
// NOTE: until we implement variable name tracking across generated scopes,
// prefixes must be kept unique across all emitters and generators
std::string kArrayVarPrefix = "elements";

explicit ArrayCppEmitter(const CppType& cpp_type, std::string_view dslx_type,
int64_t array_size,
std::unique_ptr<CppEmitter> element_emitter)
Expand Down Expand Up @@ -455,23 +464,27 @@ class ArrayCppEmitter : public CppEmitter {

std::string AssignToValue(std::string_view lhs, std::string_view rhs,
int64_t nesting) const override {
std::string tmp_var_name =
UniqueVarName({std::string(rhs)}, kArrayVarPrefix);
std::string ind_var = absl::StrCat("i", nesting);

std::vector<std::string> loop_body;
loop_body.push_back("::xls::Value element;");
std::string element_assignment = element_emitter_->AssignToValue(
"element", absl::StrFormat("%s[%s]", rhs, ind_var), nesting + 1);
loop_body.push_back(element_assignment);
loop_body.push_back("elements.push_back(element);");
loop_body.push_back(
absl::StrFormat("%s.push_back(element);", tmp_var_name));

std::vector<std::string> pieces;
pieces.push_back("std::vector<::xls::Value> elements;");
pieces.push_back(
absl::StrFormat("std::vector<::xls::Value> %s;", tmp_var_name));
pieces.push_back(absl::StrFormat("for (int64_t %s = 0; %s < %d; ++%s) {",
ind_var, ind_var, array_size(), ind_var));
pieces.push_back(Indent(absl::StrJoin(loop_body, "\n"), 2));
pieces.push_back("}");
pieces.push_back(
absl::StrFormat("%s = ::xls::Value::ArrayOrDie(elements);", lhs));
pieces.push_back(absl::StrFormat("%s = ::xls::Value::ArrayOrDie(%s);", lhs,
tmp_var_name));
std::string lines = absl::StrJoin(pieces, "\n");

return absl::StrCat("{\n", Indent(lines, 2), "\n}");
Expand Down Expand Up @@ -575,6 +588,10 @@ class ArrayCppEmitter : public CppEmitter {
// std::tuple.
class TupleCppEmitter : public CppEmitter {
public:
// NOTE: until we implement variable name tracking across generated scopes,
// prefixes must be kept unique across all emitters and generators
std::string kTupleVarPrefix = "entries";

explicit TupleCppEmitter(
const CppType& cpp_type, std::string_view dslx_type,
std::vector<std::unique_ptr<CppEmitter>> element_emitters)
Expand Down Expand Up @@ -606,16 +623,20 @@ class TupleCppEmitter : public CppEmitter {
std::string AssignToValue(std::string_view lhs, std::string_view rhs,
int64_t nesting) const override {
std::vector<std::string> pieces;
pieces.push_back("std::vector<::xls::Value> members;");
pieces.push_back(absl::StrFormat("members.resize(%d);", size()));
std::vector<std::string> user_defined_names{std::string(rhs)};
std::string tmp_var_name =
UniqueVarName(user_defined_names, kTupleVarPrefix);
pieces.push_back(
absl::StrFormat("std::vector<::xls::Value> %s;", tmp_var_name));
pieces.push_back(absl::StrFormat("%s.resize(%d);", tmp_var_name, size()));
for (int64_t i = 0; i < size(); ++i) {
std::string assignment = element_emitters_[i]->AssignToValue(
absl::StrFormat("members[%d]", i),
absl::StrFormat("%s[%d]", tmp_var_name, i),
absl::StrFormat("std::get<%d>(%s)", i, rhs), nesting + 1);
pieces.push_back(assignment);
}
pieces.push_back(
absl::StrFormat("%s = ::xls::Value::Tuple(members);", lhs));
absl::StrFormat("%s = ::xls::Value::Tuple(%s);", lhs, tmp_var_name));
return absl::StrFormat("{\n%s\n}", Indent(absl::StrJoin(pieces, "\n"), 2));
}

Expand Down Expand Up @@ -803,8 +824,29 @@ std::string SanitizeCppName(std::string_view name) {
return std::string{name};
}

std::string PreserveTrimmedCharacter(std::string_view original,
std::string_view sanitized,
char c_to_preserve) {
bool all_match_so_far = true;
auto still_underscore = [&](char c) -> bool {
all_match_so_far &= (c == c_to_preserve);
return all_match_so_far;
};
int64_t leading_underscores =
std::count_if(original.begin(), original.end(), still_underscore);
all_match_so_far = true;
int64_t trailing_underscores =
std::count_if(original.rbegin(), original.rend(), still_underscore);
return absl::StrCat(std::string(leading_underscores, c_to_preserve),
sanitized,
std::string(trailing_underscores, c_to_preserve));
}

std::string DslxTypeNameToCpp(std::string_view dslx_type) {
return SanitizeCppName(Camelize(dslx_type));
std::string cpp_name = SanitizeCppName(Camelize(dslx_type));
// Retain trimmed underscores to ensure names are unique. This matters for
// user defined names only differing by underscores.
return PreserveTrimmedCharacter(dslx_type, cpp_name, '_');
}

std::string DslxModuleNameToCppNamespace(std::string_view dslx_module) {
Expand Down Expand Up @@ -856,4 +898,27 @@ std::string DslxModuleNameToCppNamespace(std::string_view dslx_module) {
"Unsupported TypeAnnotation kind: ", type_annotation->ToString()));
}

std::string UniqueVarName(absl::Span<const std::string> user_names,
std::string_view prefix) {
std::string unique_name = std::string(prefix);
if (!absl::c_contains(user_names, unique_name)) {
return unique_name;
}
std::vector<std::string> existing_user_names;
absl::c_copy_if(user_names, std::back_inserter(existing_user_names),
[prefix](std::string_view name) {
return absl::StartsWith(name, prefix);
});
absl::c_sort(existing_user_names);
// Appending a character avoids collision with previous names because the
// potentially colliding names are sorted. Names [x, x_, x__, ...] will be
// caught by subsequent iterations and enough "_" will be appended.
for (int64_t i = 0; i < existing_user_names.size(); ++i) {
if (existing_user_names[i] == unique_name) {
unique_name += "_";
}
}
return unique_name;
}

} // namespace xls::dslx
4 changes: 4 additions & 0 deletions xls/dslx/cpp_transpiler/cpp_emitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "xls/dslx/frontend/ast.h"
#include "xls/dslx/import_data.h"
#include "xls/dslx/type_system/type_info.h"
Expand All @@ -40,6 +41,9 @@ std::string DslxTypeNameToCpp(std::string_view dslx_type);
// (https://abseil.io/tips/130).
std::string DslxModuleNameToCppNamespace(std::string_view dslx_module);

std::string UniqueVarName(absl::Span<const std::string> user_names,
std::string_view prefix);

struct CppType {
std::string name;
std::string namespace_prefix = "";
Expand Down
4 changes: 3 additions & 1 deletion xls/dslx/cpp_transpiler/cpp_transpiler_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,9 @@ struct MyStruct {
y: u15,
z: u8,
w: s63,
v: u1,
result: u1,
result__: u1,
result_: u1,
})";

auto import_data = CreateImportDataForTest();
Expand Down
61 changes: 38 additions & 23 deletions xls/dslx/cpp_transpiler/cpp_type_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "absl/types/span.h"
#include "absl/types/variant.h"
#include "xls/common/indent.h"
#include "xls/common/status/status_macros.h"
Expand Down Expand Up @@ -465,6 +466,11 @@ class TypeAliasCppTypeGenerator : public CppTypeGenerator {
// A type generator for emitting a C++ struct for representing a DSLX struct.
class StructCppTypeGenerator : public CppTypeGenerator {
public:
// NOTE: until we implement variable name tracking across generated scopes,
// prefixes must be kept unique across all emitters and generators
std::string kResultVarPrefix = "result";
std::string kListVarPrefix = "members";

explicit StructCppTypeGenerator(
std::string_view cpp_type, std::string_view dslx_type,
const StructDef* struct_def,
Expand Down Expand Up @@ -558,6 +564,8 @@ class StructCppTypeGenerator : public CppTypeGenerator {

protected:
CppSource FromValueMethod() const {
std::string tmp_var_name =
UniqueVarName(cpp_member_names_, kResultVarPrefix);
std::vector<std::string> pieces;
pieces.push_back(absl::StrFormat(
"if (!value.IsTuple() || value.size() != %d) {", struct_def_->size()));
Expand All @@ -566,14 +574,14 @@ class StructCppTypeGenerator : public CppTypeGenerator {
"tuple of %d elements.\");",
struct_def_->size()));
pieces.push_back("}");
pieces.push_back(absl::StrFormat("%s result;", cpp_type()));
pieces.push_back(absl::StrFormat("%s %s;", cpp_type(), tmp_var_name));
for (int i = 0; i < struct_def_->members().size(); i++) {
std::string assignment = member_emitters_[i]->AssignFromValue(
/*lhs=*/absl::StrFormat("result.%s", cpp_member_names_[i]),
/*lhs=*/absl::StrFormat("%s.%s", tmp_var_name, cpp_member_names_[i]),
/*rhs=*/absl::StrFormat("value.element(%d)", i), /*nesting=*/0);
pieces.push_back(assignment);
}
pieces.push_back("return result;");
pieces.push_back(absl::StrFormat("return %s;", tmp_var_name));
std::string body = absl::StrJoin(pieces, "\n");

return CppSource{
Expand All @@ -587,17 +595,20 @@ class StructCppTypeGenerator : public CppTypeGenerator {
}

CppSource ToValueMethod() const {
std::string tmp_var_name = UniqueVarName(cpp_member_names_, kListVarPrefix);
std::vector<std::string> pieces;
pieces.push_back("std::vector<::xls::Value> members;");
pieces.push_back(
absl::StrFormat("members.resize(%d);", struct_def_->members().size()));
absl::StrFormat("std::vector<::xls::Value> %s;", tmp_var_name));
pieces.push_back(absl::StrFormat("%s.resize(%d);", tmp_var_name,
struct_def_->members().size()));
for (int i = 0; i < struct_def_->members().size(); i++) {
std::string assignment = member_emitters_[i]->AssignToValue(
/*lhs=*/absl::StrFormat("members[%d]", i),
/*lhs=*/absl::StrFormat("%s[%d]", tmp_var_name, i),
/*rhs=*/cpp_member_names_[i], /*nesting=*/0);
pieces.push_back(assignment);
}
pieces.push_back("return ::xls::Value::Tuple(members);");
pieces.push_back(
absl::StrFormat("return ::xls::Value::Tuple(%s);", tmp_var_name));
std::string body = absl::StrJoin(pieces, "\n");

return CppSource{
Expand Down Expand Up @@ -626,19 +637,21 @@ class StructCppTypeGenerator : public CppTypeGenerator {
}

CppSource ToStringMethod() const {
std::string tmp_var_name =
UniqueVarName(cpp_member_names_, kResultVarPrefix);
std::vector<std::string> pieces;
pieces.push_back(
absl::StrFormat("std::string result = \"%s {\\n\";", cpp_type()));
pieces.push_back(absl::StrFormat("std::string %s = \"%s {\\n\";",
tmp_var_name, cpp_type()));
for (int i = 0; i < struct_def_->members().size(); i++) {
pieces.push_back(absl::StrFormat(
"result += __indent(indent + 1) + \"%s: \";", cpp_member_names_[i]));
pieces.push_back(absl::StrFormat("%s += __indent(indent + 1) + \"%s: \";",
tmp_var_name, cpp_member_names_[i]));
std::string to_string = member_emitters_[i]->ToString(
"result", "indent + 2", cpp_member_names_[i], /*nesting=*/0);
tmp_var_name, "indent + 2", cpp_member_names_[i], /*nesting=*/0);
pieces.push_back(to_string);
pieces.push_back("result += \",\\n\";");
pieces.push_back(tmp_var_name + " += \",\\n\";");
}
pieces.push_back("result += __indent(indent) + \"}\";");
pieces.push_back("return result;");
pieces.push_back(tmp_var_name + " += __indent(indent) + \"}\";");
pieces.push_back(absl::StrFormat("return %s;", tmp_var_name));
std::string body = absl::StrJoin(pieces, "\n");

return CppSource{.header = "std::string ToString(int indent = 0) const;",
Expand All @@ -648,19 +661,21 @@ class StructCppTypeGenerator : public CppTypeGenerator {
}

CppSource ToDslxStringMethod() const {
std::string tmp_var_name =
UniqueVarName(cpp_member_names_, kResultVarPrefix);
std::vector<std::string> pieces;
pieces.push_back(
absl::StrFormat("std::string result = \"%s {\\n\";", dslx_type()));
pieces.push_back(absl::StrFormat("std::string %s = \"%s {\\n\";",
tmp_var_name, dslx_type()));
for (int i = 0; i < struct_def_->members().size(); i++) {
pieces.push_back(absl::StrFormat(
"result += __indent(indent + 1) + \"%s: \";", cpp_member_names_[i]));
pieces.push_back(absl::StrFormat("%s += __indent(indent + 1) + \"%s: \";",
tmp_var_name, cpp_member_names_[i]));
std::string to_string = member_emitters_[i]->ToDslxString(
"result", "indent + 2", cpp_member_names_[i], /*nesting=*/0);
tmp_var_name, "indent + 2", cpp_member_names_[i], /*nesting=*/0);
pieces.push_back(to_string);
pieces.push_back("result += \",\\n\";");
pieces.push_back(tmp_var_name + " += \",\\n\";");
}
pieces.push_back("result += __indent(indent) + \"}\";");
pieces.push_back("return result;");
pieces.push_back(tmp_var_name + " += __indent(indent) + \"}\";");
pieces.push_back(absl::StrFormat("return %s;", tmp_var_name));
std::string body = absl::StrJoin(pieces, "\n");

return CppSource{
Expand Down
Loading