initial implementation of the darwin_objc unstable feature

This commit is contained in:
Jo Bates
2025-06-04 19:49:13 -07:00
parent 02c7b1a7ac
commit 1ebf69d1b1
39 changed files with 1381 additions and 11 deletions

View File

@@ -16,6 +16,7 @@ use rustc_middle::mir::mono::MonoItem;
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
use rustc_middle::ty::{self, Instance};
use rustc_middle::{bug, span_bug};
use rustc_span::Symbol;
use tracing::{debug, instrument, trace};
use crate::common::CodegenCx;
@@ -331,6 +332,10 @@ impl<'ll> CodegenCx<'ll, '_> {
}
g
} else if let Some(classname) = fn_attrs.objc_class {
self.get_objc_classref(classname)
} else if let Some(methname) = fn_attrs.objc_selector {
self.get_objc_selref(methname)
} else {
check_and_apply_linkage(self, fn_attrs, llty, sym, def_id)
};
@@ -543,8 +548,225 @@ impl<'ll> CodegenCx<'ll, '_> {
/// Add a global value to a list to be stored in the `llvm.compiler.used` variable,
/// an array of ptr.
pub(crate) fn add_compiler_used_global(&mut self, global: &'ll Value) {
self.compiler_used_statics.push(global);
pub(crate) fn add_compiler_used_global(&self, global: &'ll Value) {
self.compiler_used_statics.borrow_mut().push(global);
}
// We do our best here to match what Clang does when compiling Objective-C natively.
// See Clang's `CGObjCCommonMac::CreateCStringLiteral`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134
fn define_objc_classname(&self, classname: &str) -> &'ll Value {
assert_eq!(self.objc_abi_version(), 1);
let llval = self.null_terminate_const_bytes(classname.as_bytes());
let llty = self.val_ty(llval);
let sym = self.generate_local_symbol_name("OBJC_CLASS_NAME_");
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
set_global_alignment(self, g, self.tcx.data_layout.i8_align.abi);
llvm::set_initializer(g, llval);
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
llvm::set_section(g, c"__TEXT,__cstring,cstring_literals");
llvm::LLVMSetGlobalConstant(g, llvm::TRUE);
llvm::LLVMSetUnnamedAddress(g, llvm::UnnamedAddr::Global);
self.add_compiler_used_global(g);
g
}
// We do our best here to match what Clang does when compiling Objective-C natively.
// See Clang's `ObjCNonFragileABITypesHelper`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L6052
fn get_objc_class_t(&self) -> &'ll Type {
if let Some(class_t) = self.objc_class_t.get() {
return class_t;
}
assert_eq!(self.objc_abi_version(), 2);
// struct _class_t {
// struct _class_t* isa;
// struct _class_t* const superclass;
// void* cache;
// IMP* vtable;
// struct class_ro_t* ro;
// }
let class_t = self.type_named_struct("struct._class_t");
let els = [self.type_ptr(); 5];
let packed = false;
self.set_struct_body(class_t, &els, packed);
self.objc_class_t.set(Some(class_t));
class_t
}
// We do our best here to match what Clang does when compiling Objective-C natively. We
// deduplicate references within a CGU, but we need a reference definition in each referencing
// CGU. All attempts at using external references to a single reference definition result in
// linker errors.
fn get_objc_classref(&self, classname: Symbol) -> &'ll Value {
let mut classrefs = self.objc_classrefs.borrow_mut();
if let Some(classref) = classrefs.get(&classname).copied() {
return classref;
}
let g = match self.objc_abi_version() {
1 => {
// See Clang's `CGObjCMac::EmitClassRefFromId`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5205
let llval = self.define_objc_classname(classname.as_str());
let llty = self.type_ptr();
let sym = self.generate_local_symbol_name("OBJC_CLASS_REFERENCES_");
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
llvm::set_initializer(g, llval);
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
llvm::set_section(g, c"__OBJC,__cls_refs,literal_pointers,no_dead_strip");
self.add_compiler_used_global(g);
g
}
2 => {
// See Clang's `CGObjCNonFragileABIMac::EmitClassRefFromId`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7423
let llval = {
let extern_sym = format!("OBJC_CLASS_$_{}", classname.as_str());
let extern_llty = self.get_objc_class_t();
self.declare_global(&extern_sym, extern_llty)
};
let llty = self.type_ptr();
let sym = self.generate_local_symbol_name("OBJC_CLASSLIST_REFERENCES_$_");
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
llvm::set_initializer(g, llval);
llvm::set_linkage(g, llvm::Linkage::InternalLinkage);
llvm::set_section(g, c"__DATA,__objc_classrefs,regular,no_dead_strip");
self.add_compiler_used_global(g);
g
}
_ => unreachable!(),
};
classrefs.insert(classname, g);
g
}
// We do our best here to match what Clang does when compiling Objective-C natively. We
// deduplicate references within a CGU, but we need a reference definition in each referencing
// CGU. All attempts at using external references to a single reference definition result in
// linker errors.
//
// Newer versions of Apple Clang generate calls to `@"objc_msgSend$methname"` selector stub
// functions. We don't currently do that. The code we generate is closer to what Apple Clang
// generates with the `-fno-objc-msgsend-selector-stubs` option.
fn get_objc_selref(&self, methname: Symbol) -> &'ll Value {
let mut selrefs = self.objc_selrefs.borrow_mut();
if let Some(selref) = selrefs.get(&methname).copied() {
return selref;
}
let abi_version = self.objc_abi_version();
// See Clang's `CGObjCCommonMac::CreateCStringLiteral`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L4134
let methname_llval = self.null_terminate_const_bytes(methname.as_str().as_bytes());
let methname_llty = self.val_ty(methname_llval);
let methname_sym = self.generate_local_symbol_name("OBJC_METH_VAR_NAME_");
let methname_g = self.define_global(&methname_sym, methname_llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", methname_sym);
});
set_global_alignment(self, methname_g, self.tcx.data_layout.i8_align.abi);
llvm::set_initializer(methname_g, methname_llval);
llvm::set_linkage(methname_g, llvm::Linkage::PrivateLinkage);
llvm::set_section(
methname_g,
match abi_version {
1 => c"__TEXT,__cstring,cstring_literals",
2 => c"__TEXT,__objc_methname,cstring_literals",
_ => unreachable!(),
},
);
llvm::LLVMSetGlobalConstant(methname_g, llvm::TRUE);
llvm::LLVMSetUnnamedAddress(methname_g, llvm::UnnamedAddr::Global);
self.add_compiler_used_global(methname_g);
// See Clang's `CGObjCMac::EmitSelectorAddr`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5243
// And Clang's `CGObjCNonFragileABIMac::EmitSelectorAddr`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L7586
let selref_llval = methname_g;
let selref_llty = self.type_ptr();
let selref_sym = self.generate_local_symbol_name("OBJC_SELECTOR_REFERENCES_");
let selref_g = self.define_global(&selref_sym, selref_llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", selref_sym);
});
set_global_alignment(self, selref_g, self.tcx.data_layout.pointer_align().abi);
llvm::set_initializer(selref_g, selref_llval);
llvm::set_externally_initialized(selref_g, true);
llvm::set_linkage(
selref_g,
match abi_version {
1 => llvm::Linkage::PrivateLinkage,
2 => llvm::Linkage::InternalLinkage,
_ => unreachable!(),
},
);
llvm::set_section(
selref_g,
match abi_version {
1 => c"__OBJC,__message_refs,literal_pointers,no_dead_strip",
2 => c"__DATA,__objc_selrefs,literal_pointers,no_dead_strip",
_ => unreachable!(),
},
);
self.add_compiler_used_global(selref_g);
selrefs.insert(methname, selref_g);
selref_g
}
// We do our best here to match what Clang does when compiling Objective-C natively.
// See Clang's `ObjCTypesHelper`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5936
// And Clang's `CGObjCMac::EmitModuleInfo`:
// https://github.com/llvm/llvm-project/blob/llvmorg-20.1.8/clang/lib/CodeGen/CGObjCMac.cpp#L5151
pub(crate) fn define_objc_module_info(&mut self) {
assert_eq!(self.objc_abi_version(), 1);
// struct _objc_module {
// long version; // Hardcoded to 7 in Clang.
// long size; // sizeof(struct _objc_module)
// char* name; // Hardcoded to classname "" in Clang.
// struct _objc_symtab* symtab; // Null without class or category definitions.
// }
let llty = self.type_named_struct("struct._objc_module");
let i32_llty = self.type_i32();
let ptr_llty = self.type_ptr();
let packed = false;
self.set_struct_body(llty, &[i32_llty, i32_llty, ptr_llty, ptr_llty], packed);
let version = self.const_uint(i32_llty, 7);
let size = self.const_uint(i32_llty, 16);
let name = self.define_objc_classname("");
let symtab = self.const_null(ptr_llty);
let llval = crate::common::named_struct(llty, &[version, size, name, symtab]);
let sym = "OBJC_MODULES";
let g = self.define_global(&sym, llty).unwrap_or_else(|| {
bug!("symbol `{}` is already defined", sym);
});
set_global_alignment(self, g, self.tcx.data_layout.pointer_align().abi);
llvm::set_initializer(g, llval);
llvm::set_linkage(g, llvm::Linkage::PrivateLinkage);
llvm::set_section(g, c"__OBJC,__module_info,regular,no_dead_strip");
self.add_compiler_used_global(g);
}
}