// 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. use back::link; use lib; use lib::llvm::*; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::callee; use middle::trans::common::*; use middle::trans::debuginfo; use middle::trans::expr; use middle::trans::type_of::*; use middle::ty; use util::common::indenter; use util::ppaux; use middle::trans::type_::Type; use std::str; use syntax::ast; use syntax::ast::ident; use syntax::ast_map::path_mod; use syntax::ast_util; use syntax::codemap::span; pub fn trans_block(bcx: block, b: &ast::blk, dest: expr::Dest) -> block { let _icx = push_ctxt("trans_block"); let mut bcx = bcx; for b.node.stmts.iter().advance |s| { debuginfo::update_source_pos(bcx, b.span); bcx = trans_stmt(bcx, *s); } match b.node.expr { Some(e) => { debuginfo::update_source_pos(bcx, e.span); bcx = expr::trans_into(bcx, e, dest); } None => { assert!(dest == expr::Ignore || bcx.unreachable); } } return bcx; } pub fn trans_if(bcx: block, cond: @ast::expr, thn: &ast::blk, els: Option<@ast::expr>, dest: expr::Dest) -> block { debug!("trans_if(bcx=%s, cond=%s, thn=%?, dest=%s)", bcx.to_str(), bcx.expr_to_str(cond), thn.node.id, dest.to_str(bcx.ccx())); let _indenter = indenter(); let _icx = push_ctxt("trans_if"); let Result {bcx, val: cond_val} = expr::trans_to_datum(bcx, cond).to_result(); let then_bcx_in = scope_block(bcx, thn.info(), "then"); let else_bcx_in = scope_block(bcx, els.info(), "else"); let cond_val = bool_to_i1(bcx, cond_val); CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb); debug!("then_bcx_in=%s, else_bcx_in=%s", then_bcx_in.to_str(), else_bcx_in.to_str()); let then_bcx_out = trans_block(then_bcx_in, thn, dest); let then_bcx_out = trans_block_cleanups(then_bcx_out, block_cleanups(then_bcx_in)); // Calling trans_block directly instead of trans_expr // because trans_expr will create another scope block // context for the block, but we've already got the // 'else' context let else_bcx_out = match els { Some(elexpr) => { match elexpr.node { ast::expr_if(_, _, _) => { let elseif_blk = ast_util::block_from_expr(elexpr); trans_block(else_bcx_in, &elseif_blk, dest) } ast::expr_block(ref blk) => { trans_block(else_bcx_in, blk, dest) } // would be nice to have a constraint on ifs _ => bcx.tcx().sess.bug("strange alternative in if") } } _ => else_bcx_in }; let else_bcx_out = trans_block_cleanups(else_bcx_out, block_cleanups(else_bcx_in)); return join_blocks(bcx, [then_bcx_out, else_bcx_out]); } pub fn join_blocks(parent_bcx: block, in_cxs: &[block]) -> block { let out = sub_block(parent_bcx, "join"); let mut reachable = false; for in_cxs.iter().advance |bcx| { if !bcx.unreachable { Br(*bcx, out.llbb); reachable = true; } } if !reachable { Unreachable(out); } return out; } pub fn trans_while(bcx: block, cond: @ast::expr, body: &ast::blk) -> block { let _icx = push_ctxt("trans_while"); let next_bcx = sub_block(bcx, "while next"); // bcx // | // loop_bcx // | // cond_bcx_in <--------+ // | | // cond_bcx_out | // | | | // | body_bcx_in | // +------+ | | // | body_bcx_out --+ // next_bcx let loop_bcx = loop_scope_block(bcx, next_bcx, None, "`while`", body.info()); let cond_bcx_in = scope_block(loop_bcx, cond.info(), "while loop cond"); let body_bcx_in = scope_block(loop_bcx, body.info(), "while loop body"); Br(bcx, loop_bcx.llbb); Br(loop_bcx, cond_bcx_in.llbb); // compile the condition let Result {bcx: cond_bcx_out, val: cond_val} = expr::trans_to_datum(cond_bcx_in, cond).to_result(); let cond_val = bool_to_i1(cond_bcx_out, cond_val); let cond_bcx_out = trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in)); CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb); // loop body: let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore); cleanup_and_Br(body_bcx_out, body_bcx_in, cond_bcx_in.llbb); return next_bcx; } pub fn trans_loop(bcx:block, body: &ast::blk, opt_label: Option) -> block { let _icx = push_ctxt("trans_loop"); let next_bcx = sub_block(bcx, "next"); let body_bcx_in = loop_scope_block(bcx, next_bcx, opt_label, "`loop`", body.info()); Br(bcx, body_bcx_in.llbb); let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore); cleanup_and_Br(body_bcx_out, body_bcx_in, body_bcx_in.llbb); return next_bcx; } pub fn trans_log(log_ex: &ast::expr, lvl: @ast::expr, bcx: block, e: @ast::expr) -> block { let _icx = push_ctxt("trans_log"); let ccx = bcx.ccx(); let mut bcx = bcx; if ty::type_is_bot(expr_ty(bcx, lvl)) { return expr::trans_into(bcx, lvl, expr::Ignore); } let (modpath, modname) = { let path = &mut bcx.fcx.path; let mut modpath = ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))]; for path.iter().advance |e| { match *e { path_mod(_) => { modpath.push(*e) } _ => {} } } let modname = path_str(ccx.sess, modpath); (modpath, modname) }; let global = if ccx.module_data.contains_key(&modname) { ccx.module_data.get_copy(&modname) } else { let s = link::mangle_internal_name_by_path_and_seq( ccx, modpath, "loglevel"); let global; unsafe { global = str::as_c_str(s, |buf| { llvm::LLVMAddGlobal(ccx.llmod, Type::i32().to_ref(), buf) }); llvm::LLVMSetGlobalConstant(global, False); llvm::LLVMSetInitializer(global, C_null(Type::i32())); lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage); } ccx.module_data.insert(modname, global); global }; let current_level = Load(bcx, global); let level = unpack_result!(bcx, { do with_scope_result(bcx, lvl.info(), "level") |bcx| { expr::trans_to_datum(bcx, lvl).to_result() } }); let llenabled = ICmp(bcx, lib::llvm::IntUGE, current_level, level); do with_cond(bcx, llenabled) |bcx| { do with_scope(bcx, log_ex.info(), "log") |bcx| { let mut bcx = bcx; // Translate the value to be logged let val_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, e)); // Call the polymorphic log function let val = val_datum.to_ref_llval(bcx); let did = bcx.tcx().lang_items.log_type_fn(); let bcx = callee::trans_lang_call_with_type_params( bcx, did, [level, val], [val_datum.ty], expr::Ignore); bcx } } } pub fn trans_break_cont(bcx: block, opt_label: Option, to_end: bool) -> block { let _icx = push_ctxt("trans_break_cont"); // Locate closest loop block, outputting cleanup as we go. let mut unwind = bcx; let mut cur_scope = unwind.scope; let mut target = unwind; loop { cur_scope = match cur_scope { Some(@scope_info { loop_break: Some(brk), loop_label: l, parent, _ }) => { // If we're looking for a labeled loop, check the label... target = if to_end { brk } else { unwind }; match opt_label { Some(desired) => match l { Some(actual) if actual == desired => break, // If it doesn't match the one we want, // don't break _ => parent, }, None => break, } } Some(inf) => inf.parent, None => { unwind = match unwind.parent { Some(bcx) => bcx, // This is a return from a loop body block None => { Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.get()); cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); Unreachable(bcx); return bcx; } }; unwind.scope } } } cleanup_and_Br(bcx, unwind, target.llbb); Unreachable(bcx); return bcx; } pub fn trans_break(bcx: block, label_opt: Option) -> block { return trans_break_cont(bcx, label_opt, true); } pub fn trans_cont(bcx: block, label_opt: Option) -> block { return trans_break_cont(bcx, label_opt, false); } pub fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block { let _icx = push_ctxt("trans_ret"); let mut bcx = bcx; let dest = match copy bcx.fcx.loop_ret { Some((flagptr, retptr)) => { // This is a loop body return. Must set continue flag (our retptr) // to false, return flag to true, and then store the value in the // parent's retptr. Store(bcx, C_bool(true), flagptr); Store(bcx, C_bool(false), bcx.fcx.llretptr.get()); expr::SaveIn(match e { Some(x) => PointerCast(bcx, retptr, type_of(bcx.ccx(), expr_ty(bcx, x)).ptr_to()), None => retptr }) } None => match bcx.fcx.llretptr { None => expr::Ignore, Some(retptr) => expr::SaveIn(retptr), } }; match e { Some(x) => { bcx = expr::trans_into(bcx, x, dest); } _ => () } cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); Unreachable(bcx); return bcx; } pub fn trans_fail_expr(bcx: block, sp_opt: Option, fail_expr: Option<@ast::expr>) -> block { let _icx = push_ctxt("trans_fail_expr"); let mut bcx = bcx; match fail_expr { Some(arg_expr) => { let ccx = bcx.ccx(); let tcx = ccx.tcx; let arg_datum = unpack_datum!( bcx, expr::trans_to_datum(bcx, arg_expr)); if ty::type_is_str(arg_datum.ty) { let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx); return trans_fail_value(bcx, sp_opt, lldata); } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) { return bcx; } else { bcx.sess().span_bug( arg_expr.span, ~"fail called with unsupported type " + ppaux::ty_to_str(tcx, arg_datum.ty)); } } _ => trans_fail(bcx, sp_opt, @"explicit failure") } } pub fn trans_fail(bcx: block, sp_opt: Option, fail_str: @str) -> block { let _icx = push_ctxt("trans_fail"); let V_fail_str = C_cstr(bcx.ccx(), fail_str); return trans_fail_value(bcx, sp_opt, V_fail_str); } fn trans_fail_value(bcx: block, sp_opt: Option, V_fail_str: ValueRef) -> block { let _icx = push_ctxt("trans_fail_value"); let ccx = bcx.ccx(); let (V_filename, V_line) = match sp_opt { Some(sp) => { let sess = bcx.sess(); let loc = sess.parse_sess.cm.lookup_char_pos(sp.lo); (C_cstr(bcx.ccx(), loc.file.name), loc.line as int) } None => { (C_cstr(bcx.ccx(), @""), 0) } }; let V_str = PointerCast(bcx, V_fail_str, Type::i8p()); let V_filename = PointerCast(bcx, V_filename, Type::i8p()); let args = ~[V_str, V_filename, C_int(ccx, V_line)]; let bcx = callee::trans_lang_call( bcx, bcx.tcx().lang_items.fail_fn(), args, Some(expr::Ignore)).bcx; Unreachable(bcx); return bcx; } pub fn trans_fail_bounds_check(bcx: block, sp: span, index: ValueRef, len: ValueRef) -> block { let _icx = push_ctxt("trans_fail_bounds_check"); let (filename, line) = filename_and_line_num_from_span(bcx, sp); let args = ~[filename, line, index, len]; let bcx = callee::trans_lang_call( bcx, bcx.tcx().lang_items.fail_bounds_check_fn(), args, Some(expr::Ignore)).bcx; Unreachable(bcx); return bcx; }