Skip to content

Commit 3547083

Browse files
committed
Expose type layout information in graph rendering
Leverage the LayoutShape and field type information recently added to TypeMetadata (commits 74a4261, 18f452d) to provide actual useful data instead of leaving it to gather dust in the JSON output. Introduce TypeEntry, LayoutInfo, and associated structures to index.rs, replacing the rather spartan string-only type index with something that knows about field offsets, sizes, alignments, and discriminants. One might argue this is what a type index ought to have done from the start. Add rendering methods to GraphContext for displaying types with their memory layouts. The DOT output now includes a TYPES legend (in a fetching shade of lavender) and locals are annotated with size and alignment. This allows tooling to answer questions like "how large is this struct" and "at what offset does field 3 live" without resorting to prayer or manual calculation.
1 parent be7a54a commit 3547083

File tree

3 files changed

+427
-20
lines changed

3 files changed

+427
-20
lines changed

src/mk_graph/context.rs

Lines changed: 148 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use stable_mir::ty::{ConstantKind, IndexedVal, MirConst, Ty};
1111

1212
use crate::printer::SmirJson;
1313

14-
use super::index::{AllocIndex, TypeIndex};
14+
use super::index::{AllocIndex, LayoutInfo, TypeEntry, TypeIndex, TypeKind};
1515
use super::util::{function_string, short_fn_name, GraphLabelString};
1616

1717
// =============================================================================
@@ -255,4 +255,151 @@ impl GraphContext {
255255
InlineAsm { .. } => "InlineAsm".to_string(),
256256
}
257257
}
258+
259+
// =========================================================================
260+
// Type and Layout Rendering
261+
// =========================================================================
262+
263+
/// Get detailed type information for a type
264+
pub fn get_type_entry(&self, ty: Ty) -> Option<&TypeEntry> {
265+
self.types.get(ty)
266+
}
267+
268+
/// Get layout information for a type
269+
pub fn get_layout(&self, ty: Ty) -> Option<&LayoutInfo> {
270+
self.types.get_layout(ty)
271+
}
272+
273+
/// Render a type with its size and alignment
274+
pub fn render_type_with_layout(&self, ty: Ty) -> String {
275+
let name = self.types.get_name(ty);
276+
match self.types.get_layout(ty) {
277+
Some(layout) => format!("{} ({} bytes, align {})", name, layout.size, layout.align),
278+
None => name,
279+
}
280+
}
281+
282+
/// Render a type with detailed field layout (for structs/unions)
283+
pub fn render_type_detailed(&self, ty: Ty) -> String {
284+
match self.types.get(ty) {
285+
Some(entry) => entry.detailed_description(&self.types),
286+
None => format!("{}", ty),
287+
}
288+
}
289+
290+
/// Generate lines describing a type's memory layout
291+
pub fn render_type_layout_lines(&self, ty: Ty) -> Vec<String> {
292+
let mut lines = Vec::new();
293+
let entry = match self.types.get(ty) {
294+
Some(e) => e,
295+
None => {
296+
lines.push(format!("{}", ty));
297+
return lines;
298+
}
299+
};
300+
301+
// Header with size/align
302+
let header = match &entry.layout {
303+
Some(layout) => format!(
304+
"{} ({} bytes, align {})",
305+
entry.name, layout.size, layout.align
306+
),
307+
None => entry.name.clone(),
308+
};
309+
lines.push(header);
310+
311+
// Field details for composite types
312+
match &entry.kind {
313+
TypeKind::Struct { fields } => {
314+
for (i, field) in fields.iter().enumerate() {
315+
let field_ty_name = self.types.get_name(field.ty);
316+
let field_layout = self.types.get_layout(field.ty);
317+
let size_str = field_layout
318+
.map(|l| format!(" ({} bytes)", l.size))
319+
.unwrap_or_default();
320+
match field.offset {
321+
Some(off) => lines.push(format!(
322+
" @{:3}: field{}: {}{}",
323+
off, i, field_ty_name, size_str
324+
)),
325+
None => lines.push(format!(" field{}: {}{}", i, field_ty_name, size_str)),
326+
}
327+
}
328+
}
329+
TypeKind::Union { fields } => {
330+
lines.push(" (all fields at offset 0)".to_string());
331+
for (i, field) in fields.iter().enumerate() {
332+
let field_ty_name = self.types.get_name(field.ty);
333+
let field_layout = self.types.get_layout(field.ty);
334+
let size_str = field_layout
335+
.map(|l| format!(" ({} bytes)", l.size))
336+
.unwrap_or_default();
337+
lines.push(format!(" field{}: {}{}", i, field_ty_name, size_str));
338+
}
339+
}
340+
TypeKind::Enum { variants } => {
341+
for (i, variant) in variants.iter().enumerate() {
342+
lines.push(format!(
343+
" variant {} (discriminant {}):",
344+
i, variant.discriminant
345+
));
346+
for (j, field) in variant.fields.iter().enumerate() {
347+
let field_ty_name = self.types.get_name(field.ty);
348+
lines.push(format!(" field{}: {}", j, field_ty_name));
349+
}
350+
}
351+
}
352+
TypeKind::Tuple { fields } => {
353+
for (i, &field_ty) in fields.iter().enumerate() {
354+
let field_ty_name = self.types.get_name(field_ty);
355+
let offset = entry.layout.as_ref().and_then(|l| l.field_offset(i));
356+
match offset {
357+
Some(off) => lines.push(format!(" @{:3}: .{}: {}", off, i, field_ty_name)),
358+
None => lines.push(format!(" .{}: {}", i, field_ty_name)),
359+
}
360+
}
361+
}
362+
TypeKind::Array { elem_ty, len } => {
363+
let elem_name = self.types.get_name(*elem_ty);
364+
let len_str = len.map(|l| l.to_string()).unwrap_or("?".to_string());
365+
lines.push(format!(" [{}; {}]", elem_name, len_str));
366+
}
367+
_ => {}
368+
}
369+
370+
lines
371+
}
372+
373+
/// Generate the types legend as lines for display (types with layout info)
374+
pub fn types_legend_lines(&self) -> Vec<String> {
375+
let mut lines = vec!["TYPES".to_string()];
376+
377+
// Collect and sort types that have interesting layout info
378+
let mut entries: Vec<_> = self
379+
.types
380+
.iter()
381+
.filter(|(_, entry)| {
382+
// Only include composite types with layout
383+
matches!(
384+
entry.kind,
385+
TypeKind::Struct { .. }
386+
| TypeKind::Union { .. }
387+
| TypeKind::Enum { .. }
388+
| TypeKind::Tuple { .. }
389+
)
390+
})
391+
.collect();
392+
entries.sort_by(|a, b| a.1.name.cmp(&b.1.name));
393+
394+
for (_ty_id, entry) in entries {
395+
let layout_str = entry
396+
.layout
397+
.as_ref()
398+
.map(|l| format!(" ({} bytes)", l.size))
399+
.unwrap_or_default();
400+
lines.push(format!("{}{}", entry.name, layout_str));
401+
}
402+
403+
lines
404+
}
258405
}

0 commit comments

Comments
 (0)