Skip to content

Commit

Permalink
Rollup merge of #71970 - thombles:ios-bitcode-improvements, r=alexcri…
Browse files Browse the repository at this point in the history
…chton

Improve bitcode generation for Apple platforms

Some improvements for iOS bitcode support suggested by Alex over at getditto/rust-bitcode#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.
  • Loading branch information
Dylan-DPC authored May 8, 2020
2 parents a9eb01a + 4fea9cd commit e3a4ff0
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 24 deletions.
18 changes: 9 additions & 9 deletions src/librustc_codegen_llvm/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand All @@ -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),
Expand All @@ -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),
Expand Down
24 changes: 9 additions & 15 deletions src/librustc_codegen_ssa/back/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
12 changes: 12 additions & 0 deletions src/librustc_target/spec/aarch64_apple_ios.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
})
Expand Down
1 change: 1 addition & 0 deletions src/librustc_target/spec/aarch64_apple_tvos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
})
Expand Down
10 changes: 10 additions & 0 deletions src/librustc_target/spec/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit e3a4ff0

Please sign in to comment.