Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various improvements to MIR and LLVM IR Construction #32980

Merged
merged 9 commits into from
Apr 28, 2016
6 changes: 1 addition & 5 deletions src/librustc_mir/build/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
StmtKind::Expr { scope, expr } => {
unpack!(block = this.in_scope(scope, block, |this, _| {
let expr = this.hir.mirror(expr);
let expr_span = expr.span;
let temp = this.temp(expr.ty.clone());
unpack!(block = this.into(&temp, block, expr));
unpack!(block = this.build_drop(block, expr_span, temp));
block.unit()
this.stmt_expr(block, expr)
}));
}
StmtKind::Let { remainder_scope, init_scope, pattern, initializer } => {
Expand Down
7 changes: 5 additions & 2 deletions src/librustc_mir/build/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs),
fields))
}
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } => {
block = unpack!(this.stmt_expr(block, expr));
block.and(this.unit_rvalue())
}
ExprKind::Literal { .. } |
ExprKind::Block { .. } |
ExprKind::Match { .. } |
Expand All @@ -201,8 +206,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
ExprKind::Index { .. } |
ExprKind::VarRef { .. } |
ExprKind::SelfRef |
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } |
ExprKind::Break { .. } |
ExprKind::Continue { .. } |
ExprKind::Return { .. } |
Expand Down
87 changes: 9 additions & 78 deletions src/librustc_mir/build/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@

use build::{BlockAnd, BlockAndExtension, Builder};
use build::expr::category::{Category, RvalueFunc};
use build::scope::LoopScope;
use hair::*;
use rustc::middle::region::CodeExtent;
use rustc::ty;
use rustc::mir::repr::*;
use syntax::codemap::Span;

impl<'a,'tcx> Builder<'a,'tcx> {
/// Compile `expr`, storing the result into `destination`, which
Expand Down Expand Up @@ -207,65 +204,6 @@ impl<'a,'tcx> Builder<'a,'tcx> {
}
exit_block.unit()
}
ExprKind::Assign { lhs, rhs } => {
// Note: we evaluate assignments right-to-left. This
// is better for borrowck interaction with overloaded
// operators like x[j] = x[i].
let lhs = this.hir.mirror(lhs);
let lhs_span = lhs.span;
let rhs = unpack!(block = this.as_operand(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));
unpack!(block = this.build_drop(block, lhs_span, lhs.clone()));
this.cfg.push_assign(block, scope_id, expr_span, &lhs, Rvalue::Use(rhs));
block.unit()
}
ExprKind::AssignOp { op, lhs, rhs } => {
// FIXME(#28160) there is an interesting semantics
// question raised here -- should we "freeze" the
// value of the lhs here? I'm inclined to think not,
// since it seems closer to the semantics of the
// overloaded version, which takes `&mut self`. This
// only affects weird things like `x += {x += 1; x}`
// -- is that equal to `x + (x + 1)` or `2*(x+1)`?

// As above, RTL.
let rhs = unpack!(block = this.as_operand(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));

// we don't have to drop prior contents or anything
// because AssignOp is only legal for Copy types
// (overloaded ops should be desugared into a call).
this.cfg.push_assign(block, scope_id, expr_span, &lhs,
Rvalue::BinaryOp(op,
Operand::Consume(lhs.clone()),
rhs));

block.unit()
}
ExprKind::Continue { label } => {
this.break_or_continue(expr_span, label, block,
|loop_scope| loop_scope.continue_block)
}
ExprKind::Break { label } => {
this.break_or_continue(expr_span, label, block, |loop_scope| {
loop_scope.might_break = true;
loop_scope.break_block
})
}
ExprKind::Return { value } => {
block = match value {
Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
None => {
this.cfg.push_assign_unit(block, scope_id,
expr_span, &Lvalue::ReturnPointer);
block
}
};
let extent = this.extent_of_return_scope();
let return_block = this.return_block();
this.exit_scope(expr_span, extent, block, return_block);
this.cfg.start_new_block().unit()
}
ExprKind::Call { ty, fun, args } => {
let diverges = match ty.sty {
ty::TyFnDef(_, _, ref f) | ty::TyFnPtr(ref f) => {
Expand Down Expand Up @@ -294,6 +232,15 @@ impl<'a,'tcx> Builder<'a,'tcx> {
success.unit()
}

// These cases don't actually need a destination
ExprKind::Assign { .. } |
ExprKind::AssignOp { .. } |
ExprKind::Continue { .. } |
ExprKind::Break { .. } |
ExprKind::Return {.. } => {
this.stmt_expr(block, expr)
}

// these are the cases that are more naturally handled by some other mode
ExprKind::Unary { .. } |
ExprKind::Binary { .. } |
Expand Down Expand Up @@ -327,20 +274,4 @@ impl<'a,'tcx> Builder<'a,'tcx> {
}
}
}

fn break_or_continue<F>(&mut self,
span: Span,
label: Option<CodeExtent>,
block: BasicBlock,
exit_selector: F)
-> BlockAnd<()>
where F: FnOnce(&mut LoopScope) -> BasicBlock
{
let (exit_block, extent) = {
let loop_scope = self.find_loop_scope(span, label);
(exit_selector(loop_scope), loop_scope.extent)
};
self.exit_scope(span, extent, block, exit_block);
self.cfg.start_new_block().unit()
}
}
1 change: 1 addition & 0 deletions src/librustc_mir/build/expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,4 @@ mod as_operand;
mod as_temp;
mod category;
mod into;
mod stmt;
135 changes: 135 additions & 0 deletions src/librustc_mir/build/expr/stmt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use build::{BlockAnd, BlockAndExtension, Builder};
use build::scope::LoopScope;
use hair::*;
use rustc::middle::region::CodeExtent;
use rustc::mir::repr::*;
use syntax::codemap::Span;

impl<'a,'tcx> Builder<'a,'tcx> {

pub fn stmt_expr(&mut self, mut block: BasicBlock, expr: Expr<'tcx>) -> BlockAnd<()> {
let this = self;
let expr_span = expr.span;
let scope_id = this.innermost_scope_id();
// Handle a number of expressions that don't need a destination at all. This
// avoids needing a mountain of temporary `()` variables.
match expr.kind {
ExprKind::Scope { extent, value } => {
let value = this.hir.mirror(value);
this.in_scope(extent, block, |this, _| this.stmt_expr(block, value))
}
ExprKind::Assign { lhs, rhs } => {
let lhs = this.hir.mirror(lhs);
let rhs = this.hir.mirror(rhs);
let scope_id = this.innermost_scope_id();
let lhs_span = lhs.span;

let lhs_ty = lhs.ty;
let rhs_ty = rhs.ty;

let lhs_needs_drop = this.hir.needs_drop(lhs_ty);
let rhs_needs_drop = this.hir.needs_drop(rhs_ty);

// Note: we evaluate assignments right-to-left. This
// is better for borrowck interaction with overloaded
// operators like x[j] = x[i].

// Generate better code for things that don't need to be
// dropped.
let rhs = if lhs_needs_drop || rhs_needs_drop {
let op = unpack!(block = this.as_operand(block, rhs));
Rvalue::Use(op)
} else {
unpack!(block = this.as_rvalue(block, rhs))
};

let lhs = unpack!(block = this.as_lvalue(block, lhs));
unpack!(block = this.build_drop(block, lhs_span, lhs.clone(), lhs_ty));
this.cfg.push_assign(block, scope_id, expr_span, &lhs, rhs);
block.unit()
}
ExprKind::AssignOp { op, lhs, rhs } => {
// FIXME(#28160) there is an interesting semantics
// question raised here -- should we "freeze" the
// value of the lhs here? I'm inclined to think not,
// since it seems closer to the semantics of the
// overloaded version, which takes `&mut self`. This
// only affects weird things like `x += {x += 1; x}`
// -- is that equal to `x + (x + 1)` or `2*(x+1)`?

// As above, RTL.
let rhs = unpack!(block = this.as_operand(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));

// we don't have to drop prior contents or anything
// because AssignOp is only legal for Copy types
// (overloaded ops should be desugared into a call).
this.cfg.push_assign(block, scope_id, expr_span, &lhs,
Rvalue::BinaryOp(op,
Operand::Consume(lhs.clone()),
rhs));

block.unit()
}
ExprKind::Continue { label } => {
this.break_or_continue(expr_span, label, block,
|loop_scope| loop_scope.continue_block)
}
ExprKind::Break { label } => {
this.break_or_continue(expr_span, label, block, |loop_scope| {
loop_scope.might_break = true;
loop_scope.break_block
})
}
ExprKind::Return { value } => {
block = match value {
Some(value) => unpack!(this.into(&Lvalue::ReturnPointer, block, value)),
None => {
this.cfg.push_assign_unit(block, scope_id,
expr_span, &Lvalue::ReturnPointer);
block
}
};
let extent = this.extent_of_return_scope();
let return_block = this.return_block();
this.exit_scope(expr_span, extent, block, return_block);
this.cfg.start_new_block().unit()
}
_ => {
let expr_span = expr.span;
let expr_ty = expr.ty;
let temp = this.temp(expr.ty.clone());
unpack!(block = this.into(&temp, block, expr));
unpack!(block = this.build_drop(block, expr_span, temp, expr_ty));
block.unit()
}
}
}

fn break_or_continue<F>(&mut self,
span: Span,
label: Option<CodeExtent>,
block: BasicBlock,
exit_selector: F)
-> BlockAnd<()>
where F: FnOnce(&mut LoopScope) -> BasicBlock
{
let (exit_block, extent) = {
let loop_scope = self.find_loop_scope(span, label);
(exit_selector(loop_scope), loop_scope.extent)
};
self.exit_scope(span, extent, block, exit_block);
self.cfg.start_new_block().unit()
}

}
4 changes: 4 additions & 0 deletions src/librustc_mir/build/misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ impl<'a,'tcx> Builder<'a,'tcx> {
Operand::Constant(constant)
}

pub fn unit_rvalue(&mut self) -> Rvalue<'tcx> {
Rvalue::Aggregate(AggregateKind::Tuple, vec![])
}

pub fn push_usize(&mut self,
block: BasicBlock,
scope_id: ScopeId,
Expand Down
7 changes: 5 additions & 2 deletions src/librustc_mir/build/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,8 +497,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
pub fn build_drop(&mut self,
block: BasicBlock,
span: Span,
value: Lvalue<'tcx>)
-> BlockAnd<()> {
value: Lvalue<'tcx>,
ty: Ty<'tcx>) -> BlockAnd<()> {
if !self.hir.needs_drop(ty) {
return block.unit();
}
let scope_id = self.innermost_scope_id();
let next_target = self.cfg.start_new_block();
let diverge_target = self.diverge_cleanup();
Expand Down
55 changes: 31 additions & 24 deletions src/librustc_trans/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
args: &[ValueRef],
then: BasicBlockRef,
catch: BasicBlockRef,
bundle: Option<&OperandBundleDef>)
-> ValueRef {
bundle: Option<&OperandBundleDef>) -> ValueRef {
self.count_insn("invoke");

debug!("Invoke {:?} with args ({})",
Expand All @@ -176,6 +175,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.collect::<Vec<String>>()
.join(", "));

check_call("invoke", llfn, args);

let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(0 as *mut _);

unsafe {
Expand Down Expand Up @@ -856,28 +857,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.collect::<Vec<String>>()
.join(", "));

let mut fn_ty = val_ty(llfn);
// Strip off pointers
while fn_ty.kind() == llvm::TypeKind::Pointer {
fn_ty = fn_ty.element_type();
}

assert!(fn_ty.kind() == llvm::TypeKind::Function,
"builder::call not passed a function");

let param_tys = fn_ty.func_params();

let iter = param_tys.into_iter()
.zip(args.iter().map(|&v| val_ty(v)));
for (i, (expected_ty, actual_ty)) in iter.enumerate() {
if expected_ty != actual_ty {
bug!("Type mismatch in function call of {:?}. \
Expected {:?} for param {}, got {:?}",
Value(llfn),
expected_ty, i, actual_ty);

}
}
check_call("call", llfn, args);

let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(0 as *mut _);

Expand Down Expand Up @@ -1121,3 +1101,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
}

fn check_call(typ: &str, llfn: ValueRef, args: &[ValueRef]) {
if cfg!(debug_assertions) {
let mut fn_ty = val_ty(llfn);
// Strip off pointers
while fn_ty.kind() == llvm::TypeKind::Pointer {
fn_ty = fn_ty.element_type();
}

assert!(fn_ty.kind() == llvm::TypeKind::Function,
"builder::{} not passed a function", typ);

let param_tys = fn_ty.func_params();

let iter = param_tys.into_iter()
.zip(args.iter().map(|&v| val_ty(v)));
for (i, (expected_ty, actual_ty)) in iter.enumerate() {
if expected_ty != actual_ty {
bug!("Type mismatch in function call of {:?}. \
Expected {:?} for param {}, got {:?}",
Value(llfn),
expected_ty, i, actual_ty);

}
}
}
}
Loading