]> git.proxmox.com Git - rustc.git/blob - src/librustc_trans/trans/mir/mod.rs
75ce33da2c9b9da66eb84a77ad40fbdabb3f1eaf
[rustc.git] / src / librustc_trans / trans / mir / mod.rs
1 // Copyright 2012-2014 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.
4 //
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.
10
11 use libc::c_uint;
12 use llvm::{self, ValueRef};
13 use rustc::mir::repr as mir;
14 use rustc::mir::tcx::LvalueTy;
15 use trans::base;
16 use trans::build;
17 use trans::common::{self, Block};
18 use trans::debuginfo::DebugLoc;
19 use trans::expr;
20 use trans::type_of;
21
22 use self::lvalue::LvalueRef;
23 use self::operand::OperandRef;
24
25 // FIXME DebugLoc is always None right now
26
27 /// Master context for translating MIR.
28 pub struct MirContext<'bcx, 'tcx:'bcx> {
29 mir: &'bcx mir::Mir<'tcx>,
30
31 /// Function context
32 fcx: &'bcx common::FunctionContext<'bcx, 'tcx>,
33
34 /// When unwinding is initiated, we have to store this personality
35 /// value somewhere so that we can load it and re-use it in the
36 /// resume instruction. The personality is (afaik) some kind of
37 /// value used for C++ unwinding, which must filter by type: we
38 /// don't really care about it very much. Anyway, this value
39 /// contains an alloca into which the personality is stored and
40 /// then later loaded when generating the DIVERGE_BLOCK.
41 llpersonalityslot: Option<ValueRef>,
42
43 /// A `Block` for each MIR `BasicBlock`
44 blocks: Vec<Block<'bcx, 'tcx>>,
45
46 /// Cached unreachable block
47 unreachable_block: Option<Block<'bcx, 'tcx>>,
48
49 /// An LLVM alloca for each MIR `VarDecl`
50 vars: Vec<LvalueRef<'tcx>>,
51
52 /// The location where each MIR `TempDecl` is stored. This is
53 /// usually an `LvalueRef` representing an alloca, but not always:
54 /// sometimes we can skip the alloca and just store the value
55 /// directly using an `OperandRef`, which makes for tighter LLVM
56 /// IR. The conditions for using an `OperandRef` are as follows:
57 ///
58 /// - the type of the temporary must be judged "immediate" by `type_is_immediate`
59 /// - the operand must never be referenced indirectly
60 /// - we should not take its address using the `&` operator
61 /// - nor should it appear in an lvalue path like `tmp.a`
62 /// - the operand must be defined by an rvalue that can generate immediate
63 /// values
64 ///
65 /// Avoiding allocs can also be important for certain intrinsics,
66 /// notably `expect`.
67 temps: Vec<TempRef<'tcx>>,
68
69 /// The arguments to the function; as args are lvalues, these are
70 /// always indirect, though we try to avoid creating an alloca
71 /// when we can (and just reuse the pointer the caller provided).
72 args: Vec<LvalueRef<'tcx>>,
73 }
74
75 enum TempRef<'tcx> {
76 Lvalue(LvalueRef<'tcx>),
77 Operand(Option<OperandRef<'tcx>>),
78 }
79
80 ///////////////////////////////////////////////////////////////////////////
81
82 pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
83 let fcx = bcx.fcx;
84 let mir = bcx.mir();
85
86 let mir_blocks = bcx.mir().all_basic_blocks();
87
88 // Analyze the temps to determine which must be lvalues
89 // FIXME
90 let lvalue_temps = analyze::lvalue_temps(bcx, mir);
91
92 // Allocate variable and temp allocas
93 let vars = mir.var_decls.iter()
94 .map(|decl| (bcx.monomorphize(&decl.ty), decl.name))
95 .map(|(mty, name)| LvalueRef::alloca(bcx, mty, &name.as_str()))
96 .collect();
97 let temps = mir.temp_decls.iter()
98 .map(|decl| bcx.monomorphize(&decl.ty))
99 .enumerate()
100 .map(|(i, mty)| if lvalue_temps.contains(&i) {
101 TempRef::Lvalue(LvalueRef::alloca(bcx,
102 mty,
103 &format!("temp{:?}", i)))
104 } else {
105 // If this is an immediate temp, we do not create an
106 // alloca in advance. Instead we wait until we see the
107 // definition and update the operand there.
108 TempRef::Operand(None)
109 })
110 .collect();
111 let args = arg_value_refs(bcx, mir);
112
113 // Allocate a `Block` for every basic block
114 let block_bcxs: Vec<Block<'bcx,'tcx>> =
115 mir_blocks.iter()
116 .map(|&bb|{
117 let is_cleanup = mir.basic_block_data(bb).is_cleanup;
118 fcx.new_block(is_cleanup, &format!("{:?}", bb), None)
119 })
120 .collect();
121
122 // Branch to the START block
123 let start_bcx = block_bcxs[mir::START_BLOCK.index()];
124 build::Br(bcx, start_bcx.llbb, DebugLoc::None);
125
126 let mut mircx = MirContext {
127 mir: mir,
128 fcx: fcx,
129 llpersonalityslot: None,
130 blocks: block_bcxs,
131 unreachable_block: None,
132 vars: vars,
133 temps: temps,
134 args: args,
135 };
136
137 // Translate the body of each block
138 for &bb in &mir_blocks {
139 mircx.trans_block(bb);
140 }
141 }
142
143 /// Produce, for each argument, a `ValueRef` pointing at the
144 /// argument's value. As arguments are lvalues, these are always
145 /// indirect.
146 fn arg_value_refs<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>,
147 mir: &mir::Mir<'tcx>)
148 -> Vec<LvalueRef<'tcx>> {
149 // FIXME tupled_args? I think I'd rather that mapping is done in MIR land though
150 let fcx = bcx.fcx;
151 let tcx = bcx.tcx();
152 let mut idx = fcx.arg_offset() as c_uint;
153 mir.arg_decls
154 .iter()
155 .enumerate()
156 .map(|(arg_index, arg_decl)| {
157 let arg_ty = bcx.monomorphize(&arg_decl.ty);
158 let llval = if type_of::arg_is_indirect(bcx.ccx(), arg_ty) {
159 // Don't copy an indirect argument to an alloca, the caller
160 // already put it in a temporary alloca and gave it up, unless
161 // we emit extra-debug-info, which requires local allocas :(.
162 // FIXME: lifetimes, debug info
163 let llarg = llvm::get_param(fcx.llfn, idx);
164 idx += 1;
165 llarg
166 } else if common::type_is_fat_ptr(tcx, arg_ty) {
167 // we pass fat pointers as two words, but we want to
168 // represent them internally as a pointer to two words,
169 // so make an alloca to store them in.
170 let lldata = llvm::get_param(fcx.llfn, idx);
171 let llextra = llvm::get_param(fcx.llfn, idx + 1);
172 idx += 2;
173 let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index));
174 build::Store(bcx, lldata, expr::get_dataptr(bcx, lltemp));
175 build::Store(bcx, llextra, expr::get_meta(bcx, lltemp));
176 lltemp
177 } else {
178 // otherwise, arg is passed by value, so make a
179 // temporary and store it there
180 let llarg = llvm::get_param(fcx.llfn, idx);
181 idx += 1;
182 let lltemp = base::alloc_ty(bcx, arg_ty, &format!("arg{}", arg_index));
183 base::store_ty(bcx, llarg, lltemp, arg_ty);
184 lltemp
185 };
186 LvalueRef::new_sized(llval, LvalueTy::from_ty(arg_ty))
187 })
188 .collect()
189 }
190
191 mod analyze;
192 mod block;
193 mod constant;
194 mod lvalue;
195 mod rvalue;
196 mod operand;
197 mod statement;
198 mod did;