Rollup merge of #71970 - thombles:ios-bitcode-improvements, r=alexcrichton

Improve bitcode generation for Apple platforms

Some improvements for iOS bitcode support suggested by Alex over at https://github.com/getditto/rust-bitcode/issues/9. r? @alexcrichton

This improves Rust's bitcode generation so that provided you have a compatible LLVM version, Rust targeting iOS should work out of the box when compiled into bitcode-enabled apps, and when submitted to the App Store. I've tested these changes using Xcode 11.4.1 and Apple's vendored LLVM, [tag `swift-5.2.3-RELEASE`](https://github.com/apple/llvm-project/releases/tag/swift-5.2.3-RELEASE).

1. Force `aarch64-apple-ios` and `aarch64-apple-tvos` targets to always emit full bitcode sections, even when cargo is trying to optimise by invoking `rustc` with `-Cembed-bitcode=no`. Since Apple recommends bitcode on iOS and requires it on tvOS it is likely that this is what developers intend. Currently you need to override the codegen options with `RUSTFLAGS`, which is far from obvious.
2. Provide an LLVM cmdline in the target spec. Apple's bitcode verification process looks for some arguments. For Rust modules to be accepted we must pretend they were produced similarly. A suitable default is provided in `TargetOptions` for iOS, copied directly from the a clang equivalent section.

In the context of Apple platforms, the predominant purpose of bitcode is App Store submissions, so simulator and 32-bit targets are not relevant. I'm hoping that the cmdline strings will not be a maintenance burden to keep up-to-date. If the event of any future incompatibilities, hopefully a custom target config would offer enough flexibility to work around it. It's impossible to say for sure.

Due to unrelated build errors I haven't been able to build and test a full tvOS toolchain. I've stopped short of providing a similar `bitcode_llvm_cmdline` until I can actually test it.
This commit is contained in:
Dylan DPC 2020-05-08 18:48:28 +02:00 committed by GitHub
commit e3a4ff0d76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 41 additions and 24 deletions

View File

@ -651,10 +651,8 @@ pub(crate) unsafe fn codegen(
"LLVM_module_codegen_embed_bitcode",
&module.name[..],
);
embed_bitcode(cgcx, llcx, llmod, Some(data));
embed_bitcode(cgcx, llcx, llmod, &config.bc_cmdline, data);
}
} else if config.emit_obj == EmitObj::ObjectCode(BitcodeSection::Marker) {
embed_bitcode(cgcx, llcx, llmod, None);
}
if config.emit_ir {
@ -777,8 +775,8 @@ pub(crate) unsafe fn codegen(
/// * __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.
/// recognize what's going on. A suitable cmdline value is taken from the
/// target spec.
///
/// Furthermore debug/O1 builds don't actually embed bitcode but rather just
/// embed an empty section.
@ -789,9 +787,10 @@ unsafe fn embed_bitcode(
cgcx: &CodegenContext<LlvmCodegenBackend>,
llcx: &llvm::Context,
llmod: &llvm::Module,
bitcode: Option<&[u8]>,
cmdline: &str,
bitcode: &[u8],
) {
let llconst = common::bytes_in_context(llcx, bitcode.unwrap_or(&[]));
let llconst = common::bytes_in_context(llcx, bitcode);
let llglobal = llvm::LLVMAddGlobal(
llmod,
common::val_ty(llconst),
@ -800,14 +799,15 @@ unsafe fn embed_bitcode(
llvm::LLVMSetInitializer(llglobal, llconst);
let is_apple = cgcx.opts.target_triple.triple().contains("-ios")
|| cgcx.opts.target_triple.triple().contains("-darwin");
|| cgcx.opts.target_triple.triple().contains("-darwin")
|| cgcx.opts.target_triple.triple().contains("-tvos");
let section = if is_apple { "__LLVM,__bitcode\0" } else { ".llvmbc\0" };
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
let llconst = common::bytes_in_context(llcx, &[]);
let llconst = common::bytes_in_context(llcx, cmdline.as_bytes());
let llglobal = llvm::LLVMAddGlobal(
llmod,
common::val_ty(llconst),

View File

@ -68,10 +68,6 @@ pub enum BitcodeSection {
// No bitcode section.
None,
// An empty bitcode section (to placate tools such as the iOS linker that
// require this section even if they don't use it).
Marker,
// A full, uncompressed bitcode section.
Full,
}
@ -101,6 +97,7 @@ pub struct ModuleConfig {
pub emit_ir: bool,
pub emit_asm: bool,
pub emit_obj: EmitObj,
pub bc_cmdline: String,
// Miscellaneous flags. These are mostly copied from command-line
// options.
@ -147,14 +144,8 @@ impl ModuleConfig {
|| sess.opts.cg.linker_plugin_lto.enabled()
{
EmitObj::Bitcode
} else if need_crate_bitcode_for_rlib(sess) {
let force_full = need_crate_bitcode_for_rlib(sess);
match sess.opts.optimize {
config::OptLevel::No | config::OptLevel::Less if !force_full => {
EmitObj::ObjectCode(BitcodeSection::Marker)
}
_ => EmitObj::ObjectCode(BitcodeSection::Full),
}
} else if need_bitcode_in_object(sess) {
EmitObj::ObjectCode(BitcodeSection::Full)
} else {
EmitObj::ObjectCode(BitcodeSection::None)
};
@ -211,6 +202,7 @@ impl ModuleConfig {
false
),
emit_obj,
bc_cmdline: sess.target.target.options.bitcode_llvm_cmdline.clone(),
verify_llvm_ir: sess.verify_llvm_ir(),
no_prepopulate_passes: sess.opts.cg.no_prepopulate_passes,
@ -372,10 +364,12 @@ pub struct CompiledModules {
pub allocator_module: Option<CompiledModule>,
}
fn need_crate_bitcode_for_rlib(sess: &Session) -> bool {
sess.opts.cg.embed_bitcode
fn need_bitcode_in_object(sess: &Session) -> bool {
let requested_for_rlib = sess.opts.cg.embed_bitcode
&& sess.crate_types.borrow().contains(&CrateType::Rlib)
&& sess.opts.output_types.contains_key(&OutputType::Exe)
&& sess.opts.output_types.contains_key(&OutputType::Exe);
let forced_by_target = sess.target.target.options.forces_embed_bitcode;
requested_for_rlib || forced_by_target
}
fn need_pre_lto_bitcode_for_incr_comp(sess: &Session) -> bool {

View File

@ -19,6 +19,18 @@ pub fn target() -> TargetResult {
eliminate_frame_pointer: false,
max_atomic_width: Some(128),
abi_blacklist: super::arm_base::abi_blacklist(),
forces_embed_bitcode: true,
// Taken from a clang build on Xcode 11.4.1.
// These arguments are not actually invoked - they just have
// to look right to pass App Store validation.
bitcode_llvm_cmdline: "-triple\0\
arm64-apple-ios11.0.0\0\
-emit-obj\0\
-disable-llvm-passes\0\
-target-abi\0\
darwinpcs\0\
-Os\0"
.to_string(),
..base
},
})

View File

@ -19,6 +19,7 @@ pub fn target() -> TargetResult {
eliminate_frame_pointer: false,
max_atomic_width: Some(128),
abi_blacklist: super::arm_base::abi_blacklist(),
forces_embed_bitcode: true,
..base
},
})

View File

@ -783,6 +783,10 @@ pub struct TargetOptions {
// If we give emcc .o files that are actually .bc files it
// will 'just work'.
pub obj_is_bitcode: bool,
/// Whether the target requires that emitted object code includes bitcode.
pub forces_embed_bitcode: bool,
/// Content of the LLVM cmdline section associated with embedded bitcode.
pub bitcode_llvm_cmdline: String,
/// Don't use this field; instead use the `.min_atomic_width()` method.
pub min_atomic_width: Option<u64>,
@ -939,6 +943,8 @@ impl Default for TargetOptions {
allow_asm: true,
has_elf_tls: false,
obj_is_bitcode: false,
forces_embed_bitcode: false,
bitcode_llvm_cmdline: String::new(),
min_atomic_width: None,
max_atomic_width: None,
atomic_cas: true,
@ -1278,6 +1284,8 @@ impl Target {
key!(main_needs_argc_argv, bool);
key!(has_elf_tls, bool);
key!(obj_is_bitcode, bool);
key!(forces_embed_bitcode, bool);
key!(bitcode_llvm_cmdline);
key!(max_atomic_width, Option<u64>);
key!(min_atomic_width, Option<u64>);
key!(atomic_cas, bool);
@ -1505,6 +1513,8 @@ impl ToJson for Target {
target_option_val!(main_needs_argc_argv);
target_option_val!(has_elf_tls);
target_option_val!(obj_is_bitcode);
target_option_val!(forces_embed_bitcode);
target_option_val!(bitcode_llvm_cmdline);
target_option_val!(min_atomic_width);
target_option_val!(max_atomic_width);
target_option_val!(atomic_cas);