Skip to content

Commit

Permalink
rustc: use monomorphic const_eval for cross-crate enum discriminants.
Browse files Browse the repository at this point in the history
  • Loading branch information
eddyb committed Apr 15, 2017
1 parent 63064ec commit 6dc21b7
Show file tree
Hide file tree
Showing 14 changed files with 139 additions and 149 deletions.
47 changes: 46 additions & 1 deletion src/librustc/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1660,7 +1660,7 @@ impl<'a, 'gcx, 'tcx> AdtDef {
self.variants.iter().map(move |v| {
let mut discr = prev_discr.map_or(initial, |d| d.wrap_incr());
if let VariantDiscr::Explicit(expr_did) = v.discr {
match tcx.maps.monomorphic_const_eval.borrow()[&expr_did] {
match queries::monomorphic_const_eval::get(tcx, DUMMY_SP, expr_did) {
Ok(ConstVal::Integral(v)) => {
discr = v;
}
Expand All @@ -1673,6 +1673,51 @@ impl<'a, 'gcx, 'tcx> AdtDef {
})
}

/// Compute the discriminant value used by a specific variant.
/// Unlike `discriminants`, this is (amortized) constant-time,
/// only doing at most one query for evaluating an explicit
/// discriminant (the last one before the requested variant),
/// assuming there are no constant-evaluation errors there.
pub fn discriminant_for_variant(&self,
tcx: TyCtxt<'a, 'gcx, 'tcx>,
variant_index: usize)
-> ConstInt {
let repr_type = self.repr.discr_type();
let mut explicit_value = repr_type.initial_discriminant(tcx.global_tcx());
let mut explicit_index = variant_index;
loop {
match self.variants[explicit_index].discr {
ty::VariantDiscr::Relative(0) => break,
ty::VariantDiscr::Relative(distance) => {
explicit_index -= distance;
}
ty::VariantDiscr::Explicit(expr_did) => {
match queries::monomorphic_const_eval::get(tcx, DUMMY_SP, expr_did) {
Ok(ConstVal::Integral(v)) => {
explicit_value = v;
break;
}
_ => {
explicit_index -= 1;
}
}
}
}
}
let discr = explicit_value.to_u128_unchecked()
.wrapping_add((variant_index - explicit_index) as u128);
match repr_type {
attr::UnsignedInt(ty) => {
ConstInt::new_unsigned_truncating(discr, ty,
tcx.sess.target.uint_type)
}
attr::SignedInt(ty) => {
ConstInt::new_signed_truncating(discr as i128, ty,
tcx.sess.target.int_type)
}
}
}

pub fn destructor(&self, tcx: TyCtxt<'a, 'gcx, 'tcx>) -> Option<Destructor> {
queries::adt_destructor::get(tcx, DUMMY_SP, self.did)
}
Expand Down
11 changes: 7 additions & 4 deletions src/librustc_const_eval/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -804,10 +804,13 @@ pub fn provide(providers: &mut Providers) {
fn monomorphic_const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
def_id: DefId)
-> EvalResult<'tcx> {
ty::queries::mir_const_qualif::get(tcx, DUMMY_SP, def_id);
let cx = ConstContext::with_tables(tcx, tcx.item_tables(def_id));

let id = tcx.hir.as_local_node_id(def_id).unwrap();
let body = tcx.hir.body_owned_by(id);
cx.eval(&tcx.hir.body(body).value)
let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
ty::queries::mir_const_qualif::get(tcx, DUMMY_SP, def_id);
tcx.hir.body(tcx.hir.body_owned_by(id))
} else {
tcx.sess.cstore.maybe_get_item_body(tcx, def_id).unwrap()
};
cx.eval(&body.value)
}
2 changes: 2 additions & 0 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
let mut extern_providers = ty::maps::Providers::default();
cstore::provide(&mut extern_providers);
ty::provide_extern(&mut extern_providers);
// FIXME(eddyb) get rid of this once we replace const_eval with miri.
rustc_const_eval::provide(&mut extern_providers);

TyCtxt::create_and_enter(sess,
local_providers,
Expand Down
11 changes: 11 additions & 0 deletions src/librustc_incremental/persist/dirty_clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,17 @@ impl<'a, 'tcx, 'm> intravisit::Visitor<'tcx> for DirtyCleanMetadataVisitor<'a, '
intravisit::walk_item(self, item);
}

fn visit_variant(&mut self,
variant: &'tcx hir::Variant,
generics: &'tcx hir::Generics,
parent_id: ast::NodeId) {
if let Some(e) = variant.node.disr_expr {
self.check_item(e.node_id, variant.span);
}

intravisit::walk_variant(self, variant, generics, parent_id);
}

fn visit_variant_data(&mut self,
variant_data: &'tcx hir::VariantData,
_: ast::Name,
Expand Down
24 changes: 5 additions & 19 deletions src/librustc_metadata/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,25 +511,15 @@ impl<'a, 'tcx> CrateMetadata {
def
}

fn get_variant(&self,
item: &Entry<'tcx>,
index: DefIndex,
tcx: TyCtxt<'a, 'tcx, 'tcx>)
-> (ty::VariantDef, Option<DefIndex>) {
fn get_variant(&self, item: &Entry, index: DefIndex) -> ty::VariantDef {
let data = match item.kind {
EntryKind::Variant(data) |
EntryKind::Struct(data, _) |
EntryKind::Union(data, _) => data.decode(self),
_ => bug!(),
};

if let ty::VariantDiscr::Explicit(def_id) = data.discr {
// The original crate wouldn't have compiled if this is missing.
let result = Ok(data.evaluated_discr.unwrap());
tcx.maps.monomorphic_const_eval.borrow_mut().insert(def_id, result);
}

(ty::VariantDef {
ty::VariantDef {
did: self.local_def_id(data.struct_ctor.unwrap_or(index)),
name: self.item_name(index),
fields: item.children.decode(self).map(|index| {
Expand All @@ -542,7 +532,7 @@ impl<'a, 'tcx> CrateMetadata {
}).collect(),
discr: data.discr,
ctor_kind: data.ctor_kind,
}, data.struct_ctor)
}
}

pub fn get_adt_def(&self,
Expand All @@ -561,15 +551,11 @@ impl<'a, 'tcx> CrateMetadata {
item.children
.decode(self)
.map(|index| {
let (variant, struct_ctor) =
self.get_variant(&self.entry(index), index, tcx);
assert_eq!(struct_ctor, None);
variant
self.get_variant(&self.entry(index), index)
})
.collect()
} else {
let (variant, _struct_ctor) = self.get_variant(&item, item_id, tcx);
vec![variant]
vec![self.get_variant(&item, item_id)]
};
let (kind, repr) = match item.kind {
EntryKind::Enum(repr) => (ty::AdtKind::Enum, repr),
Expand Down
46 changes: 37 additions & 9 deletions src/librustc_metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,6 @@ impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
let data = VariantData {
ctor_kind: variant.ctor_kind,
discr: variant.discr,
evaluated_discr: match variant.discr {
ty::VariantDiscr::Explicit(def_id) => {
ty::queries::monomorphic_const_eval::get(tcx, DUMMY_SP, def_id).ok()
}
ty::VariantDiscr::Relative(_) => None
},
struct_ctor: None,
};

Expand Down Expand Up @@ -408,7 +402,6 @@ impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
let data = VariantData {
ctor_kind: variant.ctor_kind,
discr: variant.discr,
evaluated_discr: None,
struct_ctor: Some(def_id.index),
};

Expand Down Expand Up @@ -697,7 +690,6 @@ impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
EntryKind::Struct(self.lazy(&VariantData {
ctor_kind: variant.ctor_kind,
discr: variant.discr,
evaluated_discr: None,
struct_ctor: struct_ctor,
}), repr_options)
}
Expand All @@ -708,7 +700,6 @@ impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
EntryKind::Union(self.lazy(&VariantData {
ctor_kind: variant.ctor_kind,
discr: variant.discr,
evaluated_discr: None,
struct_ctor: None,
}), repr_options)
}
Expand Down Expand Up @@ -1037,6 +1028,17 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for EncodeVisitor<'a, 'b, 'tcx> {
EntryBuilder::encode_info_for_foreign_item,
(def_id, ni));
}
fn visit_variant(&mut self,
v: &'tcx hir::Variant,
g: &'tcx hir::Generics,
id: ast::NodeId) {
intravisit::walk_variant(self, v, g, id);

if let Some(discr) = v.node.disr_expr {
let def_id = self.index.tcx.hir.body_owner_def_id(discr);
self.index.record(def_id, EntryBuilder::encode_info_for_embedded_const, def_id);
}
}
fn visit_generics(&mut self, generics: &'tcx hir::Generics) {
intravisit::walk_generics(self, generics);
self.index.encode_info_for_generics(generics);
Expand Down Expand Up @@ -1160,6 +1162,32 @@ impl<'a, 'b: 'a, 'tcx: 'b> EntryBuilder<'a, 'b, 'tcx> {
}
}

fn encode_info_for_embedded_const(&mut self, def_id: DefId) -> Entry<'tcx> {
debug!("EntryBuilder::encode_info_for_embedded_const({:?})", def_id);
let tcx = self.tcx;
let id = tcx.hir.as_local_node_id(def_id).unwrap();
let body = tcx.hir.body_owned_by(id);

Entry {
kind: EntryKind::Const(ty::queries::mir_const_qualif::get(tcx, DUMMY_SP, def_id)),
visibility: self.lazy(&ty::Visibility::Public),
span: self.lazy(&tcx.def_span(def_id)),
attributes: LazySeq::empty(),
children: LazySeq::empty(),
stability: None,
deprecation: None,

ty: Some(self.encode_item_type(def_id)),
inherent_impls: LazySeq::empty(),
variances: LazySeq::empty(),
generics: Some(self.encode_generics(def_id)),
predicates: Some(self.encode_predicates(def_id)),

ast: Some(self.encode_body(body)),
mir: self.encode_mir(def_id),
}
}

fn encode_attributes(&mut self, attrs: &[ast::Attribute]) -> LazySeq<ast::Attribute> {
// NOTE: This must use lazy_seq_from_slice(), not lazy_seq() because
// we really on the HashStable specialization for [Attribute]
Expand Down
13 changes: 5 additions & 8 deletions src/librustc_metadata/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use rustc::hir;
use rustc::hir::def::{self, CtorKind};
use rustc::hir::def_id::{DefIndex, DefId};
use rustc::ich::StableHashingContext;
use rustc::middle::const_val::ConstVal;
use rustc::middle::cstore::{DepKind, LinkagePreference, NativeLibrary};
use rustc::middle::lang_items;
use rustc::mir;
Expand Down Expand Up @@ -271,9 +270,9 @@ pub enum EntryKind<'tcx> {
Type,
Enum(ReprOptions),
Field,
Variant(Lazy<VariantData<'tcx>>),
Struct(Lazy<VariantData<'tcx>>, ReprOptions),
Union(Lazy<VariantData<'tcx>>, ReprOptions),
Variant(Lazy<VariantData>),
Struct(Lazy<VariantData>, ReprOptions),
Union(Lazy<VariantData>, ReprOptions),
Fn(Lazy<FnData>),
ForeignFn(Lazy<FnData>),
Mod(Lazy<ModData>),
Expand Down Expand Up @@ -374,20 +373,18 @@ pub struct FnData {
impl_stable_hash_for!(struct FnData { constness, arg_names });

#[derive(RustcEncodable, RustcDecodable)]
pub struct VariantData<'tcx> {
pub struct VariantData {
pub ctor_kind: CtorKind,
pub discr: ty::VariantDiscr,
pub evaluated_discr: Option<ConstVal<'tcx>>,

/// If this is a struct's only variant, this
/// is the index of the "struct ctor" item.
pub struct_ctor: Option<DefIndex>,
}

impl_stable_hash_for!(struct VariantData<'tcx> {
impl_stable_hash_for!(struct VariantData {
ctor_kind,
discr,
evaluated_discr,
struct_ctor
});

Expand Down
20 changes: 9 additions & 11 deletions src/librustc_trans/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@
//! used unboxed and any field can have pointers (including mutable)
//! taken to it, implementing them for Rust seems difficult.

use super::Disr;

use std;

use llvm::{ValueRef, True, IntEQ, IntNE};
Expand Down Expand Up @@ -347,31 +345,31 @@ fn load_discr(bcx: &Builder, ity: layout::Integer, ptr: ValueRef,

/// Set the discriminant for a new value of the given case of the given
/// representation.
pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: Disr) {
pub fn trans_set_discr<'a, 'tcx>(bcx: &Builder<'a, 'tcx>, t: Ty<'tcx>, val: ValueRef, to: u64) {
let l = bcx.ccx.layout_of(t);
match *l {
layout::CEnum{ discr, min, max, .. } => {
assert_discr_in_range(Disr(min), Disr(max), to);
bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true),
assert_discr_in_range(min, max, to);
bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to, true),
val, None);
}
layout::General{ discr, .. } => {
bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to.0, true),
bcx.store(C_integral(Type::from_integer(bcx.ccx, discr), to, true),
bcx.struct_gep(val, 0), None);
}
layout::Univariant { .. }
| layout::UntaggedUnion { .. }
| layout::Vector { .. } => {
assert_eq!(to, Disr(0));
assert_eq!(to, 0);
}
layout::RawNullablePointer { nndiscr, .. } => {
if to.0 != nndiscr {
if to != nndiscr {
let llptrty = val_ty(val).element_type();
bcx.store(C_null(llptrty), val, None);
}
}
layout::StructWrappedNullablePointer { nndiscr, ref discrfield, ref nonnull, .. } => {
if to.0 != nndiscr {
if to != nndiscr {
if target_sets_discr_via_memset(bcx) {
// Issue #34427: As workaround for LLVM bug on
// ARM, use memset of 0 on whole struct rather
Expand All @@ -397,7 +395,7 @@ fn target_sets_discr_via_memset<'a, 'tcx>(bcx: &Builder<'a, 'tcx>) -> bool {
bcx.sess().target.target.arch == "arm" || bcx.sess().target.target.arch == "aarch64"
}

pub fn assert_discr_in_range(min: Disr, max: Disr, discr: Disr) {
pub fn assert_discr_in_range<D: PartialOrd>(min: D, max: D, discr: D) {
if min <= max {
assert!(min <= discr && discr <= max)
} else {
Expand All @@ -415,7 +413,7 @@ fn roundup(x: u64, a: u32) -> u64 { let a = a as u64; ((x + (a - 1)) / a) * a }
/// (Not to be confused with `common::const_get_elt`, which operates on
/// raw LLVM-level structs and arrays.)
pub fn const_get_field<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>,
val: ValueRef, _discr: Disr,
val: ValueRef,
ix: usize) -> ValueRef {
let l = ccx.layout_of(t);
match *l {
Expand Down
Loading

0 comments on commit 6dc21b7

Please sign in to comment.