From 6d7c66e6e82229134fcc5a2b5a23545f8a17f1ce Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 15:46:30 -0400 Subject: [PATCH 01/18] Introduce a "liberated fn sigs" map so that we have easy access to this information when constructing MIR. --- src/librustc/middle/ty/context.rs | 8 ++++++++ src/librustc_typeck/check/mod.rs | 19 ++---------------- src/librustc_typeck/check/regionck.rs | 27 +++++++++++++++++++------- src/librustc_typeck/check/writeback.rs | 23 ++++++++++++++++++++++ 4 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index 830232cf373b8..a8f045074bd72 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -121,6 +121,13 @@ pub struct Tables<'tcx> { /// Records the type of each closure. The def ID is the ID of the /// expression defining the closure. pub closure_kinds: DefIdMap, + + /// For each fn, records the "liberated" types of its arguments + /// and return type. Liberated means that all bound regions + /// (including late-bound regions) are replaced with free + /// equivalents. This table is not used in trans (since regions + /// are erased there) and hence is not serialized to metadata. + pub liberated_fn_sigs: NodeMap>, } impl<'tcx> Tables<'tcx> { @@ -133,6 +140,7 @@ impl<'tcx> Tables<'tcx> { upvar_capture_map: FnvHashMap(), closure_tys: DefIdMap(), closure_kinds: DefIdMap(), + liberated_fn_sigs: NodeMap(), } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index f085ce23e3f0c..dc2b2b75ab6d1 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -164,11 +164,6 @@ pub struct Inherited<'a, 'tcx: 'a> { tables: &'a RefCell>, - // A mapping from each fn's id to its signature, with all bound - // regions replaced with free ones. Unlike the other tables, this - // one is never copied into the tcx: it is only used by regionck. - fn_sig_map: RefCell>>>, - // When we process a call like `c()` where `c` is a closure type, // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or // `FnOnce` closure. In that case, we defer full resolution of the @@ -314,7 +309,6 @@ impl<'a, 'tcx> Inherited<'a, 'tcx> { infcx: infer::new_infer_ctxt(tcx, tables, Some(param_env), true), locals: RefCell::new(NodeMap()), tables: tables, - fn_sig_map: RefCell::new(NodeMap()), deferred_call_resolutions: RefCell::new(DefIdMap()), deferred_cast_checks: RefCell::new(Vec::new()), } @@ -620,22 +614,13 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, ccx: ccx }; - // Remember return type so that regionck can access it later. - let mut fn_sig_tys: Vec = - arg_tys.iter() - .cloned() - .collect(); - if let ty::FnConverging(ret_ty) = ret_ty { fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType); - fn_sig_tys.push(ret_ty); // FIXME(#25759) just take implied bounds from the arguments } - debug!("fn-sig-map: fn_id={} fn_sig_tys={:?}", - fn_id, - fn_sig_tys); + debug!("fn-sig-map: fn_id={} fn_sig={:?}", fn_id, fn_sig); - inherited.fn_sig_map.borrow_mut().insert(fn_id, fn_sig_tys); + inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig.clone()); { let mut visit = GatherLocalsVisitor { fcx: &fcx, }; diff --git a/src/librustc_typeck/check/regionck.rs b/src/librustc_typeck/check/regionck.rs index 6e60f926b7cc9..3cdc9b559446e 100644 --- a/src/librustc_typeck/check/regionck.rs +++ b/src/librustc_typeck/check/regionck.rs @@ -284,19 +284,32 @@ impl<'a, 'tcx> Rcx<'a, 'tcx> { // When we enter a function, we can derive debug!("visit_fn_body(id={})", id); - let fn_sig_map = self.fcx.inh.fn_sig_map.borrow(); - let fn_sig = match fn_sig_map.get(&id) { - Some(f) => f, - None => { - self.tcx().sess.bug( - &format!("No fn-sig entry for id={}", id)); + let fn_sig = { + let fn_sig_map = &self.infcx().tables.borrow().liberated_fn_sigs; + match fn_sig_map.get(&id) { + Some(f) => f.clone(), + None => { + self.tcx().sess.bug( + &format!("No fn-sig entry for id={}", id)); + } } }; let old_region_bounds_pairs_len = self.region_bound_pairs.len(); + // Collect the types from which we create inferred bounds. + // For the return type, if diverging, substitute `bool` just + // because it will have no effect. + // + // FIXME(#25759) return types should not be implied bounds + let fn_sig_tys: Vec<_> = + fn_sig.inputs.iter() + .cloned() + .chain(Some(fn_sig.output.unwrap_or(self.tcx().types.bool))) + .collect(); + let old_body_id = self.set_body_id(body.id); - self.relate_free_regions(&fn_sig[..], body.id, span); + self.relate_free_regions(&fn_sig_tys[..], body.id, span); link_fn_args(self, self.tcx().region_maps.node_extent(body.id), &fn_decl.inputs[..]); diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 2c18a245159cf..cfab28f923e09 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -43,6 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &hir::Expr) { wbcx.visit_expr(e); wbcx.visit_upvar_borrow_map(); wbcx.visit_closures(); + wbcx.visit_liberated_fn_sigs(); } pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, @@ -63,6 +64,7 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, } wbcx.visit_upvar_borrow_map(); wbcx.visit_closures(); + wbcx.visit_liberated_fn_sigs(); } /////////////////////////////////////////////////////////////////////////// @@ -361,6 +363,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_liberated_fn_sigs(&self) { + for (&node_id, fn_sig) in self.fcx.inh.tables.borrow().liberated_fn_sigs.iter() { + let fn_sig = self.resolve(fn_sig, ResolvingFnSig(node_id)); + self.tcx().tables.borrow_mut().liberated_fn_sigs.insert(node_id, fn_sig.clone()); + } + } + fn resolve>(&self, t: &T, reason: ResolveReason) -> T { t.fold_with(&mut Resolver::new(self.fcx, reason)) } @@ -376,6 +385,7 @@ enum ResolveReason { ResolvingPattern(Span), ResolvingUpvar(ty::UpvarId), ResolvingClosure(DefId), + ResolvingFnSig(ast::NodeId), } impl ResolveReason { @@ -387,6 +397,9 @@ impl ResolveReason { ResolvingUpvar(upvar_id) => { tcx.expr_span(upvar_id.closure_expr_id) } + ResolvingFnSig(id) => { + tcx.map.span(id) + } ResolvingClosure(did) => { if let Some(node_id) = tcx.map.as_local_node_id(did) { tcx.expr_span(node_id) @@ -463,6 +476,16 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { span_err!(self.tcx.sess, span, E0196, "cannot determine a type for this closure") } + + ResolvingFnSig(id) => { + // any failures here should also fail when + // resolving the patterns, closure types, or + // something else. + let span = self.reason.span(self.tcx); + self.tcx.sess.delay_span_bug( + span, + &format!("cannot resolve some aspect of fn sig for {:?}", id)); + } } } } From 88a9c3e764d7938ecbc9c25e840cb60dbd70d170 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:14:25 -0400 Subject: [PATCH 02/18] Build the MIR using the liberated fn sigs, and track the return type --- src/librustc_mir/build/mod.rs | 18 ++++++----- src/librustc_mir/mir_map.rs | 56 ++++++++++++++++++++++------------- src/librustc_mir/repr.rs | 4 ++- 3 files changed, 49 insertions(+), 29 deletions(-) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 857540e2109c0..8e1f08775156e 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -10,7 +10,7 @@ use hair; use rustc::middle::region::CodeExtent; -use rustc::middle::ty::Ty; +use rustc::middle::ty::{FnOutput, Ty}; use rustc_data_structures::fnv::FnvHashMap; use rustc_front::hir; use repr::*; @@ -75,13 +75,14 @@ macro_rules! unpack { /////////////////////////////////////////////////////////////////////////// // construct() -- the main entry point for building MIR for a function -pub fn construct<'a, 'tcx>(mut hir: Cx<'a, 'tcx>, - _span: Span, - implicit_arguments: Vec>, - explicit_arguments: Vec<(Ty<'tcx>, PatNode<'tcx>)>, - argument_extent: CodeExtent, - ast_block: &'tcx hir::Block) - -> Mir<'tcx> { +pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>, + _span: Span, + implicit_arguments: Vec>, + explicit_arguments: Vec<(Ty<'tcx>, PatNode<'tcx>)>, + argument_extent: CodeExtent, + return_ty: FnOutput<'tcx>, + ast_block: &'tcx hir::Block) + -> Mir<'tcx> { let cfg = CFG { basic_blocks: vec![] }; // it's handy to have a temporary of type `()` sometimes, so make @@ -121,6 +122,7 @@ pub fn construct<'a, 'tcx>(mut hir: Cx<'a, 'tcx>, var_decls: builder.var_decls, arg_decls: arg_decls, temp_decls: builder.temp_decls, + return_ty: return_ty, } } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 1acbc8d733d5c..555f8896b4bc5 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -189,26 +189,42 @@ impl<'a, 'm, 'tcx> visit::Visitor<'tcx> for InnerDump<'a,'m,'tcx> { } } -fn build_mir<'a, 'tcx: 'a>(cx: Cx<'a, 'tcx>, - implicit_arg_tys: Vec>, - fn_id: ast::NodeId, - span: Span, - decl: &'tcx hir::FnDecl, - body: &'tcx hir::Block) - -> Result, ErrorReported> { - let arguments = decl.inputs - .iter() - .map(|arg| { - let ty = cx.tcx().node_id_to_type(arg.id); - (ty, PatNode::irrefutable(&arg.pat)) - }) - .collect(); - - let parameter_scope = cx.tcx().region_maps.lookup_code_extent(CodeExtentData::ParameterScope { - fn_id: fn_id, - body_id: body.id, - }); - Ok(build::construct(cx, span, implicit_arg_tys, arguments, parameter_scope, body)) +fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>, + implicit_arg_tys: Vec>, + fn_id: ast::NodeId, + span: Span, + decl: &'tcx hir::FnDecl, + body: &'tcx hir::Block) + -> Result, ErrorReported> { + // fetch the fully liberated fn signature (that is, all bound + // types/lifetimes replaced) + let fn_sig = match cx.tcx().tables.borrow().liberated_fn_sigs.get(&fn_id) { + Some(f) => f.clone(), + None => { + cx.tcx().sess.span_bug(span, + &format!("no liberated fn sig for {:?}", fn_id)); + } + }; + + let arguments = + decl.inputs + .iter() + .enumerate() + .map(|(index, arg)| { + (fn_sig.inputs[index], PatNode::irrefutable(&arg.pat)) + }) + .collect(); + + let parameter_scope = + cx.tcx().region_maps.lookup_code_extent( + CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id }); + Ok(build::construct(cx, + span, + implicit_arg_tys, + arguments, + parameter_scope, + fn_sig.output, + body)) } fn closure_self_ty<'a, 'tcx>(tcx: &ty::ctxt<'tcx>, diff --git a/src/librustc_mir/repr.rs b/src/librustc_mir/repr.rs index 5059955c5dc33..09f174e2ba694 100644 --- a/src/librustc_mir/repr.rs +++ b/src/librustc_mir/repr.rs @@ -12,7 +12,7 @@ use rustc::middle::const_eval::ConstVal; use rustc::middle::def_id::DefId; use rustc::middle::region::CodeExtent; use rustc::middle::subst::Substs; -use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty}; +use rustc::middle::ty::{AdtDef, ClosureSubsts, FnOutput, Region, Ty}; use rustc_back::slice; use rustc_data_structures::fnv::FnvHashMap; use rustc_front::hir::InlineAsm; @@ -25,6 +25,8 @@ use std::u32; pub struct Mir<'tcx> { pub basic_blocks: Vec>, + pub return_ty: FnOutput<'tcx>, + // for every node id pub extents: FnvHashMap>, From 1e30f3e52be35c8992ae882a38b9c74e6adbfef6 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:16:11 -0400 Subject: [PATCH 03/18] Change ShallowDrop to Free, so that it matches what trans will do --- src/librustc_mir/build/expr/as_rvalue.rs | 2 +- src/librustc_mir/repr.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 3cfc51ec3c98a..c5831b881abe5 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -71,7 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // schedule a shallow free of that memory, lest we unwind: let extent = this.extent_of_innermost_scope().unwrap(); - this.schedule_drop(expr_span, extent, DropKind::Shallow, &result, value_ty); + this.schedule_drop(expr_span, extent, DropKind::Free, &result, value_ty); // initialize the box contents: let contents = result.clone().deref(); diff --git a/src/librustc_mir/repr.rs b/src/librustc_mir/repr.rs index 09f174e2ba694..54e00dc0a68ad 100644 --- a/src/librustc_mir/repr.rs +++ b/src/librustc_mir/repr.rs @@ -355,8 +355,8 @@ pub enum StatementKind<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum DropKind { - Shallow, - Deep, + Free, // free a partially constructed box, should go away eventually + Deep } impl<'tcx> Debug for Statement<'tcx> { @@ -364,7 +364,7 @@ impl<'tcx> Debug for Statement<'tcx> { use self::StatementKind::*; match self.kind { Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv), - Drop(DropKind::Shallow, ref lv) => write!(fmt, "shallow_drop {:?}", lv), + Drop(DropKind::Free, ref lv) => write!(fmt, "free {:?}", lv), Drop(DropKind::Deep, ref lv) => write!(fmt, "drop {:?}", lv), } } From 15c1da4e27587951a8806b6485a8051103019a02 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:19:02 -0400 Subject: [PATCH 04/18] Convert from using named fields to always using indices --- src/librustc/middle/ty/mod.rs | 7 +++++++ src/librustc_mir/build/expr/as_rvalue.rs | 9 ++++++--- src/librustc_mir/repr.rs | 21 ++++++++++++++------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/librustc/middle/ty/mod.rs b/src/librustc/middle/ty/mod.rs index 573634414932b..aa18974470178 100644 --- a/src/librustc/middle/ty/mod.rs +++ b/src/librustc/middle/ty/mod.rs @@ -1731,6 +1731,13 @@ impl<'tcx, 'container> VariantDefData<'tcx, 'container> { self.fields.iter().find(|f| f.name == name) } + #[inline] + pub fn index_of_field_named(&self, + name: ast::Name) + -> Option { + self.fields.iter().position(|f| f.name == name) + } + #[inline] pub fn field_named(&self, name: ast::Name) -> &FieldDefData<'tcx, 'container> { self.find_field_named(name).unwrap() diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index c5831b881abe5..23ca22129fdc0 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -149,16 +149,19 @@ impl<'a,'tcx> Builder<'a,'tcx> { block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars)) } ExprKind::Adt { adt_def, variant_index, substs, fields, base } => { // see (*) above - // first process the set of fields + // first process the set of fields that were provided + // (evaluating them in order given by user) let fields_map: FnvHashMap<_, _> = fields.into_iter() .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr)))) .collect(); - let field_names = this.hir.fields(adt_def, variant_index); - + // if base expression is given, evaluate it now let base = base.map(|base| unpack!(block = this.as_lvalue(block, base))); + // get list of all fields that we will need + let field_names = this.hir.all_fields(adt_def, variant_index); + // for the actual values we use, take either the // expr the user specified or, if they didn't // specify something for this field name, create a diff --git a/src/librustc_mir/repr.rs b/src/librustc_mir/repr.rs index 54e00dc0a68ad..5bf326ba5a5fe 100644 --- a/src/librustc_mir/repr.rs +++ b/src/librustc_mir/repr.rs @@ -443,10 +443,19 @@ pub type LvalueProjection<'tcx> = pub type LvalueElem<'tcx> = ProjectionElem<'tcx,Operand<'tcx>>; +/// Index into the list of fields found in a `VariantDef` #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -pub enum Field { - Named(Name), - Indexed(usize), +pub struct Field(u32); + +impl Field { + pub fn new(value: usize) -> Field { + assert!(value < (u32::MAX) as usize); + Field(value as u32) + } + + pub fn index(self) -> usize { + self.0 as usize + } } impl<'tcx> Lvalue<'tcx> { @@ -491,10 +500,8 @@ impl<'tcx> Debug for Lvalue<'tcx> { write!(fmt,"({:?} as {:?})", data.base, variant_index), ProjectionElem::Deref => write!(fmt,"(*{:?})", data.base), - ProjectionElem::Field(Field::Named(name)) => - write!(fmt,"{:?}.{:?}", data.base, name), - ProjectionElem::Field(Field::Indexed(index)) => - write!(fmt,"{:?}.{:?}", data.base, index), + ProjectionElem::Field(field) => + write!(fmt,"{:?}.{:?}", data.base, field.index()), ProjectionElem::Index(ref index) => write!(fmt,"{:?}[{:?}]", data.base, index), ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => From 3c07b4611815d4d593fba22aaaa975ed97818919 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:20:00 -0400 Subject: [PATCH 05/18] Pass the mir map to trans --- mk/crates.mk | 2 +- src/librustc_driver/driver.rs | 27 +++++++++++++--------- src/librustc_driver/pretty.rs | 4 ++-- src/librustc_trans/lib.rs | 2 ++ src/librustc_trans/trans/base.rs | 7 +++++- src/librustc_trans/trans/context.rs | 8 +++++++ src/librustdoc/core.rs | 2 +- src/test/run-make/execution-engine/test.rs | 4 ++-- 8 files changed, 38 insertions(+), 18 deletions(-) diff --git a/mk/crates.mk b/mk/crates.mk index 6673b9b28e716..2a627a6da2bd6 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -103,7 +103,7 @@ DEPS_rustc_mir := rustc rustc_front syntax DEPS_rustc_resolve := rustc rustc_front log syntax DEPS_rustc_platform_intrinsics := rustc rustc_llvm DEPS_rustc_privacy := rustc rustc_front log syntax -DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back \ +DEPS_rustc_trans := arena flate getopts graphviz libc rustc rustc_back rustc_mir \ log syntax serialize rustc_llvm rustc_front rustc_platform_intrinsics DEPS_rustc_typeck := rustc syntax rustc_front rustc_platform_intrinsics diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 69c4dcb0a197c..06708a5127f34 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -11,6 +11,7 @@ use rustc::front; use rustc::front::map as hir_map; use rustc_mir as mir; +use rustc_mir::mir_map::MirMap; use rustc::session::Session; use rustc::session::config::{self, Input, OutputFilenames, OutputType}; use rustc::session::search_paths::PathKind; @@ -22,6 +23,7 @@ use rustc::middle::dependency_format; use rustc::middle; use rustc::plugin::registry::Registry; use rustc::plugin; +use rustc::util::nodemap::NodeMap; use rustc::util::common::time; use rustc_borrowck as borrowck; use rustc_resolve as resolve; @@ -146,7 +148,7 @@ pub fn compile_input(sess: Session, &arenas, &id, control.make_glob_map, - |tcx, analysis| { + |tcx, mir_map, analysis| { { let state = CompileState::state_after_analysis(input, @@ -170,7 +172,7 @@ pub fn compile_input(sess: Session, println!("Pre-trans"); tcx.print_debug_stats(); } - let trans = phase_4_translate_to_llvm(tcx, analysis); + let trans = phase_4_translate_to_llvm(tcx, &mir_map, analysis); if log_enabled!(::log::INFO) { println!("Post-trans"); @@ -670,6 +672,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, f: F) -> R where F: for<'a> FnOnce(&'a ty::ctxt<'tcx>, + MirMap<'tcx>, ty::CrateAnalysis) -> R { let time_passes = sess.time_passes(); @@ -751,18 +754,18 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, time(time_passes, "match checking", || middle::check_match::check_crate(tcx)); - match tcx.sess.opts.unstable_features { + let mir_map = match tcx.sess.opts.unstable_features { UnstableFeatures::Disallow => { // use this as a shorthand for beta/stable, and skip // MIR construction there until known regressions are // addressed + NodeMap() } UnstableFeatures::Allow | UnstableFeatures::Cheat => { - let _mir_map = - time(time_passes, "MIR dump", || - mir::mir_map::build_mir_for_crate(tcx)); + time(time_passes, "MIR dump", || + mir::mir_map::build_mir_for_crate(tcx)) } - } + }; time(time_passes, "liveness checking", || middle::liveness::check_crate(tcx)); @@ -804,7 +807,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, // The above three passes generate errors w/o aborting tcx.sess.abort_if_errors(); - f(tcx, ty::CrateAnalysis { + f(tcx, mir_map, ty::CrateAnalysis { export_map: export_map, exported_items: exported_items, public_items: public_items, @@ -817,8 +820,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, /// Run the translation phase to LLVM, after which the AST and analysis can /// be discarded. -pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) - -> trans::CrateTranslation { +pub fn phase_4_translate_to_llvm<'tcx>(tcx: &ty::ctxt<'tcx>, + mir_map: &MirMap<'tcx>, + analysis: ty::CrateAnalysis) + -> trans::CrateTranslation { let time_passes = tcx.sess.time_passes(); time(time_passes, "resolving dependency formats", || @@ -826,7 +831,7 @@ pub fn phase_4_translate_to_llvm(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) // Option dance to work around the lack of stack once closures. time(time_passes, "translation", move || - trans::trans_crate(tcx, analysis)) + trans::trans_crate(tcx, mir_map, analysis)) } /// Run LLVM itself, producing a bitcode file, assembly file or object file diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index a30c437197c3b..f53822d2400b4 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -182,7 +182,7 @@ impl PpSourceMode { arenas, id, resolve::MakeGlobMap::No, - |tcx, _| { + |tcx, _, _| { let annotation = TypedAnnotation { tcx: tcx }; f(&annotation, payload, &ast_map.forest.krate) }) @@ -782,7 +782,7 @@ pub fn pretty_print_input(sess: Session, &arenas, &id, resolve::MakeGlobMap::No, - |tcx, _| { + |tcx, _, _| { print_flowgraph(variants, tcx, code, mode, out) }) } diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index e02ce49132a7c..bc5b6f6b9d7d9 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -37,6 +37,7 @@ #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![feature(rustc_private)] +#![feature(slice_patterns)] #![feature(staged_api)] #![feature(unicode)] #![feature(vec_push_all)] @@ -52,6 +53,7 @@ extern crate rustc; extern crate rustc_back; extern crate rustc_front; extern crate rustc_llvm as llvm; +extern crate rustc_mir; extern crate rustc_platform_intrinsics as intrinsics; extern crate serialize; diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 46a4cb0c92ee7..f28b7e8f52dc3 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -44,6 +44,7 @@ use middle::pat_util::simple_name; use middle::subst::Substs; use middle::ty::{self, Ty, HasTypeFlags}; use rustc::front::map as hir_map; +use rustc_mir::mir_map::MirMap; use session::config::{self, NoDebugInfo, FullDebugInfo}; use session::Session; use trans::_match; @@ -2737,7 +2738,10 @@ pub fn filter_reachable_ids(ccx: &SharedCrateContext) -> NodeSet { }).collect() } -pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslation { +pub fn trans_crate<'tcx>(tcx: &ty::ctxt<'tcx>, + mir_map: &MirMap<'tcx>, + analysis: ty::CrateAnalysis) + -> CrateTranslation { let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis; let krate = tcx.map.krate(); @@ -2779,6 +2783,7 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat let shared_ccx = SharedCrateContext::new(&link_meta.crate_name, codegen_units, tcx, + &mir_map, export_map, Sha256::new(), link_meta.clone(), diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 01d0c51a08dd9..1f1d43feeb38e 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -14,6 +14,7 @@ use metadata::common::LinkMeta; use middle::def::ExportMap; use middle::def_id::DefId; use middle::traits; +use rustc_mir::mir_map::MirMap; use trans::adt; use trans::base; use trans::builder::Builder; @@ -70,6 +71,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> { stats: Stats, check_overflow: bool, check_drop_flag_for_sanity: bool, + mir_map: &'a MirMap<'tcx>, available_drop_glues: RefCell, String>>, use_dll_storage_attrs: bool, @@ -251,6 +253,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { pub fn new(crate_name: &str, local_count: usize, tcx: &'b ty::ctxt<'tcx>, + mir_map: &'b MirMap<'tcx>, export_map: ExportMap, symbol_hasher: Sha256, link_meta: LinkMeta, @@ -317,6 +320,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> { link_meta: link_meta, symbol_hasher: RefCell::new(symbol_hasher), tcx: tcx, + mir_map: mir_map, stats: Stats { n_glues_created: Cell::new(0), n_null_glues: Cell::new(0), @@ -803,6 +807,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { pub fn use_dll_storage_attrs(&self) -> bool { self.shared.use_dll_storage_attrs() } + + pub fn mir_map(&self) -> &'b MirMap<'tcx> { + self.shared.mir_map + } } pub struct TypeOfDepthLock<'a, 'tcx: 'a>(&'a LocalCrateContext<'tcx>); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 4955951d36e84..d07d44a2a9412 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -145,7 +145,7 @@ pub fn run_core(search_paths: SearchPaths, cfgs: Vec, externs: Externs, &arenas, &name, resolve::MakeGlobMap::No, - |tcx, analysis| { + |tcx, _, analysis| { let ty::CrateAnalysis { exported_items, public_items, .. } = analysis; // Convert from a NodeId set to a DefId set since we don't always have easy access diff --git a/src/test/run-make/execution-engine/test.rs b/src/test/run-make/execution-engine/test.rs index f4031a3aaae1c..f2dd155595ac4 100644 --- a/src/test/run-make/execution-engine/test.rs +++ b/src/test/run-make/execution-engine/test.rs @@ -229,9 +229,9 @@ fn compile_program(input: &str, sysroot: PathBuf) let ast_map = driver::make_map(&sess, &mut hir_forest); driver::phase_3_run_analysis_passes( - &sess, ast_map, &arenas, &id, MakeGlobMap::No, |tcx, analysis| { + &sess, ast_map, &arenas, &id, MakeGlobMap::No, |tcx, mir_map, analysis| { - let trans = driver::phase_4_translate_to_llvm(tcx, analysis); + let trans = driver::phase_4_translate_to_llvm(tcx, &mir_map, analysis); let crates = tcx.sess.cstore.get_used_crates(RequireDynamic); From b5d358084395b76277d2c92f73c28338f9432a25 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:24:05 -0400 Subject: [PATCH 06/18] Move the "HAIR" code that reads the tcx tables etc out of the `tcx` module and into `hair/cx`, now that we don't have a trait defining the interface --- src/librustc_mir/build/mod.rs | 2 +- src/librustc_mir/{tcx => hair/cx}/block.rs | 28 +++------ src/librustc_mir/{tcx => hair/cx}/expr.rs | 60 ++++++++++++++------ src/librustc_mir/{tcx => hair/cx}/mod.rs | 17 ++---- src/librustc_mir/{tcx => hair/cx}/pattern.rs | 37 +++++++++--- src/librustc_mir/{tcx => hair/cx}/to_ref.rs | 14 +---- src/librustc_mir/{hair.rs => hair/mod.rs} | 8 +-- src/librustc_mir/lib.rs | 5 +- src/librustc_mir/mir_map.rs | 2 +- 9 files changed, 94 insertions(+), 79 deletions(-) rename src/librustc_mir/{tcx => hair/cx}/block.rs (82%) rename src/librustc_mir/{tcx => hair/cx}/expr.rs (94%) rename src/librustc_mir/{tcx => hair/cx}/mod.rs (90%) rename src/librustc_mir/{tcx => hair/cx}/pattern.rs (89%) rename src/librustc_mir/{tcx => hair/cx}/to_ref.rs (85%) rename src/librustc_mir/{hair.rs => hair/mod.rs} (98%) diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 8e1f08775156e..56a20167b7942 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -9,6 +9,7 @@ // except according to those terms. use hair; +use hair::cx::{Cx, PatNode}; use rustc::middle::region::CodeExtent; use rustc::middle::ty::{FnOutput, Ty}; use rustc_data_structures::fnv::FnvHashMap; @@ -16,7 +17,6 @@ use rustc_front::hir; use repr::*; use syntax::ast; use syntax::codemap::Span; -use tcx::{Cx, PatNode}; struct Builder<'a, 'tcx: 'a> { hir: Cx<'a, 'tcx>, diff --git a/src/librustc_mir/tcx/block.rs b/src/librustc_mir/hair/cx/block.rs similarity index 82% rename from src/librustc_mir/tcx/block.rs rename to src/librustc_mir/hair/cx/block.rs index dc168bc7c2b6c..a407c42372a81 100644 --- a/src/librustc_mir/tcx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -9,10 +9,9 @@ // except according to those terms. use hair::*; - -use tcx::Cx; -use tcx::pattern::PatNode; -use tcx::to_ref::ToRef; +use hair::cx::Cx; +use hair::cx::pattern::PatNode; +use hair::cx::to_ref::ToRef; use rustc::middle::region::{BlockRemainder, CodeExtentData}; use rustc_front::hir; use syntax::ast; @@ -34,22 +33,11 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block { } } -impl<'tcx> Mirror<'tcx> for &'tcx hir::Stmt { - type Output = Stmt<'tcx>; - - fn make_mirror<'a>(self, _cx: &mut Cx<'a, 'tcx>) -> Stmt<'tcx> { - // In order to get the scoping correct, we eagerly mirror - // statements when we translate the enclosing block, so we - // should in fact never get to this point. - panic!("statements are eagerly mirrored"); - } -} - -fn mirror_stmts<'a, 'tcx: 'a, STMTS>(cx: &mut Cx<'a, 'tcx>, - block_id: ast::NodeId, - mut stmts: STMTS) - -> Vec> - where STMTS: Iterator)> +fn mirror_stmts<'a,'tcx:'a,STMTS>(cx: &mut Cx<'a,'tcx>, + block_id: ast::NodeId, + mut stmts: STMTS) + -> Vec> + where STMTS: Iterator)> { let mut result = vec![]; while let Some((index, stmt)) = stmts.next() { diff --git a/src/librustc_mir/tcx/expr.rs b/src/librustc_mir/hair/cx/expr.rs similarity index 94% rename from src/librustc_mir/tcx/expr.rs rename to src/librustc_mir/hair/cx/expr.rs index 9f7ecf522876f..847d76f7d17a9 100644 --- a/src/librustc_mir/tcx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -12,16 +12,16 @@ use hair::*; use repr::*; use rustc_data_structures::fnv::FnvHashMap; use std::rc::Rc; -use tcx::Cx; -use tcx::block; -use tcx::pattern::PatNode; -use tcx::to_ref::ToRef; +use hair::cx::Cx; +use hair::cx::block; +use hair::cx::pattern::PatNode; +use hair::cx::to_ref::ToRef; use rustc::front::map; use rustc::middle::const_eval; use rustc::middle::def; use rustc::middle::region::CodeExtent; use rustc::middle::pat_util; -use rustc::middle::ty::{self, Ty}; +use rustc::middle::ty::{self, VariantDef, Ty}; use rustc_front::hir; use rustc_front::util as hir_util; use syntax::parse::token; @@ -170,11 +170,12 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { hir::ExprStruct(_, ref fields, ref base) => { match expr_ty.sty { ty::TyStruct(adt, substs) => { + let field_refs = field_refs(&adt.variants[0], fields); ExprKind::Adt { adt_def: adt, variant_index: 0, substs: substs, - fields: fields.to_ref(), + fields: field_refs, base: base.to_ref(), } } @@ -183,11 +184,12 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { def::DefVariant(enum_id, variant_id, true) => { debug_assert!(adt.did == enum_id); let index = adt.variant_index_with_id(variant_id); + let field_refs = field_refs(&adt.variants[index], fields); ExprKind::Adt { adt_def: adt, variant_index: index, substs: substs, - fields: fields.to_ref(), + fields: field_refs, base: base.to_ref(), } } @@ -238,11 +240,10 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { } }; - let field_expr_ref = |s: &'tcx P, nm: &str| { - FieldExprRef { - name: Field::Named(token::intern(nm)), - expr: s.to_ref(), - } + let field_expr_ref = |s: &'tcx P, name: &str| { + let name = token::intern(name); + let index = adt_def.variants[0].index_of_field_named(name).unwrap(); + FieldExprRef { name: Field::new(index), expr: s.to_ref() } }; let start_field = start.as_ref() @@ -293,12 +294,25 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { hir::ExprLoop(ref body, _) => ExprKind::Loop { condition: None, body: block::to_expr_ref(cx, body) }, - hir::ExprField(ref source, name) => - ExprKind::Field { lhs: source.to_ref(), - name: Field::Named(name.node) }, + hir::ExprField(ref source, name) => { + let index = match cx.tcx.expr_ty_adjusted(source).sty { + ty::TyStruct(adt_def, _) => + adt_def.variants[0].index_of_field_named(name.node), + ref ty => + cx.tcx.sess.span_bug( + self.span, + &format!("field of non-struct: {:?}", ty)), + }; + let index = index.unwrap_or_else(|| { + cx.tcx.sess.span_bug( + self.span, + &format!("no index found for field `{}`", name.node)); + }); + ExprKind::Field { lhs: source.to_ref(), name: Field::new(index) } + } hir::ExprTupField(ref source, index) => ExprKind::Field { lhs: source.to_ref(), - name: Field::Indexed(index.node) }, + name: Field::new(index.node as usize) }, hir::ExprCast(ref source, _) => ExprKind::Cast { source: source.to_ref() }, hir::ExprBox(ref value) => @@ -616,7 +630,7 @@ fn convert_var<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, // at this point we have `self.n`, which loads up the upvar let field_kind = ExprKind::Field { lhs: self_expr.to_ref(), - name: Field::Indexed(index), + name: Field::new(index), }; // ...but the upvar might be an `&T` or `&mut T` capture, at which @@ -814,3 +828,15 @@ fn loop_label<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, expr: &'tcx hir::Expr) -> Cod } } } + +fn field_refs<'tcx>(variant: VariantDef<'tcx>, + fields: &'tcx [hir::Field]) + -> Vec> +{ + fields.iter() + .map(|field| FieldExprRef { + name: Field::new(variant.index_of_field_named(field.name.node).unwrap()), + expr: field.expr.to_ref(), + }) + .collect() +} diff --git a/src/librustc_mir/tcx/mod.rs b/src/librustc_mir/hair/cx/mod.rs similarity index 90% rename from src/librustc_mir/tcx/mod.rs rename to src/librustc_mir/hair/cx/mod.rs index 92b026e5035b4..8d4b05afcb6e6 100644 --- a/src/librustc_mir/tcx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -24,7 +24,7 @@ use rustc::middle::infer::InferCtxt; use rustc::middle::subst::{Subst, Substs}; use rustc::middle::ty::{self, Ty}; use syntax::codemap::Span; -use syntax::parse::token::{self, special_idents}; +use syntax::parse::token; #[derive(Copy, Clone)] pub struct Cx<'a, 'tcx: 'a> { @@ -87,18 +87,9 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { adt_def.variants.len() } - pub fn fields(&mut self, adt_def: ty::AdtDef<'tcx>, variant_index: usize) -> Vec { - adt_def.variants[variant_index] - .fields - .iter() - .enumerate() - .map(|(index, field)| { - if field.name == special_idents::unnamed_field.name { - Field::Indexed(index) - } else { - Field::Named(field.name) - } - }) + pub fn all_fields(&mut self, adt_def: ty::AdtDef<'tcx>, variant_index: usize) -> Vec { + (0..adt_def.variants[variant_index].fields.len()) + .map(Field::new) .collect() } diff --git a/src/librustc_mir/tcx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs similarity index 89% rename from src/librustc_mir/tcx/pattern.rs rename to src/librustc_mir/hair/cx/pattern.rs index db4346364968c..31dbffa0ae348 100644 --- a/src/librustc_mir/tcx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -9,12 +9,11 @@ // except according to those terms. use hair::*; +use hair::cx::Cx; +use hair::cx::to_ref::ToRef; use repr::*; - use rustc_data_structures::fnv::FnvHashMap; use std::rc::Rc; -use tcx::Cx; -use tcx::to_ref::ToRef; use rustc::middle::const_eval; use rustc::middle::def; use rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding}; @@ -223,7 +222,7 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> { subpatterns.iter() .enumerate() .map(|(i, subpattern)| FieldPatternRef { - field: Field::Indexed(i), + field: Field::new(i), pattern: self.pat_ref(subpattern), }) .collect(); @@ -273,7 +272,7 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> { .flat_map(|v| v.iter()) .enumerate() .map(|(i, field)| FieldPatternRef { - field: Field::Indexed(i), + field: Field::new(i), pattern: self.pat_ref(field), }) .collect(); @@ -281,13 +280,35 @@ impl<'tcx> Mirror<'tcx> for PatNode<'tcx> { } hir::PatStruct(_, ref fields, _) => { + let pat_ty = cx.tcx.node_id_to_type(self.pat.id); + let adt_def = match pat_ty.sty { + ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def, + _ => { + cx.tcx.sess.span_bug( + self.pat.span, + "struct pattern not applied to struct or enum"); + } + }; + + let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def(); + let variant_def = adt_def.variant_of_def(def); + let subpatterns = fields.iter() - .map(|field| FieldPatternRef { - field: Field::Named(field.node.name), - pattern: self.pat_ref(&field.node.pat), + .map(|field| { + let index = variant_def.index_of_field_named(field.node.name); + let index = index.unwrap_or_else(|| { + cx.tcx.sess.span_bug( + self.pat.span, + &format!("no field with name {:?}", field.node.name)); + }); + FieldPatternRef { + field: Field::new(index), + pattern: self.pat_ref(&field.node.pat), + } }) .collect(); + self.variant_or_leaf(cx, subpatterns) } diff --git a/src/librustc_mir/tcx/to_ref.rs b/src/librustc_mir/hair/cx/to_ref.rs similarity index 85% rename from src/librustc_mir/tcx/to_ref.rs rename to src/librustc_mir/hair/cx/to_ref.rs index 13ca82e3e4c71..e0b8abfbd9ce3 100644 --- a/src/librustc_mir/tcx/to_ref.rs +++ b/src/librustc_mir/hair/cx/to_ref.rs @@ -9,9 +9,8 @@ // except according to those terms. use hair::*; -use repr::*; -use tcx::pattern::PatNode; +use hair::cx::pattern::PatNode; use rustc_front::hir; use syntax::ptr::P; @@ -79,14 +78,3 @@ impl<'a,'tcx:'a,T,U> ToRef for &'tcx Vec self.iter().map(|expr| expr.to_ref()).collect() } } - -impl<'a,'tcx:'a> ToRef for &'tcx hir::Field { - type Output = FieldExprRef<'tcx>; - - fn to_ref(self) -> FieldExprRef<'tcx> { - FieldExprRef { - name: Field::Named(self.name.node), - expr: self.expr.to_ref(), - } - } -} diff --git a/src/librustc_mir/hair.rs b/src/librustc_mir/hair/mod.rs similarity index 98% rename from src/librustc_mir/hair.rs rename to src/librustc_mir/hair/mod.rs index 641cbae4be271..becaa19974d8e 100644 --- a/src/librustc_mir/hair.rs +++ b/src/librustc_mir/hair/mod.rs @@ -22,7 +22,9 @@ use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty}; use rustc_front::hir; use syntax::ast; use syntax::codemap::Span; -use tcx::{Cx, PatNode}; +use self::cx::{Cx, PatNode}; + +pub mod cx; #[derive(Clone, Debug)] pub struct ItemRef<'tcx> { @@ -41,7 +43,6 @@ pub struct Block<'tcx> { #[derive(Clone, Debug)] pub enum StmtRef<'tcx> { - Hair(&'tcx hir::Stmt), Mirror(Box>), } @@ -392,9 +393,8 @@ impl<'tcx> Mirror<'tcx> for Stmt<'tcx> { impl<'tcx> Mirror<'tcx> for StmtRef<'tcx> { type Output = Stmt<'tcx>; - fn make_mirror<'a>(self, hir: &mut Cx<'a, 'tcx>) -> Stmt<'tcx> { + fn make_mirror<'a>(self, _: &mut Cx<'a,'tcx>) -> Stmt<'tcx> { match self { - StmtRef::Hair(h) => h.make_mirror(hir), StmtRef::Mirror(m) => *m, } } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 934dd660177b8..215f708cadd0c 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -31,7 +31,8 @@ extern crate syntax; pub mod build; pub mod mir_map; -pub mod hair; +mod hair; pub mod repr; mod graphviz; -mod tcx; + + diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 555f8896b4bc5..ebcb1db1151ee 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -23,8 +23,8 @@ extern crate rustc_front; use build; use dot; use repr::Mir; +use hair::cx::{PatNode, Cx}; use std::fs::File; -use tcx::{PatNode, Cx}; use self::rustc::middle::infer; use self::rustc::middle::region::CodeExtentData; From 3ab29d337834383c159b2a4e275581f97ad34a25 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:28:51 -0400 Subject: [PATCH 07/18] Add adt_def into Switch, since it's convenient to have in trans --- src/librustc_mir/build/matches/test.rs | 1 + src/librustc_mir/repr.rs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index d5745eb28c7a8..0d01df485fa8a 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -92,6 +92,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { .collect(); self.cfg.terminate(block, Terminator::Switch { discr: lvalue.clone(), + adt_def: adt_def, targets: target_blocks.clone() }); target_blocks diff --git a/src/librustc_mir/repr.rs b/src/librustc_mir/repr.rs index 5bf326ba5a5fe..eb919f36cc370 100644 --- a/src/librustc_mir/repr.rs +++ b/src/librustc_mir/repr.rs @@ -247,6 +247,7 @@ pub enum Terminator<'tcx> { /// lvalue evaluates to some enum; jump depending on the branch Switch { discr: Lvalue<'tcx>, + adt_def: AdtDef<'tcx>, targets: Vec, }, @@ -279,7 +280,7 @@ impl<'tcx> Terminator<'tcx> { Goto { target: ref b } => slice::ref_slice(b), Panic { target: ref b } => slice::ref_slice(b), If { cond: _, targets: ref b } => b, - Switch { discr: _, targets: ref b } => b, + Switch { discr: _, adt_def: _, targets: ref b } => b, Diverge => &[], Return => &[], Call { data: _, targets: ref b } => b, @@ -318,7 +319,7 @@ impl<'tcx> Debug for Terminator<'tcx> { write!(fmt, "panic -> {:?}", target), If { cond: ref lv, ref targets } => write!(fmt, "if({:?}) -> {:?}", lv, targets), - Switch { discr: ref lv, ref targets } => + Switch { discr: ref lv, adt_def: _, ref targets } => write!(fmt, "switch({:?}) -> {:?}", lv, targets), Diverge => write!(fmt, "diverge"), From 044096b3e9a8d02461d49fb5559bb11c4308e701 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:29:50 -0400 Subject: [PATCH 08/18] Change Call operands to be, well, Operands --- src/librustc_mir/build/expr/into.rs | 4 ++-- src/librustc_mir/build/matches/test.rs | 19 ++++++++++--------- src/librustc_mir/build/misc.rs | 26 +++++++++++--------------- src/librustc_mir/repr.rs | 4 ++-- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index a7d68b09b5459..57c6db79c5271 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -211,10 +211,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { this.cfg.start_new_block().unit() } ExprKind::Call { fun, args } => { - let fun = unpack!(block = this.as_lvalue(block, fun)); + let fun = unpack!(block = this.as_operand(block, fun)); let args: Vec<_> = args.into_iter() - .map(|arg| unpack!(block = this.as_lvalue(block, arg))) + .map(|arg| unpack!(block = this.as_operand(block, arg))) .collect(); let success = this.cfg.start_new_block(); let panic = this.diverge_cleanup(); diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 0d01df485fa8a..e035f53dacf41 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -100,27 +100,28 @@ impl<'a,'tcx> Builder<'a,'tcx> { TestKind::Eq { value, ty } => { // call PartialEq::eq(discrim, constant) - let constant = self.push_literal(block, test.span, ty.clone(), value); + let constant = self.literal_operand(test.span, ty.clone(), value); let item_ref = self.hir.partial_eq(ty); - self.call_comparison_fn(block, test.span, item_ref, lvalue.clone(), constant) + self.call_comparison_fn(block, test.span, item_ref, + Operand::Consume(lvalue.clone()), constant) } TestKind::Range { lo, hi, ty } => { // Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`. - let lo = self.push_literal(block, test.span, ty.clone(), lo); - let hi = self.push_literal(block, test.span, ty.clone(), hi); + let lo = self.literal_operand(test.span, ty.clone(), lo); + let hi = self.literal_operand(test.span, ty.clone(), hi); let item_ref = self.hir.partial_le(ty); let lo_blocks = self.call_comparison_fn(block, test.span, item_ref.clone(), lo, - lvalue.clone()); + Operand::Consume(lvalue.clone())); let hi_blocks = self.call_comparison_fn(lo_blocks[0], test.span, item_ref, - lvalue.clone(), + Operand::Consume(lvalue.clone()), hi); let failure = self.cfg.start_new_block(); @@ -165,14 +166,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { block: BasicBlock, span: Span, item_ref: ItemRef<'tcx>, - lvalue1: Lvalue<'tcx>, - lvalue2: Lvalue<'tcx>) + lvalue1: Operand<'tcx>, + lvalue2: Operand<'tcx>) -> Vec { let target_blocks = vec![self.cfg.start_new_block(), self.cfg.start_new_block()]; let bool_ty = self.hir.bool_ty(); let eq_result = self.temp(bool_ty); - let func = self.push_item_ref(block, span, item_ref); + let func = self.item_ref_operand(span, item_ref); let call_blocks = [self.cfg.start_new_block(), self.diverge_cleanup()]; self.cfg.terminate(block, Terminator::Call { diff --git a/src/librustc_mir/build/misc.rs b/src/librustc_mir/build/misc.rs index 86b6df19b77e7..41274f3f3736e 100644 --- a/src/librustc_mir/build/misc.rs +++ b/src/librustc_mir/build/misc.rs @@ -34,20 +34,17 @@ impl<'a,'tcx> Builder<'a,'tcx> { lvalue } - pub fn push_literal(&mut self, - block: BasicBlock, - span: Span, - ty: Ty<'tcx>, - literal: Literal<'tcx>) - -> Lvalue<'tcx> { - let temp = self.temp(ty.clone()); + pub fn literal_operand(&mut self, + span: Span, + ty: Ty<'tcx>, + literal: Literal<'tcx>) + -> Operand<'tcx> { let constant = Constant { span: span, ty: ty, literal: literal, }; - self.cfg.push_assign_constant(block, span, &temp, constant); - temp + Operand::Constant(constant) } pub fn push_usize(&mut self, block: BasicBlock, span: Span, value: usize) -> Lvalue<'tcx> { @@ -63,15 +60,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { temp } - pub fn push_item_ref(&mut self, - block: BasicBlock, - span: Span, - item_ref: ItemRef<'tcx>) - -> Lvalue<'tcx> { + pub fn item_ref_operand(&mut self, + span: Span, + item_ref: ItemRef<'tcx>) + -> Operand<'tcx> { let literal = Literal::Item { def_id: item_ref.def_id, substs: item_ref.substs, }; - self.push_literal(block, span, item_ref.ty, literal) + self.literal_operand(span, item_ref.ty, literal) } } diff --git a/src/librustc_mir/repr.rs b/src/librustc_mir/repr.rs index eb919f36cc370..89b1afa872381 100644 --- a/src/librustc_mir/repr.rs +++ b/src/librustc_mir/repr.rs @@ -294,10 +294,10 @@ pub struct CallData<'tcx> { pub destination: Lvalue<'tcx>, /// the fn being called - pub func: Lvalue<'tcx>, + pub func: Operand<'tcx>, /// the arguments - pub args: Vec>, + pub args: Vec>, } impl<'tcx> BasicBlockData<'tcx> { From 0a62158a4e1bb012d5b0778701dd67b65a8754c2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:30:17 -0400 Subject: [PATCH 09/18] Add helper methods that require tcx; these compute types of lvalues and operands --- src/librustc_mir/lib.rs | 2 +- src/librustc_mir/tcx/mod.rs | 125 ++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 src/librustc_mir/tcx/mod.rs diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 215f708cadd0c..5c52dfe2bd673 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -34,5 +34,5 @@ pub mod mir_map; mod hair; pub mod repr; mod graphviz; - +pub mod tcx; diff --git a/src/librustc_mir/tcx/mod.rs b/src/librustc_mir/tcx/mod.rs new file mode 100644 index 0000000000000..3b9d9228a1680 --- /dev/null +++ b/src/librustc_mir/tcx/mod.rs @@ -0,0 +1,125 @@ +// Copyright 2015 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. + +/*! + * Methods for the various MIR types. These are intended for use after + * building is complete. + */ + +use repr::*; +use rustc::middle::subst::Substs; +use rustc::middle::ty::{self, AdtDef, Ty}; + +#[derive(Copy, Clone, Debug)] +pub enum LvalueTy<'tcx> { + /// Normal type. + Ty { ty: Ty<'tcx> }, + + /// Downcast to a particular variant of an enum. + Downcast { adt_def: AdtDef<'tcx>, + substs: &'tcx Substs<'tcx>, + variant_index: usize }, +} + +impl<'tcx> LvalueTy<'tcx> { + pub fn from_ty(ty: Ty<'tcx>) -> LvalueTy<'tcx> { + LvalueTy::Ty { ty: ty } + } + + pub fn to_ty(&self, tcx: &ty::ctxt<'tcx>) -> Ty<'tcx> { + match *self { + LvalueTy::Ty { ty } => + ty, + LvalueTy::Downcast { adt_def, substs, variant_index: _ } => + tcx.mk_enum(adt_def, substs), + } + } + + pub fn projection_ty(self, + tcx: &ty::ctxt<'tcx>, + elem: &LvalueElem<'tcx>) + -> LvalueTy<'tcx> + { + match *elem { + ProjectionElem::Deref => + LvalueTy::Ty { + ty: self.to_ty(tcx).builtin_deref(true, ty::LvaluePreference::NoPreference) + .unwrap() + .ty + }, + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. } => + LvalueTy::Ty { + ty: self.to_ty(tcx).builtin_index().unwrap() + }, + ProjectionElem::Downcast(adt_def1, index) => + match self.to_ty(tcx).sty { + ty::TyEnum(adt_def, substs) => { + assert!(index < adt_def.variants.len()); + assert_eq!(adt_def, adt_def1); + LvalueTy::Downcast { adt_def: adt_def, + substs: substs, + variant_index: index } + } + _ => { + tcx.sess.bug(&format!("cannot downcast non-enum type: `{:?}`", self)) + } + }, + ProjectionElem::Field(field) => { + let field_ty = match self { + LvalueTy::Ty { ty } => match ty.sty { + ty::TyStruct(adt_def, substs) => + adt_def.struct_variant().fields[field.index()].ty(tcx, substs), + ty::TyTuple(ref tys) => + tys[field.index()], + _ => + tcx.sess.bug(&format!("cannot get field of type: `{:?}`", ty)), + }, + LvalueTy::Downcast { adt_def, substs, variant_index } => + adt_def.variants[variant_index].fields[field.index()].ty(tcx, substs), + }; + LvalueTy::Ty { ty: field_ty } + } + } + } +} + +impl<'tcx> Mir<'tcx> { + pub fn operand_ty(&self, + tcx: &ty::ctxt<'tcx>, + operand: &Operand<'tcx>) + -> Ty<'tcx> + { + match *operand { + Operand::Consume(ref l) => self.lvalue_ty(tcx, l).to_ty(tcx), + Operand::Constant(ref c) => c.ty, + } + } + + pub fn lvalue_ty(&self, + tcx: &ty::ctxt<'tcx>, + lvalue: &Lvalue<'tcx>) + -> LvalueTy<'tcx> + { + match *lvalue { + Lvalue::Var(index) => + LvalueTy::Ty { ty: self.var_decls[index as usize].ty }, + Lvalue::Temp(index) => + LvalueTy::Ty { ty: self.temp_decls[index as usize].ty }, + Lvalue::Arg(index) => + LvalueTy::Ty { ty: self.arg_decls[index as usize].ty }, + Lvalue::Static(def_id) => + LvalueTy::Ty { ty: tcx.lookup_item_type(def_id).ty }, + Lvalue::ReturnPointer => + LvalueTy::Ty { ty: self.return_ty.unwrap() }, + Lvalue::Projection(ref proj) => + self.lvalue_ty(tcx, &proj.base).projection_ty(tcx, &proj.elem) + } + } +} From 81ff2c2f8e964c33ac8d4b3570fc881658301068 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:33:08 -0400 Subject: [PATCH 10/18] Change adt case handling fn to be less tied to match --- src/librustc_trans/trans/_match.rs | 2 +- src/librustc_trans/trans/adt.rs | 10 ++++------ src/librustc_trans/trans/base.rs | 9 ++------- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 2654ab3339a10..3c53d55886565 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -303,7 +303,7 @@ impl<'a, 'tcx> Opt<'a, 'tcx> { RangeResult(Result::new(bcx, l1), Result::new(bcx, l2)) } Variant(disr_val, ref repr, _, _) => { - adt::trans_case(bcx, &**repr, disr_val) + SingleResult(Result::new(bcx, adt::trans_case(bcx, &**repr, disr_val))) } SliceLengthEqual(length, _) => { SingleResult(Result::new(bcx, C_uint(ccx, length))) diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index de09e33ec1427..a4f66110450df 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -945,15 +945,13 @@ fn load_discr(bcx: Block, ity: IntType, ptr: ValueRef, min: Disr, max: Disr) /// /// This should ideally be less tightly tied to `_match`. pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr) - -> _match::OptResult<'blk, 'tcx> { + -> ValueRef { match *r { CEnum(ity, _, _) => { - _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), - discr as u64, true))) + C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true) } General(ity, _, _) => { - _match::SingleResult(Result::new(bcx, C_integral(ll_inttype(bcx.ccx(), ity), - discr as u64, true))) + C_integral(ll_inttype(bcx.ccx(), ity), discr as u64, true) } Univariant(..) => { bcx.ccx().sess().bug("no cases for univariants or structs") @@ -961,7 +959,7 @@ pub fn trans_case<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, r: &Repr, discr: Disr) RawNullablePointer { .. } | StructWrappedNullablePointer { .. } => { assert!(discr == 0 || discr == 1); - _match::SingleResult(Result::new(bcx, C_bool(bcx.ccx(), discr != 0))) + C_bool(bcx.ccx(), discr != 0) } } } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index f28b7e8f52dc3..9530c6d4058ee 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -498,13 +498,8 @@ pub fn iter_structural_ty<'blk, 'tcx, F>(cx: Block<'blk, 'tcx>, &format!("enum-iter-variant-{}", &variant.disr_val.to_string()) ); - match adt::trans_case(cx, &*repr, variant.disr_val) { - _match::SingleResult(r) => { - AddCase(llswitch, r.val, variant_cx.llbb) - } - _ => ccx.sess().unimpl("value from adt::trans_case \ - in iter_structural_ty") - } + let case_val = adt::trans_case(cx, &*repr, variant.disr_val); + AddCase(llswitch, case_val, variant_cx.llbb); let variant_cx = iter_variant(variant_cx, &*repr, From 877b93add2a0d7cc603fa3146a3b9b0af0215e9d Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:35:15 -0400 Subject: [PATCH 11/18] Move shifting code out of expr and into somewhere more accessible --- src/librustc_trans/trans/base.rs | 12 ++++- src/librustc_trans/trans/common.rs | 78 +++++++++++++++++++++++++++++- src/librustc_trans/trans/expr.rs | 61 ----------------------- 3 files changed, 88 insertions(+), 63 deletions(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 9530c6d4058ee..a536060efbd0f 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -75,6 +75,7 @@ use trans::intrinsic; use trans::machine; use trans::machine::{llsize_of, llsize_of_real}; use trans::meth; +use trans::mir; use trans::monomorphize; use trans::tvec; use trans::type_::Type; @@ -1231,7 +1232,10 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, false }; + let mir = ccx.mir_map().get(&id); + let mut fcx = FunctionContext { + mir: mir, llfn: llfndecl, llenv: None, llretslotptr: Cell::new(None), @@ -1571,7 +1575,7 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, llfndecl: ValueRef, param_substs: &'tcx Substs<'tcx>, fn_ast_id: ast::NodeId, - _attributes: &[ast::Attribute], + attributes: &[ast::Attribute], output_type: ty::FnOutput<'tcx>, abi: Abi, closure_env: closure::ClosureEnv<'b>) { @@ -1600,6 +1604,12 @@ pub fn trans_closure<'a, 'b, 'tcx>(ccx: &CrateContext<'a, 'tcx>, &arena); let mut bcx = init_function(&fcx, false, output_type); + if attributes.iter().any(|item| item.check_name("rustc_mir")) { + mir::trans_mir(bcx); + fcx.cleanup(); + return; + } + // cleanup scope for the incoming arguments let fn_cleanup_debug_loc = debuginfo::get_cleanup_debug_loc_for_ast_node(ccx, fn_ast_id, body.span, true); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index b39b7818a6350..b5f192b972742 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -16,7 +16,7 @@ pub use self::ExprOrMethodCall::*; use session::Session; use llvm; -use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef}; +use llvm::{ValueRef, BasicBlockRef, BuilderRef, ContextRef, TypeKind}; use llvm::{True, False, Bool}; use middle::cfg; use middle::def; @@ -40,6 +40,7 @@ use middle::traits; use middle::ty::{self, HasTypeFlags, Ty}; use middle::ty::fold::{TypeFolder, TypeFoldable}; use rustc_front::hir; +use rustc_mir::repr::Mir; use util::nodemap::{FnvHashMap, NodeMap}; use arena::TypedArena; @@ -328,6 +329,11 @@ impl<'tcx> DropFlagHintsMap<'tcx> { // Function context. Every LLVM function we create will have one of // these. pub struct FunctionContext<'a, 'tcx: 'a> { + // The MIR for this function. At present, this is optional because + // we only have MIR available for things that are local to the + // crate. + pub mir: Option<&'a Mir<'tcx>>, + // The ValueRef returned from a call to llvm::LLVMAddFunction; the // address of the first instruction in the sequence of // instructions for this function that will go in the .text @@ -407,6 +413,10 @@ pub struct FunctionContext<'a, 'tcx: 'a> { } impl<'a, 'tcx> FunctionContext<'a, 'tcx> { + pub fn mir(&self) -> &'a Mir<'tcx> { + self.mir.unwrap() + } + pub fn arg_offset(&self) -> usize { self.env_arg_pos() + if self.llenv.is_some() { 1 } else { 0 } } @@ -644,6 +654,10 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> { } pub fn sess(&self) -> &'blk Session { self.fcx.ccx.sess() } + pub fn mir(&self) -> &'blk Mir<'tcx> { + self.fcx.mir() + } + pub fn name(&self, name: ast::Name) -> String { name.to_string() } @@ -1132,3 +1146,65 @@ pub fn inlined_variant_def<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.sess().bug(&format!("no variant for {:?}::{}", adt_def, inlined_vid)) }) } + +// To avoid UB from LLVM, these two functions mask RHS with an +// appropriate mask unconditionally (i.e. the fallback behavior for +// all shifts). For 32- and 64-bit types, this matches the semantics +// of Java. (See related discussion on #1877 and #10183.) + +pub fn build_unchecked_lshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + binop_debug_loc: DebugLoc) -> ValueRef { + let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); + build::Shl(bcx, lhs, rhs, binop_debug_loc) +} + +pub fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + lhs_t: Ty<'tcx>, + lhs: ValueRef, + rhs: ValueRef, + binop_debug_loc: DebugLoc) -> ValueRef { + let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); + let is_signed = lhs_t.is_signed(); + if is_signed { + build::AShr(bcx, lhs, rhs, binop_debug_loc) + } else { + build::LShr(bcx, lhs, rhs, binop_debug_loc) + } +} + +fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + rhs: ValueRef, + debug_loc: DebugLoc) -> ValueRef { + let rhs_llty = val_ty(rhs); + build::And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc) +} + +pub fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + llty: Type, + mask_llty: Type, + invert: bool) -> ValueRef { + let kind = llty.kind(); + match kind { + TypeKind::Integer => { + // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. + let val = llty.int_width() - 1; + if invert { + C_integral(mask_llty, !val, true) + } else { + C_integral(mask_llty, val, false) + } + }, + TypeKind::Vector => { + let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert); + build::VectorSplat(bcx, mask_llty.vector_length(), mask) + }, + _ => panic!("shift_mask_val: expected Integer or Vector, found {:?}", kind), + } +} + diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 6144de7109fba..7648587e35268 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -2574,29 +2574,6 @@ impl OverflowOpViaInputCheck { } } -fn shift_mask_val<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - llty: Type, - mask_llty: Type, - invert: bool) -> ValueRef { - let kind = llty.kind(); - match kind { - TypeKind::Integer => { - // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. - let val = llty.int_width() - 1; - if invert { - C_integral(mask_llty, !val, true) - } else { - C_integral(mask_llty, val, false) - } - }, - TypeKind::Vector => { - let mask = shift_mask_val(bcx, llty.element_type(), mask_llty.element_type(), invert); - VectorSplat(bcx, mask_llty.vector_length(), mask) - }, - _ => panic!("shift_mask_val: expected Integer or Vector, found {:?}", kind), - } -} - // Check if an integer or vector contains a nonzero element. fn build_nonzero_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, value: ValueRef, @@ -2616,44 +2593,6 @@ fn build_nonzero_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, } } -// To avoid UB from LLVM, these two functions mask RHS with an -// appropriate mask unconditionally (i.e. the fallback behavior for -// all shifts). For 32- and 64-bit types, this matches the semantics -// of Java. (See related discussion on #1877 and #10183.) - -fn build_unchecked_lshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - binop_debug_loc: DebugLoc) -> ValueRef { - let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShl, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); - Shl(bcx, lhs, rhs, binop_debug_loc) -} - -fn build_unchecked_rshift<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - lhs_t: Ty<'tcx>, - lhs: ValueRef, - rhs: ValueRef, - binop_debug_loc: DebugLoc) -> ValueRef { - let rhs = base::cast_shift_expr_rhs(bcx, hir::BinOp_::BiShr, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bcx, rhs, binop_debug_loc); - let is_signed = lhs_t.is_signed(); - if is_signed { - AShr(bcx, lhs, rhs, binop_debug_loc) - } else { - LShr(bcx, lhs, rhs, binop_debug_loc) - } -} - -fn shift_mask_rhs<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, - rhs: ValueRef, - debug_loc: DebugLoc) -> ValueRef { - let rhs_llty = val_ty(rhs); - And(bcx, rhs, shift_mask_val(bcx, rhs_llty, rhs_llty, false), debug_loc) -} - fn with_overflow_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, oop: OverflowOp, info: NodeIdAndSpan, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef, From 02017b30ebebfaeb64a5a86a885773f38057beba Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Oct 2015 17:42:25 -0400 Subject: [PATCH 12/18] New trans codepath that builds fn body from MIR instead. --- src/librustc_trans/trans/common.rs | 6 + src/librustc_trans/trans/mir/block.rs | 106 ++++++++++ src/librustc_trans/trans/mir/constant.rs | 63 ++++++ src/librustc_trans/trans/mir/lvalue.rs | 149 ++++++++++++++ src/librustc_trans/trans/mir/mod.rs | 162 +++++++++++++++ src/librustc_trans/trans/mir/operand.rs | 90 ++++++++ src/librustc_trans/trans/mir/rvalue.rs | 238 ++++++++++++++++++++++ src/librustc_trans/trans/mir/statement.rs | 48 +++++ src/librustc_trans/trans/mod.rs | 1 + src/test/run-pass/mir_trans_spike1.rs | 24 +++ 10 files changed, 887 insertions(+) create mode 100644 src/librustc_trans/trans/mir/block.rs create mode 100644 src/librustc_trans/trans/mir/constant.rs create mode 100644 src/librustc_trans/trans/mir/lvalue.rs create mode 100644 src/librustc_trans/trans/mir/mod.rs create mode 100644 src/librustc_trans/trans/mir/operand.rs create mode 100644 src/librustc_trans/trans/mir/rvalue.rs create mode 100644 src/librustc_trans/trans/mir/statement.rs create mode 100644 src/test/run-pass/mir_trans_spike1.rs diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index b5f192b972742..8d6ba53dd222d 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -743,6 +743,12 @@ pub fn C_floating(s: &str, t: Type) -> ValueRef { } } +pub fn C_floating_f64(f: f64, t: Type) -> ValueRef { + unsafe { + llvm::LLVMConstReal(t.to_ref(), f) + } +} + pub fn C_nil(ccx: &CrateContext) -> ValueRef { C_struct(ccx, &[], false) } diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs new file mode 100644 index 0000000000000..c9f4123c17127 --- /dev/null +++ b/src/librustc_trans/trans/mir/block.rs @@ -0,0 +1,106 @@ +// Copyright 2012-2014 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 llvm::BasicBlockRef; +use rustc_mir::repr as mir; +use trans::base; +use trans::build; +use trans::common::Block; +use trans::debuginfo::DebugLoc; + +use super::MirContext; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_block(&mut self, bb: mir::BasicBlock) { + debug!("trans_block({:?})", bb); + + let mut bcx = self.bcx(bb); + let data = self.mir.basic_block_data(bb); + + for statement in &data.statements { + bcx = self.trans_statement(bcx, statement); + } + + debug!("trans_block: terminator: {:?}", data.terminator); + + match data.terminator { + mir::Terminator::Goto { target } => { + build::Br(bcx, self.llblock(target), DebugLoc::None) + } + + mir::Terminator::Panic { .. } => { + unimplemented!() + } + + mir::Terminator::If { ref cond, targets: [true_bb, false_bb] } => { + let cond = self.trans_operand(bcx, cond); + let lltrue = self.llblock(true_bb); + let llfalse = self.llblock(false_bb); + build::CondBr(bcx, cond.llval, lltrue, llfalse, DebugLoc::None); + } + + mir::Terminator::Switch { .. } => { + unimplemented!() + } + + mir::Terminator::Diverge => { + if let Some(llpersonalityslot) = self.llpersonalityslot { + let lp = build::Load(bcx, llpersonalityslot); + // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality); + build::Resume(bcx, lp); + } else { + // This fn never encountered anything fallible, so + // a Diverge cannot actually happen. Note that we + // do a total hack to ensure that we visit the + // DIVERGE block last. + build::Unreachable(bcx); + } + } + + mir::Terminator::Return => { + let return_ty = bcx.monomorphize(&self.mir.return_ty); + base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None); + } + + mir::Terminator::Call { .. } => { + unimplemented!() + //let llbb = unimplemented!(); // self.make_landing_pad(panic_bb); + // + //let tr_dest = self.trans_lvalue(bcx, &data.destination); + // + //// Create the callee. This will always be a fn + //// ptr and hence a kind of scalar. + //let callee = self.trans_operand(bcx, &data.func); + // + //// Process the arguments. + // + //let args = unimplemented!(); + // + //callee::trans_call_inner(bcx, + // DebugLoc::None, + // |bcx, _| Callee { + // bcx: bcx, + // data: CalleeData::Fn(callee.llval), + // ty: callee.ty, + // }, + // args, + // Some(Dest::SaveIn(tr_dest.llval))); + } + } + } + + fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> { + self.blocks[bb.index()] + } + + fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef { + self.blocks[bb.index()].llbb + } +} diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs new file mode 100644 index 0000000000000..1b61001834a99 --- /dev/null +++ b/src/librustc_trans/trans/mir/constant.rs @@ -0,0 +1,63 @@ +// Copyright 2012-2014 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 llvm::ValueRef; +use rustc::middle::const_eval::ConstVal; +use rustc_mir::repr as mir; +use trans::consts::{self, TrueConst}; +use trans::common::{self, Block}; +use trans::type_of; + +use super::MirContext; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_constant(&mut self, + bcx: Block<'bcx, 'tcx>, + constant: &mir::Constant<'tcx>) + -> ValueRef + { + let ccx = bcx.ccx(); + let constant_ty = bcx.monomorphize(&constant.ty); + let llty = type_of::type_of(ccx, constant_ty); + match constant.literal { + mir::Literal::Item { .. } => { + unimplemented!() + } + mir::Literal::Value { ref value } => { + match *value { + ConstVal::Float(v) => common::C_floating_f64(v, llty), + ConstVal::Bool(v) => common::C_bool(ccx, v), + ConstVal::Int(v) => common::C_integral(llty, v as u64, true), + ConstVal::Uint(v) => common::C_integral(llty, v, false), + ConstVal::Str(ref v) => common::C_str_slice(ccx, v.clone()), + ConstVal::ByteStr(ref v) => consts::addr_of(ccx, + common::C_bytes(ccx, v), + 1, + "byte_str"), + ConstVal::Struct(id) | ConstVal::Tuple(id) => { + let expr = bcx.tcx().map.expect_expr(id); + let (llval, _) = match consts::const_expr(ccx, + expr, + bcx.fcx.param_substs, + None, + TrueConst::Yes) { + Ok(v) => v, + Err(_) => panic!("constant eval failure"), + }; + llval + } + ConstVal::Function(_) => { + unimplemented!() + } + } + } + } + } +} diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs new file mode 100644 index 0000000000000..282c6a7e6de17 --- /dev/null +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -0,0 +1,149 @@ +// Copyright 2012-2014 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 llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_mir::repr as mir; +use rustc_mir::tcx::LvalueTy; +use trans::adt; +use trans::base; +use trans::build; +use trans::common::{self, Block}; +use trans::debuginfo::DebugLoc; +use trans::machine; +use trans::tvec; + +use super::MirContext; + +#[derive(Copy, Clone)] +pub struct LvalueRef<'tcx> { + /// Pointer to the contents of the lvalue + pub llval: ValueRef, + + /// Monomorphized type of this lvalue, including variant information + pub ty: LvalueTy<'tcx>, +} + +impl<'tcx> LvalueRef<'tcx> { + pub fn new(llval: ValueRef, lvalue_ty: LvalueTy<'tcx>) -> LvalueRef<'tcx> { + LvalueRef { llval: llval, ty: lvalue_ty } + } + + pub fn alloca<'bcx>(bcx: Block<'bcx, 'tcx>, + ty: Ty<'tcx>, + name: &str) + -> LvalueRef<'tcx> + { + let lltemp = base::alloc_ty(bcx, ty, name); + LvalueRef::new(lltemp, LvalueTy::from_ty(ty)) + } +} + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_lvalue(&mut self, + bcx: Block<'bcx, 'tcx>, + lvalue: &mir::Lvalue<'tcx>) + -> LvalueRef<'tcx> { + debug!("trans_lvalue(lvalue={:?})", lvalue); + + let fcx = bcx.fcx; + let ccx = fcx.ccx; + let tcx = bcx.tcx(); + match *lvalue { + mir::Lvalue::Var(index) => self.vars[index as usize], + mir::Lvalue::Temp(index) => self.temps[index as usize], + mir::Lvalue::Arg(index) => self.args[index as usize], + mir::Lvalue::Static(_def_id) => unimplemented!(), + mir::Lvalue::ReturnPointer => { + let return_ty = bcx.monomorphize(&self.mir.return_ty); + let llval = fcx.get_ret_slot(bcx, return_ty, "return"); + LvalueRef::new(llval, LvalueTy::from_ty(return_ty.unwrap())) + } + mir::Lvalue::Projection(ref projection) => { + let tr_base = self.trans_lvalue(bcx, &projection.base); + let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); + let llprojected = match projection.elem { + mir::ProjectionElem::Deref => { + let base_ty = tr_base.ty.to_ty(tcx); + base::load_ty(bcx, tr_base.llval, base_ty) + } + mir::ProjectionElem::Field(ref field) => { + let base_ty = tr_base.ty.to_ty(tcx); + let base_repr = adt::represent_type(ccx, base_ty); + let discr = match tr_base.ty { + LvalueTy::Ty { .. } => 0, + LvalueTy::Downcast { adt_def: _, substs: _, variant_index: v } => v, + }; + let discr = discr as u64; + adt::trans_field_ptr(bcx, &base_repr, tr_base.llval, discr, field.index()) + } + mir::ProjectionElem::Index(ref index) => { + let base_ty = tr_base.ty.to_ty(tcx); + let index = self.trans_operand(bcx, index); + let llindex = self.prepare_index(bcx, index.llval); + let (llbase, _) = tvec::get_base_and_len(bcx, tr_base.llval, base_ty); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: false, + min_length: _ } => { + let base_ty = tr_base.ty.to_ty(tcx); + let lloffset = common::C_u32(bcx.ccx(), offset); + let llindex = self.prepare_index(bcx, lloffset); + let (llbase, _) = tvec::get_base_and_len(bcx, + tr_base.llval, + base_ty); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: true, + min_length: _ } => { + let lloffset = common::C_u32(bcx.ccx(), offset); + let base_ty = tr_base.ty.to_ty(tcx); + let (llbase, lllen) = tvec::get_base_and_len(bcx, + tr_base.llval, + base_ty); + let llindex = build::Sub(bcx, lllen, lloffset, DebugLoc::None); + let llindex = self.prepare_index(bcx, llindex); + build::InBoundsGEP(bcx, llbase, &[llindex]) + } + mir::ProjectionElem::Downcast(..) => { + tr_base.llval + } + }; + LvalueRef { + llval: llprojected, + ty: projected_ty, + } + } + } + } + + /// Adjust the bitwidth of an index since LLVM is less forgiving + /// than we are. + /// + /// nmatsakis: is this still necessary? Not sure. + fn prepare_index(&mut self, + bcx: Block<'bcx, 'tcx>, + llindex: ValueRef) + -> ValueRef + { + let ccx = bcx.ccx(); + let index_size = machine::llbitsize_of_real(bcx.ccx(), common::val_ty(llindex)); + let int_size = machine::llbitsize_of_real(bcx.ccx(), ccx.int_type()); + if index_size < int_size { + build::ZExt(bcx, llindex, ccx.int_type()) + } else if index_size > int_size { + build::Trunc(bcx, llindex, ccx.int_type()) + } else { + llindex + } + } +} diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs new file mode 100644 index 0000000000000..6ed839d1a442d --- /dev/null +++ b/src/librustc_trans/trans/mir/mod.rs @@ -0,0 +1,162 @@ +// Copyright 2012-2014 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 libc::c_uint; +use llvm::{self, ValueRef}; +use rustc_mir::repr as mir; +use rustc_mir::tcx::LvalueTy; +use std::cell::Cell; +use trans::base; +use trans::build; +use trans::common::{self, Block}; +use trans::debuginfo::DebugLoc; +use trans::expr; +use trans::type_of; + +use self::lvalue::LvalueRef; + +// FIXME DebugLoc is always None right now + +/// Master context for translating MIR. +pub struct MirContext<'bcx, 'tcx:'bcx> { + mir: &'bcx mir::Mir<'tcx>, + + /// When unwinding is initiated, we have to store this personality + /// value somewhere so that we can load it and re-use it in the + /// resume instruction. The personality is (afaik) some kind of + /// value used for C++ unwinding, which must filter by type: we + /// don't really care about it very much. Anyway, this value + /// contains an alloca into which the personality is stored and + /// then later loaded when generating the DIVERGE_BLOCK. + llpersonalityslot: Option, + + /// A `Block` for each MIR `BasicBlock` + blocks: Vec>, + + /// An LLVM alloca for each MIR `VarDecl` + vars: Vec>, + + /// An LLVM alloca for each MIR `TempDecl` + temps: Vec>, + + /// The arguments to the function; as args are lvalues, these are + /// always indirect, though we try to avoid creating an alloca + /// when we can (and just reuse the pointer the caller provided). + args: Vec>, +} + +/////////////////////////////////////////////////////////////////////////// + +pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { + let fcx = bcx.fcx; + let mir = bcx.mir(); + + let mir_blocks = bcx.mir().all_basic_blocks(); + + // Allocate variable and temp allocas + let vars = mir.var_decls.iter() + .map(|decl| (bcx.monomorphize(&decl.ty), decl.name)) + .map(|(mty, name)| LvalueRef::alloca(bcx, mty, &name.as_str())) + .collect(); + let temps = mir.temp_decls.iter() + .map(|decl| bcx.monomorphize(&decl.ty)) + .enumerate() + .map(|(i, mty)| LvalueRef::alloca(bcx, mty, &format!("temp{:?}", i))) + .collect(); + let args = arg_value_refs(bcx, mir); + + // Allocate a `Block` for every basic block + let block_bcxs: Vec> = + mir_blocks.iter() + .map(|&bb| fcx.new_block(false, &format!("{:?}", bb), None)) + .collect(); + + // Branch to the START block + let start_bcx = block_bcxs[mir::START_BLOCK.index()]; + build::Br(bcx, start_bcx.llbb, DebugLoc::None); + + let mut mircx = MirContext { + mir: mir, + llpersonalityslot: None, + blocks: block_bcxs, + vars: vars, + temps: temps, + args: args, + }; + + // Translate the body of each block + for &bb in &mir_blocks { + if bb != mir::DIVERGE_BLOCK { + mircx.trans_block(bb); + } + } + + // Total hack: translate DIVERGE_BLOCK last. This is so that any + // panics which the fn may do can initialize the + // `llpersonalityslot` cell. We don't do this up front because the + // LLVM type of it is (frankly) annoying to compute. + mircx.trans_block(mir::DIVERGE_BLOCK); +} + +/// Produce, for each argument, a `ValueRef` pointing at the +/// argument's value. As arguments are lvalues, these are always +/// indirect. +fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>, + mir: &mir::Mir<'tcx>) + -> Vec> { + // FIXME tupled_args? I think I'd rather that mapping is done in MIR land though + let fcx = bcx.fcx; + let tcx = bcx.tcx(); + let mut idx = fcx.arg_offset() as c_uint; + mir.arg_decls + .iter() + .enumerate() + .map(|(arg_index, arg_decl)| { + let arg_ty = bcx.monomorphize(&arg_decl.ty); + let llval = if type_of::arg_is_indirect(bcx.ccx(), arg_ty) { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up, unless + // we emit extra-debug-info, which requires local allocas :(. + // FIXME: lifetimes, debug info + let llarg = llvm::get_param(fcx.llfn, idx); + idx += 1; + llarg + } else if common::type_is_fat_ptr(tcx, arg_ty) { + // we pass fat pointers as two words, but we want to + // represent them internally as a pointer two two words, + // so make an alloca to store them in. + let lldata = llvm::get_param(fcx.llfn, idx); + let llextra = llvm::get_param(fcx.llfn, idx + 1); + idx += 2; + let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); + build::Store(bcx, lldata, expr::get_dataptr(bcx, lltemp)); + build::Store(bcx, llextra, expr::get_dataptr(bcx, lltemp)); + lltemp + } else { + // otherwise, arg is passed by value, so make a + // temporary and store it there + let llarg = llvm::get_param(fcx.llfn, idx); + idx += 1; + let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index)); + build::Store(bcx, llarg, lltemp); + lltemp + }; + LvalueRef::new(llval, LvalueTy::from_ty(arg_ty)) + }) + .collect() +} + +mod block; +mod constant; +mod lvalue; +mod rvalue; +mod operand; +mod statement; + diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs new file mode 100644 index 0000000000000..786b84ae80759 --- /dev/null +++ b/src/librustc_trans/trans/mir/operand.rs @@ -0,0 +1,90 @@ +// Copyright 2012-2014 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 llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_mir::repr as mir; +use trans::base; +use trans::build; +use trans::common::Block; +use trans::datum; + +use super::MirContext; + +pub struct OperandRef<'tcx> { + // This will be "indirect" if `appropriate_rvalue_mode` returns + // ByRef, and otherwise ByValue. + pub llval: ValueRef, + + // The type of value being returned. + pub ty: Ty<'tcx> +} + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_operand(&mut self, + bcx: Block<'bcx, 'tcx>, + operand: &mir::Operand<'tcx>) + -> OperandRef<'tcx> + { + debug!("trans_operand(operand={:?})", operand); + + match *operand { + mir::Operand::Consume(ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + debug!("trans_operand: tr_lvalue={} @ {:?}", + bcx.val_to_string(tr_lvalue.llval), + ty); + let llval = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) { + datum::ByValue => build::Load(bcx, tr_lvalue.llval), + datum::ByRef => tr_lvalue.llval, + }; + OperandRef { + llval: llval, + ty: ty + } + } + + mir::Operand::Constant(ref constant) => { + let llval = self.trans_constant(bcx, constant); + let ty = bcx.monomorphize(&constant.ty); + OperandRef { + llval: llval, + ty: ty, + } + } + } + } + + pub fn trans_operand_into(&mut self, + bcx: Block<'bcx, 'tcx>, + lldest: ValueRef, + operand: &mir::Operand<'tcx>) + { + debug!("trans_operand_into(lldest={}, operand={:?})", + bcx.val_to_string(lldest), + operand); + + match *operand { + mir::Operand::Consume(ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let lvalue_ty = tr_lvalue.ty.to_ty(bcx.tcx()); + debug!("trans_operand_into: tr_lvalue={} @ {:?}", + bcx.val_to_string(tr_lvalue.llval), + lvalue_ty); + base::memcpy_ty(bcx, lldest, tr_lvalue.llval, lvalue_ty); + } + + mir::Operand::Constant(..) => { + unimplemented!() + } + } + } +} diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs new file mode 100644 index 0000000000000..416aa061276fe --- /dev/null +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -0,0 +1,238 @@ +// Copyright 2012-2014 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 back::abi; +use llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc_front::hir; +use rustc_mir::repr as mir; + +use trans::asm; +use trans::base; +use trans::build; +use trans::common::{self, Block, Result}; +use trans::debuginfo::DebugLoc; +use trans::declare; +use trans::machine; +use trans::type_::Type; +use trans::type_of; +use trans::tvec; + +use super::MirContext; +use super::operand::OperandRef; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_rvalue(&mut self, + bcx: Block<'bcx, 'tcx>, + lldest: ValueRef, + rvalue: &mir::Rvalue<'tcx>) + -> Block<'bcx, 'tcx> + { + debug!("trans_rvalue(lldest={}, rvalue={:?})", + bcx.val_to_string(lldest), + rvalue); + + match *rvalue { + mir::Rvalue::Use(ref operand) => { + self.trans_operand_into(bcx, lldest, operand); + bcx + } + + mir::Rvalue::Cast(..) => { + unimplemented!() + } + + mir::Rvalue::Repeat(..) => { + unimplemented!() + } + + mir::Rvalue::Ref(_, _, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + // Note: lvalues are indirect, so storing the `llval` into the + // destination effectively creates a reference. + build::Store(bcx, tr_lvalue.llval, lldest); + bcx + } + + mir::Rvalue::Len(ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let (_, lllen) = tvec::get_base_and_len(bcx, + tr_lvalue.llval, + tr_lvalue.ty.to_ty(bcx.tcx())); + build::Store(bcx, lllen, lldest); + bcx + } + + mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { + let lhs = self.trans_operand(bcx, lhs); + let rhs = self.trans_operand(bcx, rhs); + let is_float = lhs.ty.is_fp(); + let is_signed = lhs.ty.is_signed(); + let binop_debug_loc = DebugLoc::None; + let llval = match op { + mir::BinOp::Add => if is_float { + build::FAdd(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Add(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Sub => if is_float { + build::FSub(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Sub(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Mul => if is_float { + build::FMul(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::Mul(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Div => if is_float { + build::FDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else if is_signed { + build::SDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::UDiv(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::Rem => if is_float { + // LLVM currently always lowers the `frem` instructions appropriate + // library calls typically found in libm. Notably f64 gets wired up + // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for + // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's + // instead just an inline function in a header that goes up to a + // f64, uses `fmod`, and then comes back down to a f32. + // + // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will + // still unconditionally lower frem instructions over 32-bit floats + // to a call to `fmodf`. To work around this we special case MSVC + // 32-bit float rem instructions and instead do the call out to + // `fmod` ourselves. + // + // Note that this is currently duplicated with src/libcore/ops.rs + // which does the same thing, and it would be nice to perhaps unify + // these two implementations on day! Also note that we call `fmod` + // for both 32 and 64-bit floats because if we emit any FRem + // instruction at all then LLVM is capable of optimizing it into a + // 32-bit FRem (which we're trying to avoid). + let tcx = bcx.tcx(); + let use_fmod = tcx.sess.target.target.options.is_like_msvc && + tcx.sess.target.target.arch == "x86"; + if use_fmod { + let f64t = Type::f64(bcx.ccx()); + let fty = Type::func(&[f64t, f64t], &f64t); + let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty, + tcx.types.f64); + if lhs.ty == tcx.types.f32 { + let lllhs = build::FPExt(bcx, lhs.llval, f64t); + let llrhs = build::FPExt(bcx, rhs.llval, f64t); + let llres = build::Call(bcx, llfn, &[lllhs, llrhs], + None, binop_debug_loc); + build::FPTrunc(bcx, llres, Type::f32(bcx.ccx())) + } else { + build::Call(bcx, llfn, &[lhs.llval, rhs.llval], + None, binop_debug_loc) + } + } else { + build::FRem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } + } else if is_signed { + build::SRem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + } else { + build::URem(bcx, lhs.llval, rhs.llval, binop_debug_loc) + }, + mir::BinOp::BitOr => build::Or(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::BitAnd => build::And(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::BitXor => build::Xor(bcx, lhs.llval, rhs.llval, binop_debug_loc), + mir::BinOp::Shl => common::build_unchecked_lshift(bcx, + lhs.llval, + rhs.llval, + binop_debug_loc), + mir::BinOp::Shr => common::build_unchecked_rshift(bcx, + lhs.ty, + lhs.llval, + rhs.llval, + binop_debug_loc), + mir::BinOp::Eq => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiEq, binop_debug_loc), + mir::BinOp::Lt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiLt, binop_debug_loc), + mir::BinOp::Le => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiLe, binop_debug_loc), + mir::BinOp::Ne => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiNe, binop_debug_loc), + mir::BinOp::Ge => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiGe, binop_debug_loc), + mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, + hir::BiGt, binop_debug_loc), + }; + build::Store(bcx, llval, lldest); + bcx + } + + mir::Rvalue::UnaryOp(op, ref operand) => { + let operand = self.trans_operand(bcx, operand); + let is_float = operand.ty.is_fp(); + let debug_loc = DebugLoc::None; + let llval = match op { + mir::UnOp::Not => build::Not(bcx, operand.llval, debug_loc), + mir::UnOp::Neg => if is_float { + build::FNeg(bcx, operand.llval, debug_loc) + } else { + build::Neg(bcx, operand.llval, debug_loc) + } + }; + build::Store(bcx, llval, lldest); + bcx + } + + mir::Rvalue::Box(content_ty) => { + let content_ty: Ty<'tcx> = content_ty; + let llty = type_of::type_of(bcx.ccx(), content_ty); + let llsize = machine::llsize_of(bcx.ccx(), llty); + let align = type_of::align_of(bcx.ccx(), content_ty); + let llalign = common::C_uint(bcx.ccx(), align); + let llty_ptr = llty.ptr_to(); + let box_ty = bcx.tcx().mk_box(content_ty); + let Result { bcx, val: llval } = base::malloc_raw_dyn(bcx, + llty_ptr, + box_ty, + llsize, + llalign, + DebugLoc::None); + build::Store(bcx, llval, lldest); + bcx + } + + mir::Rvalue::Aggregate(_, ref operands) => { + for (i, operand) in operands.iter().enumerate() { + let lldest_i = build::GEPi(bcx, lldest, &[0, i]); + self.trans_operand_into(bcx, lldest_i, operand); + } + bcx + } + + mir::Rvalue::Slice { ref input, from_start, from_end } => { + let ccx = bcx.ccx(); + let input = self.trans_lvalue(bcx, input); + let (llbase, lllen) = tvec::get_base_and_len(bcx, + input.llval, + input.ty.to_ty(bcx.tcx())); + let llbase1 = build::GEPi(bcx, llbase, &[from_start]); + let adj = common::C_uint(ccx, from_start + from_end); + let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None); + build::Store(bcx, llbase1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_ADDR])); + build::Store(bcx, lllen1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_EXTRA])); + bcx + } + + mir::Rvalue::InlineAsm(inline_asm) => { + asm::trans_inline_asm(bcx, inline_asm) + } + } + } +} diff --git a/src/librustc_trans/trans/mir/statement.rs b/src/librustc_trans/trans/mir/statement.rs new file mode 100644 index 0000000000000..17a20fb817c89 --- /dev/null +++ b/src/librustc_trans/trans/mir/statement.rs @@ -0,0 +1,48 @@ +// Copyright 2012-2014 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::middle::ty::LvaluePreference; +use rustc_mir::repr as mir; +use trans::common::Block; +use trans::debuginfo::DebugLoc; +use trans::glue; + +use super::MirContext; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_statement(&mut self, + bcx: Block<'bcx, 'tcx>, + statement: &mir::Statement<'tcx>) + -> Block<'bcx, 'tcx> { + debug!("trans_statement(statement={:?})", statement); + + match statement.kind { + mir::StatementKind::Assign(ref lvalue, ref rvalue) => { + let tr_dest = self.trans_lvalue(bcx, lvalue); + self.trans_rvalue(bcx, tr_dest.llval, rvalue); + bcx + } + + mir::StatementKind::Drop(mir::DropKind::Deep, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + glue::drop_ty(bcx, tr_lvalue.llval, ty, DebugLoc::None) + } + + mir::StatementKind::Drop(mir::DropKind::Free, ref lvalue) => { + let tr_lvalue = self.trans_lvalue(bcx, lvalue); + let ty = tr_lvalue.ty.to_ty(bcx.tcx()); + let content_ty = ty.builtin_deref(true, LvaluePreference::NoPreference); + let content_ty = content_ty.unwrap().ty; + glue::trans_exchange_free_ty(bcx, tr_lvalue.llval, content_ty, DebugLoc::None) + } + } + } +} diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index 04854501312bf..fa37b00553982 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -52,6 +52,7 @@ mod llrepr; mod machine; mod _match; mod meth; +mod mir; mod monomorphize; mod tvec; mod type_; diff --git a/src/test/run-pass/mir_trans_spike1.rs b/src/test/run-pass/mir_trans_spike1.rs new file mode 100644 index 0000000000000..9a06ab78e73b4 --- /dev/null +++ b/src/test/run-pass/mir_trans_spike1.rs @@ -0,0 +1,24 @@ +// Copyright 2012 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. + +// A simple spike test for MIR version of trans. + +#![feature(rustc_attrs)] + +#[rustc_mir] +fn sum(x: i32, y: i32) -> i32 { + x + y +} + +fn main() { + let x = sum(22, 44); + assert_eq!(x, 66); + println!("sum()={:?}", x); +} From e84829d51d9aa8e2623dabd4dcd4935d22008cb5 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 2 Nov 2015 09:39:59 -0500 Subject: [PATCH 13/18] Plumbing to omit allocas for temps when possible (currently unused) --- src/librustc_trans/lib.rs | 1 + src/librustc_trans/trans/mir/lvalue.rs | 9 +- src/librustc_trans/trans/mir/mod.rs | 38 +++++- src/librustc_trans/trans/mir/rvalue.rs | 134 ++++++++++++++++------ src/librustc_trans/trans/mir/statement.rs | 28 ++++- 5 files changed, 166 insertions(+), 44 deletions(-) diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index bc5b6f6b9d7d9..84ce458ed14f7 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -51,6 +51,7 @@ extern crate graphviz; extern crate libc; extern crate rustc; extern crate rustc_back; +extern crate rustc_data_structures; extern crate rustc_front; extern crate rustc_llvm as llvm; extern crate rustc_mir; diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index 282c6a7e6de17..1ce7b55a9c686 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -20,7 +20,7 @@ use trans::debuginfo::DebugLoc; use trans::machine; use trans::tvec; -use super::MirContext; +use super::{MirContext, TempRef}; #[derive(Copy, Clone)] pub struct LvalueRef<'tcx> { @@ -58,7 +58,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let tcx = bcx.tcx(); match *lvalue { mir::Lvalue::Var(index) => self.vars[index as usize], - mir::Lvalue::Temp(index) => self.temps[index as usize], + mir::Lvalue::Temp(index) => match self.temps[index as usize] { + TempRef::Lvalue(lvalue) => + lvalue, + TempRef::Operand(..) => + tcx.sess.bug(&format!("using operand temp {:?} as lvalue", lvalue)), + }, mir::Lvalue::Arg(index) => self.args[index as usize], mir::Lvalue::Static(_def_id) => unimplemented!(), mir::Lvalue::ReturnPointer => { diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 6ed839d1a442d..760018b4313c8 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -10,9 +10,9 @@ use libc::c_uint; use llvm::{self, ValueRef}; +use rustc_data_structures::fnv::FnvHashSet; use rustc_mir::repr as mir; use rustc_mir::tcx::LvalueTy; -use std::cell::Cell; use trans::base; use trans::build; use trans::common::{self, Block}; @@ -21,6 +21,7 @@ use trans::expr; use trans::type_of; use self::lvalue::LvalueRef; +use self::operand::OperandRef; // FIXME DebugLoc is always None right now @@ -43,8 +44,19 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// An LLVM alloca for each MIR `VarDecl` vars: Vec>, - /// An LLVM alloca for each MIR `TempDecl` - temps: Vec>, + /// The location where each MIR `TempDecl` is stored. This is + /// usually an `LvalueRef` representing an alloca, but not always: + /// sometimes we can skip the alloca and just store the value + /// directly using an `OperandRef`, which makes for tighter LLVM + /// IR. The conditions for using an `OperandRef` are as follows: + /// + /// - the type of the temporary must be judged "immediate" by `type_is_immediate` + /// - the operand must never be referenced indirectly + /// - we should not take its address using the `&` operator + /// - nor should it appear in an lvalue path like `tmp.a` + /// - the operand must be defined by an rvalue that can generate immediate + /// values + temps: Vec>, /// The arguments to the function; as args are lvalues, these are /// always indirect, though we try to avoid creating an alloca @@ -52,6 +64,11 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { args: Vec>, } +enum TempRef<'tcx> { + Lvalue(LvalueRef<'tcx>), + Operand(Option>), +} + /////////////////////////////////////////////////////////////////////////// pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { @@ -60,6 +77,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { let mir_blocks = bcx.mir().all_basic_blocks(); + // Analyze the temps to determine which must be lvalues + // FIXME + let lvalue_temps: FnvHashSet = (0..mir.temp_decls.len()).collect(); + // Allocate variable and temp allocas let vars = mir.var_decls.iter() .map(|decl| (bcx.monomorphize(&decl.ty), decl.name)) @@ -68,7 +89,16 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { let temps = mir.temp_decls.iter() .map(|decl| bcx.monomorphize(&decl.ty)) .enumerate() - .map(|(i, mty)| LvalueRef::alloca(bcx, mty, &format!("temp{:?}", i))) + .map(|(i, mty)| if lvalue_temps.contains(&i) { + TempRef::Lvalue(LvalueRef::alloca(bcx, + mty, + &format!("temp{:?}", i))) + } else { + // If this is an immediate temp, we do not create an + // alloca in advance. Instead we wait until we see the + // definition and update the operand there. + TempRef::Operand(None) + }) .collect(); let args = arg_value_refs(bcx, mir); diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 416aa061276fe..c82726fd0e577 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -53,12 +53,87 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { unimplemented!() } + mir::Rvalue::Aggregate(_, ref operands) => { + for (i, operand) in operands.iter().enumerate() { + let lldest_i = build::GEPi(bcx, lldest, &[0, i]); + self.trans_operand_into(bcx, lldest_i, operand); + } + bcx + } + + mir::Rvalue::Slice { ref input, from_start, from_end } => { + let ccx = bcx.ccx(); + let input = self.trans_lvalue(bcx, input); + let (llbase, lllen) = tvec::get_base_and_len(bcx, + input.llval, + input.ty.to_ty(bcx.tcx())); + let llbase1 = build::GEPi(bcx, llbase, &[from_start]); + let adj = common::C_uint(ccx, from_start + from_end); + let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None); + build::Store(bcx, llbase1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_ADDR])); + build::Store(bcx, lllen1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_EXTRA])); + bcx + } + + mir::Rvalue::InlineAsm(inline_asm) => { + asm::trans_inline_asm(bcx, inline_asm) + } + + _ => { + assert!(self.rvalue_creates_operand(rvalue)); + let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); + build::Store(bcx, temp.llval, lldest); + bcx + } + } + } + + pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { + match *rvalue { + mir::Rvalue::Use(..) | // (*) + mir::Rvalue::Ref(..) | + mir::Rvalue::Len(..) | + mir::Rvalue::Cast(..) | // (*) + mir::Rvalue::BinaryOp(..) | + mir::Rvalue::UnaryOp(..) | + mir::Rvalue::Box(..) => + true, + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) | + mir::Rvalue::Slice { .. } | + mir::Rvalue::InlineAsm(..) => + false, + } + + // (*) this is only true if the type is suitable + } + + pub fn trans_rvalue_operand(&mut self, + bcx: Block<'bcx, 'tcx>, + rvalue: &mir::Rvalue<'tcx>) + -> (Block<'bcx, 'tcx>, OperandRef<'tcx>) + { + assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); + + match *rvalue { + mir::Rvalue::Use(ref operand) => { + let operand = self.trans_operand(bcx, operand); + (bcx, operand) + } + + mir::Rvalue::Cast(..) => { + unimplemented!() + } + mir::Rvalue::Ref(_, _, ref lvalue) => { let tr_lvalue = self.trans_lvalue(bcx, lvalue); + // Note: lvalues are indirect, so storing the `llval` into the // destination effectively creates a reference. - build::Store(bcx, tr_lvalue.llval, lldest); - bcx + (bcx, OperandRef { + llval: tr_lvalue.llval, + ty: tr_lvalue.ty.to_ty(bcx.tcx()), + }) } mir::Rvalue::Len(ref lvalue) => { @@ -66,8 +141,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let (_, lllen) = tvec::get_base_and_len(bcx, tr_lvalue.llval, tr_lvalue.ty.to_ty(bcx.tcx())); - build::Store(bcx, lllen, lldest); - bcx + (bcx, OperandRef { + llval: lllen, + ty: bcx.tcx().types.usize, + }) } mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { @@ -170,8 +247,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::BinOp::Gt => base::compare_scalar_types(bcx, lhs.llval, rhs.llval, lhs.ty, hir::BiGt, binop_debug_loc), }; - build::Store(bcx, llval, lldest); - bcx + (bcx, OperandRef { + llval: llval, + ty: lhs.ty, + }) } mir::Rvalue::UnaryOp(op, ref operand) => { @@ -186,12 +265,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { build::Neg(bcx, operand.llval, debug_loc) } }; - build::Store(bcx, llval, lldest); - bcx + (bcx, OperandRef { + llval: llval, + ty: operand.ty, + }) } mir::Rvalue::Box(content_ty) => { - let content_ty: Ty<'tcx> = content_ty; + let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty); let llty = type_of::type_of(bcx.ccx(), content_ty); let llsize = machine::llsize_of(bcx.ccx(), llty); let align = type_of::align_of(bcx.ccx(), content_ty); @@ -204,34 +285,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { llsize, llalign, DebugLoc::None); - build::Store(bcx, llval, lldest); - bcx - } - - mir::Rvalue::Aggregate(_, ref operands) => { - for (i, operand) in operands.iter().enumerate() { - let lldest_i = build::GEPi(bcx, lldest, &[0, i]); - self.trans_operand_into(bcx, lldest_i, operand); - } - bcx + (bcx, OperandRef { + llval: llval, + ty: box_ty, + }) } - mir::Rvalue::Slice { ref input, from_start, from_end } => { - let ccx = bcx.ccx(); - let input = self.trans_lvalue(bcx, input); - let (llbase, lllen) = tvec::get_base_and_len(bcx, - input.llval, - input.ty.to_ty(bcx.tcx())); - let llbase1 = build::GEPi(bcx, llbase, &[from_start]); - let adj = common::C_uint(ccx, from_start + from_end); - let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None); - build::Store(bcx, llbase1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_ADDR])); - build::Store(bcx, lllen1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_EXTRA])); - bcx - } - - mir::Rvalue::InlineAsm(inline_asm) => { - asm::trans_inline_asm(bcx, inline_asm) + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) | + mir::Rvalue::Slice { .. } | + mir::Rvalue::InlineAsm(..) => { + bcx.tcx().sess.bug(&format!("cannot generate operand from rvalue {:?}", rvalue)); } } } diff --git a/src/librustc_trans/trans/mir/statement.rs b/src/librustc_trans/trans/mir/statement.rs index 17a20fb817c89..95ff049836eb4 100644 --- a/src/librustc_trans/trans/mir/statement.rs +++ b/src/librustc_trans/trans/mir/statement.rs @@ -15,6 +15,7 @@ use trans::debuginfo::DebugLoc; use trans::glue; use super::MirContext; +use super::TempRef; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_statement(&mut self, @@ -25,9 +26,30 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match statement.kind { mir::StatementKind::Assign(ref lvalue, ref rvalue) => { - let tr_dest = self.trans_lvalue(bcx, lvalue); - self.trans_rvalue(bcx, tr_dest.llval, rvalue); - bcx + match *lvalue { + mir::Lvalue::Temp(index) => { + let index = index as usize; + match self.temps[index as usize] { + TempRef::Lvalue(tr_dest) => { + self.trans_rvalue(bcx, tr_dest.llval, rvalue) + } + TempRef::Operand(None) => { + let (bcx, operand) = self.trans_rvalue_operand(bcx, rvalue); + self.temps[index] = TempRef::Operand(Some(operand)); + bcx + } + TempRef::Operand(Some(_)) => { + bcx.tcx().sess.span_bug( + statement.span, + &format!("operand {:?} already assigned", rvalue)); + } + } + } + _ => { + let tr_dest = self.trans_lvalue(bcx, lvalue); + self.trans_rvalue(bcx, tr_dest.llval, rvalue) + } + } } mir::StatementKind::Drop(mir::DropKind::Deep, ref lvalue) => { From 544b06d455a067ab54a850e34bdc7794f23c6734 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Nov 2015 06:33:59 -0500 Subject: [PATCH 14/18] Add a MIR visitor --- src/librustc_mir/lib.rs | 1 + src/librustc_mir/visit.rs | 239 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) create mode 100644 src/librustc_mir/visit.rs diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 5c52dfe2bd673..ed5df21d91193 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -35,4 +35,5 @@ mod hair; pub mod repr; mod graphviz; pub mod tcx; +pub mod visit; diff --git a/src/librustc_mir/visit.rs b/src/librustc_mir/visit.rs new file mode 100644 index 0000000000000..b4d6075d0adb7 --- /dev/null +++ b/src/librustc_mir/visit.rs @@ -0,0 +1,239 @@ +// Copyright 2012-2014 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::middle::ty::Region; +use repr::*; + +pub trait Visitor<'tcx> { + // Override these, and call `self.super_xxx` to revert back to the + // default behavior. + + fn visit_mir(&mut self, mir: &Mir<'tcx>) { + self.super_mir(mir); + } + + fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { + self.super_basic_block_data(block, data); + } + + fn visit_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { + self.super_statement(block, statement); + } + + fn visit_assign(&mut self, block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { + self.super_assign(block, lvalue, rvalue); + } + + fn visit_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { + self.super_terminator(block, terminator); + } + + fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { + self.super_rvalue(rvalue); + } + + fn visit_operand(&mut self, operand: &Operand<'tcx>) { + self.super_operand(operand); + } + + fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: LvalueContext) { + self.super_lvalue(lvalue, context); + } + + fn visit_branch(&mut self, source: BasicBlock, target: BasicBlock) { + self.super_branch(source, target); + } + + fn visit_constant(&mut self, constant: &Constant<'tcx>) { + self.super_constant(constant); + } + + // The `super_xxx` methods comprise the default behavior and are + // not meant to be overidden. + + fn super_mir(&mut self, mir: &Mir<'tcx>) { + for block in mir.all_basic_blocks() { + let data = mir.basic_block_data(block); + self.visit_basic_block_data(block, data); + } + } + + fn super_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) { + for statement in &data.statements { + self.visit_statement(block, statement); + } + self.visit_terminator(block, &data.terminator); + } + + fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { + match statement.kind { + StatementKind::Assign(ref lvalue, ref rvalue) => { + self.visit_assign(block, lvalue, rvalue); + } + StatementKind::Drop(_, ref lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Drop); + } + } + } + + fn super_assign(&mut self, _block: BasicBlock, lvalue: &Lvalue<'tcx>, rvalue: &Rvalue<'tcx>) { + self.visit_lvalue(lvalue, LvalueContext::Store); + self.visit_rvalue(rvalue); + } + + fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { + match *terminator { + Terminator::Goto { target } | + Terminator::Panic { target } => { + self.visit_branch(block, target); + } + + Terminator::If { ref cond, ref targets } => { + self.visit_operand(cond); + for &target in &targets[..] { + self.visit_branch(block, target); + } + } + + Terminator::Switch { ref discr, adt_def: _, ref targets } => { + self.visit_lvalue(discr, LvalueContext::Inspect); + for &target in targets { + self.visit_branch(block, target); + } + } + + Terminator::Diverge | + Terminator::Return => { + } + + Terminator::Call { ref data, ref targets } => { + self.visit_lvalue(&data.destination, LvalueContext::Store); + self.visit_operand(&data.func); + for arg in &data.args { + self.visit_operand(arg); + } + for &target in &targets[..] { + self.visit_branch(block, target); + } + } + } + } + + fn super_rvalue(&mut self, rvalue: &Rvalue<'tcx>) { + match *rvalue { + Rvalue::Use(ref operand) => { + self.visit_operand(operand); + } + + Rvalue::Repeat(ref value, ref len) => { + self.visit_operand(value); + self.visit_operand(len); + } + + Rvalue::Ref(r, bk, ref path) => { + self.visit_lvalue(path, LvalueContext::Borrow { + region: r, + kind: bk + }); + } + + Rvalue::Len(ref path) => { + self.visit_lvalue(path, LvalueContext::Inspect); + } + + Rvalue::Cast(_, ref operand, _) => { + self.visit_operand(operand); + } + + Rvalue::BinaryOp(_, ref lhs, ref rhs) => { + self.visit_operand(lhs); + self.visit_operand(rhs); + } + + Rvalue::UnaryOp(_, ref op) => { + self.visit_operand(op); + } + + Rvalue::Box(_) => { + } + + Rvalue::Aggregate(_, ref operands) => { + for operand in operands { + self.visit_operand(operand); + } + } + + Rvalue::Slice { ref input, from_start, from_end } => { + self.visit_lvalue(input, LvalueContext::Slice { + from_start: from_start, + from_end: from_end, + }); + } + + Rvalue::InlineAsm(_) => { + } + } + } + + fn super_operand(&mut self, operand: &Operand<'tcx>) { + match *operand { + Operand::Consume(ref lvalue) => { + self.visit_lvalue(lvalue, LvalueContext::Consume); + } + Operand::Constant(ref constant) => { + self.visit_constant(constant); + } + } + } + + fn super_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: LvalueContext) { + match *lvalue { + Lvalue::Var(_) | + Lvalue::Temp(_) | + Lvalue::Arg(_) | + Lvalue::Static(_) | + Lvalue::ReturnPointer => { + } + Lvalue::Projection(ref proj) => { + self.visit_lvalue(&proj.base, LvalueContext::Projection); + } + } + } + + fn super_branch(&mut self, _source: BasicBlock, _target: BasicBlock) { + } + + fn super_constant(&mut self, _constant: &Constant<'tcx>) { + } +} + +#[derive(Copy, Clone, Debug)] +pub enum LvalueContext { + // Appears as LHS of an assignment or as dest of a call + Store, + + // Being dropped + Drop, + + // Being inspected in some way, like loading a len + Inspect, + + // Being borrowed + Borrow { region: Region, kind: BorrowKind }, + + // Being sliced -- this should be same as being borrowed, probably + Slice { from_start: usize, from_end: usize }, + + // Used as base for another lvalue, e.g. `x` in `x.y` + Projection, + + // Consumed as part of an operand + Consume, +} From 6a5b263503dce838c2a09619171b409c9f11bb9f Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Nov 2015 06:35:09 -0500 Subject: [PATCH 15/18] Add (and use) an analysis to determine which temps can forgo an alloca. --- src/librustc_trans/trans/mir/analyze.rs | 115 ++++++++++++++++++++++++ src/librustc_trans/trans/mir/mod.rs | 4 +- src/librustc_trans/trans/mir/operand.rs | 22 ++++- src/librustc_trans/trans/mir/rvalue.rs | 44 ++++----- 4 files changed, 160 insertions(+), 25 deletions(-) create mode 100644 src/librustc_trans/trans/mir/analyze.rs diff --git a/src/librustc_trans/trans/mir/analyze.rs b/src/librustc_trans/trans/mir/analyze.rs new file mode 100644 index 0000000000000..acf6e53468ef5 --- /dev/null +++ b/src/librustc_trans/trans/mir/analyze.rs @@ -0,0 +1,115 @@ +// Copyright 2012-2014 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. + +//! An analysis to determine which temporaries require allocas and +//! which do not. + +use rustc_data_structures::fnv::FnvHashSet; +use rustc_mir::repr as mir; +use rustc_mir::visit::{Visitor, LvalueContext}; +use trans::common::{self, Block}; +use super::rvalue; + +pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, + mir: &mir::Mir<'tcx>) + -> FnvHashSet { + let mut analyzer = TempAnalyzer::new(); + + analyzer.visit_mir(mir); + + for (index, temp_decl) in mir.temp_decls.iter().enumerate() { + let ty = bcx.monomorphize(&temp_decl.ty); + debug!("temp {:?} has type {:?}", index, ty); + if + ty.is_scalar() || + ty.is_unique() || + ty.is_region_ptr() || + ty.is_simd() + { + // These sorts of types are immediates that we can store + // in an ValueRef without an alloca. + assert!(common::type_is_immediate(bcx.ccx(), ty)); + } else { + // These sorts of types require an alloca. Note that + // type_is_immediate() may *still* be true, particularly + // for newtypes, but we currently force some types + // (e.g. structs) into an alloca unconditionally, just so + // that we don't have to deal with having two pathways + // (gep vs getvalue etc). + analyzer.mark_as_lvalue(index); + } + } + + analyzer.lvalue_temps +} + +struct TempAnalyzer { + lvalue_temps: FnvHashSet, +} + +impl TempAnalyzer { + fn new() -> TempAnalyzer { + TempAnalyzer { lvalue_temps: FnvHashSet() } + } + + fn mark_as_lvalue(&mut self, temp: usize) { + debug!("marking temp {} as lvalue", temp); + self.lvalue_temps.insert(temp); + } +} + +impl<'tcx> Visitor<'tcx> for TempAnalyzer { + fn visit_assign(&mut self, + block: mir::BasicBlock, + lvalue: &mir::Lvalue<'tcx>, + rvalue: &mir::Rvalue<'tcx>) { + debug!("visit_assign(block={:?}, lvalue={:?}, rvalue={:?})", block, lvalue, rvalue); + + match *lvalue { + mir::Lvalue::Temp(index) => { + if !rvalue::rvalue_creates_operand(rvalue) { + self.mark_as_lvalue(index as usize); + } + } + _ => { + self.visit_lvalue(lvalue, LvalueContext::Store); + } + } + + self.visit_rvalue(rvalue); + } + + fn visit_lvalue(&mut self, + lvalue: &mir::Lvalue<'tcx>, + context: LvalueContext) { + debug!("visit_lvalue(lvalue={:?}, context={:?})", lvalue, context); + + match *lvalue { + mir::Lvalue::Temp(index) => { + match context { + LvalueContext::Consume => { + } + LvalueContext::Store | + LvalueContext::Drop | + LvalueContext::Inspect | + LvalueContext::Borrow { .. } | + LvalueContext::Slice { .. } | + LvalueContext::Projection => { + self.mark_as_lvalue(index as usize); + } + } + } + _ => { + } + } + + self.super_lvalue(lvalue, context); + } +} diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 760018b4313c8..353ac2e5f79ca 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -10,7 +10,6 @@ use libc::c_uint; use llvm::{self, ValueRef}; -use rustc_data_structures::fnv::FnvHashSet; use rustc_mir::repr as mir; use rustc_mir::tcx::LvalueTy; use trans::base; @@ -79,7 +78,7 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { // Analyze the temps to determine which must be lvalues // FIXME - let lvalue_temps: FnvHashSet = (0..mir.temp_decls.len()).collect(); + let lvalue_temps = analyze::lvalue_temps(bcx, mir); // Allocate variable and temp allocas let vars = mir.var_decls.iter() @@ -183,6 +182,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>, .collect() } +mod analyze; mod block; mod constant; mod lvalue; diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs index 786b84ae80759..a0308032ac07b 100644 --- a/src/librustc_trans/trans/mir/operand.rs +++ b/src/librustc_trans/trans/mir/operand.rs @@ -16,8 +16,9 @@ use trans::build; use trans::common::Block; use trans::datum; -use super::MirContext; +use super::{MirContext, TempRef}; +#[derive(Copy, Clone)] pub struct OperandRef<'tcx> { // This will be "indirect" if `appropriate_rvalue_mode` returns // ByRef, and otherwise ByValue. @@ -37,6 +38,25 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match *operand { mir::Operand::Consume(ref lvalue) => { + // watch out for temporaries that do not have an + // alloca; they are handled somewhat differently + if let &mir::Lvalue::Temp(index) = lvalue { + match self.temps[index as usize] { + TempRef::Operand(Some(o)) => { + return o; + } + TempRef::Operand(None) => { + bcx.tcx().sess.bug( + &format!("use of {:?} before def", lvalue)); + } + TempRef::Lvalue(..) => { + // use path below + } + } + } + + // for most lvalues, to consume them we just load them + // out from their home let tr_lvalue = self.trans_lvalue(bcx, lvalue); let ty = tr_lvalue.ty.to_ty(bcx.tcx()); debug!("trans_operand: tr_lvalue={} @ {:?}", diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index c82726fd0e577..7c0912592b6fd 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -80,7 +80,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } _ => { - assert!(self.rvalue_creates_operand(rvalue)); + assert!(rvalue_creates_operand(rvalue)); let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue); build::Store(bcx, temp.llval, lldest); bcx @@ -88,32 +88,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { - match *rvalue { - mir::Rvalue::Use(..) | // (*) - mir::Rvalue::Ref(..) | - mir::Rvalue::Len(..) | - mir::Rvalue::Cast(..) | // (*) - mir::Rvalue::BinaryOp(..) | - mir::Rvalue::UnaryOp(..) | - mir::Rvalue::Box(..) => - true, - mir::Rvalue::Repeat(..) | - mir::Rvalue::Aggregate(..) | - mir::Rvalue::Slice { .. } | - mir::Rvalue::InlineAsm(..) => - false, - } - - // (*) this is only true if the type is suitable - } - pub fn trans_rvalue_operand(&mut self, bcx: Block<'bcx, 'tcx>, rvalue: &mir::Rvalue<'tcx>) -> (Block<'bcx, 'tcx>, OperandRef<'tcx>) { - assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); + assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); match *rvalue { mir::Rvalue::Use(ref operand) => { @@ -300,3 +280,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } } + +pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { + match *rvalue { + mir::Rvalue::Use(..) | // (*) + mir::Rvalue::Ref(..) | + mir::Rvalue::Len(..) | + mir::Rvalue::Cast(..) | // (*) + mir::Rvalue::BinaryOp(..) | + mir::Rvalue::UnaryOp(..) | + mir::Rvalue::Box(..) => + true, + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) | + mir::Rvalue::Slice { .. } | + mir::Rvalue::InlineAsm(..) => + false, + } + + // (*) this is only true if the type is suitable +} From 9c9f4be9df863e529b09f195f013710e676fb5c1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Nov 2015 06:44:45 -0500 Subject: [PATCH 16/18] correct typos --- src/librustc_trans/trans/mir/mod.rs | 2 +- src/librustc_trans/trans/mir/rvalue.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 353ac2e5f79ca..fb652c6dc7e3e 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -159,7 +159,7 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>, llarg } else if common::type_is_fat_ptr(tcx, arg_ty) { // we pass fat pointers as two words, but we want to - // represent them internally as a pointer two two words, + // represent them internally as a pointer to two words, // so make an alloca to store them in. let lldata = llvm::get_param(fcx.llfn, idx); let llextra = llvm::get_param(fcx.llfn, idx + 1); diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 7c0912592b6fd..dc73c60c9a077 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -172,7 +172,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // // Note that this is currently duplicated with src/libcore/ops.rs // which does the same thing, and it would be nice to perhaps unify - // these two implementations on day! Also note that we call `fmod` + // these two implementations one day! Also note that we call `fmod` // for both 32 and 64-bit floats because if we emit any FRem // instruction at all then LLVM is capable of optimizing it into a // 32-bit FRem (which we're trying to avoid). From b46c0fc49749affc0fc953e8cdc136f5aff5c1b3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Nov 2015 15:50:04 -0500 Subject: [PATCH 17/18] address nits from dotdash --- src/librustc_trans/trans/mir/analyze.rs | 4 ++-- src/librustc_trans/trans/mir/mod.rs | 3 +++ src/librustc_trans/trans/mir/rvalue.rs | 10 ++++++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/librustc_trans/trans/mir/analyze.rs b/src/librustc_trans/trans/mir/analyze.rs index acf6e53468ef5..f5fa897bca631 100644 --- a/src/librustc_trans/trans/mir/analyze.rs +++ b/src/librustc_trans/trans/mir/analyze.rs @@ -30,7 +30,7 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, if ty.is_scalar() || ty.is_unique() || - ty.is_region_ptr() || + (ty.is_region_ptr() && !common::type_is_fat_ptr(bcx.tcx(), ty)) || ty.is_simd() { // These sorts of types are immediates that we can store @@ -42,7 +42,7 @@ pub fn lvalue_temps<'bcx,'tcx>(bcx: Block<'bcx,'tcx>, // for newtypes, but we currently force some types // (e.g. structs) into an alloca unconditionally, just so // that we don't have to deal with having two pathways - // (gep vs getvalue etc). + // (gep vs extractvalue etc). analyzer.mark_as_lvalue(index); } } diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index fb652c6dc7e3e..3b018cc132184 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -55,6 +55,9 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// - nor should it appear in an lvalue path like `tmp.a` /// - the operand must be defined by an rvalue that can generate immediate /// values + /// + /// Avoiding allocs can also be important for certain intrinsics, + /// notably `expect`. temps: Vec>, /// The arguments to the function; as args are lvalues, these are diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index dc73c60c9a077..94cc7857a14f0 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -20,6 +20,7 @@ use trans::build; use trans::common::{self, Block, Result}; use trans::debuginfo::DebugLoc; use trans::declare; +use trans::expr; use trans::machine; use trans::type_::Type; use trans::type_of; @@ -55,6 +56,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { mir::Rvalue::Aggregate(_, ref operands) => { for (i, operand) in operands.iter().enumerate() { + // Note: perhaps this should be StructGep, but + // note that in some cases the values here will + // not be structs but arrays. let lldest_i = build::GEPi(bcx, lldest, &[0, i]); self.trans_operand_into(bcx, lldest_i, operand); } @@ -70,8 +74,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let llbase1 = build::GEPi(bcx, llbase, &[from_start]); let adj = common::C_uint(ccx, from_start + from_end); let lllen1 = build::Sub(bcx, lllen, adj, DebugLoc::None); - build::Store(bcx, llbase1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_ADDR])); - build::Store(bcx, lllen1, build::GEPi(bcx, lldest, &[0, abi::FAT_PTR_EXTRA])); + let lladdrdest = expr::get_dataptr(bcx, lldest); + build::Store(bcx, llbase1, lladdrdest); + let llmetadest = expr::get_meta(bcx, lldest); + build::Store(bcx, lllen1, llmetadest); bcx } From e78786315b44037258970ee9187f3fd6f89fc61b Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 3 Nov 2015 20:38:02 -0500 Subject: [PATCH 18/18] remove unused import --- src/librustc_trans/trans/mir/rvalue.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index 94cc7857a14f0..ad89ee79de40f 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use back::abi; use llvm::ValueRef; use rustc::middle::ty::Ty; use rustc_front::hir;