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>
This commit is contained in:
Antonio Maiorano
2022-01-14 21:57:22 +00:00
committed by Tint LUCI CQ
parent 65112dc77c
commit e232e35723
4 changed files with 131 additions and 89 deletions

View File

@@ -56,6 +56,47 @@ TEST_F(StructTest, FriendlyName) {
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

View File

@@ -14,6 +14,8 @@
#include "src/sem/struct.h"
#include <cmath>
#include <iomanip>
#include <string>
#include <utility>
@@ -74,6 +76,82 @@ std::string Struct::FriendlyName(const SymbolTable& symbols) const {
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_;
}

View File

@@ -150,6 +150,11 @@ class Struct : public Castable<Struct, Type> {
/// 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;