blob: 6a4171afe4bc10f084e7a6f7f85437c1a1da3785 [file] [log] [blame] [edit]
//! Support for serializing the dep-graph and reloading it.
// tidy-alphabetical-start
#![allow(internal_features)]
#![feature(adt_const_params)]
#![feature(core_intrinsics)]
#![feature(min_specialization)]
#![feature(rustc_attrs)]
#![feature(try_blocks)]
// tidy-alphabetical-end
use std::marker::ConstParamTy;
use rustc_data_structures::sync::AtomicU64;
use rustc_middle::dep_graph::{self, DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex};
use rustc_middle::queries::{
self, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates,
};
use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache};
use rustc_middle::query::plumbing::{
HashResult, QueryState, QuerySystem, QuerySystemFns, QueryVTable,
};
use rustc_middle::query::{AsLocalKey, CycleError, CycleErrorHandling, QueryCache, QueryMode};
use rustc_middle::ty::TyCtxt;
use rustc_span::{ErrorGuaranteed, Span};
pub use crate::dep_kind_vtables::make_dep_kind_vtables;
pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack};
pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all};
use crate::plumbing::{encode_all_query_results, try_mark_green};
use crate::profiling_support::QueryKeyStringCache;
pub use crate::profiling_support::alloc_self_profile_query_strings;
use crate::values::Value;
#[macro_use]
mod plumbing;
mod dep_kind_vtables;
mod error;
mod execution;
mod job;
mod profiling_support;
mod values;
#[derive(ConstParamTy)] // Allow this struct to be used for const-generic values.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct QueryFlags {
/// True if this query has the `anon` modifier.
is_anon: bool,
/// True if this query has the `depth_limit` modifier.
is_depth_limit: bool,
/// True if this query has the `feedable` modifier.
is_feedable: bool,
}
/// Combines a [`QueryVTable`] with some additional compile-time booleans.
/// "Dispatcher" should be understood as a near-synonym of "vtable".
///
/// Baking these boolean flags into the type gives a modest but measurable
/// improvement to compiler perf and compiler code size; see
/// <https://github.com/rust-lang/rust/pull/151633>.
struct SemiDynamicQueryDispatcher<'tcx, C: QueryCache, const FLAGS: QueryFlags> {
vtable: &'tcx QueryVTable<'tcx, C>,
}
// Manually implement Copy/Clone, because deriving would put trait bounds on the cache type.
impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Copy
for SemiDynamicQueryDispatcher<'tcx, C, FLAGS>
{
}
impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Clone
for SemiDynamicQueryDispatcher<'tcx, C, FLAGS>
{
fn clone(&self) -> Self {
*self
}
}
impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> SemiDynamicQueryDispatcher<'tcx, C, FLAGS> {
#[inline(always)]
fn name(self) -> &'static str {
self.vtable.name
}
#[inline(always)]
fn will_cache_on_disk_for_key(self, tcx: TyCtxt<'tcx>, key: &C::Key) -> bool {
self.vtable.will_cache_on_disk_for_key_fn.map_or(false, |f| f(tcx, key))
}
// Don't use this method to access query results, instead use the methods on TyCtxt.
#[inline(always)]
fn query_state(self, qcx: QueryCtxt<'tcx>) -> &'tcx QueryState<'tcx, C::Key> {
// Safety:
// This is just manually doing the subfield referencing through pointer math.
unsafe {
&*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>)
.byte_add(self.vtable.query_state)
.cast::<QueryState<'tcx, C::Key>>()
}
}
// Don't use this method to access query results, instead use the methods on TyCtxt.
#[inline(always)]
fn query_cache(self, qcx: QueryCtxt<'tcx>) -> &'tcx C {
// Safety:
// This is just manually doing the subfield referencing through pointer math.
unsafe {
&*(&qcx.tcx.query_system.caches as *const QueryCaches<'tcx>)
.byte_add(self.vtable.query_cache)
.cast::<C>()
}
}
/// Calls `tcx.$query(key)` for this query, and discards the returned value.
/// See [`QueryVTable::call_query_method_fn`] for details of this strange operation.
#[inline(always)]
fn call_query_method(self, tcx: TyCtxt<'tcx>, key: C::Key) {
(self.vtable.call_query_method_fn)(tcx, key)
}
/// Calls the actual provider function for this query.
/// See [`QueryVTable::invoke_provider_fn`] for more details.
#[inline(always)]
fn invoke_provider(self, qcx: QueryCtxt<'tcx>, key: C::Key) -> C::Value {
(self.vtable.invoke_provider_fn)(qcx.tcx, key)
}
#[inline(always)]
fn try_load_from_disk(
self,
qcx: QueryCtxt<'tcx>,
key: &C::Key,
prev_index: SerializedDepNodeIndex,
index: DepNodeIndex,
) -> Option<C::Value> {
// `?` will return None immediately for queries that never cache to disk.
self.vtable.try_load_from_disk_fn?(qcx.tcx, key, prev_index, index)
}
#[inline]
fn is_loadable_from_disk(
self,
qcx: QueryCtxt<'tcx>,
key: &C::Key,
index: SerializedDepNodeIndex,
) -> bool {
self.vtable.is_loadable_from_disk_fn.map_or(false, |f| f(qcx.tcx, key, index))
}
/// Synthesize an error value to let compilation continue after a cycle.
fn value_from_cycle_error(
self,
tcx: TyCtxt<'tcx>,
cycle_error: &CycleError,
guar: ErrorGuaranteed,
) -> C::Value {
(self.vtable.value_from_cycle_error)(tcx, cycle_error, guar)
}
#[inline(always)]
fn format_value(self) -> fn(&C::Value) -> String {
self.vtable.format_value
}
#[inline(always)]
fn anon(self) -> bool {
FLAGS.is_anon
}
#[inline(always)]
fn eval_always(self) -> bool {
self.vtable.eval_always
}
#[inline(always)]
fn depth_limit(self) -> bool {
FLAGS.is_depth_limit
}
#[inline(always)]
fn feedable(self) -> bool {
FLAGS.is_feedable
}
#[inline(always)]
fn dep_kind(self) -> DepKind {
self.vtable.dep_kind
}
#[inline(always)]
fn cycle_error_handling(self) -> CycleErrorHandling {
self.vtable.cycle_error_handling
}
#[inline(always)]
fn hash_result(self) -> HashResult<C::Value> {
self.vtable.hash_result
}
fn construct_dep_node(self, tcx: TyCtxt<'tcx>, key: &C::Key) -> DepNode {
DepNode::construct(tcx, self.dep_kind(), key)
}
}
/// Provides access to vtable-like operations for a query
/// (by creating a [`SemiDynamicQueryDispatcher`]),
/// but also keeps track of the "unerased" value type of the query
/// (i.e. the actual result type in the query declaration).
///
/// This trait allows some per-query code to be defined in generic functions
/// with a trait bound, instead of having to be defined inline within a macro
/// expansion.
///
/// There is one macro-generated implementation of this trait for each query,
/// on the type `rustc_query_impl::query_impl::$name::QueryType`.
trait QueryDispatcherUnerased<'tcx, C: QueryCache, const FLAGS: QueryFlags> {
type UnerasedValue;
const NAME: &'static &'static str;
fn query_dispatcher(tcx: TyCtxt<'tcx>) -> SemiDynamicQueryDispatcher<'tcx, C, FLAGS>;
fn restore_val(value: C::Value) -> Self::UnerasedValue;
}
pub fn query_system<'tcx>(
local_providers: Providers,
extern_providers: ExternProviders,
on_disk_cache: Option<OnDiskCache>,
incremental: bool,
) -> QuerySystem<'tcx> {
QuerySystem {
states: Default::default(),
arenas: Default::default(),
caches: Default::default(),
query_vtables: make_query_vtables(),
on_disk_cache,
fns: QuerySystemFns {
engine: engine(incremental),
local_providers,
extern_providers,
encode_query_results: encode_all_query_results,
try_mark_green,
},
jobs: AtomicU64::new(1),
}
}
rustc_middle::rustc_with_all_queries! { define_queries! }
pub fn provide(providers: &mut rustc_middle::util::Providers) {
providers.hooks.alloc_self_profile_query_strings = alloc_self_profile_query_strings;
providers.hooks.query_key_hash_verify_all = query_key_hash_verify_all;
}