450 lines
15 KiB
Rust
450 lines
15 KiB
Rust
// 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 <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 llvm::ValueRef;
|
|
use middle::def;
|
|
use middle::lang_items::{PanicFnLangItem, PanicBoundsCheckFnLangItem};
|
|
use trans::base::*;
|
|
use trans::basic_block::BasicBlock;
|
|
use trans::build::*;
|
|
use trans::callee;
|
|
use trans::cleanup::CleanupMethods;
|
|
use trans::cleanup;
|
|
use trans::common::*;
|
|
use trans::consts;
|
|
use trans::debuginfo;
|
|
use trans::debuginfo::{DebugLoc, ToDebugLoc};
|
|
use trans::expr;
|
|
use trans::machine;
|
|
use trans;
|
|
use middle::ty;
|
|
|
|
use rustc_front::hir;
|
|
use rustc_front::util as ast_util;
|
|
|
|
use syntax::ast;
|
|
use syntax::parse::token::InternedString;
|
|
use syntax::parse::token;
|
|
|
|
pub fn trans_stmt<'blk, 'tcx>(cx: Block<'blk, 'tcx>,
|
|
s: &hir::Stmt)
|
|
-> Block<'blk, 'tcx> {
|
|
let _icx = push_ctxt("trans_stmt");
|
|
let fcx = cx.fcx;
|
|
debug!("trans_stmt({:?})", s);
|
|
|
|
if cx.unreachable.get() {
|
|
return cx;
|
|
}
|
|
|
|
if cx.sess().asm_comments() {
|
|
add_span_comment(cx, s.span, &format!("{:?}", s));
|
|
}
|
|
|
|
let mut bcx = cx;
|
|
|
|
let id = ast_util::stmt_id(s);
|
|
let cleanup_debug_loc =
|
|
debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), id, s.span, false);
|
|
fcx.push_ast_cleanup_scope(cleanup_debug_loc);
|
|
|
|
match s.node {
|
|
hir::StmtExpr(ref e, _) | hir::StmtSemi(ref e, _) => {
|
|
bcx = trans_stmt_semi(bcx, &**e);
|
|
}
|
|
hir::StmtDecl(ref d, _) => {
|
|
match d.node {
|
|
hir::DeclLocal(ref local) => {
|
|
bcx = init_local(bcx, &**local);
|
|
debuginfo::create_local_var_metadata(bcx, &**local);
|
|
}
|
|
// Inner items are visited by `trans_item`/`trans_meth`.
|
|
hir::DeclItem(_) => {},
|
|
}
|
|
}
|
|
}
|
|
|
|
bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, ast_util::stmt_id(s));
|
|
|
|
return bcx;
|
|
}
|
|
|
|
pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &hir::Expr)
|
|
-> Block<'blk, 'tcx> {
|
|
let _icx = push_ctxt("trans_stmt_semi");
|
|
|
|
if cx.unreachable.get() {
|
|
return cx;
|
|
}
|
|
|
|
let ty = expr_ty(cx, e);
|
|
if cx.fcx.type_needs_drop(ty) {
|
|
expr::trans_to_lvalue(cx, e, "stmt").bcx
|
|
} else {
|
|
expr::trans_into(cx, e, expr::Ignore)
|
|
}
|
|
}
|
|
|
|
pub fn trans_block<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
b: &hir::Block,
|
|
mut dest: expr::Dest)
|
|
-> Block<'blk, 'tcx> {
|
|
let _icx = push_ctxt("trans_block");
|
|
|
|
if bcx.unreachable.get() {
|
|
return bcx;
|
|
}
|
|
|
|
let fcx = bcx.fcx;
|
|
let mut bcx = bcx;
|
|
|
|
let cleanup_debug_loc =
|
|
debuginfo::get_cleanup_debug_loc_for_ast_node(bcx.ccx(), b.id, b.span, true);
|
|
fcx.push_ast_cleanup_scope(cleanup_debug_loc);
|
|
|
|
for s in &b.stmts {
|
|
bcx = trans_stmt(bcx, s);
|
|
}
|
|
|
|
if dest != expr::Ignore {
|
|
let block_ty = node_id_type(bcx, b.id);
|
|
|
|
if b.expr.is_none() || type_is_zero_size(bcx.ccx(), block_ty) {
|
|
dest = expr::Ignore;
|
|
} else if b.expr.is_some() {
|
|
// If the block has an expression, but that expression isn't reachable,
|
|
// don't save into the destination given, ignore it.
|
|
if let Some(ref cfg) = bcx.fcx.cfg {
|
|
if !cfg.node_is_reachable(b.expr.as_ref().unwrap().id) {
|
|
dest = expr::Ignore;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
match b.expr {
|
|
Some(ref e) => {
|
|
if !bcx.unreachable.get() {
|
|
bcx = expr::trans_into(bcx, &**e, dest);
|
|
}
|
|
}
|
|
None => {
|
|
assert!(dest == expr::Ignore || bcx.unreachable.get());
|
|
}
|
|
}
|
|
|
|
bcx = fcx.pop_and_trans_ast_cleanup_scope(bcx, b.id);
|
|
|
|
return bcx;
|
|
}
|
|
|
|
pub fn trans_if<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
if_id: ast::NodeId,
|
|
cond: &hir::Expr,
|
|
thn: &hir::Block,
|
|
els: Option<&hir::Expr>,
|
|
dest: expr::Dest)
|
|
-> Block<'blk, 'tcx> {
|
|
debug!("trans_if(bcx={}, if_id={}, cond={:?}, thn={}, dest={})",
|
|
bcx.to_str(), if_id, cond, thn.id,
|
|
dest.to_string(bcx.ccx()));
|
|
let _icx = push_ctxt("trans_if");
|
|
|
|
if bcx.unreachable.get() {
|
|
return bcx;
|
|
}
|
|
|
|
let mut bcx = bcx;
|
|
|
|
let cond_val = unpack_result!(bcx, expr::trans(bcx, cond).to_llbool());
|
|
|
|
// Drop branches that are known to be impossible
|
|
if let Some(cv) = const_to_opt_uint(cond_val) {
|
|
if cv == 1 {
|
|
// if true { .. } [else { .. }]
|
|
bcx = trans_block(bcx, &*thn, dest);
|
|
trans::debuginfo::clear_source_location(bcx.fcx);
|
|
} else {
|
|
if let Some(elexpr) = els {
|
|
bcx = expr::trans_into(bcx, &*elexpr, dest);
|
|
trans::debuginfo::clear_source_location(bcx.fcx);
|
|
}
|
|
}
|
|
|
|
return bcx;
|
|
}
|
|
|
|
let name = format!("then-block-{}-", thn.id);
|
|
let then_bcx_in = bcx.fcx.new_id_block(&name[..], thn.id);
|
|
let then_bcx_out = trans_block(then_bcx_in, &*thn, dest);
|
|
trans::debuginfo::clear_source_location(bcx.fcx);
|
|
|
|
let cond_source_loc = cond.debug_loc();
|
|
|
|
let next_bcx;
|
|
match els {
|
|
Some(elexpr) => {
|
|
let else_bcx_in = bcx.fcx.new_id_block("else-block", elexpr.id);
|
|
let else_bcx_out = expr::trans_into(else_bcx_in, &*elexpr, dest);
|
|
next_bcx = bcx.fcx.join_blocks(if_id,
|
|
&[then_bcx_out, else_bcx_out]);
|
|
CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb, cond_source_loc);
|
|
}
|
|
|
|
None => {
|
|
next_bcx = bcx.fcx.new_id_block("next-block", if_id);
|
|
Br(then_bcx_out, next_bcx.llbb, DebugLoc::None);
|
|
CondBr(bcx, cond_val, then_bcx_in.llbb, next_bcx.llbb, cond_source_loc);
|
|
}
|
|
}
|
|
|
|
// Clear the source location because it is still set to whatever has been translated
|
|
// right before.
|
|
trans::debuginfo::clear_source_location(next_bcx.fcx);
|
|
|
|
next_bcx
|
|
}
|
|
|
|
pub fn trans_while<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
loop_expr: &hir::Expr,
|
|
cond: &hir::Expr,
|
|
body: &hir::Block)
|
|
-> Block<'blk, 'tcx> {
|
|
let _icx = push_ctxt("trans_while");
|
|
|
|
if bcx.unreachable.get() {
|
|
return bcx;
|
|
}
|
|
|
|
let fcx = bcx.fcx;
|
|
|
|
// bcx
|
|
// |
|
|
// cond_bcx_in <--------+
|
|
// | |
|
|
// cond_bcx_out |
|
|
// | | |
|
|
// | body_bcx_in |
|
|
// cleanup_blk | |
|
|
// | body_bcx_out --+
|
|
// next_bcx_in
|
|
|
|
let next_bcx_in = fcx.new_id_block("while_exit", loop_expr.id);
|
|
let cond_bcx_in = fcx.new_id_block("while_cond", cond.id);
|
|
let body_bcx_in = fcx.new_id_block("while_body", body.id);
|
|
|
|
fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, cond_bcx_in]);
|
|
|
|
Br(bcx, cond_bcx_in.llbb, loop_expr.debug_loc());
|
|
|
|
// compile the block where we will handle loop cleanups
|
|
let cleanup_llbb = fcx.normal_exit_block(loop_expr.id, cleanup::EXIT_BREAK);
|
|
|
|
// compile the condition
|
|
let Result {bcx: cond_bcx_out, val: cond_val} =
|
|
expr::trans(cond_bcx_in, cond).to_llbool();
|
|
|
|
CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, cleanup_llbb, cond.debug_loc());
|
|
|
|
// loop body:
|
|
let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
|
|
Br(body_bcx_out, cond_bcx_in.llbb, DebugLoc::None);
|
|
|
|
fcx.pop_loop_cleanup_scope(loop_expr.id);
|
|
return next_bcx_in;
|
|
}
|
|
|
|
pub fn trans_loop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
loop_expr: &hir::Expr,
|
|
body: &hir::Block)
|
|
-> Block<'blk, 'tcx> {
|
|
let _icx = push_ctxt("trans_loop");
|
|
|
|
if bcx.unreachable.get() {
|
|
return bcx;
|
|
}
|
|
|
|
let fcx = bcx.fcx;
|
|
|
|
// bcx
|
|
// |
|
|
// body_bcx_in
|
|
// |
|
|
// body_bcx_out
|
|
//
|
|
// next_bcx
|
|
//
|
|
// Links between body_bcx_in and next_bcx are created by
|
|
// break statements.
|
|
|
|
let next_bcx_in = bcx.fcx.new_id_block("loop_exit", loop_expr.id);
|
|
let body_bcx_in = bcx.fcx.new_id_block("loop_body", body.id);
|
|
|
|
fcx.push_loop_cleanup_scope(loop_expr.id, [next_bcx_in, body_bcx_in]);
|
|
|
|
Br(bcx, body_bcx_in.llbb, loop_expr.debug_loc());
|
|
let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
|
|
Br(body_bcx_out, body_bcx_in.llbb, DebugLoc::None);
|
|
|
|
fcx.pop_loop_cleanup_scope(loop_expr.id);
|
|
|
|
// If there are no predecessors for the next block, we just translated an endless loop and the
|
|
// next block is unreachable
|
|
if BasicBlock(next_bcx_in.llbb).pred_iter().next().is_none() {
|
|
Unreachable(next_bcx_in);
|
|
}
|
|
|
|
return next_bcx_in;
|
|
}
|
|
|
|
pub fn trans_break_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
expr: &hir::Expr,
|
|
opt_label: Option<ast::Name>,
|
|
exit: usize)
|
|
-> Block<'blk, 'tcx> {
|
|
let _icx = push_ctxt("trans_break_cont");
|
|
|
|
if bcx.unreachable.get() {
|
|
return bcx;
|
|
}
|
|
|
|
let fcx = bcx.fcx;
|
|
|
|
// Locate loop that we will break to
|
|
let loop_id = match opt_label {
|
|
None => fcx.top_loop_scope(),
|
|
Some(_) => {
|
|
match bcx.tcx().def_map.borrow().get(&expr.id).map(|d| d.full_def()) {
|
|
Some(def::DefLabel(loop_id)) => loop_id,
|
|
r => {
|
|
bcx.tcx().sess.bug(&format!("{:?} in def-map for label", r))
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Generate appropriate cleanup code and branch
|
|
let cleanup_llbb = fcx.normal_exit_block(loop_id, exit);
|
|
Br(bcx, cleanup_llbb, expr.debug_loc());
|
|
Unreachable(bcx); // anything afterwards should be ignored
|
|
return bcx;
|
|
}
|
|
|
|
pub fn trans_break<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
expr: &hir::Expr,
|
|
label_opt: Option<ast::Name>)
|
|
-> Block<'blk, 'tcx> {
|
|
return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_BREAK);
|
|
}
|
|
|
|
pub fn trans_cont<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
expr: &hir::Expr,
|
|
label_opt: Option<ast::Name>)
|
|
-> Block<'blk, 'tcx> {
|
|
return trans_break_cont(bcx, expr, label_opt, cleanup::EXIT_LOOP);
|
|
}
|
|
|
|
pub fn trans_ret<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
return_expr: &hir::Expr,
|
|
retval_expr: Option<&hir::Expr>)
|
|
-> Block<'blk, 'tcx> {
|
|
let _icx = push_ctxt("trans_ret");
|
|
|
|
if bcx.unreachable.get() {
|
|
return bcx;
|
|
}
|
|
|
|
let fcx = bcx.fcx;
|
|
let mut bcx = bcx;
|
|
let dest = match (fcx.llretslotptr.get(), retval_expr) {
|
|
(Some(_), Some(retval_expr)) => {
|
|
let ret_ty = expr_ty_adjusted(bcx, &*retval_expr);
|
|
expr::SaveIn(fcx.get_ret_slot(bcx, ty::FnConverging(ret_ty), "ret_slot"))
|
|
}
|
|
_ => expr::Ignore,
|
|
};
|
|
if let Some(x) = retval_expr {
|
|
bcx = expr::trans_into(bcx, &*x, dest);
|
|
match dest {
|
|
expr::SaveIn(slot) if fcx.needs_ret_allocas => {
|
|
Store(bcx, slot, fcx.llretslotptr.get().unwrap());
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
let cleanup_llbb = fcx.return_exit_block();
|
|
Br(bcx, cleanup_llbb, return_expr.debug_loc());
|
|
Unreachable(bcx);
|
|
return bcx;
|
|
}
|
|
|
|
pub fn trans_fail<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
call_info: NodeIdAndSpan,
|
|
fail_str: InternedString)
|
|
-> Block<'blk, 'tcx> {
|
|
let ccx = bcx.ccx();
|
|
let _icx = push_ctxt("trans_fail_value");
|
|
|
|
if bcx.unreachable.get() {
|
|
return bcx;
|
|
}
|
|
|
|
let v_str = C_str_slice(ccx, fail_str);
|
|
let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
|
|
let filename = token::intern_and_get_ident(&loc.file.name);
|
|
let filename = C_str_slice(ccx, filename);
|
|
let line = C_u32(ccx, loc.line as u32);
|
|
let expr_file_line_const = C_struct(ccx, &[v_str, filename, line], false);
|
|
let align = machine::llalign_of_min(ccx, val_ty(expr_file_line_const));
|
|
let expr_file_line = consts::addr_of(ccx, expr_file_line_const, align, "panic_loc");
|
|
let args = vec!(expr_file_line);
|
|
let did = langcall(bcx, Some(call_info.span), "", PanicFnLangItem);
|
|
let bcx = callee::trans_lang_call(bcx,
|
|
did,
|
|
&args[..],
|
|
Some(expr::Ignore),
|
|
call_info.debug_loc()).bcx;
|
|
Unreachable(bcx);
|
|
return bcx;
|
|
}
|
|
|
|
pub fn trans_fail_bounds_check<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|
call_info: NodeIdAndSpan,
|
|
index: ValueRef,
|
|
len: ValueRef)
|
|
-> Block<'blk, 'tcx> {
|
|
let ccx = bcx.ccx();
|
|
let _icx = push_ctxt("trans_fail_bounds_check");
|
|
|
|
if bcx.unreachable.get() {
|
|
return bcx;
|
|
}
|
|
|
|
// Extract the file/line from the span
|
|
let loc = bcx.sess().codemap().lookup_char_pos(call_info.span.lo);
|
|
let filename = token::intern_and_get_ident(&loc.file.name);
|
|
|
|
// Invoke the lang item
|
|
let filename = C_str_slice(ccx, filename);
|
|
let line = C_u32(ccx, loc.line as u32);
|
|
let file_line_const = C_struct(ccx, &[filename, line], false);
|
|
let align = machine::llalign_of_min(ccx, val_ty(file_line_const));
|
|
let file_line = consts::addr_of(ccx, file_line_const, align, "panic_bounds_check_loc");
|
|
let args = vec!(file_line, index, len);
|
|
let did = langcall(bcx, Some(call_info.span), "", PanicBoundsCheckFnLangItem);
|
|
let bcx = callee::trans_lang_call(bcx,
|
|
did,
|
|
&args[..],
|
|
Some(expr::Ignore),
|
|
call_info.debug_loc()).bcx;
|
|
Unreachable(bcx);
|
|
return bcx;
|
|
}
|