Improve Objective-C support (#4777)

* WIP: Objective-C support

* Further work on implementation

* ObjC dynamic cast

* Add swift stub class attribute

* Classes, protocols and ivars

* Fix compilation issues

* Fix objc ir codegen

* Add objc linker option

* Add swift stub classref get ir gen

* Minor cleanup

* Fix objc link flag being added on non-darwin platforms

* Refactor objc gen

* remove use of std::nullopt

* Emit protocol tables

* Remove unused variable

* Formatting

* Fix build in release mode. Thanks for nothing, c++.

* Fix consistency

* Fix dynamic casts

* Fix tocall parentfd ref and arm msgsend call

* Make instance variables work

* Implicitly add isa pointer to objc classes.

* Fix protocol referencing & allow pragma mangle

* Fix protocol linkage

* Fix direct call support

* always generate var type for methods

* Fix test 16096a

* Fix extern ivar symbol gen, retain method decls

* Remove arm32 and x86 support

* Check method and ivar info before pushing to member list

* Make ObjcMethod info untyped.

* Make ivar and method gen more robust

* Generate optional protocol symbols

* Use bitcasting instead of creating multiple type defs

* Fix invalid protocol list struct gen

* More codegen robustness

* emit protocol table as const

* Make protocol table anon struct

* Fix callable type, generate protocol_list_t properly.

* Cast vthis to argtype

* Handle protorefs and classrefs properly

* seperate label ref and deref

* Fix method lookup

* Enable objective-c tests

* Enable objc_call_static test

* Scan both classes and protocols for method ref

* Enable objective-c tests on arm as well.

* supress objc linker warning in tests

* Fix class and protocol gen structure

* Fix objc_protocol_sections test

* ObjcMethod only get callee for functions with bodies

* Fix protocol class method gen

* Make ObjcMethod anon again

* Fix missing emit calls

* Fix classref gen

* Implement some of the requested changes

* Enable compilable tests

* Fix property selector gen, ugly hack for final funcs.

* Fix segfault in referencing fd->type

* Refactor implementation

* Fix null references in class and method lookup

* include unordered_map

* Get functionality on-par with prev impl.

* Fix super context calls

* Move -L-w flag to d_do_test and use IN_LLVM in objc.d/h

* add LDC version tag to -L-w flag

* Update CHANGELOG.md
This commit is contained in:
Luna 2024-12-03 04:26:27 +01:00 committed by GitHub
parent a1e694c16b
commit 82878ef32c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 1568 additions and 195 deletions

View file

@ -7,6 +7,7 @@
- ldc2.conf: %%ldcconfigpath%% placeholder added - specifies the directory where current configuration file is located. (#4717) - ldc2.conf: %%ldcconfigpath%% placeholder added - specifies the directory where current configuration file is located. (#4717)
- Add support for building against a system copy of zlib through `-DPHOBOS_SYSTEM_ZLIB=ON`. (#4742) - Add support for building against a system copy of zlib through `-DPHOBOS_SYSTEM_ZLIB=ON`. (#4742)
- Emscripten: The compiler now mimicks a musl Linux platform wrt. extra predefined versions (`linux`, `Posix`, `CRuntime_Musl`, `CppRuntime_LLVM`). (#4750) - Emscripten: The compiler now mimicks a musl Linux platform wrt. extra predefined versions (`linux`, `Posix`, `CRuntime_Musl`, `CppRuntime_LLVM`). (#4750)
- Objective-C: The compiler now properly supports Objective-C classes and protocols, as well as swift stub classes (via the `@swift` UDA). (#4777)
#### Platform support #### Platform support
- Supports LLVM 15 - 19. - Supports LLVM 15 - 19.

View file

@ -627,6 +627,7 @@ immutable Msgtable[] msgtable =
{ "udaHidden", "_hidden" }, { "udaHidden", "_hidden" },
{ "udaNoSanitize", "noSanitize" }, { "udaNoSanitize", "noSanitize" },
{ "udaNoSplitStack", "_noSplitStack" }, { "udaNoSplitStack", "_noSplitStack" },
{ "udaSwiftStub", "swift"},
// IN_LLVM: DCompute specific types and functionss // IN_LLVM: DCompute specific types and functionss
{ "dcompute" }, { "dcompute" },

View file

@ -156,6 +156,9 @@ extern (C++) struct ObjcClassDeclaration
/// `true` if this class is externally defined. /// `true` if this class is externally defined.
bool isExtern = false; bool isExtern = false;
/// `true` if this class is a Swift stub
version(IN_LLVM) bool isSwiftStub = false;
/// Name of this class. /// Name of this class.
Identifier identifier; Identifier identifier;
@ -264,6 +267,16 @@ extern(C++) abstract class Objc
*/ */
abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const; abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const;
/**
* Marks the given class as a Swift stub class.
*
* Params:
* cd = the class declaration to set as a swift stub
* sc = the scope from the semantic phase
*/
version(IN_LLVM)
abstract void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const;
/** /**
* Validates function declarations declared optional. * Validates function declarations declared optional.
* *
@ -452,6 +465,12 @@ static if (!IN_LLVM)
// noop // noop
} }
version(IN_LLVM)
override void setAsSwiftStub(ClassDeclaration, Scope*) const
{
// noop
}
override void validateOptional(FuncDeclaration) const override void validateOptional(FuncDeclaration) const
{ {
// noop // noop
@ -532,6 +551,7 @@ version (IN_LLVM) {} else
{ {
cd.classKind = ClassKind.objc; cd.classKind = ClassKind.objc;
cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0; cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0;
this.setAsSwiftStub(cd, cd._scope);
} }
override void setObjc(InterfaceDeclaration id) override void setObjc(InterfaceDeclaration id)
@ -826,6 +846,42 @@ version (IN_LLVM) {} else
errorSupplemental(expression.loc, "`tupleof` is not available for members " ~ errorSupplemental(expression.loc, "`tupleof` is not available for members " ~
"of Objective-C classes. Please use the Objective-C runtime instead"); "of Objective-C classes. Please use the Objective-C runtime instead");
} }
version(IN_LLVM) {
override void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const
{
const count = declaredAsSwiftStubCount(cd, sc);
cd.objc.isSwiftStub = count > 0;
if (count > 1)
.error(cd.loc, "%s `%s` can only declare a class as a swift stub once", cd.kind, cd.toPrettyChars);
}
/// Returns: the number of times `cd` has been declared as optional.
private int declaredAsSwiftStubCount(ClassDeclaration cd, Scope* sc) const
{
int count;
foreachUda(cd, sc, (e) {
if (!e.isTypeExp())
return 0;
auto typeExp = e.isTypeExp();
if (typeExp.type.ty != Tenum)
return 0;
auto typeEnum = cast(TypeEnum) typeExp.type;
if (isCoreUda(typeEnum.sym, Id.udaSwiftStub))
count++;
return 0;
});
return count;
}
}
} }
/* /*

View file

@ -37,6 +37,9 @@ struct ObjcClassDeclaration
{ {
d_bool isMeta; d_bool isMeta;
d_bool isExtern; d_bool isExtern;
#if IN_LLVM
d_bool isSwiftStub;
#endif
Identifier* identifier; Identifier* identifier;
ClassDeclaration* classDeclaration; ClassDeclaration* classDeclaration;
@ -67,6 +70,9 @@ public:
virtual void checkLinkage(FuncDeclaration* fd) = 0; virtual void checkLinkage(FuncDeclaration* fd) = 0;
virtual bool isVirtual(const FuncDeclaration*) const = 0; virtual bool isVirtual(const FuncDeclaration*) const = 0;
virtual void setAsOptional(FuncDeclaration *fd, Scope *sc) const = 0; virtual void setAsOptional(FuncDeclaration *fd, Scope *sc) const = 0;
#if IN_LLVM
virtual void setAsSwiftStub(ClassDeclaration* cd, Scope *sc) const = 0;
#endif
virtual void validateOptional(FuncDeclaration *fd) const = 0; virtual void validateOptional(FuncDeclaration *fd) const = 0;
virtual ClassDeclaration* getParent(FuncDeclaration*, ClassDeclaration*) const = 0; virtual ClassDeclaration* getParent(FuncDeclaration*, ClassDeclaration*) const = 0;
virtual void addToClassMethodList(FuncDeclaration*, ClassDeclaration*) const = 0; virtual void addToClassMethodList(FuncDeclaration*, ClassDeclaration*) const = 0;

View file

@ -48,6 +48,10 @@ static llvm::cl::opt<bool> linkNoCpp(
"link-no-cpp", llvm::cl::ZeroOrMore, llvm::cl::Hidden, "link-no-cpp", llvm::cl::ZeroOrMore, llvm::cl::Hidden,
llvm::cl::desc("Disable automatic linking with the C++ standard library.")); llvm::cl::desc("Disable automatic linking with the C++ standard library."));
static llvm::cl::opt<bool> linkNoObjc(
"link-no-objc", llvm::cl::ZeroOrMore, llvm::cl::Hidden,
llvm::cl::desc("Disable automatic linking with the Objective-C runtime library."));
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
namespace { namespace {
@ -72,6 +76,7 @@ private:
virtual void addXRayLinkFlags(const llvm::Triple &triple); virtual void addXRayLinkFlags(const llvm::Triple &triple);
virtual bool addCompilerRTArchiveLinkFlags(llvm::StringRef baseName, virtual bool addCompilerRTArchiveLinkFlags(llvm::StringRef baseName,
const llvm::Triple &triple); const llvm::Triple &triple);
virtual void addObjcStdlibLinkFlags(const llvm::Triple &triple);
virtual void addLinker(); virtual void addLinker();
virtual void addUserSwitches(); virtual void addUserSwitches();
@ -467,6 +472,13 @@ void ArgsBuilder::addCppStdlibLinkFlags(const llvm::Triple &triple) {
} }
} }
void ArgsBuilder::addObjcStdlibLinkFlags(const llvm::Triple &triple) {
if (linkNoObjc)
return;
args.push_back("-lobjc");
}
// Adds all required link flags for PGO. // Adds all required link flags for PGO.
void ArgsBuilder::addProfileRuntimeLinkFlags(const llvm::Triple &triple) { void ArgsBuilder::addProfileRuntimeLinkFlags(const llvm::Triple &triple) {
const auto searchPaths = const auto searchPaths =
@ -732,6 +744,13 @@ void ArgsBuilder::addDefaultPlatformLibs() {
break; break;
} }
if (triple.isOSDarwin()) {
// libobjc is more or less required, so we link against it here.
// This could be prettier, though.
addObjcStdlibLinkFlags(triple);
}
if (triple.isWindowsGNUEnvironment()) { if (triple.isWindowsGNUEnvironment()) {
// This is really more of a kludge, as linking in the Winsock functions // This is really more of a kludge, as linking in the Winsock functions
// should be handled by the pragma(lib, ...) in std.socket, but it // should be handled by the pragma(lib, ...) in std.socket, but it

View file

@ -29,12 +29,11 @@ using namespace dmd;
*/ */
struct AArch64TargetABI : TargetABI { struct AArch64TargetABI : TargetABI {
private: private:
const bool isDarwin;
IndirectByvalRewrite indirectByvalRewrite; IndirectByvalRewrite indirectByvalRewrite;
ArgTypesRewrite argTypesRewrite; ArgTypesRewrite argTypesRewrite;
bool isAAPCS64VaList(Type *t) { bool isAAPCS64VaList(Type *t) {
if (isDarwin) if (isDarwin())
return false; return false;
// look for a __va_list struct in a `std` C++ namespace // look for a __va_list struct in a `std` C++ namespace
@ -51,7 +50,7 @@ private:
} }
public: public:
AArch64TargetABI() : isDarwin(global.params.targetTriple->isOSDarwin()) {} AArch64TargetABI() {}
bool returnInArg(TypeFunction *tf, bool) override { bool returnInArg(TypeFunction *tf, bool) override {
if (tf->isref()) { if (tf->isref()) {
@ -108,7 +107,7 @@ public:
} }
// https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html#//apple_ref/doc/uid/TP40013702-SW1 // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html#//apple_ref/doc/uid/TP40013702-SW1
if (isDarwin) { if (isDarwin()) {
if (auto ts = tb->isTypeStruct()) { if (auto ts = tb->isTypeStruct()) {
if (ts->sym->fields.empty() && ts->sym->isPOD()) { if (ts->sym->fields.empty() && ts->sym->isPOD()) {
fty.args.erase(fty.args.begin() + i); fty.args.erase(fty.args.begin() + i);
@ -166,7 +165,7 @@ public:
} }
Type *vaListType() override { Type *vaListType() override {
if (isDarwin) if (isDarwin())
return TargetABI::vaListType(); // char* return TargetABI::vaListType(); // char*
// We need to pass the actual va_list type for correct mangling. Simply // We need to pass the actual va_list type for correct mangling. Simply
@ -176,9 +175,11 @@ public:
return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")); return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list"));
} }
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override { const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override {
assert(isDarwin());
// see objc/message.h for objc_msgSend selection rules // see objc/message.h for objc_msgSend selection rules
return "objc_msgSend"; return directcall ? "objc_msgSendSuper" : "objc_msgSend";
} }
}; };

View file

@ -207,7 +207,7 @@ Type *TargetABI::vaListType() {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
const char *TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty) { const char *TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) {
llvm_unreachable("Unknown Objective-C ABI"); llvm_unreachable("Unknown Objective-C ABI");
} }

View file

@ -70,6 +70,7 @@ protected:
// interface called by codegen // interface called by codegen
struct TargetABI { struct TargetABI {
public:
virtual ~TargetABI() = default; virtual ~TargetABI() = default;
/// Returns the ABI for the target we're compiling for /// Returns the ABI for the target we're compiling for
@ -117,6 +118,11 @@ struct TargetABI {
global.params.targetTriple->getOS() == llvm::Triple::NetBSD; global.params.targetTriple->getOS() == llvm::Triple::NetBSD;
} }
/// Returns true if the target is darwin-based.
bool isDarwin() {
return global.params.targetTriple->isOSDarwin();
}
/// Returns true if the D function uses sret (struct return). /// Returns true if the D function uses sret (struct return).
/// `needsThis` is true if the function type is for a non-static member /// `needsThis` is true if the function type is for a non-static member
/// function. /// function.
@ -171,7 +177,7 @@ struct TargetABI {
virtual Type *vaListType(); virtual Type *vaListType();
/// Returns Objective-C message send function /// Returns Objective-C message send function
virtual const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty); virtual const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall);
/***** Static Helpers *****/ /***** Static Helpers *****/

View file

@ -122,14 +122,6 @@ struct ArmTargetABI : TargetABI {
// solution is found there, this should be adapted). // solution is found there, this should be adapted).
return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list")); return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list"));
} }
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
// see objc/message.h for objc_msgSend selection rules
if (fty.arg_sret) {
return "objc_msgSend_stret";
}
return "objc_msgSend";
}
}; };
TargetABI *getArmTargetABI() { return new ArmTargetABI; } TargetABI *getArmTargetABI() { return new ArmTargetABI; }

View file

@ -165,7 +165,7 @@ struct X86_64TargetABI : TargetABI {
Type *vaListType() override; Type *vaListType() override;
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override; const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override;
private: private:
LLType *getValistType(); LLType *getValistType();
@ -196,9 +196,6 @@ private:
} }
}; };
// The public getter for abi.cpp
TargetABI *getX86_64TargetABI() { return new X86_64TargetABI; }
bool X86_64TargetABI::returnInArg(TypeFunction *tf, bool) { bool X86_64TargetABI::returnInArg(TypeFunction *tf, bool) {
if (tf->isref()) { if (tf->isref()) {
return false; return false;
@ -382,21 +379,19 @@ Type *X86_64TargetABI::vaListType() {
TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag"))); TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag")));
} }
const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) {
IrFuncTy &fty) { assert(isDarwin());
// see objc/message.h for objc_msgSend selection rules // see objc/message.h for objc_msgSend selection rules
if (fty.arg_sret) { if (fty.arg_sret) {
return "objc_msgSend_stret"; return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret";
} }
if (ret) { // float, double, long double return
// complex long double return if (ret && ret->isfloating()) {
if (ret->ty == TY::Tcomplex80) { return ret->ty == TY::Tcomplex80 ? "objc_msgSend_fp2ret" : "objc_msgSend_fpret";
return "objc_msgSend_fp2ret";
}
// long double return
if (ret->ty == TY::Tfloat80 || ret->ty == TY::Timaginary80) {
return "objc_msgSend_fpret";
}
} }
return "objc_msgSend"; return directcall ? "objc_msgSendSuper" : "objc_msgSend";
} }
// The public getter for abi.cpp
TargetABI *getX86_64TargetABI() { return new X86_64TargetABI; }

View file

@ -23,15 +23,13 @@
using namespace dmd; using namespace dmd;
struct X86TargetABI : TargetABI { struct X86TargetABI : TargetABI {
const bool isDarwin;
const bool isMSVC; const bool isMSVC;
bool returnStructsInRegs; bool returnStructsInRegs;
IntegerRewrite integerRewrite; IntegerRewrite integerRewrite;
IndirectByvalRewrite indirectByvalRewrite; IndirectByvalRewrite indirectByvalRewrite;
X86TargetABI() X86TargetABI()
: isDarwin(global.params.targetTriple->isOSDarwin()), : isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) {
isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) {
using llvm::Triple; using llvm::Triple;
auto os = global.params.targetTriple->getOS(); auto os = global.params.targetTriple->getOS();
returnStructsInRegs = returnStructsInRegs =
@ -230,7 +228,7 @@ struct X86TargetABI : TargetABI {
// Clang does not pass empty structs, while it seems that GCC does, // Clang does not pass empty structs, while it seems that GCC does,
// at least on Linux x86. We don't know whether the C compiler will // at least on Linux x86. We don't know whether the C compiler will
// be Clang or GCC, so just assume Clang on Darwin and G++ on Linux. // be Clang or GCC, so just assume Clang on Darwin and G++ on Linux.
if (externD || !isDarwin) if (externD || !isDarwin())
return; return;
size_t i = 0; size_t i = 0;
@ -272,19 +270,6 @@ struct X86TargetABI : TargetABI {
} }
} }
} }
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
// see objc/message.h for objc_msgSend selection rules
assert(isDarwin);
if (fty.arg_sret) {
return "objc_msgSend_stret";
}
// float, double, long double return
if (ret && ret->isfloating() && !ret->iscomplex()) {
return "objc_msgSend_fpret";
}
return "objc_msgSend";
}
}; };
// The public getter for abi.cpp. // The public getter for abi.cpp.

View file

@ -327,6 +327,15 @@ DValue *DtoCastClass(const Loc &loc, DValue *val, Type *_to) {
return DtoDynamicCastObject(loc, val, _to); return DtoDynamicCastObject(loc, val, _to);
} }
bool DtoIsObjcLinkage(Type *_to) {
if (auto to = _to->isTypeClass()) {
DtoResolveClass(to->sym);
return to->sym->classKind == ClassKind::objc;
}
return false;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static void resolveObjectAndClassInfoClasses() { static void resolveObjectAndClassInfoClasses() {
@ -339,15 +348,39 @@ static void resolveObjectAndClassInfoClasses() {
} }
DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) { DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) {
resolveObjectAndClassInfoClasses();
// Dynamic casting in Objective-C works differently from D.
// We call objc_opt_isKindOfClass to get a bool defining
// whether the cast is valid, if it is then we go ahead.
if (DtoIsObjcLinkage(_to)) {
llvm::Function *kindOfClassFunc =
getRuntimeFunction(loc, gIR->module, "objc_opt_isKindOfClass");
// Get the object.
LLValue *obj = DtoRVal(val);
// Get class_t handle
LLValue *objTy = getNullPtr();
if (auto thandle = _to->isClassHandle()) {
objTy = gIR->objc.deref(thandle, getOpaquePtrType());
}
// objc_opt_isKindOfClass will check if id is null
// by itself, so we don't need to add an extra check.
// objc_opt_isKindOfClass(id) ? id : null
LLValue *objCastable = gIR->CreateCallOrInvoke(kindOfClassFunc, obj, objTy);
LLValue *ret = gIR->ir->CreateSelect(objCastable, obj, getNullPtr());
return new DImValue(_to, ret);
}
// call: // call:
// Object _d_dynamic_cast(Object o, ClassInfo c) // Object _d_dynamic_cast(Object o, ClassInfo c)
llvm::Function *func = llvm::Function *func =
getRuntimeFunction(loc, gIR->module, "_d_dynamic_cast"); getRuntimeFunction(loc, gIR->module, "_d_dynamic_cast");
LLFunctionType *funcTy = func->getFunctionType(); LLFunctionType *funcTy = func->getFunctionType();
resolveObjectAndClassInfoClasses();
// Object o // Object o
LLValue *obj = DtoRVal(val); LLValue *obj = DtoRVal(val);
assert(funcTy->getParamType(0) == obj->getType()); assert(funcTy->getParamType(0) == obj->getType());
@ -368,14 +401,46 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *_to) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) { DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *_to) {
// call:
// Object _d_interface_cast(void* p, ClassInfo c)
llvm::Function *func =
getRuntimeFunction(loc, gIR->module, "_d_interface_cast");
resolveObjectAndClassInfoClasses(); resolveObjectAndClassInfoClasses();
// Dynamic casting in Objective-C works differently from D.
// In this case we want to call the Objective-C runtime to first
// get a Class object from the `id`.
// Then check if class_conformsToProtocol returns true,
// if it does, then we can cast and return the casted value,
// otherwise return null.
if (DtoIsObjcLinkage(_to)) {
llvm::Function *getClassFunc =
getRuntimeFunction(loc, gIR->module, "object_getClass");
llvm::Function *kindOfProtocolFunc =
getRuntimeFunction(loc, gIR->module, "class_conformsToProtocol");
// id -> Class
LLValue *obj = DtoRVal(val);
LLValue *objClass = gIR->CreateCallOrInvoke(getClassFunc, obj);
// Get prototype_t handle
LLValue *protoTy = getNullPtr();
if (auto ifhndl = _to->isClassHandle()->isInterfaceDeclaration()) {
protoTy = gIR->objc.deref(ifhndl, getOpaquePtrType());
}
// Class && kindOfProtocolFunc(Class) ? id : null
LLValue *ret = gIR->ir->CreateSelect(
gIR->CreateCallOrInvoke(kindOfProtocolFunc, objClass, protoTy),
obj,
getNullPtr()
);
return new DImValue(_to, ret);
}
// call:
// Object _d_interface_cast(void* p, ClassInfo c)
llvm::Function *func =
getRuntimeFunction(loc, gIR->module, "_d_interface_cast");
// void* p // void* p
LLValue *ptr = DtoRVal(val); LLValue *ptr = DtoRVal(val);

View file

@ -39,6 +39,8 @@ DValue *DtoDynamicCastObject(const Loc &loc, DValue *val, Type *to);
DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *to); DValue *DtoDynamicCastInterface(const Loc &loc, DValue *val, Type *to);
bool DtoIsObjcLinkage(Type *to);
/// Returns pair of function pointer and vtable pointer. /// Returns pair of function pointer and vtable pointer.
std::pair<llvm::Value *, llvm::Value *> std::pair<llvm::Value *, llvm::Value *>
DtoVirtualFunctionPointer(DValue *inst, FuncDeclaration *fdecl); DtoVirtualFunctionPointer(DValue *inst, FuncDeclaration *fdecl);

View file

@ -109,6 +109,12 @@ public:
m->accept(this); m->accept(this);
} }
// Objective-C protocols don't have TypeInfo.
if (decl->classKind == ClassKind::objc) {
gIR->objc.getProtocol(decl);
return;
}
// Emit TypeInfo. // Emit TypeInfo.
IrClass *ir = getIrAggr(decl); IrClass *ir = getIrAggr(decl);
if (!ir->suppressTypeInfo()) { if (!ir->suppressTypeInfo()) {
@ -205,6 +211,12 @@ public:
m->accept(this); m->accept(this);
} }
// Objective-C class structure is initialized by calling getClassRef.
if (decl->classKind == ClassKind::objc) {
gIR->objc.getClass(decl);
return;
}
IrClass *ir = getIrAggr(decl); IrClass *ir = getIrAggr(decl);
ir->getInitSymbol(/*define=*/true); ir->getInitSymbol(/*define=*/true);

View file

@ -140,16 +140,31 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype,
} }
bool hasObjCSelector = false; bool hasObjCSelector = false;
if (fd && fd->_linkage == LINK::objc && thistype) { if (fd && fd->_linkage == LINK::objc) {
auto ftype = (TypeFunction*)fd->type;
if (fd->objc.selector) { if (fd->objc.selector) {
hasObjCSelector = true; hasObjCSelector = true;
} else if (fd->parent->isClassDeclaration()) { } else if (fd->parent->isClassDeclaration()) {
error(fd->loc, "%s `%s` is missing Objective-C `@selector`", fd->kind(), if(fd->isFinal() || ftype->isproperty()) {
fd->toPrettyChars());
// HACK: Ugly hack, but final functions for some reason don't actually declare a selector.
// However, this does make it more flexible.
// Also this will automatically generate selectors for @property declared
// functions, which the DIP specifies.
// Final function selector gen should be fixed, however.
fd->objc.selector = ObjcSelector::create(fd);
hasObjCSelector = true;
} else {
error(fd->loc, "%s `%s` is missing Objective-C `@selector`", fd->kind(),
fd->toPrettyChars());
}
} }
} }
if (hasObjCSelector) { if (hasObjCSelector) {
// TODO: make arg_objcselector to match dmd type
// SEL is in libobjc an opaque pointer.
// As such a void* is fine.
newIrFty.arg_objcSelector = new IrFuncTyArg(Type::tvoidptr, false); newIrFty.arg_objcSelector = new IrFuncTyArg(Type::tvoidptr, false);
++nextLLArgIdx; ++nextLLArgIdx;
} }

View file

@ -65,3 +65,6 @@ using llvm::IRBuilder;
#define LLConstantFP llvm::ConstantFP #define LLConstantFP llvm::ConstantFP
#define LLSmallVector llvm::SmallVector #define LLSmallVector llvm::SmallVector
#define LLConstantList std::vector<LLConstant *>
#define LLStringRef llvm::StringRef

View file

@ -1841,6 +1841,18 @@ DLValue *DtoIndexAggregate(LLValue *src, AggregateDeclaration *ad,
// ourselves, DtoType below would be enough. // ourselves, DtoType below would be enough.
DtoResolveDsymbol(ad); DtoResolveDsymbol(ad);
if (ad->classKind == ClassKind::objc) {
auto tHandle = getI32Type();
auto tOffset = DtoLoad(tHandle, gIR->objc.getIvar(vd)->offset);
// Offset is now stored in tOffset.
LLValue *ptr = src;
ptr = DtoBitCast(ptr, getOpaquePtrType());
ptr = DtoGEP1(getI8Type(), ptr, tOffset);
return new DLValue(vd->type, ptr);
}
// Look up field to index or offset to apply. // Look up field to index or offset to apply.
auto irTypeAggr = getIrType(ad->type)->isAggr(); auto irTypeAggr = getIrType(ad->type)->isAggr();
assert(irTypeAggr); assert(irTypeAggr);

View file

@ -205,7 +205,7 @@ bool DtoLowerMagicIntrinsic(IRState *p, FuncDeclaration *fndecl, CallExp *e,
/// ///
DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
Expressions *arguments, LLValue *sretPointer = nullptr); Expressions *arguments, LLValue *sretPointer = nullptr, bool directcall = false);
Type *stripModifiers(Type *type, bool transitive = false); Type *stripModifiers(Type *type, bool transitive = false);

View file

@ -10,122 +10,909 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "gen/objcgen.h" #include "gen/objcgen.h"
#include "dmd/mtype.h"
#include "dmd/objc.h" #include "dmd/objc.h"
#include "dmd/expression.h"
#include "dmd/declaration.h"
#include "dmd/identifier.h"
#include "gen/irstate.h" #include "gen/irstate.h"
#include "gen/runtime.h"
namespace { #include "ir/irfunction.h"
enum ABI { none = 0, fragile = 1, nonFragile = 2 };
ABI abi = nonFragile;
}
bool objc_isSupported(const llvm::Triple &triple) { bool objc_isSupported(const llvm::Triple &triple) {
if (triple.isOSDarwin()) { if (triple.isOSDarwin()) {
// Objective-C only supported on Darwin at this time // Objective-C only supported on Darwin at this time
// Additionally only Objective-C 2 is supported.
switch (triple.getArch()) { switch (triple.getArch()) {
case llvm::Triple::aarch64: // arm64 iOS, tvOS case llvm::Triple::aarch64: // arm64 iOS, tvOS, macOS, watchOS, visionOS
case llvm::Triple::arm: // armv6 iOS
case llvm::Triple::thumb: // thumbv7 iOS, watchOS
case llvm::Triple::x86_64: // OSX, iOS, tvOS sim case llvm::Triple::x86_64: // OSX, iOS, tvOS sim
abi = nonFragile;
return true;
case llvm::Triple::x86: // OSX, iOS, watchOS sim
abi = fragile;
return true; return true;
default: default:
break; return false;
} }
} }
return false; return false;
} }
LLGlobalVariable *ObjCState::getCStringVar(const char *symbol, // TYPE ENCODINGS
const llvm::StringRef &str, std::string objcGetTypeEncoding(Type *t) {
const char *section) { std::string tmp;
auto init = llvm::ConstantDataArray::getString(module.getContext(), str); switch (t->ty) {
auto var = new LLGlobalVariable(module, init->getType(), false, case TY::Tclass: {
LLGlobalValue::PrivateLinkage, init, symbol); if (auto klass = t->isTypeClass()) {
var->setSection(section); return klass->sym->classKind == ClassKind::objc ? "@" : "?";
return var; }
return "?";
}
case TY::Tfunction: {
tmp = objcGetTypeEncoding(t->nextOf());
tmp.append("@:");
if (auto func = t->isTypeFunction()) {
for (size_t i = 0; i < func->parameterList.length(); i++)
tmp.append(objcGetTypeEncoding(func->parameterList[i]->type));
}
return tmp;
}
case TY::Tpointer: {
// C string (char*)
if (t->nextOf()->ty == TY::Tchar)
return "*";
tmp.append("^");
tmp.append(objcGetTypeEncoding(t->nextOf()));
return tmp;
}
case TY::Tsarray: {
// Static arrays are encoded in the form of:
// [<element count><element type>]
auto typ = t->isTypeSArray();
uinteger_t count = typ->dim->toUInteger();
tmp.append("[");
tmp.append(std::to_string(count));
tmp.append(objcGetTypeEncoding(typ->next));
tmp.append("]");
return tmp;
}
case TY::Tstruct: {
// Structs are encoded in the form of:
// {<name>=<element types>}
// Unions are encoded as
// (<name>=<element types>)
auto sym = t->isTypeStruct()->sym;
bool isUnion = sym->isUnionDeclaration();
tmp.append(isUnion ? "(" : "{");
tmp.append(t->toChars());
tmp.append("=");
for(unsigned int i = 0; i < sym->numArgTypes(); i++) {
tmp.append(objcGetTypeEncoding(sym->argType(i)));
}
tmp.append(isUnion ? ")" : "}");
return tmp;
}
case TY::Tvoid: return "v";
case TY::Tbool: return "B";
case TY::Tint8: return "c";
case TY::Tuns8: return "C";
case TY::Tchar: return "C";
case TY::Tint16: return "s";
case TY::Tuns16: return "S";
case TY::Twchar: return "S";
case TY::Tint32: return "i";
case TY::Tuns32: return "I";
case TY::Tdchar: return "I";
case TY::Tint64: return "q";
case TY::Tuns64: return "Q";
case TY::Tfloat32: return "f";
case TY::Tcomplex32: return "jf";
case TY::Tfloat64: return "d";
case TY::Tcomplex64: return "jd";
case TY::Tfloat80: return "D";
case TY::Tcomplex80: return "jD";
default: return "?"; // unknown
}
} }
LLGlobalVariable *ObjCState::getMethVarName(const llvm::StringRef &name) { //
auto it = methVarNameMap.find(name); // STRING HELPERS
if (it != methVarNameMap.end()) { //
std::string objcGetClassRoSymbol(const char *name, bool meta) {
return objcGetSymbolName(meta ? "_OBJC_METACLASS_RO_$_" : "_OBJC_CLASS_RO_$_", name);
}
std::string objcGetClassSymbol(const char *name, bool meta) {
return objcGetSymbolName(meta ? "OBJC_METACLASS_$_" : "OBJC_CLASS_$_", name);
}
std::string objcGetClassLabelSymbol(const char *name) {
return objcGetSymbolName("OBJC_LABEL_CLASS_$_", name);
}
std::string objcGetClassMethodListSymbol(const char *className, bool meta) {
return objcGetSymbolName(meta ? "_OBJC_$_CLASS_METHODS_" : "_OBJC_$_INSTANCE_METHODS_", className);
}
std::string objcGetProtoMethodListSymbol(const char *className, bool meta, bool optional) {
return optional ?
objcGetSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_OPT_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_", className) :
objcGetSymbolName(meta ? "_OBJC_$_PROTOCOL_CLASS_METHODS_" : "_OBJC_$_PROTOCOL_INSTANCE_METHODS_", className);
}
std::string objcGetIvarListSymbol(const char *className) {
return objcGetSymbolName("_OBJC_$_INSTANCE_VARIABLES_", className);
}
std::string objcGetProtoSymbol(const char *name) {
return objcGetSymbolName("_OBJC_PROTOCOL_$_", name);
}
std::string objcGetProtoListSymbol(const char *name, bool isProtocol) {
return objcGetSymbolName(isProtocol ? "_OBJC_$_PROTOCOL_REFS_" : "_OBJC_CLASS_PROTOCOLS_$_", name);
}
std::string objcGetProtoLabelSymbol(const char *name) {
return objcGetSymbolName("_OBJC_LABEL_PROTOCOL_$_", name);
}
std::string objcGetIvarSymbol(const char *className, const char *varName) {
return ("OBJC_IVAR_$_" + std::string(className) + "." + std::string(varName));
}
std::string objcGetSymbolName(const char *dsymPrefix, const char *dsymName) {
return (std::string(dsymPrefix) + std::string(dsymName));
}
const char *objcResolveName(Dsymbol *decl) {
// Function names are based on selector.
if (auto funcdecl = decl->isFuncDeclaration()) {
return funcdecl->objc.selector->stringvalue;
}
// Class and interface names are determined by objc identifier.
if (auto classdecl = decl->isClassDeclaration()) {
return classdecl->objc.identifier->toChars();
}
return decl->ident->toChars();
}
//
// TYPE HELPERS
//
LLStructType *objcGetStubClassType(const llvm::Module& module) {
auto stubClassType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_STUBCLASS);
if (stubClassType)
return stubClassType;
stubClassType = LLStructType::create(
module.getContext(),
{
getOpaquePtrType(), // objc_object* isa
getOpaquePtrType(), // function pointer.
},
OBJC_STRUCTNAME_STUBCLASS
);
return stubClassType;
}
LLStructType *objcGetClassRoType(const llvm::Module& module) {
auto classRoType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_CLASSRO);
if (classRoType)
return classRoType;
classRoType = LLStructType::create(
module.getContext(),
{
getI32Type(), // uint32_t flags
getI32Type(), // uint32_t instanceStart
getI32Type(), // uint32_t instanceSize
getOpaquePtrType(), // void* layoutOrNonMetaClass
getOpaquePtrType(), // const char* name
getOpaquePtrType(), // method_list_t* baseMethods
getOpaquePtrType(), // protocol_list_t* baseProtocols
getOpaquePtrType(), // ivar_list_t* ivars
getOpaquePtrType(), // const uint8_t* weakIvarLayout
getOpaquePtrType(), // property_list_t* baseProperties
},
OBJC_STRUCTNAME_CLASSRO
);
return classRoType;
}
LLStructType *objcGetClassType(const llvm::Module& module) {
auto classType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_CLASS);
if (classType)
return classType;
classType = LLStructType::create(
module.getContext(),
{
getOpaquePtrType(), // objc_object* isa
getOpaquePtrType(), // objc_object* superclass
getOpaquePtrType(), // cache_t* cache
getOpaquePtrType(), // void* vtbl; (unused, set to null)
getOpaquePtrType(), // class_ro_t* ro
},
OBJC_STRUCTNAME_CLASS
);
return classType;
}
LLStructType *objcGetMethodType(const llvm::Module& module) {
auto methType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_METHOD);
if (methType)
return methType;
return LLStructType::create(
module.getContext(),
{
getOpaquePtrType(), // SEL name
getOpaquePtrType(), // const char *types
getOpaquePtrType(), // IMP imp
},
OBJC_STRUCTNAME_METHOD
);
}
LLStructType *objcGetIvarType(const llvm::Module& module) {
auto ivarType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_IVAR);
if (ivarType)
return ivarType;
ivarType = LLStructType::create(
module.getContext(),
{
getOpaquePtrType(), // int32_t *offset
getOpaquePtrType(), // const char *name
getOpaquePtrType(), // const char *type
getI32Type(), // uint32_t alignment_raw
getI32Type(), // uint32_t size
},
OBJC_STRUCTNAME_IVAR
);
return ivarType;
}
LLStructType *objcGetProtocolType(const llvm::Module& module) {
auto protoType = LLStructType::getTypeByName(module.getContext(), OBJC_STRUCTNAME_PROTO);
if (protoType)
return protoType;
protoType = LLStructType::create(
module.getContext(),
{
getOpaquePtrType(), // objc_object* isa
getOpaquePtrType(), // protocol_list_t* protocols
getOpaquePtrType(), // const char *mangledName
getOpaquePtrType(), // method_list_t* instanceMethods
getOpaquePtrType(), // method_list_t* classMethods
getOpaquePtrType(), // method_list_t* optionalInstanceMethods
getOpaquePtrType(), // method_list_t* optionalClassMethods
getOpaquePtrType(), // property_list_t* instanceProperties
getI32Type(), // uint32_t size
getI32Type(), // uint32_t flags
// Further fields follow but are optional and are fixed up at
// runtime.
},
OBJC_STRUCTNAME_PROTO
);
return protoType;
}
//
// *_list_t helpers.
//
LLConstant *objcEmitList(llvm::Module &module, LLConstantList objects, bool alignSizeT, bool countOnly) {
LLConstantList members;
// Emit nullptr for empty lists.
if (objects.empty())
return nullptr;
if (!countOnly) {
// Size of stored struct.
size_t allocSize = getTypeAllocSize(objects.front()->getType());
members.push_back(
alignSizeT ?
DtoConstSize_t(allocSize) :
DtoConstUint(allocSize)
);
}
// Object count
size_t objCount = objects.size();
members.push_back(
alignSizeT ?
DtoConstSize_t(objCount) :
DtoConstUint(objCount)
);
// Insert all the objects in to a constant array.
// This matches the codegen by the objective-c compiler.
auto arrayType = LLArrayType::get(
objects.front()->getType(),
objects.size()
);
members.push_back(LLConstantArray::get(
arrayType,
objects
));
return LLConstantStruct::getAnon(
members,
true
);
}
//
// Other helpers
//
LLConstant *objcOffsetIvar(size_t ivaroffset) {
return DtoConstUint(getPointerSize()+ivaroffset);
}
size_t objcGetClassFlags(ClassDeclaration *decl) {
size_t flags = 0;
if (!decl->baseClass)
flags |= RO_ROOT;
if (decl->objc.isMeta)
flags |= RO_META;
return flags;
}
ClassDeclaration *objcGetMetaClass(ClassDeclaration *decl) {
if (decl->objc.isMeta) {
auto curr = decl;
while (curr->baseClass)
curr = curr->baseClass;
return curr;
}
// Meta class for normal class.
return decl->objc.metaclass;
}
ClassDeclaration *objcGetSuper(ClassDeclaration *decl) {
return (decl->objc.isRootClass() || !decl->baseClass) ?
decl :
decl->baseClass;
}
//
// CLASSES
//
ptrdiff_t objcGetInstanceStart(llvm::Module &module, ClassDeclaration *decl, bool meta) {
ptrdiff_t start = meta ?
getTypeAllocSize(objcGetClassType(module)) :
getPointerSize();
// Meta-classes have no body.
if (meta || !decl->members || decl->members->length == 0)
return start;
for(d_size_t idx = 0; idx < decl->members->length; idx++)
{
auto var = (*decl->members)[idx]->isVarDeclaration();
if (var && var->isField())
return start+var->offset;
}
return start;
}
size_t objcGetInstanceSize(llvm::Module &module, ClassDeclaration *decl, bool meta) {
size_t start = meta ?
getTypeAllocSize(objcGetClassType(module)) :
getPointerSize();
if (meta)
return start;
return start+decl->size(decl->loc);
}
// Gets the empty cache variable, and creates a reference to it
// if needed.
LLGlobalVariable *getEmptyCache() {
static LLGlobalVariable *objcCache;
if(!objcCache)
objcCache = makeGlobal("_objc_empty_cache", nullptr, "", true, false);
return objcCache;
}
LLConstant *ObjCState::getClassRoTable(ClassDeclaration *decl) {
if (auto it = classRoTables.find(decl); it != classRoTables.end()) {
return it->second;
}
// No need to generate RO tables for externs.
// nor for null declarations.
if (!decl || decl->objc.isExtern)
return getNullPtr();
// Base Methods
auto meta = decl->objc.isMeta;
auto name = objcResolveName(decl);
auto sym = objcGetClassRoSymbol(name, meta);
LLConstantList members;
LLGlobalVariable *ivarList = nullptr;
LLGlobalVariable *protocolList = nullptr;
LLGlobalVariable *methodList = nullptr;
if (auto baseMethods = createMethodList(decl)) {
methodList = getOrCreate(objcGetClassMethodListSymbol(name, meta), baseMethods->getType(), OBJC_SECNAME_CONST);
methodList->setInitializer(baseMethods);
}
// Base Protocols
if (auto baseProtocols = createProtocolList(decl)) {
protocolList = getOrCreate(objcGetProtoListSymbol(name, false), baseProtocols->getType(), OBJC_SECNAME_CONST);
protocolList->setInitializer(baseProtocols);
}
if (!meta) {
// Instance variables
if (auto baseIvars = createIvarList(decl)) {
ivarList = getOrCreate(objcGetIvarListSymbol(name), baseIvars->getType(), OBJC_SECNAME_CONST);
ivarList->setInitializer(baseIvars);
}
}
// Build struct.
members.push_back(DtoConstUint(objcGetClassFlags(decl)));
members.push_back(DtoConstUint(objcGetInstanceStart(module, decl, meta)));
members.push_back(DtoConstUint(objcGetInstanceSize(module, decl, meta)));
members.push_back(getNullPtr());
members.push_back(getClassName(decl));
members.push_back(wrapNull(methodList));
members.push_back(wrapNull(protocolList));
members.push_back(wrapNull(ivarList));
members.push_back(getNullPtr());
members.push_back(getNullPtr());
auto table = makeGlobalWithBytes(sym, members, objcGetClassRoType(module));
table->setSection(OBJC_SECNAME_DATA);
classRoTables[decl] = table;
this->retain(table);
return table;
}
LLConstant *ObjCState::getClassTable(ClassDeclaration *decl) {
if (auto it = classTables.find(decl); it != classTables.end()) {
return it->second;
}
// If decl is null, just return a null pointer.
if (!decl)
return getNullPtr();
auto name = objcResolveName(decl);
auto sym = objcGetClassSymbol(name, decl->objc.isMeta);
auto table = getOrCreate(sym, objcGetClassType(module), OBJC_SECNAME_DATA, decl->objc.isExtern);
classTables[decl] = table;
this->retain(table);
// Extern tables don't need a body.
if (decl->objc.isExtern)
return table;
LLConstantList members;
members.push_back(getClassTable(objcGetMetaClass(decl))); // isa
members.push_back(getClassTable(objcGetSuper(decl))); // super
members.push_back(getEmptyCache()); // cache
members.push_back(getNullPtr()); // vtbl
members.push_back(getClassRoTable(decl)); // ro
table->setInitializer(LLConstantStruct::get(
objcGetClassType(module),
members
));
return table;
}
ObjcClassInfo *ObjCState::getClass(ClassDeclaration *decl) {
assert(!decl->isInterfaceDeclaration() && "Attempted to pass protocol into getClass!");
if (auto it = classes.find(decl); it != classes.end()) {
return &classes[decl];
}
// Since we may end up referring to this very quickly
// the name should be assigned ASAP.
classes[decl] = { .decl = decl };
auto classInfo = &classes[decl];
classInfo->table = (LLGlobalVariable *)getClassTable(decl);
classInfo->name = (LLGlobalVariable *)getClassName(decl);
classInfo->ref = (LLGlobalVariable *)getClassRef(decl);
if (!decl->objc.isMeta)
classInfo->ref->setInitializer(classInfo->table);
return classInfo;
}
LLConstant *ObjCState::getClassName(ClassDeclaration *decl) {
LLStringRef className(objcResolveName(decl));
if (auto it = classNames.find(className); it != classNames.end()) {
return it->second; return it->second;
} }
auto var = getCStringVar("OBJC_METH_VAR_NAME_", name, auto retval = makeGlobalStr(className, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME);
abi == nonFragile classNames[className] = retval;
? "__TEXT,__objc_methname,cstring_literals" this->retain(retval);
: "__TEXT,__cstring,cstring_literals"); return retval;
methVarNameMap[name] = var;
retain(var);
return var;
} }
LLGlobalVariable *ObjCState::getMethVarRef(const ObjcSelector &sel) { LLConstant *ObjCState::getClassRef(ClassDeclaration *decl) {
llvm::StringRef s(sel.stringvalue, sel.stringlen); LLStringRef className(objcResolveName(decl));
auto it = methVarRefMap.find(s); if (auto it = classRefs.find(className); it != classRefs.end()) {
if (it != methVarRefMap.end()) {
return it->second; return it->second;
} }
auto gvar = getMethVarName(s); auto retval = makeGlobal("OBJC_CLASSLIST_REFERENCES_$_", getOpaquePtrType(), OBJC_SECNAME_CLASSREFS);
auto selref = new LLGlobalVariable( classRefs[className] = retval;
module, gvar->getType(), this->retain(retval);
false, // prevent const elimination optimization return retval;
LLGlobalValue::PrivateLinkage, gvar, "OBJC_SELECTOR_REFERENCES_", nullptr,
LLGlobalVariable::NotThreadLocal, 0,
true); // externally initialized
selref->setSection(
abi == nonFragile
? "__DATA,__objc_selrefs,literal_pointers,no_dead_strip"
: "__OBJC,__message_refs,literal_pointers,no_dead_strip");
// Save for later lookup and prevent optimizer elimination
methVarRefMap[s] = selref;
retain(selref);
return selref;
} }
void ObjCState::retain(LLConstant *sym) {
retainedSymbols.push_back(sym); //
// PROTOCOLS
//
LLConstant *ObjCState::createProtocolTable(InterfaceDeclaration *decl) {
LLConstantList members;
LLGlobalVariable *protocolList = nullptr;
LLGlobalVariable *classMethodList = nullptr;
LLGlobalVariable *instanceMethodList = nullptr;
LLGlobalVariable *optClassMethodList = nullptr;
LLGlobalVariable *optInstanceMethodList = nullptr;
auto protoInfo = &protocols[decl];
auto name = objcResolveName(decl);
// Base Protocols
if (auto baseProtocols = createProtocolList(decl)) {
auto sym = objcGetProtoListSymbol(name, true);
protocolList = getOrCreateWeak(sym, baseProtocols->getType(), OBJC_SECNAME_CONST);
protocolList->setInitializer(baseProtocols);
}
// Instance methods
if (auto instanceMethodConsts = createMethodList(decl, false)) {
auto sym = objcGetProtoMethodListSymbol(name, false, false);
instanceMethodList = makeGlobal(sym, instanceMethodConsts->getType(), OBJC_SECNAME_CONST);
instanceMethodList->setInitializer(instanceMethodConsts);
instanceMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage);
instanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility);
}
// Optional instance methods
if (auto optInstanceMethodConsts = createMethodList(decl, true)) {
auto sym = objcGetProtoMethodListSymbol(name, false, true);
optInstanceMethodList = makeGlobal(sym, optInstanceMethodConsts->getType(), OBJC_SECNAME_CONST);
optInstanceMethodList->setInitializer(optInstanceMethodConsts);
optInstanceMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage);
optInstanceMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility);
}
// Class methods
if (auto classMethodConsts = createMethodList(decl->objc.metaclass, false)) {
auto sym = objcGetProtoMethodListSymbol(name, true, false);
classMethodList = makeGlobal(sym, classMethodConsts->getType(), OBJC_SECNAME_CONST);
classMethodList->setInitializer(classMethodConsts);
classMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage);
classMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility);
}
// Optional class methods
if (auto optClassMethodConsts = createMethodList(decl->objc.metaclass, true)) {
auto sym = objcGetProtoMethodListSymbol(name, true, true);
optClassMethodList = makeGlobal(sym, optClassMethodConsts->getType(), OBJC_SECNAME_CONST);
optClassMethodList->setInitializer(optClassMethodConsts);
optClassMethodList->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage);
optClassMethodList->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility);
}
auto protoType = objcGetProtocolType(module);
auto allocSize = getTypeAllocSize(protoType);
members.push_back(getNullPtr()); // isa
members.push_back(protoInfo->name); // mangledName
members.push_back(wrapNull(protocolList)); // protocols
members.push_back(wrapNull(instanceMethodList)); // instanceMethods
members.push_back(wrapNull(classMethodList)); // classMethods
members.push_back(wrapNull(optInstanceMethodList)); // optionalInstanceMethods
members.push_back(wrapNull(optClassMethodList)); // optionalClassMethods
members.push_back(getNullPtr()); // instanceProperties (TODO)
members.push_back(DtoConstUint(allocSize)); // size
members.push_back(DtoConstUint(0)); // flags
return LLConstantStruct::getAnon(
members,
true
);
} }
ObjcProtocolInfo *ObjCState::getProtocol(InterfaceDeclaration *decl) {
assert(decl->isInterfaceDeclaration() && "Attempted to pass class into getProtocol!");
if (auto it = protocols.find(decl); it != protocols.end()) {
return &protocols[decl];
}
protocols[decl] = { .decl = decl };
auto protoInfo = &protocols[decl];
auto name = objcResolveName(decl);
auto protoName = objcGetProtoSymbol(name);
auto protoLabel = objcGetProtoLabelSymbol(name);
protoInfo->name = makeGlobalStr(name, "OBJC_CLASS_NAME_", OBJC_SECNAME_CLASSNAME);
// We want it to be locally hidden and weak since the protocols
// may be declared in multiple object files.
auto protoTableConst = createProtocolTable(decl);
protoInfo->table = getOrCreateWeak(protoName, protoTableConst->getType(), OBJC_SECNAME_DATA);
protoInfo->table->setInitializer(protoTableConst);
protoInfo->ref = getOrCreateWeak(protoLabel, getOpaquePtrType(), OBJC_SECNAME_PROTOLIST);
protoInfo->ref->setInitializer(protoInfo->table);
this->retain(protoInfo->table);
this->retain(protoInfo->ref);
return protoInfo;
}
LLConstant *ObjCState::createProtocolList(ClassDeclaration *decl) {
LLConstantList protoList;
auto ifaces = decl->interfaces;
// Protocols
for(size_t i = 0; i < ifaces.length; i++) {
if (auto iface = ifaces.ptr[i]) {
if (auto ifacesym = (InterfaceDeclaration *)iface->sym) {
// Only add interfaces which have objective-c linkage
// TODO: throw an error if you try to include a non-objective-c interface?
if (ifacesym->classKind == ClassKind::objc) {
if (auto proto = getProtocol(ifacesym)) {
protoList.push_back(proto->table);
}
}
}
}
}
return objcEmitList(module, protoList, true, true);
}
//
// METHODS
//
ObjcMethodInfo *ObjCState::getMethod(FuncDeclaration *decl) {
if (auto it = methods.find(decl); it != methods.end()) {
return &methods[decl];
}
// Skip functions not marked as extern(Objective-C).
if (decl->_linkage != LINK::objc)
return nullptr;
methods[decl] = { .decl = decl };
auto methodInfo = &methods[decl];
auto name = objcResolveName(decl);
auto type = objcGetTypeEncoding(decl->type);
methodInfo->name = makeGlobalStr(name, "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME);
methodInfo->type = makeGlobalStr(type, "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE);
methodInfo->selector = makeGlobalRef(methodInfo->name, "OBJC_SELECTOR_REFERENCES_", OBJC_SECNAME_SELREFS, false, true);
methodInfo->llfunction = decl->fbody ?
DtoBitCast(DtoCallee(decl), getOpaquePtrType()) :
getNullPtr();
this->retain(methodInfo->name);
this->retain(methodInfo->type);
this->retain(methodInfo->selector);
return &methods[decl];
}
LLConstant *ObjCState::createMethodInfo(FuncDeclaration *decl) {
auto method = getMethod(decl);
return LLConstantStruct::get(
objcGetMethodType(module),
{ method->name, method->type, method->llfunction }
);
}
LLConstant *ObjCState::createMethodList(ClassDeclaration *decl, bool optional) {
LLConstantList methodList;
if (decl) {
auto methodDeclList = getMethodsForType(decl, optional);
for(auto func : methodDeclList) {
methodList.push_back(createMethodInfo(func));
}
}
return objcEmitList(module, methodList, false);
}
//
// INSTANCE VARIABLES
//
ObjcIvarInfo* ObjCState::getIvar(VarDeclaration *decl) {
if (auto it = ivars.find(decl); it != ivars.end()) {
return &ivars[decl];
}
if (auto klass = decl->parent->isClassDeclaration()) {
auto ivarsym = objcGetIvarSymbol(objcResolveName(decl->parent), objcResolveName(decl));
ivars[decl] = { .decl = decl };
auto ivarInfo = &ivars[decl];
// Extern classes should generate globals
// which can be filled out by the Objective-C runtime.
if (klass->objc.isExtern) {
ivarInfo->name = makeGlobal("OBJC_METH_VAR_NAME_", nullptr, OBJC_SECNAME_METHNAME, true, true);
ivarInfo->type = makeGlobal("OBJC_METH_VAR_TYPE_", nullptr, OBJC_SECNAME_METHTYPE, true, true);
// It will be filled out by the runtime, but make sure it's there nontheless.
ivarInfo->offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR);
ivarInfo->offset->setInitializer(objcOffsetIvar(0));
this->retain(ivarInfo->name);
this->retain(ivarInfo->type);
this->retain(ivarInfo->offset);
return &ivars[decl];
}
// Non-extern ivars should emit all the data so that the
// objective-c runtime has a starting point.
// the offset *WILL* change during runtime!
ivarInfo->name = makeGlobalStr(decl->ident->toChars(), "OBJC_METH_VAR_NAME_", OBJC_SECNAME_METHNAME);
ivarInfo->type = makeGlobalStr(objcGetTypeEncoding(decl->type), "OBJC_METH_VAR_TYPE_", OBJC_SECNAME_METHTYPE);
ivarInfo->offset = getOrCreate(ivarsym, getI32Type(), OBJC_SECNAME_IVAR);
ivarInfo->offset->setInitializer(objcOffsetIvar(decl->offset));
this->retain(ivarInfo->name);
this->retain(ivarInfo->type);
this->retain(ivarInfo->offset);
return &ivars[decl];
}
return nullptr;
}
LLConstant *ObjCState::createIvarInfo(VarDeclaration *decl) {
auto ivar = getIvar(decl);
LLConstantList members;
members.push_back(ivar->offset);
members.push_back(ivar->name);
members.push_back(ivar->type);
members.push_back(DtoConstUint(decl->alignment.isDefault() ? -1 : decl->alignment.get()));
members.push_back(DtoConstUint(decl->size(decl->loc)));
return LLConstantStruct::get(
objcGetIvarType(module),
members
);
}
LLConstant *ObjCState::createIvarList(ClassDeclaration *decl) {
LLConstantList ivarList;
for(auto field : decl->fields) {
ivarList.push_back(createIvarInfo(field));
}
return objcEmitList(module, ivarList, false);
}
//
// HELPERS
//
LLValue *ObjCState::deref(ClassDeclaration *decl, LLType *as) {
// Protocols can also have static functions
// as such we need to also be able to dereference them.
if (auto proto = decl->isInterfaceDeclaration()) {
return DtoLoad(as, getProtocol(proto)->ref);
}
// Classes may be class stubs.
// in that case, we need to call objc_loadClassRef instead of just
// loading from the classref.
auto classref = getClass(decl)->ref;
if (decl->objc.isExtern && decl->objc.isSwiftStub) {
auto loadClassFunc = getRuntimeFunction(decl->loc, module, "objc_loadClassRef");
return DtoBitCast(
gIR->CreateCallOrInvoke(loadClassFunc, classref, ""),
as
);
}
return DtoLoad(as, classref);
}
ObjcList<FuncDeclaration *> ObjCState::getMethodsForType(ClassDeclaration *decl, bool optional) {
ObjcList<FuncDeclaration *> funcs;
bool isProtocol = decl->isInterfaceDeclaration();
if (decl) {
for(size_t i = 0; i < decl->objc.methodList.length; i++) {
auto method = decl->objc.methodList.ptr[i];
if (isProtocol) {
if (method->objc.isOptional == optional)
funcs.push_back(method);
continue;
}
if (method->fbody)
funcs.push_back(method);
}
}
return funcs;
}
//
// FINALIZATION
//
void ObjCState::finalize() { void ObjCState::finalize() {
if (!retainedSymbols.empty()) { if (retainedSymbols.size() > 0) {
genImageInfo();
// add in references so optimizer won't remove symbols.
retainSymbols(); retainSymbols();
genImageInfo();
} }
} }
void ObjCState::retain(LLConstant *symbol) {
retainedSymbols.push_back(symbol);
}
void ObjCState::genImageInfo() { void ObjCState::genImageInfo() {
// Use LLVM to generate image info module.addModuleFlag(llvm::Module::Error, "Objective-C Version", 2u); // Only support ABI 2. (Non-fragile)
const char *section = module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version", 0u); // version
(abi == nonFragile ? "__DATA,__objc_imageinfo,regular,no_dead_strip" module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section", llvm::MDString::get(module.getContext(), OBJC_SECNAME_IMAGEINFO));
: "__OBJC,__image_info"); module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection", 0u); // flags
module.addModuleFlag(llvm::Module::Error, "Objective-C Version",
abi); // unused?
module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Version",
0u); // version
module.addModuleFlag(llvm::Module::Error, "Objective-C Image Info Section",
llvm::MDString::get(module.getContext(), section));
module.addModuleFlag(llvm::Module::Override, "Objective-C Garbage Collection",
0u); // flags
} }
void ObjCState::retainSymbols() { void ObjCState::retainSymbols() {
// put all objc symbols in the llvm.compiler.used array so optimizer won't if (!retainedSymbols.empty()) {
// remove. auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(),
auto arrayType = LLArrayType::get(retainedSymbols.front()->getType(), retainedSymbols.size());
retainedSymbols.size()); auto usedArray = LLConstantArray::get(arrayType, retainedSymbols);
auto usedArray = LLConstantArray::get(arrayType, retainedSymbols); auto var = new LLGlobalVariable(module, arrayType, false,
auto var = new LLGlobalVariable(module, arrayType, false, LLGlobalValue::AppendingLinkage, usedArray,
LLGlobalValue::AppendingLinkage, usedArray, "llvm.compiler.used");
"llvm.compiler.used"); var->setSection("llvm.metadata");
var->setSection("llvm.metadata"); }
} }

View file

@ -14,7 +14,11 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <unordered_map>
#include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringMap.h"
#include "gen/tollvm.h"
#include "dmd/mtype.h"
#include "dmd/errors.h"
struct ObjcSelector; struct ObjcSelector;
namespace llvm { namespace llvm {
@ -24,31 +28,182 @@ class Module;
class Triple; class Triple;
} }
// Forward decl.
class Declaration;
class ClassDeclaration;
class FuncDeclaration;
class InterfaceDeclaration;
class VarDeclaration;
class Identifier;
class Type;
// Fwd declaration.
class ObjCState;
// class is a metaclass
#define RO_META (1<<0)
// class is a root class
#define RO_ROOT (1<<1)
// Section Names
#define OBJC_SECNAME_CLASSNAME "__TEXT,__objc_classname, cstring_literals"
#define OBJC_SECNAME_METHNAME "__TEXT,__objc_methname, cstring_literals"
#define OBJC_SECNAME_METHTYPE "__TEXT,__objc_methtype, cstring_literals"
#define OBJC_SECNAME_SELREFS "__DATA,__objc_selrefs, literal_pointers, no_dead_strip"
#define OBJC_SECNAME_IMAGEINFO "__DATA,__objc_imageinfo, regular, no_dead_strip"
#define OBJC_SECNAME_CLASSREFS "__DATA,__objc_classrefs, regular, no_dead_strip"
#define OBJC_SECNAME_CLASSLIST "__DATA,__objc_classlist, regular, no_dead_strip"
#define OBJC_SECNAME_STUBS "__DATA,__objc_stubs, regular, no_dead_strip"
#define OBJC_SECNAME_CATLIST "__DATA,__objc_catlist, regular, no_dead_strip"
#define OBJC_SECNAME_PROTOLIST "__DATA,__objc_protolist, coalesced, no_dead_strip"
#define OBJC_SECNAME_PROTOREFS "__DATA,__objc_protorefs, regular"
#define OBJC_SECNAME_CONST "__DATA,__objc_const"
#define OBJC_SECNAME_DATA "__DATA,__objc_data"
#define OBJC_SECNAME_IVAR "__DATA,__objc_ivar"
// Names of Objective-C runtime structs
#define OBJC_STRUCTNAME_CLASSRO "class_ro_t"
#define OBJC_STRUCTNAME_CLASS "class_t"
#define OBJC_STRUCTNAME_STUBCLASS "stub_class_t"
#define OBJC_STRUCTNAME_PROTO "protocol_t"
#define OBJC_STRUCTNAME_IVAR "ivar_t"
#define OBJC_STRUCTNAME_METHOD "objc_method"
#define ObjcList std::vector
#define ObjcMap std::unordered_map
// Gets whether Objective-C is supported.
bool objc_isSupported(const llvm::Triple &triple); bool objc_isSupported(const llvm::Triple &triple);
// Generate name strings
std::string objcGetClassRoSymbol(const char *name, bool meta);
std::string objcGetClassSymbol(const char *name, bool meta);
std::string objcGetClassLabelSymbol(const char *name);
std::string objcGetClassMethodListSymbol(const char *className, bool meta);
std::string objcGetIvarListSymbol(const char *className);
std::string objcGetIvarSymbol(const char *className, const char *varName);
std::string objcGetProtoMethodListSymbol(const char *className, bool meta, bool optional);
std::string objcGetProtoSymbol(const char *name);
std::string objcGetProtoListSymbol(const char *name);
std::string objcGetSymbolName(const char *dsymPrefix, const char *dsymName);
// Utility which fetches the appropriate Objective-C
// name for a declaration.
const char *objcResolveName(Dsymbol *decl);
// Gets the Objective-C type encoding for D type t
std::string objcGetTypeEncoding(Type *t);
// class_t
LLStructType *objcGetClassType(const llvm::Module& module);
// class_ro_t
LLStructType *objcGetClassRoType(const llvm::Module& module);
// stub_class_t
LLStructType *objcGetStubClassType(const llvm::Module& module);
// objc_method
LLStructType *objcGetMethodType(const llvm::Module& module);
// ivar_t
LLStructType *objcGetIvarType(const llvm::Module& module);
// protocol_t
LLStructType *objcGetProtocolType(const llvm::Module& module);
// xyz_list_t (count-only)
LLConstant *objcEmitList(llvm::Module &module, LLConstantList objects, bool alignSizeT = false, bool countOnly = false);
struct ObjcClassInfo {
ClassDeclaration *decl;
LLGlobalVariable *ref;
LLGlobalVariable *name;
LLGlobalVariable *table;
};
struct ObjcProtocolInfo {
InterfaceDeclaration *decl;
LLGlobalVariable *ref;
LLGlobalVariable *name;
LLGlobalVariable *table;
};
struct ObjcMethodInfo {
FuncDeclaration *decl;
LLGlobalVariable *name;
LLGlobalVariable *type;
LLGlobalVariable *selector;
LLConstant *llfunction;
};
struct ObjcIvarInfo {
VarDeclaration *decl;
LLGlobalVariable *name;
LLGlobalVariable *type;
LLGlobalVariable *offset;
};
// Objective-C state tied to an LLVM module (object file). // Objective-C state tied to an LLVM module (object file).
class ObjCState { class ObjCState {
public: public:
ObjCState(llvm::Module &module) : module(module) {}
llvm::GlobalVariable *getMethVarRef(const ObjcSelector &sel); ObjCState(llvm::Module &module) : module(module) { }
ObjcClassInfo *getClass(ClassDeclaration *decl);
ObjcProtocolInfo *getProtocol(InterfaceDeclaration *decl);
ObjcMethodInfo *getMethod(FuncDeclaration *decl);
ObjcIvarInfo *getIvar(VarDeclaration *decl);
LLValue *deref(ClassDeclaration *decl, LLType *as);
void finalize(); void finalize();
private: private:
llvm::Module &module; llvm::Module &module;
// symbols that shouldn't be optimized away // Creates an ivar_t struct which can be
std::vector<llvm::Constant *> retainedSymbols; // used in ivar lists.
ObjcMap<VarDeclaration *, ObjcIvarInfo> ivars;
LLConstant *createIvarInfo(VarDeclaration *decl);
LLConstant *createIvarList(ClassDeclaration *decl);
llvm::StringMap<llvm::GlobalVariable *> methVarNameMap; // Creates an objc_method struct which can be
llvm::StringMap<llvm::GlobalVariable *> methVarRefMap; // used in method lists.
ObjcMap<FuncDeclaration *, ObjcMethodInfo> methods;
LLConstant *createMethodInfo(FuncDeclaration *decl);
LLConstant *createMethodList(ClassDeclaration *decl, bool optional = false);
llvm::GlobalVariable *getCStringVar(const char *symbol, // class_t and class_ro_t generation.
const llvm::StringRef &str, ObjcMap<ClassDeclaration *, ObjcClassInfo> classes;
const char *section); ObjcMap<ClassDeclaration *, LLGlobalVariable *> classTables;
llvm::GlobalVariable *getMethVarName(const llvm::StringRef &name); ObjcMap<ClassDeclaration *, LLGlobalVariable *> classRoTables;
void retain(llvm::Constant *sym); LLConstant *getClassRoTable(ClassDeclaration *decl);
LLConstant *getClassTable(ClassDeclaration *decl);
// Class names and refs need to be replicated
// for RO structs, as such we store
// then seperately.
llvm::StringMap<LLGlobalVariable *> classNames;
llvm::StringMap<LLGlobalVariable *> classRefs;
LLConstant *getClassName(ClassDeclaration *decl);
LLConstant *getClassRef(ClassDeclaration *decl);
// protocol_t generation.
ObjcMap<InterfaceDeclaration *, ObjcProtocolInfo> protocols;
LLConstant *createProtocolTable(InterfaceDeclaration *decl);
LLConstant *createProtocolList(ClassDeclaration *decl);
// Private helpers
ObjcList<FuncDeclaration *> getMethodsForType(ClassDeclaration *decl, bool optional = false);
ObjcList<LLConstant *> retainedSymbols;
void retain(LLConstant *symbol);
void genImageInfo(); void genImageInfo();
void retainSymbols(); void retainSymbols();
}; };

View file

@ -824,6 +824,10 @@ static void buildRuntimeModule() {
{stringTy, arrayOf(sizeTy), arrayOf(uintTy), ubyteTy}); {stringTy, arrayOf(sizeTy), arrayOf(uintTy), ubyteTy});
} }
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
if (target.objc.supported) { if (target.objc.supported) {
assert(global.params.targetTriple->isOSDarwin()); assert(global.params.targetTriple->isOSDarwin());
@ -839,24 +843,58 @@ static void buildRuntimeModule() {
{objectPtrTy, selectorPtrTy}, {}, {objectPtrTy, selectorPtrTy}, {},
AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind)); AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind));
// id objc_msgSendSuper(obj_super_t *super, SEL op, ...)
// NOTE: obj_super_t is defined as struct { id, Class }
createFwdDecl(LINK::c, objectPtrTy, {"objc_msgSendSuper"},
{objectPtrTy, selectorPtrTy}, {},
AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind));
// Class object_getClass(id obj)
createFwdDecl(LINK::c, objectPtrTy, {"object_getClass"},
{objectPtrTy}, {},
AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind));
// Class objc_loadClassRef(Class function(Class* stub))
// SEE: https://github.com/swiftlang/swift/blob/main/docs/ObjCInterop.md
createFwdDecl(LINK::c, objectPtrTy, {"objc_loadClassRef"},
{objectPtrTy}, {},
AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind));
// Needed for safe casting
// bool objc_opt_isKindOfClass(id obj, Class otherClass)
// This features a fast path over using the msgSend version.
// https://github.com/apple-oss-distributions/objc4/blob/main/runtime/NSObject.mm#L2123
createFwdDecl(LINK::c, boolTy, {"objc_opt_isKindOfClass"},
{objectPtrTy, objectPtrTy}, {},
AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind));
// bool class_conformsToProtocol(Class cls, Protocol *protocol)
createFwdDecl(LINK::c, boolTy, {"class_conformsToProtocol"},
{objectPtrTy, objectPtrTy}, {},
AttrSet(NoAttrs, ~0U, llvm::Attribute::NonLazyBind));
switch (global.params.targetTriple->getArch()) { switch (global.params.targetTriple->getArch()) {
case llvm::Triple::x86_64: case llvm::Triple::x86_64:
// creal objc_msgSend_fp2ret(id self, SEL op, ...) // creal objc_msgSend_fp2ret(id self, SEL op, ...)
createFwdDecl(LINK::c, Type::tcomplex80, {"objc_msgSend_fp2ret"}, createFwdDecl(LINK::c, Type::tcomplex80, {"objc_msgSend_fp2ret"},
{objectPtrTy, selectorPtrTy}); {objectPtrTy, selectorPtrTy});
// fall-thru
case llvm::Triple::x86:
// x86_64 real return only, x86 float, double, real return // x86_64 real return only, x86 float, double, real return
// real objc_msgSend_fpret(id self, SEL op, ...) // real objc_msgSend_fpret(id self, SEL op, ...)
createFwdDecl(LINK::c, realTy, {"objc_msgSend_fpret"}, createFwdDecl(LINK::c, realTy, {"objc_msgSend_fpret"},
{objectPtrTy, selectorPtrTy}); {objectPtrTy, selectorPtrTy});
// fall-thru
case llvm::Triple::arm:
case llvm::Triple::thumb:
// used when return value is aggregate via a hidden sret arg // used when return value is aggregate via a hidden sret arg
// void objc_msgSend_stret(T *sret_arg, id self, SEL op, ...) // void objc_msgSend_stret(T *sret_arg, id self, SEL op, ...)
createFwdDecl(LINK::c, voidTy, {"objc_msgSend_stret"}, createFwdDecl(LINK::c, voidTy, {"objc_msgSend_stret"},
{objectPtrTy, selectorPtrTy}); {objectPtrTy, objectPtrTy, selectorPtrTy});
// See: https://github.com/apple-oss-distributions/objc4/blob/main/runtime/Messengers.subproj/objc-msg-x86_64.s#L1059
// void objc_msgSend_stret(T *sret_arg, objc_super_t *super, SEL op, ...)
createFwdDecl(LINK::c, voidTy, {"objc_msgSendSuper_stret"},
{objectPtrTy, objectPtrTy, selectorPtrTy});
break; break;
default: default:
break; break;

View file

@ -646,13 +646,13 @@ public:
tf(DtoTypeFunction(fnval)), tf(DtoTypeFunction(fnval)),
llArgTypesBegin(llCalleeType->param_begin()) {} llArgTypesBegin(llCalleeType->param_begin()) {}
void addImplicitArgs() { void addImplicitArgs(bool directcall) {
if (gABI->passThisBeforeSret(tf)) { if (gABI->passThisBeforeSret(tf)) {
addContext(); addContext(directcall);
addSret(); addSret();
} else { } else {
addSret(); addSret();
addContext(); addContext(directcall);
} }
addArguments(); addArguments();
@ -702,15 +702,17 @@ private:
} }
// Adds an optional context/this pointer argument and sets hasContext. // Adds an optional context/this pointer argument and sets hasContext.
void addContext() { void addContext(bool directcall) {
const bool thiscall = irFty.arg_this; const bool thiscall = irFty.arg_this;
const bool nestedcall = irFty.arg_nest; const bool nestedcall = irFty.arg_nest;
const bool objccall = irFty.type->linkage == LINK::objc;
hasContext = thiscall || nestedcall || isDelegateCall; hasContext = thiscall || nestedcall || isDelegateCall || objccall;
if (!hasContext) if (!hasContext)
return; return;
size_t index = args.size(); size_t index = args.size();
auto argtype = *(llArgTypesBegin + index);
if (dfnval && (dfnval->func->ident == Id::ensure || if (dfnval && (dfnval->func->ident == Id::ensure ||
dfnval->func->ident == Id::require)) { dfnval->func->ident == Id::require)) {
@ -732,8 +734,33 @@ private:
} }
args.push_back(thisptrLval); args.push_back(thisptrLval);
} else if (thiscall && dfnval && dfnval->vthis) { } else if (thiscall && dfnval && dfnval->vthis) {
// ... or a normal 'this' argument
args.push_back(dfnval->vthis); if (objccall && directcall) {
// ... or a Objective-c direct call argument
if (auto func = dfnval->func->isFuncDeclaration()) {
if (auto klass = func->isThis()->isClassDeclaration()) {
// Create obj_super struct with (this, <class ref>)
auto obj_super = DtoAggrPair(
DtoBitCast(dfnval->vthis, argtype),
gIR->objc.deref(klass, getOpaquePtrType()),
"super"
);
// Allocate and store obj_super struct into a new variable.
auto clsaddr = DtoRawAlloca(obj_super->getType(), 16, "super");
DtoStore(obj_super, clsaddr);
args.push_back(clsaddr);
}
}
} else {
// ... or a normal 'this' argument
args.push_back(DtoBitCast(dfnval->vthis, argtype));
}
} else if (isDelegateCall) { } else if (isDelegateCall) {
// ... or a delegate context arg // ... or a delegate context arg
LLValue *ctxarg; LLValue *ctxarg;
@ -767,10 +794,9 @@ private:
if (irFty.arg_objcSelector) { if (irFty.arg_objcSelector) {
assert(dfnval); assert(dfnval);
const auto selector = dfnval->func->objc.selector;
assert(selector); auto methodptr = gIR->objc.getMethod(dfnval->func)->selector;
LLGlobalVariable *selptr = gIR->objc.getMethVarRef(*selector); args.push_back(DtoLoad(methodptr->getType(), methodptr));
args.push_back(DtoLoad(selptr->getValueType(), selptr));
} }
} }
@ -813,7 +839,7 @@ static LLValue *DtoCallableValue(DValue *fn) {
// FIXME: this function is a mess ! // FIXME: this function is a mess !
DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval, DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
Expressions *arguments, LLValue *sretPointer) { Expressions *arguments, LLValue *sretPointer, bool directcall) {
IF_LOG Logger::println("DtoCallFunction()"); IF_LOG Logger::println("DtoCallFunction()");
LOG_SCOPE LOG_SCOPE
@ -858,7 +884,7 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
// handle implicit arguments (sret, context/this, _arguments) // handle implicit arguments (sret, context/this, _arguments)
ImplicitArgumentsBuilder iab(args, attrs, loc, fnval, callableTy, arguments, ImplicitArgumentsBuilder iab(args, attrs, loc, fnval, callableTy, arguments,
resulttype, sretPointer); resulttype, sretPointer);
iab.addImplicitArgs(); iab.addImplicitArgs(directcall);
// handle explicit arguments // handle explicit arguments
@ -880,9 +906,12 @@ DValue *DtoCallFunction(const Loc &loc, Type *resulttype, DValue *fnval,
} }
if (irFty.arg_objcSelector) { if (irFty.arg_objcSelector) {
// Use runtime msgSend function bitcasted as original call // Use runtime msgSend function bitcasted as original call
const char *msgSend = gABI->objcMsgSendFunc(resulttype, irFty); const char *msgSend = gABI->objcMsgSendFunc(resulttype, irFty, directcall);
auto t = callable->getType();
callable = getRuntimeFunction(loc, gIR->module, msgSend); callable = getRuntimeFunction(loc, gIR->module, msgSend);
callable = DtoBitCast(callable, t);
} }
// call the function // call the function

View file

@ -329,6 +329,30 @@ public:
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
void visit(ObjcClassReferenceExp *e) override {
IF_LOG Logger::print("ObjcClassReferenceExp::toElem: %s @ %s\n", e->toChars(),
e->type->toChars());
LOG_SCOPE;
auto lType = DtoType(e->type);
if (auto iface = e->classDeclaration->isInterfaceDeclaration()) {
// Protocols
result = new DImValue(e->type, gIR->objc.deref(iface, lType));
return;
} else {
// Classes
result = new DImValue(e->type, gIR->objc.deref(e->classDeclaration, lType));
return;
}
llvm_unreachable("Unknown type for ObjcClassReferenceExp.");
}
//////////////////////////////////////////////////////////////////////////////
void visit(VarExp *e) override { void visit(VarExp *e) override {
IF_LOG Logger::print("VarExp::toElem: %s @ %s\n", e->toChars(), IF_LOG Logger::print("VarExp::toElem: %s @ %s\n", e->toChars(),
e->type->toChars()); e->type->toChars());
@ -801,7 +825,7 @@ public:
} }
DValue *result = DValue *result =
DtoCallFunction(e->loc, e->type, fnval, e->arguments, sretPointer); DtoCallFunction(e->loc, e->type, fnval, e->arguments, sretPointer, e->directcall);
if (canEmitVTableUnchangedAssumption && dfnval->vtable) { if (canEmitVTableUnchangedAssumption && dfnval->vtable) {
// Reload vtable ptr. It's the first element so instead of GEP+load we can // Reload vtable ptr. It's the first element so instead of GEP+load we can
@ -1017,10 +1041,9 @@ public:
auto &PGO = gIR->funcGen().pgo; auto &PGO = gIR->funcGen().pgo;
PGO.setCurrentStmt(e); PGO.setCurrentStmt(e);
DValue *l = toElem(e->e1);
Type *e1type = e->e1->type->toBasetype(); Type *e1type = e->e1->type->toBasetype();
DValue *l = toElem(e->e1);
if (VarDeclaration *vd = e->var->isVarDeclaration()) { if (VarDeclaration *vd = e->var->isVarDeclaration()) {
AggregateDeclaration *ad; AggregateDeclaration *ad;
LLValue *aggrPtr; LLValue *aggrPtr;

View file

@ -691,8 +691,122 @@ llvm::GlobalVariable *isaGlobalVar(LLValue *v) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
LLGlobalVariable *makeGlobal(LLStringRef name, LLType* type, LLStringRef section, bool extern_, bool externInit) {
if (!type)
type = getOpaquePtrType();
auto var = new LLGlobalVariable(
gIR->module,
type,
false,
extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage,
nullptr,
name,
nullptr,
LLGlobalValue::NotThreadLocal,
0u,
externInit
);
if (!section.empty())
var->setSection(section);
return var;
}
LLGlobalVariable *makeGlobalWithBytes(LLStringRef name, LLConstantList packedContents, LLStructType* type, bool extern_, bool externInit) {
if (packedContents.empty()) {
packedContents.push_back(getNullPtr());
}
// Handle initializer.
LLConstant *init;
if (type) {
init = LLConstantStruct::get(
type,
packedContents
);
} else {
init = LLConstantStruct::getAnon(
packedContents,
true
);
type = reinterpret_cast<LLStructType *>(init->getType());
}
auto var = new LLGlobalVariable(
gIR->module,
type,
false,
extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage,
init,
name,
nullptr,
LLGlobalValue::NotThreadLocal,
0u,
externInit
);
return var;
}
LLGlobalVariable *makeGlobalRef(LLGlobalVariable *to, LLStringRef name, LLStringRef section, bool extern_, bool externInit) {
auto var = makeGlobal(name, to->getType(), section, extern_, externInit);
var->setInitializer(to);
return var;
}
LLGlobalVariable *makeGlobalStr(LLStringRef text, LLStringRef name, LLStringRef section, bool extern_, bool externInit) {
auto init = llvm::ConstantDataArray::getString(gIR->context(), text);
auto var = new LLGlobalVariable(
gIR->module,
init->getType(),
false,
extern_ ? LLGlobalValue::ExternalLinkage : LLGlobalValue::PrivateLinkage,
init,
name,
nullptr,
LLGlobalValue::NotThreadLocal,
0u,
externInit
);
if (!section.empty())
var->setSection(section);
return var;
}
LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer) {
auto global = gIR->module.getGlobalVariable(name, true);
if (global)
return global;
return makeGlobal(name, type, section, true, extInitializer);
}
LLGlobalVariable *getOrCreateWeak(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer) {
auto global = gIR->module.getGlobalVariable(name, true);
if (global)
return global;
global = makeGlobal(name, type, section, false, extInitializer);
global->setLinkage(llvm::GlobalValue::LinkageTypes::WeakAnyLinkage);
global->setVisibility(llvm::GlobalValue::VisibilityTypes::HiddenVisibility);
return global;
}
////////////////////////////////////////////////////////////////////////////////
LLType *getI8Type() { return LLType::getInt8Ty(gIR->context()); } LLType *getI8Type() { return LLType::getInt8Ty(gIR->context()); }
LLType *getI16Type() { return LLType::getInt16Ty(gIR->context()); }
LLType *getI32Type() { return LLType::getInt32Ty(gIR->context()); }
LLType *getI64Type() { return LLType::getInt64Ty(gIR->context()); }
LLType *getSizeTType() { return DtoSize_t(); }
LLPointerType *getOpaquePtrType(unsigned addressSpace) { LLPointerType *getOpaquePtrType(unsigned addressSpace) {
return LLPointerType::get(gIR->context(), addressSpace); return LLPointerType::get(gIR->context(), addressSpace);
} }
@ -703,6 +817,8 @@ llvm::ConstantPointerNull *getNullPtr() {
LLConstant *getNullValue(LLType *t) { return LLConstant::getNullValue(t); } LLConstant *getNullValue(LLType *t) { return LLConstant::getNullValue(t); }
LLConstant *wrapNull(LLConstant *v) { return v ? v : getNullPtr(); }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
size_t getTypeBitSize(LLType *t) { return gDataLayout->getTypeSizeInBits(t); } size_t getTypeBitSize(LLType *t) { return gDataLayout->getTypeSizeInBits(t); }
@ -711,6 +827,10 @@ size_t getTypeStoreSize(LLType *t) { return gDataLayout->getTypeStoreSize(t); }
size_t getTypeAllocSize(LLType *t) { return gDataLayout->getTypeAllocSize(t); } size_t getTypeAllocSize(LLType *t) { return gDataLayout->getTypeAllocSize(t); }
size_t getPointerSize() { return gDataLayout->getPointerSize(0); }
size_t getPointerSizeInBits() { return gDataLayout->getPointerSizeInBits(0); }
unsigned int getABITypeAlign(LLType *t) { unsigned int getABITypeAlign(LLType *t) {
return gDataLayout->getABITypeAlign(t).value(); return gDataLayout->getABITypeAlign(t).value();
} }

View file

@ -146,16 +146,32 @@ LLConstantInt *isaConstantInt(LLValue *v);
llvm::Argument *isaArgument(LLValue *v); llvm::Argument *isaArgument(LLValue *v);
LLGlobalVariable *isaGlobalVar(LLValue *v); LLGlobalVariable *isaGlobalVar(LLValue *v);
// llvm::GlobalVariable wrappers for quickly making
// new global variables and references to them.
LLGlobalVariable *makeGlobal(LLStringRef name, LLType* type = nullptr, LLStringRef section = "", bool extern_ = false, bool externInit = false);
LLGlobalVariable *makeGlobalWithBytes(LLStringRef name, LLConstantList packedContents, LLStructType* type = nullptr, bool extern_ = false, bool externInit = false);
LLGlobalVariable *makeGlobalRef(LLGlobalVariable *to, LLStringRef name = "", LLStringRef section = "", bool extern_ = false, bool externInit = false);
LLGlobalVariable *makeGlobalStr(LLStringRef text, LLStringRef name = "", LLStringRef section = "", bool extern_ = false, bool externInit = false);
LLGlobalVariable *getOrCreate(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer=false);
LLGlobalVariable *getOrCreateWeak(LLStringRef name, LLType* type, LLStringRef section, bool extInitializer=false);
// llvm::T::get(...) wrappers // llvm::T::get(...) wrappers
LLType *getI8Type(); LLType *getI8Type();
LLType *getI16Type();
LLType *getI32Type();
LLType *getI64Type();
LLType *getSizeTType();
LLPointerType *getOpaquePtrType(unsigned addressSpace = 0); LLPointerType *getOpaquePtrType(unsigned addressSpace = 0);
llvm::ConstantPointerNull *getNullPtr(); llvm::ConstantPointerNull *getNullPtr();
LLConstant *getNullValue(LLType *t); LLConstant *getNullValue(LLType *t);
LLConstant *wrapNull(LLConstant *v);
// type sizes // type sizes
size_t getTypeBitSize(LLType *t); size_t getTypeBitSize(LLType *t);
size_t getTypeStoreSize(LLType *t); size_t getTypeStoreSize(LLType *t);
size_t getTypeAllocSize(LLType *t); size_t getTypeAllocSize(LLType *t);
size_t getPointerSize();
size_t getPointerSizeInBits();
// type alignments // type alignments
unsigned int getABITypeAlign(LLType *t); unsigned int getABITypeAlign(LLType *t);

View file

@ -55,6 +55,11 @@ IrClass::IrClass(ClassDeclaration *cd) : IrAggr(cd) {
} }
void IrClass::addInterfaceVtbls(ClassDeclaration *cd) { void IrClass::addInterfaceVtbls(ClassDeclaration *cd) {
// No interface vtables in Objective-C
if (cd->classKind == ClassKind::objc)
return;
if (cd->baseClass && !cd->isInterfaceDeclaration()) { if (cd->baseClass && !cd->isInterfaceDeclaration()) {
addInterfaceVtbls(cd->baseClass); addInterfaceVtbls(cd->baseClass);
} }

View file

@ -64,6 +64,15 @@ void AggrTypeBuilder::addAggregate(
if (n == 0) if (n == 0)
return; return;
// Objective-C instance variables are laid out at runtime.
// as such, we should not generate the aggregate body.
if (auto klass = ad->isClassDeclaration()) {
if (klass->classKind == ClassKind::objc) {
this->addType(getOpaquePtrType(), getPointerSize());
return;
}
}
// Unions may lead to overlapping fields, and we need to flatten them for LLVM // Unions may lead to overlapping fields, and we need to flatten them for LLVM
// IR. We usually take the first field (in declaration order) of an // IR. We usually take the first field (in declaration order) of an
// overlapping set, but a literal with an explicit initializer for a dominated // overlapping set, but a literal with an explicit initializer for a dominated
@ -196,9 +205,9 @@ void AggrTypeBuilder::addAggregate(
if (vd->offset < m_offset) { if (vd->offset < m_offset) {
error(vd->loc, error(vd->loc,
"%s `%s` overlaps previous field. This is an ICE, please file an " "%s `%s` @ %u overlaps previous field @ %u. This is an ICE, please file an "
"LDC issue.", "LDC issue.",
vd->kind(), vd->toPrettyChars()); vd->kind(), vd->toPrettyChars(), vd->offset, m_offset);
fatal(); fatal();
} }

View file

@ -83,6 +83,14 @@ llvm::Type *IrTypeClass::getMemoryLLType() {
AggrTypeBuilder builder; AggrTypeBuilder builder;
// Objective-C just has an ISA pointer, so just
// throw that in there.
if (cd->classKind == ClassKind::objc) {
builder.addType(getOpaquePtrType(), target.ptrsize);
isaStruct(type)->setBody(builder.defaultTypes(), builder.isPacked());
return type;
}
// add vtbl // add vtbl
builder.addType(llvm::PointerType::get(vtbl_type, 0), target.ptrsize); builder.addType(llvm::PointerType::get(vtbl_type, 0), target.ptrsize);

View file

@ -40,6 +40,7 @@ version (D_ObjectiveC)
{ {
version = UdaOptional; version = UdaOptional;
version = UdaSelector; version = UdaSelector;
version = UdaSwift;
} }
version (Posix) version (Posix)
@ -50,6 +51,7 @@ version (CoreDdoc)
version = UdaGNUAbiTag; version = UdaGNUAbiTag;
version = UdaOptional; version = UdaOptional;
version = UdaSelector; version = UdaSelector;
version = UdaSwift;
} }
/** /**
@ -183,6 +185,16 @@ version (UdaSelector) struct selector
version (UdaOptional) version (UdaOptional)
enum optional; enum optional;
/**
* Use this attribute to indicate that a Objective-C class is a Swift stub class.
*
* This is only allowed on classes, and classes marked as swift Objective-C classes
* cannot be subclassed.
*/
version (UdaSwift)
enum swift;
/** /**
* Use this attribute to declare an ABI tag on a C++ symbol. * Use this attribute to declare an ABI tag on a C++ symbol.
* *

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: // EXTRA_OBJC_SOURCES:
import core.attribute : selector; import core.attribute : selector;

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: // EXTRA_OBJC_SOURCES:
import core.attribute : selector; import core.attribute : selector;

View file

@ -619,7 +619,9 @@ string[string] getEnvironment()
} }
version(OSX) version(OSX)
version(X86_64) version (IN_LLVM)
env["D_OBJC"] = "1";
else version(X86_64)
env["D_OBJC"] = "1"; env["D_OBJC"] = "1";
} }
return env; return env;

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: // EXTRA_OBJC_SOURCES:
// REQUIRED_ARGS: -L-framework -LFoundation // REQUIRED_ARGS: -L-framework -LFoundation

View file

@ -1,5 +1,3 @@
// LDC: not implemented yet (issue #2670)
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: // EXTRA_OBJC_SOURCES:
// REQUIRED_ARGS: -L-framework -LFoundation // REQUIRED_ARGS: -L-framework -LFoundation

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: objc_class.m // EXTRA_OBJC_SOURCES: objc_class.m
// REQUIRED_ARGS: -L-framework -LFoundation // REQUIRED_ARGS: -L-framework -LFoundation

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: objc_instance_variable.m // EXTRA_OBJC_SOURCES: objc_instance_variable.m
// REQUIRED_ARGS: -L-framework -LFoundation // REQUIRED_ARGS: -L-framework -LFoundation

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: objc_instance_variable.m // EXTRA_OBJC_SOURCES: objc_instance_variable.m
// REQUIRED_ARGS: -L-framework -LFoundation // REQUIRED_ARGS: -L-framework -LFoundation

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: objc_objc_msgSend.m // EXTRA_OBJC_SOURCES: objc_objc_msgSend.m
// REQUIRED_ARGS: -L-framework -LFoundation // REQUIRED_ARGS: -L-framework -LFoundation

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: objc_protocol.m // EXTRA_OBJC_SOURCES: objc_protocol.m
// REQUIRED_ARGS: -L-lobjc // REQUIRED_ARGS: -L-lobjc

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: // EXTRA_OBJC_SOURCES:
// REQUIRED_ARGS: -L-lobjc // REQUIRED_ARGS: -L-lobjc
@ -8,7 +7,7 @@
import core.attribute : selector, optional; import core.attribute : selector, optional;
extern (Objective-C): extern(C):
struct Protocol; struct Protocol;
struct objc_selector; struct objc_selector;
@ -38,6 +37,8 @@ objc_method_description protocol_getMethodDescription(
Protocol* proto, SEL aSel, bool isRequiredMethod, bool isInstanceMethod Protocol* proto, SEL aSel, bool isRequiredMethod, bool isInstanceMethod
); );
extern (Objective-C):
interface Foo interface Foo
{ {
void foo1() @selector("foo1"); void foo1() @selector("foo1");

View file

@ -1,4 +1,3 @@
// DISABLED: LDC
// EXTRA_OBJC_SOURCES: objc_super_call.m // EXTRA_OBJC_SOURCES: objc_super_call.m
// REQUIRED_ARGS: -L-framework -LFoundation // REQUIRED_ARGS: -L-framework -LFoundation

View file

@ -816,6 +816,14 @@ bool gatherTestParameters(ref TestArgs testArgs, string input_dir, string input_
testArgs.objcSources = split(extraObjcSourcesStr); testArgs.objcSources = split(extraObjcSourcesStr);
// Using the ld64 linker may result in linker warnings being generated
// which are irrelevant to the parts of Objective-C ABI which D implements.
// As such we supress linker warnings for Objective-C tests to avoid
// linker errors when linking against Objective-C compiler output.
version(LDC)
if (objc)
testArgs.requiredArgs ~= " -L-w";
// swap / with $SEP // swap / with $SEP
if (envData.sep && envData.sep != "/") if (envData.sep && envData.sep != "/")
foreach (ref s; testArgs.sources) foreach (ref s; testArgs.sources)