Auto merge of #115641 - durin42:llvm-18-fatlto-take-2, r=nikic
lto: load bitcode sections by name Upstream change llvm/llvm-project@6b539f5eb8 changed `isSectionBitcode` works and it now only respects `.llvm.lto` sections instead of also `.llvmbc`, which it says was never intended to be used for LTO. We instead load sections by name, and sniff for raw bitcode by hand. This is an alternative approach to #115136, where we tried the same thing using the `object` crate, but it got too fraught to continue. r? `@nikic` `@rustbot` label: +llvm-main
This commit is contained in:
commit
ffc48e3eda
@ -1,4 +1,6 @@
|
|||||||
use crate::back::write::{self, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers};
|
use crate::back::write::{
|
||||||
|
self, bitcode_section_name, save_temp_bitcode, CodegenDiagnosticsStage, DiagnosticHandlers,
|
||||||
|
};
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib,
|
DynamicLinkingWithLTO, LlvmError, LtoBitcodeFromRlib, LtoDisallowed, LtoDylib,
|
||||||
};
|
};
|
||||||
@ -120,6 +122,7 @@ fn prepare_lto(
|
|||||||
info!("adding bitcode from {}", name);
|
info!("adding bitcode from {}", name);
|
||||||
match get_bitcode_slice_from_object_data(
|
match get_bitcode_slice_from_object_data(
|
||||||
child.data(&*archive_data).expect("corrupt rlib"),
|
child.data(&*archive_data).expect("corrupt rlib"),
|
||||||
|
cgcx,
|
||||||
) {
|
) {
|
||||||
Ok(data) => {
|
Ok(data) => {
|
||||||
let module = SerializedModule::FromRlib(data.to_vec());
|
let module = SerializedModule::FromRlib(data.to_vec());
|
||||||
@ -141,10 +144,29 @@ fn prepare_lto(
|
|||||||
Ok((symbols_below_threshold, upstream_modules))
|
Ok((symbols_below_threshold, upstream_modules))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bitcode_slice_from_object_data(obj: &[u8]) -> Result<&[u8], LtoBitcodeFromRlib> {
|
fn get_bitcode_slice_from_object_data<'a>(
|
||||||
|
obj: &'a [u8],
|
||||||
|
cgcx: &CodegenContext<LlvmCodegenBackend>,
|
||||||
|
) -> Result<&'a [u8], LtoBitcodeFromRlib> {
|
||||||
|
// We're about to assume the data here is an object file with sections, but if it's raw LLVM IR that
|
||||||
|
// won't work. Fortunately, if that's what we have we can just return the object directly, so we sniff
|
||||||
|
// the relevant magic strings here and return.
|
||||||
|
if obj.starts_with(b"\xDE\xC0\x17\x0B") || obj.starts_with(b"BC\xC0\xDE") {
|
||||||
|
return Ok(obj);
|
||||||
|
}
|
||||||
|
// We drop the "__LLVM," prefix here because on Apple platforms there's a notion of "segment name"
|
||||||
|
// which in the public API for sections gets treated as part of the section name, but internally
|
||||||
|
// in MachOObjectFile.cpp gets treated separately.
|
||||||
|
let section_name = bitcode_section_name(cgcx).trim_start_matches("__LLVM,");
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
let data =
|
let data = unsafe {
|
||||||
unsafe { llvm::LLVMRustGetBitcodeSliceFromObjectData(obj.as_ptr(), obj.len(), &mut len) };
|
llvm::LLVMRustGetSliceFromObjectDataByName(
|
||||||
|
obj.as_ptr(),
|
||||||
|
obj.len(),
|
||||||
|
section_name.as_ptr(),
|
||||||
|
&mut len,
|
||||||
|
)
|
||||||
|
};
|
||||||
if !data.is_null() {
|
if !data.is_null() {
|
||||||
assert!(len != 0);
|
assert!(len != 0);
|
||||||
let bc = unsafe { slice::from_raw_parts(data, len) };
|
let bc = unsafe { slice::from_raw_parts(data, len) };
|
||||||
|
@ -873,6 +873,27 @@ fn create_section_with_flags_asm(section_name: &str, section_flags: &str, data:
|
|||||||
asm
|
asm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn target_is_apple(cgcx: &CodegenContext<LlvmCodegenBackend>) -> bool {
|
||||||
|
cgcx.opts.target_triple.triple().contains("-ios")
|
||||||
|
|| cgcx.opts.target_triple.triple().contains("-darwin")
|
||||||
|
|| cgcx.opts.target_triple.triple().contains("-tvos")
|
||||||
|
|| cgcx.opts.target_triple.triple().contains("-watchos")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_is_aix(cgcx: &CodegenContext<LlvmCodegenBackend>) -> bool {
|
||||||
|
cgcx.opts.target_triple.triple().contains("-aix")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn bitcode_section_name(cgcx: &CodegenContext<LlvmCodegenBackend>) -> &'static str {
|
||||||
|
if target_is_apple(cgcx) {
|
||||||
|
"__LLVM,__bitcode\0"
|
||||||
|
} else if target_is_aix(cgcx) {
|
||||||
|
".ipa\0"
|
||||||
|
} else {
|
||||||
|
".llvmbc\0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Embed the bitcode of an LLVM module in the LLVM module itself.
|
/// 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
|
/// This is done primarily for iOS where it appears to be standard to compile C
|
||||||
@ -933,11 +954,8 @@ unsafe fn embed_bitcode(
|
|||||||
// Unfortunately, LLVM provides no way to set custom section flags. For ELF
|
// Unfortunately, LLVM provides no way to set custom section flags. For ELF
|
||||||
// and COFF we emit the sections using module level inline assembly for that
|
// and COFF we emit the sections using module level inline assembly for that
|
||||||
// reason (see issue #90326 for historical background).
|
// reason (see issue #90326 for historical background).
|
||||||
let is_aix = cgcx.opts.target_triple.triple().contains("-aix");
|
let is_aix = target_is_aix(cgcx);
|
||||||
let is_apple = cgcx.opts.target_triple.triple().contains("-ios")
|
let is_apple = target_is_apple(cgcx);
|
||||||
|| cgcx.opts.target_triple.triple().contains("-darwin")
|
|
||||||
|| cgcx.opts.target_triple.triple().contains("-tvos")
|
|
||||||
|| cgcx.opts.target_triple.triple().contains("-watchos");
|
|
||||||
if is_apple
|
if is_apple
|
||||||
|| is_aix
|
|| is_aix
|
||||||
|| cgcx.opts.target_triple.triple().starts_with("wasm")
|
|| cgcx.opts.target_triple.triple().starts_with("wasm")
|
||||||
@ -952,13 +970,7 @@ unsafe fn embed_bitcode(
|
|||||||
);
|
);
|
||||||
llvm::LLVMSetInitializer(llglobal, llconst);
|
llvm::LLVMSetInitializer(llglobal, llconst);
|
||||||
|
|
||||||
let section = if is_apple {
|
let section = bitcode_section_name(cgcx);
|
||||||
"__LLVM,__bitcode\0"
|
|
||||||
} else if is_aix {
|
|
||||||
".ipa\0"
|
|
||||||
} else {
|
|
||||||
".llvmbc\0"
|
|
||||||
};
|
|
||||||
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
|
llvm::LLVMSetSection(llglobal, section.as_ptr().cast());
|
||||||
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
|
||||||
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
|
llvm::LLVMSetGlobalConstant(llglobal, llvm::True);
|
||||||
|
@ -2322,6 +2322,12 @@ extern "C" {
|
|||||||
len: usize,
|
len: usize,
|
||||||
out_len: &mut usize,
|
out_len: &mut usize,
|
||||||
) -> *const u8;
|
) -> *const u8;
|
||||||
|
pub fn LLVMRustGetSliceFromObjectDataByName(
|
||||||
|
data: *const u8,
|
||||||
|
len: usize,
|
||||||
|
name: *const u8,
|
||||||
|
out_len: &mut usize,
|
||||||
|
) -> *const u8;
|
||||||
|
|
||||||
pub fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>;
|
pub fn LLVMRustLinkerNew(M: &Module) -> &mut Linker<'_>;
|
||||||
pub fn LLVMRustLinkerAdd(
|
pub fn LLVMRustLinkerAdd(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
@ -1558,6 +1559,38 @@ LLVMRustGetBitcodeSliceFromObjectData(const char *data,
|
|||||||
return BitcodeOrError->getBufferStart();
|
return BitcodeOrError->getBufferStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find a section of an object file by name. Fail if the section is missing or
|
||||||
|
// empty.
|
||||||
|
extern "C" const char *LLVMRustGetSliceFromObjectDataByName(const char *data,
|
||||||
|
size_t len,
|
||||||
|
const char *name,
|
||||||
|
size_t *out_len) {
|
||||||
|
*out_len = 0;
|
||||||
|
StringRef Data(data, len);
|
||||||
|
MemoryBufferRef Buffer(Data, ""); // The id is unused.
|
||||||
|
file_magic Type = identify_magic(Buffer.getBuffer());
|
||||||
|
Expected<std::unique_ptr<object::ObjectFile>> ObjFileOrError =
|
||||||
|
object::ObjectFile::createObjectFile(Buffer, Type);
|
||||||
|
if (!ObjFileOrError) {
|
||||||
|
LLVMRustSetLastError(toString(ObjFileOrError.takeError()).c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
for (const object::SectionRef &Sec : (*ObjFileOrError)->sections()) {
|
||||||
|
Expected<StringRef> Name = Sec.getName();
|
||||||
|
if (Name && *Name == name) {
|
||||||
|
Expected<StringRef> SectionOrError = Sec.getContents();
|
||||||
|
if (!SectionOrError) {
|
||||||
|
LLVMRustSetLastError(toString(SectionOrError.takeError()).c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
*out_len = SectionOrError->size();
|
||||||
|
return SectionOrError->data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LLVMRustSetLastError("could not find requested section");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Computes the LTO cache key for the provided 'ModId' in the given 'Data',
|
// Computes the LTO cache key for the provided 'ModId' in the given 'Data',
|
||||||
// storing the result in 'KeyOut'.
|
// storing the result in 'KeyOut'.
|
||||||
// Currently, this cache key is a SHA-1 hash of anything that could affect
|
// Currently, this cache key is a SHA-1 hash of anything that could affect
|
||||||
|
Loading…
x
Reference in New Issue
Block a user