]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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. | |
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 | ||
1a4d82fc | 12 | use llvm; |
5bcae85e | 13 | use llvm::{SetUnnamedAddr}; |
9e0c209e SL |
14 | use llvm::{ValueRef, True}; |
15 | use rustc_const_eval::ConstEvalErr; | |
54a0048b SL |
16 | use rustc::hir::def_id::DefId; |
17 | use rustc::hir::map as hir_map; | |
9e0c209e | 18 | use {debuginfo, machine}; |
3157f602 | 19 | use base::{self, push_ctxt}; |
a7813a04 | 20 | use trans_item::TransItem; |
9e0c209e | 21 | use common::{CrateContext, val_ty}; |
54a0048b | 22 | use declare; |
9e0c209e | 23 | use monomorphize::{Instance}; |
54a0048b SL |
24 | use type_::Type; |
25 | use type_of; | |
9e0c209e | 26 | use rustc::ty; |
1a4d82fc | 27 | |
54a0048b | 28 | use rustc::hir; |
e9174d1e | 29 | |
c1a9b12d | 30 | use std::ffi::{CStr, CString}; |
9e0c209e SL |
31 | use syntax::ast; |
32 | use syntax::attr; | |
1a4d82fc JJ |
33 | |
34 | pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef { | |
35 | unsafe { | |
36 | llvm::LLVMConstPointerCast(val, ty.to_ref()) | |
37 | } | |
38 | } | |
39 | ||
a7813a04 XL |
40 | pub fn addr_of_mut(ccx: &CrateContext, |
41 | cv: ValueRef, | |
42 | align: machine::llalign, | |
43 | kind: &str) | |
44 | -> ValueRef { | |
1a4d82fc | 45 | unsafe { |
c30ab7b3 | 46 | let name = ccx.generate_local_symbol_name(kind); |
9346a6ac | 47 | let gv = declare::define_global(ccx, &name[..], val_ty(cv)).unwrap_or_else(||{ |
54a0048b | 48 | bug!("symbol `{}` is already defined", name); |
9346a6ac | 49 | }); |
1a4d82fc | 50 | llvm::LLVMSetInitializer(gv, cv); |
b039eaaf | 51 | llvm::LLVMSetAlignment(gv, align); |
9e0c209e | 52 | llvm::LLVMRustSetLinkage(gv, llvm::Linkage::InternalLinkage); |
85aaf69f | 53 | SetUnnamedAddr(gv, true); |
1a4d82fc JJ |
54 | gv |
55 | } | |
56 | } | |
57 | ||
85aaf69f SL |
58 | pub fn addr_of(ccx: &CrateContext, |
59 | cv: ValueRef, | |
b039eaaf | 60 | align: machine::llalign, |
9346a6ac | 61 | kind: &str) |
85aaf69f | 62 | -> ValueRef { |
3157f602 XL |
63 | if let Some(&gv) = ccx.const_globals().borrow().get(&cv) { |
64 | unsafe { | |
65 | // Upgrade the alignment in cases where the same constant is used with different | |
66 | // alignment requirements | |
67 | if align > llvm::LLVMGetAlignment(gv) { | |
68 | llvm::LLVMSetAlignment(gv, align); | |
b039eaaf | 69 | } |
b039eaaf | 70 | } |
3157f602 | 71 | return gv; |
85aaf69f | 72 | } |
b039eaaf | 73 | let gv = addr_of_mut(ccx, cv, align, kind); |
85aaf69f SL |
74 | unsafe { |
75 | llvm::LLVMSetGlobalConstant(gv, True); | |
76 | } | |
77 | ccx.const_globals().borrow_mut().insert(cv, gv); | |
78 | gv | |
79 | } | |
80 | ||
9e0c209e | 81 | pub fn get_static(ccx: &CrateContext, def_id: DefId) -> ValueRef { |
a7813a04 | 82 | let instance = Instance::mono(ccx.shared(), def_id); |
54a0048b | 83 | if let Some(&g) = ccx.instances().borrow().get(&instance) { |
9e0c209e | 84 | return g; |
54a0048b SL |
85 | } |
86 | ||
9e0c209e | 87 | let ty = ccx.tcx().lookup_item_type(def_id).ty; |
54a0048b | 88 | let g = if let Some(id) = ccx.tcx().map.as_local_node_id(def_id) { |
5bcae85e | 89 | |
54a0048b | 90 | let llty = type_of::type_of(ccx, ty); |
5bcae85e | 91 | let (g, attrs) = match ccx.tcx().map.get(id) { |
54a0048b | 92 | hir_map::NodeItem(&hir::Item { |
5bcae85e | 93 | ref attrs, span, node: hir::ItemStatic(..), .. |
54a0048b | 94 | }) => { |
5bcae85e SL |
95 | let sym = ccx.symbol_map() |
96 | .get(TransItem::Static(id)) | |
97 | .expect("Local statics should always be in the SymbolMap"); | |
98 | // Make sure that this is never executed for something inlined. | |
99 | assert!(!ccx.tcx().map.is_inlined_node_id(id)); | |
100 | ||
101 | let defined_in_current_codegen_unit = ccx.codegen_unit() | |
102 | .items() | |
103 | .contains_key(&TransItem::Static(id)); | |
9e0c209e SL |
104 | assert!(!defined_in_current_codegen_unit); |
105 | ||
106 | if declare::get_declared_value(ccx, sym).is_some() { | |
107 | span_bug!(span, "trans: Conflicting symbol names for static?"); | |
5bcae85e SL |
108 | } |
109 | ||
110 | let g = declare::define_global(ccx, sym, llty).unwrap(); | |
111 | ||
112 | (g, attrs) | |
54a0048b SL |
113 | } |
114 | ||
115 | hir_map::NodeForeignItem(&hir::ForeignItem { | |
3157f602 | 116 | ref attrs, span, node: hir::ForeignItemStatic(..), .. |
54a0048b | 117 | }) => { |
5bcae85e | 118 | let sym = instance.symbol_name(ccx.shared()); |
54a0048b SL |
119 | let g = if let Some(name) = |
120 | attr::first_attr_value_str_by_name(&attrs, "linkage") { | |
121 | // If this is a static with a linkage specified, then we need to handle | |
122 | // it a little specially. The typesystem prevents things like &T and | |
123 | // extern "C" fn() from being non-null, so we can't just declare a | |
124 | // static and call it a day. Some linkages (like weak) will make it such | |
125 | // that the static actually has a null value. | |
126 | let linkage = match base::llvm_linkage_by_name(&name) { | |
127 | Some(linkage) => linkage, | |
128 | None => { | |
129 | ccx.sess().span_fatal(span, "invalid linkage specified"); | |
130 | } | |
131 | }; | |
132 | let llty2 = match ty.sty { | |
133 | ty::TyRawPtr(ref mt) => type_of::type_of(ccx, mt.ty), | |
134 | _ => { | |
135 | ccx.sess().span_fatal(span, "must have type `*const T` or `*mut T`"); | |
136 | } | |
137 | }; | |
138 | unsafe { | |
139 | // Declare a symbol `foo` with the desired linkage. | |
3157f602 | 140 | let g1 = declare::declare_global(ccx, &sym, llty2); |
9e0c209e | 141 | llvm::LLVMRustSetLinkage(g1, linkage); |
54a0048b SL |
142 | |
143 | // Declare an internal global `extern_with_linkage_foo` which | |
144 | // is initialized with the address of `foo`. If `foo` is | |
145 | // discarded during linking (for example, if `foo` has weak | |
146 | // linkage and there are no definitions), then | |
147 | // `extern_with_linkage_foo` will instead be initialized to | |
148 | // zero. | |
149 | let mut real_name = "_rust_extern_with_linkage_".to_string(); | |
3157f602 | 150 | real_name.push_str(&sym); |
54a0048b SL |
151 | let g2 = declare::define_global(ccx, &real_name, llty).unwrap_or_else(||{ |
152 | ccx.sess().span_fatal(span, | |
5bcae85e | 153 | &format!("symbol `{}` is already defined", &sym)) |
54a0048b | 154 | }); |
9e0c209e | 155 | llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage); |
54a0048b SL |
156 | llvm::LLVMSetInitializer(g2, g1); |
157 | g2 | |
158 | } | |
159 | } else { | |
160 | // Generate an external declaration. | |
3157f602 | 161 | declare::declare_global(ccx, &sym, llty) |
54a0048b SL |
162 | }; |
163 | ||
5bcae85e | 164 | (g, attrs) |
54a0048b SL |
165 | } |
166 | ||
167 | item => bug!("get_static: expected static, found {:?}", item) | |
5bcae85e SL |
168 | }; |
169 | ||
170 | for attr in attrs { | |
171 | if attr.check_name("thread_local") { | |
172 | llvm::set_thread_local(g, true); | |
173 | } | |
54a0048b | 174 | } |
5bcae85e SL |
175 | |
176 | g | |
54a0048b | 177 | } else { |
5bcae85e SL |
178 | let sym = instance.symbol_name(ccx.shared()); |
179 | ||
54a0048b SL |
180 | // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? |
181 | // FIXME(nagisa): investigate whether it can be changed into define_global | |
3157f602 | 182 | let g = declare::declare_global(ccx, &sym, type_of::type_of(ccx, ty)); |
54a0048b SL |
183 | // Thread-local statics in some other crate need to *always* be linked |
184 | // against in a thread-local fashion, so we need to be sure to apply the | |
185 | // thread-local attribute locally if it was present remotely. If we | |
186 | // don't do this then linker errors can be generated where the linker | |
187 | // complains that one object files has a thread local version of the | |
188 | // symbol and another one doesn't. | |
189 | for attr in ccx.tcx().get_attrs(def_id).iter() { | |
190 | if attr.check_name("thread_local") { | |
191 | llvm::set_thread_local(g, true); | |
192 | } | |
193 | } | |
194 | if ccx.use_dll_storage_attrs() { | |
5bcae85e SL |
195 | unsafe { |
196 | llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); | |
197 | } | |
54a0048b SL |
198 | } |
199 | g | |
200 | }; | |
201 | ||
202 | ccx.instances().borrow_mut().insert(instance, g); | |
a7813a04 | 203 | ccx.statics().borrow_mut().insert(g, def_id); |
9e0c209e | 204 | g |
54a0048b SL |
205 | } |
206 | ||
c1a9b12d | 207 | pub fn trans_static(ccx: &CrateContext, |
e9174d1e | 208 | m: hir::Mutability, |
c1a9b12d | 209 | id: ast::NodeId, |
9cc50fc6 | 210 | attrs: &[ast::Attribute]) |
b039eaaf | 211 | -> Result<ValueRef, ConstEvalErr> { |
1a4d82fc JJ |
212 | unsafe { |
213 | let _icx = push_ctxt("trans_static"); | |
54a0048b | 214 | let def_id = ccx.tcx().map.local_def_id(id); |
9e0c209e | 215 | let g = get_static(ccx, def_id); |
c1a9b12d | 216 | |
9e0c209e | 217 | let v = ::mir::trans_static_initializer(ccx, def_id)?; |
c1a9b12d | 218 | |
1a4d82fc JJ |
219 | // boolean SSA values are i1, but they have to be stored in i8 slots, |
220 | // otherwise some LLVM optimization passes don't work as expected | |
54a0048b SL |
221 | let mut val_llty = val_ty(v); |
222 | let v = if val_llty == Type::i1(ccx) { | |
223 | val_llty = Type::i8(ccx); | |
224 | llvm::LLVMConstZExt(v, val_llty.to_ref()) | |
1a4d82fc JJ |
225 | } else { |
226 | v | |
227 | }; | |
c1a9b12d | 228 | |
9e0c209e SL |
229 | let ty = ccx.tcx().lookup_item_type(def_id).ty; |
230 | let llty = type_of::type_of(ccx, ty); | |
54a0048b | 231 | let g = if val_llty == llty { |
9e0c209e | 232 | g |
c1a9b12d SL |
233 | } else { |
234 | // If we created the global with the wrong type, | |
235 | // correct the type. | |
236 | let empty_string = CString::new("").unwrap(); | |
9e0c209e | 237 | let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g)); |
c1a9b12d | 238 | let name_string = CString::new(name_str_ref.to_bytes()).unwrap(); |
9e0c209e | 239 | llvm::LLVMSetValueName(g, empty_string.as_ptr()); |
5bcae85e | 240 | let new_g = llvm::LLVMRustGetOrInsertGlobal( |
54a0048b | 241 | ccx.llmod(), name_string.as_ptr(), val_llty.to_ref()); |
c1a9b12d SL |
242 | // To avoid breaking any invariants, we leave around the old |
243 | // global for the moment; we'll replace all references to it | |
244 | // with the new global later. (See base::trans_crate.) | |
9e0c209e | 245 | ccx.statics_to_rauw().borrow_mut().push((g, new_g)); |
c1a9b12d SL |
246 | new_g |
247 | }; | |
9e0c209e | 248 | llvm::LLVMSetAlignment(g, type_of::align_of(ccx, ty)); |
1a4d82fc JJ |
249 | llvm::LLVMSetInitializer(g, v); |
250 | ||
251 | // As an optimization, all shared statics which do not have interior | |
252 | // mutability are placed into read-only memory. | |
e9174d1e | 253 | if m != hir::MutMutable { |
9e0c209e | 254 | let tcontents = ty.type_contents(ccx.tcx()); |
1a4d82fc | 255 | if !tcontents.interior_unsafe() { |
c1a9b12d | 256 | llvm::LLVMSetGlobalConstant(g, llvm::True); |
1a4d82fc JJ |
257 | } |
258 | } | |
c1a9b12d | 259 | |
1a4d82fc | 260 | debuginfo::create_global_var_metadata(ccx, id, g); |
c1a9b12d SL |
261 | |
262 | if attr::contains_name(attrs, | |
263 | "thread_local") { | |
264 | llvm::set_thread_local(g, true); | |
265 | } | |
5bcae85e SL |
266 | |
267 | base::set_link_section(ccx, g, attrs); | |
268 | ||
b039eaaf | 269 | Ok(g) |
1a4d82fc JJ |
270 | } |
271 | } |