mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-29 06:30:39 +03:00
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:
parent
a1e694c16b
commit
82878ef32c
42 changed files with 1568 additions and 195 deletions
|
@ -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.
|
||||||
|
|
1
dmd/id.d
1
dmd/id.d
|
@ -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" },
|
||||||
|
|
56
dmd/objc.d
56
dmd/objc.d
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 *****/
|
||||||
|
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
949
gen/objcgen.cpp
949
gen/objcgen.cpp
|
@ -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");
|
}
|
||||||
}
|
}
|
177
gen/objcgen.h
177
gen/objcgen.h
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
29
gen/toir.cpp
29
gen/toir.cpp
|
@ -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;
|
||||||
|
|
120
gen/tollvm.cpp
120
gen/tollvm.cpp
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
16
gen/tollvm.h
16
gen/tollvm.h
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// DISABLED: LDC
|
|
||||||
// EXTRA_OBJC_SOURCES:
|
// EXTRA_OBJC_SOURCES:
|
||||||
|
|
||||||
import core.attribute : selector;
|
import core.attribute : selector;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// DISABLED: LDC
|
|
||||||
// EXTRA_OBJC_SOURCES:
|
// EXTRA_OBJC_SOURCES:
|
||||||
|
|
||||||
import core.attribute : selector;
|
import core.attribute : selector;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
// DISABLED: LDC
|
|
||||||
// EXTRA_OBJC_SOURCES:
|
// EXTRA_OBJC_SOURCES:
|
||||||
// REQUIRED_ARGS: -L-framework -LFoundation
|
// REQUIRED_ARGS: -L-framework -LFoundation
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue