Auto merge of #48896 - alexcrichton:bitcode-in-object, r=michaelwoerister
rustc: Enable embedding LLVM bitcode for iOS This commit updates rustc to embed bitcode in each object file generated by default when compiling for iOS. This was determined in #35968 as a step towards better compatibility with the iOS toolchain, so let's give it a spin and see how it turns out! Note that this also updates the `cc` dependency which should propagate this change of embedding bitcode for C dependencies as well.
This commit is contained in:
@@ -77,7 +77,7 @@ pub fn find(build: &mut Build) {
|
|||||||
.collect::<HashSet<_>>();
|
.collect::<HashSet<_>>();
|
||||||
for target in targets.into_iter() {
|
for target in targets.into_iter() {
|
||||||
let mut cfg = cc::Build::new();
|
let mut cfg = cc::Build::new();
|
||||||
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false)
|
cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false)
|
||||||
.target(&target).host(&build.build);
|
.target(&target).host(&build.build);
|
||||||
if target.contains("msvc") {
|
if target.contains("msvc") {
|
||||||
cfg.static_crt(true);
|
cfg.static_crt(true);
|
||||||
@@ -109,7 +109,7 @@ pub fn find(build: &mut Build) {
|
|||||||
let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
|
let hosts = build.hosts.iter().cloned().chain(iter::once(build.build)).collect::<HashSet<_>>();
|
||||||
for host in hosts.into_iter() {
|
for host in hosts.into_iter() {
|
||||||
let mut cfg = cc::Build::new();
|
let mut cfg = cc::Build::new();
|
||||||
cfg.cargo_metadata(false).opt_level(0).warnings(false).debug(false).cpp(true)
|
cfg.cargo_metadata(false).opt_level(2).warnings(false).debug(false).cpp(true)
|
||||||
.target(&host).host(&build.build);
|
.target(&host).host(&build.build);
|
||||||
let config = build.config.target_config.get(&host);
|
let config = build.config.target_config.get(&host);
|
||||||
if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
|
if let Some(cxx) = config.and_then(|c| c.cxx.as_ref()) {
|
||||||
|
|||||||
@@ -1288,6 +1288,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
|||||||
"run `dsymutil` and delete intermediate object files"),
|
"run `dsymutil` and delete intermediate object files"),
|
||||||
ui_testing: bool = (false, parse_bool, [UNTRACKED],
|
ui_testing: bool = (false, parse_bool, [UNTRACKED],
|
||||||
"format compiler diagnostics in a way that's better suitable for UI testing"),
|
"format compiler diagnostics in a way that's better suitable for UI testing"),
|
||||||
|
embed_bitcode: bool = (false, parse_bool, [TRACKED],
|
||||||
|
"embed LLVM bitcode in object files"),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_lib_output() -> CrateType {
|
pub fn default_lib_output() -> CrateType {
|
||||||
|
|||||||
@@ -473,6 +473,9 @@ pub struct TargetOptions {
|
|||||||
/// The default visibility for symbols in this target should be "hidden"
|
/// The default visibility for symbols in this target should be "hidden"
|
||||||
/// rather than "default"
|
/// rather than "default"
|
||||||
pub default_hidden_visibility: bool,
|
pub default_hidden_visibility: bool,
|
||||||
|
|
||||||
|
/// Whether or not bitcode is embedded in object files
|
||||||
|
pub embed_bitcode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TargetOptions {
|
impl Default for TargetOptions {
|
||||||
@@ -544,6 +547,7 @@ impl Default for TargetOptions {
|
|||||||
i128_lowering: false,
|
i128_lowering: false,
|
||||||
codegen_backend: "llvm".to_string(),
|
codegen_backend: "llvm".to_string(),
|
||||||
default_hidden_visibility: false,
|
default_hidden_visibility: false,
|
||||||
|
embed_bitcode: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -792,6 +796,7 @@ impl Target {
|
|||||||
key!(no_builtins, bool);
|
key!(no_builtins, bool);
|
||||||
key!(codegen_backend);
|
key!(codegen_backend);
|
||||||
key!(default_hidden_visibility, bool);
|
key!(default_hidden_visibility, bool);
|
||||||
|
key!(embed_bitcode, bool);
|
||||||
|
|
||||||
if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
|
if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
|
||||||
for name in array.iter().filter_map(|abi| abi.as_string()) {
|
for name in array.iter().filter_map(|abi| abi.as_string()) {
|
||||||
@@ -990,6 +995,7 @@ impl ToJson for Target {
|
|||||||
target_option_val!(no_builtins);
|
target_option_val!(no_builtins);
|
||||||
target_option_val!(codegen_backend);
|
target_option_val!(codegen_backend);
|
||||||
target_option_val!(default_hidden_visibility);
|
target_option_val!(default_hidden_visibility);
|
||||||
|
target_option_val!(embed_bitcode);
|
||||||
|
|
||||||
if default.abi_blacklist != self.options.abi_blacklist {
|
if default.abi_blacklist != self.options.abi_blacklist {
|
||||||
d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter()
|
d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter()
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ use syntax_pos::MultiSpan;
|
|||||||
use syntax_pos::symbol::Symbol;
|
use syntax_pos::symbol::Symbol;
|
||||||
use type_::Type;
|
use type_::Type;
|
||||||
use context::{is_pie_binary, get_reloc_model};
|
use context::{is_pie_binary, get_reloc_model};
|
||||||
|
use common::{C_bytes_in_context, val_ty};
|
||||||
use jobserver::{Client, Acquired};
|
use jobserver::{Client, Acquired};
|
||||||
use rustc_demangle;
|
use rustc_demangle;
|
||||||
|
|
||||||
@@ -262,6 +263,8 @@ pub struct ModuleConfig {
|
|||||||
// emscripten's ecc compiler, when used as the linker.
|
// emscripten's ecc compiler, when used as the linker.
|
||||||
obj_is_bitcode: bool,
|
obj_is_bitcode: bool,
|
||||||
no_integrated_as: bool,
|
no_integrated_as: bool,
|
||||||
|
embed_bitcode: bool,
|
||||||
|
embed_bitcode_marker: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleConfig {
|
impl ModuleConfig {
|
||||||
@@ -279,6 +282,8 @@ impl ModuleConfig {
|
|||||||
emit_asm: false,
|
emit_asm: false,
|
||||||
emit_obj: false,
|
emit_obj: false,
|
||||||
obj_is_bitcode: false,
|
obj_is_bitcode: false,
|
||||||
|
embed_bitcode: false,
|
||||||
|
embed_bitcode_marker: false,
|
||||||
no_integrated_as: false,
|
no_integrated_as: false,
|
||||||
|
|
||||||
no_verify: false,
|
no_verify: false,
|
||||||
@@ -299,6 +304,17 @@ impl ModuleConfig {
|
|||||||
self.time_passes = sess.time_passes();
|
self.time_passes = sess.time_passes();
|
||||||
self.inline_threshold = sess.opts.cg.inline_threshold;
|
self.inline_threshold = sess.opts.cg.inline_threshold;
|
||||||
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
|
self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
|
||||||
|
let embed_bitcode = sess.target.target.options.embed_bitcode ||
|
||||||
|
sess.opts.debugging_opts.embed_bitcode;
|
||||||
|
if embed_bitcode {
|
||||||
|
match sess.opts.optimize {
|
||||||
|
config::OptLevel::No |
|
||||||
|
config::OptLevel::Less => {
|
||||||
|
self.embed_bitcode_marker = embed_bitcode;
|
||||||
|
}
|
||||||
|
_ => self.embed_bitcode = embed_bitcode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Copy what clang does by turning on loop vectorization at O2 and
|
// Copy what clang does by turning on loop vectorization at O2 and
|
||||||
// slp vectorization at O3. Otherwise configure other optimization aspects
|
// slp vectorization at O3. Otherwise configure other optimization aspects
|
||||||
@@ -662,7 +678,7 @@ unsafe fn codegen(cgcx: &CodegenContext,
|
|||||||
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
|
let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
|
||||||
|
|
||||||
|
|
||||||
if write_bc || config.emit_bc_compressed {
|
if write_bc || config.emit_bc_compressed || config.embed_bitcode {
|
||||||
let thin;
|
let thin;
|
||||||
let old;
|
let old;
|
||||||
let data = if llvm::LLVMRustThinLTOAvailable() {
|
let data = if llvm::LLVMRustThinLTOAvailable() {
|
||||||
@@ -681,6 +697,11 @@ unsafe fn codegen(cgcx: &CodegenContext,
|
|||||||
timeline.record("write-bc");
|
timeline.record("write-bc");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if config.embed_bitcode {
|
||||||
|
embed_bitcode(cgcx, llcx, llmod, Some(data));
|
||||||
|
timeline.record("embed-bc");
|
||||||
|
}
|
||||||
|
|
||||||
if config.emit_bc_compressed {
|
if config.emit_bc_compressed {
|
||||||
let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
|
let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
|
||||||
let data = bytecode::encode(&mtrans.llmod_id, data);
|
let data = bytecode::encode(&mtrans.llmod_id, data);
|
||||||
@@ -689,6 +710,8 @@ unsafe fn codegen(cgcx: &CodegenContext,
|
|||||||
}
|
}
|
||||||
timeline.record("compress-bc");
|
timeline.record("compress-bc");
|
||||||
}
|
}
|
||||||
|
} else if config.embed_bitcode_marker {
|
||||||
|
embed_bitcode(cgcx, llcx, llmod, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()),
|
time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()),
|
||||||
@@ -796,6 +819,59 @@ unsafe fn codegen(cgcx: &CodegenContext,
|
|||||||
&cgcx.output_filenames))
|
&cgcx.output_filenames))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Embed the bitcode of an LLVM module in the LLVM module itself.
|
||||||
|
///
|
||||||
|
/// This is done primarily for iOS where it appears to be standard to compile C
|
||||||
|
/// code at least with `-fembed-bitcode` which creates two sections in the
|
||||||
|
/// executable:
|
||||||
|
///
|
||||||
|
/// * __LLVM,__bitcode
|
||||||
|
/// * __LLVM,__cmdline
|
||||||
|
///
|
||||||
|
/// It appears *both* of these sections are necessary to get the linker to
|
||||||
|
/// recognize what's going on. For us though we just always throw in an empty
|
||||||
|
/// cmdline section.
|
||||||
|
///
|
||||||
|
/// Furthermore debug/O1 builds don't actually embed bitcode but rather just
|
||||||
|
/// embed an empty section.
|
||||||
|
///
|
||||||
|
/// Basically all of this is us attempting to follow in the footsteps of clang
|
||||||
|
/// on iOS. See #35968 for lots more info.
|
||||||
|
unsafe fn embed_bitcode(cgcx: &CodegenContext,
|
||||||
|
llcx: ContextRef,
|
||||||
|
llmod: ModuleRef,
|
||||||
|
bitcode: Option<&[u8]>) {
|
||||||
|
let llconst = C_bytes_in_context(llcx, bitcode.unwrap_or(&[]));
|
||||||
|
let llglobal = llvm::LLVMAddGlobal(
|
||||||
|
llmod,
|
||||||
|
val_ty(llconst).to_ref(),
|
||||||
|
"rustc.embedded.module\0".as_ptr() as *const _,
|
||||||
|
);
|
||||||
|
llvm::LLVMSetInitializer(llglobal, llconst);
|
||||||
|
let section = if cgcx.opts.target_triple.contains("-ios") {
|
||||||
|
"__LLVM,__bitcode\0"
|
||||||
|
} else {
|
||||||
|
".llvmbc\0"
|
||||||
|
};
|
||||||
|
llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
|
||||||
|
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||||
|
|
||||||
|
let llconst = C_bytes_in_context(llcx, &[]);
|
||||||
|
let llglobal = llvm::LLVMAddGlobal(
|
||||||
|
llmod,
|
||||||
|
val_ty(llconst).to_ref(),
|
||||||
|
"rustc.embedded.cmdline\0".as_ptr() as *const _,
|
||||||
|
);
|
||||||
|
llvm::LLVMSetInitializer(llglobal, llconst);
|
||||||
|
let section = if cgcx.opts.target_triple.contains("-ios") {
|
||||||
|
"__LLVM,__cmdline\0"
|
||||||
|
} else {
|
||||||
|
".llvmcmd\0"
|
||||||
|
};
|
||||||
|
llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
|
||||||
|
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct CompiledModules {
|
pub(crate) struct CompiledModules {
|
||||||
pub modules: Vec<CompiledModule>,
|
pub modules: Vec<CompiledModule>,
|
||||||
pub metadata_module: CompiledModule,
|
pub metadata_module: CompiledModule,
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ fn main() {
|
|||||||
|
|
||||||
fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
|
fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
|
||||||
let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", ".libs")?;
|
let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", ".libs")?;
|
||||||
|
let cflags = env::var("CFLAGS").unwrap_or_default() + " -fvisibility=hidden -O2";
|
||||||
|
|
||||||
run(Command::new("sh")
|
run(Command::new("sh")
|
||||||
.current_dir(&native.out_dir)
|
.current_dir(&native.out_dir)
|
||||||
@@ -98,7 +99,7 @@ fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
|
|||||||
.arg("--disable-host-shared")
|
.arg("--disable-host-shared")
|
||||||
.arg(format!("--host={}", build_helper::gnu_target(target)))
|
.arg(format!("--host={}", build_helper::gnu_target(target)))
|
||||||
.arg(format!("--build={}", build_helper::gnu_target(host)))
|
.arg(format!("--build={}", build_helper::gnu_target(host)))
|
||||||
.env("CFLAGS", env::var("CFLAGS").unwrap_or_default() + " -fvisibility=hidden"));
|
.env("CFLAGS", cflags));
|
||||||
|
|
||||||
run(Command::new(build_helper::make(host))
|
run(Command::new(build_helper::make(host))
|
||||||
.current_dir(&native.out_dir)
|
.current_dir(&native.out_dir)
|
||||||
|
|||||||
Reference in New Issue
Block a user