Skip to content

Commit 82878ef

Browse files
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
1 parent a1e694c commit 82878ef

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1564
-191
lines changed

CHANGELOG.md

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

1112
#### Platform support
1213
- Supports LLVM 15 - 19.

dmd/id.d

+1
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ immutable Msgtable[] msgtable =
627627
{ "udaHidden", "_hidden" },
628628
{ "udaNoSanitize", "noSanitize" },
629629
{ "udaNoSplitStack", "_noSplitStack" },
630+
{ "udaSwiftStub", "swift"},
630631

631632
// IN_LLVM: DCompute specific types and functionss
632633
{ "dcompute" },

dmd/objc.d

+56
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ extern (C++) struct ObjcClassDeclaration
156156
/// `true` if this class is externally defined.
157157
bool isExtern = false;
158158

159+
/// `true` if this class is a Swift stub
160+
version(IN_LLVM) bool isSwiftStub = false;
161+
159162
/// Name of this class.
160163
Identifier identifier;
161164

@@ -264,6 +267,16 @@ extern(C++) abstract class Objc
264267
*/
265268
abstract void setAsOptional(FuncDeclaration functionDeclaration, Scope* sc) const;
266269

270+
/**
271+
* Marks the given class as a Swift stub class.
272+
*
273+
* Params:
274+
* cd = the class declaration to set as a swift stub
275+
* sc = the scope from the semantic phase
276+
*/
277+
version(IN_LLVM)
278+
abstract void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const;
279+
267280
/**
268281
* Validates function declarations declared optional.
269282
*
@@ -452,6 +465,12 @@ static if (!IN_LLVM)
452465
// noop
453466
}
454467

468+
version(IN_LLVM)
469+
override void setAsSwiftStub(ClassDeclaration, Scope*) const
470+
{
471+
// noop
472+
}
473+
455474
override void validateOptional(FuncDeclaration) const
456475
{
457476
// noop
@@ -532,6 +551,7 @@ version (IN_LLVM) {} else
532551
{
533552
cd.classKind = ClassKind.objc;
534553
cd.objc.isExtern = (cd.storage_class & STC.extern_) > 0;
554+
this.setAsSwiftStub(cd, cd._scope);
535555
}
536556

537557
override void setObjc(InterfaceDeclaration id)
@@ -826,6 +846,42 @@ version (IN_LLVM) {} else
826846
errorSupplemental(expression.loc, "`tupleof` is not available for members " ~
827847
"of Objective-C classes. Please use the Objective-C runtime instead");
828848
}
849+
850+
version(IN_LLVM) {
851+
override void setAsSwiftStub(ClassDeclaration cd, Scope* sc) const
852+
{
853+
const count = declaredAsSwiftStubCount(cd, sc);
854+
cd.objc.isSwiftStub = count > 0;
855+
856+
if (count > 1)
857+
.error(cd.loc, "%s `%s` can only declare a class as a swift stub once", cd.kind, cd.toPrettyChars);
858+
}
859+
860+
/// Returns: the number of times `cd` has been declared as optional.
861+
private int declaredAsSwiftStubCount(ClassDeclaration cd, Scope* sc) const
862+
{
863+
int count;
864+
865+
foreachUda(cd, sc, (e) {
866+
if (!e.isTypeExp())
867+
return 0;
868+
869+
auto typeExp = e.isTypeExp();
870+
871+
if (typeExp.type.ty != Tenum)
872+
return 0;
873+
874+
auto typeEnum = cast(TypeEnum) typeExp.type;
875+
876+
if (isCoreUda(typeEnum.sym, Id.udaSwiftStub))
877+
count++;
878+
879+
return 0;
880+
});
881+
882+
return count;
883+
}
884+
}
829885
}
830886

831887
/*

dmd/objc.h

+6
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ struct ObjcClassDeclaration
3737
{
3838
d_bool isMeta;
3939
d_bool isExtern;
40+
#if IN_LLVM
41+
d_bool isSwiftStub;
42+
#endif
4043

4144
Identifier* identifier;
4245
ClassDeclaration* classDeclaration;
@@ -67,6 +70,9 @@ class Objc
6770
virtual void checkLinkage(FuncDeclaration* fd) = 0;
6871
virtual bool isVirtual(const FuncDeclaration*) const = 0;
6972
virtual void setAsOptional(FuncDeclaration *fd, Scope *sc) const = 0;
73+
#if IN_LLVM
74+
virtual void setAsSwiftStub(ClassDeclaration* cd, Scope *sc) const = 0;
75+
#endif
7076
virtual void validateOptional(FuncDeclaration *fd) const = 0;
7177
virtual ClassDeclaration* getParent(FuncDeclaration*, ClassDeclaration*) const = 0;
7278
virtual void addToClassMethodList(FuncDeclaration*, ClassDeclaration*) const = 0;

driver/linker-gcc.cpp

+19
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ static llvm::cl::opt<bool> linkNoCpp(
4848
"link-no-cpp", llvm::cl::ZeroOrMore, llvm::cl::Hidden,
4949
llvm::cl::desc("Disable automatic linking with the C++ standard library."));
5050

51+
static llvm::cl::opt<bool> linkNoObjc(
52+
"link-no-objc", llvm::cl::ZeroOrMore, llvm::cl::Hidden,
53+
llvm::cl::desc("Disable automatic linking with the Objective-C runtime library."));
54+
5155
//////////////////////////////////////////////////////////////////////////////
5256

5357
namespace {
@@ -72,6 +76,7 @@ class ArgsBuilder {
7276
virtual void addXRayLinkFlags(const llvm::Triple &triple);
7377
virtual bool addCompilerRTArchiveLinkFlags(llvm::StringRef baseName,
7478
const llvm::Triple &triple);
79+
virtual void addObjcStdlibLinkFlags(const llvm::Triple &triple);
7580

7681
virtual void addLinker();
7782
virtual void addUserSwitches();
@@ -467,6 +472,13 @@ void ArgsBuilder::addCppStdlibLinkFlags(const llvm::Triple &triple) {
467472
}
468473
}
469474

475+
void ArgsBuilder::addObjcStdlibLinkFlags(const llvm::Triple &triple) {
476+
if (linkNoObjc)
477+
return;
478+
479+
args.push_back("-lobjc");
480+
}
481+
470482
// Adds all required link flags for PGO.
471483
void ArgsBuilder::addProfileRuntimeLinkFlags(const llvm::Triple &triple) {
472484
const auto searchPaths =
@@ -732,6 +744,13 @@ void ArgsBuilder::addDefaultPlatformLibs() {
732744
break;
733745
}
734746

747+
if (triple.isOSDarwin()) {
748+
749+
// libobjc is more or less required, so we link against it here.
750+
// This could be prettier, though.
751+
addObjcStdlibLinkFlags(triple);
752+
}
753+
735754
if (triple.isWindowsGNUEnvironment()) {
736755
// This is really more of a kludge, as linking in the Winsock functions
737756
// should be handled by the pragma(lib, ...) in std.socket, but it

gen/abi/aarch64.cpp

+8-7
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,11 @@ using namespace dmd;
2929
*/
3030
struct AArch64TargetABI : TargetABI {
3131
private:
32-
const bool isDarwin;
3332
IndirectByvalRewrite indirectByvalRewrite;
3433
ArgTypesRewrite argTypesRewrite;
3534

3635
bool isAAPCS64VaList(Type *t) {
37-
if (isDarwin)
36+
if (isDarwin())
3837
return false;
3938

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

5352
public:
54-
AArch64TargetABI() : isDarwin(global.params.targetTriple->isOSDarwin()) {}
53+
AArch64TargetABI() {}
5554

5655
bool returnInArg(TypeFunction *tf, bool) override {
5756
if (tf->isref()) {
@@ -108,7 +107,7 @@ struct AArch64TargetABI : TargetABI {
108107
}
109108

110109
// https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html#//apple_ref/doc/uid/TP40013702-SW1
111-
if (isDarwin) {
110+
if (isDarwin()) {
112111
if (auto ts = tb->isTypeStruct()) {
113112
if (ts->sym->fields.empty() && ts->sym->isPOD()) {
114113
fty.args.erase(fty.args.begin() + i);
@@ -166,7 +165,7 @@ struct AArch64TargetABI : TargetABI {
166165
}
167166

168167
Type *vaListType() override {
169-
if (isDarwin)
168+
if (isDarwin())
170169
return TargetABI::vaListType(); // char*
171170

172171
// We need to pass the actual va_list type for correct mangling. Simply
@@ -176,9 +175,11 @@ struct AArch64TargetABI : TargetABI {
176175
return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list"));
177176
}
178177

179-
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
178+
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) override {
179+
assert(isDarwin());
180+
180181
// see objc/message.h for objc_msgSend selection rules
181-
return "objc_msgSend";
182+
return directcall ? "objc_msgSendSuper" : "objc_msgSend";
182183
}
183184
};
184185

gen/abi/abi.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ Type *TargetABI::vaListType() {
207207

208208
//////////////////////////////////////////////////////////////////////////////
209209

210-
const char *TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty) {
210+
const char *TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) {
211211
llvm_unreachable("Unknown Objective-C ABI");
212212
}
213213

gen/abi/abi.h

+7-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ struct ABIRewrite {
7070

7171
// interface called by codegen
7272
struct TargetABI {
73+
public:
7374
virtual ~TargetABI() = default;
7475

7576
/// Returns the ABI for the target we're compiling for
@@ -117,6 +118,11 @@ struct TargetABI {
117118
global.params.targetTriple->getOS() == llvm::Triple::NetBSD;
118119
}
119120

121+
/// Returns true if the target is darwin-based.
122+
bool isDarwin() {
123+
return global.params.targetTriple->isOSDarwin();
124+
}
125+
120126
/// Returns true if the D function uses sret (struct return).
121127
/// `needsThis` is true if the function type is for a non-static member
122128
/// function.
@@ -171,7 +177,7 @@ struct TargetABI {
171177
virtual Type *vaListType();
172178

173179
/// Returns Objective-C message send function
174-
virtual const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty);
180+
virtual const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall);
175181

176182
/***** Static Helpers *****/
177183

gen/abi/arm.cpp

-8
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,6 @@ struct ArmTargetABI : TargetABI {
122122
// solution is found there, this should be adapted).
123123
return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list"));
124124
}
125-
126-
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
127-
// see objc/message.h for objc_msgSend selection rules
128-
if (fty.arg_sret) {
129-
return "objc_msgSend_stret";
130-
}
131-
return "objc_msgSend";
132-
}
133125
};
134126

135127
TargetABI *getArmTargetABI() { return new ArmTargetABI; }

gen/abi/x86-64.cpp

+12-17
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ struct X86_64TargetABI : TargetABI {
165165

166166
Type *vaListType() override;
167167

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

170170
private:
171171
LLType *getValistType();
@@ -196,9 +196,6 @@ struct X86_64TargetABI : TargetABI {
196196
}
197197
};
198198

199-
// The public getter for abi.cpp
200-
TargetABI *getX86_64TargetABI() { return new X86_64TargetABI; }
201-
202199
bool X86_64TargetABI::returnInArg(TypeFunction *tf, bool) {
203200
if (tf->isref()) {
204201
return false;
@@ -382,21 +379,19 @@ Type *X86_64TargetABI::vaListType() {
382379
TypeIdentifier::create(Loc(), Identifier::idPool("__va_list_tag")));
383380
}
384381

385-
const char *X86_64TargetABI::objcMsgSendFunc(Type *ret,
386-
IrFuncTy &fty) {
382+
const char *X86_64TargetABI::objcMsgSendFunc(Type *ret, IrFuncTy &fty, bool directcall) {
383+
assert(isDarwin());
384+
387385
// see objc/message.h for objc_msgSend selection rules
388386
if (fty.arg_sret) {
389-
return "objc_msgSend_stret";
387+
return directcall ? "objc_msgSendSuper_stret" : "objc_msgSend_stret";
390388
}
391-
if (ret) {
392-
// complex long double return
393-
if (ret->ty == TY::Tcomplex80) {
394-
return "objc_msgSend_fp2ret";
395-
}
396-
// long double return
397-
if (ret->ty == TY::Tfloat80 || ret->ty == TY::Timaginary80) {
398-
return "objc_msgSend_fpret";
399-
}
389+
// float, double, long double return
390+
if (ret && ret->isfloating()) {
391+
return ret->ty == TY::Tcomplex80 ? "objc_msgSend_fp2ret" : "objc_msgSend_fpret";
400392
}
401-
return "objc_msgSend";
393+
return directcall ? "objc_msgSendSuper" : "objc_msgSend";
402394
}
395+
396+
// The public getter for abi.cpp
397+
TargetABI *getX86_64TargetABI() { return new X86_64TargetABI; }

gen/abi/x86.cpp

+2-17
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,13 @@
2323
using namespace dmd;
2424

2525
struct X86TargetABI : TargetABI {
26-
const bool isDarwin;
2726
const bool isMSVC;
2827
bool returnStructsInRegs;
2928
IntegerRewrite integerRewrite;
3029
IndirectByvalRewrite indirectByvalRewrite;
3130

3231
X86TargetABI()
33-
: isDarwin(global.params.targetTriple->isOSDarwin()),
34-
isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) {
32+
: isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) {
3533
using llvm::Triple;
3634
auto os = global.params.targetTriple->getOS();
3735
returnStructsInRegs =
@@ -230,7 +228,7 @@ struct X86TargetABI : TargetABI {
230228
// Clang does not pass empty structs, while it seems that GCC does,
231229
// at least on Linux x86. We don't know whether the C compiler will
232230
// be Clang or GCC, so just assume Clang on Darwin and G++ on Linux.
233-
if (externD || !isDarwin)
231+
if (externD || !isDarwin())
234232
return;
235233

236234
size_t i = 0;
@@ -272,19 +270,6 @@ struct X86TargetABI : TargetABI {
272270
}
273271
}
274272
}
275-
276-
const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
277-
// see objc/message.h for objc_msgSend selection rules
278-
assert(isDarwin);
279-
if (fty.arg_sret) {
280-
return "objc_msgSend_stret";
281-
}
282-
// float, double, long double return
283-
if (ret && ret->isfloating() && !ret->iscomplex()) {
284-
return "objc_msgSend_fpret";
285-
}
286-
return "objc_msgSend";
287-
}
288273
};
289274

290275
// The public getter for abi.cpp.

0 commit comments

Comments
 (0)