mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-28 06:00:46 +03:00

* 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
520 lines
14 KiB
C++
520 lines
14 KiB
C++
//===-- declarations.cpp --------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "dmd/aggregate.h"
|
||
#include "dmd/declaration.h"
|
||
#include "dmd/enum.h"
|
||
#include "dmd/errors.h"
|
||
#include "dmd/expression.h"
|
||
#include "dmd/id.h"
|
||
#include "dmd/import.h"
|
||
#include "dmd/init.h"
|
||
#include "dmd/nspace.h"
|
||
#include "dmd/root/rmem.h"
|
||
#include "dmd/target.h"
|
||
#include "dmd/template.h"
|
||
#include "driver/cl_options.h"
|
||
#include "gen/classes.h"
|
||
#include "gen/functions.h"
|
||
#include "gen/irstate.h"
|
||
#include "gen/llvm.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "gen/logger.h"
|
||
#include "gen/tollvm.h"
|
||
#include "gen/typinf.h"
|
||
#include "gen/uda.h"
|
||
#include "ir/irdsymbol.h"
|
||
#include "ir/irtype.h"
|
||
#include "ir/irvar.h"
|
||
#include "llvm/ADT/SmallString.h"
|
||
|
||
using namespace dmd;
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
class CodegenVisitor : public Visitor {
|
||
IRState *irs;
|
||
|
||
public:
|
||
explicit CodegenVisitor(IRState *irs) : irs(irs) {}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
// Import all functions from class Visitor
|
||
using Visitor::visit;
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(Dsymbol *sym) override {
|
||
IF_LOG Logger::println("Ignoring Dsymbol::codegen for %s",
|
||
sym->toPrettyChars());
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(Import *im) override {
|
||
IF_LOG Logger::println("Import::codegen for %s", im->toPrettyChars());
|
||
LOG_SCOPE
|
||
|
||
irs->DBuilder.EmitImport(im);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(Nspace *ns) override {
|
||
IF_LOG Logger::println("Nspace::codegen for %s", ns->toPrettyChars());
|
||
LOG_SCOPE
|
||
|
||
if (!isError(ns) && ns->members) {
|
||
for (auto sym : *ns->members)
|
||
sym->accept(this);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(InterfaceDeclaration *decl) override {
|
||
IF_LOG Logger::println("InterfaceDeclaration::codegen: '%s'",
|
||
decl->toPrettyChars());
|
||
LOG_SCOPE
|
||
|
||
assert(!irs->dcomputetarget);
|
||
|
||
if (decl->ir->isDefined()) {
|
||
return;
|
||
}
|
||
|
||
if (decl->type->ty == TY::Terror) {
|
||
error(decl->loc, "%s `%s` had semantic errors when compiling",
|
||
decl->kind(), decl->toPrettyChars());
|
||
decl->ir->setDefined();
|
||
return;
|
||
}
|
||
|
||
if (!(decl->members && decl->symtab)) {
|
||
return;
|
||
}
|
||
|
||
DtoResolveClass(decl);
|
||
decl->ir->setDefined();
|
||
|
||
// Emit any members (e.g. final functions).
|
||
for (auto m : *decl->members) {
|
||
m->accept(this);
|
||
}
|
||
|
||
// Objective-C protocols don't have TypeInfo.
|
||
if (decl->classKind == ClassKind::objc) {
|
||
gIR->objc.getProtocol(decl);
|
||
return;
|
||
}
|
||
|
||
// Emit TypeInfo.
|
||
IrClass *ir = getIrAggr(decl);
|
||
if (!ir->suppressTypeInfo()) {
|
||
ir->getClassInfoSymbol(/*define=*/true);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(StructDeclaration *decl) override {
|
||
IF_LOG Logger::println("StructDeclaration::codegen: '%s'",
|
||
decl->toPrettyChars());
|
||
LOG_SCOPE
|
||
|
||
if (decl->ir->isDefined()) {
|
||
return;
|
||
}
|
||
|
||
if (decl->type->ty == TY::Terror) {
|
||
error(decl->loc, "%s `%s` had semantic errors when compiling",
|
||
decl->kind(), decl->toPrettyChars());
|
||
decl->ir->setDefined();
|
||
return;
|
||
}
|
||
|
||
if (!(decl->members && decl->symtab)) {
|
||
// nothing to do for opaque structs anymore
|
||
return;
|
||
}
|
||
|
||
DtoResolveStruct(decl);
|
||
decl->ir->setDefined();
|
||
|
||
for (auto m : *decl->members) {
|
||
m->accept(this);
|
||
}
|
||
|
||
// Skip __initZ and typeinfo for @compute device code.
|
||
// TODO: support global variables and thus __initZ
|
||
if (!irs->dcomputetarget) {
|
||
IrStruct *ir = getIrAggr(decl);
|
||
|
||
// Define the __initZ symbol.
|
||
if (!decl->zeroInit()) {
|
||
ir->getInitSymbol(/*define=*/true);
|
||
}
|
||
|
||
// Emit special __xopEquals/__xopCmp/__xtoHash member functions required
|
||
// for the TypeInfo.
|
||
if (!ir->suppressTypeInfo()) {
|
||
if (decl->xeq && decl->xeq != decl->xerreq) {
|
||
decl->xeq->accept(this);
|
||
}
|
||
if (decl->xcmp && decl->xcmp != decl->xerrcmp) {
|
||
decl->xcmp->accept(this);
|
||
}
|
||
if (decl->xhash) {
|
||
decl->xhash->accept(this);
|
||
}
|
||
|
||
// the TypeInfo itself is emitted into each referencing CU
|
||
}
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(ClassDeclaration *decl) override {
|
||
IF_LOG Logger::println("ClassDeclaration::codegen: '%s'",
|
||
decl->toPrettyChars());
|
||
LOG_SCOPE
|
||
|
||
assert(!irs->dcomputetarget);
|
||
|
||
if (decl->ir->isDefined()) {
|
||
return;
|
||
}
|
||
|
||
if (decl->type->ty == TY::Terror) {
|
||
error(decl->loc, "%s `%s` had semantic errors when compiling",
|
||
decl->kind(), decl->toPrettyChars());
|
||
decl->ir->setDefined();
|
||
return;
|
||
}
|
||
|
||
if (!(decl->members && decl->symtab)) {
|
||
return;
|
||
}
|
||
|
||
DtoResolveClass(decl);
|
||
decl->ir->setDefined();
|
||
|
||
for (auto m : *decl->members) {
|
||
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);
|
||
|
||
ir->getInitSymbol(/*define=*/true);
|
||
|
||
ir->getVtblSymbol(/*define*/true);
|
||
|
||
ir->defineInterfaceVtbls();
|
||
|
||
// Emit TypeInfo.
|
||
if (!ir->suppressTypeInfo()) {
|
||
ir->getClassInfoSymbol(/*define=*/true);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(TupleDeclaration *decl) override {
|
||
IF_LOG Logger::println("TupleDeclaration::codegen(): '%s'",
|
||
decl->toPrettyChars());
|
||
LOG_SCOPE
|
||
|
||
if (decl->ir->isDefined()) {
|
||
return;
|
||
}
|
||
decl->ir->setDefined();
|
||
|
||
decl->foreachVar(this);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(VarDeclaration *decl) override {
|
||
IF_LOG Logger::println("VarDeclaration::codegen(): '%s'",
|
||
decl->toPrettyChars());
|
||
LOG_SCOPE;
|
||
|
||
if (decl->ir->isDefined()) {
|
||
return;
|
||
}
|
||
|
||
if (decl->type->ty == TY::Terror) {
|
||
error(decl->loc, "%s `%s` had semantic errors when compiling",
|
||
decl->kind(), decl->toPrettyChars());
|
||
decl->ir->setDefined();
|
||
return;
|
||
}
|
||
|
||
DtoResolveVariable(decl);
|
||
decl->ir->setDefined();
|
||
|
||
// just forward aliases
|
||
if (decl->aliasTuple) {
|
||
Logger::println("aliasTuple");
|
||
decl->toAlias()->accept(this);
|
||
return;
|
||
}
|
||
|
||
// global variable
|
||
if (decl->isDataseg()) {
|
||
Logger::println("data segment");
|
||
|
||
assert(!(decl->storage_class & STCmanifest) &&
|
||
"manifest constant being codegen'd!");
|
||
assert(!irs->dcomputetarget);
|
||
|
||
getIrGlobal(decl)->getValue(/*define=*/true);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(EnumDeclaration *decl) override {
|
||
IF_LOG Logger::println("Ignoring EnumDeclaration::codegen: '%s'",
|
||
decl->toPrettyChars());
|
||
|
||
if (decl->type->ty == TY::Terror) {
|
||
error(decl->loc, "%s `%s` had semantic errors when compiling",
|
||
decl->kind(), decl->toPrettyChars());
|
||
return;
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(FuncDeclaration *decl) override {
|
||
// don't touch function aliases, they don't contribute any new symbols
|
||
if (!decl->skipCodegen() && !decl->isFuncAliasDeclaration() &&
|
||
// skip fwd declarations (IR-declared lazily)
|
||
decl->fbody) {
|
||
DtoDefineFunction(decl);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(TemplateInstance *decl) override {
|
||
IF_LOG Logger::println("TemplateInstance::codegen: '%s'",
|
||
decl->toPrettyChars());
|
||
LOG_SCOPE
|
||
|
||
if (decl->ir->isDefined()) {
|
||
Logger::println("Already defined, skipping.");
|
||
return;
|
||
}
|
||
decl->ir->setDefined();
|
||
|
||
if (isError(decl)) {
|
||
Logger::println("Has errors, skipping.");
|
||
return;
|
||
}
|
||
|
||
if (!decl->members) {
|
||
Logger::println("Has no members, skipping.");
|
||
return;
|
||
}
|
||
|
||
// With -linkonce-templates-aggressive, only non-speculative instances make
|
||
// it to module members (see `TemplateInstance.appendToModuleMember()`), and
|
||
// we don't need full needsCodegen() culling in that case; isDiscardable()
|
||
// is sufficient. Speculative ones are lazily emitted if actually referenced
|
||
// during codegen - per IR module.
|
||
if ((global.params.linkonceTemplates == LinkonceTemplates::aggressive &&
|
||
decl->isDiscardable()) ||
|
||
(global.params.linkonceTemplates != LinkonceTemplates::aggressive &&
|
||
!decl->needsCodegen())) {
|
||
Logger::println("Does not need codegen, skipping.");
|
||
return;
|
||
}
|
||
|
||
if (irs->dcomputetarget && (decl->tempdecl == Type::rtinfo ||
|
||
decl->tempdecl == Type::rtinfoImpl)) {
|
||
// Emitting object.RTInfo(Impl) template instantiations in dcompute
|
||
// modules would require dcompute support for global variables.
|
||
Logger::println("Skipping object.RTInfo(Impl) template instantiations "
|
||
"in dcompute modules.");
|
||
return;
|
||
}
|
||
|
||
for (auto &m : *decl->members) {
|
||
m->accept(this);
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(TemplateMixin *decl) override {
|
||
IF_LOG Logger::println("TemplateInstance::codegen: '%s'",
|
||
decl->toPrettyChars());
|
||
LOG_SCOPE
|
||
|
||
if (decl->ir->isDefined()) {
|
||
return;
|
||
}
|
||
decl->ir->setDefined();
|
||
|
||
if (!isError(decl) && decl->members) {
|
||
for (auto m : *decl->members) {
|
||
m->accept(this);
|
||
}
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(AttribDeclaration *decl) override {
|
||
Dsymbols *d = decl->include(nullptr);
|
||
|
||
if (d) {
|
||
for (auto s : *d) {
|
||
s->accept(this);
|
||
}
|
||
}
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
static llvm::StringRef getPragmaStringArg(PragmaDeclaration *decl,
|
||
d_size_t i = 0) {
|
||
assert(decl->args && decl->args->length > i);
|
||
auto se = (*decl->args)[i]->isStringExp();
|
||
assert(se);
|
||
DString str = se->peekString();
|
||
return {str.ptr, str.length};
|
||
}
|
||
|
||
void visit(PragmaDeclaration *decl) override {
|
||
const auto &triple = *global.params.targetTriple;
|
||
|
||
#if LDC_LLVM_VER >= 1800
|
||
#define endswith ends_with
|
||
#define startswith starts_with
|
||
#endif
|
||
|
||
if (decl->ident == Id::lib) {
|
||
assert(!irs->dcomputetarget);
|
||
llvm::StringRef name = getPragmaStringArg(decl);
|
||
|
||
if (triple.isWindowsGNUEnvironment()) {
|
||
if (name.endswith(".lib")) {
|
||
// On MinGW, strip the .lib suffix, if any, to improve compatibility
|
||
// with code written for DMD (we pass the name to GCC via -l, just as
|
||
// on Posix).
|
||
name = name.drop_back(4);
|
||
}
|
||
|
||
if (name.startswith("shell32")) {
|
||
// Another DMD compatibility kludge: Ignore
|
||
// pragma(lib, "shell32.lib"), it is implicitly provided by
|
||
// MinGW.
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (triple.isWindowsMSVCEnvironment()) {
|
||
if (name.endswith(".a")) {
|
||
name = name.drop_back(2);
|
||
}
|
||
if (name.endswith(".lib")) {
|
||
name = name.drop_back(4);
|
||
}
|
||
|
||
// embed linker directive in COFF object file; don't push to
|
||
// global.params.linkswitches
|
||
std::string arg = ("/DEFAULTLIB:\"" + name + "\"").str();
|
||
gIR->addLinkerOption(llvm::StringRef(arg));
|
||
} else {
|
||
const bool isStaticLib = name.endswith(".a");
|
||
const size_t nameLen = name.size();
|
||
|
||
char *arg = nullptr;
|
||
if (!isStaticLib) { // name => -lname
|
||
const size_t n = nameLen + 3;
|
||
arg = static_cast<char *>(mem.xmalloc(n));
|
||
arg[0] = '-';
|
||
arg[1] = 'l';
|
||
memcpy(arg + 2, name.data(), nameLen);
|
||
arg[n - 1] = 0;
|
||
} else {
|
||
arg = static_cast<char *>((mem.xmalloc(nameLen + 1)));
|
||
memcpy(arg, name.data(), nameLen);
|
||
arg[nameLen] = 0;
|
||
}
|
||
|
||
global.params.linkswitches.push(arg);
|
||
|
||
if (triple.isOSBinFormatMachO()) {
|
||
// embed linker directive in Mach-O object file too
|
||
gIR->addLinkerOption(llvm::StringRef(arg));
|
||
} else if (triple.isOSBinFormatELF()) {
|
||
// embed library name as dependent library in ELF object file too
|
||
// (supported by LLD v9+)
|
||
gIR->addLinkerDependentLib(name);
|
||
}
|
||
}
|
||
} else if (decl->ident == Id::linkerDirective) {
|
||
// embed in object file (if supported)
|
||
if (target.supportsLinkerDirective()) {
|
||
assert(decl->args);
|
||
llvm::SmallVector<llvm::StringRef, 2> args;
|
||
args.reserve(decl->args->length);
|
||
for (d_size_t i = 0; i < decl->args->length; ++i)
|
||
args.push_back(getPragmaStringArg(decl, i));
|
||
gIR->addLinkerOption(args);
|
||
}
|
||
}
|
||
visit(static_cast<AttribDeclaration *>(decl));
|
||
|
||
#if LDC_LLVM_VER >= 1800
|
||
#undef endswith
|
||
#undef startswith
|
||
#endif
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(TypeInfoDeclaration *decl) override {
|
||
llvm_unreachable("Should be emitted from codegen layer only");
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////
|
||
|
||
void visit(CAsmDeclaration *ad) override {
|
||
auto se = ad->code->isStringExp();
|
||
assert(se);
|
||
|
||
DString str = se->peekString();
|
||
if (str.length)
|
||
irs->module.appendModuleInlineAsm({str.ptr, str.length});
|
||
}
|
||
};
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
void Declaration_codegen(Dsymbol *decl) { Declaration_codegen(decl, gIR); }
|
||
|
||
void Declaration_codegen(Dsymbol *decl, IRState *irs) {
|
||
CodegenVisitor v(irs);
|
||
decl->accept(&v);
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|