From 0af3775dd2c93cdaf8902f83eb21037e474e058f Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 7 Feb 2017 22:46:21 +0100 Subject: [PATCH] translate tuple-variant constructors using MIR --- src/librustc/ty/mod.rs | 13 +++- src/librustc_metadata/encoder.rs | 4 +- src/librustc_mir/lib.rs | 2 + src/librustc_mir/mir_map.rs | 63 ++++++++++++++++++ src/librustc_mir/shim.rs | 109 +++++++++++++++++++++++++++++++ src/librustc_trans/base.rs | 78 ++-------------------- src/librustc_trans/callee.rs | 33 ---------- src/librustc_trans/collector.rs | 19 ++---- src/librustc_trans/common.rs | 10 ++- src/librustc_trans/mir/block.rs | 6 +- src/librustc_trans/trans_item.rs | 16 +++-- 11 files changed, 215 insertions(+), 138 deletions(-) create mode 100644 src/librustc_mir/shim.rs diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 3c37c7353d683..360fa24bf3687 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1264,10 +1264,17 @@ impl<'a, 'tcx> ParameterEnvironment<'tcx> { def_id, ROOT_CODE_EXTENT) } - _ => { + Some(hir_map::NodeStructCtor(..)) | + Some(hir_map::NodeVariant(..)) => { + let def_id = tcx.hir.local_def_id(id); + tcx.construct_parameter_environment(tcx.hir.span(id), + def_id, + ROOT_CODE_EXTENT) + } + it => { bug!("ParameterEnvironment::from_item(): \ - `{}` is not an item", - tcx.hir.node_to_string(id)) + `{}` = {:?} is unsupported", + tcx.hir.node_to_string(id), it) } } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 0c31e30671dc2..044ed529ef74c 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -293,7 +293,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { predicates: Some(self.encode_predicates(def_id)), ast: None, - mir: None, + mir: self.encode_mir(def_id), } } @@ -426,7 +426,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { predicates: Some(self.encode_predicates(def_id)), ast: None, - mir: None, + mir: self.encode_mir(def_id), } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index f21f1881c832e..19028bfa531be 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -22,6 +22,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(associated_consts)] #![feature(box_patterns)] +#![feature(box_syntax)] #![feature(i128_type)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] @@ -50,6 +51,7 @@ pub mod callgraph; pub mod def_use; pub mod graphviz; mod hair; +mod shim; pub mod mir_map; pub mod pretty; pub mod transform; diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 58f23a5c81bd7..3fa7131a2b6b0 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,6 +22,7 @@ use rustc::dep_graph::DepNode; use rustc::mir::Mir; use rustc::mir::transform::MirSource; use rustc::mir::visit::MutVisitor; +use shim; use pretty; use hair::cx::Cx; @@ -30,6 +31,7 @@ use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::maps::Providers; use rustc::ty::subst::Substs; use rustc::hir; +use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use syntax::abi::Abi; use syntax::ast; use syntax_pos::Span; @@ -44,6 +46,31 @@ pub fn build_mir_for_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.visit_all_bodies_in_krate(|body_owner_def_id, _body_id| { tcx.item_mir(body_owner_def_id); }); + + // Tuple struct/variant constructors don't have a BodyId, so we need + // to build them separately. + struct GatherCtors<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx> + } + impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> { + fn visit_variant_data(&mut self, + v: &'tcx hir::VariantData, + _: ast::Name, + _: &'tcx hir::Generics, + _: ast::NodeId, + _: Span) { + if let hir::VariantData::Tuple(_, node_id) = *v { + self.tcx.item_mir(self.tcx.hir.local_def_id(node_id)); + } + intravisit::walk_struct_def(self, v) + } + fn nested_visit_map<'b>(&'b mut self) -> NestedVisitorMap<'b, 'tcx> { + NestedVisitorMap::None + } + } + tcx.visit_all_item_likes_in_krate(DepNode::Mir, &mut GatherCtors { + tcx: tcx + }.as_deep_visitor()); } } @@ -95,6 +122,10 @@ fn build_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) _ => hir::BodyId { node_id: expr.id } } } + hir::map::NodeVariant(variant) => + return create_constructor_shim(tcx, id, &variant.node.data), + hir::map::NodeStructCtor(ctor) => + return create_constructor_shim(tcx, id, ctor), _ => unsupported() }; @@ -180,6 +211,38 @@ impl<'a, 'gcx: 'tcx, 'tcx> MutVisitor<'tcx> for GlobalizeMir<'a, 'gcx> { } } +fn create_constructor_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + ctor_id: ast::NodeId, + v: &'tcx hir::VariantData) + -> &'tcx RefCell> +{ + let span = tcx.hir.span(ctor_id); + if let hir::VariantData::Tuple(ref fields, ctor_id) = *v { + let pe = ty::ParameterEnvironment::for_item(tcx, ctor_id); + tcx.infer_ctxt(pe, Reveal::UserFacing).enter(|infcx| { + let (mut mir, src) = + shim::build_adt_ctor(&infcx, ctor_id, fields, span); + + // Convert the Mir to global types. + let tcx = infcx.tcx.global_tcx(); + let mut globalizer = GlobalizeMir { + tcx: tcx, + span: mir.span + }; + globalizer.visit_mir(&mut mir); + let mir = unsafe { + mem::transmute::>(mir) + }; + + pretty::dump_mir(tcx, "mir_map", &0, src, &mir); + + tcx.alloc_mir(mir) + }) + } else { + span_bug!(span, "attempting to create MIR for non-tuple variant {:?}", v); + } +} + /////////////////////////////////////////////////////////////////////////// // BuildMir -- walks a crate, looking for fn items and methods to build MIR from diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs new file mode 100644 index 0000000000000..3705a317715f4 --- /dev/null +++ b/src/librustc_mir/shim.rs @@ -0,0 +1,109 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::hir; +use rustc::infer; +use rustc::mir::*; +use rustc::mir::transform::MirSource; +use rustc::ty; + +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; + +use syntax::ast; +use syntax_pos::Span; + +use std::iter; + +fn local_decls_for_sig<'tcx>(sig: &ty::FnSig<'tcx>) + -> IndexVec> +{ + iter::once(LocalDecl { + mutability: Mutability::Mut, + ty: sig.output(), + name: None, + source_info: None + }).chain(sig.inputs().iter().map(|ity| LocalDecl { + mutability: Mutability::Not, + ty: *ity, + name: None, + source_info: None, + })).collect() +} + +pub fn build_adt_ctor<'a, 'gcx, 'tcx>(infcx: &infer::InferCtxt<'a, 'gcx, 'tcx>, + ctor_id: ast::NodeId, + fields: &[hir::StructField], + span: Span) + -> (Mir<'tcx>, MirSource) +{ + let tcx = infcx.tcx; + let def_id = tcx.hir.local_def_id(ctor_id); + let sig = match tcx.item_type(def_id).sty { + ty::TyFnDef(_, _, fty) => tcx.no_late_bound_regions(&fty) + .expect("LBR in ADT constructor signature"), + _ => bug!("unexpected type for ctor {:?}", def_id) + }; + let sig = tcx.erase_regions(&sig); + + let (adt_def, substs) = match sig.output().sty { + ty::TyAdt(adt_def, substs) => (adt_def, substs), + _ => bug!("unexpected type for ADT ctor {:?}", sig.output()) + }; + + debug!("build_ctor: def_id={:?} sig={:?} fields={:?}", def_id, sig, fields); + + let local_decls = local_decls_for_sig(&sig); + + let source_info = SourceInfo { + span: span, + scope: ARGUMENT_VISIBILITY_SCOPE + }; + + let variant_no = if adt_def.is_enum() { + adt_def.variant_index_with_id(def_id) + } else { + 0 + }; + + // return = ADT(arg0, arg1, ...); return + let start_block = BasicBlockData { + statements: vec![Statement { + source_info: source_info, + kind: StatementKind::Assign( + Lvalue::Local(RETURN_POINTER), + Rvalue::Aggregate( + AggregateKind::Adt(adt_def, variant_no, substs, None), + (1..sig.inputs().len()+1).map(|i| { + Operand::Consume(Lvalue::Local(Local::new(i))) + }).collect() + ) + ) + }], + terminator: Some(Terminator { + source_info: source_info, + kind: TerminatorKind::Return, + }), + is_cleanup: false + }; + + let mir = Mir::new( + IndexVec::from_elem_n(start_block, 1), + IndexVec::from_elem_n( + VisibilityScopeData { span: span, parent_scope: None }, 1 + ), + IndexVec::new(), + sig.output(), + local_decls, + sig.inputs().len(), + vec![], + span + ); + (mir, MirSource::Fn(ctor_id)) +} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs index 1b43491e73c8f..fe2b21895cce6 100644 --- a/src/librustc_trans/base.rs +++ b/src/librustc_trans/base.rs @@ -34,10 +34,8 @@ use back::linker::LinkerInfo; use back::symbol_export::{self, ExportedSymbols}; use llvm::{Linkage, ValueRef, Vector, get_param}; use llvm; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::def_id::LOCAL_CRATE; use middle::lang_items::StartFnLangItem; -use rustc::ty::subst::Substs; -use rustc::mir::tcx::LvalueTy; use rustc::traits; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::adjustment::CustomCoerceUnsized; @@ -47,9 +45,8 @@ use rustc::util::common::time; use session::config::{self, NoDebugInfo}; use rustc_incremental::IncrementalHashesMap; use session::{self, DataTypeKind, Session}; -use abi::{self, FnType}; +use abi; use mir::lvalue::LvalueRef; -use adt; use attributes; use builder::Builder; use callee::{Callee}; @@ -65,7 +62,7 @@ use context::{SharedCrateContext, CrateContextList}; use debuginfo; use declare; use machine; -use machine::{llalign_of_min, llsize_of}; +use machine::llsize_of; use meth; use mir; use monomorphize::{self, Instance}; @@ -76,7 +73,6 @@ use trans_item::{TransItem, DefPathBasedNames}; use type_::Type; use type_of; use value::Value; -use Disr; use util::nodemap::{NodeSet, FxHashMap, FxHashSet}; use libc::c_uint; @@ -615,72 +611,6 @@ pub fn trans_instance<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, instance: Instance mir::trans_mir(ccx, lldecl, &mir, instance, sig); } -pub fn trans_ctor_shim<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>, - disr: Disr, - llfn: ValueRef) { - attributes::inline(llfn, attributes::InlineAttr::Hint); - attributes::set_frame_pointer_elimination(ccx, llfn); - - let ctor_ty = common::def_ty(ccx.shared(), def_id, substs); - let sig = ccx.tcx().erase_late_bound_regions_and_normalize(&ctor_ty.fn_sig()); - let fn_ty = FnType::new(ccx, sig, &[]); - - let bcx = Builder::new_block(ccx, llfn, "entry-block"); - if !fn_ty.ret.is_ignore() { - // But if there are no nested returns, we skip the indirection - // and have a single retslot - let dest = if fn_ty.ret.is_indirect() { - get_param(llfn, 0) - } else { - // We create an alloca to hold a pointer of type `ret.original_ty` - // which will hold the pointer to the right alloca which has the - // final ret value - bcx.alloca(fn_ty.ret.memory_ty(ccx), "sret_slot") - }; - // Can return unsized value - let mut dest_val = LvalueRef::new_sized_ty(dest, sig.output(), Alignment::AbiAligned); - dest_val.ty = LvalueTy::Downcast { - adt_def: sig.output().ty_adt_def().unwrap(), - substs: substs, - variant_index: disr.0 as usize, - }; - let mut llarg_idx = fn_ty.ret.is_indirect() as usize; - let mut arg_idx = 0; - for (i, arg_ty) in sig.inputs().iter().enumerate() { - let (lldestptr, _) = dest_val.trans_field_ptr(&bcx, i); - let arg = &fn_ty.args[arg_idx]; - arg_idx += 1; - if common::type_is_fat_ptr(bcx.ccx, arg_ty) { - let meta = &fn_ty.args[arg_idx]; - arg_idx += 1; - arg.store_fn_arg(&bcx, &mut llarg_idx, get_dataptr(&bcx, lldestptr)); - meta.store_fn_arg(&bcx, &mut llarg_idx, get_meta(&bcx, lldestptr)); - } else { - arg.store_fn_arg(&bcx, &mut llarg_idx, lldestptr); - } - } - adt::trans_set_discr(&bcx, sig.output(), dest, disr); - - if fn_ty.ret.is_indirect() { - bcx.ret_void(); - return; - } - - if let Some(cast_ty) = fn_ty.ret.cast { - bcx.ret(bcx.load( - bcx.pointercast(dest, cast_ty.ptr_to()), - Some(llalign_of_min(ccx, fn_ty.ret.ty)) - )); - } else { - bcx.ret(bcx.load(dest, None)) - } - } else { - bcx.ret_void(); - } -} - pub fn llvm_linkage_by_name(name: &str) -> Option { // Use the names from src/llvm/docs/LangRef.rst here. Most types are only // applicable to variable declarations and may not really make sense for @@ -721,7 +651,7 @@ pub fn set_link_section(ccx: &CrateContext, } /// Create the `main` function which will initialise the rust runtime and call -/// users’ main function. +/// users main function. pub fn maybe_create_entry_wrapper(ccx: &CrateContext) { let (main_def_id, span) = match *ccx.sess().entry_fn.borrow() { Some((id, span)) => { diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 762aaf1ce1d1b..a5b42a973cf27 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -22,7 +22,6 @@ use rustc::ty::subst::{Substs, Subst}; use rustc::traits; use abi::{Abi, FnType}; use attributes; -use base; use builder::Builder; use common::{self, CrateContext}; use cleanup::CleanupScope; @@ -35,7 +34,6 @@ use meth; use monomorphize::Instance; use trans_item::TransItem; use type_of; -use Disr; use rustc::ty::{self, Ty, TypeFoldable}; use rustc::hir; use std::iter; @@ -46,9 +44,6 @@ use mir::lvalue::Alignment; #[derive(Debug)] pub enum CalleeData { - /// Constructor for enum variant/tuple-like-struct. - NamedTupleConstructor(Disr), - /// Function pointer. Fn(ValueRef), @@ -92,16 +87,6 @@ impl<'tcx> Callee<'tcx> { } } - // FIXME(eddyb) Detect ADT constructors more efficiently. - if let Some(adt_def) = fn_ty.fn_ret().skip_binder().ty_adt_def() { - if let Some(i) = adt_def.variants.iter().position(|v| def_id == v.did) { - return Callee { - data: NamedTupleConstructor(Disr::for_variant(tcx, adt_def, i)), - ty: fn_ty - }; - } - } - let (llfn, ty) = get_fn(ccx, def_id, substs); Callee::ptr(llfn, ty) } @@ -185,24 +170,6 @@ impl<'tcx> Callee<'tcx> { match self.data { Fn(llfn) => llfn, Virtual(_) => meth::trans_object_shim(ccx, self), - NamedTupleConstructor(disr) => match self.ty.sty { - ty::TyFnDef(def_id, substs, _) => { - let instance = Instance::new(def_id, substs); - if let Some(&llfn) = ccx.instances().borrow().get(&instance) { - return llfn; - } - - let sym = ccx.symbol_map().get_or_compute(ccx.shared(), - TransItem::Fn(instance)); - assert!(!ccx.codegen_unit().contains_item(&TransItem::Fn(instance))); - let lldecl = declare::define_internal_fn(ccx, &sym, self.ty); - base::trans_ctor_shim(ccx, def_id, substs, disr, lldecl); - ccx.instances().borrow_mut().insert(instance, lldecl); - - lldecl - } - _ => bug!("expected fn item type, found {}", self.ty) - }, Intrinsic => bug!("intrinsic {} getting reified", self.ty) } } diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index 8d1db38999c6b..5e6b10f826ce0 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -630,14 +630,15 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { -> bool { match tcx.item_type(def_id).sty { ty::TyFnDef(def_id, _, _) => { - // Some constructors also have type TyFnDef but they are - // always instantiated inline and don't result in a - // translation item. Same for FFI functions. + // foreign items are linked from another library, not + // translated locally. if let Some(hir_map::NodeForeignItem(_)) = tcx.hir.get_if_local(def_id) { return false; } } - ty::TyClosure(..) => {} + ty::TyClosure(..) => { + // TODO: trans items for closures + } _ => return false } @@ -697,16 +698,6 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { fn should_trans_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool { - if let ty::TyFnDef(_, _, sig) = tcx.item_type(def_id).sty { - if let Some(adt_def) = sig.output().skip_binder().ty_adt_def() { - if adt_def.variants.iter().any(|v| def_id == v.did) { - // HACK: ADT constructors are translated in-place and - // do not have a trans-item. - return false; - } - } - } - if def_id.is_local() { true } else { diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index a509587f80fd0..0e536d58a56fb 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -37,6 +37,7 @@ use libc::{c_uint, c_char}; use std::iter; use syntax::ast; +use syntax::attr; use syntax::symbol::InternedString; use syntax_pos::Span; @@ -601,8 +602,13 @@ pub fn ty_fn_sig<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } } -pub fn is_closure(tcx: TyCtxt, def_id: DefId) -> bool { - tcx.def_key(def_id).disambiguated_data.data == DefPathData::ClosureExpr +pub fn requests_inline(tcx: TyCtxt, def_id: DefId) -> bool { + match tcx.def_key(def_id).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(..) | + DefPathData::ClosureExpr => true, + _ => attr::requests_inline(&tcx.get_attrs(def_id)[..]), + } } /// Given a DefId and some Substs, produces the monomorphic item type. diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 9d40419d338b8..2f1a2c9134c39 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -16,7 +16,7 @@ use rustc::ty::{self, layout, TypeFoldable}; use rustc::mir; use abi::{Abi, FnType, ArgType}; use base::{self, Lifetime}; -use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; +use callee::{Callee, CalleeData, Fn, Intrinsic, Virtual}; use builder::Builder; use common::{self, Funclet}; use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef}; @@ -491,10 +491,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } let fn_ptr = match callee.data { - NamedTupleConstructor(_) => { - // FIXME translate this like mir::Rvalue::Aggregate. - callee.reify(bcx.ccx) - } Intrinsic => { use intrinsic::trans_intrinsic_call; diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index d19f04b9554fb..5ec9c2a59957d 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -26,6 +26,7 @@ use monomorphize::Instance; use rustc::dep_graph::DepNode; use rustc::hir; use rustc::hir::def_id::DefId; +use rustc::hir::map::definitions::DefPathData; use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; use rustc::ty::subst::Substs; use rustc_const_eval::fatal_const_eval_err; @@ -178,9 +179,14 @@ impl<'a, 'tcx> TransItem<'tcx> { llvm::SetUniqueComdat(ccx.llmod(), lldecl); } - if let ty::TyClosure(..) = mono_ty.sty { - // set an inline hint for all closures - attributes::inline(lldecl, attributes::InlineAttr::Hint); + debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); + match ccx.tcx().def_key(instance.def).disambiguated_data.data { + DefPathData::StructCtor | + DefPathData::EnumVariant(..) | + DefPathData::ClosureExpr => { + attributes::inline(lldecl, attributes::InlineAttr::Hint); + } + _ => {} } attributes::from_fn_attrs(ccx, &attrs, lldecl); @@ -252,8 +258,8 @@ impl<'a, 'tcx> TransItem<'tcx> { match *self { TransItem::Fn(ref instance) => { if self.explicit_linkage(tcx).is_none() && - (common::is_closure(tcx, instance.def) || - attr::requests_inline(&tcx.get_attrs(instance.def)[..])) { + common::requests_inline(tcx, instance.def) + { InstantiationMode::LocalCopy } else { InstantiationMode::GloballyShared