Skip to content

Commit

Permalink
Improve Objective-C support (#4777)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
LunaTheFoxgirl authored Dec 3, 2024
1 parent a1e694c commit 82878ef
Show file tree
Hide file tree
Showing 42 changed files with 1,564 additions and 191 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- 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)
- 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
- Supports LLVM 15 - 19.
Expand Down
1 change: 1 addition & 0 deletions dmd/id.d
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ immutable Msgtable[] msgtable =
{ "udaHidden", "_hidden" },
{ "udaNoSanitize", "noSanitize" },
{ "udaNoSplitStack", "_noSplitStack" },
{ "udaSwiftStub", "swift"},

// IN_LLVM: DCompute specific types and functionss
{ "dcompute" },
Expand Down
56 changes: 56 additions & 0 deletions dmd/objc.d
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ extern (C++) struct ObjcClassDeclaration
/// `true` if this class is externally defined.
bool isExtern = false;

/// `true` if this class is a Swift stub
version(IN_LLVM) bool isSwiftStub = false;

/// Name of this class.
Identifier identifier;

Expand Down Expand Up @@ -264,6 +267,16 @@ extern(C++) abstract class Objc
*/
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.
*
Expand Down Expand Up @@ -452,6 +465,12 @@ static if (!IN_LLVM)
// noop
}

version(IN_LLVM)
override void setAsSwiftStub(ClassDeclaration, Scope*) const
{
// noop
}

override void validateOptional(FuncDeclaration) const
{
// noop
Expand Down Expand Up @@ -532,6 +551,7 @@ version (IN_LLVM) {} else
{
cd.classKind = ClassKind.objc;
cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0;
this.setAsSwiftStub(cd, cd._scope);
}

override void setObjc(InterfaceDeclaration id)
Expand Down Expand Up @@ -826,6 +846,42 @@ version (IN_LLVM) {} else
errorSupplemental(expression.loc, "`tupleof` is not available for members " ~
"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;
}
}
}

/*
Expand Down
6 changes: 6 additions & 0 deletions dmd/objc.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ struct ObjcClassDeclaration
{
d_bool isMeta;
d_bool isExtern;
#if IN_LLVM
d_bool isSwiftStub;
#endif

Identifier* identifier;
ClassDeclaration* classDeclaration;
Expand Down Expand Up @@ -67,6 +70,9 @@ class Objc
virtual void checkLinkage(FuncDeclaration* fd) = 0;
virtual bool isVirtual(const FuncDeclaration*) 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 ClassDeclaration* getParent(FuncDeclaration*, ClassDeclaration*) const = 0;
virtual void addToClassMethodList(FuncDeclaration*, ClassDeclaration*) const = 0;
Expand Down
19 changes: 19 additions & 0 deletions driver/linker-gcc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ static llvm::cl::opt<bool> linkNoCpp(
"link-no-cpp", llvm::cl::ZeroOrMore, llvm::cl::Hidden,
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 {
Expand All @@ -72,6 +76,7 @@ class ArgsBuilder {
virtual void addXRayLinkFlags(const llvm::Triple &triple);
virtual bool addCompilerRTArchiveLinkFlags(llvm::StringRef baseName,
const llvm::Triple &triple);
virtual void addObjcStdlibLinkFlags(const llvm::Triple &triple);

virtual void addLinker();
virtual void addUserSwitches();
Expand Down Expand Up @@ -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.
void ArgsBuilder::addProfileRuntimeLinkFlags(const llvm::Triple &triple) {
const auto searchPaths =
Expand Down Expand Up @@ -732,6 +744,13 @@ void ArgsBuilder::addDefaultPlatformLibs() {
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()) {
// 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
Expand Down
15 changes: 8 additions & 7 deletions gen/abi/aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ using namespace dmd;
*/
struct AArch64TargetABI : TargetABI {
private:
const bool isDarwin;
IndirectByvalRewrite indirectByvalRewrite;
ArgTypesRewrite argTypesRewrite;

bool isAAPCS64VaList(Type *t) {
if (isDarwin)
if (isDarwin())
return false;

// look for a __va_list struct in a `std` C++ namespace
Expand All @@ -51,7 +50,7 @@ struct AArch64TargetABI : TargetABI {
}

public:
AArch64TargetABI() : isDarwin(global.params.targetTriple->isOSDarwin()) {}
AArch64TargetABI() {}

bool returnInArg(TypeFunction *tf, bool) override {
if (tf->isref()) {
Expand Down Expand Up @@ -108,7 +107,7 @@ struct AArch64TargetABI : TargetABI {
}

// 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 (ts->sym->fields.empty() && ts->sym->isPOD()) {
fty.args.erase(fty.args.begin() + i);
Expand Down Expand Up @@ -166,7 +165,7 @@ struct AArch64TargetABI : TargetABI {
}

Type *vaListType() override {
if (isDarwin)
if (isDarwin())
return TargetABI::vaListType(); // char*

// We need to pass the actual va_list type for correct mangling. Simply
Expand All @@ -176,9 +175,11 @@ struct AArch64TargetABI : TargetABI {
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
return "objc_msgSend";
return directcall ? "objc_msgSendSuper" : "objc_msgSend";
}
};

Expand Down
2 changes: 1 addition & 1 deletion gen/abi/abi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}

Expand Down
8 changes: 7 additions & 1 deletion gen/abi/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct ABIRewrite {

// interface called by codegen
struct TargetABI {
public:
virtual ~TargetABI() = default;

/// Returns the ABI for the target we're compiling for
Expand Down Expand Up @@ -117,6 +118,11 @@ struct TargetABI {
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).
/// `needsThis` is true if the function type is for a non-static member
/// function.
Expand Down Expand Up @@ -171,7 +177,7 @@ struct TargetABI {
virtual Type *vaListType();

/// 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 *****/

Expand Down
8 changes: 0 additions & 8 deletions gen/abi/arm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,6 @@ struct ArmTargetABI : TargetABI {
// solution is found there, this should be adapted).
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; }
29 changes: 12 additions & 17 deletions gen/abi/x86-64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ struct X86_64TargetABI : TargetABI {

Type *vaListType() override;

const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override;
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override;

private:
LLType *getValistType();
Expand Down Expand Up @@ -196,9 +196,6 @@ struct X86_64TargetABI : TargetABI {
}
};

// The public getter for abi.cpp
TargetABI *getX86_64TargetABI() { return new X86_64TargetABI; }

bool X86_64TargetABI::returnInArg(TypeFunction *tf, bool) {
if (tf->isref()) {
return false;
Expand Down Expand Up @@ -382,21 +379,19 @@ Type *X86_64TargetABI::vaListType() {
TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag")));
}

const char *X86_64TargetABI::objcMsgSendFunc(Type *ret,
IrFuncTy &fty) {
const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) {
assert(isDarwin());

// see objc/message.h for objc_msgSend selection rules
if (fty.arg_sret) {
return "objc_msgSend_stret";
return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret";
}
if (ret) {
// complex long double return
if (ret->ty == TY::Tcomplex80) {
return "objc_msgSend_fp2ret";
}
// long double return
if (ret->ty == TY::Tfloat80 || ret->ty == TY::Timaginary80) {
return "objc_msgSend_fpret";
}
// float, double, long double return
if (ret && ret->isfloating()) {
return ret->ty == TY::Tcomplex80 ? "objc_msgSend_fp2ret" : "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; }
19 changes: 2 additions & 17 deletions gen/abi/x86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,13 @@
using namespace dmd;

struct X86TargetABI : TargetABI {
const bool isDarwin;
const bool isMSVC;
bool returnStructsInRegs;
IntegerRewrite integerRewrite;
IndirectByvalRewrite indirectByvalRewrite;

X86TargetABI()
: isDarwin(global.params.targetTriple->isOSDarwin()),
isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) {
: isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) {
using llvm::Triple;
auto os = global.params.targetTriple->getOS();
returnStructsInRegs =
Expand Down Expand Up @@ -230,7 +228,7 @@ struct X86TargetABI : TargetABI {
// 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
// be Clang or GCC, so just assume Clang on Darwin and G++ on Linux.
if (externD || !isDarwin)
if (externD || !isDarwin())
return;

size_t i = 0;
Expand Down Expand Up @@ -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.
Expand Down
Loading

0 comments on commit 82878ef

Please sign in to comment.