Factor out structure layout print function
Simplifies Resolver::ValidateStorageClassLayout and would allow us to
use this function for displaying structure layout in a language server,
for example.
Bug: tint:1348
Change-Id: I9d83329f0a168e5d8c094b3282d07cd1ab321dca
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/76080
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/resolver/resolver_validation.cc b/src/resolver/resolver_validation.cc
index bc70f94..1df63ba 100644
--- a/src/resolver/resolver_validation.cc
+++ b/src/resolver/resolver_validation.cc
@@ -15,8 +15,6 @@
#include "src/resolver/resolver.h"
#include <algorithm>
-#include <cmath>
-#include <iomanip>
#include <limits>
#include <utility>
@@ -257,87 +255,6 @@
return builder_->Symbols().NameFor(sm->Declaration()->symbol);
};
- auto type_name_of = [this](const sem::StructMember* sm) {
- return TypeNameOf(sm->Type());
- };
-
- // TODO(amaiorano): Output struct and member decorations so that this output
- // can be copied verbatim back into source
- auto get_struct_layout_string = [this, member_name_of, type_name_of](
- const sem::Struct* st) -> std::string {
- std::stringstream ss;
-
- if (st->Members().empty()) {
- TINT_ICE(Resolver, diagnostics_) << "Validation should have ensured that "
- "structs have at least one member";
- return {};
- }
- const auto* const last_member = st->Members().back();
- const uint32_t last_member_struct_padding_offset =
- last_member->Offset() + last_member->Size();
-
- // Compute max widths to align output
- const auto offset_w =
- static_cast<int>(::log10(last_member_struct_padding_offset)) + 1;
- const auto size_w = static_cast<int>(::log10(st->Size())) + 1;
- const auto align_w = static_cast<int>(::log10(st->Align())) + 1;
-
- auto print_struct_begin_line = [&](size_t align, size_t size,
- std::string struct_name) {
- ss << "/* " << std::setw(offset_w) << " "
- << "align(" << std::setw(align_w) << align << ") size("
- << std::setw(size_w) << size << ") */ struct " << struct_name
- << " {\n";
- };
-
- auto print_struct_end_line = [&]() {
- ss << "/* "
- << std::setw(offset_w + size_w + align_w) << " "
- << "*/ };";
- };
-
- auto print_member_line = [&](size_t offset, size_t align, size_t size,
- std::string s) {
- ss << "/* offset(" << std::setw(offset_w) << offset << ") align("
- << std::setw(align_w) << align << ") size(" << std::setw(size_w)
- << size << ") */ " << s << ";\n";
- };
-
- print_struct_begin_line(st->Align(), st->Size(), TypeNameOf(st));
-
- for (size_t i = 0; i < st->Members().size(); ++i) {
- auto* const m = st->Members()[i];
-
- // Output field alignment padding, if any
- auto* const prev_member = (i == 0) ? nullptr : st->Members()[i - 1];
- if (prev_member) {
- uint32_t padding =
- m->Offset() - (prev_member->Offset() + prev_member->Size());
- if (padding > 0) {
- size_t padding_offset = m->Offset() - padding;
- print_member_line(padding_offset, 1, padding,
- "// -- implicit field alignment padding --");
- }
- }
-
- // Output member
- std::string member_name = member_name_of(m);
- print_member_line(m->Offset(), m->Align(), m->Size(),
- member_name_of(m) + " : " + type_name_of(m));
- }
-
- // Output struct size padding, if any
- uint32_t struct_padding = st->Size() - last_member_struct_padding_offset;
- if (struct_padding > 0) {
- print_member_line(last_member_struct_padding_offset, 1, struct_padding,
- "// -- implicit struct size padding --");
- }
-
- print_struct_end_line();
-
- return ss.str();
- };
-
if (!ast::IsHostShareable(sc)) {
return true;
}
@@ -348,7 +265,8 @@
// Validate that member is at a valid byte offset
if (m->Offset() % required_align != 0) {
- AddError("the offset of a struct member of type '" + type_name_of(m) +
+ AddError("the offset of a struct member of type '" +
+ m->Type()->UnwrapRef()->FriendlyName(builder_->Symbols()) +
"' in storage class '" + ast::ToString(sc) +
"' must be a multiple of " + std::to_string(required_align) +
" bytes, but '" + member_name_of(m) +
@@ -357,12 +275,12 @@
std::to_string(required_align) + ")]] on this member",
m->Declaration()->source);
- AddNote("see layout of struct:\n" + get_struct_layout_string(str),
+ AddNote("see layout of struct:\n" + str->Layout(builder_->Symbols()),
str->Declaration()->source);
if (auto* member_str = m->Type()->As<sem::Struct>()) {
AddNote("and layout of struct member:\n" +
- get_struct_layout_string(member_str),
+ member_str->Layout(builder_->Symbols()),
member_str->Declaration()->source);
}
@@ -384,12 +302,12 @@
"'. Consider setting [[align(16)]] on this member",
m->Declaration()->source);
- AddNote("see layout of struct:\n" + get_struct_layout_string(str),
+ AddNote("see layout of struct:\n" + str->Layout(builder_->Symbols()),
str->Declaration()->source);
auto* prev_member_str = prev_member->Type()->As<sem::Struct>();
AddNote("and layout of previous member struct:\n" +
- get_struct_layout_string(prev_member_str),
+ prev_member_str->Layout(builder_->Symbols()),
prev_member_str->Declaration()->source);
return false;
}
@@ -413,7 +331,7 @@
utils::RoundUp(required_align, arr->Stride())) +
")]] on the array type",
m->Declaration()->type->source);
- AddNote("see layout of struct:\n" + get_struct_layout_string(str),
+ AddNote("see layout of struct:\n" + str->Layout(builder_->Symbols()),
str->Declaration()->source);
return false;
}
diff --git a/src/sem/sem_struct_test.cc b/src/sem/sem_struct_test.cc
index 104df11..ca1c8e4 100644
--- a/src/sem/sem_struct_test.cc
+++ b/src/sem/sem_struct_test.cc
@@ -56,6 +56,47 @@
EXPECT_EQ(s->FriendlyName(Symbols()), "my_struct");
}
+TEST_F(StructTest, Layout) {
+ auto* inner_st = //
+ Structure("Inner", {
+ Member("a", ty.i32()),
+ Member("b", ty.u32()),
+ Member("c", ty.f32()),
+ Member("d", ty.vec3<f32>()),
+ Member("e", ty.mat4x2<f32>()),
+ });
+
+ auto* outer_st =
+ Structure("Outer", {
+ Member("inner", ty.type_name("Inner")),
+ Member("a", ty.i32()),
+ });
+
+ auto p = Build();
+ ASSERT_TRUE(p.IsValid()) << p.Diagnostics().str();
+
+ auto* sem_inner_st = p.Sem().Get(inner_st);
+ auto* sem_outer_st = p.Sem().Get(outer_st);
+
+ EXPECT_EQ(sem_inner_st->Layout(p.Symbols()),
+ R"(/* align(16) size(64) */ struct Inner {
+/* offset( 0) align( 4) size( 4) */ a : i32;
+/* offset( 4) align( 4) size( 4) */ b : u32;
+/* offset( 8) align( 4) size( 4) */ c : f32;
+/* offset(12) align( 1) size( 4) */ // -- implicit field alignment padding --;
+/* offset(16) align(16) size(12) */ d : vec3<f32>;
+/* offset(28) align( 1) size( 4) */ // -- implicit field alignment padding --;
+/* offset(32) align( 8) size(32) */ e : mat4x2<f32>;
+/* */ };)");
+
+ EXPECT_EQ(sem_outer_st->Layout(p.Symbols()),
+ R"(/* align(16) size(80) */ struct Outer {
+/* offset( 0) align(16) size(64) */ inner : Inner;
+/* offset(64) align( 4) size( 4) */ a : i32;
+/* offset(68) align( 1) size(12) */ // -- implicit struct size padding --;
+/* */ };)");
+}
+
} // namespace
} // namespace sem
} // namespace tint
diff --git a/src/sem/struct.cc b/src/sem/struct.cc
index d764115..796eb39 100644
--- a/src/sem/struct.cc
+++ b/src/sem/struct.cc
@@ -14,6 +14,8 @@
#include "src/sem/struct.h"
+#include <cmath>
+#include <iomanip>
#include <string>
#include <utility>
@@ -74,6 +76,82 @@
return symbols.NameFor(name_);
}
+std::string Struct::Layout(const tint::SymbolTable& symbols) const {
+ std::stringstream ss;
+
+ auto member_name_of = [&](const sem::StructMember* sm) {
+ return symbols.NameFor(sm->Declaration()->symbol);
+ };
+
+ if (Members().empty()) {
+ return {};
+ }
+ const auto* const last_member = Members().back();
+ const uint32_t last_member_struct_padding_offset =
+ last_member->Offset() + last_member->Size();
+
+ // Compute max widths to align output
+ const auto offset_w =
+ static_cast<int>(::log10(last_member_struct_padding_offset)) + 1;
+ const auto size_w = static_cast<int>(::log10(Size())) + 1;
+ const auto align_w = static_cast<int>(::log10(Align())) + 1;
+
+ auto print_struct_begin_line = [&](size_t align, size_t size,
+ std::string struct_name) {
+ ss << "/* " << std::setw(offset_w) << " "
+ << "align(" << std::setw(align_w) << align << ") size("
+ << std::setw(size_w) << size << ") */ struct " << struct_name << " {\n";
+ };
+
+ auto print_struct_end_line = [&]() {
+ ss << "/* "
+ << std::setw(offset_w + size_w + align_w) << " "
+ << "*/ };";
+ };
+
+ auto print_member_line = [&](size_t offset, size_t align, size_t size,
+ std::string s) {
+ ss << "/* offset(" << std::setw(offset_w) << offset << ") align("
+ << std::setw(align_w) << align << ") size(" << std::setw(size_w) << size
+ << ") */ " << s << ";\n";
+ };
+
+ print_struct_begin_line(Align(), Size(), UnwrapRef()->FriendlyName(symbols));
+
+ for (size_t i = 0; i < Members().size(); ++i) {
+ auto* const m = Members()[i];
+
+ // Output field alignment padding, if any
+ auto* const prev_member = (i == 0) ? nullptr : Members()[i - 1];
+ if (prev_member) {
+ uint32_t padding =
+ m->Offset() - (prev_member->Offset() + prev_member->Size());
+ if (padding > 0) {
+ size_t padding_offset = m->Offset() - padding;
+ print_member_line(padding_offset, 1, padding,
+ "// -- implicit field alignment padding --");
+ }
+ }
+
+ // Output member
+ std::string member_name = member_name_of(m);
+ print_member_line(
+ m->Offset(), m->Align(), m->Size(),
+ member_name + " : " + m->Type()->UnwrapRef()->FriendlyName(symbols));
+ }
+
+ // Output struct size padding, if any
+ uint32_t struct_padding = Size() - last_member_struct_padding_offset;
+ if (struct_padding > 0) {
+ print_member_line(last_member_struct_padding_offset, 1, struct_padding,
+ "// -- implicit struct size padding --");
+ }
+
+ print_struct_end_line();
+
+ return ss.str();
+}
+
bool Struct::IsConstructible() const {
return constructible_;
}
diff --git a/src/sem/struct.h b/src/sem/struct.h
index d7eab4b..3c42c6c 100644
--- a/src/sem/struct.h
+++ b/src/sem/struct.h
@@ -150,6 +150,11 @@
/// declared in WGSL.
std::string FriendlyName(const SymbolTable& symbols) const override;
+ /// @param symbols the program's symbol table
+ /// @returns a multiline string that describes the layout of this struct,
+ /// including size and alignment information.
+ std::string Layout(const tint::SymbolTable& symbols) const;
+
/// @returns true if constructible as per
/// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
bool IsConstructible() const override;