rust/src/librustc_trans/trans/controlflow.rs
Björn Steinbrink 6ad079e341 Set proper alignment on constants
For enum variants, the default alignment for a specific variant might be
lower than the alignment of the enum type itself. In such cases we, for
example, generate memcpy calls with an alignment that's higher than the
alignment of the constant we copy from.

To avoid that, we need to explicitly set the required alignment on
constants.

Fixes #28912.
2015-10-10 01:15:36 +02:00

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;
}