Support relative offsets when encoding spans

The relative offset is often smaller than the absolute offset, and with
the LEB128 encoding, this ends up cutting the overall metadata size
considerably (~1.5 megabytes on libcore). We can support both relative
and absolute encodings essentially for free since we already take a full
byte to differentiate between direct and indirect encodings (so an extra
variant is quite cheap).
This commit is contained in:
Mark Rousskov 2023-12-25 11:49:51 -05:00
parent 71696e516d
commit 9c5293c27b
3 changed files with 24 additions and 3 deletions

View File

@ -507,10 +507,18 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnId {
impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span { impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span {
fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Span { fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Span {
let start = decoder.position();
let mode = SpanEncodingMode::decode(decoder); let mode = SpanEncodingMode::decode(decoder);
let data = match mode { let data = match mode {
SpanEncodingMode::Direct => SpanData::decode(decoder), SpanEncodingMode::Direct => SpanData::decode(decoder),
SpanEncodingMode::Shorthand(position) => decoder.with_position(position, |decoder| { SpanEncodingMode::RelativeOffset(offset) => {
decoder.with_position(start - offset, |decoder| {
let mode = SpanEncodingMode::decode(decoder);
debug_assert!(matches!(mode, SpanEncodingMode::Direct));
SpanData::decode(decoder)
})
}
SpanEncodingMode::AbsoluteOffset(addr) => decoder.with_position(addr, |decoder| {
let mode = SpanEncodingMode::decode(decoder); let mode = SpanEncodingMode::decode(decoder);
debug_assert!(matches!(mode, SpanEncodingMode::Direct)); debug_assert!(matches!(mode, SpanEncodingMode::Direct));
SpanData::decode(decoder) SpanData::decode(decoder)

View File

@ -169,7 +169,19 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnId {
impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span { impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span {
fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) { fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
match s.span_shorthands.entry(*self) { match s.span_shorthands.entry(*self) {
Entry::Occupied(o) => SpanEncodingMode::Shorthand(*o.get()).encode(s), Entry::Occupied(o) => {
// If an offset is smaller than the absolute position, we encode with the offset.
// This saves space since smaller numbers encode in less bits.
let last_location = *o.get();
// This cannot underflow. Metadata is written with increasing position(), so any
// previously saved offset must be smaller than the current position.
let offset = s.opaque.position() - last_location;
if offset < last_location {
SpanEncodingMode::RelativeOffset(offset).encode(s)
} else {
SpanEncodingMode::AbsoluteOffset(last_location).encode(s)
}
}
Entry::Vacant(v) => { Entry::Vacant(v) => {
let position = s.opaque.position(); let position = s.opaque.position();
v.insert(position); v.insert(position);

View File

@ -68,7 +68,8 @@ pub const METADATA_HEADER: &[u8] = &[b'r', b'u', b's', b't', 0, 0, 0, METADATA_V
#[derive(Encodable, Decodable)] #[derive(Encodable, Decodable)]
enum SpanEncodingMode { enum SpanEncodingMode {
Shorthand(usize), RelativeOffset(usize),
AbsoluteOffset(usize),
Direct, Direct,
} }