Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compress most of spans to 32 bits #44646

Merged
merged 1 commit into from
Sep 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/ty/maps/plumbing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ macro_rules! define_maps {

profq_msg!(tcx,
ProfileQueriesMsg::QueryBegin(
span.clone(),
span.data(),
QueryMsg::$name(profq_key!(tcx, key))
)
);
Expand Down
5 changes: 3 additions & 2 deletions src/librustc/util/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use std::path::Path;
use std::time::{Duration, Instant};

use std::sync::mpsc::{Sender};
use syntax_pos::{Span};
use syntax_pos::{SpanData};
use ty::maps::{QueryMsg};
use dep_graph::{DepNode};

Expand Down Expand Up @@ -61,7 +61,8 @@ pub enum ProfileQueriesMsg {
/// end a task
TaskEnd,
/// begin a new query
QueryBegin(Span, QueryMsg),
/// can't use `Span` because queries are sent to other thread
QueryBegin(SpanData, QueryMsg),
/// query is satisfied by using an already-known value for the given key
CacheHit,
/// query requires running a provider; providers may nest, permitting queries to nest.
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_driver/profile/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// except according to those terms.

use super::*;
use syntax_pos::Span;
use syntax_pos::SpanData;
use rustc::ty::maps::QueryMsg;
use std::fs::File;
use std::time::{Duration, Instant};
Expand All @@ -18,7 +18,7 @@ use rustc::dep_graph::{DepNode};

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Query {
pub span: Span,
pub span: SpanData,
pub msg: QueryMsg,
}
pub enum Effect {
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax_pos/hygiene.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use std::fmt;

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

#[derive(Copy, Clone, Default)]
pub struct SyntaxContextData {
Expand Down
64 changes: 38 additions & 26 deletions src/libsyntax_pos/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@
#![feature(optin_builtin_traits)]
#![allow(unused_attributes)]
#![feature(specialization)]
#![feature(staged_api)]

use std::borrow::Cow;
use std::cell::{Cell, RefCell};
use std::cmp;
use std::cmp::{self, Ordering};
use std::fmt;
use std::hash::Hasher;
use std::ops::{Add, Sub};
Expand All @@ -47,6 +46,9 @@ extern crate serialize as rustc_serialize; // used by deriving
pub mod hygiene;
pub use hygiene::{SyntaxContext, ExpnInfo, ExpnFormat, NameAndSpan, CompilerDesugaringKind};

mod span_encoding;
pub use span_encoding::{Span, DUMMY_SP};

pub mod symbol;

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

#[allow(deprecated)]
pub const DUMMY_SP: Span = Span { lo: BytePos(0), hi: BytePos(0), ctxt: NO_EXPANSION };
// The interner in thread-local, so `Span` shouldn't move between threads.
impl !Send for Span {}
impl !Sync for Span {}

impl PartialOrd for Span {
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
PartialOrd::partial_cmp(&self.data(), &rhs.data())
}
}
impl Ord for Span {
fn cmp(&self, rhs: &Self) -> Ordering {
Ord::cmp(&self.data(), &rhs.data())
}
}

/// A collection of spans. Spans have two orthogonal attributes:
///
Expand All @@ -90,38 +102,32 @@ pub struct MultiSpan {
}

impl Span {
#[allow(deprecated)]
#[inline]
pub fn new(lo: BytePos, hi: BytePos, ctxt: SyntaxContext) -> Self {
if lo <= hi { Span { lo, hi, ctxt } } else { Span { lo: hi, hi: lo, ctxt } }
}

#[allow(deprecated)]
#[inline]
pub fn lo(self) -> BytePos {
self.lo
self.data().lo
}
#[inline]
pub fn with_lo(self, lo: BytePos) -> Span {
Span::new(lo, self.hi(), self.ctxt())
let base = self.data();
Span::new(lo, base.hi, base.ctxt)
}
#[allow(deprecated)]
#[inline]
pub fn hi(self) -> BytePos {
self.hi
self.data().hi
}
#[inline]
pub fn with_hi(self, hi: BytePos) -> Span {
Span::new(self.lo(), hi, self.ctxt())
let base = self.data();
Span::new(base.lo, hi, base.ctxt)
}
#[allow(deprecated)]
#[inline]
pub fn ctxt(self) -> SyntaxContext {
self.ctxt
self.data().ctxt
}
#[inline]
pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
Span::new(self.lo(), self.hi(), ctxt)
let base = self.data();
Span::new(base.lo, base.hi, ctxt)
}

/// Returns a new span representing just the end-point of this span
Expand Down Expand Up @@ -342,6 +348,12 @@ impl fmt::Debug for Span {
}
}

impl fmt::Debug for SpanData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
SPAN_DEBUG.with(|span_debug| span_debug.get()(Span::new(self.lo, self.hi, self.ctxt), f))
}
}

impl MultiSpan {
pub fn new() -> MultiSpan {
MultiSpan {
Expand Down
143 changes: 143 additions & 0 deletions src/libsyntax_pos/span_encoding.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// Spans are encoded using 1-bit tag and 2 different encoding formats (one for each tag value).
// One format is used for keeping span data inline,
// another contains index into an out-of-line span interner.
// The encoding format for inline spans were obtained by optimizing over crates in rustc/libstd.
// See https://internals.rust-lang.org/t/rfc-compiler-refactoring-spans/1357/28

use {BytePos, SpanData};
use hygiene::SyntaxContext;

use rustc_data_structures::fx::FxHashMap;
use std::cell::RefCell;

/// A compressed span.
/// Contains either fields of `SpanData` inline if they are small, or index into span interner.
/// The primary goal of `Span` is to be as small as possible and fit into other structures
/// (that's why it uses `packed` as well). Decoding speed is the second priority.
/// See `SpanData` for the info on span fields in decoded representation.
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[repr(packed)]
pub struct Span(u32);

/// Dummy span, both position and length are zero, syntax context is zero as well.
/// This span is kept inline and encoded with format 0.
pub const DUMMY_SP: Span = Span(0);

impl Span {
#[inline]
pub fn new(lo: BytePos, hi: BytePos, ctxt: SyntaxContext) -> Self {
encode(&match lo <= hi {
true => SpanData { lo, hi, ctxt },
false => SpanData { lo: hi, hi: lo, ctxt },
})
}

#[inline]
pub fn data(self) -> SpanData {
decode(self)
}
}

// Tags
const TAG_INLINE: u32 = 0;
const TAG_INTERNED: u32 = 1;
const TAG_MASK: u32 = 1;

// Fields indexes
const BASE_INDEX: usize = 0;
const LEN_INDEX: usize = 1;
const CTXT_INDEX: usize = 2;

// Tag = 0, inline format.
// -----------------------------------
// | base 31:8 | len 7:1 | tag 0:0 |
// -----------------------------------
const INLINE_SIZES: [u32; 3] = [24, 7, 0];
const INLINE_OFFSETS: [u32; 3] = [8, 1, 1];

// Tag = 1, interned format.
// ------------------------
// | index 31:1 | tag 0:0 |
// ------------------------
const INTERNED_INDEX_SIZE: u32 = 31;
const INTERNED_INDEX_OFFSET: u32 = 1;

#[inline]
fn encode(sd: &SpanData) -> Span {
let (base, len, ctxt) = (sd.lo.0, sd.hi.0 - sd.lo.0, sd.ctxt.0);

let val = if (base >> INLINE_SIZES[BASE_INDEX]) == 0 &&
(len >> INLINE_SIZES[LEN_INDEX]) == 0 &&
(ctxt >> INLINE_SIZES[CTXT_INDEX]) == 0 {
(base << INLINE_OFFSETS[BASE_INDEX]) | (len << INLINE_OFFSETS[LEN_INDEX]) |
(ctxt << INLINE_OFFSETS[CTXT_INDEX]) | TAG_INLINE
} else {
let index = with_span_interner(|interner| interner.intern(sd));
(index << INTERNED_INDEX_OFFSET) | TAG_INTERNED
};
Span(val)
}

#[inline]
fn decode(span: Span) -> SpanData {
let val = span.0;

// Extract a field at position `pos` having size `size`.
let extract = |pos: u32, size: u32| {
let mask = ((!0u32) as u64 >> (32 - size)) as u32; // Can't shift u32 by 32
(val >> pos) & mask
};

let (base, len, ctxt) = if val & TAG_MASK == TAG_INLINE {(
extract(INLINE_OFFSETS[BASE_INDEX], INLINE_SIZES[BASE_INDEX]),
extract(INLINE_OFFSETS[LEN_INDEX], INLINE_SIZES[LEN_INDEX]),
extract(INLINE_OFFSETS[CTXT_INDEX], INLINE_SIZES[CTXT_INDEX]),
)} else {
let index = extract(INTERNED_INDEX_OFFSET, INTERNED_INDEX_SIZE);
return with_span_interner(|interner| *interner.get(index));
};
SpanData { lo: BytePos(base), hi: BytePos(base + len), ctxt: SyntaxContext(ctxt) }
}

#[derive(Default)]
struct SpanInterner {
spans: FxHashMap<SpanData, u32>,
span_data: Vec<SpanData>,
}

impl SpanInterner {
fn intern(&mut self, span_data: &SpanData) -> u32 {
if let Some(index) = self.spans.get(span_data) {
return *index;
}

let index = self.spans.len() as u32;
self.span_data.push(*span_data);
self.spans.insert(*span_data, index);
index
}

#[inline]
fn get(&self, index: u32) -> &SpanData {
&self.span_data[index as usize]
}
}

// If an interner exists in TLS, return it. Otherwise, prepare a fresh one.
#[inline]
fn with_span_interner<T, F: FnOnce(&mut SpanInterner) -> T>(f: F) -> T {
thread_local!(static INTERNER: RefCell<SpanInterner> = {
RefCell::new(SpanInterner::default())
});
INTERNER.with(|interner| f(&mut *interner.borrow_mut()))
}