//===-- declarations.cpp --------------------------------------------------===// // // LDC – the LLVM D compiler // // This file is distributed under the BSD-style LDC license. See the LICENSE // file for details. // //===----------------------------------------------------------------------===// #include "aggregate.h" #include "declaration.h" #include "enum.h" #include "id.h" #include "init.h" #include "nspace.h" #include "rmem.h" #include "template.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/irtype.h" #include "ir/irvar.h" #include "llvm/ADT/SmallString.h" ////////////////////////////////////////////////////////////////////////////// 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) LLVM_OVERRIDE { IF_LOG Logger::println("Ignoring Dsymbol::codegen for %s", sym->toPrettyChars()); } ////////////////////////////////////////////////////////////////////////// void visit(Nspace *ns) LLVM_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) LLVM_OVERRIDE { IF_LOG Logger::println("InterfaceDeclaration::codegen: '%s'", decl->toPrettyChars()); LOG_SCOPE assert(!irs->dcomputetarget); if (decl->ir->isDefined()) { return; } if (decl->type->ty == Terror) { error(decl->loc, "had semantic errors when compiling"); decl->ir->setDefined(); return; } if (decl->members && decl->symtab) { DtoResolveClass(decl); decl->ir->setDefined(); // Emit any members (e.g. final functions). for (auto m : *decl->members) { m->accept(this); } // Emit TypeInfo. DtoTypeInfoOf(decl->type, /*base=*/false); // Declare __InterfaceZ. IrAggr *ir = getIrAggr(decl); llvm::GlobalVariable *interfaceZ = ir->getClassInfoSymbol(); // Only define if not speculative. if (!isSpeculativeType(decl->type)) { interfaceZ->setInitializer(ir->getClassInfoInit()); setLinkage(decl, interfaceZ); } } } ////////////////////////////////////////////////////////////////////////// void visit(StructDeclaration *decl) LLVM_OVERRIDE { IF_LOG Logger::println("StructDeclaration::codegen: '%s'", decl->toPrettyChars()); LOG_SCOPE if (decl->ir->isDefined()) { return; } if (decl->type->ty == Terror) { error(decl->loc, "had semantic errors when compiling"); decl->ir->setDefined(); return; } if (!(decl->members && decl->symtab)) { 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) { // Define the __initZ symbol. IrAggr *ir = getIrAggr(decl); auto &initZ = ir->getInitSymbol(); auto initGlobal = llvm::cast(initZ); initZ = irs->setGlobalVarInitializer(initGlobal, ir->getDefaultInit()); setLinkage(decl, initGlobal); // emit typeinfo DtoTypeInfoOf(decl->type, /*base=*/false); } // Emit __xopEquals/__xopCmp/__xtoHash. 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); } } ////////////////////////////////////////////////////////////////////////// void visit(ClassDeclaration *decl) LLVM_OVERRIDE { IF_LOG Logger::println("ClassDeclaration::codegen: '%s'", decl->toPrettyChars()); LOG_SCOPE assert(!irs->dcomputetarget); if (decl->ir->isDefined()) { return; } if (decl->type->ty == Terror) { error(decl->loc, "had semantic errors when compiling"); decl->ir->setDefined(); return; } if (decl->members && decl->symtab) { DtoResolveClass(decl); decl->ir->setDefined(); for (auto m : *decl->members) { m->accept(this); } IrAggr *ir = getIrAggr(decl); const auto lwc = DtoLinkage(decl); auto &initZ = ir->getInitSymbol(); auto initGlobal = llvm::cast(initZ); initZ = irs->setGlobalVarInitializer(initGlobal, ir->getDefaultInit()); setLinkage(lwc, initGlobal); llvm::GlobalVariable *vtbl = ir->getVtblSymbol(); vtbl->setInitializer(ir->getVtblInit()); setLinkage(lwc, vtbl); llvm::GlobalVariable *classZ = ir->getClassInfoSymbol(); if (!isSpeculativeType(decl->type)) { classZ->setInitializer(ir->getClassInfoInit()); setLinkage(lwc, classZ); } } } ////////////////////////////////////////////////////////////////////////// void visit(TupleDeclaration *decl) LLVM_OVERRIDE { IF_LOG Logger::println("TupleDeclaration::codegen(): '%s'", decl->toPrettyChars()); LOG_SCOPE if (decl->ir->isDefined()) { return; } decl->ir->setDefined(); assert(decl->isexp); assert(decl->objects); for (auto o : *decl->objects) { DsymbolExp *exp = static_cast(o); assert(exp->op == TOKdsymbol); exp->s->accept(this); } } ////////////////////////////////////////////////////////////////////////// void visit(VarDeclaration *decl) LLVM_OVERRIDE { IF_LOG Logger::println("VarDeclaration::codegen(): '%s'", decl->toPrettyChars()); LOG_SCOPE; if (decl->ir->isDefined()) { return; } if (decl->type->ty == Terror) { error(decl->loc, "had semantic errors when compiling"); decl->ir->setDefined(); return; } DtoResolveVariable(decl); decl->ir->setDefined(); // just forward aliases if (decl->aliassym) { Logger::println("alias sym"); 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); IrGlobal *irGlobal = getIrGlobal(decl); LLGlobalVariable *gvar = llvm::cast(irGlobal->value); assert(gvar && "DtoResolveVariable should have created value"); if (global.params.vtls && gvar->isThreadLocal() && !(decl->storage_class & STCtemp)) { const char *p = decl->loc.toChars(); fprintf(global.stdmsg, "%s: %s is thread local\n", p, decl->toChars()); } // Check if we are defining or just declaring the global in this module. // If we reach here during codegen of an available_externally function, // new variable declarations should stay external and therefore must not // have an initializer. if (!(decl->storage_class & STCextern) && !decl->inNonRoot()) { // Build the initializer. Might use irGlobal->value! LLConstant *initVal = DtoConstInitializer(decl->loc, decl->type, decl->_init); // Cache it. assert(!irGlobal->constInit); irGlobal->constInit = initVal; // Set the initializer, swapping out the variable if the types do not // match. irGlobal->value = irs->setGlobalVarInitializer(gvar, initVal); // Finalize linkage & DLL storage class. const auto lwc = DtoLinkage(decl); setLinkage(lwc, gvar); if (gvar->hasDLLImportStorageClass()) { gvar->setDLLStorageClass(LLGlobalValue::DLLExportStorageClass); } // Also set up the debug info. irs->DBuilder.EmitGlobalVariable(gvar, decl); } // If this global is used from a naked function, we need to create an // artificial "use" for it, or it could be removed by the optimizer if // the only reference to it is in inline asm. if (irGlobal->nakedUse) { irs->usedArray.push_back(gvar); } IF_LOG Logger::cout() << *gvar << '\n'; } } ////////////////////////////////////////////////////////////////////////// void visit(EnumDeclaration *decl) LLVM_OVERRIDE { IF_LOG Logger::println("Ignoring EnumDeclaration::codegen: '%s'", decl->toPrettyChars()); if (decl->type->ty == Terror) { error(decl->loc, "had semantic errors when compiling"); return; } } ////////////////////////////////////////////////////////////////////////// void visit(FuncDeclaration *decl) LLVM_OVERRIDE { // don't touch function aliases, they don't contribute any new symbols if (!decl->isFuncAliasDeclaration()) { DtoDefineFunction(decl); } } ////////////////////////////////////////////////////////////////////////// void visit(TemplateInstance *decl) LLVM_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; } // Force codegen if this is a templated function with pragma(inline, true). if ((decl->members->dim == 1) && ((*decl->members)[0]->isFuncDeclaration()) && ((*decl->members)[0]->isFuncDeclaration()->inlining == PINLINEalways)) { Logger::println("needsCodegen() == false, but function is marked with " "pragma(inline, true), so it really does need " "codegen."); } else { // FIXME: This is #673 all over again. if (!decl->needsCodegen()) { Logger::println("Does not need codegen, skipping."); return; } } for (auto &m : *decl->members) { m->accept(this); } } ////////////////////////////////////////////////////////////////////////// void visit(TemplateMixin *decl) LLVM_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) LLVM_OVERRIDE { Dsymbols *d = decl->include(nullptr, nullptr); if (d) { for (auto s : *d) { s->accept(this); } } } ////////////////////////////////////////////////////////////////////////// void visit(PragmaDeclaration *decl) LLVM_OVERRIDE { if (decl->ident == Id::lib) { assert(decl->args && decl->args->dim == 1); assert(!irs->dcomputetarget); Expression *e = static_cast(decl->args->data[0]); assert(e->op == TOKstring); StringExp *se = static_cast(e); const std::string name(se->toPtr(), se->numberOfCodeUnits()); auto nameLen = name.size(); if (global.params.targetTriple->isWindowsGNUEnvironment()) { if (nameLen > 4 && !memcmp(&name[nameLen - 4], ".lib", 4)) { // 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). nameLen -= 4; } if (nameLen >= 7 && !memcmp(name.data(), "shell32", 7)) { // Another DMD compatibility kludge: Ignore // pragma(lib, "shell32.lib"), it is implicitly provided by // MinGW. return; } } // With LLVM 3.3 or later we can place the library name in the object // file. This seems to be supported only on Windows. if (global.params.targetTriple->isWindowsMSVCEnvironment()) { llvm::SmallString<24> LibName(name); // Win32: /DEFAULTLIB:"curl" if (LibName.endswith(".a")) { LibName = LibName.substr(0, LibName.size() - 2); } if (LibName.endswith(".lib")) { LibName = LibName.substr(0, LibName.size() - 4); } llvm::SmallString<24> tmp("/DEFAULTLIB:\""); tmp.append(LibName); tmp.append("\""); LibName = tmp; // Embed library name as linker option in object file auto Value = llvm::MDString::get(gIR->context(), LibName); gIR->LinkerMetadataArgs.push_back( llvm::MDNode::get(gIR->context(), Value)); } else { size_t const n = nameLen + 3; char *arg = static_cast(mem.xmalloc(n)); arg[0] = '-'; arg[1] = 'l'; memcpy(arg + 2, name.data(), nameLen); arg[n - 1] = 0; global.params.linkswitches->push(arg); } } visit(static_cast(decl)); } ////////////////////////////////////////////////////////////////////////// void visit(TypeInfoDeclaration *decl) LLVM_OVERRIDE { if (!irs->dcomputetarget) TypeInfoDeclaration_codegen(decl, irs); } ////////////////////////////////////////////////////////////////////////// void visit(TypeInfoClassDeclaration *decl) LLVM_OVERRIDE { if (!irs->dcomputetarget) TypeInfoClassDeclaration_codegen(decl, irs); } }; ////////////////////////////////////////////////////////////////////////////// void Declaration_codegen(Dsymbol *decl) { Declaration_codegen(decl, gIR); } void Declaration_codegen(Dsymbol *decl, IRState *irs) { CodegenVisitor v(irs); decl->accept(&v); } //////////////////////////////////////////////////////////////////////////////