Skip to content

Commit fe45ba6

Browse files
committed
fix comments in dynamic size/align computation logic
1 parent c3def26 commit fe45ba6

File tree

2 files changed

+56
-44
lines changed

2 files changed

+56
-44
lines changed

compiler/rustc_codegen_ssa/src/size_of_val.rs

+38-19
Original file line numberDiff line numberDiff line change
@@ -84,37 +84,41 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
8484
debug!("DST {} layout: {:?}", t, layout);
8585

8686
let i = layout.fields.count() - 1;
87-
let sized_size = layout.fields.offset(i).bytes();
87+
let unsized_offset_unadjusted = layout.fields.offset(i).bytes();
8888
let sized_align = layout.align.abi.bytes();
89-
debug!("DST {} statically sized prefix size: {} align: {}", t, sized_size, sized_align);
90-
let sized_size = bx.const_usize(sized_size);
89+
debug!(
90+
"DST {} offset of dyn field: {}, statically sized align: {}",
91+
t, unsized_offset_unadjusted, sized_align
92+
);
93+
let unsized_offset_unadjusted = bx.const_usize(unsized_offset_unadjusted);
9194
let sized_align = bx.const_usize(sized_align);
9295

9396
// Recurse to get the size of the dynamically sized field (must be
9497
// the last field).
9598
let field_ty = layout.field(bx, i).ty;
9699
let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info);
97100

98-
// FIXME (#26403, #27023): We should be adding padding
99-
// to `sized_size` (to accommodate the `unsized_align`
100-
// required of the unsized field that follows) before
101-
// summing it with `sized_size`. (Note that since #26403
102-
// is unfixed, we do not yet add the necessary padding
103-
// here. But this is where the add would go.)
101+
// # First compute the dynamic alignment
104102

105-
// Return the sum of sizes and max of aligns.
106-
let size = bx.add(sized_size, unsized_size);
107-
108-
// Packed types ignore the alignment of their fields.
103+
// Packed type alignment would have to be capped, but their tails always have known alignment.
104+
// Therefore, their alignment has already been taken into account when computing `sized_align`
105+
// and `unsized_offset_unadjusted`, so no further adjustment is needed.
109106
if let ty::Adt(def, _) = t.kind() {
110107
if def.repr().packed() {
108+
let unsized_tail =
109+
bx.tcx().struct_tail_with_normalize(field_ty, |ty| ty, || {});
110+
assert!(matches!(unsized_tail.kind(), ty::Slice(..) | ty::Str));
111+
// Therefore we do not need to adjust anything.
112+
// It's not actually correct to say that the unsized field has this alignment, but the
113+
// code below works correctly if we set this and it avoids having to compute
114+
// the actual alignment (which is `unsized_align.min(packed)`).
111115
unsized_align = sized_align;
112116
}
113117
}
114118

115119
// Choose max of two known alignments (combined value must
116120
// be aligned according to more restrictive of the two).
117-
let align = match (
121+
let full_align = match (
118122
bx.const_to_opt_u128(sized_align, false),
119123
bx.const_to_opt_u128(unsized_align, false),
120124
) {
@@ -129,6 +133,21 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
129133
}
130134
};
131135

136+
// # Then compute the dynamic size
137+
138+
// The full formula for the size would be:
139+
// let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align);
140+
// let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align);
141+
// However, `unsized_size` is a multiple of `unsized_align`.
142+
// Therefore, we can equivalently do the `align_to(unsized_align)` *after* adding `unsized_size`:
143+
// let full_size = (offset + unsized_size).align_to(unsized_align).align_to(full_align);
144+
// Furthermore, `align >= unsized_align`, and therefore we only need to do:
145+
// let full_size = (offset + unsized_size).align_to(full_align);
146+
// This formula also has the advantage of working correctly when the type is packed
147+
// and we set `unsized_align = sized_align`.
148+
149+
let full_size = bx.add(unsized_offset_unadjusted, unsized_size);
150+
132151
// Issue #27023: must add any necessary padding to `size`
133152
// (to make it a multiple of `align`) before returning it.
134153
//
@@ -140,12 +159,12 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
140159
//
141160
// `(size + (align-1)) & -align`
142161
let one = bx.const_usize(1);
143-
let addend = bx.sub(align, one);
144-
let add = bx.add(size, addend);
145-
let neg = bx.neg(align);
146-
let size = bx.and(add, neg);
162+
let addend = bx.sub(full_align, one);
163+
let add = bx.add(full_size, addend);
164+
let neg = bx.neg(full_align);
165+
let full_size = bx.and(add, neg);
147166

148-
(size, align)
167+
(full_size, full_align)
149168
}
150169
_ => bug!("size_and_align_of_dst: {t} not supported"),
151170
}

compiler/rustc_const_eval/src/interpret/eval_context.rs

+18-25
Original file line numberDiff line numberDiff line change
@@ -686,14 +686,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
686686
assert!(layout.fields.count() > 0);
687687
trace!("DST layout: {:?}", layout);
688688

689-
let sized_size = layout.fields.offset(layout.fields.count() - 1);
689+
let unsized_offset_unadjusted = layout.fields.offset(layout.fields.count() - 1);
690690
let sized_align = layout.align.abi;
691-
trace!(
692-
"DST {} statically sized prefix size: {:?} align: {:?}",
693-
layout.ty,
694-
sized_size,
695-
sized_align
696-
);
697691

698692
// Recurse to get the size of the dynamically sized field (must be
699693
// the last field). Can't have foreign types here, how would we
@@ -707,36 +701,35 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
707701
return Ok(None);
708702
};
709703

710-
// FIXME (#26403, #27023): We should be adding padding
711-
// to `sized_size` (to accommodate the `unsized_align`
712-
// required of the unsized field that follows) before
713-
// summing it with `sized_size`. (Note that since #26403
714-
// is unfixed, we do not yet add the necessary padding
715-
// here. But this is where the add would go.)
716-
717-
// Return the sum of sizes and max of aligns.
718-
let size = sized_size + unsized_size; // `Size` addition
704+
// # First compute the dynamic alignment
719705

720-
// Packed types ignore the alignment of their fields.
706+
// Packed type alignment needs to be capped.
721707
if let ty::Adt(def, _) = layout.ty.kind() {
722-
if def.repr().packed() {
723-
unsized_align = sized_align;
708+
if let Some(packed) = def.repr().pack {
709+
unsized_align = unsized_align.min(packed);
724710
}
725711
}
726712

727713
// Choose max of two known alignments (combined value must
728714
// be aligned according to more restrictive of the two).
729-
let align = sized_align.max(unsized_align);
715+
let full_align = sized_align.max(unsized_align);
716+
717+
// # Then compute the dynamic size
730718

731-
// Issue #27023: must add any necessary padding to `size`
732-
// (to make it a multiple of `align`) before returning it.
733-
let size = size.align_to(align);
719+
let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align);
720+
let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align);
721+
722+
// Just for our sanitiy's sake, assert that this is equal to what codegen would compute.
723+
assert_eq!(
724+
full_size,
725+
(unsized_offset_unadjusted + unsized_size).align_to(full_align)
726+
);
734727

735728
// Check if this brought us over the size limit.
736-
if size > self.max_size_of_val() {
729+
if full_size > self.max_size_of_val() {
737730
throw_ub!(InvalidMeta(InvalidMetaKind::TooBig));
738731
}
739-
Ok(Some((size, align)))
732+
Ok(Some((full_size, full_align)))
740733
}
741734
ty::Dynamic(_, _, ty::Dyn) => {
742735
let vtable = metadata.unwrap_meta().to_pointer(self)?;

0 commit comments

Comments
 (0)