Skip to content

Commit f069c88

Browse files
committed
Compress "small" spans to 32 bits and intern "large" spans
1 parent 3a7b960 commit f069c88

File tree

6 files changed

+227
-32
lines changed

6 files changed

+227
-32
lines changed

src/librustc/ty/maps.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ macro_rules! define_maps {
895895

896896
profq_msg!(tcx,
897897
ProfileQueriesMsg::QueryBegin(
898-
span.clone(),
898+
span.data(),
899899
QueryMsg::$name(profq_key!(tcx, key))
900900
)
901901
);

src/librustc/util/common.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::path::Path;
2020
use std::time::{Duration, Instant};
2121

2222
use std::sync::mpsc::{Sender};
23-
use syntax_pos::{Span};
23+
use syntax_pos::{SpanData};
2424
use ty::maps::{QueryMsg};
2525
use dep_graph::{DepNode};
2626

@@ -61,7 +61,8 @@ pub enum ProfileQueriesMsg {
6161
/// end a task
6262
TaskEnd,
6363
/// begin a new query
64-
QueryBegin(Span, QueryMsg),
64+
/// can't use `Span` because queries are sent to other thread
65+
QueryBegin(SpanData, QueryMsg),
6566
/// query is satisfied by using an already-known value for the given key
6667
CacheHit,
6768
/// query requires running a provider; providers may nest, permitting queries to nest.

src/librustc_driver/profile/trace.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use super::*;
12-
use syntax_pos::Span;
12+
use syntax_pos::SpanData;
1313
use rustc::ty::maps::QueryMsg;
1414
use std::fs::File;
1515
use std::time::{Duration, Instant};
@@ -18,7 +18,7 @@ use rustc::dep_graph::{DepNode};
1818

1919
#[derive(Debug, Clone, Eq, PartialEq)]
2020
pub struct Query {
21-
pub span: Span,
21+
pub span: SpanData,
2222
pub msg: QueryMsg,
2323
}
2424
pub enum Effect {

src/libsyntax_pos/hygiene.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use std::fmt;
2525

2626
/// A SyntaxContext represents a chain of macro expansions (represented by marks).
2727
#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
28-
pub struct SyntaxContext(u32);
28+
pub struct SyntaxContext(pub(super) u32);
2929

3030
#[derive(Copy, Clone, Default)]
3131
pub struct SyntaxContextData {

src/libsyntax_pos/lib.rs

+39-26
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
#![feature(optin_builtin_traits)]
2626
#![allow(unused_attributes)]
2727
#![feature(specialization)]
28-
#![feature(staged_api)]
2928

3029
use std::borrow::Cow;
3130
use std::cell::{Cell, RefCell};
32-
use std::cmp;
31+
use std::cmp::{self, Ordering};
32+
use std::collections::HashMap;
3333
use std::fmt;
3434
use std::hash::Hasher;
3535
use std::ops::{Add, Sub};
@@ -47,6 +47,9 @@ extern crate serialize as rustc_serialize; // used by deriving
4747
pub mod hygiene;
4848
pub use hygiene::{SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind};
4949

50+
mod span_encoding;
51+
pub use span_encoding::{Span, DUMMY_SP};
52+
5053
pub mod symbol;
5154

5255
pub type FileName = String;
@@ -59,23 +62,33 @@ pub type FileName = String;
5962
/// able to use many of the functions on spans in codemap and you cannot assume
6063
/// that the length of the span = hi - lo; there may be space in the BytePos
6164
/// range between files.
65+
///
66+
/// `SpanData` is public because `Span` uses a thread-local interner and can't be
67+
/// sent to other threads, but some pieces of performance infra run in a separate thread.
68+
/// Using `Span` is generally preferred.
6269
#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)]
63-
pub struct Span {
64-
#[unstable(feature = "rustc_private", issue = "27812")]
65-
#[rustc_deprecated(since = "1.21", reason = "use getters/setters instead")]
70+
pub struct SpanData {
6671
pub lo: BytePos,
67-
#[unstable(feature = "rustc_private", issue = "27812")]
68-
#[rustc_deprecated(since = "1.21", reason = "use getters/setters instead")]
6972
pub hi: BytePos,
7073
/// Information about where the macro came from, if this piece of
7174
/// code was created by a macro expansion.
72-
#[unstable(feature = "rustc_private", issue = "27812")]
73-
#[rustc_deprecated(since = "1.21", reason = "use getters/setters instead")]
7475
pub ctxt: SyntaxContext,
7576
}
7677

77-
#[allow(deprecated)]
78-
pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), ctxt: NO_EXPANSION };
78+
// The interner in thread-local, so `Span` shouldn't move between threads.
79+
impl !Send for Span {}
80+
impl !Sync for Span {}
81+
82+
impl PartialOrd for Span {
83+
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
84+
PartialOrd::partial_cmp(&self.data(), &rhs.data())
85+
}
86+
}
87+
impl Ord for Span {
88+
fn cmp(&self, rhs: &Self) -> Ordering {
89+
Ord::cmp(&self.data(), &rhs.data())
90+
}
91+
}
7992

8093
/// A collection of spans. Spans have two orthogonal attributes:
8194
///
@@ -90,38 +103,32 @@ pub struct MultiSpan {
90103
}
91104

92105
impl Span {
93-
#[allow(deprecated)]
94-
#[inline]
95-
pub fn new(lo: BytePos, hi: BytePos, ctxt: SyntaxContext) -> Self {
96-
if lo <= hi { Span { lo, hi, ctxt } } else { Span { lo: hi, hi: lo, ctxt } }
97-
}
98-
99-
#[allow(deprecated)]
100106
#[inline]
101107
pub fn lo(self) -> BytePos {
102-
self.lo
108+
self.data().lo
103109
}
104110
#[inline]
105111
pub fn with_lo(self, lo: BytePos) -> Span {
106-
Span::new(lo, self.hi(), self.ctxt())
112+
let base = self.data();
113+
Span::new(lo, base.hi, base.ctxt)
107114
}
108-
#[allow(deprecated)]
109115
#[inline]
110116
pub fn hi(self) -> BytePos {
111-
self.hi
117+
self.data().hi
112118
}
113119
#[inline]
114120
pub fn with_hi(self, hi: BytePos) -> Span {
115-
Span::new(self.lo(), hi, self.ctxt())
121+
let base = self.data();
122+
Span::new(base.lo, hi, base.ctxt)
116123
}
117-
#[allow(deprecated)]
118124
#[inline]
119125
pub fn ctxt(self) -> SyntaxContext {
120-
self.ctxt
126+
self.data().ctxt
121127
}
122128
#[inline]
123129
pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
124-
Span::new(self.lo(), self.hi(), ctxt)
130+
let base = self.data();
131+
Span::new(base.lo, base.hi, ctxt)
125132
}
126133

127134
/// Returns a new span representing just the end-point of this span
@@ -342,6 +349,12 @@ impl fmt::Debug for Span {
342349
}
343350
}
344351

352+
impl fmt::Debug for SpanData {
353+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
354+
SPAN_DEBUG.with(|span_debug| span_debug.get()(Span::new(self.lo, self.hi, self.ctxt), f))
355+
}
356+
}
357+
345358
impl MultiSpan {
346359
pub fn new() -> MultiSpan {
347360
MultiSpan {

src/libsyntax_pos/span_encoding.rs

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Spans are encoded using 2-bit tag and 4 different encoding formats for each tag.
12+
// Three formats are used for keeping span data inline,
13+
// the fourth one contains index into out-of-line span interner.
14+
// The encoding formats for inline spans were obtained by optimizing over crates in rustc/libstd.
15+
// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28
16+
17+
use super::*;
18+
19+
/// A compressed span.
20+
/// Contains either fields of `SpanData` inline if they are small, or index into span interner.
21+
/// The primary goal of `Span` is to be as small as possible and fit into other structures
22+
/// (that's why it uses `packed` as well). Decoding speed is the second priority.
23+
/// See `SpanData` for the info on span fields in decoded representation.
24+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
25+
#[repr(packed)]
26+
pub struct Span(u32);
27+
28+
/// Dummy span, both position and length are zero, syntax context is zero as well.
29+
/// This span is kept inline and encoded with format 0.
30+
pub const DUMMY_SP: Span = Span(0);
31+
32+
impl Span {
33+
#[inline]
34+
pub fn new(lo: BytePos, hi: BytePos, ctxt: SyntaxContext) -> Self {
35+
encode(&match lo <= hi {
36+
true => SpanData { lo, hi, ctxt },
37+
false => SpanData { lo: hi, hi: lo, ctxt },
38+
})
39+
}
40+
41+
#[inline]
42+
pub fn data(self) -> SpanData {
43+
decode(self)
44+
}
45+
}
46+
47+
// Tags
48+
const TAG_INLINE0: u32 = 0b00;
49+
const TAG_INLINE1: u32 = 0b01;
50+
const TAG_INLINE2: u32 = 0b10;
51+
const TAG_INTERNED: u32 = 0b11;
52+
const TAG_MASK: u32 = 0b11;
53+
54+
// Fields indexes
55+
const BASE_INDEX: usize = 0;
56+
const LEN_INDEX: usize = 1;
57+
const CTXT_INDEX: usize = 2;
58+
59+
// Tag = 0b00, inline format 0.
60+
// -----------------------------------
61+
// | base 31:8 | len 7:2 | tag 1:0 |
62+
// -----------------------------------
63+
const INLINE0_SIZES: [u32; 3] = [24, 6, 0];
64+
const INLINE0_OFFSETS: [u32; 3] = [8, 2, 2];
65+
66+
// Tag = 0b01, inline format 1.
67+
// -----------------------------------
68+
// | base 31:10 | len 9:2 | tag 1:0 |
69+
// -----------------------------------
70+
const INLINE1_SIZES: [u32; 3] = [22, 8, 0];
71+
const INLINE1_OFFSETS: [u32; 3] = [10, 2, 2];
72+
73+
// Tag = 0b10, inline format 2.
74+
// ------------------------------------------------
75+
// | base 31:14 | len 13:13 | ctxt 12:2 | tag 1:0 |
76+
// ------------------------------------------------
77+
const INLINE2_SIZES: [u32; 3] = [18, 1, 11];
78+
const INLINE2_OFFSETS: [u32; 3] = [14, 13, 2];
79+
80+
// Tag = 0b11, interned format.
81+
// ------------------------
82+
// | index 31:3 | tag 1:0 |
83+
// ------------------------
84+
const INTERNED_INDEX_SIZE: u32 = 30;
85+
const INTERNED_INDEX_OFFSET: u32 = 2;
86+
87+
fn encode(sd: &SpanData) -> Span {
88+
let (base, len, ctxt) = (sd.lo.0, sd.hi.0 - sd.lo.0, sd.ctxt.0);
89+
90+
// Can we fit the span data into this encoding?
91+
let fits = |sizes: [u32; 3]| {
92+
(base >> sizes[BASE_INDEX]) == 0 && (len >> sizes[LEN_INDEX]) == 0 &&
93+
(ctxt >> sizes[CTXT_INDEX]) == 0
94+
};
95+
// Turn fields into a single `u32` value.
96+
let compose = |offsets: [u32; 3], tag| {
97+
(base << offsets[BASE_INDEX]) | (len << offsets[LEN_INDEX]) |
98+
(ctxt << offsets[CTXT_INDEX]) | tag
99+
};
100+
101+
let val = if fits(INLINE0_SIZES) {
102+
compose(INLINE0_OFFSETS, TAG_INLINE0)
103+
} else if fits(INLINE1_SIZES) {
104+
compose(INLINE1_OFFSETS, TAG_INLINE1)
105+
} else if fits(INLINE2_SIZES) {
106+
compose(INLINE2_OFFSETS, TAG_INLINE2)
107+
} else {
108+
let index = with_span_interner(|interner| interner.intern(sd));
109+
if (index >> INTERNED_INDEX_SIZE) == 0 {
110+
(index << INTERNED_INDEX_OFFSET) | TAG_INTERNED
111+
} else {
112+
panic!("too many spans in a crate");
113+
}
114+
};
115+
Span(val)
116+
}
117+
118+
fn decode(span: Span) -> SpanData {
119+
let val = span.0;
120+
121+
// Extract a field at position `pos` having size `size`.
122+
let extract = |pos, size| {
123+
let mask = ((!0u32) as u64 >> (32 - size)) as u32; // Can't shift u32 by 32
124+
(val >> pos) & mask
125+
};
126+
127+
let (base, len, ctxt) = match val & TAG_MASK {
128+
TAG_INLINE0 => (
129+
extract(INLINE0_OFFSETS[BASE_INDEX], INLINE0_SIZES[BASE_INDEX]),
130+
extract(INLINE0_OFFSETS[LEN_INDEX], INLINE0_SIZES[LEN_INDEX]),
131+
extract(INLINE0_OFFSETS[CTXT_INDEX], INLINE0_SIZES[CTXT_INDEX]),
132+
),
133+
TAG_INLINE1 => (
134+
extract(INLINE1_OFFSETS[BASE_INDEX], INLINE1_SIZES[BASE_INDEX]),
135+
extract(INLINE1_OFFSETS[LEN_INDEX], INLINE1_SIZES[LEN_INDEX]),
136+
extract(INLINE1_OFFSETS[CTXT_INDEX], INLINE1_SIZES[CTXT_INDEX]),
137+
),
138+
TAG_INLINE2 => (
139+
extract(INLINE2_OFFSETS[BASE_INDEX], INLINE2_SIZES[BASE_INDEX]),
140+
extract(INLINE2_OFFSETS[LEN_INDEX], INLINE2_SIZES[LEN_INDEX]),
141+
extract(INLINE2_OFFSETS[CTXT_INDEX], INLINE2_SIZES[CTXT_INDEX]),
142+
),
143+
TAG_INTERNED => {
144+
let index = extract(INTERNED_INDEX_OFFSET, INTERNED_INDEX_SIZE);
145+
return with_span_interner(|interner| *interner.get(index));
146+
}
147+
_ => unreachable!()
148+
};
149+
SpanData { lo: BytePos(base), hi: BytePos(base + len), ctxt: SyntaxContext(ctxt) }
150+
}
151+
152+
#[derive(Default)]
153+
struct SpanInterner {
154+
spans: HashMap<SpanData, u32>,
155+
span_data: Vec<SpanData>,
156+
}
157+
158+
impl SpanInterner {
159+
fn intern(&mut self, span_data: &SpanData) -> u32 {
160+
if let Some(index) = self.spans.get(span_data) {
161+
return *index;
162+
}
163+
164+
let index = self.spans.len() as u32;
165+
self.span_data.push(*span_data);
166+
self.spans.insert(*span_data, index);
167+
index
168+
}
169+
170+
fn get(&self, index: u32) -> &SpanData {
171+
&self.span_data[index as usize]
172+
}
173+
}
174+
175+
// If an interner exists in TLS, return it. Otherwise, prepare a fresh one.
176+
fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
177+
thread_local!(static INTERNER: RefCell<SpanInterner> = {
178+
RefCell::new(SpanInterner::default())
179+
});
180+
INTERNER.with(|interner| f(&mut *interner.borrow_mut()))
181+
}

0 commit comments

Comments
 (0)