]>
Commit | Line | Data |
---|---|---|
9346a6ac | 1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
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. | |
54a0048b | 10 | |
9346a6ac AL |
11 | //! Translate the completed AST to the LLVM IR. |
12 | //! | |
13 | //! Some functions here, such as trans_block and trans_expr, return a value -- | |
54a0048b SL |
14 | //! the result of the translation to LLVM -- while others, such as trans_fn |
15 | //! and trans_item, are called only for the side effect of adding a | |
9346a6ac AL |
16 | //! particular definition to the LLVM IR output we're producing. |
17 | //! | |
18 | //! Hopefully useful general knowledge about trans: | |
19 | //! | |
20 | //! * There's no way to find out the Ty type of a ValueRef. Doing so | |
21 | //! would be "trying to get the eggs out of an omelette" (credit: | |
22 | //! pcwalton). You can, instead, find out its TypeRef by calling val_ty, | |
23 | //! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int, | |
24 | //! int) and rec(x=int, y=int, z=int) will have the same TypeRef. | |
1a4d82fc | 25 | |
5bcae85e SL |
26 | use super::ModuleLlvm; |
27 | use super::ModuleSource; | |
1a4d82fc | 28 | use super::ModuleTranslation; |
3b2f2976 | 29 | use super::ModuleKind; |
1a4d82fc | 30 | |
ff7c6d11 | 31 | use abi; |
3157f602 | 32 | use back::link; |
ea8adc8c XL |
33 | use back::symbol_export; |
34 | use back::write::{self, OngoingCrateTranslation, create_target_machine}; | |
35 | use llvm::{ContextRef, ModuleRef, ValueRef, Vector, get_param}; | |
1a4d82fc | 36 | use llvm; |
7cac9316 | 37 | use metadata; |
ea8adc8c | 38 | use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; |
7cac9316 | 39 | use rustc::middle::lang_items::StartFnLangItem; |
ff7c6d11 XL |
40 | use rustc::mir::mono::{Linkage, Visibility, Stats}; |
41 | use rustc::middle::cstore::{EncodedMetadata}; | |
32a655c1 | 42 | use rustc::ty::{self, Ty, TyCtxt}; |
ff7c6d11 | 43 | use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; |
ea8adc8c | 44 | use rustc::ty::maps::Providers; |
ff7c6d11 XL |
45 | use rustc::dep_graph::{DepNode, DepConstructor}; |
46 | use rustc::ty::subst::Kind; | |
ea8adc8c | 47 | use rustc::middle::cstore::{self, LinkMeta, LinkagePreference}; |
3b2f2976 | 48 | use rustc::util::common::{time, print_time_passes_entry}; |
ea8adc8c | 49 | use rustc::session::config::{self, NoDebugInfo}; |
7cac9316 | 50 | use rustc::session::Session; |
ea8adc8c | 51 | use rustc_incremental; |
041b39d2 | 52 | use allocator; |
ff7c6d11 | 53 | use mir::place::PlaceRef; |
54a0048b | 54 | use attributes; |
32a655c1 | 55 | use builder::Builder; |
cc61c64b | 56 | use callee; |
ea8adc8c | 57 | use common::{C_bool, C_bytes_in_context, C_i32, C_usize}; |
ff7c6d11 | 58 | use rustc_mir::monomorphize::collector::{self, MonoItemCollectionMode}; |
2c00a5a8 | 59 | use common::{self, C_struct_in_context, C_array, val_ty}; |
54a0048b | 60 | use consts; |
2c00a5a8 | 61 | use context::{self, CodegenCx}; |
32a655c1 | 62 | use debuginfo; |
54a0048b | 63 | use declare; |
54a0048b SL |
64 | use meth; |
65 | use mir; | |
ff7c6d11 XL |
66 | use monomorphize::Instance; |
67 | use monomorphize::partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt}; | |
2c00a5a8 | 68 | use rustc_trans_utils::symbol_names_test; |
3b2f2976 | 69 | use time_graph; |
ff7c6d11 | 70 | use trans_item::{MonoItem, BaseMonoItemExt, MonoItemExt, DefPathBasedNames}; |
54a0048b | 71 | use type_::Type; |
ff7c6d11 | 72 | use type_of::LayoutLlvmExt; |
ea8adc8c XL |
73 | use rustc::util::nodemap::{NodeSet, FxHashMap, FxHashSet, DefIdSet}; |
74 | use CrateInfo; | |
1a4d82fc | 75 | |
ea8adc8c | 76 | use std::any::Any; |
abe05a73 | 77 | use std::ffi::CString; |
1a4d82fc | 78 | use std::str; |
3b2f2976 XL |
79 | use std::sync::Arc; |
80 | use std::time::{Instant, Duration}; | |
2c00a5a8 | 81 | use std::{i32, usize}; |
ff7c6d11 | 82 | use std::iter; |
ea8adc8c | 83 | use std::sync::mpsc; |
cc61c64b | 84 | use syntax_pos::Span; |
ea8adc8c | 85 | use syntax_pos::symbol::InternedString; |
b039eaaf | 86 | use syntax::attr; |
54a0048b | 87 | use rustc::hir; |
e9174d1e | 88 | use syntax::ast; |
1a4d82fc | 89 | |
ff7c6d11 | 90 | use mir::operand::OperandValue; |
1a4d82fc | 91 | |
ea8adc8c | 92 | pub use rustc_trans_utils::{find_exported_symbols, check_for_rustc_errors_attr}; |
ff7c6d11 | 93 | pub use rustc_mir::monomorphize::item::linkage_by_name; |
ea8adc8c | 94 | |
1a4d82fc | 95 | pub struct StatRecorder<'a, 'tcx: 'a> { |
2c00a5a8 | 96 | cx: &'a CodegenCx<'a, 'tcx>, |
1a4d82fc | 97 | name: Option<String>, |
c34b1796 | 98 | istart: usize, |
1a4d82fc JJ |
99 | } |
100 | ||
101 | impl<'a, 'tcx> StatRecorder<'a, 'tcx> { | |
2c00a5a8 XL |
102 | pub fn new(cx: &'a CodegenCx<'a, 'tcx>, name: String) -> StatRecorder<'a, 'tcx> { |
103 | let istart = cx.stats.borrow().n_llvm_insns; | |
1a4d82fc | 104 | StatRecorder { |
2c00a5a8 | 105 | cx, |
1a4d82fc | 106 | name: Some(name), |
3b2f2976 | 107 | istart, |
1a4d82fc JJ |
108 | } |
109 | } | |
110 | } | |
111 | ||
1a4d82fc JJ |
112 | impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { |
113 | fn drop(&mut self) { | |
2c00a5a8 XL |
114 | if self.cx.sess().trans_stats() { |
115 | let mut stats = self.cx.stats.borrow_mut(); | |
ea8adc8c XL |
116 | let iend = stats.n_llvm_insns; |
117 | stats.fn_stats.push((self.name.take().unwrap(), iend - self.istart)); | |
118 | stats.n_fns += 1; | |
1a4d82fc | 119 | // Reset LLVM insn count to avoid compound costs. |
ea8adc8c | 120 | stats.n_llvm_insns = self.istart; |
1a4d82fc JJ |
121 | } |
122 | } | |
123 | } | |
124 | ||
54a0048b | 125 | pub fn bin_op_to_icmp_predicate(op: hir::BinOp_, |
92a42be0 | 126 | signed: bool) |
85aaf69f SL |
127 | -> llvm::IntPredicate { |
128 | match op { | |
e9174d1e SL |
129 | hir::BiEq => llvm::IntEQ, |
130 | hir::BiNe => llvm::IntNE, | |
131 | hir::BiLt => if signed { llvm::IntSLT } else { llvm::IntULT }, | |
132 | hir::BiLe => if signed { llvm::IntSLE } else { llvm::IntULE }, | |
133 | hir::BiGt => if signed { llvm::IntSGT } else { llvm::IntUGT }, | |
134 | hir::BiGe => if signed { llvm::IntSGE } else { llvm::IntUGE }, | |
85aaf69f | 135 | op => { |
54a0048b SL |
136 | bug!("comparison_op_to_icmp_predicate: expected comparison operator, \ |
137 | found {:?}", | |
138 | op) | |
85aaf69f | 139 | } |
1a4d82fc JJ |
140 | } |
141 | } | |
142 | ||
54a0048b | 143 | pub fn bin_op_to_fcmp_predicate(op: hir::BinOp_) -> llvm::RealPredicate { |
85aaf69f | 144 | match op { |
e9174d1e SL |
145 | hir::BiEq => llvm::RealOEQ, |
146 | hir::BiNe => llvm::RealUNE, | |
147 | hir::BiLt => llvm::RealOLT, | |
148 | hir::BiLe => llvm::RealOLE, | |
149 | hir::BiGt => llvm::RealOGT, | |
150 | hir::BiGe => llvm::RealOGE, | |
85aaf69f | 151 | op => { |
54a0048b SL |
152 | bug!("comparison_op_to_fcmp_predicate: expected comparison operator, \ |
153 | found {:?}", | |
154 | op); | |
92a42be0 SL |
155 | } |
156 | } | |
157 | } | |
158 | ||
32a655c1 | 159 | pub fn compare_simd_types<'a, 'tcx>( |
2c00a5a8 | 160 | bx: &Builder<'a, 'tcx>, |
32a655c1 SL |
161 | lhs: ValueRef, |
162 | rhs: ValueRef, | |
163 | t: Ty<'tcx>, | |
164 | ret_ty: Type, | |
165 | op: hir::BinOp_ | |
166 | ) -> ValueRef { | |
85aaf69f | 167 | let signed = match t.sty { |
62682a34 | 168 | ty::TyFloat(_) => { |
54a0048b | 169 | let cmp = bin_op_to_fcmp_predicate(op); |
2c00a5a8 | 170 | return bx.sext(bx.fcmp(cmp, lhs, rhs), ret_ty); |
1a4d82fc | 171 | }, |
62682a34 SL |
172 | ty::TyUint(_) => false, |
173 | ty::TyInt(_) => true, | |
54a0048b | 174 | _ => bug!("compare_simd_types: invalid SIMD type"), |
85aaf69f SL |
175 | }; |
176 | ||
54a0048b | 177 | let cmp = bin_op_to_icmp_predicate(op, signed); |
85aaf69f SL |
178 | // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension |
179 | // to get the correctly sized type. This will compile to a single instruction | |
180 | // once the IR is converted to assembly if the SIMD instruction is supported | |
181 | // by the target architecture. | |
2c00a5a8 | 182 | bx.sext(bx.icmp(cmp, lhs, rhs), ret_ty) |
1a4d82fc JJ |
183 | } |
184 | ||
92a42be0 SL |
185 | /// Retrieve the information we are losing (making dynamic) in an unsizing |
186 | /// adjustment. | |
187 | /// | |
188 | /// The `old_info` argument is a bit funny. It is intended for use | |
3b2f2976 | 189 | /// in an upcast, where the new vtable for an object will be derived |
92a42be0 | 190 | /// from the old one. |
2c00a5a8 | 191 | pub fn unsized_info<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, |
92a42be0 SL |
192 | source: Ty<'tcx>, |
193 | target: Ty<'tcx>, | |
54a0048b | 194 | old_info: Option<ValueRef>) |
92a42be0 | 195 | -> ValueRef { |
2c00a5a8 | 196 | let (source, target) = cx.tcx.struct_lockstep_tails(source, target); |
92a42be0 | 197 | match (&source.sty, &target.sty) { |
ea8adc8c | 198 | (&ty::TyArray(_, len), &ty::TySlice(_)) => { |
2c00a5a8 | 199 | C_usize(cx, len.val.to_const_int().unwrap().to_u64().unwrap()) |
ea8adc8c | 200 | } |
476ff2be | 201 | (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { |
92a42be0 SL |
202 | // For now, upcasts are limited to changes in marker |
203 | // traits, and hence never actually require an actual | |
204 | // change to the vtable. | |
205 | old_info.expect("unsized_info: missing old info for trait upcast") | |
206 | } | |
476ff2be | 207 | (_, &ty::TyDynamic(ref data, ..)) => { |
2c00a5a8 XL |
208 | let vtable_ptr = cx.layout_of(cx.tcx.mk_mut_ptr(target)) |
209 | .field(cx, abi::FAT_PTR_EXTRA); | |
210 | consts::ptrcast(meth::get_vtable(cx, source, data.principal()), | |
211 | vtable_ptr.llvm_type(cx)) | |
92a42be0 | 212 | } |
54a0048b | 213 | _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", |
92a42be0 | 214 | source, |
54a0048b | 215 | target), |
92a42be0 SL |
216 | } |
217 | } | |
218 | ||
219 | /// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. | |
32a655c1 | 220 | pub fn unsize_thin_ptr<'a, 'tcx>( |
2c00a5a8 | 221 | bx: &Builder<'a, 'tcx>, |
32a655c1 SL |
222 | src: ValueRef, |
223 | src_ty: Ty<'tcx>, | |
224 | dst_ty: Ty<'tcx> | |
225 | ) -> (ValueRef, ValueRef) { | |
92a42be0 SL |
226 | debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); |
227 | match (&src_ty.sty, &dst_ty.sty) { | |
92a42be0 SL |
228 | (&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }), |
229 | &ty::TyRef(_, ty::TypeAndMut { ty: b, .. })) | | |
230 | (&ty::TyRef(_, ty::TypeAndMut { ty: a, .. }), | |
231 | &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) | | |
232 | (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), | |
233 | &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { | |
2c00a5a8 XL |
234 | assert!(bx.cx.type_is_sized(a)); |
235 | let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); | |
236 | (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) | |
32a655c1 SL |
237 | } |
238 | (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { | |
239 | let (a, b) = (src_ty.boxed_ty(), dst_ty.boxed_ty()); | |
2c00a5a8 XL |
240 | assert!(bx.cx.type_is_sized(a)); |
241 | let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); | |
242 | (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) | |
92a42be0 | 243 | } |
ff7c6d11 XL |
244 | (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { |
245 | assert_eq!(def_a, def_b); | |
246 | ||
2c00a5a8 XL |
247 | let src_layout = bx.cx.layout_of(src_ty); |
248 | let dst_layout = bx.cx.layout_of(dst_ty); | |
ff7c6d11 XL |
249 | let mut result = None; |
250 | for i in 0..src_layout.fields.count() { | |
2c00a5a8 | 251 | let src_f = src_layout.field(bx.cx, i); |
ff7c6d11 XL |
252 | assert_eq!(src_layout.fields.offset(i).bytes(), 0); |
253 | assert_eq!(dst_layout.fields.offset(i).bytes(), 0); | |
254 | if src_f.is_zst() { | |
255 | continue; | |
256 | } | |
257 | assert_eq!(src_layout.size, src_f.size); | |
258 | ||
2c00a5a8 | 259 | let dst_f = dst_layout.field(bx.cx, i); |
ff7c6d11 XL |
260 | assert_ne!(src_f.ty, dst_f.ty); |
261 | assert_eq!(result, None); | |
2c00a5a8 | 262 | result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty)); |
ff7c6d11 XL |
263 | } |
264 | let (lldata, llextra) = result.unwrap(); | |
265 | // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. | |
2c00a5a8 XL |
266 | (bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0)), |
267 | bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1))) | |
ff7c6d11 | 268 | } |
54a0048b | 269 | _ => bug!("unsize_thin_ptr: called on bad types"), |
92a42be0 SL |
270 | } |
271 | } | |
272 | ||
273 | /// Coerce `src`, which is a reference to a value of type `src_ty`, | |
274 | /// to a value of type `dst_ty` and store the result in `dst` | |
2c00a5a8 | 275 | pub fn coerce_unsized_into<'a, 'tcx>(bx: &Builder<'a, 'tcx>, |
ff7c6d11 XL |
276 | src: PlaceRef<'tcx>, |
277 | dst: PlaceRef<'tcx>) { | |
278 | let src_ty = src.layout.ty; | |
279 | let dst_ty = dst.layout.ty; | |
32a655c1 | 280 | let coerce_ptr = || { |
2c00a5a8 | 281 | let (base, info) = match src.load(bx).val { |
ff7c6d11 XL |
282 | OperandValue::Pair(base, info) => { |
283 | // fat-ptr to fat-ptr unsize preserves the vtable | |
284 | // i.e. &'a fmt::Debug+Send => &'a fmt::Debug | |
285 | // So we need to pointercast the base to ensure | |
286 | // the types match up. | |
2c00a5a8 XL |
287 | let thin_ptr = dst.layout.field(bx.cx, abi::FAT_PTR_ADDR); |
288 | (bx.pointercast(base, thin_ptr.llvm_type(bx.cx)), info) | |
ff7c6d11 XL |
289 | } |
290 | OperandValue::Immediate(base) => { | |
2c00a5a8 | 291 | unsize_thin_ptr(bx, base, src_ty, dst_ty) |
ff7c6d11 XL |
292 | } |
293 | OperandValue::Ref(..) => bug!() | |
32a655c1 | 294 | }; |
2c00a5a8 | 295 | OperandValue::Pair(base, info).store(bx, dst); |
32a655c1 | 296 | }; |
92a42be0 | 297 | match (&src_ty.sty, &dst_ty.sty) { |
92a42be0 SL |
298 | (&ty::TyRef(..), &ty::TyRef(..)) | |
299 | (&ty::TyRef(..), &ty::TyRawPtr(..)) | | |
300 | (&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => { | |
32a655c1 SL |
301 | coerce_ptr() |
302 | } | |
303 | (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { | |
304 | coerce_ptr() | |
92a42be0 SL |
305 | } |
306 | ||
ff7c6d11 | 307 | (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { |
92a42be0 SL |
308 | assert_eq!(def_a, def_b); |
309 | ||
ff7c6d11 | 310 | for i in 0..def_a.variants[0].fields.len() { |
2c00a5a8 XL |
311 | let src_f = src.project_field(bx, i); |
312 | let dst_f = dst.project_field(bx, i); | |
92a42be0 | 313 | |
ff7c6d11 | 314 | if dst_f.layout.is_zst() { |
92a42be0 SL |
315 | continue; |
316 | } | |
317 | ||
ff7c6d11 | 318 | if src_f.layout.ty == dst_f.layout.ty { |
2c00a5a8 | 319 | memcpy_ty(bx, dst_f.llval, src_f.llval, src_f.layout, |
ff7c6d11 | 320 | src_f.align.min(dst_f.align)); |
92a42be0 | 321 | } else { |
2c00a5a8 | 322 | coerce_unsized_into(bx, src_f, dst_f); |
92a42be0 SL |
323 | } |
324 | } | |
325 | } | |
54a0048b SL |
326 | _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", |
327 | src_ty, | |
328 | dst_ty), | |
92a42be0 | 329 | } |
1a4d82fc JJ |
330 | } |
331 | ||
32a655c1 SL |
332 | pub fn cast_shift_expr_rhs( |
333 | cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef | |
334 | ) -> ValueRef { | |
335 | cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) | |
92a42be0 SL |
336 | } |
337 | ||
338 | pub fn cast_shift_const_rhs(op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef) -> ValueRef { | |
339 | cast_shift_rhs(op, | |
340 | lhs, | |
341 | rhs, | |
1a4d82fc JJ |
342 | |a, b| unsafe { llvm::LLVMConstTrunc(a, b.to_ref()) }, |
343 | |a, b| unsafe { llvm::LLVMConstZExt(a, b.to_ref()) }) | |
344 | } | |
345 | ||
e9174d1e | 346 | fn cast_shift_rhs<F, G>(op: hir::BinOp_, |
c34b1796 AL |
347 | lhs: ValueRef, |
348 | rhs: ValueRef, | |
349 | trunc: F, | |
350 | zext: G) | |
92a42be0 SL |
351 | -> ValueRef |
352 | where F: FnOnce(ValueRef, Type) -> ValueRef, | |
353 | G: FnOnce(ValueRef, Type) -> ValueRef | |
1a4d82fc JJ |
354 | { |
355 | // Shifts may have any size int on the rhs | |
54a0048b | 356 | if op.is_shift() { |
85aaf69f SL |
357 | let mut rhs_llty = val_ty(rhs); |
358 | let mut lhs_llty = val_ty(lhs); | |
92a42be0 SL |
359 | if rhs_llty.kind() == Vector { |
360 | rhs_llty = rhs_llty.element_type() | |
361 | } | |
362 | if lhs_llty.kind() == Vector { | |
363 | lhs_llty = lhs_llty.element_type() | |
364 | } | |
85aaf69f SL |
365 | let rhs_sz = rhs_llty.int_width(); |
366 | let lhs_sz = lhs_llty.int_width(); | |
367 | if lhs_sz < rhs_sz { | |
368 | trunc(rhs, lhs_llty) | |
369 | } else if lhs_sz > rhs_sz { | |
370 | // FIXME (#1877: If shifting by negative | |
371 | // values becomes not undefined then this is wrong. | |
372 | zext(rhs, lhs_llty) | |
1a4d82fc JJ |
373 | } else { |
374 | rhs | |
375 | } | |
85aaf69f SL |
376 | } else { |
377 | rhs | |
1a4d82fc JJ |
378 | } |
379 | } | |
380 | ||
e9174d1e SL |
381 | /// Returns whether this session's target will use SEH-based unwinding. |
382 | /// | |
383 | /// This is only true for MSVC targets, and even then the 64-bit MSVC target | |
384 | /// currently uses SEH-ish unwinding with DWARF info tables to the side (same as | |
385 | /// 64-bit MinGW) instead of "full SEH". | |
386 | pub fn wants_msvc_seh(sess: &Session) -> bool { | |
7453a54e | 387 | sess.target.target.options.is_like_msvc |
e9174d1e SL |
388 | } |
389 | ||
2c00a5a8 XL |
390 | pub fn call_assume<'a, 'tcx>(bx: &Builder<'a, 'tcx>, val: ValueRef) { |
391 | let assume_intrinsic = bx.cx.get_intrinsic("llvm.assume"); | |
392 | bx.call(assume_intrinsic, &[val], None); | |
c30ab7b3 SL |
393 | } |
394 | ||
2c00a5a8 XL |
395 | pub fn from_immediate(bx: &Builder, val: ValueRef) -> ValueRef { |
396 | if val_ty(val) == Type::i1(bx.cx) { | |
397 | bx.zext(val, Type::i8(bx.cx)) | |
c34b1796 AL |
398 | } else { |
399 | val | |
400 | } | |
401 | } | |
402 | ||
2c00a5a8 | 403 | pub fn to_immediate(bx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef { |
ff7c6d11 XL |
404 | if let layout::Abi::Scalar(ref scalar) = layout.abi { |
405 | if scalar.is_bool() { | |
2c00a5a8 | 406 | return bx.trunc(val, Type::i1(bx.cx)); |
32a655c1 | 407 | } |
92a42be0 | 408 | } |
ff7c6d11 | 409 | val |
92a42be0 SL |
410 | } |
411 | ||
2c00a5a8 | 412 | pub fn call_memcpy(bx: &Builder, |
ff7c6d11 XL |
413 | dst: ValueRef, |
414 | src: ValueRef, | |
415 | n_bytes: ValueRef, | |
416 | align: Align) { | |
2c00a5a8 XL |
417 | let cx = bx.cx; |
418 | let ptr_width = &cx.sess().target.target.target_pointer_width; | |
e9174d1e | 419 | let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width); |
2c00a5a8 XL |
420 | let memcpy = cx.get_intrinsic(&key); |
421 | let src_ptr = bx.pointercast(src, Type::i8p(cx)); | |
422 | let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); | |
423 | let size = bx.intcast(n_bytes, cx.isize_ty, false); | |
424 | let align = C_i32(cx, align.abi() as i32); | |
425 | let volatile = C_bool(cx, false); | |
426 | bx.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); | |
1a4d82fc JJ |
427 | } |
428 | ||
32a655c1 | 429 | pub fn memcpy_ty<'a, 'tcx>( |
2c00a5a8 | 430 | bx: &Builder<'a, 'tcx>, |
32a655c1 SL |
431 | dst: ValueRef, |
432 | src: ValueRef, | |
ff7c6d11 XL |
433 | layout: TyLayout<'tcx>, |
434 | align: Align, | |
32a655c1 | 435 | ) { |
ff7c6d11 | 436 | let size = layout.size.bytes(); |
cc61c64b | 437 | if size == 0 { |
92a42be0 SL |
438 | return; |
439 | } | |
1a4d82fc | 440 | |
2c00a5a8 | 441 | call_memcpy(bx, dst, src, C_usize(bx.cx, size), align); |
54a0048b SL |
442 | } |
443 | ||
2c00a5a8 | 444 | pub fn call_memset<'a, 'tcx>(bx: &Builder<'a, 'tcx>, |
cc61c64b XL |
445 | ptr: ValueRef, |
446 | fill_byte: ValueRef, | |
447 | size: ValueRef, | |
448 | align: ValueRef, | |
449 | volatile: bool) -> ValueRef { | |
2c00a5a8 | 450 | let ptr_width = &bx.cx.sess().target.target.target_pointer_width; |
54a0048b | 451 | let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); |
2c00a5a8 XL |
452 | let llintrinsicfn = bx.cx.get_intrinsic(&intrinsic_key); |
453 | let volatile = C_bool(bx.cx, volatile); | |
454 | bx.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None) | |
1a4d82fc JJ |
455 | } |
456 | ||
2c00a5a8 XL |
457 | pub fn trans_instance<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, instance: Instance<'tcx>) { |
458 | let _s = if cx.sess().trans_stats() { | |
476ff2be | 459 | let mut instance_name = String::new(); |
2c00a5a8 | 460 | DefPathBasedNames::new(cx.tcx, true, true) |
cc61c64b | 461 | .push_def_path(instance.def_id(), &mut instance_name); |
2c00a5a8 | 462 | Some(StatRecorder::new(cx, instance_name)) |
476ff2be SL |
463 | } else { |
464 | None | |
465 | }; | |
1a4d82fc | 466 | |
3157f602 XL |
467 | // this is an info! to allow collecting monomorphization statistics |
468 | // and to allow finding the last function before LLVM aborts from | |
469 | // release builds. | |
476ff2be | 470 | info!("trans_instance({})", instance); |
a7813a04 | 471 | |
2c00a5a8 XL |
472 | let fn_ty = instance.ty(cx.tcx); |
473 | let sig = common::ty_fn_sig(cx, fn_ty); | |
474 | let sig = cx.tcx.erase_late_bound_regions_and_normalize(&sig); | |
476ff2be | 475 | |
2c00a5a8 | 476 | let lldecl = match cx.instances.borrow().get(&instance) { |
476ff2be SL |
477 | Some(&val) => val, |
478 | None => bug!("Instance `{:?}` not already declared", instance) | |
479 | }; | |
480 | ||
2c00a5a8 | 481 | cx.stats.borrow_mut().n_closures += 1; |
476ff2be | 482 | |
cc61c64b XL |
483 | // The `uwtable` attribute according to LLVM is: |
484 | // | |
485 | // This attribute indicates that the ABI being targeted requires that an | |
486 | // unwind table entry be produced for this function even if we can show | |
487 | // that no exceptions passes by it. This is normally the case for the | |
488 | // ELF x86-64 abi, but it can be disabled for some compilation units. | |
489 | // | |
490 | // Typically when we're compiling with `-C panic=abort` (which implies this | |
491 | // `no_landing_pads` check) we don't need `uwtable` because we can't | |
492 | // generate any exceptions! On Windows, however, exceptions include other | |
493 | // events such as illegal instructions, segfaults, etc. This means that on | |
494 | // Windows we end up still needing the `uwtable` attribute even if the `-C | |
495 | // panic=abort` flag is passed. | |
496 | // | |
497 | // You can also find more info on why Windows is whitelisted here in: | |
498 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 | |
2c00a5a8 XL |
499 | if !cx.sess().no_landing_pads() || |
500 | cx.sess().target.target.options.is_like_windows { | |
476ff2be SL |
501 | attributes::emit_uwtable(lldecl, true); |
502 | } | |
503 | ||
2c00a5a8 XL |
504 | let mir = cx.tcx.instance_mir(instance.def); |
505 | mir::trans_mir(cx, lldecl, &mir, instance, sig); | |
1a4d82fc JJ |
506 | } |
507 | ||
2c00a5a8 | 508 | pub fn set_link_section(cx: &CodegenCx, |
5bcae85e SL |
509 | llval: ValueRef, |
510 | attrs: &[ast::Attribute]) { | |
511 | if let Some(sect) = attr::first_attr_value_str_by_name(attrs, "link_section") { | |
476ff2be | 512 | if contains_null(§.as_str()) { |
2c00a5a8 | 513 | cx.sess().fatal(&format!("Illegal null byte in link_section value: `{}`", §)); |
3157f602 XL |
514 | } |
515 | unsafe { | |
476ff2be | 516 | let buf = CString::new(sect.as_str().as_bytes()).unwrap(); |
3157f602 XL |
517 | llvm::LLVMSetSection(llval, buf.as_ptr()); |
518 | } | |
c1a9b12d SL |
519 | } |
520 | } | |
521 | ||
3b2f2976 | 522 | /// Create the `main` function which will initialize the rust runtime and call |
cc61c64b | 523 | /// users main function. |
2c00a5a8 XL |
524 | fn maybe_create_entry_wrapper(cx: &CodegenCx) { |
525 | let (main_def_id, span) = match *cx.sess().entry_fn.borrow() { | |
5bcae85e | 526 | Some((id, span)) => { |
2c00a5a8 | 527 | (cx.tcx.hir.local_def_id(id), span) |
1a4d82fc | 528 | } |
5bcae85e SL |
529 | None => return, |
530 | }; | |
54a0048b | 531 | |
2c00a5a8 | 532 | let instance = Instance::mono(cx.tcx, main_def_id); |
5bcae85e | 533 | |
2c00a5a8 | 534 | if !cx.codegen_unit.contains_item(&MonoItem::Fn(instance)) { |
5bcae85e SL |
535 | // We want to create the wrapper in the same codegen unit as Rust's main |
536 | // function. | |
537 | return; | |
1a4d82fc | 538 | } |
1a4d82fc | 539 | |
2c00a5a8 | 540 | let main_llfn = callee::get_fn(cx, instance); |
5bcae85e | 541 | |
2c00a5a8 | 542 | let et = cx.sess().entry_type.get().unwrap(); |
1a4d82fc | 543 | match et { |
2c00a5a8 XL |
544 | config::EntryMain => create_entry_fn(cx, span, main_llfn, main_def_id, true), |
545 | config::EntryStart => create_entry_fn(cx, span, main_llfn, main_def_id, false), | |
1a4d82fc JJ |
546 | config::EntryNone => {} // Do nothing. |
547 | } | |
548 | ||
2c00a5a8 | 549 | fn create_entry_fn<'cx>(cx: &'cx CodegenCx, |
9346a6ac | 550 | sp: Span, |
1a4d82fc | 551 | rust_main: ValueRef, |
ff7c6d11 | 552 | rust_main_def_id: DefId, |
1a4d82fc | 553 | use_start_lang_item: bool) { |
2c00a5a8 | 554 | let llfty = Type::func(&[Type::c_int(cx), Type::i8p(cx).ptr_to()], &Type::c_int(cx)); |
1a4d82fc | 555 | |
2c00a5a8 | 556 | let main_ret_ty = cx.tcx.fn_sig(rust_main_def_id).output(); |
ff7c6d11 XL |
557 | // Given that `main()` has no arguments, |
558 | // then its return type cannot have | |
559 | // late-bound regions, since late-bound | |
560 | // regions must appear in the argument | |
561 | // listing. | |
562 | let main_ret_ty = main_ret_ty.no_late_bound_regions().unwrap(); | |
563 | ||
2c00a5a8 | 564 | if declare::get_defined_value(cx, "main").is_some() { |
9346a6ac | 565 | // FIXME: We should be smart and show a better diagnostic here. |
2c00a5a8 | 566 | cx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times") |
9cc50fc6 SL |
567 | .help("did you use #[no_mangle] on `fn main`? Use #[start] instead") |
568 | .emit(); | |
2c00a5a8 | 569 | cx.sess().abort_if_errors(); |
54a0048b SL |
570 | bug!(); |
571 | } | |
2c00a5a8 | 572 | let llfn = declare::declare_cfn(cx, "main", llfty); |
1a4d82fc | 573 | |
c30ab7b3 | 574 | // `main` should respect same config for frame pointer elimination as rest of code |
2c00a5a8 | 575 | attributes::set_frame_pointer_elimination(cx, llfn); |
c30ab7b3 | 576 | |
2c00a5a8 | 577 | let bx = Builder::new_block(cx, llfn, "top"); |
1a4d82fc | 578 | |
2c00a5a8 | 579 | debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(&bx); |
1a4d82fc | 580 | |
ea8adc8c XL |
581 | // Params from native main() used as args for rust start function |
582 | let param_argc = get_param(llfn, 0); | |
583 | let param_argv = get_param(llfn, 1); | |
2c00a5a8 | 584 | let arg_argc = bx.intcast(param_argc, cx.isize_ty, true); |
ea8adc8c XL |
585 | let arg_argv = param_argv; |
586 | ||
32a655c1 | 587 | let (start_fn, args) = if use_start_lang_item { |
2c00a5a8 XL |
588 | let start_def_id = cx.tcx.require_lang_item(StartFnLangItem); |
589 | let start_fn = callee::resolve_and_get_fn(cx, start_def_id, cx.tcx.mk_substs( | |
ff7c6d11 | 590 | iter::once(Kind::from(main_ret_ty)))); |
2c00a5a8 | 591 | (start_fn, vec![bx.pointercast(rust_main, Type::i8p(cx).ptr_to()), |
ea8adc8c | 592 | arg_argc, arg_argv]) |
32a655c1 SL |
593 | } else { |
594 | debug!("using user-defined start fn"); | |
ea8adc8c | 595 | (rust_main, vec![arg_argc, arg_argv]) |
32a655c1 | 596 | }; |
1a4d82fc | 597 | |
2c00a5a8 XL |
598 | let result = bx.call(start_fn, &args, None); |
599 | bx.ret(bx.intcast(result, Type::c_int(cx), true)); | |
1a4d82fc JJ |
600 | } |
601 | } | |
602 | ||
54a0048b SL |
603 | fn contains_null(s: &str) -> bool { |
604 | s.bytes().any(|b| b == 0) | |
1a4d82fc JJ |
605 | } |
606 | ||
cc61c64b | 607 | fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, |
ea8adc8c | 608 | llmod_id: &str, |
cc61c64b XL |
609 | link_meta: &LinkMeta, |
610 | exported_symbols: &NodeSet) | |
ff7c6d11 | 611 | -> (ContextRef, ModuleRef, EncodedMetadata) { |
041b39d2 XL |
612 | use std::io::Write; |
613 | use flate2::Compression; | |
614 | use flate2::write::DeflateEncoder; | |
1a4d82fc | 615 | |
cc61c64b | 616 | let (metadata_llcx, metadata_llmod) = unsafe { |
ea8adc8c | 617 | context::create_context_and_module(tcx.sess, llmod_id) |
cc61c64b XL |
618 | }; |
619 | ||
c30ab7b3 SL |
620 | #[derive(PartialEq, Eq, PartialOrd, Ord)] |
621 | enum MetadataKind { | |
622 | None, | |
623 | Uncompressed, | |
624 | Compressed | |
625 | } | |
626 | ||
cc61c64b | 627 | let kind = tcx.sess.crate_types.borrow().iter().map(|ty| { |
c30ab7b3 SL |
628 | match *ty { |
629 | config::CrateTypeExecutable | | |
630 | config::CrateTypeStaticlib | | |
631 | config::CrateTypeCdylib => MetadataKind::None, | |
632 | ||
32a655c1 | 633 | config::CrateTypeRlib => MetadataKind::Uncompressed, |
c30ab7b3 SL |
634 | |
635 | config::CrateTypeDylib | | |
636 | config::CrateTypeProcMacro => MetadataKind::Compressed, | |
637 | } | |
638 | }).max().unwrap(); | |
639 | ||
640 | if kind == MetadataKind::None { | |
3b2f2976 XL |
641 | return (metadata_llcx, |
642 | metadata_llmod, | |
ff7c6d11 | 643 | EncodedMetadata::new()); |
1a4d82fc JJ |
644 | } |
645 | ||
ff7c6d11 | 646 | let metadata = tcx.encode_metadata(link_meta, exported_symbols); |
c30ab7b3 | 647 | if kind == MetadataKind::Uncompressed { |
ff7c6d11 | 648 | return (metadata_llcx, metadata_llmod, metadata); |
c30ab7b3 SL |
649 | } |
650 | ||
651 | assert!(kind == MetadataKind::Compressed); | |
ea8adc8c | 652 | let mut compressed = tcx.metadata_encoding_version(); |
ff7c6d11 | 653 | DeflateEncoder::new(&mut compressed, Compression::fast()) |
041b39d2 | 654 | .write_all(&metadata.raw_data).unwrap(); |
1a4d82fc | 655 | |
cc61c64b XL |
656 | let llmeta = C_bytes_in_context(metadata_llcx, &compressed); |
657 | let llconst = C_struct_in_context(metadata_llcx, &[llmeta], false); | |
658 | let name = symbol_export::metadata_symbol_name(tcx); | |
85aaf69f | 659 | let buf = CString::new(name).unwrap(); |
1a4d82fc | 660 | let llglobal = unsafe { |
cc61c64b | 661 | llvm::LLVMAddGlobal(metadata_llmod, val_ty(llconst).to_ref(), buf.as_ptr()) |
1a4d82fc JJ |
662 | }; |
663 | unsafe { | |
664 | llvm::LLVMSetInitializer(llglobal, llconst); | |
7cac9316 | 665 | let section_name = metadata::metadata_section_name(&tcx.sess.target.target); |
5bcae85e SL |
666 | let name = CString::new(section_name).unwrap(); |
667 | llvm::LLVMSetSection(llglobal, name.as_ptr()); | |
668 | ||
669 | // Also generate a .section directive to force no | |
670 | // flags, at least for ELF outputs, so that the | |
671 | // metadata doesn't get loaded into memory. | |
672 | let directive = format!(".section {}", section_name); | |
673 | let directive = CString::new(directive).unwrap(); | |
cc61c64b | 674 | llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) |
1a4d82fc | 675 | } |
ff7c6d11 | 676 | return (metadata_llcx, metadata_llmod, metadata); |
e9174d1e SL |
677 | } |
678 | ||
abe05a73 | 679 | pub struct ValueIter { |
e9174d1e SL |
680 | cur: ValueRef, |
681 | step: unsafe extern "C" fn(ValueRef) -> ValueRef, | |
682 | } | |
683 | ||
684 | impl Iterator for ValueIter { | |
685 | type Item = ValueRef; | |
1a4d82fc | 686 | |
e9174d1e SL |
687 | fn next(&mut self) -> Option<ValueRef> { |
688 | let old = self.cur; | |
689 | if !old.is_null() { | |
b039eaaf | 690 | self.cur = unsafe { (self.step)(old) }; |
e9174d1e SL |
691 | Some(old) |
692 | } else { | |
693 | None | |
694 | } | |
1a4d82fc | 695 | } |
e9174d1e | 696 | } |
1a4d82fc | 697 | |
abe05a73 | 698 | pub fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter { |
e9174d1e SL |
699 | unsafe { |
700 | ValueIter { | |
701 | cur: llvm::LLVMGetFirstGlobal(llmod), | |
702 | step: llvm::LLVMGetNextGlobal, | |
703 | } | |
704 | } | |
705 | } | |
1a4d82fc | 706 | |
ea8adc8c XL |
707 | pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, |
708 | rx: mpsc::Receiver<Box<Any + Send>>) | |
709 | -> OngoingCrateTranslation { | |
3157f602 | 710 | |
ea8adc8c | 711 | check_for_rustc_errors_attr(tcx); |
3157f602 | 712 | |
ff7c6d11 | 713 | if let Some(true) = tcx.sess.opts.debugging_opts.thinlto { |
ea8adc8c XL |
714 | if unsafe { !llvm::LLVMRustThinLTOAvailable() } { |
715 | tcx.sess.fatal("this compiler's LLVM does not support ThinLTO"); | |
e9174d1e | 716 | } |
ea8adc8c | 717 | } |
e9174d1e | 718 | |
ff7c6d11 | 719 | let crate_hash = tcx.crate_hash(LOCAL_CRATE); |
ea8adc8c XL |
720 | let link_meta = link::build_link_meta(crate_hash); |
721 | let exported_symbol_node_ids = find_exported_symbols(tcx); | |
3b2f2976 | 722 | |
3157f602 | 723 | // Translate the metadata. |
ea8adc8c | 724 | let llmod_id = "metadata"; |
ff7c6d11 | 725 | let (metadata_llcx, metadata_llmod, metadata) = |
cc61c64b | 726 | time(tcx.sess.time_passes(), "write metadata", || { |
ea8adc8c | 727 | write_metadata(tcx, llmod_id, &link_meta, &exported_symbol_node_ids) |
cc61c64b | 728 | }); |
3157f602 XL |
729 | |
730 | let metadata_module = ModuleTranslation { | |
8bb4bdeb | 731 | name: link::METADATA_MODULE_NAME.to_string(), |
ea8adc8c | 732 | llmod_id: llmod_id.to_string(), |
5bcae85e | 733 | source: ModuleSource::Translated(ModuleLlvm { |
cc61c64b XL |
734 | llcx: metadata_llcx, |
735 | llmod: metadata_llmod, | |
ea8adc8c | 736 | tm: create_target_machine(tcx.sess), |
5bcae85e | 737 | }), |
3b2f2976 | 738 | kind: ModuleKind::Metadata, |
3157f602 | 739 | }; |
041b39d2 | 740 | |
3b2f2976 XL |
741 | let time_graph = if tcx.sess.opts.debugging_opts.trans_time_graph { |
742 | Some(time_graph::TimeGraph::new()) | |
743 | } else { | |
744 | None | |
745 | }; | |
041b39d2 | 746 | |
32a655c1 SL |
747 | // Skip crate items and just output metadata in -Z no-trans mode. |
748 | if tcx.sess.opts.debugging_opts.no_trans || | |
749 | !tcx.sess.opts.output_types.should_trans() { | |
3b2f2976 | 750 | let ongoing_translation = write::start_async_translation( |
ea8adc8c | 751 | tcx, |
3b2f2976 | 752 | time_graph.clone(), |
3b2f2976 XL |
753 | link_meta, |
754 | metadata, | |
ea8adc8c XL |
755 | rx, |
756 | 1); | |
3b2f2976 | 757 | |
ea8adc8c XL |
758 | ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); |
759 | ongoing_translation.translation_finished(tcx); | |
3b2f2976 | 760 | |
ff7c6d11 | 761 | assert_and_save_dep_graph(tcx); |
3b2f2976 XL |
762 | |
763 | ongoing_translation.check_for_errors(tcx.sess); | |
764 | ||
765 | return ongoing_translation; | |
32a655c1 SL |
766 | } |
767 | ||
5bcae85e SL |
768 | // Run the translation item collector and partition the collected items into |
769 | // codegen units. | |
ea8adc8c | 770 | let codegen_units = |
2c00a5a8 | 771 | tcx.collect_and_partition_translation_items(LOCAL_CRATE).1; |
ea8adc8c XL |
772 | let codegen_units = (*codegen_units).clone(); |
773 | ||
774 | // Force all codegen_unit queries so they are already either red or green | |
775 | // when compile_codegen_unit accesses them. We are not able to re-execute | |
776 | // the codegen_unit query from just the DepNode, so an unknown color would | |
777 | // lead to having to re-execute compile_codegen_unit, possibly | |
778 | // unnecessarily. | |
779 | if tcx.dep_graph.is_fully_enabled() { | |
780 | for cgu in &codegen_units { | |
781 | tcx.codegen_unit(cgu.name().clone()); | |
3b2f2976 | 782 | } |
ea8adc8c | 783 | } |
3b2f2976 XL |
784 | |
785 | let ongoing_translation = write::start_async_translation( | |
ea8adc8c | 786 | tcx, |
3b2f2976 | 787 | time_graph.clone(), |
3b2f2976 XL |
788 | link_meta, |
789 | metadata, | |
ea8adc8c XL |
790 | rx, |
791 | codegen_units.len()); | |
3b2f2976 XL |
792 | |
793 | // Translate an allocator shim, if any | |
ea8adc8c | 794 | let allocator_module = if let Some(kind) = tcx.sess.allocator_kind.get() { |
3b2f2976 | 795 | unsafe { |
ea8adc8c | 796 | let llmod_id = "allocator"; |
3b2f2976 | 797 | let (llcx, llmod) = |
ea8adc8c | 798 | context::create_context_and_module(tcx.sess, llmod_id); |
3b2f2976 XL |
799 | let modules = ModuleLlvm { |
800 | llmod, | |
801 | llcx, | |
ea8adc8c | 802 | tm: create_target_machine(tcx.sess), |
3b2f2976 XL |
803 | }; |
804 | time(tcx.sess.time_passes(), "write allocator module", || { | |
805 | allocator::trans(tcx, &modules, kind) | |
806 | }); | |
807 | ||
808 | Some(ModuleTranslation { | |
809 | name: link::ALLOCATOR_MODULE_NAME.to_string(), | |
ea8adc8c | 810 | llmod_id: llmod_id.to_string(), |
3b2f2976 XL |
811 | source: ModuleSource::Translated(modules), |
812 | kind: ModuleKind::Allocator, | |
813 | }) | |
814 | } | |
815 | } else { | |
816 | None | |
817 | }; | |
818 | ||
819 | if let Some(allocator_module) = allocator_module { | |
ea8adc8c | 820 | ongoing_translation.submit_pre_translated_module_to_llvm(tcx, allocator_module); |
3b2f2976 XL |
821 | } |
822 | ||
ea8adc8c | 823 | ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); |
3b2f2976 XL |
824 | |
825 | // We sort the codegen units by size. This way we can schedule work for LLVM | |
2c00a5a8 | 826 | // a bit more efficiently. |
3b2f2976 XL |
827 | let codegen_units = { |
828 | let mut codegen_units = codegen_units; | |
2c00a5a8 | 829 | codegen_units.sort_by_key(|cgu| usize::MAX - cgu.size_estimate()); |
3b2f2976 XL |
830 | codegen_units |
831 | }; | |
832 | ||
833 | let mut total_trans_time = Duration::new(0, 0); | |
ea8adc8c | 834 | let mut all_stats = Stats::default(); |
3b2f2976 | 835 | |
ea8adc8c | 836 | for cgu in codegen_units.into_iter() { |
3b2f2976 XL |
837 | ongoing_translation.wait_for_signal_to_translate_item(); |
838 | ongoing_translation.check_for_errors(tcx.sess); | |
839 | ||
ea8adc8c XL |
840 | // First, if incremental compilation is enabled, we try to re-use the |
841 | // codegen unit from the cache. | |
842 | if tcx.dep_graph.is_fully_enabled() { | |
843 | let cgu_id = cgu.work_product_id(); | |
844 | ||
845 | // Check whether there is a previous work-product we can | |
846 | // re-use. Not only must the file exist, and the inputs not | |
847 | // be dirty, but the hash of the symbols we will generate must | |
848 | // be the same. | |
849 | if let Some(buf) = tcx.dep_graph.previous_work_product(&cgu_id) { | |
850 | let dep_node = &DepNode::new(tcx, | |
851 | DepConstructor::CompileCodegenUnit(cgu.name().clone())); | |
852 | ||
853 | // We try to mark the DepNode::CompileCodegenUnit green. If we | |
854 | // succeed it means that none of the dependencies has changed | |
855 | // and we can safely re-use. | |
856 | if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, dep_node) { | |
857 | // Append ".rs" to LLVM module identifier. | |
858 | // | |
859 | // LLVM code generator emits a ".file filename" directive | |
860 | // for ELF backends. Value of the "filename" is set as the | |
861 | // LLVM module identifier. Due to a LLVM MC bug[1], LLVM | |
862 | // crashes if the module identifier is same as other symbols | |
863 | // such as a function name in the module. | |
864 | // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 | |
865 | let llmod_id = format!("{}.rs", cgu.name()); | |
866 | ||
867 | let module = ModuleTranslation { | |
868 | name: cgu.name().to_string(), | |
869 | source: ModuleSource::Preexisting(buf), | |
870 | kind: ModuleKind::Regular, | |
871 | llmod_id, | |
872 | }; | |
873 | tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true); | |
874 | write::submit_translated_module_to_llvm(tcx, module, 0); | |
875 | // Continue to next cgu, this one is done. | |
876 | continue | |
877 | } | |
878 | } else { | |
879 | // This can happen if files were deleted from the cache | |
880 | // directory for some reason. We just re-compile then. | |
3b2f2976 | 881 | } |
ea8adc8c | 882 | } |
3b2f2976 | 883 | |
ea8adc8c XL |
884 | let _timing_guard = time_graph.as_ref().map(|time_graph| { |
885 | time_graph.start(write::TRANS_WORKER_TIMELINE, | |
886 | write::TRANS_WORK_PACKAGE_KIND, | |
887 | &format!("codegen {}", cgu.name())) | |
888 | }); | |
889 | let start_time = Instant::now(); | |
890 | all_stats.extend(tcx.compile_codegen_unit(*cgu.name())); | |
891 | total_trans_time += start_time.elapsed(); | |
3b2f2976 XL |
892 | ongoing_translation.check_for_errors(tcx.sess); |
893 | } | |
894 | ||
ea8adc8c XL |
895 | ongoing_translation.translation_finished(tcx); |
896 | ||
3b2f2976 XL |
897 | // Since the main thread is sometimes blocked during trans, we keep track |
898 | // -Ztime-passes output manually. | |
899 | print_time_passes_entry(tcx.sess.time_passes(), | |
900 | "translate to LLVM IR", | |
901 | total_trans_time); | |
902 | ||
ea8adc8c | 903 | if tcx.sess.opts.incremental.is_some() { |
2c00a5a8 | 904 | ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); |
1a4d82fc JJ |
905 | } |
906 | ||
cc61c64b | 907 | symbol_names_test::report_symbol_names(tcx); |
5bcae85e | 908 | |
2c00a5a8 | 909 | if tcx.sess.trans_stats() { |
1a4d82fc | 910 | println!("--- trans stats ---"); |
ea8adc8c XL |
911 | println!("n_glues_created: {}", all_stats.n_glues_created); |
912 | println!("n_null_glues: {}", all_stats.n_null_glues); | |
913 | println!("n_real_glues: {}", all_stats.n_real_glues); | |
1a4d82fc | 914 | |
ea8adc8c XL |
915 | println!("n_fns: {}", all_stats.n_fns); |
916 | println!("n_inlines: {}", all_stats.n_inlines); | |
917 | println!("n_closures: {}", all_stats.n_closures); | |
1a4d82fc | 918 | println!("fn stats:"); |
ea8adc8c XL |
919 | all_stats.fn_stats.sort_by_key(|&(_, insns)| insns); |
920 | for &(ref name, insns) in all_stats.fn_stats.iter() { | |
921 | println!("{} insns, {}", insns, *name); | |
1a4d82fc JJ |
922 | } |
923 | } | |
5bcae85e | 924 | |
2c00a5a8 | 925 | if tcx.sess.count_llvm_insns() { |
ea8adc8c | 926 | for (k, v) in all_stats.llvm_insns.iter() { |
1a4d82fc JJ |
927 | println!("{:7} {}", *v, *k); |
928 | } | |
929 | } | |
930 | ||
3b2f2976 | 931 | ongoing_translation.check_for_errors(tcx.sess); |
041b39d2 | 932 | |
ff7c6d11 | 933 | assert_and_save_dep_graph(tcx); |
3b2f2976 XL |
934 | ongoing_translation |
935 | } | |
c30ab7b3 | 936 | |
ff7c6d11 | 937 | fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { |
3b2f2976 XL |
938 | time(tcx.sess.time_passes(), |
939 | "assert dep graph", | |
940 | || rustc_incremental::assert_dep_graph(tcx)); | |
941 | ||
942 | time(tcx.sess.time_passes(), | |
943 | "serialize dep graph", | |
ff7c6d11 | 944 | || rustc_incremental::save_dep_graph(tcx)); |
1a4d82fc | 945 | } |
92a42be0 | 946 | |
ea8adc8c XL |
947 | fn collect_and_partition_translation_items<'a, 'tcx>( |
948 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
949 | cnum: CrateNum, | |
950 | ) -> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>) | |
951 | { | |
952 | assert_eq!(cnum, LOCAL_CRATE); | |
953 | let time_passes = tcx.sess.time_passes(); | |
7453a54e | 954 | |
ea8adc8c | 955 | let collection_mode = match tcx.sess.opts.debugging_opts.print_trans_items { |
7453a54e SL |
956 | Some(ref s) => { |
957 | let mode_string = s.to_lowercase(); | |
958 | let mode_string = mode_string.trim(); | |
959 | if mode_string == "eager" { | |
ff7c6d11 | 960 | MonoItemCollectionMode::Eager |
7453a54e SL |
961 | } else { |
962 | if mode_string != "lazy" { | |
963 | let message = format!("Unknown codegen-item collection mode '{}'. \ | |
964 | Falling back to 'lazy' mode.", | |
965 | mode_string); | |
ea8adc8c | 966 | tcx.sess.warn(&message); |
7453a54e SL |
967 | } |
968 | ||
ff7c6d11 XL |
969 | MonoItemCollectionMode::Lazy |
970 | } | |
971 | } | |
972 | None => { | |
973 | if tcx.sess.opts.cg.link_dead_code { | |
974 | MonoItemCollectionMode::Eager | |
975 | } else { | |
976 | MonoItemCollectionMode::Lazy | |
7453a54e SL |
977 | } |
978 | } | |
7453a54e SL |
979 | }; |
980 | ||
5bcae85e SL |
981 | let (items, inlining_map) = |
982 | time(time_passes, "translation item collection", || { | |
ff7c6d11 | 983 | collector::collect_crate_mono_items(tcx, collection_mode) |
7453a54e SL |
984 | }); |
985 | ||
2c00a5a8 | 986 | ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter()); |
5bcae85e | 987 | |
ff7c6d11 | 988 | let strategy = if tcx.sess.opts.incremental.is_some() { |
a7813a04 XL |
989 | PartitioningStrategy::PerModule |
990 | } else { | |
abe05a73 | 991 | PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units()) |
a7813a04 XL |
992 | }; |
993 | ||
994 | let codegen_units = time(time_passes, "codegen unit partitioning", || { | |
ea8adc8c | 995 | partitioning::partition(tcx, |
a7813a04 XL |
996 | items.iter().cloned(), |
997 | strategy, | |
ea8adc8c XL |
998 | &inlining_map) |
999 | .into_iter() | |
1000 | .map(Arc::new) | |
1001 | .collect::<Vec<_>>() | |
a7813a04 XL |
1002 | }); |
1003 | ||
ea8adc8c XL |
1004 | let translation_items: DefIdSet = items.iter().filter_map(|trans_item| { |
1005 | match *trans_item { | |
ff7c6d11 | 1006 | MonoItem::Fn(ref instance) => Some(instance.def_id()), |
ea8adc8c XL |
1007 | _ => None, |
1008 | } | |
1009 | }).collect(); | |
5bcae85e | 1010 | |
ea8adc8c | 1011 | if tcx.sess.opts.debugging_opts.print_trans_items.is_some() { |
476ff2be | 1012 | let mut item_to_cgus = FxHashMap(); |
a7813a04 XL |
1013 | |
1014 | for cgu in &codegen_units { | |
5bcae85e | 1015 | for (&trans_item, &linkage) in cgu.items() { |
a7813a04 XL |
1016 | item_to_cgus.entry(trans_item) |
1017 | .or_insert(Vec::new()) | |
5bcae85e | 1018 | .push((cgu.name().clone(), linkage)); |
a7813a04 XL |
1019 | } |
1020 | } | |
1021 | ||
1022 | let mut item_keys: Vec<_> = items | |
1023 | .iter() | |
1024 | .map(|i| { | |
ea8adc8c | 1025 | let mut output = i.to_string(tcx); |
a7813a04 XL |
1026 | output.push_str(" @@"); |
1027 | let mut empty = Vec::new(); | |
3b2f2976 | 1028 | let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); |
a7813a04 XL |
1029 | cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone()); |
1030 | cgus.dedup(); | |
3b2f2976 | 1031 | for &(ref cgu_name, (linkage, _)) in cgus.iter() { |
a7813a04 | 1032 | output.push_str(" "); |
cc61c64b | 1033 | output.push_str(&cgu_name); |
a7813a04 XL |
1034 | |
1035 | let linkage_abbrev = match linkage { | |
ea8adc8c XL |
1036 | Linkage::External => "External", |
1037 | Linkage::AvailableExternally => "Available", | |
1038 | Linkage::LinkOnceAny => "OnceAny", | |
1039 | Linkage::LinkOnceODR => "OnceODR", | |
1040 | Linkage::WeakAny => "WeakAny", | |
1041 | Linkage::WeakODR => "WeakODR", | |
1042 | Linkage::Appending => "Appending", | |
1043 | Linkage::Internal => "Internal", | |
1044 | Linkage::Private => "Private", | |
1045 | Linkage::ExternalWeak => "ExternalWeak", | |
1046 | Linkage::Common => "Common", | |
a7813a04 XL |
1047 | }; |
1048 | ||
1049 | output.push_str("["); | |
1050 | output.push_str(linkage_abbrev); | |
1051 | output.push_str("]"); | |
1052 | } | |
1053 | output | |
1054 | }) | |
1055 | .collect(); | |
1056 | ||
7453a54e SL |
1057 | item_keys.sort(); |
1058 | ||
1059 | for item in item_keys { | |
1060 | println!("TRANS_ITEM {}", item); | |
1061 | } | |
5bcae85e | 1062 | } |
7453a54e | 1063 | |
ea8adc8c XL |
1064 | (Arc::new(translation_items), Arc::new(codegen_units)) |
1065 | } | |
1066 | ||
1067 | impl CrateInfo { | |
1068 | pub fn new(tcx: TyCtxt) -> CrateInfo { | |
1069 | let mut info = CrateInfo { | |
1070 | panic_runtime: None, | |
1071 | compiler_builtins: None, | |
1072 | profiler_runtime: None, | |
1073 | sanitizer_runtime: None, | |
1074 | is_no_builtins: FxHashSet(), | |
1075 | native_libraries: FxHashMap(), | |
1076 | used_libraries: tcx.native_libraries(LOCAL_CRATE), | |
1077 | link_args: tcx.link_args(LOCAL_CRATE), | |
1078 | crate_name: FxHashMap(), | |
1079 | used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), | |
1080 | used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), | |
1081 | used_crate_source: FxHashMap(), | |
1082 | }; | |
1083 | ||
1084 | for &cnum in tcx.crates().iter() { | |
1085 | info.native_libraries.insert(cnum, tcx.native_libraries(cnum)); | |
1086 | info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); | |
1087 | info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum)); | |
1088 | if tcx.is_panic_runtime(cnum) { | |
1089 | info.panic_runtime = Some(cnum); | |
1090 | } | |
1091 | if tcx.is_compiler_builtins(cnum) { | |
1092 | info.compiler_builtins = Some(cnum); | |
1093 | } | |
1094 | if tcx.is_profiler_runtime(cnum) { | |
1095 | info.profiler_runtime = Some(cnum); | |
1096 | } | |
1097 | if tcx.is_sanitizer_runtime(cnum) { | |
1098 | info.sanitizer_runtime = Some(cnum); | |
1099 | } | |
1100 | if tcx.is_no_builtins(cnum) { | |
1101 | info.is_no_builtins.insert(cnum); | |
1102 | } | |
1103 | } | |
1104 | ||
1105 | ||
1106 | return info | |
1107 | } | |
1108 | } | |
1109 | ||
1110 | fn is_translated_function(tcx: TyCtxt, id: DefId) -> bool { | |
1111 | let (all_trans_items, _) = | |
1112 | tcx.collect_and_partition_translation_items(LOCAL_CRATE); | |
1113 | all_trans_items.contains(&id) | |
1114 | } | |
1115 | ||
1116 | fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1117 | cgu: InternedString) -> Stats { | |
1118 | let cgu = tcx.codegen_unit(cgu); | |
1119 | ||
1120 | let start_time = Instant::now(); | |
1121 | let (stats, module) = module_translation(tcx, cgu); | |
1122 | let time_to_translate = start_time.elapsed(); | |
1123 | ||
1124 | // We assume that the cost to run LLVM on a CGU is proportional to | |
1125 | // the time we needed for translating it. | |
1126 | let cost = time_to_translate.as_secs() * 1_000_000_000 + | |
1127 | time_to_translate.subsec_nanos() as u64; | |
1128 | ||
1129 | write::submit_translated_module_to_llvm(tcx, | |
1130 | module, | |
1131 | cost); | |
1132 | return stats; | |
1133 | ||
1134 | fn module_translation<'a, 'tcx>( | |
1135 | tcx: TyCtxt<'a, 'tcx, 'tcx>, | |
1136 | cgu: Arc<CodegenUnit<'tcx>>) | |
1137 | -> (Stats, ModuleTranslation) | |
1138 | { | |
1139 | let cgu_name = cgu.name().to_string(); | |
1140 | ||
1141 | // Append ".rs" to LLVM module identifier. | |
1142 | // | |
1143 | // LLVM code generator emits a ".file filename" directive | |
1144 | // for ELF backends. Value of the "filename" is set as the | |
1145 | // LLVM module identifier. Due to a LLVM MC bug[1], LLVM | |
1146 | // crashes if the module identifier is same as other symbols | |
1147 | // such as a function name in the module. | |
1148 | // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 | |
1149 | let llmod_id = format!("{}-{}.rs", | |
1150 | cgu.name(), | |
abe05a73 XL |
1151 | tcx.crate_disambiguator(LOCAL_CRATE) |
1152 | .to_fingerprint().to_hex()); | |
ea8adc8c XL |
1153 | |
1154 | // Instantiate translation items without filling out definitions yet... | |
2c00a5a8 | 1155 | let cx = CodegenCx::new(tcx, cgu, &llmod_id); |
ea8adc8c | 1156 | let module = { |
2c00a5a8 XL |
1157 | let trans_items = cx.codegen_unit |
1158 | .items_in_deterministic_order(cx.tcx); | |
ea8adc8c | 1159 | for &(trans_item, (linkage, visibility)) in &trans_items { |
2c00a5a8 | 1160 | trans_item.predefine(&cx, linkage, visibility); |
ea8adc8c XL |
1161 | } |
1162 | ||
1163 | // ... and now that we have everything pre-defined, fill out those definitions. | |
1164 | for &(trans_item, _) in &trans_items { | |
2c00a5a8 | 1165 | trans_item.define(&cx); |
ea8adc8c XL |
1166 | } |
1167 | ||
1168 | // If this codegen unit contains the main function, also create the | |
1169 | // wrapper here | |
2c00a5a8 | 1170 | maybe_create_entry_wrapper(&cx); |
ea8adc8c XL |
1171 | |
1172 | // Run replace-all-uses-with for statics that need it | |
2c00a5a8 | 1173 | for &(old_g, new_g) in cx.statics_to_rauw.borrow().iter() { |
ea8adc8c XL |
1174 | unsafe { |
1175 | let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g)); | |
1176 | llvm::LLVMReplaceAllUsesWith(old_g, bitcast); | |
1177 | llvm::LLVMDeleteGlobal(old_g); | |
1178 | } | |
1179 | } | |
1180 | ||
1181 | // Create the llvm.used variable | |
1182 | // This variable has type [N x i8*] and is stored in the llvm.metadata section | |
2c00a5a8 | 1183 | if !cx.used_statics.borrow().is_empty() { |
ea8adc8c XL |
1184 | let name = CString::new("llvm.used").unwrap(); |
1185 | let section = CString::new("llvm.metadata").unwrap(); | |
2c00a5a8 | 1186 | let array = C_array(Type::i8(&cx).ptr_to(), &*cx.used_statics.borrow()); |
ea8adc8c XL |
1187 | |
1188 | unsafe { | |
2c00a5a8 | 1189 | let g = llvm::LLVMAddGlobal(cx.llmod, |
ea8adc8c XL |
1190 | val_ty(array).to_ref(), |
1191 | name.as_ptr()); | |
1192 | llvm::LLVMSetInitializer(g, array); | |
1193 | llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); | |
1194 | llvm::LLVMSetSection(g, section.as_ptr()); | |
1195 | } | |
1196 | } | |
1197 | ||
1198 | // Finalize debuginfo | |
2c00a5a8 XL |
1199 | if cx.sess().opts.debuginfo != NoDebugInfo { |
1200 | debuginfo::finalize(&cx); | |
ea8adc8c XL |
1201 | } |
1202 | ||
1203 | let llvm_module = ModuleLlvm { | |
2c00a5a8 XL |
1204 | llcx: cx.llcx, |
1205 | llmod: cx.llmod, | |
1206 | tm: create_target_machine(cx.sess()), | |
ea8adc8c XL |
1207 | }; |
1208 | ||
ea8adc8c XL |
1209 | ModuleTranslation { |
1210 | name: cgu_name, | |
1211 | source: ModuleSource::Translated(llvm_module), | |
1212 | kind: ModuleKind::Regular, | |
1213 | llmod_id, | |
1214 | } | |
1215 | }; | |
1216 | ||
2c00a5a8 | 1217 | (cx.into_stats(), module) |
ea8adc8c XL |
1218 | } |
1219 | } | |
1220 | ||
abe05a73 | 1221 | pub fn provide(providers: &mut Providers) { |
ea8adc8c XL |
1222 | providers.collect_and_partition_translation_items = |
1223 | collect_and_partition_translation_items; | |
1224 | ||
1225 | providers.is_translated_function = is_translated_function; | |
1226 | ||
1227 | providers.codegen_unit = |tcx, name| { | |
1228 | let (_, all) = tcx.collect_and_partition_translation_items(LOCAL_CRATE); | |
1229 | all.iter() | |
1230 | .find(|cgu| *cgu.name() == name) | |
1231 | .cloned() | |
1232 | .expect(&format!("failed to find cgu with name {:?}", name)) | |
1233 | }; | |
1234 | providers.compile_codegen_unit = compile_codegen_unit; | |
1235 | } | |
1236 | ||
ea8adc8c XL |
1237 | pub fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { |
1238 | match linkage { | |
1239 | Linkage::External => llvm::Linkage::ExternalLinkage, | |
1240 | Linkage::AvailableExternally => llvm::Linkage::AvailableExternallyLinkage, | |
1241 | Linkage::LinkOnceAny => llvm::Linkage::LinkOnceAnyLinkage, | |
1242 | Linkage::LinkOnceODR => llvm::Linkage::LinkOnceODRLinkage, | |
1243 | Linkage::WeakAny => llvm::Linkage::WeakAnyLinkage, | |
1244 | Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, | |
1245 | Linkage::Appending => llvm::Linkage::AppendingLinkage, | |
1246 | Linkage::Internal => llvm::Linkage::InternalLinkage, | |
1247 | Linkage::Private => llvm::Linkage::PrivateLinkage, | |
1248 | Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, | |
1249 | Linkage::Common => llvm::Linkage::CommonLinkage, | |
1250 | } | |
1251 | } | |
1252 | ||
1253 | pub fn visibility_to_llvm(linkage: Visibility) -> llvm::Visibility { | |
1254 | match linkage { | |
1255 | Visibility::Default => llvm::Visibility::Default, | |
1256 | Visibility::Hidden => llvm::Visibility::Hidden, | |
1257 | Visibility::Protected => llvm::Visibility::Protected, | |
1258 | } | |
1259 | } | |
1260 | ||
1261 | // FIXME(mw): Anything that is produced via DepGraph::with_task() must implement | |
1262 | // the HashStable trait. Normally DepGraph::with_task() calls are | |
1263 | // hidden behind queries, but CGU creation is a special case in two | |
1264 | // ways: (1) it's not a query and (2) CGU are output nodes, so their | |
1265 | // Fingerprints are not actually needed. It remains to be clarified | |
1266 | // how exactly this case will be handled in the red/green system but | |
1267 | // for now we content ourselves with providing a no-op HashStable | |
1268 | // implementation for CGUs. | |
1269 | mod temp_stable_hash_impls { | |
1270 | use rustc_data_structures::stable_hasher::{StableHasherResult, StableHasher, | |
1271 | HashStable}; | |
1272 | use ModuleTranslation; | |
1273 | ||
1274 | impl<HCX> HashStable<HCX> for ModuleTranslation { | |
1275 | fn hash_stable<W: StableHasherResult>(&self, | |
1276 | _: &mut HCX, | |
1277 | _: &mut StableHasher<W>) { | |
1278 | // do nothing | |
1279 | } | |
1280 | } | |
5bcae85e | 1281 | } |