Added StaticMethods trait
This commit is contained in:
parent
d77e34f35b
commit
3c082a23e8
@ -24,7 +24,7 @@
|
||||
use type_of::LayoutLlvmExt;
|
||||
use value::Value;
|
||||
use rustc::ty::{self, Ty};
|
||||
use interfaces::{BaseTypeMethods, DerivedTypeMethods};
|
||||
use interfaces::{BaseTypeMethods, DerivedTypeMethods, StaticMethods};
|
||||
|
||||
use rustc::ty::layout::{Align, LayoutOf};
|
||||
|
||||
@ -32,17 +32,6 @@
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
pub fn ptrcast(val: &'ll Value, ty: &'ll Type) -> &'ll Value {
|
||||
unsafe {
|
||||
llvm::LLVMConstPointerCast(val, ty)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bitcast(val: &'ll Value, ty: &'ll Type) -> &'ll Value {
|
||||
unsafe {
|
||||
llvm::LLVMConstBitCast(val, ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_global_alignment(cx: &CodegenCx<'ll, '_>,
|
||||
gv: &'ll Value,
|
||||
@ -63,178 +52,6 @@ fn set_global_alignment(cx: &CodegenCx<'ll, '_>,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addr_of_mut(
|
||||
cx: &CodegenCx<'ll, '_>,
|
||||
cv: &'ll Value,
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> &'ll Value {
|
||||
unsafe {
|
||||
let gv = match kind {
|
||||
Some(kind) if !cx.tcx.sess.fewer_names() => {
|
||||
let name = cx.generate_local_symbol_name(kind);
|
||||
let gv = declare::define_global(cx, &name[..],
|
||||
cx.val_ty(cv)).unwrap_or_else(||{
|
||||
bug!("symbol `{}` is already defined", name);
|
||||
});
|
||||
llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage);
|
||||
gv
|
||||
},
|
||||
_ => declare::define_private_global(cx, cx.val_ty(cv)),
|
||||
};
|
||||
llvm::LLVMSetInitializer(gv, cv);
|
||||
set_global_alignment(cx, gv, align);
|
||||
SetUnnamedAddr(gv, true);
|
||||
gv
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addr_of(
|
||||
cx: &CodegenCx<'ll, '_>,
|
||||
cv: &'ll Value,
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> &'ll Value {
|
||||
if let Some(&gv) = cx.const_globals.borrow().get(&cv) {
|
||||
unsafe {
|
||||
// Upgrade the alignment in cases where the same constant is used with different
|
||||
// alignment requirements
|
||||
let llalign = align.abi() as u32;
|
||||
if llalign > llvm::LLVMGetAlignment(gv) {
|
||||
llvm::LLVMSetAlignment(gv, llalign);
|
||||
}
|
||||
}
|
||||
return gv;
|
||||
}
|
||||
let gv = addr_of_mut(cx, cv, align, kind);
|
||||
unsafe {
|
||||
llvm::LLVMSetGlobalConstant(gv, True);
|
||||
}
|
||||
cx.const_globals.borrow_mut().insert(cv, gv);
|
||||
gv
|
||||
}
|
||||
|
||||
pub fn get_static(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll Value {
|
||||
let instance = Instance::mono(cx.tcx, def_id);
|
||||
if let Some(&g) = cx.instances.borrow().get(&instance) {
|
||||
return g;
|
||||
}
|
||||
|
||||
let defined_in_current_codegen_unit = cx.codegen_unit
|
||||
.items()
|
||||
.contains_key(&MonoItem::Static(def_id));
|
||||
assert!(!defined_in_current_codegen_unit,
|
||||
"consts::get_static() should always hit the cache for \
|
||||
statics defined in the same CGU, but did not for `{:?}`",
|
||||
def_id);
|
||||
|
||||
let ty = instance.ty(cx.tcx);
|
||||
let sym = cx.tcx.symbol_name(instance).as_str();
|
||||
|
||||
debug!("get_static: sym={} instance={:?}", sym, instance);
|
||||
|
||||
let g = if let Some(id) = cx.tcx.hir.as_local_node_id(def_id) {
|
||||
|
||||
let llty = cx.layout_of(ty).llvm_type(cx);
|
||||
let (g, attrs) = match cx.tcx.hir.get(id) {
|
||||
Node::Item(&hir::Item {
|
||||
ref attrs, span, node: hir::ItemKind::Static(..), ..
|
||||
}) => {
|
||||
if declare::get_declared_value(cx, &sym[..]).is_some() {
|
||||
span_bug!(span, "Conflicting symbol names for static?");
|
||||
}
|
||||
|
||||
let g = declare::define_global(cx, &sym[..], llty).unwrap();
|
||||
|
||||
if !cx.tcx.is_reachable_non_generic(def_id) {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden);
|
||||
}
|
||||
}
|
||||
|
||||
(g, attrs)
|
||||
}
|
||||
|
||||
Node::ForeignItem(&hir::ForeignItem {
|
||||
ref attrs, span, node: hir::ForeignItemKind::Static(..), ..
|
||||
}) => {
|
||||
let fn_attrs = cx.tcx.codegen_fn_attrs(def_id);
|
||||
(check_and_apply_linkage(cx, &fn_attrs, ty, sym, Some(span)), attrs)
|
||||
}
|
||||
|
||||
item => bug!("get_static: expected static, found {:?}", item)
|
||||
};
|
||||
|
||||
debug!("get_static: sym={} attrs={:?}", sym, attrs);
|
||||
|
||||
for attr in attrs {
|
||||
if attr.check_name("thread_local") {
|
||||
llvm::set_thread_local_mode(g, cx.tls_model);
|
||||
}
|
||||
}
|
||||
|
||||
g
|
||||
} else {
|
||||
// FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
|
||||
debug!("get_static: sym={} item_attr={:?}", sym, cx.tcx.item_attrs(def_id));
|
||||
|
||||
let attrs = cx.tcx.codegen_fn_attrs(def_id);
|
||||
let g = check_and_apply_linkage(cx, &attrs, ty, sym, None);
|
||||
|
||||
// Thread-local statics in some other crate need to *always* be linked
|
||||
// against in a thread-local fashion, so we need to be sure to apply the
|
||||
// thread-local attribute locally if it was present remotely. If we
|
||||
// don't do this then linker errors can be generated where the linker
|
||||
// complains that one object files has a thread local version of the
|
||||
// symbol and another one doesn't.
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
|
||||
llvm::set_thread_local_mode(g, cx.tls_model);
|
||||
}
|
||||
|
||||
let needs_dll_storage_attr =
|
||||
cx.use_dll_storage_attrs && !cx.tcx.is_foreign_item(def_id) &&
|
||||
// ThinLTO can't handle this workaround in all cases, so we don't
|
||||
// emit the attrs. Instead we make them unnecessary by disallowing
|
||||
// dynamic linking when cross-language LTO is enabled.
|
||||
!cx.tcx.sess.opts.debugging_opts.cross_lang_lto.enabled();
|
||||
|
||||
// If this assertion triggers, there's something wrong with commandline
|
||||
// argument validation.
|
||||
debug_assert!(!(cx.tcx.sess.opts.debugging_opts.cross_lang_lto.enabled() &&
|
||||
cx.tcx.sess.target.target.options.is_like_msvc &&
|
||||
cx.tcx.sess.opts.cg.prefer_dynamic));
|
||||
|
||||
if needs_dll_storage_attr {
|
||||
// This item is external but not foreign, i.e. it originates from an external Rust
|
||||
// crate. Since we don't know whether this crate will be linked dynamically or
|
||||
// statically in the final application, we always mark such symbols as 'dllimport'.
|
||||
// If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs to
|
||||
// make things work.
|
||||
//
|
||||
// However, in some scenarios we defer emission of statics to downstream
|
||||
// crates, so there are cases where a static with an upstream DefId
|
||||
// is actually present in the current crate. We can find out via the
|
||||
// is_codegened_item query.
|
||||
if !cx.tcx.is_codegened_item(def_id) {
|
||||
unsafe {
|
||||
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
}
|
||||
}
|
||||
g
|
||||
};
|
||||
|
||||
if cx.use_dll_storage_attrs && cx.tcx.is_dllimport_foreign_item(def_id) {
|
||||
// For foreign (native) libs we know the exact storage type to use.
|
||||
unsafe {
|
||||
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
}
|
||||
|
||||
cx.instances.borrow_mut().insert(instance, g);
|
||||
g
|
||||
}
|
||||
|
||||
fn check_and_apply_linkage(
|
||||
cx: &CodegenCx<'ll, 'tcx>,
|
||||
attrs: &CodegenFnAttrs,
|
||||
@ -294,146 +111,337 @@ fn check_and_apply_linkage(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn codegen_static<'a, 'tcx>(
|
||||
cx: &CodegenCx<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
is_mutable: bool,
|
||||
) {
|
||||
pub fn ptrcast(val: &'ll Value, ty: &'ll Type) -> &'ll Value {
|
||||
unsafe {
|
||||
let attrs = cx.tcx.codegen_fn_attrs(def_id);
|
||||
llvm::LLVMConstPointerCast(val, ty)
|
||||
}
|
||||
}
|
||||
|
||||
let (v, alloc) = match ::mir::codegen_static_initializer(cx, def_id) {
|
||||
Ok(v) => v,
|
||||
// Error has already been reported
|
||||
Err(_) => return,
|
||||
};
|
||||
impl StaticMethods<'tcx> for CodegenCx<'ll, 'tcx> {
|
||||
|
||||
let g = get_static(cx, def_id);
|
||||
fn static_ptrcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
|
||||
ptrcast(val, ty)
|
||||
}
|
||||
|
||||
// boolean SSA values are i1, but they have to be stored in i8 slots,
|
||||
// otherwise some LLVM optimization passes don't work as expected
|
||||
let mut val_llty = cx.val_ty(v);
|
||||
let v = if val_llty == cx.type_i1() {
|
||||
val_llty = cx.type_i8();
|
||||
llvm::LLVMConstZExt(v, val_llty)
|
||||
} else {
|
||||
v
|
||||
};
|
||||
fn static_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
|
||||
unsafe {
|
||||
llvm::LLVMConstBitCast(val, ty)
|
||||
}
|
||||
}
|
||||
|
||||
fn static_addr_of_mut(
|
||||
&self,
|
||||
cv: &'ll Value,
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> &'ll Value {
|
||||
unsafe {
|
||||
let gv = match kind {
|
||||
Some(kind) if !&self.tcx.sess.fewer_names() => {
|
||||
let name = &self.generate_local_symbol_name(kind);
|
||||
let gv = declare::define_global(&self, &name[..],
|
||||
&self.val_ty(cv)).unwrap_or_else(||{
|
||||
bug!("symbol `{}` is already defined", name);
|
||||
});
|
||||
llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage);
|
||||
gv
|
||||
},
|
||||
_ => declare::define_private_global(&self, &self.val_ty(cv)),
|
||||
};
|
||||
llvm::LLVMSetInitializer(gv, cv);
|
||||
set_global_alignment(&self, gv, align);
|
||||
SetUnnamedAddr(gv, true);
|
||||
gv
|
||||
}
|
||||
}
|
||||
|
||||
fn static_addr_of(
|
||||
&self,
|
||||
cv: &'ll Value,
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> &'ll Value {
|
||||
if let Some(&gv) = &self.const_globals.borrow().get(&cv) {
|
||||
unsafe {
|
||||
// Upgrade the alignment in cases where the same constant is used with different
|
||||
// alignment requirements
|
||||
let llalign = align.abi() as u32;
|
||||
if llalign > llvm::LLVMGetAlignment(gv) {
|
||||
llvm::LLVMSetAlignment(gv, llalign);
|
||||
}
|
||||
}
|
||||
return gv;
|
||||
}
|
||||
let gv = &self.static_addr_of_mut(cv, align, kind);
|
||||
unsafe {
|
||||
llvm::LLVMSetGlobalConstant(gv, True);
|
||||
}
|
||||
&self.const_globals.borrow_mut().insert(cv, gv);
|
||||
gv
|
||||
}
|
||||
|
||||
fn get_static(&self, def_id: DefId) -> &'ll Value {
|
||||
let instance = Instance::mono(self.tcx, def_id);
|
||||
if let Some(&g) = &self.instances.borrow().get(&instance) {
|
||||
return g;
|
||||
}
|
||||
|
||||
let defined_in_current_codegen_unit = &self.codegen_unit
|
||||
.items()
|
||||
.contains_key(&MonoItem::Static(def_id));
|
||||
assert!(!defined_in_current_codegen_unit,
|
||||
"consts::get_static() should always hit the cache for \
|
||||
statics defined in the same CGU, but did not for `{:?}`",
|
||||
def_id);
|
||||
|
||||
let ty = instance.ty(self.tcx);
|
||||
let sym = self.tcx.symbol_name(instance).as_str();
|
||||
|
||||
debug!("get_static: sym={} instance={:?}", sym, instance);
|
||||
|
||||
let g = if let Some(id) = self.tcx.hir.as_local_node_id(def_id) {
|
||||
|
||||
let llty = &self.layout_of(ty).llvm_type(&self);
|
||||
let (g, attrs) = match &self.tcx.hir.get(id) {
|
||||
Node::Item(&hir::Item {
|
||||
ref attrs, span, node: hir::ItemKind::Static(..), ..
|
||||
}) => {
|
||||
if declare::get_declared_value(&self, &sym[..]).is_some() {
|
||||
span_bug!(span, "Conflicting symbol names for static?");
|
||||
}
|
||||
|
||||
let g = declare::define_global(&self, &sym[..], llty).unwrap();
|
||||
|
||||
if !&self.tcx.is_reachable_non_generic(def_id) {
|
||||
unsafe {
|
||||
llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden);
|
||||
}
|
||||
}
|
||||
|
||||
(g, attrs)
|
||||
}
|
||||
|
||||
Node::ForeignItem(&hir::ForeignItem {
|
||||
ref attrs, span, node: hir::ForeignItemKind::Static(..), ..
|
||||
}) => {
|
||||
let fn_attrs = &self.tcx.codegen_fn_attrs(def_id);
|
||||
(check_and_apply_linkage(&self, &fn_attrs, ty, sym, Some(span)), attrs)
|
||||
}
|
||||
|
||||
item => bug!("get_static: expected static, found {:?}", item)
|
||||
};
|
||||
|
||||
debug!("get_static: sym={} attrs={:?}", sym, attrs);
|
||||
|
||||
for attr in attrs {
|
||||
if attr.check_name("thread_local") {
|
||||
llvm::set_thread_local_mode(g, self.tls_model);
|
||||
}
|
||||
}
|
||||
|
||||
let instance = Instance::mono(cx.tcx, def_id);
|
||||
let ty = instance.ty(cx.tcx);
|
||||
let llty = cx.layout_of(ty).llvm_type(cx);
|
||||
let g = if val_llty == llty {
|
||||
g
|
||||
} else {
|
||||
// If we created the global with the wrong type,
|
||||
// correct the type.
|
||||
let empty_string = const_cstr!("");
|
||||
let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g));
|
||||
let name_string = CString::new(name_str_ref.to_bytes()).unwrap();
|
||||
llvm::LLVMSetValueName(g, empty_string.as_ptr());
|
||||
// FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow?
|
||||
debug!("get_static: sym={} item_attr={:?}", sym, &self.tcx.item_attrs(def_id));
|
||||
|
||||
let linkage = llvm::LLVMRustGetLinkage(g);
|
||||
let visibility = llvm::LLVMRustGetVisibility(g);
|
||||
let attrs = &self.tcx.codegen_fn_attrs(def_id);
|
||||
let g = check_and_apply_linkage(&self, &attrs, ty, sym, None);
|
||||
|
||||
let new_g = llvm::LLVMRustGetOrInsertGlobal(
|
||||
cx.llmod, name_string.as_ptr(), val_llty);
|
||||
// Thread-local statics in some other crate need to *always* be linked
|
||||
// against in a thread-local fashion, so we need to be sure to apply the
|
||||
// thread-local attribute locally if it was present remotely. If we
|
||||
// don't do this then linker errors can be generated where the linker
|
||||
// complains that one object files has a thread local version of the
|
||||
// symbol and another one doesn't.
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
|
||||
llvm::set_thread_local_mode(g, self.tls_model);
|
||||
}
|
||||
|
||||
llvm::LLVMRustSetLinkage(new_g, linkage);
|
||||
llvm::LLVMRustSetVisibility(new_g, visibility);
|
||||
let needs_dll_storage_attr =
|
||||
self.use_dll_storage_attrs && !&self.tcx.is_foreign_item(def_id) &&
|
||||
// ThinLTO can't handle this workaround in all cases, so we don't
|
||||
// emit the attrs. Instead we make them unnecessary by disallowing
|
||||
// dynamic linking when cross-language LTO is enabled.
|
||||
!&self.tcx.sess.opts.debugging_opts.cross_lang_lto.enabled();
|
||||
|
||||
// To avoid breaking any invariants, we leave around the old
|
||||
// global for the moment; we'll replace all references to it
|
||||
// with the new global later. (See base::codegen_backend.)
|
||||
cx.statics_to_rauw.borrow_mut().push((g, new_g));
|
||||
new_g
|
||||
// If this assertion triggers, there's something wrong with commandline
|
||||
// argument validation.
|
||||
debug_assert!(!(self.tcx.sess.opts.debugging_opts.cross_lang_lto.enabled() &&
|
||||
self.tcx.sess.target.target.options.is_like_msvc &&
|
||||
self.tcx.sess.opts.cg.prefer_dynamic));
|
||||
|
||||
if needs_dll_storage_attr {
|
||||
// This item is external but not foreign, i.e. it originates from an external Rust
|
||||
// crate. Since we don't know whether this crate will be linked dynamically or
|
||||
// statically in the final application, we always mark such symbols as 'dllimport'.
|
||||
// If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs
|
||||
// to make things work.
|
||||
//
|
||||
// However, in some scenarios we defer emission of statics to downstream
|
||||
// crates, so there are cases where a static with an upstream DefId
|
||||
// is actually present in the current crate. We can find out via the
|
||||
// is_codegened_item query.
|
||||
if !&self.tcx.is_codegened_item(def_id) {
|
||||
unsafe {
|
||||
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
}
|
||||
}
|
||||
g
|
||||
};
|
||||
set_global_alignment(cx, g, cx.align_of(ty));
|
||||
llvm::LLVMSetInitializer(g, v);
|
||||
|
||||
// As an optimization, all shared statics which do not have interior
|
||||
// mutability are placed into read-only memory.
|
||||
if !is_mutable {
|
||||
if cx.type_is_freeze(ty) {
|
||||
llvm::LLVMSetGlobalConstant(g, llvm::True);
|
||||
if self.use_dll_storage_attrs && self.tcx.is_dllimport_foreign_item(def_id) {
|
||||
// For foreign (native) libs we know the exact storage type to use.
|
||||
unsafe {
|
||||
llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport);
|
||||
}
|
||||
}
|
||||
|
||||
debuginfo::create_global_var_metadata(cx, def_id, g);
|
||||
&self.instances.borrow_mut().insert(instance, g);
|
||||
g
|
||||
}
|
||||
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
|
||||
llvm::set_thread_local_mode(g, cx.tls_model);
|
||||
fn codegen_static(
|
||||
&self,
|
||||
def_id: DefId,
|
||||
is_mutable: bool,
|
||||
) {
|
||||
unsafe {
|
||||
let attrs = &self.tcx.codegen_fn_attrs(def_id);
|
||||
|
||||
// Do not allow LLVM to change the alignment of a TLS on macOS.
|
||||
//
|
||||
// By default a global's alignment can be freely increased.
|
||||
// This allows LLVM to generate more performant instructions
|
||||
// e.g. using load-aligned into a SIMD register.
|
||||
//
|
||||
// However, on macOS 10.10 or below, the dynamic linker does not
|
||||
// respect any alignment given on the TLS (radar 24221680).
|
||||
// This will violate the alignment assumption, and causing segfault at runtime.
|
||||
//
|
||||
// This bug is very easy to trigger. In `println!` and `panic!`,
|
||||
// the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS,
|
||||
// which the values would be `mem::replace`d on initialization.
|
||||
// The implementation of `mem::replace` will use SIMD
|
||||
// whenever the size is 32 bytes or higher. LLVM notices SIMD is used
|
||||
// and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary,
|
||||
// which macOS's dyld disregarded and causing crashes
|
||||
// (see issues #51794, #51758, #50867, #48866 and #44056).
|
||||
//
|
||||
// To workaround the bug, we trick LLVM into not increasing
|
||||
// the global's alignment by explicitly assigning a section to it
|
||||
// (equivalent to automatically generating a `#[link_section]` attribute).
|
||||
// See the comment in the `GlobalValue::canIncreaseAlignment()` function
|
||||
// of `lib/IR/Globals.cpp` for why this works.
|
||||
//
|
||||
// When the alignment is not increased, the optimized `mem::replace`
|
||||
// will use load-unaligned instructions instead, and thus avoiding the crash.
|
||||
//
|
||||
// We could remove this hack whenever we decide to drop macOS 10.10 support.
|
||||
if cx.tcx.sess.target.target.options.is_like_osx {
|
||||
let sect_name = if alloc.bytes.iter().all(|b| *b == 0) {
|
||||
CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0")
|
||||
} else {
|
||||
CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0")
|
||||
};
|
||||
llvm::LLVMSetSection(g, sect_name.as_ptr());
|
||||
let (v, alloc) = match ::mir::codegen_static_initializer(&self, def_id) {
|
||||
Ok(v) => v,
|
||||
// Error has already been reported
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let g = &self.get_static(def_id);
|
||||
|
||||
// boolean SSA values are i1, but they have to be stored in i8 slots,
|
||||
// otherwise some LLVM optimization passes don't work as expected
|
||||
let mut val_llty = self.val_ty(v);
|
||||
let v = if val_llty == self.type_i1() {
|
||||
val_llty = self.type_i8();
|
||||
llvm::LLVMConstZExt(v, val_llty)
|
||||
} else {
|
||||
v
|
||||
};
|
||||
|
||||
let instance = Instance::mono(self.tcx, def_id);
|
||||
let ty = instance.ty(self.tcx);
|
||||
let llty = self.layout_of(ty).llvm_type(&self);
|
||||
let g = if val_llty == llty {
|
||||
g
|
||||
} else {
|
||||
// If we created the global with the wrong type,
|
||||
// correct the type.
|
||||
let empty_string = const_cstr!("");
|
||||
let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g));
|
||||
let name_string = CString::new(name_str_ref.to_bytes()).unwrap();
|
||||
llvm::LLVMSetValueName(g, empty_string.as_ptr());
|
||||
|
||||
let linkage = llvm::LLVMRustGetLinkage(g);
|
||||
let visibility = llvm::LLVMRustGetVisibility(g);
|
||||
|
||||
let new_g = llvm::LLVMRustGetOrInsertGlobal(
|
||||
&self.llmod, name_string.as_ptr(), val_llty);
|
||||
|
||||
llvm::LLVMRustSetLinkage(new_g, linkage);
|
||||
llvm::LLVMRustSetVisibility(new_g, visibility);
|
||||
|
||||
// To avoid breaking any invariants, we leave around the old
|
||||
// global for the moment; we'll replace all references to it
|
||||
// with the new global later. (See base::codegen_backend.)
|
||||
&self.statics_to_rauw.borrow_mut().push((g, new_g));
|
||||
new_g
|
||||
};
|
||||
set_global_alignment(&self, g, self.align_of(ty));
|
||||
llvm::LLVMSetInitializer(g, v);
|
||||
|
||||
// As an optimization, all shared statics which do not have interior
|
||||
// mutability are placed into read-only memory.
|
||||
if !is_mutable {
|
||||
if self.type_is_freeze(ty) {
|
||||
llvm::LLVMSetGlobalConstant(g, llvm::True);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debuginfo::create_global_var_metadata(&self, def_id, g);
|
||||
|
||||
// Wasm statics with custom link sections get special treatment as they
|
||||
// go into custom sections of the wasm executable.
|
||||
if cx.tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
|
||||
if let Some(section) = attrs.link_section {
|
||||
let section = llvm::LLVMMDStringInContext(
|
||||
cx.llcx,
|
||||
section.as_str().as_ptr() as *const _,
|
||||
section.as_str().len() as c_uint,
|
||||
);
|
||||
let alloc = llvm::LLVMMDStringInContext(
|
||||
cx.llcx,
|
||||
alloc.bytes.as_ptr() as *const _,
|
||||
alloc.bytes.len() as c_uint,
|
||||
);
|
||||
let data = [section, alloc];
|
||||
let meta = llvm::LLVMMDNodeInContext(cx.llcx, data.as_ptr(), 2);
|
||||
llvm::LLVMAddNamedMetadataOperand(
|
||||
cx.llmod,
|
||||
"wasm.custom_sections\0".as_ptr() as *const _,
|
||||
meta,
|
||||
);
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
|
||||
llvm::set_thread_local_mode(g, self.tls_model);
|
||||
|
||||
// Do not allow LLVM to change the alignment of a TLS on macOS.
|
||||
//
|
||||
// By default a global's alignment can be freely increased.
|
||||
// This allows LLVM to generate more performant instructions
|
||||
// e.g. using load-aligned into a SIMD register.
|
||||
//
|
||||
// However, on macOS 10.10 or below, the dynamic linker does not
|
||||
// respect any alignment given on the TLS (radar 24221680).
|
||||
// This will violate the alignment assumption, and causing segfault at runtime.
|
||||
//
|
||||
// This bug is very easy to trigger. In `println!` and `panic!`,
|
||||
// the `LOCAL_STDOUT`/`LOCAL_STDERR` handles are stored in a TLS,
|
||||
// which the values would be `mem::replace`d on initialization.
|
||||
// The implementation of `mem::replace` will use SIMD
|
||||
// whenever the size is 32 bytes or higher. LLVM notices SIMD is used
|
||||
// and tries to align `LOCAL_STDOUT`/`LOCAL_STDERR` to a 32-byte boundary,
|
||||
// which macOS's dyld disregarded and causing crashes
|
||||
// (see issues #51794, #51758, #50867, #48866 and #44056).
|
||||
//
|
||||
// To workaround the bug, we trick LLVM into not increasing
|
||||
// the global's alignment by explicitly assigning a section to it
|
||||
// (equivalent to automatically generating a `#[link_section]` attribute).
|
||||
// See the comment in the `GlobalValue::canIncreaseAlignment()` function
|
||||
// of `lib/IR/Globals.cpp` for why this works.
|
||||
//
|
||||
// When the alignment is not increased, the optimized `mem::replace`
|
||||
// will use load-unaligned instructions instead, and thus avoiding the crash.
|
||||
//
|
||||
// We could remove this hack whenever we decide to drop macOS 10.10 support.
|
||||
if self.tcx.sess.target.target.options.is_like_osx {
|
||||
let sect_name = if alloc.bytes.iter().all(|b| *b == 0) {
|
||||
CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_bss\0")
|
||||
} else {
|
||||
CStr::from_bytes_with_nul_unchecked(b"__DATA,__thread_data\0")
|
||||
};
|
||||
llvm::LLVMSetSection(g, sect_name.as_ptr());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
base::set_link_section(g, &attrs);
|
||||
}
|
||||
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::USED) {
|
||||
// This static will be stored in the llvm.used variable which is an array of i8*
|
||||
let cast = llvm::LLVMConstPointerCast(g, cx.type_i8p());
|
||||
cx.used_statics.borrow_mut().push(cast);
|
||||
|
||||
// Wasm statics with custom link sections get special treatment as they
|
||||
// go into custom sections of the wasm executable.
|
||||
if self.tcx.sess.opts.target_triple.triple().starts_with("wasm32") {
|
||||
if let Some(section) = attrs.link_section {
|
||||
let section = llvm::LLVMMDStringInContext(
|
||||
&self.llcx,
|
||||
section.as_str().as_ptr() as *const _,
|
||||
section.as_str().len() as c_uint,
|
||||
);
|
||||
let alloc = llvm::LLVMMDStringInContext(
|
||||
&self.llcx,
|
||||
alloc.bytes.as_ptr() as *const _,
|
||||
alloc.bytes.len() as c_uint,
|
||||
);
|
||||
let data = [section, alloc];
|
||||
let meta = llvm::LLVMMDNodeInContext(&self.llcx, data.as_ptr(), 2);
|
||||
llvm::LLVMAddNamedMetadataOperand(
|
||||
&self.llmod,
|
||||
"wasm.custom_sections\0".as_ptr() as *const _,
|
||||
meta,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
base::set_link_section(g, &attrs);
|
||||
}
|
||||
|
||||
if attrs.flags.contains(CodegenFnAttrFlags::USED) {
|
||||
// This static will be stored in the llvm.used variable which is an array of i8*
|
||||
let cast = llvm::LLVMConstPointerCast(g, &self.type_i8p());
|
||||
&self.used_statics.borrow_mut().push(cast);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,11 @@
|
||||
mod consts;
|
||||
mod type_;
|
||||
mod intrinsic;
|
||||
mod statics;
|
||||
|
||||
pub use self::builder::BuilderMethods;
|
||||
pub use self::backend::Backend;
|
||||
pub use self::consts::ConstMethods;
|
||||
pub use self::type_::{TypeMethods, BaseTypeMethods, DerivedTypeMethods};
|
||||
pub use self::intrinsic::{IntrinsicMethods, BaseIntrinsicMethods, DerivedIntrinsicMethods};
|
||||
pub use self::statics::StaticMethods;
|
||||
|
36
src/librustc_codegen_llvm/interfaces/statics.rs
Normal file
36
src/librustc_codegen_llvm/interfaces/statics.rs
Normal file
@ -0,0 +1,36 @@
|
||||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use rustc::ty::layout::Align;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use super::backend::Backend;
|
||||
|
||||
pub trait StaticMethods<'tcx>: Backend {
|
||||
fn static_ptrcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value;
|
||||
fn static_bitcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value;
|
||||
fn static_addr_of_mut(
|
||||
&self,
|
||||
cv: Self::Value,
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> Self::Value;
|
||||
fn static_addr_of(
|
||||
&self,
|
||||
cv: Self::Value,
|
||||
align: Align,
|
||||
kind: Option<&str>,
|
||||
) -> Self::Value;
|
||||
fn get_static(&self, def_id: DefId) -> Self::Value;
|
||||
fn codegen_static(
|
||||
&self,
|
||||
def_id: DefId,
|
||||
is_mutable: bool,
|
||||
);
|
||||
}
|
@ -34,6 +34,7 @@
|
||||
|
||||
use interfaces::{
|
||||
BuilderMethods, ConstMethods, BaseTypeMethods, DerivedTypeMethods, DerivedIntrinsicMethods,
|
||||
StaticMethods,
|
||||
};
|
||||
|
||||
use rustc::session::Session;
|
||||
@ -853,7 +854,7 @@ fn codegen_msvc_try(
|
||||
|
||||
let tcx = cx.tcx;
|
||||
let tydesc = match tcx.lang_items().msvc_try_filter() {
|
||||
Some(did) => ::consts::get_static(cx, did),
|
||||
Some(did) => cx.get_static(did),
|
||||
None => bug!("msvc_try_filter not defined"),
|
||||
};
|
||||
let tok = catchpad.catch_pad(cs, &[tydesc, cx.const_i32(0), slot]);
|
||||
|
@ -12,11 +12,10 @@
|
||||
use callee;
|
||||
use context::CodegenCx;
|
||||
use builder::Builder;
|
||||
use consts;
|
||||
use monomorphize;
|
||||
use value::Value;
|
||||
|
||||
use interfaces::{BuilderMethods, ConstMethods, BaseTypeMethods, DerivedTypeMethods};
|
||||
use interfaces::{BuilderMethods, ConstMethods, BaseTypeMethods, DerivedTypeMethods, StaticMethods};
|
||||
|
||||
use rustc::ty::{self, Ty};
|
||||
use rustc::ty::layout::HasDataLayout;
|
||||
@ -120,7 +119,7 @@ pub fn get_vtable(
|
||||
|
||||
let vtable_const = cx.const_struct(&components, false);
|
||||
let align = cx.data_layout().pointer_align;
|
||||
let vtable = consts::addr_of(cx, vtable_const, align, Some("vtable"));
|
||||
let vtable = cx.static_addr_of(vtable_const, align, Some("vtable"));
|
||||
|
||||
debuginfo::create_vtable_metadata(cx, ty, vtable);
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
use callee;
|
||||
use builder::{Builder, MemFlags};
|
||||
use common::{self, IntPredicate};
|
||||
use consts;
|
||||
use meth;
|
||||
use monomorphize;
|
||||
use type_of::LayoutLlvmExt;
|
||||
@ -28,6 +27,7 @@
|
||||
|
||||
use interfaces::{
|
||||
BuilderMethods, ConstMethods, BaseTypeMethods, DerivedTypeMethods, DerivedIntrinsicMethods,
|
||||
StaticMethods,
|
||||
};
|
||||
|
||||
use syntax::symbol::Symbol;
|
||||
@ -380,10 +380,11 @@ fn codegen_terminator(&mut self,
|
||||
let index = self.codegen_operand(&mut bx, index).immediate();
|
||||
|
||||
let file_line_col = bx.cx().const_struct(&[filename, line, col], false);
|
||||
let file_line_col = consts::addr_of(bx.cx(),
|
||||
file_line_col,
|
||||
align,
|
||||
Some("panic_bounds_check_loc"));
|
||||
let file_line_col = bx.cx().static_addr_of(
|
||||
file_line_col,
|
||||
align,
|
||||
Some("panic_bounds_check_loc")
|
||||
);
|
||||
(lang_items::PanicBoundsCheckFnLangItem,
|
||||
vec![file_line_col, index, len])
|
||||
}
|
||||
@ -395,10 +396,11 @@ fn codegen_terminator(&mut self,
|
||||
&[msg_str, filename, line, col],
|
||||
false
|
||||
);
|
||||
let msg_file_line_col = consts::addr_of(bx.cx(),
|
||||
msg_file_line_col,
|
||||
align,
|
||||
Some("panic_loc"));
|
||||
let msg_file_line_col = bx.cx().static_addr_of(
|
||||
msg_file_line_col,
|
||||
align,
|
||||
Some("panic_loc")
|
||||
);
|
||||
(lang_items::PanicFnLangItem,
|
||||
vec![msg_file_line_col])
|
||||
}
|
||||
@ -518,10 +520,11 @@ fn codegen_terminator(&mut self,
|
||||
&[msg_str, filename, line, col],
|
||||
false,
|
||||
);
|
||||
let msg_file_line_col = consts::addr_of(bx.cx,
|
||||
msg_file_line_col,
|
||||
align,
|
||||
Some("panic_loc"));
|
||||
let msg_file_line_col = bx.cx.static_addr_of(
|
||||
msg_file_line_col,
|
||||
align,
|
||||
Some("panic_loc"),
|
||||
);
|
||||
|
||||
// Obtain the panic entry point.
|
||||
let def_id =
|
||||
|
@ -19,13 +19,12 @@
|
||||
use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Size};
|
||||
use builder::Builder;
|
||||
use common::{CodegenCx};
|
||||
use consts;
|
||||
use type_of::LayoutLlvmExt;
|
||||
use type_::Type;
|
||||
use syntax::ast::Mutability;
|
||||
use syntax::source_map::Span;
|
||||
use value::Value;
|
||||
use interfaces::{BuilderMethods, ConstMethods, BaseTypeMethods, DerivedTypeMethods};
|
||||
use interfaces::{BuilderMethods, ConstMethods, BaseTypeMethods, DerivedTypeMethods, StaticMethods};
|
||||
|
||||
use super::super::callee;
|
||||
use super::FunctionCx;
|
||||
@ -48,7 +47,7 @@ pub fn scalar_to_llvm(
|
||||
if layout.value == layout::Pointer {
|
||||
unsafe { llvm::LLVMConstIntToPtr(llval, llty) }
|
||||
} else {
|
||||
consts::bitcast(llval, llty)
|
||||
cx.static_bitcast(llval, llty)
|
||||
}
|
||||
},
|
||||
Scalar::Ptr(ptr) => {
|
||||
@ -57,9 +56,9 @@ pub fn scalar_to_llvm(
|
||||
Some(AllocType::Memory(alloc)) => {
|
||||
let init = const_alloc_to_llvm(cx, alloc);
|
||||
if alloc.mutability == Mutability::Mutable {
|
||||
consts::addr_of_mut(cx, init, alloc.align, None)
|
||||
cx.static_addr_of_mut(init, alloc.align, None)
|
||||
} else {
|
||||
consts::addr_of(cx, init, alloc.align, None)
|
||||
cx.static_addr_of(init, alloc.align, None)
|
||||
}
|
||||
}
|
||||
Some(AllocType::Function(fn_instance)) => {
|
||||
@ -67,19 +66,19 @@ pub fn scalar_to_llvm(
|
||||
}
|
||||
Some(AllocType::Static(def_id)) => {
|
||||
assert!(cx.tcx.is_static(def_id).is_some());
|
||||
consts::get_static(cx, def_id)
|
||||
cx.get_static(def_id)
|
||||
}
|
||||
None => bug!("missing allocation {:?}", ptr.alloc_id),
|
||||
};
|
||||
let llval = unsafe { llvm::LLVMConstInBoundsGEP(
|
||||
consts::bitcast(base_addr, cx.type_i8p()),
|
||||
cx.static_bitcast(base_addr, cx.type_i8p()),
|
||||
&cx.const_usize(ptr.offset.bytes()),
|
||||
1,
|
||||
) };
|
||||
if layout.value != layout::Pointer {
|
||||
unsafe { llvm::LLVMConstPtrToInt(llval, llty) }
|
||||
} else {
|
||||
consts::bitcast(llval, llty)
|
||||
cx.static_bitcast(llval, llty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
use base;
|
||||
use builder::Builder;
|
||||
use common::{CodegenCx, IntPredicate};
|
||||
use consts;
|
||||
use type_of::LayoutLlvmExt;
|
||||
use value::Value;
|
||||
use glue;
|
||||
@ -24,6 +23,7 @@
|
||||
|
||||
use interfaces::{
|
||||
BuilderMethods, ConstMethods, BaseTypeMethods, DerivedTypeMethods, DerivedIntrinsicMethods,
|
||||
StaticMethods,
|
||||
};
|
||||
|
||||
use super::{FunctionCx, LocalRef};
|
||||
@ -66,14 +66,14 @@ pub fn from_const_alloc(
|
||||
offset: Size,
|
||||
) -> PlaceRef<'tcx, &'ll Value> {
|
||||
let init = const_alloc_to_llvm(bx.cx(), alloc);
|
||||
let base_addr = consts::addr_of(bx.cx(), init, layout.align, None);
|
||||
let base_addr = bx.cx().static_addr_of(init, layout.align, None);
|
||||
|
||||
let llval = unsafe { LLVMConstInBoundsGEP(
|
||||
consts::bitcast(base_addr, bx.cx().type_i8p()),
|
||||
bx.cx().static_bitcast(base_addr, bx.cx().type_i8p()),
|
||||
&bx.cx().const_usize(offset.bytes()),
|
||||
1,
|
||||
)};
|
||||
let llval = consts::bitcast(llval, bx.cx().type_ptr_to(layout.llvm_type(bx.cx())));
|
||||
let llval = bx.cx().static_bitcast(llval, bx.cx().type_ptr_to(layout.llvm_type(bx.cx())));
|
||||
PlaceRef::new_sized(llval, layout, alloc.align)
|
||||
}
|
||||
|
||||
@ -497,7 +497,7 @@ pub fn codegen_place(&mut self,
|
||||
}
|
||||
mir::Place::Static(box mir::Static { def_id, ty }) => {
|
||||
let layout = cx.layout_of(self.monomorphize(&ty));
|
||||
PlaceRef::new_sized(consts::get_static(cx, def_id), layout, layout.align)
|
||||
PlaceRef::new_sized(cx.get_static(def_id), layout, layout.align)
|
||||
},
|
||||
mir::Place::Projection(box mir::Projection {
|
||||
ref base,
|
||||
|
@ -20,7 +20,6 @@
|
||||
use builder::Builder;
|
||||
use callee;
|
||||
use common::{self, IntPredicate, RealPredicate};
|
||||
use consts;
|
||||
use monomorphize;
|
||||
use type_::Type;
|
||||
use type_of::LayoutLlvmExt;
|
||||
@ -841,7 +840,7 @@ fn cast_int_to_float(bx: &Builder<'_, 'll, '_>,
|
||||
let max = bx.cx().const_uint_big(int_ty, MAX_F32_PLUS_HALF_ULP);
|
||||
let overflow = bx.icmp(IntPredicate::IntUGE, x, max);
|
||||
let infinity_bits = bx.cx().const_u32(ieee::Single::INFINITY.to_bits() as u32);
|
||||
let infinity = consts::bitcast(infinity_bits, float_ty);
|
||||
let infinity = bx.bitcast(infinity_bits, float_ty);
|
||||
bx.select(overflow, infinity, bx.uitofp(x, float_ty))
|
||||
} else {
|
||||
if signed {
|
||||
@ -922,7 +921,7 @@ fn cast_float_to_int(bx: &Builder<'_, 'll, '_>,
|
||||
64 => bx.cx().const_u64(bits as u64),
|
||||
n => bug!("unsupported float width {}", n),
|
||||
};
|
||||
consts::bitcast(bits_llval, float_ty)
|
||||
bx.bitcast(bits_llval, float_ty)
|
||||
};
|
||||
let (f_min, f_max) = match bx.cx().float_width(float_ty) {
|
||||
32 => compute_clamp_bounds_single(signed, int_ty),
|
||||
|
@ -17,7 +17,6 @@
|
||||
use asm;
|
||||
use attributes;
|
||||
use base;
|
||||
use consts;
|
||||
use context::CodegenCx;
|
||||
use declare;
|
||||
use llvm;
|
||||
@ -30,6 +29,7 @@
|
||||
use rustc::ty::TypeFoldable;
|
||||
use rustc::ty::layout::LayoutOf;
|
||||
use std::fmt;
|
||||
use interfaces::StaticMethods;
|
||||
|
||||
pub use rustc::mir::mono::MonoItem;
|
||||
|
||||
@ -54,7 +54,7 @@ fn define(&self, cx: &CodegenCx<'a, 'tcx>) {
|
||||
bug!("Expected Def::Static for {:?}, found nothing", def_id)
|
||||
}
|
||||
};
|
||||
consts::codegen_static(&cx, def_id, is_mutable);
|
||||
cx.codegen_static(def_id, is_mutable);
|
||||
}
|
||||
MonoItem::GlobalAsm(node_id) => {
|
||||
let item = cx.tcx.hir.expect_item(node_id);
|
||||
|
Loading…
Reference in New Issue
Block a user