1 // Copyright 2012 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
15 use middle::lang_items::{FailFnLangItem, FailBoundsCheckFnLangItem};
16 use middle::lang_items::LogTypeFnLangItem;
17 use middle::trans::base::*;
18 use middle::trans::build::*;
19 use middle::trans::callee;
20 use middle::trans::common::*;
21 use middle::trans::debuginfo;
22 use middle::trans::expr;
23 use middle::trans::type_of::*;
25 use util::common::indenter;
28 use middle::trans::type_::Type;
32 use syntax::ast::ident;
33 use syntax::ast_map::path_mod;
35 use syntax::codemap::span;
37 pub fn trans_block(bcx: @mut Block, b: &ast::Block, dest: expr::Dest) -> @mut Block {
38 let _icx = push_ctxt("trans_block");
40 for b.stmts.iter().advance |s| {
41 debuginfo::update_source_pos(bcx, b.span);
42 bcx = trans_stmt(bcx, *s);
46 debuginfo::update_source_pos(bcx, e.span);
47 bcx = expr::trans_into(bcx, e, dest);
50 assert!(dest == expr::Ignore || bcx.unreachable);
56 pub fn trans_if(bcx: @mut Block,
59 els: Option<@ast::expr>,
62 debug!("trans_if(bcx=%s, cond=%s, thn=%?, dest=%s)",
63 bcx.to_str(), bcx.expr_to_str(cond), thn.id,
64 dest.to_str(bcx.ccx()));
65 let _indenter = indenter();
67 let _icx = push_ctxt("trans_if");
70 // `if true` and `if false` can be trans'd more efficiently,
71 // by dropping branches that are known to be impossible.
72 ast::expr_lit(@ref l) => match l.node {
73 ast::lit_bool(true) => {
74 // if true { .. } [else { .. }]
75 let then_bcx_in = scope_block(bcx, thn.info(), "if_true_then");
76 let then_bcx_out = trans_block(then_bcx_in, thn, dest);
77 let then_bcx_out = trans_block_cleanups(then_bcx_out,
78 block_cleanups(then_bcx_in));
79 Br(bcx, then_bcx_in.llbb);
82 ast::lit_bool(false) => {
84 // if false { .. } else { .. }
86 let (else_bcx_in, else_bcx_out) =
87 trans_if_else(bcx, elexpr, dest, "if_false_else");
88 Br(bcx, else_bcx_in.llbb);
100 let Result {bcx, val: cond_val} =
101 expr::trans_to_datum(bcx, cond).to_result();
103 let then_bcx_in = scope_block(bcx, thn.info(), "then");
105 let cond_val = bool_to_i1(bcx, cond_val);
107 let then_bcx_out = trans_block(then_bcx_in, thn, dest);
108 let then_bcx_out = trans_block_cleanups(then_bcx_out,
109 block_cleanups(then_bcx_in));
111 // Calling trans_block directly instead of trans_expr
112 // because trans_expr will create another scope block
113 // context for the block, but we've already got the
115 let (else_bcx_in, next_bcx) = match els {
117 let (else_bcx_in, else_bcx_out) = trans_if_else(bcx, elexpr, dest, "else");
118 (else_bcx_in, join_blocks(bcx, [then_bcx_out, else_bcx_out]))
121 let next_bcx = sub_block(bcx, "next");
122 Br(then_bcx_out, next_bcx.llbb);
128 debug!("then_bcx_in=%s, else_bcx_in=%s",
129 then_bcx_in.to_str(), else_bcx_in.to_str());
131 CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
134 // trans `else [ if { .. } ... | { .. } ]`
135 fn trans_if_else(bcx: @mut Block, elexpr: @ast::expr,
136 dest: expr::Dest, scope_name: &str) -> (@mut Block, @mut Block) {
137 let else_bcx_in = scope_block(bcx, elexpr.info(), scope_name);
138 let else_bcx_out = match elexpr.node {
139 ast::expr_if(_, _, _) => {
140 let elseif_blk = ast_util::block_from_expr(elexpr);
141 trans_block(else_bcx_in, &elseif_blk, dest)
143 ast::expr_block(ref blk) => {
144 trans_block(else_bcx_in, blk, dest)
146 // would be nice to have a constraint on ifs
147 _ => bcx.tcx().sess.bug("strange alternative in if")
149 let else_bcx_out = trans_block_cleanups(else_bcx_out,
150 block_cleanups(else_bcx_in));
151 (else_bcx_in, else_bcx_out)
155 pub fn join_blocks(parent_bcx: @mut Block, in_cxs: &[@mut Block]) -> @mut Block {
156 let out = sub_block(parent_bcx, "join");
157 let mut reachable = false;
158 for in_cxs.iter().advance |bcx| {
159 if !bcx.unreachable {
170 pub fn trans_while(bcx: @mut Block, cond: @ast::expr, body: &ast::Block) -> @mut Block {
171 let _icx = push_ctxt("trans_while");
172 let next_bcx = sub_block(bcx, "while next");
178 // cond_bcx_in <--------+
184 // | body_bcx_out --+
187 let loop_bcx = loop_scope_block(bcx, next_bcx, None, "`while`",
189 let cond_bcx_in = scope_block(loop_bcx, cond.info(), "while loop cond");
190 let body_bcx_in = scope_block(loop_bcx, body.info(), "while loop body");
191 Br(bcx, loop_bcx.llbb);
192 Br(loop_bcx, cond_bcx_in.llbb);
194 // compile the condition
195 let Result {bcx: cond_bcx_out, val: cond_val} =
196 expr::trans_to_datum(cond_bcx_in, cond).to_result();
197 let cond_val = bool_to_i1(cond_bcx_out, cond_val);
199 trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in));
200 CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb);
203 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
204 cleanup_and_Br(body_bcx_out, body_bcx_in, cond_bcx_in.llbb);
209 pub fn trans_loop(bcx:@mut Block,
211 opt_label: Option<ident>)
213 let _icx = push_ctxt("trans_loop");
214 let next_bcx = sub_block(bcx, "next");
215 let body_bcx_in = loop_scope_block(bcx, next_bcx, opt_label, "`loop`",
217 Br(bcx, body_bcx_in.llbb);
218 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
219 cleanup_and_Br(body_bcx_out, body_bcx_in, body_bcx_in.llbb);
223 pub fn trans_log(log_ex: &ast::expr,
226 e: @ast::expr) -> @mut Block {
227 let _icx = push_ctxt("trans_log");
230 if ty::type_is_bot(expr_ty(bcx, lvl)) {
231 return expr::trans_into(bcx, lvl, expr::Ignore);
234 let (modpath, modname) = {
235 let path = &mut bcx.fcx.path;
236 let mut modpath = ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))];
237 for path.iter().advance |e| {
239 path_mod(_) => { modpath.push(*e) }
243 let modname = path_str(ccx.sess, modpath);
247 let global = if ccx.module_data.contains_key(&modname) {
248 ccx.module_data.get_copy(&modname)
250 let s = link::mangle_internal_name_by_path_and_seq(
251 ccx, modpath, "loglevel");
254 global = str::as_c_str(s, |buf| {
255 llvm::LLVMAddGlobal(ccx.llmod, Type::i32().to_ref(), buf)
257 llvm::LLVMSetGlobalConstant(global, False);
258 llvm::LLVMSetInitializer(global, C_null(Type::i32()));
259 lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage);
261 ccx.module_data.insert(modname, global);
264 let current_level = Load(bcx, global);
265 let level = unpack_result!(bcx, {
266 do with_scope_result(bcx, lvl.info(), "level") |bcx| {
267 expr::trans_to_datum(bcx, lvl).to_result()
271 let llenabled = ICmp(bcx, lib::llvm::IntUGE, current_level, level);
272 do with_cond(bcx, llenabled) |bcx| {
273 do with_scope(bcx, log_ex.info(), "log") |bcx| {
276 // Translate the value to be logged
277 let val_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, e));
279 // Call the polymorphic log function
280 let val = val_datum.to_ref_llval(bcx);
281 let did = langcall(bcx, Some(e.span), "", LogTypeFnLangItem);
282 let bcx = callee::trans_lang_call_with_type_params(
283 bcx, did, [level, val], [val_datum.ty], expr::Ignore);
289 pub fn trans_break_cont(bcx: @mut Block,
290 opt_label: Option<ident>,
293 let _icx = push_ctxt("trans_break_cont");
294 // Locate closest loop block, outputting cleanup as we go.
295 let mut unwind = bcx;
296 let mut cur_scope = unwind.scope;
299 cur_scope = match cur_scope {
301 loop_break: Some(brk),
306 // If we're looking for a labeled loop, check the label...
313 Some(desired) => match l {
314 Some(actual) if actual == desired => break,
315 // If it doesn't match the one we want,
322 Some(inf) => inf.parent,
324 unwind = match unwind.parent {
326 // This is a return from a loop body block
328 Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.get());
329 cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
338 cleanup_and_Br(bcx, unwind, target.llbb);
343 pub fn trans_break(bcx: @mut Block, label_opt: Option<ident>) -> @mut Block {
344 return trans_break_cont(bcx, label_opt, true);
347 pub fn trans_cont(bcx: @mut Block, label_opt: Option<ident>) -> @mut Block {
348 return trans_break_cont(bcx, label_opt, false);
351 pub fn trans_ret(bcx: @mut Block, e: Option<@ast::expr>) -> @mut Block {
352 let _icx = push_ctxt("trans_ret");
354 let dest = match bcx.fcx.loop_ret {
355 Some((flagptr, retptr)) => {
356 // This is a loop body return. Must set continue flag (our retptr)
357 // to false, return flag to true, and then store the value in the
359 Store(bcx, C_bool(true), flagptr);
360 Store(bcx, C_bool(false), bcx.fcx.llretptr.get());
361 expr::SaveIn(match e {
362 Some(x) => PointerCast(bcx, retptr,
363 type_of(bcx.ccx(), expr_ty(bcx, x)).ptr_to()),
367 None => match bcx.fcx.llretptr {
368 None => expr::Ignore,
369 Some(retptr) => expr::SaveIn(retptr),
374 bcx = expr::trans_into(bcx, x, dest);
378 cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
383 pub fn trans_fail_expr(bcx: @mut Block,
384 sp_opt: Option<span>,
385 fail_expr: Option<@ast::expr>)
387 let _icx = push_ctxt("trans_fail_expr");
393 let arg_datum = unpack_datum!(
394 bcx, expr::trans_to_datum(bcx, arg_expr));
396 if ty::type_is_str(arg_datum.ty) {
397 let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx);
398 return trans_fail_value(bcx, sp_opt, lldata);
399 } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) {
403 arg_expr.span, ~"fail called with unsupported type " +
404 ppaux::ty_to_str(tcx, arg_datum.ty));
407 _ => trans_fail(bcx, sp_opt, @"explicit failure")
411 pub fn trans_fail(bcx: @mut Block,
412 sp_opt: Option<span>,
415 let _icx = push_ctxt("trans_fail");
416 let V_fail_str = C_cstr(bcx.ccx(), fail_str);
417 return trans_fail_value(bcx, sp_opt, V_fail_str);
420 fn trans_fail_value(bcx: @mut Block,
421 sp_opt: Option<span>,
422 V_fail_str: ValueRef)
424 let _icx = push_ctxt("trans_fail_value");
426 let (V_filename, V_line) = match sp_opt {
428 let sess = bcx.sess();
429 let loc = sess.parse_sess.cm.lookup_char_pos(sp.lo);
430 (C_cstr(bcx.ccx(), loc.file.name),
434 (C_cstr(bcx.ccx(), @"<runtime>"), 0)
437 let V_str = PointerCast(bcx, V_fail_str, Type::i8p());
438 let V_filename = PointerCast(bcx, V_filename, Type::i8p());
439 let args = ~[V_str, V_filename, C_int(ccx, V_line)];
440 let did = langcall(bcx, sp_opt, "", FailFnLangItem);
441 let bcx = callee::trans_lang_call(bcx, did, args, Some(expr::Ignore)).bcx;
446 pub fn trans_fail_bounds_check(bcx: @mut Block, sp: span,
447 index: ValueRef, len: ValueRef) -> @mut Block {
448 let _icx = push_ctxt("trans_fail_bounds_check");
449 let (filename, line) = filename_and_line_num_from_span(bcx, sp);
450 let args = ~[filename, line, index, len];
451 let did = langcall(bcx, Some(sp), "", FailBoundsCheckFnLangItem);
452 let bcx = callee::trans_lang_call(bcx, did, args, Some(expr::Ignore)).bcx;