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.
11 use std::c_str::ToCStr;
16 use middle::lang_items::{FailFnLangItem, FailBoundsCheckFnLangItem};
17 use middle::lang_items::LogTypeFnLangItem;
18 use middle::trans::base::*;
19 use middle::trans::build::*;
20 use middle::trans::callee;
21 use middle::trans::common::*;
22 use middle::trans::expr;
23 use middle::trans::type_of::*;
25 use util::common::indenter;
28 use middle::trans::type_::Type;
31 use syntax::ast::ident;
32 use syntax::ast_map::path_mod;
34 use syntax::codemap::span;
36 pub fn trans_block(bcx: @mut Block, b: &ast::Block, dest: expr::Dest) -> @mut Block {
37 let _icx = push_ctxt("trans_block");
39 for s in b.stmts.iter() {
40 bcx = trans_stmt(bcx, *s);
44 bcx = expr::trans_into(bcx, e, dest);
47 assert!(dest == expr::Ignore || bcx.unreachable);
53 pub fn trans_if(bcx: @mut Block,
56 els: Option<@ast::expr>,
59 debug!("trans_if(bcx=%s, cond=%s, thn=%?, dest=%s)",
60 bcx.to_str(), bcx.expr_to_str(cond), thn.id,
61 dest.to_str(bcx.ccx()));
62 let _indenter = indenter();
64 let _icx = push_ctxt("trans_if");
66 let Result {bcx, val: cond_val} =
67 expr::trans_to_datum(bcx, cond).to_result();
69 let cond_val = bool_to_i1(bcx, cond_val);
71 // Drop branches that are known to be impossible
72 if is_const(cond_val) && !is_undef(cond_val) {
73 if const_to_uint(cond_val) == 1 {
74 // if true { .. } [else { .. }]
75 return do with_scope(bcx, thn.info(), "if_true_then") |bcx| {
76 let bcx_out = trans_block(bcx, thn, dest);
77 trans_block_cleanups(bcx_out, block_cleanups(bcx))
81 // if false { .. } else { .. }
83 return do with_scope(bcx, elexpr.info(), "if_false_then") |bcx| {
84 let bcx_out = trans_if_else(bcx, elexpr, dest);
85 trans_block_cleanups(bcx_out, block_cleanups(bcx))
94 let then_bcx_in = scope_block(bcx, thn.info(), "then");
96 let then_bcx_out = trans_block(then_bcx_in, thn, dest);
97 let then_bcx_out = trans_block_cleanups(then_bcx_out,
98 block_cleanups(then_bcx_in));
100 // Calling trans_block directly instead of trans_expr
101 // because trans_expr will create another scope block
102 // context for the block, but we've already got the
104 let (else_bcx_in, next_bcx) = match els {
106 let else_bcx_in = scope_block(bcx, elexpr.info(), "else");
107 let else_bcx_out = trans_if_else(else_bcx_in, elexpr, dest);
108 (else_bcx_in, join_blocks(bcx, [then_bcx_out, else_bcx_out]))
111 let next_bcx = sub_block(bcx, "next");
112 Br(then_bcx_out, next_bcx.llbb);
118 debug!("then_bcx_in=%s, else_bcx_in=%s",
119 then_bcx_in.to_str(), else_bcx_in.to_str());
121 CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
124 // trans `else [ if { .. } ... | { .. } ]`
125 fn trans_if_else(else_bcx_in: @mut Block, elexpr: @ast::expr,
126 dest: expr::Dest) -> @mut Block {
127 let else_bcx_out = match elexpr.node {
128 ast::expr_if(_, _, _) => {
129 let elseif_blk = ast_util::block_from_expr(elexpr);
130 trans_block(else_bcx_in, &elseif_blk, dest)
132 ast::expr_block(ref blk) => {
133 trans_block(else_bcx_in, blk, dest)
135 // would be nice to have a constraint on ifs
136 _ => else_bcx_in.tcx().sess.bug("strange alternative in if")
138 trans_block_cleanups(else_bcx_out, block_cleanups(else_bcx_in))
142 pub fn join_blocks(parent_bcx: @mut Block, in_cxs: &[@mut Block]) -> @mut Block {
143 let out = sub_block(parent_bcx, "join");
144 let mut reachable = false;
145 for bcx in in_cxs.iter() {
146 if !bcx.unreachable {
157 pub fn trans_while(bcx: @mut Block, cond: @ast::expr, body: &ast::Block) -> @mut Block {
158 let _icx = push_ctxt("trans_while");
159 let next_bcx = sub_block(bcx, "while next");
165 // cond_bcx_in <--------+
171 // | body_bcx_out --+
174 let loop_bcx = loop_scope_block(bcx, next_bcx, None, "`while`",
176 let cond_bcx_in = scope_block(loop_bcx, cond.info(), "while loop cond");
177 let body_bcx_in = scope_block(loop_bcx, body.info(), "while loop body");
178 Br(bcx, loop_bcx.llbb);
179 Br(loop_bcx, cond_bcx_in.llbb);
181 // compile the condition
182 let Result {bcx: cond_bcx_out, val: cond_val} =
183 expr::trans_to_datum(cond_bcx_in, cond).to_result();
184 let cond_val = bool_to_i1(cond_bcx_out, cond_val);
186 trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in));
187 CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb);
190 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
191 cleanup_and_Br(body_bcx_out, body_bcx_in, cond_bcx_in.llbb);
196 pub fn trans_loop(bcx:@mut Block,
198 opt_label: Option<ident>)
200 let _icx = push_ctxt("trans_loop");
201 let next_bcx = sub_block(bcx, "next");
202 let body_bcx_in = loop_scope_block(bcx, next_bcx, opt_label, "`loop`",
204 Br(bcx, body_bcx_in.llbb);
205 let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
206 cleanup_and_Br(body_bcx_out, body_bcx_in, body_bcx_in.llbb);
210 pub fn trans_log(log_ex: &ast::expr,
213 e: @ast::expr) -> @mut Block {
214 let _icx = push_ctxt("trans_log");
217 if ty::type_is_bot(expr_ty(bcx, lvl)) {
218 return expr::trans_into(bcx, lvl, expr::Ignore);
221 let (modpath, modname) = {
222 let path = &mut bcx.fcx.path;
223 let mut modpath = ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))];
224 for e in path.iter() {
226 path_mod(_) => { modpath.push(*e) }
230 let modname = path_str(ccx.sess, modpath);
234 let global = if ccx.module_data.contains_key(&modname) {
235 ccx.module_data.get_copy(&modname)
237 let s = link::mangle_internal_name_by_path_and_seq(
238 ccx, modpath, "loglevel");
241 global = do s.with_c_str |buf| {
242 llvm::LLVMAddGlobal(ccx.llmod, Type::i32().to_ref(), buf)
244 llvm::LLVMSetGlobalConstant(global, False);
245 llvm::LLVMSetInitializer(global, C_null(Type::i32()));
246 lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage);
248 ccx.module_data.insert(modname, global);
251 let current_level = Load(bcx, global);
252 let level = unpack_result!(bcx, {
253 do with_scope_result(bcx, lvl.info(), "level") |bcx| {
254 expr::trans_to_datum(bcx, lvl).to_result()
258 let llenabled = ICmp(bcx, lib::llvm::IntUGE, current_level, level);
259 do with_cond(bcx, llenabled) |bcx| {
260 do with_scope(bcx, log_ex.info(), "log") |bcx| {
263 // Translate the value to be logged
264 let val_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, e));
266 // Call the polymorphic log function
267 let val = val_datum.to_ref_llval(bcx);
268 let did = langcall(bcx, Some(e.span), "", LogTypeFnLangItem);
269 let bcx = callee::trans_lang_call_with_type_params(
270 bcx, did, [level, val], [val_datum.ty], expr::Ignore);
276 pub fn trans_break_cont(bcx: @mut Block,
277 opt_label: Option<ident>,
280 let _icx = push_ctxt("trans_break_cont");
281 // Locate closest loop block, outputting cleanup as we go.
282 let mut unwind = bcx;
283 let mut cur_scope = unwind.scope;
286 cur_scope = match cur_scope {
288 loop_break: Some(brk),
293 // If we're looking for a labeled loop, check the label...
300 Some(desired) => match l {
301 Some(actual) if actual == desired => break,
302 // If it doesn't match the one we want,
309 Some(inf) => inf.parent,
311 unwind = match unwind.parent {
313 // This is a return from a loop body block
315 Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.unwrap());
316 cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
325 cleanup_and_Br(bcx, unwind, target.llbb);
330 pub fn trans_break(bcx: @mut Block, label_opt: Option<ident>) -> @mut Block {
331 return trans_break_cont(bcx, label_opt, true);
334 pub fn trans_cont(bcx: @mut Block, label_opt: Option<ident>) -> @mut Block {
335 return trans_break_cont(bcx, label_opt, false);
338 pub fn trans_ret(bcx: @mut Block, e: Option<@ast::expr>) -> @mut Block {
339 let _icx = push_ctxt("trans_ret");
341 let dest = match bcx.fcx.loop_ret {
342 Some((flagptr, retptr)) => {
343 // This is a loop body return. Must set continue flag (our retptr)
344 // to false, return flag to true, and then store the value in the
346 Store(bcx, C_bool(true), flagptr);
347 Store(bcx, C_bool(false), bcx.fcx.llretptr.unwrap());
348 expr::SaveIn(match e {
349 Some(x) => PointerCast(bcx, retptr,
350 type_of(bcx.ccx(), expr_ty(bcx, x)).ptr_to()),
354 None => match bcx.fcx.llretptr {
355 None => expr::Ignore,
356 Some(retptr) => expr::SaveIn(retptr),
361 bcx = expr::trans_into(bcx, x, dest);
365 cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
370 pub fn trans_fail_expr(bcx: @mut Block,
371 sp_opt: Option<span>,
372 fail_expr: Option<@ast::expr>)
374 let _icx = push_ctxt("trans_fail_expr");
380 let arg_datum = unpack_datum!(
381 bcx, expr::trans_to_datum(bcx, arg_expr));
383 if ty::type_is_str(arg_datum.ty) {
384 let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx);
385 return trans_fail_value(bcx, sp_opt, lldata);
386 } else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) {
390 arg_expr.span, ~"fail called with unsupported type " +
391 ppaux::ty_to_str(tcx, arg_datum.ty));
394 _ => trans_fail(bcx, sp_opt, @"explicit failure")
398 pub fn trans_fail(bcx: @mut Block,
399 sp_opt: Option<span>,
402 let _icx = push_ctxt("trans_fail");
403 let V_fail_str = C_cstr(bcx.ccx(), fail_str);
404 return trans_fail_value(bcx, sp_opt, V_fail_str);
407 fn trans_fail_value(bcx: @mut Block,
408 sp_opt: Option<span>,
409 V_fail_str: ValueRef)
411 let _icx = push_ctxt("trans_fail_value");
413 let (V_filename, V_line) = match sp_opt {
415 let sess = bcx.sess();
416 let loc = sess.parse_sess.cm.lookup_char_pos(sp.lo);
417 (C_cstr(bcx.ccx(), loc.file.name),
421 (C_cstr(bcx.ccx(), @"<runtime>"), 0)
424 let V_str = PointerCast(bcx, V_fail_str, Type::i8p());
425 let V_filename = PointerCast(bcx, V_filename, Type::i8p());
426 let args = ~[V_str, V_filename, C_int(ccx, V_line)];
427 let did = langcall(bcx, sp_opt, "", FailFnLangItem);
428 let bcx = callee::trans_lang_call(bcx, did, args, Some(expr::Ignore)).bcx;
433 pub fn trans_fail_bounds_check(bcx: @mut Block, sp: span,
434 index: ValueRef, len: ValueRef) -> @mut Block {
435 let _icx = push_ctxt("trans_fail_bounds_check");
436 let (filename, line) = filename_and_line_num_from_span(bcx, sp);
437 let args = ~[filename, line, index, len];
438 let did = langcall(bcx, Some(sp), "", FailBoundsCheckFnLangItem);
439 let bcx = callee::trans_lang_call(bcx, did, args, Some(expr::Ignore)).bcx;