//===-- pragma.cpp --------------------------------------------------------===// // // LDC – the LLVM D compiler // // This file is distributed under the BSD-style LDC license. See the LICENSE // file for details. // //===----------------------------------------------------------------------===// #include "gen/pragma.h" #include "dmd/attrib.h" #include "dmd/declaration.h" #include "dmd/errors.h" #include "dmd/expression.h" #include "dmd/id.h" #include "dmd/identifier.h" #include "dmd/module.h" #include "dmd/scope.h" #include "dmd/template.h" #include "gen/inlineir.h" #include "gen/llvmhelpers.h" #include "llvm/Support/CommandLine.h" using namespace dmd; namespace { bool parseStringExp(Expression *e, const char *&res) { e = optimize(e, WANTvalue); if (e->op != EXP::string_) { return false; } auto se = static_cast(e); auto size = (se->len + 1) * se->sz; auto s = static_cast(mem.xmalloc(size)); se->writeTo(s, true); res = s; return true; } bool parseIntExp(Expression *e, dinteger_t &res) { e = optimize(e, WANTvalue); if (auto i = e->isIntegerExp()) { res = i->getInteger(); return true; } return false; } bool parseBoolExp(Expression *e, bool &res) { e = optimize(e, WANTvalue); if (auto i = e->isIntegerExp()) { if (e->type->equals(Type::tbool)) { res = (i->toInteger() != 0); return true; } } return false; } // Applies an action to matching symbols, recursively descending into nested // AttribDeclarations, and returns the number of applications. template int applyPragma(Dsymbol *s, std::function predicate, std::function action) { if (T *matchingDecl = predicate(s)) { if (matchingDecl->llvmInternal != LLVMnone) { error(s->loc, "multiple LDC specific pragmas are not allowed"); fatal(); } action(matchingDecl); return 1; } if (auto ad = s->isAttribDeclaration()) { if (ad->decl) { int count = 0; for (auto child : *ad->decl) { count += applyPragma(child, predicate, action); } return count; } } return 0; } int applyFunctionPragma(Dsymbol *s, std::function action) { return applyPragma( s, [](Dsymbol *s) { return s->isFuncDeclaration(); }, action); } int applyTemplatePragma(Dsymbol *s, std::function action) { return applyPragma( s, [](Dsymbol *s) { return s->isTemplateDeclaration(); }, action); } int applyVariablePragma(Dsymbol *s, std::function action) { return applyPragma( s, [](Dsymbol *s) { return s->isVarDeclaration(); }, action); } } // anonymous namespace LDCPragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, const char *&arg1str) { Identifier *ident = decl->ident; Expressions *args = decl->args; const auto getFirstArg = [args, sc]() { return (args && args->length > 0) ? expressionSemantic((*args)[0], sc) : nullptr; }; const auto pragmaError = [decl](const char *msg) { error(decl->loc, "`pragma(%s)` %s", decl->ident->toChars(), msg); }; // pragma(LDC_intrinsic, "string") { funcdecl(s) } if (ident == Id::LDC_intrinsic) { if (!args || args->length != 1 || !parseStringExp(getFirstArg(), arg1str)) { pragmaError("requires exactly 1 string literal parameter"); fatal(); } // Recognize LDC-specific pragmas. struct LdcIntrinsic { std::string name; LDCPragma pragma; }; static LdcIntrinsic ldcIntrinsic[] = { {"bitop.bt", LLVMbitop_bt}, {"bitop.btc", LLVMbitop_btc}, {"bitop.btr", LLVMbitop_btr}, {"bitop.bts", LLVMbitop_bts}, {"bitop.vld", LLVMbitop_vld}, {"bitop.vst", LLVMbitop_vst}, }; static std::string prefix = "ldc."; size_t arg1str_length = strlen(arg1str); if (arg1str_length > prefix.length() && std::equal(prefix.begin(), prefix.end(), arg1str)) { // Got ldc prefix, binary search through ldcIntrinsic. std::string name(arg1str + prefix.length()); size_t i = 0, j = sizeof(ldcIntrinsic) / sizeof(ldcIntrinsic[0]); do { size_t k = (i + j) / 2; int cmp = name.compare(ldcIntrinsic[k].name); if (!cmp) { return ldcIntrinsic[k].pragma; } if (cmp < 0) { j = k; } else { i = k + 1; } } while (i != j); } return LLVMintrinsic; } // pragma(LDC_global_crt_{c,d}tor [, priority]) { funcdecl(s) } // pragma(crt_{con,de}structor [, priority]) { funcdecl(s) } if (ident == Id::LDC_global_crt_ctor || ident == Id::LDC_global_crt_dtor || ident == Id::crt_constructor || ident == Id::crt_destructor) { dinteger_t priority; if (args) { if (args->length != 1 || !parseIntExp(getFirstArg(), priority)) { pragmaError("requires at most 1 integer literal parameter"); fatal(); } if (priority > 65535) { pragmaError("may not have a priority greater than 65535"); priority = 65535; } } else { priority = 65535; } char buf[8]; snprintf(buf, 8, "%llu", static_cast(priority)); arg1str = strdup(buf); return ident == Id::LDC_global_crt_ctor || ident == Id::crt_constructor ? LLVMglobal_crt_ctor : LLVMglobal_crt_dtor; } // pragma(LDC_no_typeinfo) { typedecl(s) } if (ident == Id::LDC_no_typeinfo) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMno_typeinfo; } // pragma(LDC_no_moduleinfo) ; if (ident == Id::LDC_no_moduleinfo) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } sc->_module->noModuleInfo = true; return LLVMignore; } // pragma(LDC_alloca) { funcdecl(s) } if (ident == Id::LDC_alloca) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMalloca; } // pragma(LDC_va_start) { templdecl(s) } if (ident == Id::LDC_va_start) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMva_start; } // pragma(LDC_va_copy) { funcdecl(s) } if (ident == Id::LDC_va_copy) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMva_copy; } // pragma(LDC_va_end) { funcdecl(s) } if (ident == Id::LDC_va_end) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMva_end; } // pragma(LDC_va_arg) { templdecl(s) } if (ident == Id::LDC_va_arg) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMva_arg; } // pragma(LDC_fence) { funcdecl(s) } if (ident == Id::LDC_fence) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMfence; } // pragma(LDC_atomic_load) { templdecl(s) } if (ident == Id::LDC_atomic_load) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMatomic_load; } // pragma(LDC_atomic_store) { templdecl(s) } if (ident == Id::LDC_atomic_store) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMatomic_store; } // pragma(LDC_atomic_cmp_xchg) { templdecl(s) } if (ident == Id::LDC_atomic_cmp_xchg) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMatomic_cmp_xchg; } // pragma(LDC_atomic_rmw, "string") { templdecl(s) } if (ident == Id::LDC_atomic_rmw) { if (!args || args->length != 1 || !parseStringExp(getFirstArg(), arg1str)) { pragmaError("requires exactly 1 string literal parameter"); fatal(); } return LLVMatomic_rmw; } // pragma(LDC_verbose); if (ident == Id::LDC_verbose) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } sc->_module->llvmForceLogging = true; return LLVMignore; } // pragma(LDC_inline_asm) { templdecl(s) } if (ident == Id::LDC_inline_asm) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMinline_asm; } // pragma(LDC_inline_ir) { templdecl(s) } if (ident == Id::LDC_inline_ir) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMinline_ir; } // pragma(LDC_extern_weak) { vardecl(s) } if (ident == Id::LDC_extern_weak) { if (args && args->length > 0) { pragmaError("takes no parameters"); fatal(); } return LLVMextern_weak; } // pragma(LDC_profile_instr, [true | false]) if (ident == Id::LDC_profile_instr) { // checking of this pragma is done in DtoCheckProfileInstrPragma() return LLVMprofile_instr; } return LLVMnone; } void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s, LDCPragma llvm_internal, const char *const arg1str) { if (llvm_internal == LLVMnone || llvm_internal == LLVMignore || llvm_internal == LLVMprofile_instr) { return; } Identifier *ident = decl->ident; switch (llvm_internal) { case LLVMintrinsic: { int count = applyFunctionPragma(s, [=](FuncDeclaration *fd) { fd->llvmInternal = llvm_internal; fd->mangleOverride = {strlen(arg1str), arg1str}; }); count += applyTemplatePragma(s, [=](TemplateDeclaration *td) { td->llvmInternal = llvm_internal; td->intrinsicName = arg1str; }); if (count != 1) { error(s->loc, "the `%s` pragma doesn't affect exactly 1 function/template " "declaration", ident->toChars()); fatal(); } break; } case LLVMglobal_crt_ctor: case LLVMglobal_crt_dtor: { const int count = applyFunctionPragma(s, [=](FuncDeclaration *fd) { auto tf = fd->type->isTypeFunction(); assert(tf); bool isValid = false; if (tf->next->ty != TY::Tvoid) { error(fd->loc, "%s `%s` must return `void` for `pragma(%s)`", fd->kind(), fd->toPrettyChars(), ident->toChars()); } else if (tf->parameterList.length() > 0) { error(fd->loc, "%s `%s` must not take any parameters for `pragma(%s)`", fd->kind(), fd->toPrettyChars(), ident->toChars()); } else if (fd->isThis()) { error(fd->loc, "%s `%s` cannot be a non-static member function for `pragma(%s)`", fd->kind(), fd->toPrettyChars(), ident->toChars()); } else { isValid = true; } if (!isValid) { if (llvm_internal == LLVMglobal_crt_ctor) { fd->isCrtCtor(false); } else { fd->isCrtDtor(false); } } else { if (llvm_internal == LLVMglobal_crt_ctor) { fd->isCrtCtor(true); } else { fd->isCrtDtor(true); } fd->priority = std::atoi(arg1str); } }); if (count != 1) { error(s->loc, "the `%s` pragma doesn't affect exactly 1 function declaration", ident->toChars()); fatal(); } break; } case LLVMatomic_rmw: { const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) { td->llvmInternal = llvm_internal; td->intrinsicName = arg1str; }); if (count != 1) { error(s->loc, "the `%s` pragma doesn't affect exactly 1 template declaration", ident->toChars()); fatal(); } break; } case LLVMva_start: case LLVMva_arg: case LLVMatomic_load: case LLVMatomic_store: case LLVMatomic_cmp_xchg: { const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) { if (td->parameters->length != 1) { error( s->loc, "the `%s` pragma template must have exactly one template parameter", ident->toChars()); fatal(); } else if (!td->onemember) { error(s->loc, "the `%s` pragma template must have exactly one member", ident->toChars()); fatal(); } else if (td->overnext || td->overroot) { error(s->loc, "the `%s` pragma template must not be overloaded", ident->toChars()); fatal(); } td->llvmInternal = llvm_internal; }); if (count != 1) { error(s->loc, "the `%s` pragma doesn't affect exactly 1 template declaration", ident->toChars()); fatal(); } break; } case LLVMalloca: case LLVMva_copy: case LLVMva_end: case LLVMfence: case LLVMbitop_bt: case LLVMbitop_btc: case LLVMbitop_btr: case LLVMbitop_bts: case LLVMbitop_vld: case LLVMbitop_vst: { const int count = applyFunctionPragma(s, [=](FuncDeclaration *fd) { fd->llvmInternal = llvm_internal; }); if (count != 1) { error(s->loc, "the `%s` pragma doesn't affect exactly 1 function declaration", ident->toChars()); fatal(); } break; } case LLVMno_typeinfo: applyPragma(s, [](Dsymbol *s) { return s; }, [=](Dsymbol *s) { s->llvmInternal = llvm_internal; }); break; case LLVMinline_asm: { const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) { if (td->parameters->length > 1) { error(s->loc, "the `%s` pragma template must have exactly zero or one " "template parameters", ident->toChars()); fatal(); } else if (!td->onemember) { error(s->loc, "the `%s` pragma template must have exactly one member", ident->toChars()); fatal(); } td->llvmInternal = llvm_internal; }); if (count != 1) { error(s->loc, "the `%s` pragma doesn't affect exactly 1 template declaration", ident->toChars()); fatal(); } break; } case LLVMinline_ir: { DtoCheckInlineIRPragma(ident, s); const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) { if (!td->onemember) { error(s->loc, "the `%s` pragma template must have exactly one member", ident->toChars()); fatal(); } td->llvmInternal = llvm_internal; }); if (count != 1) { error(s->loc, "the `%s` pragma doesn't affect exactly 1 template declaration", ident->toChars()); fatal(); } break; } case LLVMextern_weak: { int count = applyVariablePragma(s, [=](VarDeclaration *vd) { if (!vd->isDataseg() || !(vd->storage_class & STCextern)) { error(s->loc, "`%s` requires storage class `extern`", ident->toChars()); fatal(); } // It seems like the interaction between weak symbols and thread-local // storage is not well-defined (the address of an undefined weak TLS // symbol is non-zero on the ELF static TLS model on Linux x86_64). // Thus, just disallow this altogether. if (vd->isThreadlocal()) { error(s->loc, "`%s` cannot be applied to thread-local variable `%s`", ident->toChars(), vd->toPrettyChars()); fatal(); } vd->llvmInternal = llvm_internal; }); count += applyFunctionPragma(s, [=](FuncDeclaration *fd) { if (fd->fbody) { error(s->loc, "`%s` cannot be applied to function definitions", ident->toChars()); fatal(); } fd->llvmInternal = llvm_internal; }); if (count == 0) { error(s->loc, "the `%s` pragma doesn't affect any variable or function " "declarations", ident->toChars()); fatal(); } break; } default: warning(s->loc, "the LDC specific pragma `%s` is not yet implemented, ignoring", ident->toChars()); } } bool DtoIsIntrinsic(FuncDeclaration *fd) { return fd->llvmInternal == LLVMintrinsic || DtoIsMagicIntrinsic(fd); } bool DtoIsMagicIntrinsic(FuncDeclaration *fd) { // note: keep in sync with DtoLowerMagicIntrinsic() switch (fd->llvmInternal) { case LLVMva_start: // doesn't map to an instruction, but handled as magic intrinsic by LDC case LLVMva_copy: // ditto case LLVMva_arg: case LLVMva_end: // ditto case LLVMalloca: case LLVMfence: case LLVMatomic_store: case LLVMatomic_load: case LLVMatomic_cmp_xchg: case LLVMatomic_rmw: case LLVMbitop_bt: case LLVMbitop_btc: case LLVMbitop_btr: case LLVMbitop_bts: case LLVMbitop_vld: case LLVMbitop_vst: return true; default: return false; } } // pragma(LDC_profile_instr, [true | false]) // Return false if an error occurred. bool DtoCheckProfileInstrPragma(Expression *arg, bool &value) { return parseBoolExp(arg, value); }