]> git.lizzy.rs Git - rust.git/commitdiff
Compress "small" spans to 32 bits and intern "large" spans
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sat, 16 Sep 2017 18:43:05 +0000 (21:43 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Fri, 22 Sep 2017 21:34:13 +0000 (00:34 +0300)
src/librustc/ty/maps/plumbing.rs
src/librustc/util/common.rs
src/librustc_driver/profile/trace.rs
src/libsyntax_pos/hygiene.rs
src/libsyntax_pos/lib.rs
src/libsyntax_pos/span_encoding.rs [new file with mode: 0644]

index 87a9eef0de5329fe99f80db7d903e7eecf5e2848..581f47dc13cdf466415b57b6e34ff54beb2a3e79 100644 (file)
@@ -221,7 +221,7 @@ fn try_get_with<F, R>(tcx: TyCtxt<'a, $tcx, 'lcx>,
 
                 profq_msg!(tcx,
                     ProfileQueriesMsg::QueryBegin(
-                        span.clone(),
+                        span.data(),
                         QueryMsg::$name(profq_key!(tcx, key))
                     )
                 );
index 618a4ed331e766056cf1a207a86cd5dca747902e..9e566d2b9071f46d1dccb28c259707dc14cb9a3c 100644 (file)
@@ -20,7 +20,7 @@
 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};
 
@@ -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.
index f5079836c3ca40f6d248b42fd024bb5d23777cd3..280f3c8c79677d26e1228e506fa85db151fe9f3c 100644 (file)
@@ -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};
@@ -18,7 +18,7 @@
 
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub struct Query {
-    pub span: Span,
+    pub span: SpanData,
     pub msg: QueryMsg,
 }
 pub enum Effect {
index 919804d7efd6fe1aa366b26cc5960cd2910305f4..4790fa0a7edc2ed430640f4bc0ce21d742ec58b9 100644 (file)
@@ -25,7 +25,7 @@
 
 /// 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 {
index 27fbca19dcc4ca3a0211eb262ae3885df99ceb19..582f27981813417d7bcd3cd97d45e3441cf5c201 100644 (file)
 #![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};
@@ -47,6 +46,9 @@
 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;
 /// 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:
 ///
@@ -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
@@ -342,6 +348,12 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+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 {
diff --git a/src/libsyntax_pos/span_encoding.rs b/src/libsyntax_pos/span_encoding.rs
new file mode 100644 (file)
index 0000000..c2b3217
--- /dev/null
@@ -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()))
+}