1
Fork 0

Generate an intermediate temporary for Drop constants.

To limit the fallout from this, don't do this for the last (or only) operand in an rvalue.
This commit is contained in:
Oli Scherer 2022-03-09 17:10:48 +00:00
parent afcd33a6fc
commit db02e61038
8 changed files with 122 additions and 38 deletions

View file

@ -1,7 +1,7 @@
//! See docs in build/expr/mod.rs
use crate::build::expr::category::Category;
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
@ -20,7 +20,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr: &Expr<'tcx>,
) -> BlockAnd<Operand<'tcx>> {
let local_scope = self.local_scope();
self.as_operand(block, Some(local_scope), expr, None)
self.as_operand(block, Some(local_scope), expr, None, NeedsTemporary::Maybe)
}
/// Returns an operand suitable for use until the end of the current scope expression and
@ -94,32 +94,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
///
/// Like `as_local_call_operand`, except that the argument will
/// not be valid once `scope` ends.
#[instrument(level = "debug", skip(self, scope))]
crate fn as_operand(
&mut self,
mut block: BasicBlock,
scope: Option<region::Scope>,
expr: &Expr<'tcx>,
local_info: Option<Box<LocalInfo<'tcx>>>,
needs_temporary: NeedsTemporary,
) -> BlockAnd<Operand<'tcx>> {
debug!("as_operand(block={:?}, expr={:?} local_info={:?})", block, expr, local_info);
let this = self;
if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
let source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info);
return this.in_scope(region_scope, lint_level, |this| {
this.as_operand(block, scope, &this.thir[value], local_info)
this.as_operand(block, scope, &this.thir[value], local_info, needs_temporary)
});
}
let category = Category::of(&expr.kind).unwrap();
debug!("as_operand: category={:?} for={:?}", category, expr.kind);
debug!(?category, ?expr.kind);
match category {
Category::Constant => {
Category::Constant if let NeedsTemporary::No = needs_temporary || !expr.ty.needs_drop(this.tcx, this.param_env) => {
let constant = this.as_constant(expr);
block.and(Operand::Constant(Box::new(constant)))
}
Category::Place | Category::Rvalue(..) => {
Category::Constant | Category::Place | Category::Rvalue(..) => {
let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
if this.local_decls[operand].local_info.is_none() {
this.local_decls[operand].local_info = local_info;
@ -176,6 +177,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
this.as_operand(block, scope, expr, None)
this.as_operand(block, scope, expr, None, NeedsTemporary::Maybe)
}
}

View file

@ -4,7 +4,7 @@ use rustc_index::vec::Idx;
use crate::build::expr::as_place::PlaceBase;
use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::{BlockAnd, BlockAndExtension, Builder};
use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
use rustc_hir::lang_items::LangItem;
use rustc_middle::middle::region;
use rustc_middle::mir::AssertKind;
@ -52,17 +52,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
})
}
ExprKind::Repeat { value, count } => {
let value_operand =
unpack!(block = this.as_operand(block, scope, &this.thir[value], None));
let value_operand = unpack!(
block =
this.as_operand(block, scope, &this.thir[value], None, NeedsTemporary::No)
);
block.and(Rvalue::Repeat(value_operand, count))
}
ExprKind::Binary { op, lhs, rhs } => {
let lhs = unpack!(block = this.as_operand(block, scope, &this.thir[lhs], None));
let rhs = unpack!(block = this.as_operand(block, scope, &this.thir[rhs], None));
let lhs = unpack!(
block =
this.as_operand(block, scope, &this.thir[lhs], None, NeedsTemporary::Maybe)
);
let rhs = unpack!(
block =
this.as_operand(block, scope, &this.thir[rhs], None, NeedsTemporary::No)
);
this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs)
}
ExprKind::Unary { op, arg } => {
let arg = unpack!(block = this.as_operand(block, scope, &this.thir[arg], None));
let arg = unpack!(
block =
this.as_operand(block, scope, &this.thir[arg], None, NeedsTemporary::No)
);
// Check for -MIN on signed integers
if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() {
let bool_ty = this.tcx.types.bool;
@ -167,13 +178,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(Rvalue::Use(Operand::Move(Place::from(result))))
}
ExprKind::Cast { source } => {
let source =
unpack!(block = this.as_operand(block, scope, &this.thir[source], None));
let source = unpack!(
block =
this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No)
);
block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
}
ExprKind::Pointer { cast, source } => {
let source =
unpack!(block = this.as_operand(block, scope, &this.thir[source], None));
let source = unpack!(
block =
this.as_operand(block, scope, &this.thir[source], None, NeedsTemporary::No)
);
block.and(Rvalue::Cast(CastKind::Pointer(cast), source, expr.ty))
}
ExprKind::Array { ref fields } => {
@ -208,7 +223,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fields: Vec<_> = fields
.into_iter()
.copied()
.map(|f| unpack!(block = this.as_operand(block, scope, &this.thir[f], None)))
.map(|f| {
unpack!(
block = this.as_operand(
block,
scope,
&this.thir[f],
None,
NeedsTemporary::Maybe
)
)
})
.collect();
block.and(Rvalue::Aggregate(Box::new(AggregateKind::Array(el_ty)), fields))
@ -219,7 +244,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fields: Vec<_> = fields
.into_iter()
.copied()
.map(|f| unpack!(block = this.as_operand(block, scope, &this.thir[f], None)))
.map(|f| {
unpack!(
block = this.as_operand(
block,
scope,
&this.thir[f],
None,
NeedsTemporary::Maybe
)
)
})
.collect();
block.and(Rvalue::Aggregate(Box::new(AggregateKind::Tuple), fields))
@ -296,7 +331,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
)
),
_ => {
unpack!(block = this.as_operand(block, scope, upvar, None))
unpack!(
block = this.as_operand(
block,
scope,
upvar,
None,
NeedsTemporary::Maybe
)
)
}
}
}
@ -325,13 +368,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
literal: ConstantKind::zero_sized(this.tcx.types.unit),
}))))
}
ExprKind::Yield { .. }
| ExprKind::Literal { .. }
ExprKind::Literal { .. }
| ExprKind::NamedConst { .. }
| ExprKind::NonHirLiteral { .. }
| ExprKind::ConstParam { .. }
| ExprKind::ConstBlock { .. }
| ExprKind::StaticRef { .. }
| ExprKind::StaticRef { .. } => {
let constant = this.as_constant(expr);
block.and(Rvalue::Use(Operand::Constant(Box::new(constant))))
}
ExprKind::Yield { .. }
| ExprKind::Block { .. }
| ExprKind::Match { .. }
| ExprKind::If { .. }
@ -359,9 +407,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// so make an operand and then return that
debug_assert!(!matches!(
Category::of(&expr.kind),
Some(Category::Rvalue(RvalueFunc::AsRvalue))
Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant)
));
let operand = unpack!(block = this.as_operand(block, scope, expr, None));
let operand =
unpack!(block = this.as_operand(block, scope, expr, None, NeedsTemporary::No));
block.and(Rvalue::Use(operand))
}
}

View file

@ -1,7 +1,7 @@
//! See docs in build/expr/mod.rs
use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
use rustc_ast::InlineAsmOptions;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
@ -329,7 +329,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block,
Some(scope),
&this.thir[f.expr],
Some(local_info)
Some(local_info),
NeedsTemporary::Maybe,
)
),
)
@ -516,8 +517,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::Yield { value } => {
let scope = this.local_scope();
let value =
unpack!(block = this.as_operand(block, Some(scope), &this.thir[value], None));
let value = unpack!(
block = this.as_operand(
block,
Some(scope),
&this.thir[value],
None,
NeedsTemporary::No
)
);
let resume = this.cfg.start_new_block();
this.cfg.terminate(
block,

View file

@ -549,6 +549,18 @@ rustc_index::newtype_index! {
struct ScopeId { .. }
}
#[derive(Debug)]
enum NeedsTemporary {
/// Use this variant when whatever you are converting with `as_operand`
/// is the last thing you are converting. This means that if we introduced
/// an intermediate temporary, we'd only read it immediately after, so we can
/// also avoid it.
No,
/// For all cases where you aren't sure or that are too expensive to compute
/// for now. It is always safe to fall back to this.
Maybe,
}
///////////////////////////////////////////////////////////////////////////
/// The `BlockAnd` "monad" packages up the new basic block along with a
/// produced value (sometimes just unit, of course). The `unpack!`

View file

@ -6,6 +6,7 @@
#![feature(box_patterns)]
#![feature(control_flow_enum)]
#![feature(crate_visibility_modifier)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![feature(let_else)]
#![feature(min_specialization)]