]>
Commit | Line | Data |
---|---|---|
c295e0f8 XL |
1 | use gccjit::{LValue, RValue, ToRValue, Type}; |
2 | use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods, DerivedTypeMethods, StaticMethods}; | |
3 | use rustc_hir as hir; | |
4 | use rustc_hir::Node; | |
5 | use rustc_middle::{bug, span_bug}; | |
6 | use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; | |
7 | use rustc_middle::mir::mono::MonoItem; | |
8 | use rustc_middle::ty::{self, Instance, Ty}; | |
9 | use rustc_middle::ty::layout::LayoutOf; | |
10 | use rustc_middle::mir::interpret::{self, Allocation, ErrorHandled, Scalar as InterpScalar, read_target_uint}; | |
11 | use rustc_span::Span; | |
12 | use rustc_span::def_id::DefId; | |
13 | use rustc_target::abi::{self, Align, HasDataLayout, Primitive, Size, WrappingRange}; | |
14 | ||
15 | use crate::base; | |
16 | use crate::context::CodegenCx; | |
17 | use crate::type_of::LayoutGccExt; | |
18 | ||
19 | impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { | |
20 | pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> { | |
21 | if value.get_type() == self.bool_type.make_pointer() { | |
22 | if let Some(pointee) = typ.get_pointee() { | |
23 | if pointee.is_vector().is_some() { | |
24 | panic!() | |
25 | } | |
26 | } | |
27 | } | |
28 | self.context.new_bitcast(None, value, typ) | |
29 | } | |
30 | } | |
31 | ||
32 | impl<'gcc, 'tcx> StaticMethods for CodegenCx<'gcc, 'tcx> { | |
33 | fn static_addr_of(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> { | |
34 | if let Some(global_value) = self.const_globals.borrow().get(&cv) { | |
35 | // TODO(antoyo): upgrade alignment. | |
36 | return *global_value; | |
37 | } | |
38 | let global_value = self.static_addr_of_mut(cv, align, kind); | |
39 | // TODO(antoyo): set global constant. | |
40 | self.const_globals.borrow_mut().insert(cv, global_value); | |
41 | global_value | |
42 | } | |
43 | ||
44 | fn codegen_static(&self, def_id: DefId, is_mutable: bool) { | |
45 | let attrs = self.tcx.codegen_fn_attrs(def_id); | |
46 | ||
47 | let value = | |
48 | match codegen_static_initializer(&self, def_id) { | |
49 | Ok((value, _)) => value, | |
50 | // Error has already been reported | |
51 | Err(_) => return, | |
52 | }; | |
53 | ||
54 | let global = self.get_static(def_id); | |
55 | ||
56 | // boolean SSA values are i1, but they have to be stored in i8 slots, | |
57 | // otherwise some LLVM optimization passes don't work as expected | |
58 | let val_llty = self.val_ty(value); | |
59 | let value = | |
60 | if val_llty == self.type_i1() { | |
61 | unimplemented!(); | |
62 | } | |
63 | else { | |
64 | value | |
65 | }; | |
66 | ||
67 | let instance = Instance::mono(self.tcx, def_id); | |
68 | let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); | |
69 | let gcc_type = self.layout_of(ty).gcc_type(self, true); | |
70 | ||
71 | // TODO(antoyo): set alignment. | |
72 | ||
73 | let value = | |
74 | if value.get_type() != gcc_type { | |
75 | self.context.new_bitcast(None, value, gcc_type) | |
76 | } | |
77 | else { | |
78 | value | |
79 | }; | |
80 | global.global_set_initializer_value(value); | |
81 | ||
82 | // As an optimization, all shared statics which do not have interior | |
83 | // mutability are placed into read-only memory. | |
84 | if !is_mutable { | |
85 | if self.type_is_freeze(ty) { | |
86 | // TODO(antoyo): set global constant. | |
87 | } | |
88 | } | |
89 | ||
90 | if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) { | |
91 | // Do not allow LLVM to change the alignment of a TLS on macOS. | |
92 | // | |
93 | // By default a global's alignment can be freely increased. | |
94 | // This allows LLVM to generate more performant instructions | |
95 | // e.g., using load-aligned into a SIMD register. | |
96 | // | |
97 | // However, on macOS 10.10 or below, the dynamic linker does not | |
98 | // respect any alignment given on the TLS (radar 24221680). | |
99 | // This will violate the alignment assumption, and causing segfault at runtime. | |
100 | // | |
101 | // This bug is very easy to trigger. In `println!` and `panic!`, | |
102 | // the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS, | |
103 | // which the values would be `mem::replace`d on initialization. | |
104 | // The implementation of `mem::replace` will use SIMD | |
105 | // whenever the size is 32 bytes or higher. LLVM notices SIMD is used | |
106 | // and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary, | |
107 | // which macOS's dyld disregarded and causing crashes | |
108 | // (see issues #51794, #51758, #50867, #48866 and #44056). | |
109 | // | |
110 | // To workaround the bug, we trick LLVM into not increasing | |
111 | // the global's alignment by explicitly assigning a section to it | |
112 | // (equivalent to automatically generating a `#[link_section]` attribute). | |
113 | // See the comment in the `GlobalValue::canIncreaseAlignment()` function | |
114 | // of `lib/IR/Globals.cpp` for why this works. | |
115 | // | |
116 | // When the alignment is not increased, the optimized `mem::replace` | |
117 | // will use load-unaligned instructions instead, and thus avoiding the crash. | |
118 | // | |
119 | // We could remove this hack whenever we decide to drop macOS 10.10 support. | |
120 | if self.tcx.sess.target.options.is_like_osx { | |
121 | // The `inspect` method is okay here because we checked relocations, and | |
122 | // because we are doing this access to inspect the final interpreter state | |
123 | // (not as part of the interpreter execution). | |
124 | // | |
125 | // FIXME: This check requires that the (arbitrary) value of undefined bytes | |
126 | // happens to be zero. Instead, we should only check the value of defined bytes | |
127 | // and set all undefined bytes to zero if this allocation is headed for the | |
128 | // BSS. | |
129 | unimplemented!(); | |
130 | } | |
131 | } | |
132 | ||
133 | // Wasm statics with custom link sections get special treatment as they | |
134 | // go into custom sections of the wasm executable. | |
135 | if self.tcx.sess.opts.target_triple.triple().starts_with("wasm32") { | |
136 | if let Some(_section) = attrs.link_section { | |
137 | unimplemented!(); | |
138 | } | |
139 | } else { | |
140 | // TODO(antoyo): set link section. | |
141 | } | |
142 | ||
143 | if attrs.flags.contains(CodegenFnAttrFlags::USED) { | |
144 | self.add_used_global(global.to_rvalue()); | |
145 | } | |
146 | } | |
147 | ||
148 | /// Add a global value to a list to be stored in the `llvm.used` variable, an array of i8*. | |
149 | fn add_used_global(&self, _global: RValue<'gcc>) { | |
150 | // TODO(antoyo) | |
151 | } | |
152 | ||
153 | fn add_compiler_used_global(&self, _global: RValue<'gcc>) { | |
154 | // TODO(antoyo) | |
155 | } | |
156 | } | |
157 | ||
158 | impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> { | |
159 | pub fn static_addr_of_mut(&self, cv: RValue<'gcc>, align: Align, kind: Option<&str>) -> RValue<'gcc> { | |
160 | let global = | |
161 | match kind { | |
162 | Some(kind) if !self.tcx.sess.fewer_names() => { | |
163 | let name = self.generate_local_symbol_name(kind); | |
164 | // TODO(antoyo): check if it's okay that TLS is off here. | |
165 | // TODO(antoyo): check if it's okay that link_section is None here. | |
166 | // TODO(antoyo): set alignment here as well. | |
167 | let global = self.define_global(&name[..], self.val_ty(cv), false, None); | |
168 | // TODO(antoyo): set linkage. | |
169 | global | |
170 | } | |
171 | _ => { | |
172 | let typ = self.val_ty(cv).get_aligned(align.bytes()); | |
173 | let global = self.declare_unnamed_global(typ); | |
174 | global | |
175 | }, | |
176 | }; | |
177 | // FIXME(antoyo): I think the name coming from generate_local_symbol_name() above cannot be used | |
178 | // globally. | |
179 | global.global_set_initializer_value(cv); | |
180 | // TODO(antoyo): set unnamed address. | |
181 | global.get_address(None) | |
182 | } | |
183 | ||
184 | pub fn get_static(&self, def_id: DefId) -> LValue<'gcc> { | |
185 | let instance = Instance::mono(self.tcx, def_id); | |
186 | let fn_attrs = self.tcx.codegen_fn_attrs(def_id); | |
187 | if let Some(&global) = self.instances.borrow().get(&instance) { | |
188 | return global; | |
189 | } | |
190 | ||
191 | let defined_in_current_codegen_unit = | |
192 | self.codegen_unit.items().contains_key(&MonoItem::Static(def_id)); | |
193 | assert!( | |
194 | !defined_in_current_codegen_unit, | |
195 | "consts::get_static() should always hit the cache for \ | |
196 | statics defined in the same CGU, but did not for `{:?}`", | |
197 | def_id | |
198 | ); | |
199 | ||
200 | let ty = instance.ty(self.tcx, ty::ParamEnv::reveal_all()); | |
201 | let sym = self.tcx.symbol_name(instance).name; | |
202 | ||
203 | let global = | |
204 | if let Some(def_id) = def_id.as_local() { | |
205 | let id = self.tcx.hir().local_def_id_to_hir_id(def_id); | |
206 | let llty = self.layout_of(ty).gcc_type(self, true); | |
207 | // FIXME: refactor this to work without accessing the HIR | |
208 | let global = match self.tcx.hir().get(id) { | |
209 | Node::Item(&hir::Item { span, kind: hir::ItemKind::Static(..), .. }) => { | |
210 | if let Some(global) = self.get_declared_value(&sym) { | |
211 | if self.val_ty(global) != self.type_ptr_to(llty) { | |
212 | span_bug!(span, "Conflicting types for static"); | |
213 | } | |
214 | } | |
215 | ||
216 | let is_tls = fn_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); | |
217 | let global = self.declare_global(&sym, llty, is_tls, fn_attrs.link_section); | |
218 | ||
219 | if !self.tcx.is_reachable_non_generic(def_id) { | |
220 | // TODO(antoyo): set visibility. | |
221 | } | |
222 | ||
223 | global | |
224 | } | |
225 | ||
226 | Node::ForeignItem(&hir::ForeignItem { | |
227 | span, | |
228 | kind: hir::ForeignItemKind::Static(..), | |
229 | .. | |
230 | }) => { | |
231 | let fn_attrs = self.tcx.codegen_fn_attrs(def_id); | |
232 | check_and_apply_linkage(&self, &fn_attrs, ty, sym, span) | |
233 | } | |
234 | ||
235 | item => bug!("get_static: expected static, found {:?}", item), | |
236 | }; | |
237 | ||
238 | global | |
239 | } | |
240 | else { | |
241 | // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? | |
242 | //debug!("get_static: sym={} item_attr={:?}", sym, self.tcx.item_attrs(def_id)); | |
243 | ||
244 | let attrs = self.tcx.codegen_fn_attrs(def_id); | |
245 | let span = self.tcx.def_span(def_id); | |
246 | let global = check_and_apply_linkage(&self, &attrs, ty, sym, span); | |
247 | ||
248 | let needs_dll_storage_attr = false; // TODO(antoyo) | |
249 | ||
250 | // If this assertion triggers, there's something wrong with commandline | |
251 | // argument validation. | |
252 | debug_assert!( | |
253 | !(self.tcx.sess.opts.cg.linker_plugin_lto.enabled() | |
254 | && self.tcx.sess.target.options.is_like_msvc | |
255 | && self.tcx.sess.opts.cg.prefer_dynamic) | |
256 | ); | |
257 | ||
258 | if needs_dll_storage_attr { | |
259 | // This item is external but not foreign, i.e., it originates from an external Rust | |
260 | // crate. Since we don't know whether this crate will be linked dynamically or | |
261 | // statically in the final application, we always mark such symbols as 'dllimport'. | |
262 | // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs | |
263 | // to make things work. | |
264 | // | |
265 | // However, in some scenarios we defer emission of statics to downstream | |
266 | // crates, so there are cases where a static with an upstream DefId | |
267 | // is actually present in the current crate. We can find out via the | |
268 | // is_codegened_item query. | |
269 | if !self.tcx.is_codegened_item(def_id) { | |
270 | unimplemented!(); | |
271 | } | |
272 | } | |
273 | global | |
274 | }; | |
275 | ||
276 | // TODO(antoyo): set dll storage class. | |
277 | ||
278 | self.instances.borrow_mut().insert(instance, global); | |
279 | global | |
280 | } | |
281 | } | |
282 | ||
283 | pub fn const_alloc_to_gcc<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, alloc: &Allocation) -> RValue<'gcc> { | |
284 | let mut llvals = Vec::with_capacity(alloc.relocations().len() + 1); | |
285 | let dl = cx.data_layout(); | |
286 | let pointer_size = dl.pointer_size.bytes() as usize; | |
287 | ||
288 | let mut next_offset = 0; | |
289 | for &(offset, alloc_id) in alloc.relocations().iter() { | |
290 | let offset = offset.bytes(); | |
291 | assert_eq!(offset as usize as u64, offset); | |
292 | let offset = offset as usize; | |
293 | if offset > next_offset { | |
294 | // This `inspect` is okay since we have checked that it is not within a relocation, it | |
295 | // is within the bounds of the allocation, and it doesn't affect interpreter execution | |
296 | // (we inspect the result after interpreter execution). Any undef byte is replaced with | |
297 | // some arbitrary byte value. | |
298 | // | |
299 | // FIXME: relay undef bytes to codegen as undef const bytes | |
300 | let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(next_offset..offset); | |
301 | llvals.push(cx.const_bytes(bytes)); | |
302 | } | |
303 | let ptr_offset = | |
304 | read_target_uint( dl.endian, | |
305 | // This `inspect` is okay since it is within the bounds of the allocation, it doesn't | |
306 | // affect interpreter execution (we inspect the result after interpreter execution), | |
307 | // and we properly interpret the relocation as a relocation pointer offset. | |
308 | alloc.inspect_with_uninit_and_ptr_outside_interpreter(offset..(offset + pointer_size)), | |
309 | ) | |
310 | .expect("const_alloc_to_llvm: could not read relocation pointer") | |
311 | as u64; | |
312 | llvals.push(cx.scalar_to_backend( | |
313 | InterpScalar::from_pointer( | |
314 | interpret::Pointer::new(alloc_id, Size::from_bytes(ptr_offset)), | |
315 | &cx.tcx, | |
316 | ), | |
317 | abi::Scalar { value: Primitive::Pointer, valid_range: WrappingRange { start: 0, end: !0 } }, | |
318 | cx.type_i8p(), | |
319 | )); | |
320 | next_offset = offset + pointer_size; | |
321 | } | |
322 | if alloc.len() >= next_offset { | |
323 | let range = next_offset..alloc.len(); | |
324 | // This `inspect` is okay since we have check that it is after all relocations, it is | |
325 | // within the bounds of the allocation, and it doesn't affect interpreter execution (we | |
326 | // inspect the result after interpreter execution). Any undef byte is replaced with some | |
327 | // arbitrary byte value. | |
328 | // | |
329 | // FIXME: relay undef bytes to codegen as undef const bytes | |
330 | let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(range); | |
331 | llvals.push(cx.const_bytes(bytes)); | |
332 | } | |
333 | ||
334 | cx.const_struct(&llvals, true) | |
335 | } | |
336 | ||
337 | pub fn codegen_static_initializer<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, def_id: DefId) -> Result<(RValue<'gcc>, &'tcx Allocation), ErrorHandled> { | |
338 | let alloc = cx.tcx.eval_static_initializer(def_id)?; | |
339 | Ok((const_alloc_to_gcc(cx, alloc), alloc)) | |
340 | } | |
341 | ||
342 | fn check_and_apply_linkage<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, attrs: &CodegenFnAttrs, ty: Ty<'tcx>, sym: &str, span: Span) -> LValue<'gcc> { | |
343 | let is_tls = attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL); | |
344 | let llty = cx.layout_of(ty).gcc_type(cx, true); | |
345 | if let Some(linkage) = attrs.linkage { | |
346 | // If this is a static with a linkage specified, then we need to handle | |
347 | // it a little specially. The typesystem prevents things like &T and | |
348 | // extern "C" fn() from being non-null, so we can't just declare a | |
349 | // static and call it a day. Some linkages (like weak) will make it such | |
350 | // that the static actually has a null value. | |
351 | let llty2 = | |
352 | if let ty::RawPtr(ref mt) = ty.kind() { | |
353 | cx.layout_of(mt.ty).gcc_type(cx, true) | |
354 | } | |
355 | else { | |
356 | cx.sess().span_fatal( | |
357 | span, | |
358 | "must have type `*const T` or `*mut T` due to `#[linkage]` attribute", | |
359 | ) | |
360 | }; | |
361 | // Declare a symbol `foo` with the desired linkage. | |
362 | let global1 = cx.declare_global_with_linkage(&sym, llty2, base::global_linkage_to_gcc(linkage)); | |
363 | ||
364 | // Declare an internal global `extern_with_linkage_foo` which | |
365 | // is initialized with the address of `foo`. If `foo` is | |
366 | // discarded during linking (for example, if `foo` has weak | |
367 | // linkage and there are no definitions), then | |
368 | // `extern_with_linkage_foo` will instead be initialized to | |
369 | // zero. | |
370 | let mut real_name = "_rust_extern_with_linkage_".to_string(); | |
371 | real_name.push_str(&sym); | |
372 | let global2 = cx.define_global(&real_name, llty, is_tls, attrs.link_section); | |
373 | // TODO(antoyo): set linkage. | |
374 | global2.global_set_initializer_value(global1.get_address(None)); | |
375 | // TODO(antoyo): use global_set_initializer() when it will work. | |
376 | global2 | |
377 | } | |
378 | else { | |
379 | // Generate an external declaration. | |
380 | // FIXME(nagisa): investigate whether it can be changed into define_global | |
381 | ||
382 | // Thread-local statics in some other crate need to *always* be linked | |
383 | // against in a thread-local fashion, so we need to be sure to apply the | |
384 | // thread-local attribute locally if it was present remotely. If we | |
385 | // don't do this then linker errors can be generated where the linker | |
386 | // complains that one object files has a thread local version of the | |
387 | // symbol and another one doesn't. | |
388 | cx.declare_global(&sym, llty, is_tls, attrs.link_section) | |
389 | } | |
390 | } |