translate tuple-variant constructors using MIR

This commit is contained in:
Ariel Ben-Yehuda 2017-02-07 22:46:21 +01:00
parent a559452b05
commit 0af3775dd2
11 changed files with 215 additions and 138 deletions

View File

@ -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)
}
}
}

View File

@ -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),
}
}

View File

@ -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;

View File

@ -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<Mir<'tcx>>
{
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, Mir<'tcx>>(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

109
src/librustc_mir/shim.rs Normal file
View File

@ -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 <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.
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<Local, LocalDecl<'tcx>>
{
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))
}

View File

@ -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<Linkage> {
// 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)) => {

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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.

View File

@ -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;

View File

@ -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