From 128c634c7f14ad4a0400c53856e720bfb2cdff36 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 20 Aug 2018 20:08:24 +0200 Subject: [PATCH] also avoid recomputing the layout for unary and binary ops, where possible --- src/librustc_mir/interpret/operand.rs | 12 ++---- src/librustc_mir/interpret/step.rs | 44 +++++++++++++++++--- src/librustc_mir/interpret/terminator/mod.rs | 10 ++--- 3 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 0d7ad1460060..4257c29bfee1 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -497,19 +497,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// We cannot do self.read_value(self.eval_operand) due to eval_operand taking &mut self, /// so this helps avoid unnecessary let. - pub fn eval_operand_and_read_valty( + #[inline] + pub fn eval_operand_and_read_value( &mut self, op: &mir::Operand<'tcx>, + layout: Option>, ) -> EvalResult<'tcx, ValTy<'tcx>> { - let op = self.eval_operand(op, None)?; + let op = self.eval_operand(op, layout)?; self.read_value(op) } - pub fn eval_operand_and_read_scalar( - &mut self, - op: &mir::Operand<'tcx>, - ) -> EvalResult<'tcx, ScalarMaybeUndef> { - Ok(self.eval_operand_and_read_valty(op)?.to_scalar_or_undef()) - } /// reads a tag and produces the corresponding variant index pub fn read_discriminant_as_variant_index( diff --git a/src/librustc_mir/interpret/step.rs b/src/librustc_mir/interpret/step.rs index 56281b6688e3..f39a5ee3e4ef 100644 --- a/src/librustc_mir/interpret/step.rs +++ b/src/librustc_mir/interpret/step.rs @@ -8,6 +8,33 @@ use rustc::mir::interpret::{EvalResult, Scalar}; use super::{EvalContext, Machine}; +/// Classify whether an operator is "left-homogeneous", i.e. the LHS has the +/// same type as the result. +#[inline] +fn binop_left_homogeneous(op: mir::BinOp) -> bool { + use rustc::mir::BinOp::*; + match op { + Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | + Offset | Shl | Shr => + true, + Eq | Ne | Lt | Le | Gt | Ge => + false, + } +} +/// Classify whether an operator is "right-homogeneous", i.e. the RHS has the +/// same type as the LHS. +#[inline] +fn binop_right_homogeneous(op: mir::BinOp) -> bool { + use rustc::mir::BinOp::*; + match op { + Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | + Eq | Ne | Lt | Le | Gt | Ge => + true, + Offset | Shl | Shr => + false, + } +} + impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { pub fn inc_step_counter_and_detect_loops(&mut self) -> EvalResult<'tcx, ()> { /// The number of steps between loop detector snapshots. @@ -147,8 +174,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } BinaryOp(bin_op, ref left, ref right) => { - let left = self.eval_operand_and_read_valty(left)?; - let right = self.eval_operand_and_read_valty(right)?; + let layout = if binop_left_homogeneous(bin_op) { Some(dest.layout) } else { None }; + let left = self.eval_operand_and_read_value(left, layout)?; + let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None }; + let right = self.eval_operand_and_read_value(right, layout)?; self.binop_ignore_overflow( bin_op, left, @@ -158,8 +187,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } CheckedBinaryOp(bin_op, ref left, ref right) => { - let left = self.eval_operand_and_read_valty(left)?; - let right = self.eval_operand_and_read_valty(right)?; + // Due to the extra boolean in the result, we can never reuse the `dest.layout`. + let left = self.eval_operand_and_read_value(left, None)?; + let layout = if binop_right_homogeneous(bin_op) { Some(left.layout) } else { None }; + let right = self.eval_operand_and_read_value(right, layout)?; self.binop_with_overflow( bin_op, left, @@ -169,8 +200,9 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } UnaryOp(un_op, ref operand) => { - let val = self.eval_operand_and_read_scalar(operand)?; - let val = self.unary_op(un_op, val.not_undef()?, dest.layout)?; + // The operand always has the same type as the result. + let val = self.eval_operand_and_read_value(operand, Some(dest.layout))?; + let val = self.unary_op(un_op, val.to_scalar()?, dest.layout)?; self.write_scalar(val, dest)?; } diff --git a/src/librustc_mir/interpret/terminator/mod.rs b/src/librustc_mir/interpret/terminator/mod.rs index c8be3d1fbff2..82455cacac2d 100644 --- a/src/librustc_mir/interpret/terminator/mod.rs +++ b/src/librustc_mir/interpret/terminator/mod.rs @@ -144,18 +144,18 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { target, .. } => { - let cond_val = self.eval_operand_and_read_scalar(cond)?.not_undef()?.to_bool()?; + let cond_val = self.eval_operand_and_read_value(cond, None)?.to_scalar()?.to_bool()?; if expected == cond_val { self.goto_block(target); } else { use rustc::mir::interpret::EvalErrorKind::*; return match *msg { BoundsCheck { ref len, ref index } => { - let len = self.eval_operand_and_read_scalar(len) - .expect("can't eval len") + let len = self.eval_operand_and_read_value(len, None) + .expect("can't eval len").to_scalar()? .to_bits(self.memory().pointer_size())? as u64; - let index = self.eval_operand_and_read_scalar(index) - .expect("can't eval index") + let index = self.eval_operand_and_read_value(index, None) + .expect("can't eval index").to_scalar()? .to_bits(self.memory().pointer_size())? as u64; err!(BoundsCheck { len, index }) }